Merge pull request 'add:班级管理;' (#67) from yangws into main
Reviewed-on: #67
This commit is contained in:
commit
e6431c6ac6
|
@ -22,6 +22,7 @@
|
||||||
"@electron-toolkit/preload": "^3.0.1",
|
"@electron-toolkit/preload": "^3.0.1",
|
||||||
"@electron-toolkit/utils": "^3.0.0",
|
"@electron-toolkit/utils": "^3.0.0",
|
||||||
"@element-plus/icons-vue": "^2.3.1",
|
"@element-plus/icons-vue": "^2.3.1",
|
||||||
|
"@vitejs/plugin-vue-jsx": "^4.0.0",
|
||||||
"@vueuse/core": "^10.11.0",
|
"@vueuse/core": "^10.11.0",
|
||||||
"crypto-js": "^4.2.0",
|
"crypto-js": "^4.2.0",
|
||||||
"electron-dl-manager": "^3.0.0",
|
"electron-dl-manager": "^3.0.0",
|
||||||
|
@ -34,8 +35,9 @@
|
||||||
"pinia": "^2.1.7",
|
"pinia": "^2.1.7",
|
||||||
"pinia-plugin-persistedstate": "^3.2.1",
|
"pinia-plugin-persistedstate": "^3.2.1",
|
||||||
"spark-md5": "^3.0.2",
|
"spark-md5": "^3.0.2",
|
||||||
"vue-cropper": "^1.0.3",
|
"vue-cropper": "^1.1.4",
|
||||||
"vue-router": "^4.4.0"
|
"vue-router": "^4.4.0",
|
||||||
|
"xlsx": "^0.18.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@electron-toolkit/eslint-config": "^1.0.2",
|
"@electron-toolkit/eslint-config": "^1.0.2",
|
||||||
|
|
|
@ -0,0 +1,132 @@
|
||||||
|
// 查询evaluation列表
|
||||||
|
import request from '@/utils/request'
|
||||||
|
// 查询班级列表
|
||||||
|
export function listClassmain(query) {
|
||||||
|
return request({
|
||||||
|
url: '/education/classmain/list',
|
||||||
|
method: 'get',
|
||||||
|
params: query
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 查询学生列表
|
||||||
|
export function listClassuser(query) {
|
||||||
|
return request({
|
||||||
|
url: '/education/classuser/list',
|
||||||
|
method: 'get',
|
||||||
|
params: query
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 新增班级
|
||||||
|
export function addClassmain(data) {
|
||||||
|
return request({
|
||||||
|
url: '/education/classmain',
|
||||||
|
method: 'post',
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 查询所有学科的列表
|
||||||
|
export function listEvaluation(query) {
|
||||||
|
return request({
|
||||||
|
url: '/education/evaluation/list',
|
||||||
|
method: 'get',
|
||||||
|
params: query
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 新增小组
|
||||||
|
export function addClassgroup(data) {
|
||||||
|
return request({
|
||||||
|
url: '/education/classgroup',
|
||||||
|
method: 'post',
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
//班级详情
|
||||||
|
export function getClassmain(id) {
|
||||||
|
return request({
|
||||||
|
url: '/education/classmain/' + id,
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 获取小组列表
|
||||||
|
export function listClassgroup(query) {
|
||||||
|
return request({
|
||||||
|
url: '/education/classgroup/list',
|
||||||
|
method: 'get',
|
||||||
|
params: query
|
||||||
|
})
|
||||||
|
}
|
||||||
|
//删除小组
|
||||||
|
export function delClassgroup(id) {
|
||||||
|
return request({
|
||||||
|
url: '/education/classgroup/' + id,
|
||||||
|
method: 'delete'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
//查询小组信息
|
||||||
|
export function getClassgroup(id) {
|
||||||
|
return request({
|
||||||
|
url: '/education/classgroup/' + id,
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
//修改小组信息
|
||||||
|
export function updateClassgroup(data) {
|
||||||
|
return request({
|
||||||
|
url: '/education/classgroup',
|
||||||
|
method: 'put',
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
//新增学生
|
||||||
|
export function addStudentmain(data) {
|
||||||
|
return request({
|
||||||
|
url: '/education/studentmain',
|
||||||
|
method: 'post',
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
//修改学生信息
|
||||||
|
export function updateStudentmain(data) {
|
||||||
|
return request({
|
||||||
|
url: '/education/studentmain',
|
||||||
|
method: 'put',
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
//获取学生信息
|
||||||
|
export function getStudentmain(id) {
|
||||||
|
return request({
|
||||||
|
url: '/education/studentmain/' + id,
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
//删除学生
|
||||||
|
export function leaveClass(data) {
|
||||||
|
return request({
|
||||||
|
url: '/education/classuser/leaveClass',
|
||||||
|
method: 'post',
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
//删除学生所有数据
|
||||||
|
export function removeStudentDataAll(id) {
|
||||||
|
return request({
|
||||||
|
url: '/education/studentmain/removeStudent/' + id,
|
||||||
|
method: 'post'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
//删除教室
|
||||||
|
export function delClassroom(id) {
|
||||||
|
return request({
|
||||||
|
url: '/education/classroom/' + id,
|
||||||
|
method: 'delete'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
//导入学生
|
||||||
|
export function addStudentmainByNameArray(data) {
|
||||||
|
return request({
|
||||||
|
url: '/education/studentmain/addByNameArray',
|
||||||
|
method: 'post',
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
}
|
|
@ -0,0 +1,75 @@
|
||||||
|
<template>
|
||||||
|
<div class="update-avatar">
|
||||||
|
<img :src="image" ref="imgRef" />
|
||||||
|
<div class="toolbar">
|
||||||
|
<span @click="handleClose">取消</span>
|
||||||
|
<!-- 实时预览 -->
|
||||||
|
<div class="view"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, onMounted } from 'vue';
|
||||||
|
import 'cropperjs/dist/cropper.css';
|
||||||
|
import Cropper from 'cropperjs';
|
||||||
|
const props = defineProps({
|
||||||
|
image: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const imgRef = ref(null);
|
||||||
|
const cropper = ref(null);
|
||||||
|
|
||||||
|
const handleClose = () => {
|
||||||
|
// 你的关闭逻辑
|
||||||
|
};
|
||||||
|
|
||||||
|
const cropperInit = () => {
|
||||||
|
const image = imgRef.value;
|
||||||
|
cropper.value = new Cropper(image, {
|
||||||
|
aspectRatio: 1,
|
||||||
|
dragMode: 'move',
|
||||||
|
background: false,
|
||||||
|
preview: '.view'
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const onConfirm = () => {
|
||||||
|
cropper.value.getCroppedCanvas().toBlob(blob => {
|
||||||
|
// 你的确认逻辑
|
||||||
|
console.log('blob', blob);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
cropperInit();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<style scoped>
|
||||||
|
.update-avatar {
|
||||||
|
width: 170px;
|
||||||
|
height: 100px;
|
||||||
|
border: 1px blue solid;
|
||||||
|
|
||||||
|
}
|
||||||
|
.update-avatar img {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
.update-avatar .view {
|
||||||
|
width: 100px;
|
||||||
|
height: 100px;
|
||||||
|
border: 1px black solid;
|
||||||
|
}
|
||||||
|
.view {
|
||||||
|
width: 100px;
|
||||||
|
height: 100px;
|
||||||
|
overflow: hidden; /*这个超出设置为隐藏很重要,否则就会整个显示出来了*/
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
<template>
|
||||||
|
<el-select v-model="modelValue" v-bind="$attrs" :placeholder="placeholder">
|
||||||
|
<slot></slot>
|
||||||
|
</el-select>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, watch, defineProps, onMounted, defineEmits } from 'vue';
|
||||||
|
const placeholder = ref('')
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
statement:String,
|
||||||
|
});
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
placeholder.value = props.statement || "请选择"
|
||||||
|
})
|
||||||
|
const emit = defineEmits(['update:modelValue']);
|
||||||
|
const modelValue = ref(props.modelValue);
|
||||||
|
watch(modelValue, (value) => {
|
||||||
|
emit('update:modelValue', value);
|
||||||
|
});
|
||||||
|
</script>
|
|
@ -25,6 +25,7 @@
|
||||||
<template #dropdown>
|
<template #dropdown>
|
||||||
<el-dropdown-menu>
|
<el-dropdown-menu>
|
||||||
<el-dropdown-item @click="changePage('/profile')">个人中心</el-dropdown-item>
|
<el-dropdown-item @click="changePage('/profile')">个人中心</el-dropdown-item>
|
||||||
|
<el-dropdown-item @click="changePage('/class')">班级中心</el-dropdown-item>
|
||||||
<el-dropdown-item divided command="logout">
|
<el-dropdown-item divided command="logout">
|
||||||
<span>退出登录</span>
|
<span>退出登录</span>
|
||||||
</el-dropdown-item>
|
</el-dropdown-item>
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
/*
|
||||||
|
author: yangws
|
||||||
|
time: 2024-20-24 17:20:50
|
||||||
|
function: 用于删除班级更新主页面
|
||||||
|
*/
|
||||||
|
import { defineStore } from "pinia"
|
||||||
|
|
||||||
|
const delClassDemo = defineStore(
|
||||||
|
'del',
|
||||||
|
{
|
||||||
|
state: () => ({
|
||||||
|
idDelete: false
|
||||||
|
}),
|
||||||
|
actions: {
|
||||||
|
//删除提醒
|
||||||
|
isDelClass(){
|
||||||
|
this.idDelete = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
export default delClassDemo
|
|
@ -0,0 +1,53 @@
|
||||||
|
<template>
|
||||||
|
<el-menu
|
||||||
|
style="border:none;overflow: auto;flex: 1"
|
||||||
|
class="el-menu-vertical-demo"
|
||||||
|
:default-active="activeIndex"
|
||||||
|
@select="handleSelect"
|
||||||
|
>
|
||||||
|
<template v-for="(item,index) in classList" :key="index">
|
||||||
|
<el-sub-menu :index="`${index}`">
|
||||||
|
<template #title>
|
||||||
|
<span style="overflow: hidden;white-space: nowrap;text-overflow: ellipsis;">{{item.caption}}</span>
|
||||||
|
</template>
|
||||||
|
<el-menu-item-group>
|
||||||
|
<el-menu-item v-for="(items,routerIndex) in menuItems" :key="`${routerIndex}`" :index="`${items.index}-${item.id}`">{{items.title}}</el-menu-item>
|
||||||
|
</el-menu-item-group>
|
||||||
|
</el-sub-menu>
|
||||||
|
</template>
|
||||||
|
</el-menu>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import {ref,defineProps,defineEmits} from "vue";
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
|
const activeIndex = ref('0')
|
||||||
|
const router = useRouter()
|
||||||
|
const props = defineProps({
|
||||||
|
classList:{
|
||||||
|
type:Array,
|
||||||
|
},
|
||||||
|
menuItems:{
|
||||||
|
type:Array,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const emits = defineEmits(['handleSelect'])
|
||||||
|
|
||||||
|
//点击跳转路由
|
||||||
|
const handleSelect = (itemDom,pathKey) => {
|
||||||
|
const parts = pathKey[1].split("-")
|
||||||
|
const result = parts.slice(0, 2).join("-")
|
||||||
|
const index = props.menuItems.findIndex(item=>item.index===result)
|
||||||
|
const id = props.classList[pathKey[0]].id
|
||||||
|
// router.push({
|
||||||
|
// path: `${props.menuItems[index].path}/${id}`,
|
||||||
|
// query: { id }
|
||||||
|
// })
|
||||||
|
emits('handleSelect',{index,id})
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
|
@ -0,0 +1,417 @@
|
||||||
|
<template>
|
||||||
|
<el-card style="width: 100%;height: 100%">
|
||||||
|
<template #header>
|
||||||
|
<div class="card-header" style="text-align: left">
|
||||||
|
<el-button type="primary" @click="addGroup">新建分组</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template v-if="groupList.length > 0">
|
||||||
|
<div style="font-size: 16px;font-weight: bold;color: #000;text-align: left;margin-bottom: 5px">可用分组</div>
|
||||||
|
<div class="groupList">
|
||||||
|
<template v-for="(item,index) in groupList" :key="index">
|
||||||
|
<el-card style="width: 20%;
|
||||||
|
margin-right: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
position: relative;"
|
||||||
|
v-if="item.parentid === 0"
|
||||||
|
@mouseenter="cardEnter(item,index)"
|
||||||
|
@mouseleave="cardLeave(item,index)"
|
||||||
|
>
|
||||||
|
<div class="card-content" @click="showGroupDialog(item)">
|
||||||
|
<div style="text-align: left;margin-bottom:10px">
|
||||||
|
<el-text class="mx-1">小组名称:</el-text>
|
||||||
|
<el-text class="mx-1" type="primary" size="large">
|
||||||
|
{{item.groupname}}
|
||||||
|
</el-text>
|
||||||
|
</div>
|
||||||
|
<el-row :gutter="4">
|
||||||
|
<el-col>
|
||||||
|
<el-text class="mx-1" type="warning">共{{item.studentcount}}名学生</el-text>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</div>
|
||||||
|
<el-popconfirm
|
||||||
|
confirm-button-text="是"
|
||||||
|
title="确定删除该小组吗?"
|
||||||
|
@confirm="delGroup(item,index)"
|
||||||
|
>
|
||||||
|
<template #reference>
|
||||||
|
<div class="card-row" v-show="item.isShow">
|
||||||
|
删除
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-popconfirm>
|
||||||
|
</el-card>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<el-empty description="暂无小组" style="margin: 0 auto"/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
</el-card>
|
||||||
|
<!-- 新建小组-->
|
||||||
|
<el-dialog v-model="groupVisible" title="新建小组" width="40%">
|
||||||
|
<el-form ref="myForm" label-width="80px" :model="groupForm" :rules="rules">
|
||||||
|
<el-form-item label="小组名称" style="width: 80%" prop="groupname">
|
||||||
|
<el-input v-model="groupForm.groupname" placeholder="请输入小组名称"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="排序" style="width: 80%" prop="orderidx">
|
||||||
|
<el-input-number v-model="groupForm.orderidx" placeholder="排序"></el-input-number>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="对应学科">
|
||||||
|
{{ userStore.edusubject }}
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
请首先创建小组,完成后再点击进入添加学生
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<el-button @click="groupVisible = false">取 消</el-button>
|
||||||
|
<el-button type="primary" @click="btnGroupSave">确 定</el-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
<!-- 添加学生-->
|
||||||
|
<el-dialog v-model="dialogVisible" width="60%">
|
||||||
|
<el-form>
|
||||||
|
<el-form-item>
|
||||||
|
<!-- 选择学生-->
|
||||||
|
<div class="allItems">
|
||||||
|
<el-row :gutter="4" style="width: 100%">
|
||||||
|
<el-col :span="4">已选学生:</el-col>
|
||||||
|
<el-col :span="20" class="studentContent">
|
||||||
|
<div class="aItem" @mouseenter="showGroup(stuItem,stuIndex)" @mouseleave="hideGroup(stuItem,stuIndex)" v-for="(stuItem,stuIndex) in selectStudents" :key="stuIndex">
|
||||||
|
<el-tag closable size="large" :class="stuItem.choose?'el-row-group':''" style="width: 100%" plain type="primary" @close="selectStudent(stuItem,stuIndex)">{{stuItem.studentname}}</el-tag>
|
||||||
|
<el-button v-show="stuItem.showGroup" class="itemGroup" plain link type="primary" @click="chooseGroup(stuItem,stuIndex)">{{!stuItem.choose?'设置组长':''}}</el-button>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-divider />
|
||||||
|
<el-row :gutter="4" style="width: 100%">
|
||||||
|
<el-col :span="4">未选学生:</el-col>
|
||||||
|
<el-col :span="20" class="studentContent">
|
||||||
|
<div class="aItem" v-for="(stuItem,stuIndex) in avaliableStudents" :key="stuIndex">
|
||||||
|
<el-button style="width: 100%" plain @click="chooseItem(stuItem,stuIndex)">{{stuItem.name || stuItem.studentname}}</el-button>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||||
|
<el-button type="primary" @click="btnSave">确 定</el-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import {listClassmain, listClassuser, addClassgroup, listClassgroup, delClassgroup,getClassgroup,updateClassgroup} from '@/api/classManage/index'
|
||||||
|
import {ref, onMounted, reactive, defineProps, watch} from 'vue'
|
||||||
|
import useUserStore from '@/store/modules/user'
|
||||||
|
import {useRoute} from 'vue-router'
|
||||||
|
import {ElMessage} from "element-plus";
|
||||||
|
|
||||||
|
const userStore = useUserStore()
|
||||||
|
const route = useRoute()
|
||||||
|
const props = defineProps({
|
||||||
|
classId: {type: Number}
|
||||||
|
})
|
||||||
|
//表单验证
|
||||||
|
const groupForm = reactive({
|
||||||
|
groupname: '',
|
||||||
|
orderidx: 0,
|
||||||
|
classid: Number(props.classId),
|
||||||
|
edituserid: userStore.id,
|
||||||
|
edusubject: userStore.edusubject,
|
||||||
|
userid : 0,
|
||||||
|
studentid : 0,
|
||||||
|
parentid:0,
|
||||||
|
status:''
|
||||||
|
})
|
||||||
|
//用于可以分组的学生
|
||||||
|
const avaliableStudents = ref([])
|
||||||
|
const selectStudents = ref([])
|
||||||
|
const rules = reactive({
|
||||||
|
groupname: [
|
||||||
|
{ required: true, message: '请输入小组名称', trigger: 'blur' },
|
||||||
|
{ min: 1, max: 10, message: '小组名称必须是 1-10 位 的字符', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
})
|
||||||
|
const myForm = ref(null)
|
||||||
|
//获取对应的是哪一个班级
|
||||||
|
const routeClass = ref(0)
|
||||||
|
//弹窗控制
|
||||||
|
const dialogVisible = ref(false)
|
||||||
|
const groupVisible = ref(false)
|
||||||
|
//左侧班级控制
|
||||||
|
const classList = ref([])
|
||||||
|
//小组列表
|
||||||
|
const groupList = ref([])
|
||||||
|
//当前小组的信息
|
||||||
|
const groupFormInfo = ref({})
|
||||||
|
//获取parentId
|
||||||
|
const parentId = ref(0)
|
||||||
|
//获取组长的信息
|
||||||
|
const groupLeader = reactive({
|
||||||
|
id: 0,
|
||||||
|
name: '',
|
||||||
|
})
|
||||||
|
// 获取学生信息
|
||||||
|
const getStudentInfo = (id) => {
|
||||||
|
avaliableStudents.value = []
|
||||||
|
listClassuser({ classid:id, pageSize: 100 }).then(response => {
|
||||||
|
response.rows.forEach(item => {
|
||||||
|
item.showGroup = false
|
||||||
|
item.choose = false
|
||||||
|
if(item.inrole == 'student'){
|
||||||
|
const index = groupList.value.findIndex(items => items.studentid == item.studentid)
|
||||||
|
if(index === -1){
|
||||||
|
avaliableStudents.value.push(item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取班级信息
|
||||||
|
const getClassInfo = () => {
|
||||||
|
listClassmain({ entpid: userStore.deptId, pageSize: 500, status: 'open' }).then(response => {
|
||||||
|
classList.value = response.rows.map(item => {
|
||||||
|
return {
|
||||||
|
...item,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
routeClass.value = classList.value.findIndex(item => item.id == props.classId)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
//选择学生
|
||||||
|
const chooseItem = (item,stuIndex) => {
|
||||||
|
let formdata = {}
|
||||||
|
formdata.classid = props.classId;
|
||||||
|
formdata.edituserid = userStore.id;
|
||||||
|
formdata.userid = item.userid;
|
||||||
|
formdata.parentid = parentId.value;
|
||||||
|
formdata.studentid = item.studentid;
|
||||||
|
addClassgroup(formdata).then(response => {
|
||||||
|
if(response.code === 200)
|
||||||
|
//刷新小组
|
||||||
|
getGroupListInfo()
|
||||||
|
//刷新已选学生列表
|
||||||
|
getSelectStudentInfo(parentId.value)
|
||||||
|
})
|
||||||
|
avaliableStudents.value.splice(stuIndex,1)
|
||||||
|
}
|
||||||
|
//已选择的学生
|
||||||
|
const selectStudent = (item) => {
|
||||||
|
delClassgroup(item.id).then(() => {
|
||||||
|
getSelectStudentInfo(parentId.value)
|
||||||
|
getGroupListInfo()
|
||||||
|
})
|
||||||
|
avaliableStudents.value.push(item)
|
||||||
|
}
|
||||||
|
//选择组长
|
||||||
|
const chooseGroup = (item) => {
|
||||||
|
let choose = item.choose
|
||||||
|
selectStudents.value.forEach((items) => {
|
||||||
|
items.showGroup = false
|
||||||
|
items.choose = false
|
||||||
|
})
|
||||||
|
const index = selectStudents.value.findIndex(items => items.studentid == item.studentid)
|
||||||
|
selectStudents.value[index].choose = !choose
|
||||||
|
item.showGroup = true
|
||||||
|
updateClassgroup({ id: groupFormInfo.value.id, studentid: item.studentid }).then(() => {
|
||||||
|
getClassgroup(groupFormInfo.value.id).then(res => {
|
||||||
|
groupLeader.id = res.data.studentid;
|
||||||
|
groupLeader.name = res.data.studentname;
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
author: yangws
|
||||||
|
time: 2024-20-23 11:20:04
|
||||||
|
function:鼠标的移入移出
|
||||||
|
*/
|
||||||
|
const showGroup = (item) => {
|
||||||
|
item.showGroup = true
|
||||||
|
}
|
||||||
|
const hideGroup = (item) => {
|
||||||
|
if(item.choose) return
|
||||||
|
item.showGroup = false
|
||||||
|
}
|
||||||
|
const showGroupDialog = (item) => {
|
||||||
|
parentId.value = item.id
|
||||||
|
// 获取点击小组的信息
|
||||||
|
getClassgroup(item.id).then((response) => {
|
||||||
|
groupFormInfo.value = Object.assign({},response.data)
|
||||||
|
//未选学生列表
|
||||||
|
getStudentInfo(props.classId)
|
||||||
|
//已选学生列表
|
||||||
|
getSelectStudentInfo(parentId.value)
|
||||||
|
//获取小组列表
|
||||||
|
getGroupListInfo()
|
||||||
|
dialogVisible.value = true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const btnSave = () => {
|
||||||
|
updateClassgroup({id:groupFormInfo.value.id,groupname:groupFormInfo.value.groupname,orderidx:groupFormInfo.value.orderidx}).then((response) => {
|
||||||
|
if (response.code === 200) {
|
||||||
|
ElMessage({
|
||||||
|
message: '修改成功',
|
||||||
|
type: 'success',
|
||||||
|
})
|
||||||
|
dialogVisible.value = false
|
||||||
|
getGroupListInfo()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
//新建小组
|
||||||
|
const addGroup = () => {
|
||||||
|
groupVisible.value = true
|
||||||
|
}
|
||||||
|
const btnGroupSave = () => {
|
||||||
|
myForm.value.validate((valid) => {
|
||||||
|
if (valid) {
|
||||||
|
addClassgroup(groupForm).then((response) => {
|
||||||
|
if (response.code === 200) {
|
||||||
|
ElMessage({
|
||||||
|
message: '新增成功',
|
||||||
|
type: 'success',
|
||||||
|
})
|
||||||
|
getGroupListInfo()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
groupVisible.value = false
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
//获取小组列表
|
||||||
|
const getGroupListInfo = () => {
|
||||||
|
//获取小组列表
|
||||||
|
listClassgroup({ classid: props.classId, orderby: 'orderidx', pageSize: 100 }).then(response => {
|
||||||
|
groupList.value = response.rows.map(item => {
|
||||||
|
return {
|
||||||
|
...item,
|
||||||
|
isShow : false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
//获取已选小组学生
|
||||||
|
const getSelectStudentInfo = (id) => {
|
||||||
|
listClassgroup({ parentid: id, orderby: 'orderidx', pageSize: 100 }).then(response => {
|
||||||
|
selectStudents.value = response.rows.map(item => {
|
||||||
|
item.showGroup=false
|
||||||
|
item.choose=false
|
||||||
|
return {
|
||||||
|
...item
|
||||||
|
}
|
||||||
|
})
|
||||||
|
//获取小组长
|
||||||
|
changeGroupLeader()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
//进入小组卡片和离开小组卡片
|
||||||
|
const cardEnter = (item) => {
|
||||||
|
item.isShow = true
|
||||||
|
}
|
||||||
|
const cardLeave = (item) => {
|
||||||
|
item.isShow = false
|
||||||
|
}
|
||||||
|
//删除小组
|
||||||
|
const delGroup = (item) => {
|
||||||
|
delClassgroup(item.id).then((response) => {
|
||||||
|
if (response.code === 200) {
|
||||||
|
ElMessage({
|
||||||
|
message: '删除成功',
|
||||||
|
type: 'success',
|
||||||
|
})
|
||||||
|
getGroupListInfo()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
//改变小组长
|
||||||
|
const changeGroupLeader = () => {
|
||||||
|
const index = selectStudents.value.findIndex(item => groupFormInfo.value.studentid == item.studentid)
|
||||||
|
selectStudents.value.forEach(item => {
|
||||||
|
item.choose = false
|
||||||
|
})
|
||||||
|
if(index !== -1)
|
||||||
|
selectStudents.value[index].choose = true
|
||||||
|
}
|
||||||
|
onMounted(() => {
|
||||||
|
getClassInfo()
|
||||||
|
getStudentInfo(props.classId)
|
||||||
|
getGroupListInfo()
|
||||||
|
})
|
||||||
|
watch(()=> props.classId,()=> {
|
||||||
|
getClassInfo()
|
||||||
|
getStudentInfo(props.classId)
|
||||||
|
getGroupListInfo()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.allItems{
|
||||||
|
display: flex;
|
||||||
|
justify-content: start;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
height: auto;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.allItems .aItem{
|
||||||
|
margin-bottom: 20px;
|
||||||
|
margin-right: 5px;
|
||||||
|
position: relative;
|
||||||
|
width: 80px;
|
||||||
|
}
|
||||||
|
.allItems .aItem .itemGroup{
|
||||||
|
position: absolute;
|
||||||
|
top: 32px;
|
||||||
|
left: 10px;
|
||||||
|
}
|
||||||
|
.el-row-group::after {
|
||||||
|
content: "组";
|
||||||
|
font-size: 12px;
|
||||||
|
width: auto;
|
||||||
|
height: auto;
|
||||||
|
color: rgb(121,187,255);
|
||||||
|
transform: rotate(45deg);
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
font-weight: bold;
|
||||||
|
letter-spacing:2px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.groupList{
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
.card-row {
|
||||||
|
font-size: 12px;
|
||||||
|
width: auto;
|
||||||
|
height: auto;
|
||||||
|
color: rgb(121,187,255);
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
font-weight: bold;
|
||||||
|
letter-spacing:2px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.studentContent{
|
||||||
|
display: flex;
|
||||||
|
justify-content: start;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
max-height: 150px;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
.card-content{
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,80 @@
|
||||||
|
<template>
|
||||||
|
<el-card style="width: 100%">
|
||||||
|
<template #header>
|
||||||
|
<div style="text-align: left">
|
||||||
|
<el-button type="danger" @click="deleteClassRoom">删除班级</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-descriptions :column="1">
|
||||||
|
<el-descriptions-item label="班级名称">{{ classInfo.className }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="教师">
|
||||||
|
<template v-if="classInfo.teacher.length > 0">
|
||||||
|
<el-tag type="primary" v-for="(item, index) in classInfo.teacher" :key="index">{{item.name}}</el-tag>
|
||||||
|
</template>
|
||||||
|
<template v-else>暂无</template>
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="学生人数">{{ classInfo.student.length }}人</el-descriptions-item>
|
||||||
|
</el-descriptions>
|
||||||
|
</el-card>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import {ElMessage, ElMessageBox} from "element-plus";
|
||||||
|
import { getClassmain,listClassuser,leaveClass} from '@/api/classManage/index'
|
||||||
|
import useUserStore from '@/store/modules/user'
|
||||||
|
import {reactive,onMounted,defineProps,nextTick,watch} from 'vue'
|
||||||
|
import delClassDemo from '@/store/modules/delClass'
|
||||||
|
const props = defineProps({
|
||||||
|
classId: {
|
||||||
|
type: Number,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const classInfo = reactive({
|
||||||
|
className: '',
|
||||||
|
teacher: [],
|
||||||
|
student: []
|
||||||
|
})
|
||||||
|
const isDelClass = delClassDemo()
|
||||||
|
const userStore = useUserStore()
|
||||||
|
//删除教室
|
||||||
|
const deleteClassRoom = () => {
|
||||||
|
ElMessageBox.alert('确认删除该班级?', {
|
||||||
|
confirmButtonText: '确认',
|
||||||
|
type: 'warning'
|
||||||
|
}).then(() => {
|
||||||
|
leaveClass({ classid: props.classId, userid: userStore.id, inrole: 'teacher' }).then(() => {
|
||||||
|
ElMessage({
|
||||||
|
message: '删除成功',
|
||||||
|
type: 'success',
|
||||||
|
})
|
||||||
|
//用于更新班级列表
|
||||||
|
isDelClass.isDelClass()
|
||||||
|
}).catch((error) => {
|
||||||
|
console.log(error)
|
||||||
|
});
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const getClassInfo = () => {
|
||||||
|
if(props.classId){
|
||||||
|
getClassmain(props.classId).then(response => {
|
||||||
|
classInfo.className = response.data.caption
|
||||||
|
listClassuser({classid:props.classId,pageSize:100}).then(res => {
|
||||||
|
classInfo.teacher = res.rows.filter(item => item.inrole === 'teacher')
|
||||||
|
classInfo.student = res.rows.filter(item => item.inrole === 'student')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onMounted(() => {
|
||||||
|
nextTick(() => {
|
||||||
|
getClassInfo()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
watch(()=> props.classId,()=> {
|
||||||
|
getClassInfo()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
|
@ -0,0 +1,292 @@
|
||||||
|
<template>
|
||||||
|
<div class="common-layout">
|
||||||
|
<el-container>
|
||||||
|
<el-aside style="width: 200px;margin-top: 20px">
|
||||||
|
<el-card style="width: 200px" class="el-card-demo" :style="{'min-height': (viewportHeight - 120) + 'px'}">
|
||||||
|
<div :style="{'max-height': (viewportHeight - 120) + 'px','overflow-y': 'auto'}">
|
||||||
|
<Aside :menuItems="menuItems" :classList="classList" @handleSelect="handleSelect"></Aside>
|
||||||
|
</div>
|
||||||
|
<template #footer>
|
||||||
|
<div>
|
||||||
|
<el-button @click="addClass" type="primary" :icon="Plus" >新增班级</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-card>
|
||||||
|
</el-aside>
|
||||||
|
<el-main :style="{'min-height': (viewportHeight - 160) + 'px'}">
|
||||||
|
<!-- <router-view :style="{'height': (viewportHeight - 120) + 'px','overflow-y': 'auto'}" :key="route.path"></router-view>-->
|
||||||
|
<!-- 班级概况-->
|
||||||
|
<ClassInfo v-if="currentIndex==0" :classId="classId"></ClassInfo>
|
||||||
|
<!-- 学生列表-->
|
||||||
|
<StudentList v-else-if="currentIndex==1" :classId="classId"></StudentList>
|
||||||
|
<!-- 分组情况-->
|
||||||
|
<BasicGroup v-else-if="currentIndex==2" :classId="classId"></BasicGroup>
|
||||||
|
</el-main>
|
||||||
|
</el-container>
|
||||||
|
</div>
|
||||||
|
<el-dialog v-model="dialogVisible" title="新增班级" width="50%">
|
||||||
|
<el-form
|
||||||
|
style="width: 100%"
|
||||||
|
label-width="auto"
|
||||||
|
:model="classForm"
|
||||||
|
:rules="rules"
|
||||||
|
ref="myForm"
|
||||||
|
>
|
||||||
|
<el-form-item label="班级名称" style="margin-right: 10px;width: 50%" prop="caption">
|
||||||
|
<el-input v-model="classForm.caption" placeholder="请输入班级名称"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="任选学科" style="margin-right: 10px;">
|
||||||
|
<el-radio-group v-model="classForm.edusubject" class="ml-4">
|
||||||
|
<template v-for="(item, index) in courseList" :key="index">
|
||||||
|
<el-radio v-if="item.edustage == userStore.edustage" :value="item.itemtitle">{{ item.itemtitle }}</el-radio>
|
||||||
|
</template>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="年级" style="margin-right: 10px;" prop="agekey">
|
||||||
|
<el-radio-group v-model="classForm.edudegree">
|
||||||
|
<template v-for="(item,index) in gradeList" :key="index">
|
||||||
|
<el-radio v-if="item.edustage == userStore.edustage" :value="item.value">{{ item.label }}</el-radio>
|
||||||
|
</template>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="老师" prop="teacherid">
|
||||||
|
{{ userStore.nickName }}
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="简要说明" style="margin-right: 10px;" prop="classdesc">
|
||||||
|
<el-input v-model="classForm.classdesc" placeholder="请输入简要说明"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||||
|
<el-button type="primary" @click="btnSave">确 定</el-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import {ref, onMounted, reactive,watch,nextTick} from 'vue'
|
||||||
|
import {listClassmain, addClassmain, listEvaluation} from '@/api/classManage/index'
|
||||||
|
import { Plus } from '@element-plus/icons-vue'
|
||||||
|
import useUserStore from '@/store/modules/user'
|
||||||
|
import {ElMessage} from "element-plus";
|
||||||
|
import delClassDemo from '@/store/modules/delClass'
|
||||||
|
import StudentList from './studentList.vue'
|
||||||
|
import BasicGroup from './basicGroup.vue'
|
||||||
|
import ClassInfo from './classInfo.vue'
|
||||||
|
import Aside from './aside.vue'
|
||||||
|
|
||||||
|
const userStore = useUserStore()
|
||||||
|
const isDel = delClassDemo()
|
||||||
|
//获取班级信息
|
||||||
|
const classList = ref([])
|
||||||
|
const menuItems = [
|
||||||
|
{
|
||||||
|
index: '1-1',
|
||||||
|
title: '班级概况',
|
||||||
|
path: '/classInfo'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
index: '1-2',
|
||||||
|
title: '学生名单',
|
||||||
|
path: '/studentList'
|
||||||
|
|
||||||
|
},
|
||||||
|
{
|
||||||
|
index: '1-3',
|
||||||
|
title: '基本分组',
|
||||||
|
path: '/basicGroup'
|
||||||
|
},
|
||||||
|
]
|
||||||
|
//创建班级基本信息
|
||||||
|
const classForm = reactive({
|
||||||
|
caption: '',
|
||||||
|
classdesc: '',
|
||||||
|
teacherid: '',
|
||||||
|
agekey:'',
|
||||||
|
edusubject:'',
|
||||||
|
edudegree:''
|
||||||
|
})
|
||||||
|
const viewportHeight = ref(0)
|
||||||
|
//学科列表
|
||||||
|
const courseList = ref([])
|
||||||
|
//班级id
|
||||||
|
const classId = ref('')
|
||||||
|
//当前页面
|
||||||
|
const currentIndex = ref(0)
|
||||||
|
//年级
|
||||||
|
const gradeList = ref([
|
||||||
|
{ label: '一年级', value: '1年级', checked: false, edustage: '小学', agekey: 1 },
|
||||||
|
{ label: '二年级', value: '2年级', checked: false, edustage: '小学', agekey: 2 },
|
||||||
|
{ label: '三年级', value: '3年级', checked: false, edustage: '小学', agekey: 3 },
|
||||||
|
{ label: '四年级', value: '4年级', checked: false, edustage: '小学', agekey: 4 },
|
||||||
|
{ label: '五年级', value: '5年级', checked: false, edustage: '小学', agekey: 5},
|
||||||
|
{ label: '六年级', value: '6年级', checked: false, edustage: '小学', agekey: 6 },
|
||||||
|
{ label: '初一', value: '初一', checked: false, edustage: '初中', agekey: 7 },
|
||||||
|
{ label: '初二', value: '初二', checked: false, edustage: '初中', agekey: 8 },
|
||||||
|
{ label: '初三', value: '初三', checked: false, edustage: '初中', agekey: 9 },
|
||||||
|
{ label: '高一', value: '高一', checked: false, edustage: '高中', agekey: 10 },
|
||||||
|
{ label: '高二', value: '高二', checked: false, edustage: '高中', agekey: 11 },
|
||||||
|
{ label: '高三', value: '高三', checked: false, edustage: '高中', agekey: 12 },
|
||||||
|
])
|
||||||
|
//打开班级
|
||||||
|
const dialogVisible = ref(false)
|
||||||
|
//校验表单
|
||||||
|
const myForm = ref(null)
|
||||||
|
const rules = reactive({
|
||||||
|
caption: [
|
||||||
|
{ required: true, message: '请输入班级名称', trigger: 'blur' },
|
||||||
|
{ min: 1, max: 10, message: '班级名称必须是 1-10 位 的字符', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
// 获取班级信息
|
||||||
|
const getClassInfo = () => {
|
||||||
|
classList.value = []
|
||||||
|
listClassmain({ entpid: userStore.deptId, pageSize: 500, status: 'open' }).then(response => {
|
||||||
|
response.rows.forEach(item => {
|
||||||
|
if(item.classteacherids && Number(item.classteacherids) === userStore?.id){
|
||||||
|
classList.value.push(item)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if(classList.value.length > 0){
|
||||||
|
classId.value = classList.value[0].id
|
||||||
|
currentIndex.value = 0
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
//获取所有学科
|
||||||
|
const getCourseList = () => {
|
||||||
|
// 获取基础的学科
|
||||||
|
listEvaluation({ itemkey: "subject", pageSize: 500 }).then((res) => {
|
||||||
|
courseList.value = [...res.rows];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// 新增班级
|
||||||
|
const addClass = () => {
|
||||||
|
dialogVisible.value = true
|
||||||
|
getCourseList()
|
||||||
|
}
|
||||||
|
const btnSave = () => {
|
||||||
|
myForm.value.validate((valid) => {
|
||||||
|
if (valid) {
|
||||||
|
//查看班级是否重名
|
||||||
|
listClassmain({ entpid: userStore.deptId, status: 'open', pageSize: 500 }).then(response => {
|
||||||
|
const data = [...response.rows]
|
||||||
|
const existList = [];
|
||||||
|
data.forEach(item => {
|
||||||
|
if (parseInt(textSimilar(item.caption, classForm.caption, 2)) > 80) {
|
||||||
|
existList.push(item);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (existList.length == 0) {
|
||||||
|
const age = classForm.edudegree;
|
||||||
|
const index = gradeList.value.findIndex(item => item.label === age);
|
||||||
|
classForm.agekey = gradeList.value[index].agekey
|
||||||
|
classForm.edudegree = `${gradeList.value[index].agekey}年级`
|
||||||
|
classForm.entpid = userStore.deptId;
|
||||||
|
classForm.status = 'open';
|
||||||
|
classForm.teachername = userStore.nickName;
|
||||||
|
classForm.teacherid = userStore.id;
|
||||||
|
classForm.teacherSubject = classForm.edusubject;
|
||||||
|
addClassmain(classForm).then(response => {
|
||||||
|
if (response.code === 200) {
|
||||||
|
dialogVisible.value = false
|
||||||
|
ElMessage({
|
||||||
|
message: '新增成功',
|
||||||
|
type: 'success',
|
||||||
|
})
|
||||||
|
getClassInfo()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}else{
|
||||||
|
ElMessage({
|
||||||
|
message: '班级名称重复',
|
||||||
|
type: 'warning',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
//文本计算
|
||||||
|
const textSimilar = (s, t, f) => {
|
||||||
|
if (!s || !t) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
if (s === t) {
|
||||||
|
return 100;
|
||||||
|
}
|
||||||
|
var l = s.length > t.length ? s.length : t.length
|
||||||
|
var n = s.length
|
||||||
|
var m = t.length
|
||||||
|
var d = []
|
||||||
|
f = f || 2
|
||||||
|
var min = function (a, b, c) {
|
||||||
|
return a < b ? (a < c ? a : c) : (b < c ? b : c)
|
||||||
|
}
|
||||||
|
var i, j, si, tj, cost
|
||||||
|
if (n === 0) return m
|
||||||
|
if (m === 0) return n
|
||||||
|
for (i = 0; i <= n; i++) {
|
||||||
|
d[i] = []
|
||||||
|
d[i][0] = i
|
||||||
|
}
|
||||||
|
for (j = 0; j <= m; j++) {
|
||||||
|
d[0][j] = j
|
||||||
|
}
|
||||||
|
for (i = 1; i <= n; i++) {
|
||||||
|
si = s.charAt(i - 1)
|
||||||
|
for (j = 1; j <= m; j++) {
|
||||||
|
tj = t.charAt(j - 1)
|
||||||
|
if (si === tj) {
|
||||||
|
cost = 0
|
||||||
|
} else {
|
||||||
|
cost = 1
|
||||||
|
}
|
||||||
|
d[i][j] = min(d[i - 1][j] + 1, d[i][j - 1] + 1, d[i - 1][j - 1] + cost)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let res = (1 - d[n][m] / l) * 100
|
||||||
|
return res.toFixed(f)
|
||||||
|
}
|
||||||
|
//获取视口高度
|
||||||
|
const getViewportHeight = () => {
|
||||||
|
return Math.max(
|
||||||
|
document.documentElement.clientHeight,
|
||||||
|
window.innerHeight || 0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const handleSelect = (obj) => {
|
||||||
|
classId.value = obj.id
|
||||||
|
currentIndex.value = obj.index
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getClassInfo()
|
||||||
|
nextTick(() => {
|
||||||
|
viewportHeight.value = getViewportHeight()
|
||||||
|
})
|
||||||
|
window.addEventListener('resize', () => {
|
||||||
|
viewportHeight.value = getViewportHeight()
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
watch(() => isDel.idDelete, () => {
|
||||||
|
//判断是否更改了班级
|
||||||
|
getClassInfo()
|
||||||
|
isDel.idDelete = false
|
||||||
|
})
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.el-card-demo{
|
||||||
|
width: 200px;
|
||||||
|
overflow-y: auto;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
|
@ -0,0 +1,454 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<el-card style="width: 100%;height: 100%">
|
||||||
|
<template #header>
|
||||||
|
<div style="text-align: left;display: flex;justify-content: space-between">
|
||||||
|
<div>
|
||||||
|
<el-button type="primary" @click="addStudent(0)">新增学生</el-button>
|
||||||
|
<el-button type="primary" @click="importStudent()">导入学生</el-button>
|
||||||
|
</div>
|
||||||
|
<el-text class="mx-1">点击学生头像查看学生信息</el-text>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div>
|
||||||
|
<div class="studentContent">
|
||||||
|
<template v-if="studentList.length > 0">
|
||||||
|
<div v-for="(item,index) in studentList" :key="index" style="width: 10%" @click="addStudent(item.studentid)">
|
||||||
|
<div>
|
||||||
|
<el-avatar
|
||||||
|
src="https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>{{ item.name }}</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<el-empty description="暂无学生" style="margin: 0 auto"/>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
<!-- 新增学生-->
|
||||||
|
<el-dialog v-model="studentVisible" title="学生信息" width="60%" append-to-body>
|
||||||
|
<el-form label-width="80px" ref="myForm" :model="studentForm" :rules="rules">
|
||||||
|
<el-form-item label="学生姓名" prop="name">
|
||||||
|
<el-input v-model="studentForm.name" placeholder="请输入学生姓名" style="width: 50%"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="性别">
|
||||||
|
<el-radio-group v-model="studentForm.gender">
|
||||||
|
<el-radio value="">空缺</el-radio>
|
||||||
|
<el-radio value="男">男生</el-radio>
|
||||||
|
<el-radio value="女">女生</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="出生日期" prop="birthday">
|
||||||
|
<el-date-picker v-model="studentForm.birthday" placeholder="请选择出生日期" style="width: 50%"/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="住址" prop="address">
|
||||||
|
<el-input v-model="studentForm.address" placeholder="请输入住址" style="width: 50%"/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="联系人" prop="parentmobile">
|
||||||
|
<el-input v-model="studentForm.parentname" placeholder="请输入联系人" style="width: 50%"/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="电话" prop="parentmobile">
|
||||||
|
<el-input v-model="studentForm.parentmobile" placeholder="请输入电话" style="width: 50%"/>
|
||||||
|
</el-form-item>
|
||||||
|
<div>
|
||||||
|
<el-row :gutter="4">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label-width="100px" label="平台登录账号">
|
||||||
|
系统自动创建
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label-width="100px" label="平台登录密码">
|
||||||
|
系统自动创建,默认为123123
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</div>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<el-button type="warning" v-show="studentForm.id > 0" @click="delStudent(1)">移出班级</el-button>
|
||||||
|
<el-button type="danger" v-show="studentForm.id > 0 && studentForm.editoruserid == userStore.id" @click="delStudent(2)">删除学生</el-button>
|
||||||
|
<el-button @click="studentVisible = false">取 消</el-button>
|
||||||
|
<el-button type="primary" @click="btnStudentSave">确 定</el-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
<!-- 学生导入-->
|
||||||
|
<el-dialog title="学生导入" v-model="importVisiable" :width="600" append-to-body>
|
||||||
|
<el-form>
|
||||||
|
<el-form-item label="第一步">
|
||||||
|
<el-link type="primary" :underline="true" style="font-size: 1.2em"
|
||||||
|
@click="downloadTemplate(tableInfo)">下载学生名单的Excel模板</el-link>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="第二步">
|
||||||
|
<div>打开下载到本地的Excel,填写学生的姓名等数据。</div>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="第三步">
|
||||||
|
<div>
|
||||||
|
<el-upload :on-change="handleStudentFileUploadChange"
|
||||||
|
ref="uploadRef" :limit="1" accept=".xlsx, .xls" :auto-upload="false" drag>
|
||||||
|
<div class="el-upload__text">将Excel文件拖到此处,或<em>点击上传</em></div>
|
||||||
|
</el-upload>
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="导入学生" v-if="tableInfo.studentList.length > 0">
|
||||||
|
<template v-for="(item, index) in tableInfo.studentList" :key="index">
|
||||||
|
<el-tag size="large" style="margin: 5px" closable @close="handleRemoveImportStudent(index)">{{ item.name }}</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<div class="dialog-footer">
|
||||||
|
<el-button @click="importVisiable = false">关 闭</el-button>
|
||||||
|
<el-button type="primary" @click="HandleImportStudentData()">导入</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import {addStudentmain, listClassuser,updateStudentmain,getClassmain,getStudentmain,leaveClass,removeStudentDataAll,addStudentmainByNameArray} from '@/api/classManage/index'
|
||||||
|
import {ref, onMounted, reactive, nextTick, defineProps, watch} from 'vue'
|
||||||
|
import {ElMessage,ElMessageBox} from "element-plus";
|
||||||
|
import useUserStore from '@/store/modules/user'
|
||||||
|
import * as XLSX from 'xlsx'
|
||||||
|
|
||||||
|
const studentVisible = ref(false)
|
||||||
|
const importVisiable = ref(false)
|
||||||
|
const userStore = useUserStore()
|
||||||
|
const props = defineProps({
|
||||||
|
classId: {type: Number}
|
||||||
|
})
|
||||||
|
//学生和老师的列表
|
||||||
|
const studentList = ref([])
|
||||||
|
let studentForm = reactive({
|
||||||
|
name: '',
|
||||||
|
gender: '',
|
||||||
|
birthday: '',
|
||||||
|
address: '',
|
||||||
|
parentname: '',
|
||||||
|
parentmobile: '',
|
||||||
|
isAutoCreateTimAccount: false,
|
||||||
|
})
|
||||||
|
// 用户导入
|
||||||
|
const tableInfo = reactive({
|
||||||
|
//在这配置excel表格
|
||||||
|
columns: [
|
||||||
|
{ title: '姓名', key: 'name' },
|
||||||
|
{ title: '性别', key: 'gender' },
|
||||||
|
{ title: '手机号', key: 'phone' },
|
||||||
|
{ title: '父母手机号1', key: 'parentmobile' },
|
||||||
|
{ title: '父母手机号2', key: 'parentmobile2' },
|
||||||
|
{ title: '年级', key: 'classname' },
|
||||||
|
{ title: '地址', key: 'address' },
|
||||||
|
{ title: '生日', key: 'birthday' },
|
||||||
|
{ title: '学校名称', key: 'schoolname' },
|
||||||
|
{ title: '描述', key: 'status' },
|
||||||
|
],
|
||||||
|
studentList: [],
|
||||||
|
})
|
||||||
|
//班级详情
|
||||||
|
const classInfo = reactive({})
|
||||||
|
//验证新增学生必填
|
||||||
|
const myForm = ref(null)
|
||||||
|
const rules = reactive({
|
||||||
|
name: [
|
||||||
|
{ required: true, message: '请输入学生名称', trigger: 'blur' },
|
||||||
|
{ min: 2, max: 5, message: '学生必须是 2-5 位 的字符', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
birthday: [
|
||||||
|
{ required: true, message: '请选择出生日期', trigger: 'blur' }
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
// 获取班级学生信息
|
||||||
|
const getClassStudentInfo = () => {
|
||||||
|
studentList.value = []
|
||||||
|
listClassuser({ classid:props.classId, pageSize: 100 }).then(response => {
|
||||||
|
response.rows.forEach(item => {
|
||||||
|
if(item.inrole == 'student'){
|
||||||
|
studentList.value.push(item)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
//获取班级信息
|
||||||
|
const getClassInfo = () => {
|
||||||
|
getClassmain(props.classId).then(response => {
|
||||||
|
classInfo.value = Object.assign({},response.data )
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const btnStudentSave = () => {
|
||||||
|
myForm.value.validate((valid) => {
|
||||||
|
if (valid) {
|
||||||
|
//判断学生是否存在
|
||||||
|
if(studentForm.id > 0){
|
||||||
|
updateStudentmain(studentForm).then(() => {
|
||||||
|
ElMessage({
|
||||||
|
message: '修改成功',
|
||||||
|
type: 'success',
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}else{
|
||||||
|
studentForm.entpid = classInfo.value.entpid;
|
||||||
|
studentForm.classid = classInfo.value.id;
|
||||||
|
studentForm.classname = classInfo.value.caption;
|
||||||
|
studentForm.schoolname = classInfo.value.entpname;
|
||||||
|
studentForm.status = '';
|
||||||
|
studentForm.editoruserid = userStore.id;
|
||||||
|
addStudentmain(studentForm).then((response) => {
|
||||||
|
if (response.code === 200) {
|
||||||
|
ElMessage({
|
||||||
|
message: '新增成功',
|
||||||
|
type: 'success',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
getClassStudentInfo()
|
||||||
|
studentVisible.value = false
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
//新增学生
|
||||||
|
const addStudent = (id) => {
|
||||||
|
if(id === 0){
|
||||||
|
studentForm.id = 0
|
||||||
|
studentForm.entpid = 0
|
||||||
|
studentForm.name = ''
|
||||||
|
studentVisible.value = true
|
||||||
|
}else{
|
||||||
|
getStudentmain(id).then((response) => {
|
||||||
|
nextTick(() => {
|
||||||
|
studentForm.address = response.data.address
|
||||||
|
studentForm.name = response.data.name
|
||||||
|
studentForm.birthday = response.data.birthday
|
||||||
|
studentForm.gender = response.data.gender
|
||||||
|
studentForm.parentname = response.data.parentname
|
||||||
|
studentForm.parentmobile = response.data.parentmobile
|
||||||
|
studentForm.id = response.data.id
|
||||||
|
studentVisible.value = true
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//删除学生
|
||||||
|
const delStudent = (index) => {
|
||||||
|
//index 1 移出班级 2 删除学生
|
||||||
|
if(index === 1){
|
||||||
|
ElMessageBox.alert('是否将该学生移出班级', {
|
||||||
|
confirmButtonText: '确认',
|
||||||
|
type: 'warning',
|
||||||
|
}).then(() => {
|
||||||
|
leaveClass({ classid: props.classId, studentid: studentForm.id, inrole: 'student' }).then((response) => {
|
||||||
|
ElMessage({
|
||||||
|
type: 'info',
|
||||||
|
message: '移出成功',
|
||||||
|
})
|
||||||
|
getClassStudentInfo()
|
||||||
|
studentVisible.value = false
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
ElMessageBox.alert('确认删除该学生并删除该学生所有数据', {
|
||||||
|
confirmButtonText: '确认',
|
||||||
|
type: 'warning',
|
||||||
|
}).then(() => {
|
||||||
|
leaveClass({classid: props.classId, studentid: studentForm.id, inrole: 'student'}).then((response) => {
|
||||||
|
if (response.code === 200) {
|
||||||
|
ElMessage({
|
||||||
|
type: 'info',
|
||||||
|
message: '删除成功',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
removeStudentDataAll(studentForm.id).then((res) => {
|
||||||
|
if (res.code === 200) {
|
||||||
|
getClassStudentInfo()
|
||||||
|
studentVisible.value = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//导入学生
|
||||||
|
const importStudent = () => {
|
||||||
|
importVisiable.value = true
|
||||||
|
}
|
||||||
|
//下载导入模板
|
||||||
|
const downloadTemplate = (tableInfo) => {
|
||||||
|
let expData = [];
|
||||||
|
tableInfo.importColumns = appendTableImportColumns(tableInfo.columns);
|
||||||
|
var header = [];
|
||||||
|
tableInfo.importColumns.map((item, index) => {
|
||||||
|
header[index] = item.title;
|
||||||
|
});
|
||||||
|
//1. 新建一个工作簿
|
||||||
|
let workbook = XLSX.utils.book_new();
|
||||||
|
//2.2 把json对象转成工作表
|
||||||
|
let sheet1 = XLSX.utils.json_to_sheet(expData, { header: header });
|
||||||
|
//3.在工作簿中添加工作表
|
||||||
|
XLSX.utils.book_append_sheet(workbook, sheet1, '导入模板'); //工作簿名称
|
||||||
|
XLSX.writeFile(workbook, '学生名单导入模板.xlsx'); // 保存的文件名
|
||||||
|
}
|
||||||
|
//获取导入表格的列
|
||||||
|
const appendTableImportColumns = (columns) => {
|
||||||
|
return columns.filter((item) => {
|
||||||
|
if (item.hasOwnProperty("title")) {
|
||||||
|
if (item.hasOwnProperty("key")) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
if (item.hasOwnProperty("slot") && item.slot.split(".").length > 1) {
|
||||||
|
item.title = item.slot.split(".")[1];
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
//文件上传
|
||||||
|
const handleStudentFileUploadChange = (file) => {
|
||||||
|
let fileTemp = file.raw;
|
||||||
|
if (fileTemp) {
|
||||||
|
if ((fileTemp.type == 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
|
||||||
|
|| (fileTemp.type == 'application/vnd.ms-excel')) {
|
||||||
|
handleStudentExcel(fileTemp);
|
||||||
|
} else {
|
||||||
|
ElMessage({
|
||||||
|
type: 'warning',
|
||||||
|
message: '文件格式错误,请删除后重新上传!'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ElMessage({
|
||||||
|
type: 'warning',
|
||||||
|
message: '请上文件!'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const handleStudentExcel = (fileTemp) => {
|
||||||
|
const file = fileTemp;
|
||||||
|
var rABS = false; //是否将文件读取为二进制字符串
|
||||||
|
var f = file;
|
||||||
|
var reader = new FileReader();
|
||||||
|
FileReader.prototype.readAsBinaryString = function (f) {
|
||||||
|
var binary = "";
|
||||||
|
var rABS = false; //是否将文件读取为二进制字符串
|
||||||
|
var wb; //读取完成的数据
|
||||||
|
var outdata;
|
||||||
|
var reader = new FileReader();
|
||||||
|
reader.onload = function () {
|
||||||
|
var bytes = new Uint8Array(reader.result);
|
||||||
|
var length = bytes.byteLength;
|
||||||
|
for (var i = 0; i < length; i++) {
|
||||||
|
binary += String.fromCharCode(bytes[i]);
|
||||||
|
}
|
||||||
|
if (rABS) {
|
||||||
|
wb = XLSX.read(btoa(fixdata(binary)), {
|
||||||
|
//手动转化
|
||||||
|
type: "base64"
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
wb = XLSX.read(binary, {
|
||||||
|
type: "binary"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
outdata = XLSX.utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]]); //outdata就是你想要的东西
|
||||||
|
const tempColumnMapping = {};
|
||||||
|
for (const column of tableInfo.columns) {
|
||||||
|
tempColumnMapping[column.title] = column.key;
|
||||||
|
}
|
||||||
|
const mappedOutdata = outdata.map((item) => {
|
||||||
|
return Object.keys(item).reduce((mappedItem, key) => {
|
||||||
|
const targetKey = tempColumnMapping[key] || key;
|
||||||
|
mappedItem[targetKey] = item[key];
|
||||||
|
return mappedItem;
|
||||||
|
}, {});
|
||||||
|
});
|
||||||
|
tableInfo.studentList = [...mappedOutdata];
|
||||||
|
|
||||||
|
//此处可对数据进行处理
|
||||||
|
let arr = [];
|
||||||
|
outdata.map(v => {
|
||||||
|
let obj = {}
|
||||||
|
obj.code = v['code']
|
||||||
|
obj.name = v['name']
|
||||||
|
obj.pro = v['province']
|
||||||
|
obj.cit = v['city']
|
||||||
|
obj.dis = v['district']
|
||||||
|
arr.push(obj)
|
||||||
|
});
|
||||||
|
// _this.da = arr;
|
||||||
|
// _this.dalen = arr.length;
|
||||||
|
return arr;
|
||||||
|
};
|
||||||
|
reader.readAsArrayBuffer(f);
|
||||||
|
};
|
||||||
|
if (rABS) {
|
||||||
|
reader.readAsArrayBuffer(f);
|
||||||
|
} else {
|
||||||
|
reader.readAsBinaryString(f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//点击导入确认
|
||||||
|
const HandleImportStudentData = () => {
|
||||||
|
if(tableInfo.studentList.length == 0){
|
||||||
|
ElMessage({
|
||||||
|
type: 'warning',
|
||||||
|
message: '请选择导入的文件!'
|
||||||
|
})
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
var names = [];
|
||||||
|
tableInfo.studentList.forEach(item => {
|
||||||
|
names.push(item.name);
|
||||||
|
})
|
||||||
|
var formdata = {};
|
||||||
|
formdata.namearray = names.join(",");
|
||||||
|
formdata.entpid = classInfo.value.entpid;
|
||||||
|
formdata.classid = classInfo.value.id;
|
||||||
|
formdata.classname = classInfo.value.caption;
|
||||||
|
formdata.schoolname = classInfo.value.entpname;
|
||||||
|
formdata.status = '';
|
||||||
|
formdata.editoruserid = userStore.id;
|
||||||
|
|
||||||
|
addStudentmainByNameArray(formdata).then(res => {
|
||||||
|
if(res.code == 200){
|
||||||
|
ElMessage({
|
||||||
|
type: 'success',
|
||||||
|
message: '导入成功',
|
||||||
|
})
|
||||||
|
getClassStudentInfo()
|
||||||
|
importVisiable.value = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const handleRemoveImportStudent = (index) => {
|
||||||
|
tableInfo.studentList.splice(index, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
studentList.value = []
|
||||||
|
getClassStudentInfo()
|
||||||
|
getClassInfo()
|
||||||
|
})
|
||||||
|
watch(()=> props.classId,()=> {
|
||||||
|
getClassStudentInfo()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.studentContent{
|
||||||
|
display: flex;
|
||||||
|
justify-content: start;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
</style>
|
Loading…
Reference in New Issue