Untitled
unknown
plain_text
a year ago
19 kB
4
Indexable
import React from 'react';
import { AdminPage, renderDataTable, TableCell, FormTextBox } from 'view/component/AdminPage';
import { getSdhConfigDiemByHocPhan } from 'modules/mdSauDaiHoc/sdhConfigHocPhan/redux';
import { connect } from 'react-redux';
import { Tooltip } from '@mui/material';
import EditCauHinhHocPhan from 'modules/mdSauDaiHoc/sdhConfigHocPhan/section/edit-cau-hinh-hoc-phan';
import { getSdhConfigHocPhanDssv, storeDiemHocVienByHocPhan } from 'modules/mdSauDaiHoc/sdhConfigHocPhan/redux';
class SectionHocPhanSinhVien extends AdminPage {
defaultSortTerm = 'mssv_ASC'
diemTk = {}
ghiChu = {}
thanhPhanDiem = {}
state = { filter: {}, dataConfigMonHoc: [], dataConfigHocPhan: [], dataConfigQuyChe: [], listStudent: [], maMonHoc: '', maHocPhan: '' }
setData = (data) => {
let { maMonHoc, maHocPhan } = data;
this.props.getSdhConfigDiemByHocPhan({ maMonHoc, maHocPhan }, dataState => {
let { dataConfigHocPhan = [], dataConfigMonHoc = [] } = dataState;
this.props.getSdhConfigHocPhanDssv(this.props.maHocPhan, '', listStudent => this.setState({ listStudent, dataConfigHocPhan, dataConfigMonHoc, maMonHoc, maHocPhan }, () => {
listStudent.forEach(item => {
this.diemTk[item.mssv]?.value(item.tongKet);
});
}));
});
}
handleDiem = (e, student, thanhPhan) => {
let { maLoaiDiem } = thanhPhan,
{ mssv } = student,
diem = student.diemThanhPhan.find(e => e.maLoaiDiem == maLoaiDiem)?.diem,
{ dataConfigHocPhan } = this.state,
value = e.target.innerText?.toUpperCase();
if (parseFloat(value) < 0 || parseFloat(value) > 10) {
e.target.innerText = diem ? diem : '';
T.notify('Điểm phải nằm trong khoảng 0 - 10!', 'warning');
return;
} else if (parseFloat(value) >= 0 || parseFloat(value) <= 10 || value == '') {
this.state.listStudent.map(item => {
if (item.mssv == mssv) {
item.diemThanhPhan.map(i => {
if (i.maLoaiDiem == maLoaiDiem) {
i.diemNew = value;
i.phanTram = dataConfigHocPhan.find(e => e.maLoaiDiem == maLoaiDiem).phanTram;
}
});
}
});
}
// Update diem tong ket
let sum = '';
for (const tp of dataConfigHocPhan) {
const objectDiemTp = student.diemThanhPhan.find(e => e.maLoaiDiem == tp.maLoaiDiem);
const diemTp = objectDiemTp.diemNew ?? objectDiemTp.diem;
if (diemTp && isNaN(diemTp)) {
sum = diemTp;
break;
} else if (diemTp != '' && diemTp != null) {
if (sum == '') sum = parseFloat(diemTp) * parseInt(tp.phanTram ?? 0);
else sum += parseFloat(diemTp) * parseInt(tp.phanTram);
}
}
if (!isNaN(parseFloat(sum))) {
sum = (Math.round((sum / 100) * 10) / 10).toFixed(1);
}
this.diemTk[mssv].value(sum.toString());
}
handlePressDiem = (e) => {
let key = e.key,
value = e.target.textContent;
// Only allow numeric values, decimal point, and backspace/delete keys
const allowKeys = ['Backspace', 'Delete', 'Enter', 'ArrowUp', 'ArrowDown', 'ArrowRight', 'ArrowLeft', 'Tab'];
if (!allowKeys.includes(key)) {
if (/[0-9\.]/.test(key)) {
// Only allow one decimal point
if (key === '.' && value.includes('.')) {
e.preventDefault();
return;
}
} else if (/[0-9',']/.test(key)) {
// Prevent commas
e.preventDefault();
return;
}
}
if (key !== 'Backspace' && key !== 'Delete') {
value += key;
}
if (key == 'ArrowDown') {
const currentTd = e.target.parentElement;
const nextTd = currentTd.parentNode.nextElementSibling?.querySelector(`td:nth-child(${currentTd.cellIndex + 1})`);
if (nextTd) {
currentTd.children[0].blur();
nextTd.children[0].focus();
}
}
if (key == 'ArrowUp') {
const currentTd = e.target.parentElement;
const prevTd = currentTd.parentNode.previousElementSibling?.querySelector(`td:nth-child(${currentTd.cellIndex + 1})`);
if (prevTd) {
currentTd.children[0].blur();
prevTd.children[0].focus();
}
}
if (key == 'ArrowRight' || key === 'Enter' || key == 'NumpadEnter' || key === 'Tab') {
e.preventDefault();
const currentTd = e.target.parentElement;
const nextTd = currentTd.nextElementSibling;
currentTd.children[0].blur();
if (nextTd && nextTd.children[0].id != '') {
nextTd.children[0].focus();
} else {
const nextTr = currentTd.parentElement.nextElementSibling;
if (nextTr) {
const nextTd = nextTr.querySelector('td:nth-child(6)');
if (nextTd) {
currentTd.children[0].blur();
nextTd.children[0].focus();
}
}
}
}
if (key == 'ArrowLeft') {
const currentTd = e.target.parentElement;
const prevTd = currentTd.previousElementSibling;
if (prevTd && prevTd.children[0].id != '') {
currentTd.children[0].blur();
prevTd.children[0].focus();
} else {
const prevTr = currentTd.parentElement.previousElementSibling;
if (prevTr) {
const prevTd = prevTr.querySelector(`td:nth-child(${6 + this.state.dataConfigHocPhan.length - 1})`);
if (prevTd) {
currentTd.children[0].blur();
prevTd.children[0].focus();
}
}
}
}
}
handleHuyNhapDiem = (e) => {
e.preventDefault();
let { listStudent } = this.state;
listStudent.forEach(item => {
this.diemTk[item.mssv].value(item.tongKet);
});
this.setState({ isNhapDiem: false });
}
handleGhiChu = (value, mssv) => {
this.ghiChu[mssv].value(value);
}
save = () => {
const dataStudent = [];
const { listStudent } = this.state;
listStudent.map(item => {
item.diemThanhPhan.map(i => {
const data = {
oldId: i.id,
mssv: item.mssv,
loaiDiem: i.maLoaiDiem,
diemNew: i.diemNew,
phamTramDiem: i.phanTram,
diem: i.diem
};
if (i.diemNew && i.diemNew != i.diem) dataStudent.push(data);
});
const diemTongKet = this.diemTk[item.mssv]?.value() ?? '';
item.tongKet != diemTongKet && dataStudent.push({
oldId: item.idDiemTongKet,
mssv: item.mssv,
loaiDiem: 'TK',
diem: item.tongKet,
diemNew: diemTongKet
});
});
const dataHocPhan = {
maHocPhan: this.state.maHocPhan,
maMonHoc: this.state.maMonHoc,
namHoc: this.props.namHoc,
hocKy: this.props.hocKy
};
T.confirm('Cảnh báo', 'Bạn có chắc chắn muốn Cập nhật điểm của lớp học phần này không?', 'warning', true, isConfirm => {
if (isConfirm) {
if (dataStudent.length == 0) {
T.alert('Cập nhập điểm thành công!', 'success', false, 500);
this.setState({ isNhapDiem: false });
this.props.resetPage();
}
else {
T.alert('Đang cập nhật điểm sinh viên!', 'warning', false, null, true);
this.props.storeDiemHocVienByHocPhan(dataHocPhan, dataStudent, () => {
T.alert('Cập nhập điểm thành công!', 'success', false, 500);
this.setState({ isNhapDiem: false });
this.props.resetPage();
});
}
}
});
}
render() {
let { listStudent, dataConfigHocPhan, dataConfigMonHoc, dataConfigQuyChe, isNhapDiem, readOnly, maMonHoc, maHocPhan } = this.state;
const { namHoc, hocKy } = this.props;
let widthTP = 40 / (dataConfigHocPhan.length + 1);
const { isManage } = this.props,
className = isNhapDiem ? 'btn btn-success' : 'btn btn-primary',
icon = isNhapDiem ? 'fa-save' : 'fa-pencil',
textButton = isNhapDiem ? 'Lưu' : 'Nhập điểm',
viewing = this.props.viewing;
const permission = this.getUserPermission('sdhConfigHocPhan', ['read', 'write', 'delete']);
const conditionCauHinhDiemHp = dataConfigHocPhan.length;
// && !dataConfigHocPhan.find(item => !item.phanTram);
let studentTable = renderDataTable({
emptyTable: 'Không có dữ liệu học viên',
stickyHead: false,
header: 'thead-light',
loadingStyle: { backgroundColor: 'white' },
data: listStudent,
renderHead: () => {
return (<>
<tr>
<th style={{ width: 'auto', verticalAlign: 'middle' }}>#</th>
<th style={{ width: '10%', verticalAlign: 'middle' }}>MSSV</th>
<th style={{ width: '20%', verticalAlign: 'middle' }}>Họ và tên lót</th>
<th style={{ width: '10%', verticalAlign: 'middle' }}>Tên</th>
<th style={{ width: '20%', verticalAlign: 'middle' }}>Lớp</th>
{dataConfigHocPhan.map((item, index) => (<th key={index} style={{ width: `${widthTP}%`, whiteSpace: 'nowrap', textAlign: 'center' }}>{item.tenLoaiDiem}</th>))}
<th style={{ width: '30%', whiteSpace: 'nowrap', textAlign: 'center' }}>Điểm tổng kết</th>
{/* <th style={{ width: '50%', whiteSpace: 'nowrap', textAlign: 'center' }}>Ghi chú</th> */}
</tr>
</>
);
},
renderRow: (item, index) => {
return (
<tr key={item.mssv}>
<TableCell content={index + 1} />
<TableCell content={item.mssv} />
<TableCell content={item.ho} />
<TableCell content={item.ten} />
<TableCell style={{ whiteSpace: 'nowrap' }} content={item.lop} />
{listStudent.length && dataConfigHocPhan.map(thanhPhan => {
let loaiThanhPhan = thanhPhan.maLoaiDiem,
timeMod = item.diemThanhPhan.find(e => e.maLoaiDiem == loaiThanhPhan)?.timeModified,
userMod = item.diemThanhPhan.find(e => e.maLoaiDiem == loaiThanhPhan)?.userModified,
title = userMod ? `${T.dateToText(Number(timeMod), 'dd/mm/yyyy HH:MM:ss')} - ${userMod}` : '',
diem = item.diemThanhPhan.find(e => e.maLoaiDiem == loaiThanhPhan)?.diem ?? '',
loaiLamTron = item.diemThanhPhan.find(e => e.maLoaiDiem == loaiThanhPhan)?.loaiLamTron ?? 0;
const rate = parseFloat(loaiLamTron) / 0.1;
let diemLamTron;
if (!rate) {
diemLamTron = diem ?? '';
} else {
diemLamTron = diem ? Math.round(diem * (10 / rate)) / (10 / rate).toFixed(1).toString() : '';
}
return isNhapDiem ? <Tooltip arrow placement='top' title={title}>
<td>
<div key={`${item.mssv}_${loaiThanhPhan}`} id={`${item.mssv}_${loaiThanhPhan}`}
contentEditable suppressContentEditableWarning={true}
onBlur={e => this.handleDiem(e, item, thanhPhan)}
onKeyDown={e => this.handlePressDiem(e, thanhPhan)}
style={{ whiteSpace: 'nowrap', verticalAlign: 'middle', textAlign: 'center', cursor: 'auto', border: '1.5px solid #ced4da', borderCollapse: 'collapse', borderRadius: '4px', fontSize: 'large' }}
>
{diem}
</div>
</td>
</Tooltip> : <Tooltip arrow placement='top' title={title}>
<td><div style={{ whiteSpace: 'nowrap', textAlign: 'center' }}>{diemLamTron}</div></td>
</Tooltip>;
})}
<TableCell key={item.mssv} style={{ width: 'auto', textAlign: 'center', justifyContent: 'center' }} content={
<FormTextBox className='mb-0' ref={e => this.diemTk[item.mssv] = e} readOnly />
} />
{/* <TableCell style={{ width: 'auto', justifyContent: 'center' }} content={<FormTextBox className='mb-0' placeholder='Ghi chú' ref={e => this.ghiChu[item.mssv] = e} readOnly={readOnly || !isNhapDiem} onBlur={(e) => this.handleGhiChu(e, item.mssv)} />} /> */}
</tr>
);
}
});
return (<>
<div style={{ textAlign: 'left', margin: '10px 24px 10px 24px' }}>
<hr />
<div className='row' style={{ fontSize: '0.8rem' }}>
<div className='col-md-6' style={{ display: dataConfigQuyChe.length ? '' : 'none' }}>
<h6>Thông tin các điểm đặc biệt: </h6>
{<div className='d-flex flex-wrap' style={{ padding: 'inherit' }}>
{dataConfigQuyChe.map((item, index) => {
return <span key={`qc-${index}`} style={{ marginRight: 'auto', whiteSpace: 'nowrap' }}>- <b>{item.ma}</b>: {item.moTa} </span>;
})}
</div>}
</div>
<div className='col-md-6' style={{ display: dataConfigHocPhan.length ? '' : 'none' }}>
<h6>Thông tin các điểm thành phần: </h6>
{<div className='d-flex flex-wrap' style={{ padding: 'inherit' }}>
{dataConfigHocPhan.map((item, index) => {
return <span key={`tp-${index}`} style={{ marginRight: 'auto', whiteSpace: 'nowrap' }}>- <b>{item.tenLoaiDiem} ({item.maLoaiDiem})</b>: {item.phanTram}% (từ {item.phanTramMin}% - {item.phanTramMax}%) </span>;
})}
</div>}
</div>
</div>
<div className='row'>
<div className='col-md-12' style={{ display: 'flex', flexDirection: 'row-reverse', gap: 10, marginTop: '10px' }}>
<React.Fragment>
<Tooltip title='Cấu hình phần trăm điểm' arrow placement='top'>
<button className='btn btn-success' type='button' onClick={() => {
this.modal.show({ dataConfigHocPhan, dataConfigMonHoc });
}}>
<i className='fa fa-fw fa-lg fa-cogs' /> Cấu hình điểm
</button>
</Tooltip>
<Tooltip title='Huỷ' placement='top' arrow>
<button className='btn btn-secondary' style={{ display: isNhapDiem ? '' : 'none', height: 'fit-content' }}
onClick={e => this.handleHuyNhapDiem(e)}>
<i className='fa fa-lg fa-times' /> Hủy
</button>
</Tooltip>
<Tooltip title={textButton} placement='top' arrow>
<button className={className} style={{ height: 'fit-content', display: !dataConfigHocPhan.length || !permission.write || viewing ? 'none' : '' }} onClick={e => {
e.preventDefault();
if (isNhapDiem) this.save();
else {
if (!isManage && readOnly) {
T.alert('Không trong thời gian nhập điểm', 'warning', false, 5000);
}
else {
this.setState({ isNhapDiem: true });
}
}
}}>
<i className={'fa fa-lg ' + icon} /> {isNhapDiem ? 'Lưu' : 'Nhập điểm'}
</button>
</Tooltip>
</React.Fragment>
</div>
</div>
</div>
<div className='m-4'>
{conditionCauHinhDiemHp ? studentTable : ''}
</div>
<EditCauHinhHocPhan resetPage={this.props.resetPage} ref={e => this.modal = e} dataConfigHocPhan={dataConfigHocPhan} maMonHoc={maMonHoc} maHocPhan={maHocPhan} namHoc={namHoc} hocKy={hocKy} setData={this.setData} />
</>);
}
}
const mapStateToProps = state => ({ system: state.system });
const mapActionsToProps = {
getSdhConfigDiemByHocPhan, getSdhConfigHocPhanDssv, storeDiemHocVienByHocPhan
};
export default connect(mapStateToProps, mapActionsToProps, null, { forwardRef: true })(SectionHocPhanSinhVien);
Editor is loading...
Leave a Comment