|
|
@@ -204,8 +204,7 @@
|
|
|
</div>
|
|
|
|
|
|
<div class="student-details">
|
|
|
- <p><strong>班级:</strong>{{ student.className }}</p>
|
|
|
- <p><strong>专业:</strong>{{ student.majorName }}</p>
|
|
|
+ <p><strong>班级:</strong>{{ student.className }} <strong>专业:</strong>{{ student.majorName }}</p>
|
|
|
</div>
|
|
|
|
|
|
<div class="student-score-section">
|
|
|
@@ -249,6 +248,36 @@
|
|
|
</el-row>
|
|
|
</div>
|
|
|
|
|
|
+ <!-- 请假信息区域 -->
|
|
|
+ <div class="leave-section" v-if="getStudentLeave(student.studentId)">
|
|
|
+ <div class="leave-header">
|
|
|
+ <span>请假信息</span>
|
|
|
+ <div class="leave-header-actions">
|
|
|
+ <el-button size="small" plain @click="viewLeaveProcess(student, getStudentLeave(student.studentId))" title="查看请假流程">
|
|
|
+ 查看流程
|
|
|
+ </el-button>
|
|
|
+ <el-tag type="warning" size="small">请假中</el-tag>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="leave-info">
|
|
|
+ <div class="leave-item">
|
|
|
+ <span class="leave-label">发起时间:</span>
|
|
|
+ <span class="leave-value">{{ formatDateTime(getStudentLeave(student.studentId)?.createTime) }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="leave-item">
|
|
|
+ <span class="leave-label">请假时间:</span>
|
|
|
+ <span class="leave-value"
|
|
|
+ >{{ formatDateTime(getStudentLeave(student.studentId)?.startTime) }} 至
|
|
|
+ {{ formatDateTime(getStudentLeave(student.studentId)?.endTime) }}</span
|
|
|
+ >
|
|
|
+ </div>
|
|
|
+ <div class="leave-item">
|
|
|
+ <span class="leave-label">请假类型:</span>
|
|
|
+ <span class="leave-value">{{ getStudentLeave(student.studentId)?.leaveTypeName }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
<!-- 违纪录入区域 -->
|
|
|
<div class="violation-section">
|
|
|
<div class="violation-header">
|
|
|
@@ -315,17 +344,30 @@
|
|
|
@confirm="handleViolationConfirm"
|
|
|
@close="handleViolationClose"
|
|
|
/>
|
|
|
+
|
|
|
+ <!-- 请假流程查看弹窗 -->
|
|
|
+ <LeaveProcessDialog
|
|
|
+ ref="leaveProcessDialogRef"
|
|
|
+ :visible="leaveProcessDialogVisible"
|
|
|
+ :student-info="currentLeaveStudent"
|
|
|
+ :leave-info="currentLeaveInfo"
|
|
|
+ @update:visible="leaveProcessDialogVisible = $event"
|
|
|
+ @close="handleLeaveProcessClose"
|
|
|
+ />
|
|
|
</el-dialog>
|
|
|
</template>
|
|
|
|
|
|
<script setup name="DormHygieneCheckEditDialog" lang="ts">
|
|
|
-import { ref, reactive, computed, watch, toRefs, getCurrentInstance, type ComponentInternalInstance } from 'vue';
|
|
|
-import { Plus, InfoFilled, UploadFilled, Document, Loading, Download, Delete } from '@element-plus/icons-vue';
|
|
|
+import { ref, reactive, computed, watch, toRefs, getCurrentInstance, nextTick, type ComponentInternalInstance } from 'vue';
|
|
|
+import { ElMessage, ElMessageBox } from 'element-plus';
|
|
|
+import { Plus, InfoFilled, UploadFilled, Document, Loading, Download, Delete, View } from '@element-plus/icons-vue';
|
|
|
import { getToken } from '@/utils/auth';
|
|
|
import ImagePreview from '@/components/ImagePreview/index.vue';
|
|
|
import request from '@/utils/request';
|
|
|
-// @ts-ignore
|
|
|
+// @ts-expect-error - Component import without type declarations
|
|
|
import ViolationRecordDialog from './components/ViolationRecordDialog.vue';
|
|
|
+// @ts-expect-error - Component import without type declarations
|
|
|
+import LeaveProcessDialog from './components/LeaveProcessDialog.vue';
|
|
|
|
|
|
// 组件实例
|
|
|
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
|
|
@@ -410,12 +452,21 @@ const studentScores = ref<any[]>([]);
|
|
|
// 违纪记录数据
|
|
|
const violationRecords = ref<Map<string | number, any[]>>(new Map());
|
|
|
|
|
|
+// 学生请假数据
|
|
|
+const studentLeaves = ref<Map<string | number, any>>(new Map());
|
|
|
+
|
|
|
// 违纪记录弹窗相关
|
|
|
const violationDialogVisible = ref(false);
|
|
|
const currentStudent = ref<any>(null);
|
|
|
const currentViolation = ref<any>(null);
|
|
|
const currentViolationIndex = ref(-1);
|
|
|
|
|
|
+// 请假流程弹窗相关
|
|
|
+const leaveProcessDialogVisible = ref(false);
|
|
|
+const leaveProcessDialogRef = ref();
|
|
|
+const currentLeaveStudent = ref<any>(null);
|
|
|
+const currentLeaveInfo = ref<any>(null);
|
|
|
+
|
|
|
// 字典数据
|
|
|
const { xg_dorm_hygiene_grade, xg_student_attendance_status, xg_dorm_hygiene_check_type, xg_dorm_hygiene_check_level, student_discipline_status } =
|
|
|
toRefs<any>(
|
|
|
@@ -473,6 +524,12 @@ const formatDate = (dateStr: string) => {
|
|
|
return new Date(dateStr).toLocaleDateString();
|
|
|
};
|
|
|
|
|
|
+// 格式化日期时间
|
|
|
+const formatDateTime = (dateStr: string) => {
|
|
|
+ if (!dateStr) return '';
|
|
|
+ return new Date(dateStr).toLocaleString();
|
|
|
+};
|
|
|
+
|
|
|
// 加载违纪类型数据
|
|
|
const loadDisciplineTypes = async () => {
|
|
|
try {
|
|
|
@@ -496,9 +553,7 @@ const loadDisciplineTypes = async () => {
|
|
|
} catch (error) {
|
|
|
console.error('加载违纪类型数据失败:', error);
|
|
|
console.error('加载违纪类型数据失败:', error);
|
|
|
- if (window.ElMessage) {
|
|
|
- window.ElMessage.error('加载违纪类型数据失败');
|
|
|
- }
|
|
|
+ ElMessage.error('加载违纪类型数据失败');
|
|
|
disciplineTypeOptions.value = [];
|
|
|
}
|
|
|
};
|
|
|
@@ -514,9 +569,7 @@ const handleDisciplineTypeChange = () => {
|
|
|
const minScore = parseFloat(selectedType.minScore);
|
|
|
if (!isNaN(minScore)) {
|
|
|
hygieneForm.totalScore = minScore;
|
|
|
- if (window.ElMessage) {
|
|
|
- window.ElMessage.success(`已自动填入最低扣分:${minScore}分`);
|
|
|
- }
|
|
|
+ ElMessage.success(`已自动填入最低扣分:${minScore}分`);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
@@ -562,17 +615,15 @@ const loadStudentScores = async () => {
|
|
|
studentScores.value = res.data;
|
|
|
console.log('学生评分数据加载成功:', res.data.length, '条');
|
|
|
|
|
|
- // 同时加载违纪记录
|
|
|
- await loadViolationRecords();
|
|
|
+ // 同时加载违纪记录和请假信息
|
|
|
+ await Promise.all([loadViolationRecords(), loadStudentLeaves()]);
|
|
|
} else {
|
|
|
studentScores.value = [];
|
|
|
console.log('学生评分数据为空,响应码:', res.code);
|
|
|
}
|
|
|
} catch (error) {
|
|
|
console.error('加载学生评分数据失败:', error);
|
|
|
- if (window.ElMessage) {
|
|
|
- window.ElMessage.error('加载学生评分数据失败');
|
|
|
- }
|
|
|
+ ElMessage.error('加载学生评分数据失败');
|
|
|
studentScores.value = [];
|
|
|
} finally {
|
|
|
studentsLoading.value = false;
|
|
|
@@ -602,9 +653,39 @@ const loadViolationRecords = async () => {
|
|
|
}
|
|
|
} catch (error) {
|
|
|
console.error('加载违纪记录数据失败:', error);
|
|
|
- if (window.ElMessage) {
|
|
|
- window.ElMessage.error('加载违纪记录数据失败');
|
|
|
+ ElMessage.error('加载违纪记录数据失败');
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// 加载学生请假信息
|
|
|
+const loadStudentLeaves = async () => {
|
|
|
+ if (!hygieneForm.checkDate || studentScores.value.length === 0) return;
|
|
|
+
|
|
|
+ try {
|
|
|
+ const studentIds = studentScores.value.map((student) => student.studentId).join(',');
|
|
|
+ const res = await request({
|
|
|
+ url: '/studentLeave/studentLeave/queryByCheckDate',
|
|
|
+ method: 'get',
|
|
|
+ params: {
|
|
|
+ studentIds,
|
|
|
+ checkDate: hygieneForm.checkDate
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ if (res.code === 200 && res.data) {
|
|
|
+ studentLeaves.value.clear();
|
|
|
+ Object.keys(res.data).forEach((studentId) => {
|
|
|
+ studentLeaves.value.set(Number(studentId), res.data[studentId]);
|
|
|
+ // 如果学生有请假信息,自动设置出勤状态为3(请假)
|
|
|
+ const student = studentScores.value.find((s) => s.studentId === Number(studentId));
|
|
|
+ if (student) {
|
|
|
+ student.attendanceStatus = '3';
|
|
|
+ }
|
|
|
+ });
|
|
|
}
|
|
|
+ } catch (error) {
|
|
|
+ console.error('加载学生请假信息失败:', error);
|
|
|
+ ElMessage.error('加载学生请假信息失败');
|
|
|
}
|
|
|
};
|
|
|
|
|
|
@@ -613,6 +694,11 @@ const getStudentViolations = (studentId: string | number) => {
|
|
|
return violationRecords.value.get(studentId) || [];
|
|
|
};
|
|
|
|
|
|
+// 获取学生的请假信息
|
|
|
+const getStudentLeave = (studentId: string | number) => {
|
|
|
+ return studentLeaves.value.get(studentId) || null;
|
|
|
+};
|
|
|
+
|
|
|
// 处理附件数据
|
|
|
const handleAttachmentData = async () => {
|
|
|
console.log('handleAttachmentData 方法被调用,attachmentIds:', hygieneForm.attachmentIds);
|
|
|
@@ -805,6 +891,31 @@ const handleViolationClose = () => {
|
|
|
violationDialogVisible.value = false;
|
|
|
};
|
|
|
|
|
|
+// 查看请假流程
|
|
|
+const viewLeaveProcess = async (student: any, leaveInfo: any) => {
|
|
|
+ if (!leaveInfo || !leaveInfo.id) {
|
|
|
+ ElMessage.warning('请假信息不完整,无法查看流程');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ currentLeaveStudent.value = student;
|
|
|
+ currentLeaveInfo.value = leaveInfo;
|
|
|
+ leaveProcessDialogVisible.value = true;
|
|
|
+
|
|
|
+ // 等待弹窗打开后初始化流程数据
|
|
|
+ await nextTick();
|
|
|
+ if (leaveProcessDialogRef.value) {
|
|
|
+ leaveProcessDialogRef.value.init(leaveInfo.id);
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// 请假流程弹窗关闭
|
|
|
+const handleLeaveProcessClose = () => {
|
|
|
+ leaveProcessDialogVisible.value = false;
|
|
|
+ currentLeaveStudent.value = null;
|
|
|
+ currentLeaveInfo.value = null;
|
|
|
+};
|
|
|
+
|
|
|
// 文件上传相关方法
|
|
|
const handleBeforeUpload = (file: any) => {
|
|
|
const isValidType = /\.(jpg|jpeg|png|gif|bmp|webp|pdf|doc|docx|txt|mp4|avi|mov)$/i.test(file.name);
|
|
|
@@ -1154,7 +1265,7 @@ watch(
|
|
|
display: flex;
|
|
|
justify-content: space-between;
|
|
|
align-items: flex-start;
|
|
|
- margin-bottom: 12px;
|
|
|
+ /* margin-bottom: 12px; */
|
|
|
}
|
|
|
|
|
|
.student-name-with-gender {
|
|
|
@@ -1177,13 +1288,13 @@ watch(
|
|
|
}
|
|
|
|
|
|
.student-details {
|
|
|
- margin-bottom: 16px;
|
|
|
- font-size: 13px;
|
|
|
+ /* margin-bottom: 16px; */
|
|
|
+ font-size: 12px;
|
|
|
color: #666;
|
|
|
}
|
|
|
|
|
|
.student-details p {
|
|
|
- margin: 4px 0;
|
|
|
+ /* margin: 4px 0; */
|
|
|
}
|
|
|
|
|
|
.student-score-section {
|
|
|
@@ -1249,6 +1360,59 @@ watch(
|
|
|
padding: 0 !important;
|
|
|
}
|
|
|
|
|
|
+.leave-section {
|
|
|
+ border-top: 1px solid #f0f0f0;
|
|
|
+ padding-top: 12px;
|
|
|
+ margin-bottom: 12px;
|
|
|
+}
|
|
|
+
|
|
|
+.leave-header {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+ margin-bottom: 8px;
|
|
|
+}
|
|
|
+
|
|
|
+.leave-header-actions {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 8px;
|
|
|
+}
|
|
|
+
|
|
|
+.leave-header span {
|
|
|
+ font-size: 14px;
|
|
|
+ font-weight: 600;
|
|
|
+ color: #303133;
|
|
|
+}
|
|
|
+
|
|
|
+.leave-info {
|
|
|
+ background: #f8f9fa;
|
|
|
+ padding: 8px 12px;
|
|
|
+ border-radius: 4px;
|
|
|
+ border: 1px solid #e9ecef;
|
|
|
+}
|
|
|
+
|
|
|
+.leave-item {
|
|
|
+ display: flex;
|
|
|
+ margin-bottom: 4px;
|
|
|
+ font-size: 12px;
|
|
|
+}
|
|
|
+
|
|
|
+.leave-item:last-child {
|
|
|
+ margin-bottom: 0;
|
|
|
+}
|
|
|
+
|
|
|
+.leave-label {
|
|
|
+ color: #666;
|
|
|
+ min-width: 70px;
|
|
|
+ flex-shrink: 0;
|
|
|
+}
|
|
|
+
|
|
|
+.leave-value {
|
|
|
+ color: #303133;
|
|
|
+ flex: 1;
|
|
|
+}
|
|
|
+
|
|
|
.violation-section {
|
|
|
border-top: 1px solid #f0f0f0;
|
|
|
padding-top: 12px;
|