wuzhen update 宿管相关修改
This commit is contained in:
1 parent
a88d87047d
commit
4a768867df
50 files changed
+3228
-424
No files matched your search
@@ -11,15 +11,15 @@
|
||||
</view>
|
||||
|
||||
<u-form ref="formRef" :model="formData" :rules="rules" label-width="100">
|
||||
<!-- 违纪类型 -->
|
||||
<u-form-item label="违纪类型" prop="disciplineTypeId">
|
||||
<!-- 扣分项(多选) -->
|
||||
<u-form-item label="扣分项">
|
||||
<view
|
||||
@click="!isViewMode && (showDisciplineTypePicker = true)"
|
||||
@click="!isViewMode && openDeductionPopup()"
|
||||
style="width: 100%"
|
||||
>
|
||||
<u-input
|
||||
:value="disciplineTypeDisplay"
|
||||
placeholder="请选择违纪类型"
|
||||
:value="deductionSummaryDisplay"
|
||||
placeholder="请选择扣分项(可多选)"
|
||||
readonly
|
||||
border="none"
|
||||
:suffix-icon="isViewMode ? '' : 'arrow-down'"
|
||||
@@ -27,38 +27,70 @@
|
||||
style="pointer-events: none"
|
||||
/>
|
||||
</view>
|
||||
<u-picker
|
||||
v-if="!isViewMode"
|
||||
:show="showDisciplineTypePicker && disciplineTypeOptions.length > 0"
|
||||
:columns="disciplineTypeColumns"
|
||||
keyName="text"
|
||||
@confirm="handleDisciplineTypeConfirm"
|
||||
@cancel="showDisciplineTypePicker = false"
|
||||
></u-picker>
|
||||
</u-form-item>
|
||||
|
||||
<!-- 违纪类型说明 -->
|
||||
<view
|
||||
v-if="formData.disciplineTypeId && selectedDisciplineTypeRemark"
|
||||
class="discipline-type-remark"
|
||||
>
|
||||
<u-icon name="info-circle" color="#909399" size="14"></u-icon>
|
||||
<text class="remark-text">
|
||||
【{{ formData.disciplineTypeName }}】类型说明:{{
|
||||
selectedDisciplineTypeRemark
|
||||
}}
|
||||
</text>
|
||||
<view v-if="deductionItems.length > 0" class="deduction-list">
|
||||
<view
|
||||
v-for="(item, index) in deductionItems"
|
||||
:key="item.disciplineTypeId"
|
||||
class="deduction-item"
|
||||
>
|
||||
<view class="deduction-item-header">
|
||||
<text class="deduction-name">{{ item.disciplineTypeName }}</text>
|
||||
<u-input
|
||||
v-if="!isViewMode"
|
||||
v-model="item.score"
|
||||
type="digit"
|
||||
placeholder="扣分"
|
||||
@input="handleDeductionScoreChange"
|
||||
/>
|
||||
<text v-else class="deduction-score">{{ item.score }}分</text>
|
||||
<text class="deduction-range">({{ getDeductionMin(item) }}-{{ getDeductionMax(item) }}分)</text>
|
||||
<u-icon
|
||||
v-if="!isViewMode"
|
||||
name="close-circle"
|
||||
color="#f56c6c"
|
||||
size="18"
|
||||
@click="removeDeductionItem(index)"
|
||||
></u-icon>
|
||||
</view>
|
||||
<view v-if="getDeductionRemark(item.disciplineTypeId)" class="deduction-remark">
|
||||
<text>{{ getDeductionRemark(item.disciplineTypeId) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="deduction-total">
|
||||
合计扣分:<text class="total-value">{{ formData.totalScore }}</text> 分
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<u-popup :show="showDeductionPopup" mode="bottom" round="16" @close="showDeductionPopup = false">
|
||||
<view class="deduction-popup">
|
||||
<view class="deduction-popup-header">
|
||||
<text class="deduction-popup-title">选择扣分项</text>
|
||||
<text class="deduction-popup-action" @click="confirmDeductionSelection">确定</text>
|
||||
</view>
|
||||
<scroll-view scroll-y class="deduction-popup-body">
|
||||
<u-checkbox-group v-model="tempSelectedTypeIds" placement="column">
|
||||
<u-checkbox
|
||||
v-for="item in disciplineTypeOptions"
|
||||
:key="item.id"
|
||||
:name="String(item.id)"
|
||||
:label="`${item.typeName}(${item.minScore || 0}分起)`"
|
||||
customStyle="margin-bottom: 12px;"
|
||||
></u-checkbox>
|
||||
</u-checkbox-group>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</u-popup>
|
||||
|
||||
<!-- 宿舍扣分 -->
|
||||
<u-form-item label="宿舍扣分" prop="totalScore">
|
||||
<u-input
|
||||
v-model.number="formData.totalScore"
|
||||
v-model="formData.totalScore"
|
||||
type="digit"
|
||||
placeholder="请输入扣分(0-100分)"
|
||||
:disabled="isViewMode"
|
||||
:readonly="isViewMode"
|
||||
@input="handleTotalScoreChange"
|
||||
placeholder="自动合计"
|
||||
disabled
|
||||
readonly
|
||||
/>
|
||||
</u-form-item>
|
||||
|
||||
@@ -272,25 +304,9 @@
|
||||
<text class="student-name">{{ student.studentName }}</text>
|
||||
<!-- 性别标签 -->
|
||||
<u-tag
|
||||
v-if="
|
||||
student.gender !== undefined &&
|
||||
student.gender !== null &&
|
||||
student.gender !== ''
|
||||
"
|
||||
:text="
|
||||
student.gender === '0'
|
||||
? '男'
|
||||
: student.gender === '1'
|
||||
? '女'
|
||||
: '未知'
|
||||
"
|
||||
:type="
|
||||
student.gender === '0'
|
||||
? 'primary'
|
||||
: student.gender === '1'
|
||||
? 'error'
|
||||
: 'warning'
|
||||
"
|
||||
v-if="normalizeGender(student.gender)"
|
||||
:text="normalizeGender(student.gender) === '0' ? '男' : '女'"
|
||||
:type="normalizeGender(student.gender) === '0' ? 'primary' : 'error'"
|
||||
size="mini"
|
||||
></u-tag>
|
||||
</view>
|
||||
@@ -583,6 +599,8 @@ import { getDict, getDictLabel, getDictTagType } from '@/common/dict.js';
|
||||
import dayjs from 'dayjs';
|
||||
import weChatSDK from '@/utils/wechat.js';
|
||||
import ViolationRecordDialog from './ViolationRecordDialog.vue';
|
||||
import { normalizeStudentId, normalizeId } from '../utils/studentId.js';
|
||||
import { calcStudentViolationScoreTotal } from '../utils/violationScore.js';
|
||||
|
||||
export default {
|
||||
name: 'SingleRoomCheck',
|
||||
@@ -622,13 +640,16 @@ export default {
|
||||
attachmentIds: '',
|
||||
},
|
||||
|
||||
deductionItems: [],
|
||||
showDeductionPopup: false,
|
||||
tempSelectedTypeIds: [],
|
||||
|
||||
// 表单验证规则
|
||||
rules: {
|
||||
hygieneGrade: [{ required: true, message: '请选择卫生等级', trigger: 'change' }],
|
||||
},
|
||||
|
||||
// Picker显示状态
|
||||
showDisciplineTypePicker: false,
|
||||
showHygieneGradePicker: false,
|
||||
|
||||
// 违纪类型选项
|
||||
@@ -692,32 +713,9 @@ export default {
|
||||
return name;
|
||||
},
|
||||
|
||||
// 违纪类型列
|
||||
disciplineTypeColumns() {
|
||||
return [
|
||||
this.disciplineTypeOptions.map((item) => ({
|
||||
text: item.typeName,
|
||||
value: item.id,
|
||||
})),
|
||||
];
|
||||
},
|
||||
|
||||
// 违纪类型显示文本
|
||||
disciplineTypeDisplay() {
|
||||
if (!this.formData.disciplineTypeId) return '';
|
||||
const item = this.disciplineTypeOptions.find(
|
||||
(d) => d.id === this.formData.disciplineTypeId,
|
||||
);
|
||||
return item ? item.typeName : '';
|
||||
},
|
||||
|
||||
// 违纪类型说明
|
||||
selectedDisciplineTypeRemark() {
|
||||
if (!this.formData.disciplineTypeId) return '';
|
||||
const item = this.disciplineTypeOptions.find(
|
||||
(d) => d.id === this.formData.disciplineTypeId,
|
||||
);
|
||||
return item ? item.remark : '';
|
||||
deductionSummaryDisplay() {
|
||||
if (!this.deductionItems.length) return '';
|
||||
return this.deductionItems.map((item) => item.disciplineTypeName).join('、');
|
||||
},
|
||||
|
||||
// 卫生等级列
|
||||
@@ -822,12 +820,10 @@ export default {
|
||||
// 加载违纪类型
|
||||
await this.loadDisciplineTypes();
|
||||
|
||||
// 加载房间学生信息
|
||||
await this.loadRoomStudents();
|
||||
|
||||
// 如果有现有数据,加载编辑数据
|
||||
if (this.existingData) {
|
||||
this.loadEditData();
|
||||
if (this.existingData?.id) {
|
||||
await this.loadEditData();
|
||||
} else {
|
||||
await this.loadRoomStudents();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('初始化组件失败:', error);
|
||||
@@ -850,6 +846,52 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
normalizeGender(gender) {
|
||||
if (gender === null || gender === undefined || gender === '') {
|
||||
return undefined;
|
||||
}
|
||||
const value = String(gender);
|
||||
if (value === '0' || value === '男') {
|
||||
return '0';
|
||||
}
|
||||
if (value === '1' || value === '女') {
|
||||
return '1';
|
||||
}
|
||||
return value;
|
||||
},
|
||||
|
||||
mergeGenderFromRoomStudents(students, roomStudents) {
|
||||
if (!students.length || !roomStudents.length) {
|
||||
return students;
|
||||
}
|
||||
|
||||
const genderByStudentId = {};
|
||||
const genderByStudentNo = {};
|
||||
roomStudents.forEach((student) => {
|
||||
const gender = this.normalizeGender(student.gender);
|
||||
if (!gender) {
|
||||
return;
|
||||
}
|
||||
if (student.studentId !== undefined && student.studentId !== null) {
|
||||
genderByStudentId[String(student.studentId)] = gender;
|
||||
}
|
||||
if (student.studentNo) {
|
||||
genderByStudentNo[student.studentNo] = gender;
|
||||
}
|
||||
});
|
||||
|
||||
return students.map((student) => {
|
||||
if (this.normalizeGender(student.gender)) {
|
||||
return { ...student, gender: this.normalizeGender(student.gender) };
|
||||
}
|
||||
const gender =
|
||||
(student.studentId !== undefined && student.studentId !== null
|
||||
? genderByStudentId[String(student.studentId)]
|
||||
: undefined) || (student.studentNo ? genderByStudentNo[student.studentNo] : undefined);
|
||||
return gender ? { ...student, gender } : student;
|
||||
});
|
||||
},
|
||||
|
||||
// 加载违纪类型
|
||||
async loadDisciplineTypes() {
|
||||
try {
|
||||
@@ -897,6 +939,7 @@ export default {
|
||||
|
||||
this.roomStudents = res.data.map((student) => ({
|
||||
...student,
|
||||
studentId: normalizeStudentId(student.studentId),
|
||||
student_score: 0,
|
||||
attendance_status: '1',
|
||||
student_grade: 'A',
|
||||
@@ -937,10 +980,10 @@ export default {
|
||||
if (res.code === 200 && res.data) {
|
||||
this.studentLeaves.clear();
|
||||
Object.keys(res.data).forEach((studentId) => {
|
||||
this.studentLeaves.set(Number(studentId), res.data[studentId]);
|
||||
// 自动设置出勤状态为请假
|
||||
const normalizedId = normalizeStudentId(studentId);
|
||||
this.studentLeaves.set(normalizedId, res.data[studentId]);
|
||||
const student = this.roomStudents.find(
|
||||
(s) => s.studentId === Number(studentId),
|
||||
(s) => normalizeStudentId(s.studentId) === normalizedId,
|
||||
);
|
||||
if (student) {
|
||||
student.attendance_status = '3';
|
||||
@@ -969,6 +1012,7 @@ export default {
|
||||
this.formData.hygieneGrade = this.existingData.hygieneGrade || 'A';
|
||||
this.formData.checkRemarks = this.existingData.checkRemarks || '';
|
||||
this.formData.attachmentIds = this.existingData.attachmentIds || '';
|
||||
this.initDeductionItemsFromData(this.existingData);
|
||||
|
||||
console.log('✅ 表单基础数据回显完成:', this.formData);
|
||||
|
||||
@@ -980,7 +1024,7 @@ export default {
|
||||
// 3️⃣ 从后端接口加载学生评分数据、违纪记录和请假信息(复用Web端接口)
|
||||
// 注意:loadStudentScoresFromAPI 内部会自动调用 loadViolationRecordsFromAPI 和 loadStudentLeaves
|
||||
if (this.existingData.id) {
|
||||
await this.loadStudentScoresFromAPI(this.existingData.id);
|
||||
await this.loadStudentScoresFromAPI(normalizeId(this.existingData.id));
|
||||
}
|
||||
|
||||
console.log('✅ 所有数据回显完成');
|
||||
@@ -992,17 +1036,18 @@ export default {
|
||||
|
||||
// 从后端API加载学生评分数据(复用Web端接口)
|
||||
async loadStudentScoresFromAPI(checkId) {
|
||||
if (!checkId) {
|
||||
const normalizedCheckId = normalizeId(checkId);
|
||||
if (!normalizedCheckId) {
|
||||
console.warn('⚠️ 没有检查ID,无法加载学生评分数据');
|
||||
return;
|
||||
}
|
||||
|
||||
this.studentsLoading = true;
|
||||
try {
|
||||
console.log('🔄 从API加载学生评分数据,检查ID:', checkId);
|
||||
console.log('🔄 从API加载学生评分数据,检查ID:', normalizedCheckId);
|
||||
|
||||
const res = await request({
|
||||
url: `/dormHygieneStudentScore/dormHygieneStudentScore/getByCheckId/${checkId}`,
|
||||
url: `/dormHygieneStudentScore/dormHygieneStudentScore/getByCheckId/${normalizedCheckId}`,
|
||||
method: 'GET',
|
||||
});
|
||||
|
||||
@@ -1010,9 +1055,9 @@ export default {
|
||||
console.log('✅ 成功获取学生评分数据:', res.data.length, '条');
|
||||
|
||||
// 将后端返回的学生评分数据映射到roomStudents
|
||||
this.roomStudents = res.data.map((studentScore) => ({
|
||||
let students = res.data.map((studentScore) => ({
|
||||
// 学生基本信息
|
||||
studentId: studentScore.studentId,
|
||||
studentId: normalizeStudentId(studentScore.studentId),
|
||||
studentName: studentScore.studentName,
|
||||
studentNo: studentScore.studentNo,
|
||||
className: studentScore.className,
|
||||
@@ -1021,7 +1066,7 @@ export default {
|
||||
bedId: studentScore.bedId,
|
||||
bedName: studentScore.bedCode,
|
||||
bedCode: studentScore.bedCode,
|
||||
gender: studentScore.gender || '1',
|
||||
gender: this.normalizeGender(studentScore.gender),
|
||||
// 评分数据
|
||||
student_score: studentScore.studentScore || 0,
|
||||
attendance_status: studentScore.attendanceStatus || '1',
|
||||
@@ -1034,13 +1079,27 @@ export default {
|
||||
showGradePicker: false,
|
||||
}));
|
||||
|
||||
if (students.some((student) => !student.gender) && this.roomInfo && this.roomInfo.id) {
|
||||
const roomRes = await request({
|
||||
url: '/dormBedStudent/dormBedStudent/getRoomStudents',
|
||||
method: 'GET',
|
||||
params: { roomId: this.roomInfo.id },
|
||||
});
|
||||
if (roomRes.code === 200 && roomRes.data) {
|
||||
students = this.mergeGenderFromRoomStudents(students, roomRes.data);
|
||||
}
|
||||
}
|
||||
|
||||
this.roomStudents = students;
|
||||
|
||||
console.log('✅ 学生评分数据映射完成');
|
||||
|
||||
// 🔴 同时加载违纪记录和请假信息(复用Web端逻辑)
|
||||
await Promise.all([
|
||||
this.loadViolationRecordsFromAPI(checkId),
|
||||
this.loadViolationRecordsFromAPI(normalizedCheckId),
|
||||
this.loadStudentLeaves(),
|
||||
]);
|
||||
this.syncStudentScoreFromViolations();
|
||||
} else {
|
||||
console.warn('⚠️ 未获取到学生评分数据,保持当前学生列表');
|
||||
}
|
||||
@@ -1054,16 +1113,17 @@ export default {
|
||||
|
||||
// 从后端API加载违纪记录数据(复用Web端接口)
|
||||
async loadViolationRecordsFromAPI(checkId) {
|
||||
if (!checkId) {
|
||||
const normalizedCheckId = normalizeId(checkId);
|
||||
if (!normalizedCheckId) {
|
||||
console.warn('⚠️ 没有检查ID,无法加载违纪记录数据');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
console.log('🔄 从API加载违纪记录数据,检查ID:', checkId);
|
||||
console.log('🔄 从API加载违纪记录数据,检查ID:', normalizedCheckId);
|
||||
|
||||
const res = await request({
|
||||
url: `/dormHygieneCheck/dormHygieneCheck/getViolations/${checkId}`,
|
||||
url: `/dormHygieneCheck/dormHygieneCheck/getViolations/${normalizedCheckId}`,
|
||||
method: 'GET',
|
||||
});
|
||||
|
||||
@@ -1075,7 +1135,7 @@ export default {
|
||||
|
||||
// 按学生ID分组违纪记录
|
||||
res.data.forEach((violation) => {
|
||||
const studentId = violation.studentId;
|
||||
const studentId = normalizeStudentId(violation.studentId);
|
||||
if (!studentId) return;
|
||||
|
||||
const violations = this.violationRecords.get(studentId) || [];
|
||||
@@ -1101,6 +1161,7 @@ export default {
|
||||
'✅ 所有违纪记录数据加载完成,总学生数:',
|
||||
this.violationRecords.size,
|
||||
);
|
||||
this.syncStudentScoreFromViolations();
|
||||
} else {
|
||||
console.warn('⚠️ 未获取到违纪记录数据');
|
||||
}
|
||||
@@ -1318,7 +1379,9 @@ export default {
|
||||
console.log('👥 回显学生评分数据:', studentScores);
|
||||
|
||||
studentScores.forEach((scoreData) => {
|
||||
const student = this.roomStudents.find((s) => s.studentId === scoreData.studentId);
|
||||
const student = this.roomStudents.find(
|
||||
(s) => normalizeStudentId(s.studentId) === normalizeStudentId(scoreData.studentId),
|
||||
);
|
||||
if (student) {
|
||||
student.student_score = scoreData.studentScore || 0;
|
||||
student.attendance_status = scoreData.attendanceStatus || '1';
|
||||
@@ -1337,7 +1400,7 @@ export default {
|
||||
this.violationRecords.clear();
|
||||
|
||||
violationData.forEach((violation) => {
|
||||
const studentId = violation.studentId;
|
||||
const studentId = normalizeStudentId(violation.studentId);
|
||||
if (!studentId) return;
|
||||
|
||||
const violations = this.violationRecords.get(studentId) || [];
|
||||
@@ -1360,6 +1423,28 @@ export default {
|
||||
violation.violationAttachmentIds || violation.attachmentOssIds,
|
||||
);
|
||||
});
|
||||
this.syncStudentScoreFromViolations();
|
||||
},
|
||||
|
||||
// 根据违纪记录合计同步个人扣分
|
||||
syncStudentScoreFromViolations(studentId) {
|
||||
const syncOne = (sid) => {
|
||||
const student = this.roomStudents.find(
|
||||
(item) => normalizeStudentId(item.studentId) === sid,
|
||||
);
|
||||
if (student) {
|
||||
student.student_score = calcStudentViolationScoreTotal(this.getStudentViolations(sid));
|
||||
}
|
||||
};
|
||||
|
||||
if (studentId != null && studentId !== '') {
|
||||
syncOne(normalizeStudentId(studentId));
|
||||
return;
|
||||
}
|
||||
|
||||
this.roomStudents.forEach((student) => {
|
||||
syncOne(normalizeStudentId(student.studentId));
|
||||
});
|
||||
},
|
||||
|
||||
// 清理blob URL,防止内存泄漏
|
||||
@@ -1377,38 +1462,104 @@ export default {
|
||||
});
|
||||
},
|
||||
|
||||
// 违纪类型确认处理
|
||||
handleDisciplineTypeConfirm(e) {
|
||||
if (e && e.value && e.value.length > 0) {
|
||||
const selectedType = this.disciplineTypeOptions.find(
|
||||
(item) => item.id === e.value[0].value,
|
||||
);
|
||||
if (selectedType) {
|
||||
this.formData.disciplineTypeId = selectedType.id;
|
||||
this.formData.disciplineTypeName = selectedType.typeName;
|
||||
|
||||
// 自动填入最低扣分
|
||||
if (selectedType.minScore !== null && selectedType.minScore !== undefined) {
|
||||
this.formData.totalScore = selectedType.minScore;
|
||||
uni.showToast({
|
||||
title: `已自动填入最低扣分:${selectedType.minScore}分`,
|
||||
icon: 'none',
|
||||
});
|
||||
}
|
||||
}
|
||||
initDeductionItemsFromData(data) {
|
||||
if (data?.deductionItems && data.deductionItems.length > 0) {
|
||||
this.deductionItems = data.deductionItems.map((item, index) => ({
|
||||
...item,
|
||||
score: item.score ?? 0,
|
||||
sortOrder: item.sortOrder ?? index + 1,
|
||||
}));
|
||||
} else if (data?.disciplineTypeId) {
|
||||
this.deductionItems = [
|
||||
{
|
||||
disciplineTypeId: data.disciplineTypeId,
|
||||
disciplineTypeName: data.disciplineTypeName,
|
||||
score: data.totalScore || 0,
|
||||
sortOrder: 1,
|
||||
},
|
||||
];
|
||||
} else {
|
||||
this.deductionItems = [];
|
||||
}
|
||||
this.showDisciplineTypePicker = false;
|
||||
this.recalcDeductionTotal();
|
||||
},
|
||||
|
||||
// 宿舍总分变化处理
|
||||
handleTotalScoreChange() {
|
||||
const score = parseFloat(this.formData.totalScore);
|
||||
if (!isNaN(score) && (score < 0 || score > 100)) {
|
||||
uni.showToast({ title: '宿舍扣分必须在0-100分之间', icon: 'none' });
|
||||
this.formData.totalScore = 0;
|
||||
openDeductionPopup() {
|
||||
this.tempSelectedTypeIds = this.deductionItems.map((item) => String(item.disciplineTypeId));
|
||||
this.showDeductionPopup = true;
|
||||
},
|
||||
|
||||
confirmDeductionSelection() {
|
||||
const currentMap = new Map(
|
||||
this.deductionItems.map((item) => [String(item.disciplineTypeId), item]),
|
||||
);
|
||||
const nextItems = [];
|
||||
this.tempSelectedTypeIds.forEach((typeId, index) => {
|
||||
const option = this.disciplineTypeOptions.find((item) => String(item.id) === String(typeId));
|
||||
if (!option) return;
|
||||
const existing = currentMap.get(String(typeId));
|
||||
nextItems.push({
|
||||
disciplineTypeId: option.id,
|
||||
disciplineTypeName: option.typeName,
|
||||
score: existing ? existing.score : Number(option.minScore || 0),
|
||||
sortOrder: index + 1,
|
||||
});
|
||||
});
|
||||
this.deductionItems = nextItems;
|
||||
this.recalcDeductionTotal();
|
||||
this.showDeductionPopup = false;
|
||||
},
|
||||
|
||||
removeDeductionItem(index) {
|
||||
this.deductionItems.splice(index, 1);
|
||||
this.recalcDeductionTotal();
|
||||
},
|
||||
|
||||
handleDeductionScoreChange() {
|
||||
this.deductionItems.forEach((item) => {
|
||||
let score = parseFloat(item.score);
|
||||
if (isNaN(score)) {
|
||||
score = this.getDeductionMin(item);
|
||||
}
|
||||
const min = this.getDeductionMin(item);
|
||||
const max = this.getDeductionMax(item);
|
||||
if (score < min) score = min;
|
||||
if (score > max) score = max;
|
||||
item.score = score;
|
||||
});
|
||||
this.recalcDeductionTotal();
|
||||
if ((this.formData.totalScore || 0) > 100) {
|
||||
uni.showToast({ title: '合计扣分不能超过100分', icon: 'none' });
|
||||
}
|
||||
},
|
||||
|
||||
recalcDeductionTotal() {
|
||||
const total = this.deductionItems.reduce(
|
||||
(sum, item) => sum + (parseFloat(item.score) || 0),
|
||||
0,
|
||||
);
|
||||
this.formData.totalScore = Number(total.toFixed(2));
|
||||
},
|
||||
|
||||
getDeductionOption(typeId) {
|
||||
return this.disciplineTypeOptions.find((item) => String(item.id) === String(typeId));
|
||||
},
|
||||
|
||||
getDeductionMin(item) {
|
||||
const option = this.getDeductionOption(item.disciplineTypeId);
|
||||
return option?.minScore ?? 0;
|
||||
},
|
||||
|
||||
getDeductionMax(item) {
|
||||
const option = this.getDeductionOption(item.disciplineTypeId);
|
||||
return option?.maxScore ?? 100;
|
||||
},
|
||||
|
||||
getDeductionRemark(typeId) {
|
||||
const option = this.getDeductionOption(typeId);
|
||||
return option?.remark || '';
|
||||
},
|
||||
|
||||
// 卫生等级确认处理
|
||||
handleHygieneGradeConfirm(e) {
|
||||
if (e && e.value && e.value.length > 0) {
|
||||
@@ -2114,7 +2265,7 @@ export default {
|
||||
|
||||
// 获取学生请假信息
|
||||
getStudentLeave(studentId) {
|
||||
return this.studentLeaves.get(studentId) || null;
|
||||
return this.studentLeaves.get(normalizeStudentId(studentId)) || null;
|
||||
},
|
||||
|
||||
// 格式化日期时间
|
||||
@@ -2141,8 +2292,7 @@ export default {
|
||||
|
||||
// 删除违纪记录
|
||||
removeViolationRecord(studentId, index) {
|
||||
// 保存要删除的记录信息
|
||||
this.deleteViolationStudentId = studentId;
|
||||
this.deleteViolationStudentId = normalizeStudentId(studentId);
|
||||
this.deleteViolationIndex = index;
|
||||
// 显示确认对话框
|
||||
this.showDeleteConfirm = true;
|
||||
@@ -2159,6 +2309,7 @@ export default {
|
||||
} else {
|
||||
this.violationRecords.set(this.deleteViolationStudentId, violations);
|
||||
}
|
||||
this.syncStudentScoreFromViolations(this.deleteViolationStudentId);
|
||||
|
||||
uni.showToast({ title: '违纪记录删除成功', icon: 'success' });
|
||||
}
|
||||
@@ -2178,25 +2329,31 @@ export default {
|
||||
|
||||
// 获取学生违纪记录
|
||||
getStudentViolations(studentId) {
|
||||
return this.violationRecords.get(studentId) || [];
|
||||
return this.violationRecords.get(normalizeStudentId(studentId)) || [];
|
||||
},
|
||||
|
||||
// 违纪记录确认
|
||||
handleViolationConfirm(violationData) {
|
||||
const studentId = this.currentStudent.studentId;
|
||||
const studentId = normalizeStudentId(this.currentStudent.studentId);
|
||||
const violations = this.violationRecords.get(studentId) || [];
|
||||
|
||||
if (this.currentViolationIndex >= 0) {
|
||||
// 编辑模式
|
||||
if (Array.isArray(violationData)) {
|
||||
violations.push(...violationData);
|
||||
uni.showToast({
|
||||
title: `已添加${violationData.length}条违纪记录`,
|
||||
icon: 'success',
|
||||
});
|
||||
} else if (this.currentViolationIndex >= 0) {
|
||||
violations[this.currentViolationIndex] = violationData;
|
||||
uni.showToast({ title: '违纪记录保存成功', icon: 'success' });
|
||||
} else {
|
||||
// 新增模式
|
||||
violations.push(violationData);
|
||||
uni.showToast({ title: '违纪记录保存成功', icon: 'success' });
|
||||
}
|
||||
|
||||
this.violationRecords.set(studentId, violations);
|
||||
this.syncStudentScoreFromViolations(studentId);
|
||||
this.violationDialogVisible = false;
|
||||
uni.showToast({ title: '违纪记录保存成功', icon: 'success' });
|
||||
},
|
||||
|
||||
// 违纪记录弹窗关闭
|
||||
@@ -2210,8 +2367,15 @@ export default {
|
||||
// 表单验证
|
||||
await this.$refs.formRef.validate();
|
||||
|
||||
if ((this.formData.totalScore || 0) > 100) {
|
||||
uni.showToast({ title: '宿舍合计扣分不能超过100分', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
|
||||
this.submitLoading = true;
|
||||
|
||||
this.syncStudentScoreFromViolations();
|
||||
|
||||
// 处理附件数据
|
||||
const attachmentOssIds = this.attachmentList
|
||||
.filter((file) => file.status === 'success' && file.ossId)
|
||||
@@ -2270,6 +2434,7 @@ export default {
|
||||
|
||||
// 宿舍整体评分
|
||||
...this.formData,
|
||||
deductionItems: this.deductionItems,
|
||||
|
||||
// 学生评分数据
|
||||
studentScores,
|
||||
@@ -2791,4 +2956,90 @@ export default {
|
||||
padding: 20rpx 30rpx;
|
||||
padding-bottom: 40rpx;
|
||||
}
|
||||
|
||||
.deduction-list {
|
||||
margin: 16rpx 0 24rpx;
|
||||
padding: 20rpx;
|
||||
background: #f8f9fb;
|
||||
border-radius: 12rpx;
|
||||
}
|
||||
|
||||
.deduction-item + .deduction-item {
|
||||
margin-top: 16rpx;
|
||||
padding-top: 16rpx;
|
||||
border-top: 1rpx dashed #e4e7ed;
|
||||
}
|
||||
|
||||
.deduction-item-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 12rpx;
|
||||
}
|
||||
|
||||
.deduction-name {
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
color: #303133;
|
||||
min-width: 160rpx;
|
||||
}
|
||||
|
||||
.deduction-score {
|
||||
font-size: 26rpx;
|
||||
color: #f56c6c;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.deduction-range {
|
||||
font-size: 22rpx;
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
.deduction-remark {
|
||||
margin-top: 8rpx;
|
||||
font-size: 22rpx;
|
||||
color: #909399;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.deduction-total {
|
||||
margin-top: 16rpx;
|
||||
padding-top: 16rpx;
|
||||
border-top: 1rpx solid #e4e7ed;
|
||||
font-size: 26rpx;
|
||||
color: #606266;
|
||||
}
|
||||
|
||||
.deduction-total .total-value {
|
||||
color: #f56c6c;
|
||||
font-weight: 600;
|
||||
font-size: 30rpx;
|
||||
}
|
||||
|
||||
.deduction-popup {
|
||||
padding: 24rpx 30rpx 40rpx;
|
||||
max-height: 70vh;
|
||||
}
|
||||
|
||||
.deduction-popup-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.deduction-popup-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
.deduction-popup-action {
|
||||
font-size: 28rpx;
|
||||
color: #3c9cff;
|
||||
}
|
||||
|
||||
.deduction-popup-body {
|
||||
max-height: 55vh;
|
||||
}
|
||||
</style>
|
||||
@@ -36,40 +36,120 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 违纪类型选择 -->
|
||||
<u-form-item label="违纪类型" prop="disciplineTypeId" required>
|
||||
<view @click="handleDisciplineTypeClick" style="width: 100%">
|
||||
<u-input
|
||||
:value="disciplineTypeDisplay"
|
||||
placeholder="请选择违纪类型"
|
||||
readonly
|
||||
suffix-icon="arrow-down"
|
||||
suffix-icon-style="color: #c8c9cc;"
|
||||
@click.stop="handleDisciplineTypeClick"
|
||||
/>
|
||||
<!-- 违纪类型:编辑模式单选 -->
|
||||
<template v-if="isEditMode">
|
||||
<u-form-item label="违纪类型" prop="disciplineTypeId" required>
|
||||
<view @click="handleDisciplineTypeClick" style="width: 100%">
|
||||
<u-input
|
||||
:value="disciplineTypeDisplay"
|
||||
placeholder="请选择违纪类型"
|
||||
readonly
|
||||
suffix-icon="arrow-down"
|
||||
suffix-icon-style="color: #c8c9cc;"
|
||||
@click.stop="handleDisciplineTypeClick"
|
||||
/>
|
||||
</view>
|
||||
</u-form-item>
|
||||
|
||||
<u-picker
|
||||
:show="showDisciplineTypePicker"
|
||||
:columns="disciplineTypeColumns"
|
||||
keyName="text"
|
||||
@confirm="handleDisciplineTypeConfirm"
|
||||
@cancel="showDisciplineTypePicker = false"
|
||||
@close="showDisciplineTypePicker = false"
|
||||
></u-picker>
|
||||
|
||||
<view
|
||||
v-if="formData.disciplineTypeId && selectedDisciplineTypeRemark"
|
||||
class="discipline-type-remark"
|
||||
>
|
||||
<u-icon name="info-circle" color="#909399" size="14"></u-icon>
|
||||
<text class="remark-text">
|
||||
类型说明:{{ selectedDisciplineTypeRemark }}
|
||||
</text>
|
||||
</view>
|
||||
</u-form-item>
|
||||
</template>
|
||||
|
||||
<!-- 违纪类型选择器 -->
|
||||
<u-picker
|
||||
:show="showDisciplineTypePicker"
|
||||
:columns="disciplineTypeColumns"
|
||||
keyName="text"
|
||||
@confirm="handleDisciplineTypeConfirm"
|
||||
@cancel="showDisciplineTypePicker = false"
|
||||
@close="showDisciplineTypePicker = false"
|
||||
></u-picker>
|
||||
<!-- 违纪类型:新增模式多选 -->
|
||||
<template v-else>
|
||||
<u-form-item label="违纪类型">
|
||||
<view @click="openViolationTypePopup" style="width: 100%">
|
||||
<u-input
|
||||
:value="violationSummaryDisplay"
|
||||
placeholder="请选择违纪类型(可多选)"
|
||||
readonly
|
||||
suffix-icon="arrow-down"
|
||||
suffix-icon-style="color: #c8c9cc;"
|
||||
style="pointer-events: none"
|
||||
/>
|
||||
</view>
|
||||
</u-form-item>
|
||||
|
||||
<!-- 违纪类型说明 -->
|
||||
<view
|
||||
v-if="formData.disciplineTypeId && selectedDisciplineTypeRemark"
|
||||
class="discipline-type-remark"
|
||||
>
|
||||
<u-icon name="info-circle" color="#909399" size="14"></u-icon>
|
||||
<text class="remark-text">
|
||||
类型说明:{{ selectedDisciplineTypeRemark }}
|
||||
</text>
|
||||
</view>
|
||||
<view v-if="violationItems.length > 0" class="violation-item-list">
|
||||
<view
|
||||
v-for="(item, index) in violationItems"
|
||||
:key="item.disciplineTypeId"
|
||||
class="violation-item-row"
|
||||
>
|
||||
<view class="violation-item-header">
|
||||
<text class="violation-item-name">{{ item.disciplineTypeName }}</text>
|
||||
<u-input
|
||||
v-model="item.scoreDeduction"
|
||||
type="digit"
|
||||
placeholder="扣分"
|
||||
@input="handleViolationScoreChange"
|
||||
/>
|
||||
<text class="score-range">
|
||||
({{ getViolationMin(item) }}-{{ getViolationMax(item) }}分)
|
||||
</text>
|
||||
<u-icon
|
||||
name="close-circle"
|
||||
color="#f56c6c"
|
||||
size="18"
|
||||
@click="removeViolationItem(index)"
|
||||
></u-icon>
|
||||
</view>
|
||||
<view
|
||||
v-if="getViolationRemark(item.disciplineTypeId)"
|
||||
class="discipline-type-remark"
|
||||
>
|
||||
<text>类型说明:{{ getViolationRemark(item.disciplineTypeId) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="violation-item-total">
|
||||
合计扣分:<text class="total-value">{{ violationItemsTotalScore }}</text> 分
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<u-popup
|
||||
:show="showViolationTypePopup"
|
||||
mode="bottom"
|
||||
round="16"
|
||||
@close="showViolationTypePopup = false"
|
||||
>
|
||||
<view class="violation-type-popup">
|
||||
<view class="violation-type-popup-header">
|
||||
<text class="violation-type-popup-title">选择违纪类型</text>
|
||||
<text
|
||||
class="violation-type-popup-action"
|
||||
@click="confirmViolationTypeSelection"
|
||||
>确定</text>
|
||||
</view>
|
||||
<scroll-view scroll-y class="violation-type-popup-body">
|
||||
<u-checkbox-group v-model="tempSelectedTypeIds" placement="column">
|
||||
<u-checkbox
|
||||
v-for="item in allViolationTypes"
|
||||
:key="item.id"
|
||||
:name="String(item.id)"
|
||||
:label="getViolationTypeLabel(item)"
|
||||
customStyle="margin-bottom: 12px;"
|
||||
></u-checkbox>
|
||||
</u-checkbox-group>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</u-popup>
|
||||
</template>
|
||||
|
||||
<!-- 违纪日期 -->
|
||||
<u-form-item label="违纪日期" prop="disciplineDate" required>
|
||||
@@ -93,8 +173,13 @@
|
||||
></u-datetime-picker>
|
||||
</u-form-item>
|
||||
|
||||
<!-- 扣分分值 -->
|
||||
<u-form-item label="扣分分值" prop="scoreDeduction" required>
|
||||
<!-- 扣分分值(编辑模式) -->
|
||||
<u-form-item
|
||||
v-if="isEditMode"
|
||||
label="扣分分值"
|
||||
prop="scoreDeduction"
|
||||
required
|
||||
>
|
||||
<u-input
|
||||
v-model.number="formData.scoreDeduction"
|
||||
type="digit"
|
||||
@@ -104,7 +189,7 @@
|
||||
</u-form-item>
|
||||
|
||||
<!-- 违纪原因 -->
|
||||
<u-form-item label="违纪原因" prop="disciplineReason" required>
|
||||
<u-form-item label="违纪原因" prop="disciplineReason">
|
||||
<u-textarea
|
||||
v-model="formData.disciplineReason"
|
||||
placeholder="请输入违纪原因"
|
||||
@@ -398,11 +483,14 @@ export default {
|
||||
},
|
||||
},
|
||||
],
|
||||
disciplineReason: [{ required: true, message: '请输入违纪原因', trigger: 'blur' }],
|
||||
disciplineReason: [],
|
||||
},
|
||||
|
||||
// Picker显示状态
|
||||
showDisciplineTypePicker: false,
|
||||
showViolationTypePopup: false,
|
||||
tempSelectedTypeIds: [],
|
||||
violationItems: [],
|
||||
showDatePicker: false,
|
||||
|
||||
// 违纪日期
|
||||
@@ -435,6 +523,23 @@ export default {
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
isEditMode() {
|
||||
return !!this.violationData;
|
||||
},
|
||||
|
||||
violationSummaryDisplay() {
|
||||
if (!this.violationItems.length) return '';
|
||||
return this.violationItems.map((item) => item.disciplineTypeName).join('、');
|
||||
},
|
||||
|
||||
violationItemsTotalScore() {
|
||||
const sum = this.violationItems.reduce(
|
||||
(acc, item) => acc + (parseFloat(item.scoreDeduction) || 0),
|
||||
0,
|
||||
);
|
||||
return Number(sum.toFixed(2));
|
||||
},
|
||||
|
||||
// 违纪类型列(带区域标签)
|
||||
disciplineTypeColumns() {
|
||||
return [
|
||||
@@ -768,8 +873,9 @@ export default {
|
||||
}
|
||||
} else {
|
||||
console.log('违纪记录:新增模式,重置附件列表');
|
||||
// 重置附件列表
|
||||
this.attachmentList = [];
|
||||
this.violationItems = [];
|
||||
this.tempSelectedTypeIds = [];
|
||||
}
|
||||
},
|
||||
|
||||
@@ -919,6 +1025,11 @@ export default {
|
||||
|
||||
// 违纪类型点击处理
|
||||
handleDisciplineTypeClick() {
|
||||
if (!this.isEditMode) {
|
||||
this.openViolationTypePopup();
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('=== 违纪类型点击 ===');
|
||||
console.log('违纪类型数据量:', this.allViolationTypes.length);
|
||||
console.log('违纪类型数据:', this.allViolationTypes);
|
||||
@@ -996,6 +1107,100 @@ export default {
|
||||
this.showDisciplineTypePicker = false;
|
||||
},
|
||||
|
||||
openViolationTypePopup() {
|
||||
if (this.allViolationTypes.length === 0) {
|
||||
uni.showToast({
|
||||
title: '暂无可用的违纪类型',
|
||||
icon: 'none',
|
||||
});
|
||||
return;
|
||||
}
|
||||
this.tempSelectedTypeIds = this.violationItems.map((item) => String(item.disciplineTypeId));
|
||||
this.showViolationTypePopup = true;
|
||||
},
|
||||
|
||||
confirmViolationTypeSelection() {
|
||||
const currentMap = new Map(
|
||||
this.violationItems.map((item) => [String(item.disciplineTypeId), item]),
|
||||
);
|
||||
const nextItems = [];
|
||||
this.tempSelectedTypeIds.forEach((typeId) => {
|
||||
const option = this.allViolationTypes.find((item) => String(item.id) === String(typeId));
|
||||
if (!option) return;
|
||||
const existing = currentMap.get(String(typeId));
|
||||
nextItems.push({
|
||||
disciplineTypeId: option.id,
|
||||
disciplineTypeName: option.typeName,
|
||||
scoreDeduction: existing
|
||||
? existing.scoreDeduction
|
||||
: parseFloat(option.minScore) || 0,
|
||||
});
|
||||
});
|
||||
this.violationItems = nextItems;
|
||||
this.showViolationTypePopup = false;
|
||||
},
|
||||
|
||||
removeViolationItem(index) {
|
||||
this.violationItems.splice(index, 1);
|
||||
},
|
||||
|
||||
getViolationOption(typeId) {
|
||||
return this.allViolationTypes.find((item) => String(item.id) === String(typeId));
|
||||
},
|
||||
|
||||
getViolationMin(item) {
|
||||
const option = this.getViolationOption(item.disciplineTypeId);
|
||||
return option?.minScore ?? 0;
|
||||
},
|
||||
|
||||
getViolationMax(item) {
|
||||
const option = this.getViolationOption(item.disciplineTypeId);
|
||||
return option?.maxScore ?? 100;
|
||||
},
|
||||
|
||||
getViolationRemark(typeId) {
|
||||
const option = this.getViolationOption(typeId);
|
||||
return option?.remark || '';
|
||||
},
|
||||
|
||||
handleViolationScoreChange() {
|
||||
this.violationItems.forEach((item) => {
|
||||
let score = parseFloat(item.scoreDeduction);
|
||||
if (isNaN(score)) {
|
||||
score = this.getViolationMin(item);
|
||||
}
|
||||
const min = this.getViolationMin(item);
|
||||
const max = this.getViolationMax(item);
|
||||
if (score < min) score = min;
|
||||
if (score > max) score = max;
|
||||
item.scoreDeduction = score;
|
||||
});
|
||||
},
|
||||
|
||||
buildSharedViolationFields() {
|
||||
const attachmentOssIds = this.attachmentList
|
||||
.filter((file) => file.status === 'success' && file.ossId)
|
||||
.map((file) => file.ossId)
|
||||
.join(',');
|
||||
|
||||
return {
|
||||
studentId: this.formData.studentId,
|
||||
studentNo: this.formData.studentNo,
|
||||
studentName: this.formData.studentName,
|
||||
collegeId: this.formData.collegeId,
|
||||
collegeName: this.formData.collegeName,
|
||||
majorId: this.formData.majorId,
|
||||
majorName: this.formData.majorName,
|
||||
classId: this.formData.classId,
|
||||
className: this.formData.className,
|
||||
disciplineDate: this.formData.disciplineDate,
|
||||
disciplineReason: this.formData.disciplineReason,
|
||||
disciplineContent: this.formData.disciplineContent,
|
||||
attachmentOssIds,
|
||||
violationSource: this.formData.violationSource || '1',
|
||||
};
|
||||
},
|
||||
|
||||
// 扣分变化处理
|
||||
handleScoreChange() {
|
||||
// 确保转换为数字类型
|
||||
@@ -1844,18 +2049,55 @@ export default {
|
||||
// 确定
|
||||
async handleConfirm() {
|
||||
try {
|
||||
// 表单验证
|
||||
await this.$refs.formRef.validate();
|
||||
|
||||
this.confirmLoading = true;
|
||||
|
||||
// 处理附件数据
|
||||
const attachmentOssIds = this.attachmentList
|
||||
.filter((file) => file.status === 'success' && file.ossId)
|
||||
.map((file) => file.ossId);
|
||||
this.formData.attachmentOssIds = attachmentOssIds.join(',');
|
||||
if (this.isEditMode) {
|
||||
await this.$refs.formRef.validate();
|
||||
const attachmentOssIds = this.attachmentList
|
||||
.filter((file) => file.status === 'success' && file.ossId)
|
||||
.map((file) => file.ossId);
|
||||
this.formData.attachmentOssIds = attachmentOssIds.join(',');
|
||||
this.$emit('confirm', { ...this.formData });
|
||||
return;
|
||||
}
|
||||
|
||||
this.$emit('confirm', { ...this.formData });
|
||||
if (!this.formData.disciplineDate) {
|
||||
uni.showToast({ title: '请选择违纪日期', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
if (this.violationItems.length === 0) {
|
||||
uni.showToast({ title: '请至少选择一个违纪类型', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
|
||||
for (const item of this.violationItems) {
|
||||
const min = this.getViolationMin(item);
|
||||
const max = this.getViolationMax(item);
|
||||
const score = parseFloat(item.scoreDeduction);
|
||||
if (isNaN(score)) {
|
||||
uni.showToast({
|
||||
title: `请填写「${item.disciplineTypeName}」扣分`,
|
||||
icon: 'none',
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (score < min || score > max) {
|
||||
uni.showToast({
|
||||
title: `「${item.disciplineTypeName}」须在${min}-${max}分`,
|
||||
icon: 'none',
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const sharedFields = this.buildSharedViolationFields();
|
||||
const records = this.violationItems.map((item) => ({
|
||||
...sharedFields,
|
||||
disciplineTypeId: item.disciplineTypeId,
|
||||
disciplineTypeName: item.disciplineTypeName,
|
||||
scoreDeduction: item.scoreDeduction,
|
||||
}));
|
||||
this.$emit('confirm', records);
|
||||
} catch (error) {
|
||||
console.error('提交失败:', error);
|
||||
uni.showToast({ title: '请检查表单填写是否完整', icon: 'none' });
|
||||
@@ -1915,6 +2157,9 @@ export default {
|
||||
this.currentTypeMaxScore = 100;
|
||||
this.disciplineDateDisplay = dayjs().format('YYYY-MM-DD');
|
||||
this.disciplineDateValue = Number(new Date());
|
||||
this.violationItems = [];
|
||||
this.tempSelectedTypeIds = [];
|
||||
this.showViolationTypePopup = false;
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -2200,4 +2445,76 @@ export default {
|
||||
padding-bottom: 40rpx;
|
||||
border-top: 2rpx solid #f0f0f0;
|
||||
}
|
||||
|
||||
.violation-item-list {
|
||||
margin: 0 0 24rpx;
|
||||
padding: 20rpx;
|
||||
background: #f8f9fb;
|
||||
border-radius: 12rpx;
|
||||
}
|
||||
|
||||
.violation-item-row + .violation-item-row {
|
||||
margin-top: 16rpx;
|
||||
padding-top: 16rpx;
|
||||
border-top: 1rpx dashed #e4e7ed;
|
||||
}
|
||||
|
||||
.violation-item-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 12rpx;
|
||||
}
|
||||
|
||||
.violation-item-name {
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
color: #303133;
|
||||
min-width: 160rpx;
|
||||
}
|
||||
|
||||
.score-range {
|
||||
font-size: 22rpx;
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
.violation-item-total {
|
||||
margin-top: 16rpx;
|
||||
padding-top: 16rpx;
|
||||
border-top: 1rpx solid #e4e7ed;
|
||||
font-size: 26rpx;
|
||||
color: #606266;
|
||||
}
|
||||
|
||||
.violation-item-total .total-value {
|
||||
color: #f56c6c;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.violation-type-popup {
|
||||
padding: 24rpx 30rpx 40rpx;
|
||||
max-height: 70vh;
|
||||
}
|
||||
|
||||
.violation-type-popup-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.violation-type-popup-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
.violation-type-popup-action {
|
||||
font-size: 28rpx;
|
||||
color: #3c9cff;
|
||||
}
|
||||
|
||||
.violation-type-popup-body {
|
||||
max-height: 55vh;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,10 @@
|
||||
/** 雪花ID统一转字符串,避免 JS Number 精度丢失导致 Map/接口匹配失败 */
|
||||
export function normalizeStudentId(studentId) {
|
||||
if (studentId === null || studentId === undefined || studentId === '') {
|
||||
return '';
|
||||
}
|
||||
return String(studentId);
|
||||
}
|
||||
|
||||
/** 通用雪花ID归一化(检查ID、学生ID等) */
|
||||
export const normalizeId = normalizeStudentId;
|
||||
@@ -0,0 +1,7 @@
|
||||
/** 计算学生有效违纪记录的扣分合计(已撤销 status=2 不计入) */
|
||||
export function calcStudentViolationScoreTotal(violations) {
|
||||
const total = (violations || [])
|
||||
.filter((item) => item && item.status !== '2')
|
||||
.reduce((sum, item) => sum + (parseFloat(String(item.scoreDeduction ?? 0)) || 0), 0);
|
||||
return parseFloat(total.toFixed(2));
|
||||
}
|
||||
@@ -270,6 +270,7 @@
|
||||
import { request } from '@/common/request.js';
|
||||
import { getDict, getDictLabel, getDictTagType } from '@/common/dict.js';
|
||||
import SingleRoomCheck from './components/SingleRoomCheck.vue';
|
||||
import { normalizeId } from './utils/studentId.js';
|
||||
|
||||
export default {
|
||||
name: 'DormHygieneCheck',
|
||||
@@ -544,7 +545,7 @@ export default {
|
||||
|
||||
// 加载检查记录详情
|
||||
const res = await request({
|
||||
url: `/dormHygieneCheck/dormHygieneCheck/${item.id}`,
|
||||
url: `/dormHygieneCheck/dormHygieneCheck/${normalizeId(item.id)}`,
|
||||
method: 'GET',
|
||||
});
|
||||
|
||||
@@ -552,7 +553,10 @@ export default {
|
||||
|
||||
if (res && res.code === 200 && res.data) {
|
||||
this.currentViewItem = item;
|
||||
this.currentViewData = res.data;
|
||||
this.currentViewData = {
|
||||
...res.data,
|
||||
id: normalizeId(res.data.id),
|
||||
};
|
||||
|
||||
// 构造房间信息
|
||||
this.currentViewRoomInfo = {
|
||||
@@ -605,7 +609,7 @@ export default {
|
||||
|
||||
// 加载检查记录详情
|
||||
const res = await request({
|
||||
url: `/dormHygieneCheck/dormHygieneCheck/${item.id}`,
|
||||
url: `/dormHygieneCheck/dormHygieneCheck/${normalizeId(item.id)}`,
|
||||
method: 'GET',
|
||||
});
|
||||
|
||||
@@ -613,7 +617,10 @@ export default {
|
||||
|
||||
if (res && res.code === 200 && res.data) {
|
||||
this.currentEditItem = item;
|
||||
this.currentEditData = res.data;
|
||||
this.currentEditData = {
|
||||
...res.data,
|
||||
id: normalizeId(res.data.id),
|
||||
};
|
||||
|
||||
// 构造房间信息
|
||||
this.currentRoomInfo = {
|
||||
@@ -708,7 +715,7 @@ export default {
|
||||
uni.showLoading({ title: '删除中...' });
|
||||
|
||||
const res = await request({
|
||||
url: `/dormHygieneCheck/dormHygieneCheck/${item.id}`,
|
||||
url: `/dormHygieneCheck/dormHygieneCheck/${normalizeId(item.id)}`,
|
||||
method: 'DELETE',
|
||||
});
|
||||
|
||||
|
||||
@@ -1309,6 +1309,52 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
normalizeGender(gender) {
|
||||
if (gender === null || gender === undefined || gender === '') {
|
||||
return undefined;
|
||||
}
|
||||
const value = String(gender);
|
||||
if (value === '0' || value === '男') {
|
||||
return '0';
|
||||
}
|
||||
if (value === '1' || value === '女') {
|
||||
return '1';
|
||||
}
|
||||
return value;
|
||||
},
|
||||
|
||||
mergeGenderFromRoomStudents(students, roomStudents) {
|
||||
if (!students.length || !roomStudents.length) {
|
||||
return students;
|
||||
}
|
||||
|
||||
const genderByStudentId = {};
|
||||
const genderByStudentNo = {};
|
||||
roomStudents.forEach((student) => {
|
||||
const gender = this.normalizeGender(student.gender);
|
||||
if (!gender) {
|
||||
return;
|
||||
}
|
||||
if (student.studentId !== undefined && student.studentId !== null) {
|
||||
genderByStudentId[String(student.studentId)] = gender;
|
||||
}
|
||||
if (student.studentNo) {
|
||||
genderByStudentNo[student.studentNo] = gender;
|
||||
}
|
||||
});
|
||||
|
||||
return students.map((student) => {
|
||||
if (this.normalizeGender(student.gender)) {
|
||||
return { ...student, gender: this.normalizeGender(student.gender) };
|
||||
}
|
||||
const gender =
|
||||
(student.studentId !== undefined && student.studentId !== null
|
||||
? genderByStudentId[String(student.studentId)]
|
||||
: undefined) || (student.studentNo ? genderByStudentNo[student.studentNo] : undefined);
|
||||
return gender ? { ...student, gender } : student;
|
||||
});
|
||||
},
|
||||
|
||||
// 加载房间学生信息
|
||||
async loadRoomStudents() {
|
||||
if (!this.formData.roomId) {
|
||||
@@ -1329,8 +1375,9 @@ export default {
|
||||
studentScoresRes.data &&
|
||||
studentScoresRes.data.length > 0
|
||||
) {
|
||||
this.roomStudents = studentScoresRes.data.map((item) => ({
|
||||
let students = studentScoresRes.data.map((item) => ({
|
||||
...item,
|
||||
gender: this.normalizeGender(item.gender),
|
||||
student_score: item.studentScore || null,
|
||||
attendance_status: item.attendanceStatus || '1',
|
||||
student_grade: item.studentGrade || 'A',
|
||||
@@ -1339,6 +1386,19 @@ export default {
|
||||
bedCode: item.bedCode,
|
||||
bedName: item.bedCode,
|
||||
}));
|
||||
|
||||
if (students.some((student) => !student.gender) && this.formData.roomId) {
|
||||
const roomRes = await request({
|
||||
url: '/dormBedStudent/dormBedStudent/getRoomStudents',
|
||||
method: 'GET',
|
||||
params: { roomId: this.formData.roomId },
|
||||
});
|
||||
if (roomRes.code === 200 && roomRes.data) {
|
||||
students = this.mergeGenderFromRoomStudents(students, roomRes.data);
|
||||
}
|
||||
}
|
||||
|
||||
this.roomStudents = students;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user