v0.1测试
This commit is contained in:
130
frontend/assets/css/admin.css
Normal file
130
frontend/assets/css/admin.css
Normal file
@@ -0,0 +1,130 @@
|
||||
/**
|
||||
* 班级操行分管理系统 - 管理端样式
|
||||
*
|
||||
* 开发者: Canglan
|
||||
* 联系方式: admin@sea-studio.top
|
||||
* 版权归属: Sea Network Technology Studio
|
||||
* 许可证: MIT License
|
||||
*
|
||||
* 版权所有 © Sea Network Technology Studio
|
||||
*/
|
||||
|
||||
/* 批量操作栏 */
|
||||
.batch-bar {
|
||||
background: #f0f4ff;
|
||||
padding: 12px 16px;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 16px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.batch-info {
|
||||
color: #667eea;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* 导入区域 */
|
||||
.import-area {
|
||||
border: 2px dashed #ddd;
|
||||
border-radius: 12px;
|
||||
padding: 30px;
|
||||
text-align: center;
|
||||
margin-bottom: 20px;
|
||||
transition: border-color 0.3s;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.import-area:hover {
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
.import-area input {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.import-label {
|
||||
color: #667eea;
|
||||
text-decoration: underline;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* 预览表格 */
|
||||
.preview-table {
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
/* 筛选栏 */
|
||||
.filter-bar {
|
||||
background: #f8f9fa;
|
||||
padding: 16px;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 20px;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 16px;
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
.filter-group {
|
||||
flex: 1;
|
||||
min-width: 150px;
|
||||
}
|
||||
|
||||
.filter-group label {
|
||||
display: block;
|
||||
margin-bottom: 4px;
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.filter-group input,
|
||||
.filter-group select {
|
||||
width: 100%;
|
||||
padding: 8px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
/* 作业卡片 */
|
||||
.assignment-card {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.assignment-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 16px;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.assignment-title {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.assignment-meta {
|
||||
color: #999;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
/* 状态选择器 */
|
||||
.status-select {
|
||||
padding: 4px 8px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
/* 复选框 */
|
||||
.student-checkbox {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
cursor: pointer;
|
||||
}
|
||||
666
frontend/assets/css/style.css
Normal file
666
frontend/assets/css/style.css
Normal file
@@ -0,0 +1,666 @@
|
||||
/**
|
||||
* 班级操行分管理系统 - 全局样式
|
||||
*
|
||||
* 开发者: Canglan
|
||||
* 联系方式: admin@sea-studio.top
|
||||
* 版权归属: Sea Network Technology Studio
|
||||
* 许可证: MIT License
|
||||
*
|
||||
* 版权所有 © Sea Network Technology Studio
|
||||
*/
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
||||
background: #f5f7fb;
|
||||
min-height: 100vh;
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
/* ========== 登录页面 ========== */
|
||||
.login-container {
|
||||
background: white;
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
|
||||
padding: 40px;
|
||||
width: 400px;
|
||||
max-width: 90%;
|
||||
margin: 100px auto;
|
||||
}
|
||||
|
||||
.login-header {
|
||||
text-align: center;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.login-header h1 {
|
||||
font-size: 24px;
|
||||
color: #333;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.login-header p {
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.login-form .form-group {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.login-form label {
|
||||
display: block;
|
||||
margin-bottom: 6px;
|
||||
color: #555;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.login-form input {
|
||||
width: 100%;
|
||||
padding: 12px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
transition: border-color 0.3s;
|
||||
}
|
||||
|
||||
.login-form input:focus {
|
||||
outline: none;
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
.btn-login {
|
||||
width: 100%;
|
||||
padding: 12px;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
transition: opacity 0.3s;
|
||||
}
|
||||
|
||||
.btn-login:hover {
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.error-msg {
|
||||
background: #fee;
|
||||
color: #c33;
|
||||
padding: 10px;
|
||||
border-radius: 8px;
|
||||
margin-top: 15px;
|
||||
text-align: center;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.login-footer {
|
||||
text-align: center;
|
||||
margin-top: 30px;
|
||||
padding-top: 20px;
|
||||
border-top: 1px solid #eee;
|
||||
color: #999;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
/* ========== 公共头部 ========== */
|
||||
.header {
|
||||
background: white;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
||||
padding: 12px 24px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
font-size: 18px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.header-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.user-name {
|
||||
color: #666;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.user-role {
|
||||
background: #667eea;
|
||||
color: white;
|
||||
padding: 2px 8px;
|
||||
border-radius: 20px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.btn-logout {
|
||||
background: #e53e3e;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 6px 16px;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
font-size: 13px;
|
||||
transition: background 0.3s;
|
||||
}
|
||||
|
||||
.btn-logout:hover {
|
||||
background: #c53030;
|
||||
}
|
||||
|
||||
/* ========== 导航菜单 ========== */
|
||||
.nav {
|
||||
background: white;
|
||||
padding: 0 24px;
|
||||
border-bottom: 1px solid #eee;
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.nav-item {
|
||||
padding: 12px 20px;
|
||||
background: none;
|
||||
border: none;
|
||||
color: #666;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
transition: all 0.3s;
|
||||
border-bottom: 2px solid transparent;
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.nav-item:hover {
|
||||
color: #667eea;
|
||||
}
|
||||
|
||||
.nav-item.active {
|
||||
color: #667eea;
|
||||
border-bottom-color: #667eea;
|
||||
}
|
||||
|
||||
/* ========== 容器 ========== */
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 24px auto;
|
||||
padding: 0 24px;
|
||||
}
|
||||
|
||||
/* ========== 卡片 ========== */
|
||||
.card {
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
padding: 20px;
|
||||
margin-bottom: 20px;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.card-title {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 16px;
|
||||
padding-bottom: 12px;
|
||||
border-bottom: 2px solid #667eea;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
/* ========== 统计卡片网格 ========== */
|
||||
.stats-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
|
||||
gap: 16px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.stat-card {
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
font-size: 32px;
|
||||
font-weight: bold;
|
||||
color: #667eea;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
color: #666;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
/* ========== 表格 ========== */
|
||||
.table-wrapper {
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
th, td {
|
||||
padding: 12px;
|
||||
text-align: left;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
th {
|
||||
background: #f8f9fa;
|
||||
font-weight: 600;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
tr:hover {
|
||||
background: #f8f9fa;
|
||||
}
|
||||
|
||||
/* ========== 状态标签 ========== */
|
||||
.status-badge {
|
||||
display: inline-block;
|
||||
padding: 4px 10px;
|
||||
border-radius: 20px;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.status-submitted {
|
||||
background: #c6f6d5;
|
||||
color: #22543d;
|
||||
}
|
||||
|
||||
.status-not_submitted {
|
||||
background: #fed7d7;
|
||||
color: #742a2a;
|
||||
}
|
||||
|
||||
.status-late {
|
||||
background: #feebc8;
|
||||
color: #7c2d12;
|
||||
}
|
||||
|
||||
.status-present {
|
||||
background: #c6f6d5;
|
||||
color: #22543d;
|
||||
}
|
||||
|
||||
.status-absent {
|
||||
background: #fed7d7;
|
||||
color: #742a2a;
|
||||
}
|
||||
|
||||
.status-leave {
|
||||
background: #e9d8fd;
|
||||
color: #553c9a;
|
||||
}
|
||||
|
||||
/* ========== 按钮 ========== */
|
||||
.btn {
|
||||
padding: 8px 16px;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
font-size: 13px;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: #667eea;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background: #5a67d8;
|
||||
}
|
||||
|
||||
.btn-danger {
|
||||
background: #e53e3e;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-danger:hover {
|
||||
background: #c53030;
|
||||
}
|
||||
|
||||
.btn-success {
|
||||
background: #38a169;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-success:hover {
|
||||
background: #2f855a;
|
||||
}
|
||||
|
||||
.btn-sm {
|
||||
padding: 4px 10px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
/* ========== 模态框 ========== */
|
||||
.modal {
|
||||
display: none;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
padding: 24px;
|
||||
width: 500px;
|
||||
max-width: 90%;
|
||||
max-height: 80vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.modal-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
padding-bottom: 12px;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
.modal-header h3 {
|
||||
font-size: 18px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.modal-close {
|
||||
background: none;
|
||||
border: none;
|
||||
font-size: 24px;
|
||||
cursor: pointer;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.modal-footer {
|
||||
margin-top: 20px;
|
||||
padding-top: 16px;
|
||||
border-top: 1px solid #eee;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
/* ========== 表单 ========== */
|
||||
.form-group {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.form-group label {
|
||||
display: block;
|
||||
margin-bottom: 6px;
|
||||
font-weight: 500;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.form-group input,
|
||||
.form-group select,
|
||||
.form-group textarea {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 6px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.form-group input:focus,
|
||||
.form-group select:focus,
|
||||
.form-group textarea:focus {
|
||||
outline: none;
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
.form-group small {
|
||||
display: block;
|
||||
color: #999;
|
||||
font-size: 12px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
/* ========== 复选框组 ========== */
|
||||
.checkbox-group {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.checkbox-group input {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
/* ========== 操作栏 ========== */
|
||||
.action-bar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
flex-wrap: wrap;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.search-bar {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.search-bar input {
|
||||
padding: 8px 12px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 6px;
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
/* ========== 分页 ========== */
|
||||
.pagination {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.pagination a, .pagination span {
|
||||
padding: 6px 12px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
text-decoration: none;
|
||||
color: #666;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.pagination .active {
|
||||
background: #667eea;
|
||||
color: white;
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* ========== 提示消息 ========== */
|
||||
.toast {
|
||||
position: fixed;
|
||||
bottom: 20px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
padding: 12px 24px;
|
||||
border-radius: 8px;
|
||||
color: white;
|
||||
font-size: 14px;
|
||||
z-index: 1100;
|
||||
animation: fadeInUp 0.3s ease;
|
||||
}
|
||||
|
||||
.toast-success {
|
||||
background: #38a169;
|
||||
}
|
||||
|
||||
.toast-error {
|
||||
background: #e53e3e;
|
||||
}
|
||||
|
||||
.toast-warning {
|
||||
background: #ed8936;
|
||||
}
|
||||
|
||||
@keyframes fadeInUp {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateX(-50%) translateY(20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateX(-50%) translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* ========== 加载动画 ========== */
|
||||
.loading {
|
||||
display: inline-block;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border: 2px solid #ddd;
|
||||
border-top-color: #667eea;
|
||||
border-radius: 50%;
|
||||
animation: spin 0.8s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
/* ========== 底部 ========== */
|
||||
.footer {
|
||||
text-align: center;
|
||||
padding: 20px;
|
||||
color: #999;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
/* ========== 记录项 ========== */
|
||||
.record-item {
|
||||
padding: 12px 0;
|
||||
border-bottom: 1px solid #eee;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.record-points {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.record-points.plus {
|
||||
color: #38a169;
|
||||
}
|
||||
|
||||
.record-points.minus {
|
||||
color: #e53e3e;
|
||||
}
|
||||
|
||||
.record-reason {
|
||||
flex: 1;
|
||||
margin: 0 15px;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.record-time {
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.view-more {
|
||||
text-align: center;
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.view-more a {
|
||||
color: #667eea;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.conduct-score {
|
||||
text-align: center;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.score-number {
|
||||
font-size: 64px;
|
||||
font-weight: bold;
|
||||
color: #667eea;
|
||||
}
|
||||
|
||||
/* ========== 响应式 ========== */
|
||||
@media (max-width: 768px) {
|
||||
.container {
|
||||
padding: 0 16px;
|
||||
}
|
||||
|
||||
.stats-grid {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
th, td {
|
||||
padding: 8px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.card {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.nav {
|
||||
padding: 0 16px;
|
||||
}
|
||||
|
||||
.nav-item {
|
||||
padding: 10px 14px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.action-bar {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.search-bar {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.search-bar input {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
385
frontend/assets/js/admin.js
Normal file
385
frontend/assets/js/admin.js
Normal file
@@ -0,0 +1,385 @@
|
||||
/**
|
||||
* 班级操行分管理系统 - 管理端JS
|
||||
*
|
||||
* 开发者: Canglan
|
||||
* 联系方式: admin@sea-studio.top
|
||||
* 版权归属: Sea Network Technology Studio
|
||||
* 许可证: MIT License
|
||||
*
|
||||
* 版权所有 © Sea Network Technology Studio
|
||||
*/
|
||||
|
||||
// 全局变量
|
||||
let selectedStudentIds = [];
|
||||
let currentPage = 1;
|
||||
let totalPages = 1;
|
||||
let currentHistoryPage = 1;
|
||||
|
||||
// 显示批量加减分模态框
|
||||
function showBatchPointsModal() {
|
||||
selectedStudentIds = [];
|
||||
document.querySelectorAll('.student-checkbox:checked').forEach(cb => {
|
||||
selectedStudentIds.push(parseInt(cb.dataset.id));
|
||||
});
|
||||
|
||||
if (selectedStudentIds.length === 0) {
|
||||
showToast('请先选择学生', 'warning');
|
||||
return;
|
||||
}
|
||||
|
||||
document.getElementById('selectedStudentsCount').innerHTML = `${selectedStudentIds.length} 人`;
|
||||
document.getElementById('pointsChange').value = '';
|
||||
document.getElementById('pointsReason').value = '';
|
||||
document.getElementById('batchPointsModal').style.display = 'flex';
|
||||
}
|
||||
|
||||
// 提交批量加减分
|
||||
async function submitBatchPoints() {
|
||||
const pointsChange = parseInt(document.getElementById('pointsChange').value);
|
||||
const reason = document.getElementById('pointsReason').value;
|
||||
|
||||
if (isNaN(pointsChange) || pointsChange === 0) {
|
||||
showToast('分值不能为0', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!reason.trim()) {
|
||||
showToast('请填写原因', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
const res = await apiPost('/api/admin/conduct/add', {
|
||||
student_ids: selectedStudentIds,
|
||||
points_change: pointsChange,
|
||||
reason: reason
|
||||
});
|
||||
|
||||
if (res && res.success) {
|
||||
showToast(`操作成功: ${res.data.success_count} 人成功`);
|
||||
closeModal('batchPointsModal');
|
||||
loadStudents();
|
||||
if (typeof loadConductStudents === 'function') loadConductStudents();
|
||||
} else {
|
||||
showToast(res?.message || '操作失败', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
// 显示导入模态框
|
||||
function showImportModal() {
|
||||
document.getElementById('importModal').style.display = 'flex';
|
||||
document.getElementById('importPreview').style.display = 'none';
|
||||
document.getElementById('importPreview').innerHTML = '';
|
||||
document.getElementById('importBtn').style.display = 'none';
|
||||
document.getElementById('importFile').value = '';
|
||||
}
|
||||
|
||||
// 预览导入文件
|
||||
function previewImportFile() {
|
||||
const file = document.getElementById('importFile').files[0];
|
||||
if (!file) return;
|
||||
|
||||
const reader = new FileReader();
|
||||
reader.onload = function(e) {
|
||||
try {
|
||||
const data = JSON.parse(e.target.result);
|
||||
const students = data.students || [];
|
||||
|
||||
let html = '<h4>预览数据</h4><div class="table-wrapper"><table><thead><tr>';
|
||||
html += '<th>学号</th><th>姓名</th><th>家长手机号</th><th>初始密码</th>';
|
||||
html += '</tr></thead><tbody>';
|
||||
|
||||
students.forEach(s => {
|
||||
html += `<tr>
|
||||
<td>${escapeHtml(s.student_no || '')}</td>
|
||||
<td>${escapeHtml(s.name || '')}</td>
|
||||
<td>${escapeHtml(s.parent_phone || '')}</td>
|
||||
<td>${escapeHtml(s.password || '123456')}</td>
|
||||
</tr>`;
|
||||
});
|
||||
|
||||
html += `</tbody></table></div><p>共 ${students.length} 条记录,初始操行分默认为60分</p>`;
|
||||
document.getElementById('importPreview').innerHTML = html;
|
||||
document.getElementById('importPreview').style.display = 'block';
|
||||
document.getElementById('importBtn').style.display = 'inline-block';
|
||||
} catch (error) {
|
||||
showToast('JSON格式错误', 'error');
|
||||
}
|
||||
};
|
||||
reader.readAsText(file);
|
||||
}
|
||||
|
||||
// 执行导入
|
||||
async function doImport() {
|
||||
const file = document.getElementById('importFile').files[0];
|
||||
if (!file) {
|
||||
showToast('请选择文件', 'warning');
|
||||
return;
|
||||
}
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
|
||||
const token = getToken();
|
||||
const response = await fetch(`${API_BASE_URL}/api/admin/students/import`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`
|
||||
},
|
||||
body: formData
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result.success) {
|
||||
showToast(result.message);
|
||||
closeModal('importModal');
|
||||
loadStudents();
|
||||
} else {
|
||||
showToast(result.message, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
// 显示新增学生模态框
|
||||
function showAddStudentModal() {
|
||||
document.getElementById('addStudentModal').style.display = 'flex';
|
||||
document.getElementById('addStudentForm').reset();
|
||||
}
|
||||
|
||||
// 提交新增学生
|
||||
async function submitAddStudent() {
|
||||
const studentNo = document.getElementById('studentNo').value.trim();
|
||||
const name = document.getElementById('studentName').value.trim();
|
||||
const parentPhone = document.getElementById('parentPhone').value.trim();
|
||||
|
||||
if (!studentNo || !name) {
|
||||
showToast('请填写学号和姓名', 'warning');
|
||||
return;
|
||||
}
|
||||
|
||||
const res = await apiPost('/api/admin/students', {
|
||||
student_no: studentNo,
|
||||
name: name,
|
||||
parent_phone: parentPhone
|
||||
});
|
||||
|
||||
if (res && res.success) {
|
||||
showToast('学生添加成功');
|
||||
closeModal('addStudentModal');
|
||||
loadStudents();
|
||||
} else {
|
||||
showToast(res?.message || '添加失败', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
// 显示添加作业模态框
|
||||
function showAddAssignmentModal() {
|
||||
document.getElementById('addAssignmentModal').style.display = 'flex';
|
||||
loadSubjectsForSelect();
|
||||
}
|
||||
|
||||
// 加载科目下拉框
|
||||
async function loadSubjectsForSelect() {
|
||||
const res = await apiGet('/api/subject/list');
|
||||
if (res && res.success) {
|
||||
let html = '<option value="">请选择科目</option>';
|
||||
res.data.subjects.forEach(s => {
|
||||
if (s.is_active) {
|
||||
html += `<option value="${s.subject_id}">${s.subject_name}</option>`;
|
||||
}
|
||||
});
|
||||
document.getElementById('assignmentSubjectId').innerHTML = html;
|
||||
}
|
||||
}
|
||||
|
||||
// 提交添加作业
|
||||
async function submitAddAssignment() {
|
||||
const subjectId = document.getElementById('assignmentSubjectId').value;
|
||||
const title = document.getElementById('assignmentTitle').value.trim();
|
||||
const description = document.getElementById('assignmentDescription').value;
|
||||
const deadline = document.getElementById('assignmentDeadline').value;
|
||||
|
||||
if (!subjectId || !title || !deadline) {
|
||||
showToast('请填写完整信息', 'warning');
|
||||
return;
|
||||
}
|
||||
|
||||
const res = await apiPost('/api/admin/homework/assignment', {
|
||||
subject_id: parseInt(subjectId),
|
||||
title: title,
|
||||
description: description,
|
||||
deadline: deadline
|
||||
});
|
||||
|
||||
if (res && res.success) {
|
||||
showToast('作业发布成功');
|
||||
closeModal('addAssignmentModal');
|
||||
loadAssignments();
|
||||
} else {
|
||||
showToast(res?.message || '发布失败', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
// 显示添加考勤模态框
|
||||
async function showAddAttendanceModal() {
|
||||
await loadStudentsForSelect();
|
||||
document.getElementById('addAttendanceModal').style.display = 'flex';
|
||||
document.getElementById('attendanceDate').value = new Date().toISOString().split('T')[0];
|
||||
}
|
||||
|
||||
// 加载学生下拉框
|
||||
async function loadStudentsForSelect() {
|
||||
const res = await apiGet('/api/admin/students');
|
||||
if (res && res.success) {
|
||||
let html = '<option value="">请选择学生</option>';
|
||||
res.data.students.forEach(s => {
|
||||
html += `<option value="${s.student_id}">${s.student_no} - ${s.name}</option>`;
|
||||
});
|
||||
document.getElementById('attendanceStudentId').innerHTML = html;
|
||||
}
|
||||
}
|
||||
|
||||
// 提交添加考勤
|
||||
async function submitAddAttendance() {
|
||||
const studentId = document.getElementById('attendanceStudentId').value;
|
||||
const date = document.getElementById('attendanceDate').value;
|
||||
const status = document.getElementById('attendanceStatus').value;
|
||||
const reason = document.getElementById('attendanceReason').value;
|
||||
const applyDeduction = document.getElementById('attendanceDeduct').checked;
|
||||
|
||||
if (!studentId || !date || !status) {
|
||||
showToast('请填写完整信息', 'warning');
|
||||
return;
|
||||
}
|
||||
|
||||
const res = await apiPost('/api/admin/attendance', {
|
||||
student_id: parseInt(studentId),
|
||||
date: date,
|
||||
status: status,
|
||||
reason: reason,
|
||||
apply_deduction: applyDeduction
|
||||
});
|
||||
|
||||
if (res && res.success) {
|
||||
showToast('考勤记录添加成功');
|
||||
closeModal('addAttendanceModal');
|
||||
loadAttendanceRecords();
|
||||
} else {
|
||||
showToast(res?.message || '添加失败', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
// 显示添加管理员模态框
|
||||
function showAddAdminModal() {
|
||||
document.getElementById('addAdminModal').style.display = 'flex';
|
||||
document.getElementById('addAdminForm').reset();
|
||||
}
|
||||
|
||||
// 提交添加管理员
|
||||
async function submitAddAdmin() {
|
||||
const username = document.getElementById('adminUsername').value.trim();
|
||||
const realName = document.getElementById('adminRealName').value.trim();
|
||||
const password = document.getElementById('adminPassword').value;
|
||||
const roleType = document.getElementById('adminRole').value;
|
||||
|
||||
if (!username || !realName || !roleType) {
|
||||
showToast('请填写完整信息', 'warning');
|
||||
return;
|
||||
}
|
||||
|
||||
const res = await apiPost('/api/admin/add', {
|
||||
username: username,
|
||||
real_name: realName,
|
||||
password: password || undefined,
|
||||
role_type: roleType
|
||||
});
|
||||
|
||||
if (res && res.success) {
|
||||
let msg = `管理员 ${res.data.username} 添加成功`;
|
||||
if (res.data.password) msg += `,密码: ${res.data.password}`;
|
||||
showToast(msg);
|
||||
closeModal('addAdminModal');
|
||||
loadAdmins();
|
||||
} else {
|
||||
showToast(res?.message || '添加失败', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
// 显示添加科目模态框
|
||||
function showAddSubjectModal() {
|
||||
document.getElementById('addSubjectModal').style.display = 'flex';
|
||||
document.getElementById('addSubjectForm').reset();
|
||||
}
|
||||
|
||||
// 提交添加科目
|
||||
async function submitAddSubject() {
|
||||
const subjectName = document.getElementById('subjectName').value.trim();
|
||||
const subjectCode = document.getElementById('subjectCode').value.trim();
|
||||
|
||||
if (!subjectName) {
|
||||
showToast('请填写科目名称', 'warning');
|
||||
return;
|
||||
}
|
||||
|
||||
const res = await apiPost('/api/subject/create', {
|
||||
subject_name: subjectName,
|
||||
subject_code: subjectCode
|
||||
});
|
||||
|
||||
if (res && res.success) {
|
||||
showToast('科目添加成功');
|
||||
closeModal('addSubjectModal');
|
||||
loadSubjects();
|
||||
} else {
|
||||
showToast(res?.message || '添加失败', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
// 撤销扣分记录
|
||||
async function revokeRecord(recordId) {
|
||||
if (!confirm('确定要撤销这条扣分记录吗?')) return;
|
||||
|
||||
const res = await apiPost('/api/admin/conduct/revoke', { record_id: recordId });
|
||||
if (res && res.success) {
|
||||
showToast('撤销成功');
|
||||
loadHistory(currentHistoryPage);
|
||||
} else {
|
||||
showToast(res?.message || '撤销失败', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
// 关闭模态框
|
||||
function closeModal(modalId) {
|
||||
const modal = document.getElementById(modalId);
|
||||
if (modal) {
|
||||
modal.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
// HTML转义
|
||||
function escapeHtml(str) {
|
||||
if (!str) return '';
|
||||
return str.replace(/[&<>]/g, function(m) {
|
||||
if (m === '&') return '&';
|
||||
if (m === '<') return '<';
|
||||
if (m === '>') return '>';
|
||||
return m;
|
||||
});
|
||||
}
|
||||
|
||||
// 全选功能
|
||||
function toggleSelectAll() {
|
||||
const selectAll = document.getElementById('selectAll');
|
||||
if (selectAll) {
|
||||
document.querySelectorAll('.student-checkbox').forEach(cb => {
|
||||
cb.checked = selectAll.checked;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 绑定文件选择事件
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const fileInput = document.getElementById('importFile');
|
||||
if (fileInput) {
|
||||
fileInput.addEventListener('change', previewImportFile);
|
||||
}
|
||||
});
|
||||
242
frontend/assets/js/common.js
Normal file
242
frontend/assets/js/common.js
Normal file
@@ -0,0 +1,242 @@
|
||||
/**
|
||||
* 班级操行分管理系统 - 公共JS
|
||||
*
|
||||
* 开发者: Canglan
|
||||
* 联系方式: admin@sea-studio.top
|
||||
* 版权归属: Sea Network Technology Studio
|
||||
* 许可证: MIT License
|
||||
*
|
||||
* 版权所有 © Sea Network Technology Studio
|
||||
*/
|
||||
|
||||
// API基础地址
|
||||
const API_BASE_URL = window.API_BASE_URL || 'http://localhost:8000';
|
||||
const JWT_STORAGE_KEY = 'class_system_token';
|
||||
const USER_STORAGE_KEY = 'class_system_user';
|
||||
|
||||
// 获取Token
|
||||
function getToken() {
|
||||
return localStorage.getItem(JWT_STORAGE_KEY);
|
||||
}
|
||||
|
||||
// 获取用户信息
|
||||
function getUserInfo() {
|
||||
const userStr = localStorage.getItem(USER_STORAGE_KEY);
|
||||
if (!userStr) return null;
|
||||
try {
|
||||
return JSON.parse(userStr);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// 保存用户信息
|
||||
function setUserInfo(user) {
|
||||
localStorage.setItem(USER_STORAGE_KEY, JSON.stringify(user));
|
||||
}
|
||||
|
||||
// 清除登录信息
|
||||
function clearAuth() {
|
||||
localStorage.removeItem(JWT_STORAGE_KEY);
|
||||
localStorage.removeItem(USER_STORAGE_KEY);
|
||||
}
|
||||
|
||||
// 检查登录状态
|
||||
function checkAuth() {
|
||||
const token = getToken();
|
||||
if (!token) {
|
||||
window.location.href = '/index.php';
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// API请求封装
|
||||
async function apiRequest(url, options = {}) {
|
||||
const token = getToken();
|
||||
|
||||
const headers = {
|
||||
'Content-Type': 'application/json',
|
||||
...options.headers
|
||||
};
|
||||
|
||||
if (token) {
|
||||
headers['Authorization'] = `Bearer ${token}`;
|
||||
}
|
||||
|
||||
const config = {
|
||||
...options,
|
||||
headers
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await fetch(`${API_BASE_URL}${url}`, config);
|
||||
const data = await response.json();
|
||||
|
||||
if (response.status === 401) {
|
||||
clearAuth();
|
||||
window.location.href = '/index.php';
|
||||
return null;
|
||||
}
|
||||
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.error('API请求错误:', error);
|
||||
showToast('网络错误,请稍后重试', 'error');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// GET请求
|
||||
async function apiGet(url, params = {}) {
|
||||
const queryString = new URLSearchParams(params).toString();
|
||||
const fullUrl = queryString ? `${url}?${queryString}` : url;
|
||||
return apiRequest(fullUrl, { method: 'GET' });
|
||||
}
|
||||
|
||||
// POST请求
|
||||
async function apiPost(url, data = {}) {
|
||||
return apiRequest(url, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(data)
|
||||
});
|
||||
}
|
||||
|
||||
// PUT请求
|
||||
async function apiPut(url, data = {}) {
|
||||
return apiRequest(url, {
|
||||
method: 'PUT',
|
||||
body: JSON.stringify(data)
|
||||
});
|
||||
}
|
||||
|
||||
// DELETE请求
|
||||
async function apiDelete(url) {
|
||||
return apiRequest(url, { method: 'DELETE' });
|
||||
}
|
||||
|
||||
// 显示提示消息
|
||||
function showToast(message, type = 'success') {
|
||||
const toast = document.createElement('div');
|
||||
toast.className = `toast toast-${type}`;
|
||||
toast.textContent = message;
|
||||
document.body.appendChild(toast);
|
||||
|
||||
setTimeout(() => {
|
||||
toast.remove();
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
// 格式化日期
|
||||
function formatDate(dateStr) {
|
||||
if (!dateStr) return '-';
|
||||
const date = new Date(dateStr);
|
||||
return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`;
|
||||
}
|
||||
|
||||
// 格式化日期时间
|
||||
function formatDateTime(dateStr) {
|
||||
if (!dateStr) return '-';
|
||||
const date = new Date(dateStr);
|
||||
return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')} ${String(date.getHours()).padStart(2, '0')}:${String(date.getMinutes()).padStart(2, '0')}`;
|
||||
}
|
||||
|
||||
// 获取状态标签HTML
|
||||
function getStatusBadge(status, type = 'homework') {
|
||||
const statusMap = {
|
||||
homework: {
|
||||
'submitted': '已提交',
|
||||
'not_submitted': '未提交',
|
||||
'late': '迟交'
|
||||
},
|
||||
attendance: {
|
||||
'present': '出勤',
|
||||
'absent': '缺勤',
|
||||
'late': '迟到',
|
||||
'leave': '请假'
|
||||
}
|
||||
};
|
||||
|
||||
const texts = statusMap[type] || statusMap.homework;
|
||||
const text = texts[status] || status;
|
||||
|
||||
let className = 'status-badge ';
|
||||
switch (status) {
|
||||
case 'submitted':
|
||||
case 'present':
|
||||
className += 'status-submitted';
|
||||
break;
|
||||
case 'not_submitted':
|
||||
case 'absent':
|
||||
className += 'status-not_submitted';
|
||||
break;
|
||||
case 'late':
|
||||
className += 'status-late';
|
||||
break;
|
||||
case 'leave':
|
||||
className += 'status-leave';
|
||||
break;
|
||||
default:
|
||||
className += 'status-not_submitted';
|
||||
}
|
||||
|
||||
return `<span class="${className}">${text}</span>`;
|
||||
}
|
||||
|
||||
// 退出登录
|
||||
async function logout() {
|
||||
await apiPost('/api/auth/logout');
|
||||
clearAuth();
|
||||
window.location.href = '/index.php';
|
||||
}
|
||||
|
||||
// 加载用户信息
|
||||
function loadUserInfo() {
|
||||
const user = getUserInfo();
|
||||
const userNameSpan = document.getElementById('userName');
|
||||
if (userNameSpan && user) {
|
||||
userNameSpan.textContent = user.real_name || user.username;
|
||||
}
|
||||
}
|
||||
|
||||
// 检查是否需要修改密码
|
||||
function checkNeedChangePassword() {
|
||||
const user = getUserInfo();
|
||||
if (user && user.need_change_password) {
|
||||
const newPassword = prompt('首次登录,请设置新密码(6-20位,需包含字母和数字):');
|
||||
if (newPassword) {
|
||||
changePassword(newPassword);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 修改密码
|
||||
async function changePassword(newPassword) {
|
||||
const res = await apiPost('/api/auth/change-password', {
|
||||
old_password: newPassword,
|
||||
new_password: newPassword
|
||||
});
|
||||
|
||||
if (res && res.success) {
|
||||
showToast('密码修改成功,请重新登录');
|
||||
setTimeout(() => logout(), 1500);
|
||||
} else {
|
||||
showToast(res?.message || '密码修改失败', 'error');
|
||||
checkNeedChangePassword();
|
||||
}
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
loadUserInfo();
|
||||
|
||||
const logoutBtn = document.getElementById('logoutBtn');
|
||||
if (logoutBtn) {
|
||||
logoutBtn.addEventListener('click', logout);
|
||||
}
|
||||
|
||||
// 学生端检查强制修改密码
|
||||
if (window.location.pathname.includes('/student/')) {
|
||||
checkNeedChangePassword();
|
||||
}
|
||||
});
|
||||
13
frontend/assets/js/parent.js
Normal file
13
frontend/assets/js/parent.js
Normal file
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* 班级操行分管理系统 - 家长端JS
|
||||
*
|
||||
* 开发者: Canglan
|
||||
* 联系方式: admin@sea-studio.top
|
||||
* 版权归属: Sea Network Technology Studio
|
||||
* 许可证: MIT License
|
||||
*
|
||||
* 版权所有 © Sea Network Technology Studio
|
||||
*/
|
||||
|
||||
// 家长端专用功能
|
||||
console.log('家长端已加载');
|
||||
13
frontend/assets/js/student.js
Normal file
13
frontend/assets/js/student.js
Normal file
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* 班级操行分管理系统 - 学生端JS
|
||||
*
|
||||
* 开发者: Canglan
|
||||
* 联系方式: admin@sea-studio.top
|
||||
* 版权归属: Sea Network Technology Studio
|
||||
* 许可证: MIT License
|
||||
*
|
||||
* 版权所有 © Sea Network Technology Studio
|
||||
*/
|
||||
|
||||
// 学生端专用功能
|
||||
console.log('学生端已加载');
|
||||
44
frontend/assets/uploads/sample_import.json
Normal file
44
frontend/assets/uploads/sample_import.json
Normal file
@@ -0,0 +1,44 @@
|
||||
{
|
||||
"_comment1": "================================================",
|
||||
"_comment2": "班级操行分管理系统 - 学生批量导入模板",
|
||||
"_comment3": "开发者: Canglan | 版权: Sea Network Technology Studio",
|
||||
"_comment4": "================================================",
|
||||
"_comment5": "字段说明:",
|
||||
"_comment6": " student_no - 必填,学生学号,唯一标识",
|
||||
"_comment7": " name - 必填,学生姓名",
|
||||
"_comment8": " parent_phone - 可选,家长手机号(11位手机号)",
|
||||
"_comment9": " password - 可选,初始密码,不填则默认 123456",
|
||||
"_comment10": "================================================",
|
||||
"_comment11": "导入规则:",
|
||||
"_comment12": " 1. 学生操行分初始值 = 60分",
|
||||
"_comment13": " 2. 学生账号 = 学号,密码 = 指定的password或123456",
|
||||
"_comment14": " 3. 家长账号 = 手机号(若parent_phone有值),密码 = 指定的password或123456",
|
||||
"_comment15": " 4. 家长姓名默认显示为 '学生姓名家长'",
|
||||
"_comment16": "================================================",
|
||||
"students": [
|
||||
{
|
||||
"student_no": "20240001",
|
||||
"name": "张三",
|
||||
"parent_phone": "13800138001",
|
||||
"password": "123456"
|
||||
},
|
||||
{
|
||||
"student_no": "20240002",
|
||||
"name": "李四",
|
||||
"parent_phone": "13800138002",
|
||||
"password": "123456"
|
||||
},
|
||||
{
|
||||
"student_no": "20240003",
|
||||
"name": "王五",
|
||||
"parent_phone": "",
|
||||
"password": ""
|
||||
},
|
||||
{
|
||||
"student_no": "20240004",
|
||||
"name": "赵六",
|
||||
"parent_phone": "13800138004",
|
||||
"password": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user