Merge branch 'developer' of ssh://code.cyweb.top:30033/scj/zhxy/v3/cloud-ui into developer
This commit is contained in:
11
src/api/safety/clouddeviceposition.ts
Normal file
11
src/api/safety/clouddeviceposition.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import request from '/@/utils/request';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取所有设备位置(校门)列表
|
||||||
|
*/
|
||||||
|
export const listAll = () => {
|
||||||
|
return request({
|
||||||
|
url: '/safety/clouddeviceposition/listAll',
|
||||||
|
method: 'get',
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -78,3 +78,16 @@ export function getRank(query?: Object) {
|
|||||||
params: query,
|
params: query,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取日常巡检学年学期统计
|
||||||
|
* @param {Object} [query] - 查询参数(schoolYear, schoolTerm, deptCode)
|
||||||
|
* @returns {Promise} 请求的 Promise 对象。
|
||||||
|
*/
|
||||||
|
export function dailySummaryByYearTerm(query?: Object) {
|
||||||
|
return request({
|
||||||
|
url: '/stuwork/classcheckdaily/dailySummaryByYearTerm',
|
||||||
|
method: 'get',
|
||||||
|
params: query,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
@@ -64,3 +64,16 @@ export function putObj(obj?: Object) {
|
|||||||
data: obj,
|
data: obj,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取日常行为学年学期统计
|
||||||
|
* @param {Object} [query] - 查询参数(schoolYear, schoolTerm, deptCode)
|
||||||
|
* @returns {Promise} 请求的 Promise 对象。
|
||||||
|
*/
|
||||||
|
export function dailySummaryByYearTerm(query?: Object) {
|
||||||
|
return request({
|
||||||
|
url: '/stuwork/classhygienedaily/dailySummaryByYearTerm',
|
||||||
|
method: 'get',
|
||||||
|
params: query,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
@@ -72,3 +72,32 @@ export const exportData = (query?: any) => {
|
|||||||
responseType: 'blob',
|
responseType: 'blob',
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 审批留宿申请
|
||||||
|
* @param id 申请ID
|
||||||
|
* @param auditStatus 审核状态(2-通过,3-不通过)
|
||||||
|
* @param auditRemark 审核备注
|
||||||
|
*/
|
||||||
|
export const auditApply = (id: string, auditStatus: string, auditRemark?: string) => {
|
||||||
|
return request({
|
||||||
|
url: '/stuwork/dormliveapply/audit',
|
||||||
|
method: 'post',
|
||||||
|
params: { id, auditStatus, auditRemark },
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量审批留宿申请
|
||||||
|
* @param ids 申请ID列表
|
||||||
|
* @param auditStatus 审核状态(2-通过,3-不通过)
|
||||||
|
* @param auditRemark 审核备注
|
||||||
|
*/
|
||||||
|
export const batchAuditApply = (ids: string[], auditStatus: string, auditRemark?: string) => {
|
||||||
|
return request({
|
||||||
|
url: '/stuwork/dormliveapply/batchAudit',
|
||||||
|
method: 'post',
|
||||||
|
data: ids,
|
||||||
|
params: { auditStatus, auditRemark },
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|||||||
@@ -84,3 +84,15 @@ export const exportData = (query?: any) => {
|
|||||||
responseType: 'blob',
|
responseType: 'blob',
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据班号查询班级异动情况
|
||||||
|
* @param classCode 班号
|
||||||
|
*/
|
||||||
|
export const queryByClassCode = (classCode: string) => {
|
||||||
|
return request({
|
||||||
|
url: '/stuwork/stuturnover/queryByClassCode',
|
||||||
|
method: 'get',
|
||||||
|
params: { classCode },
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|||||||
@@ -24,8 +24,46 @@
|
|||||||
<el-descriptions-item label="门禁规则" :span="3">{{ detailData.ruleName || '-' }}</el-descriptions-item>
|
<el-descriptions-item label="门禁规则" :span="3">{{ detailData.ruleName || '-' }}</el-descriptions-item>
|
||||||
</el-descriptions>
|
</el-descriptions>
|
||||||
|
|
||||||
<!-- 详细信息 - 使用 tabs 展示六个接口的数据 -->
|
<!-- 详细信息 - 使用 tabs 展示 -->
|
||||||
<el-tabs v-model="activeTab" type="border-card">
|
<el-tabs v-model="activeTab" type="border-card">
|
||||||
|
<!-- 学生概况 -->
|
||||||
|
<el-tab-pane label="学生概况" name="studentOverview">
|
||||||
|
<div v-loading="studentOverviewLoading">
|
||||||
|
<el-descriptions :column="4" border>
|
||||||
|
<el-descriptions-item label="教室安排">
|
||||||
|
<span>{{ classroomInfo || '-' }}</span>
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="班主任姓名">
|
||||||
|
<span>{{ studentOverviewData?.classMasterName || detailData?.teacherRealName || '-' }}</span>
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="在校人数(注册)">
|
||||||
|
<el-tag type="primary" size="small">{{ studentOverviewData?.stuNum || 0 }}</el-tag>
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="在校人数(借读)">
|
||||||
|
<el-tag type="warning" size="small">{{ studentOverviewData?.borrowingStuNum || 0 }}</el-tag>
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="男(在校)">
|
||||||
|
<el-tag type="success" size="small">{{ studentOverviewData?.manStuNum || 0 }}</el-tag>
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="女(在校)">
|
||||||
|
<el-tag type="danger" size="small">{{ studentOverviewData?.girlStuNum || 0 }}</el-tag>
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="借读">
|
||||||
|
<el-tag type="info" size="small">{{ studentOverviewData?.borrowingStuNum || 0 }}</el-tag>
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="住宿人数">
|
||||||
|
<el-tag type="primary" size="small">{{ dormOverviewData?.stuNum || 0 }}</el-tag>
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="住宿(男)">
|
||||||
|
<el-tag type="success" size="small">{{ dormOverviewData?.manStuNum || 0 }}</el-tag>
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="住宿(女)">
|
||||||
|
<el-tag type="danger" size="small">{{ dormOverviewData?.girlStuNum || 0 }}</el-tag>
|
||||||
|
</el-descriptions-item>
|
||||||
|
</el-descriptions>
|
||||||
|
</div>
|
||||||
|
</el-tab-pane>
|
||||||
|
|
||||||
<el-tab-pane label="班级荣誉" name="honor">
|
<el-tab-pane label="班级荣誉" name="honor">
|
||||||
<el-table :data="honorList" border style="width: 100%" v-loading="honorLoading">
|
<el-table :data="honorList" border style="width: 100%" v-loading="honorLoading">
|
||||||
<el-table-column type="index" label="序号" width="60" align="center" />
|
<el-table-column type="index" label="序号" width="60" align="center" />
|
||||||
@@ -59,21 +97,6 @@
|
|||||||
<div v-if="classroomList.length === 0 && !classroomLoading" style="text-align: center; padding: 20px; color: #909399">暂无数据</div>
|
<div v-if="classroomList.length === 0 && !classroomLoading" style="text-align: center; padding: 20px; color: #909399">暂无数据</div>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
|
|
||||||
<el-tab-pane label="学生数量" name="studentNum">
|
|
||||||
<el-table :data="studentNumList" border style="width: 100%" v-loading="studentNumLoading">
|
|
||||||
<el-table-column type="index" label="序号" width="60" align="center" />
|
|
||||||
<el-table-column prop="stuNo" label="学号" show-overflow-tooltip />
|
|
||||||
<el-table-column prop="realName" label="姓名" show-overflow-tooltip />
|
|
||||||
<el-table-column prop="sex" label="性别" width="80" align="center">
|
|
||||||
<template #default="scope">
|
|
||||||
<span>{{ scope.row.sex === '1' ? '男' : scope.row.sex === '0' ? '女' : '-' }}</span>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column prop="phone" label="电话" show-overflow-tooltip />
|
|
||||||
</el-table>
|
|
||||||
<div v-if="studentNumList.length === 0 && !studentNumLoading" style="text-align: center; padding: 20px; color: #909399">暂无数据</div>
|
|
||||||
</el-tab-pane>
|
|
||||||
|
|
||||||
<el-tab-pane label="宿舍学生" name="dormStudent">
|
<el-tab-pane label="宿舍学生" name="dormStudent">
|
||||||
<el-table :data="dormStudentList" border style="width: 100%" v-loading="dormStudentLoading">
|
<el-table :data="dormStudentList" border style="width: 100%" v-loading="dormStudentLoading">
|
||||||
<el-table-column type="index" label="序号" width="60" align="center" />
|
<el-table-column type="index" label="序号" width="60" align="center" />
|
||||||
@@ -85,16 +108,30 @@
|
|||||||
<div v-if="dormStudentList.length === 0 && !dormStudentLoading" style="text-align: center; padding: 20px; color: #909399">暂无数据</div>
|
<div v-if="dormStudentList.length === 0 && !dormStudentLoading" style="text-align: center; padding: 20px; color: #909399">暂无数据</div>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
|
|
||||||
<el-tab-pane label="惩罚记录" name="punish">
|
<!-- 处罚处分 -->
|
||||||
<el-table :data="punishList" border style="width: 100%" v-loading="punishLoading">
|
<el-tab-pane label="处罚处分" name="punish">
|
||||||
<el-table-column type="index" label="序号" width="60" align="center" />
|
<div v-loading="punishLoading">
|
||||||
<el-table-column prop="stuNo" label="学号" show-overflow-tooltip />
|
<el-descriptions :column="3" border>
|
||||||
<el-table-column prop="realName" label="姓名" show-overflow-tooltip />
|
<el-descriptions-item label="警告">
|
||||||
<el-table-column prop="punishType" label="惩罚类型" show-overflow-tooltip />
|
<el-tag type="warning" size="small">{{ punishOverviewData?.warningNum || 0 }}人</el-tag>
|
||||||
<el-table-column prop="punishReason" label="惩罚原因" show-overflow-tooltip />
|
</el-descriptions-item>
|
||||||
<el-table-column prop="createTime" label="惩罚时间" width="120" />
|
<el-descriptions-item label="严重警告">
|
||||||
</el-table>
|
<el-tag type="danger" size="small">{{ punishOverviewData?.seriousWarningNum || 0 }}人</el-tag>
|
||||||
<div v-if="punishList.length === 0 && !punishLoading" style="text-align: center; padding: 20px; color: #909399">暂无数据</div>
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="记过">
|
||||||
|
<el-tag type="danger" size="small" effect="plain">{{ punishOverviewData?.recordDemeritNum || 0 }}人</el-tag>
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="留校察看">
|
||||||
|
<el-tag type="danger" size="small">{{ punishOverviewData?.detentionNum || 0 }}人</el-tag>
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="责令退学">
|
||||||
|
<el-tag type="danger" size="small" effect="plain">{{ punishOverviewData?.dropOutNum || 0 }}人</el-tag>
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="开除学籍">
|
||||||
|
<el-tag type="danger" size="small">{{ punishOverviewData?.expelSchoolNum || 0 }}人</el-tag>
|
||||||
|
</el-descriptions-item>
|
||||||
|
</el-descriptions>
|
||||||
|
</div>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
|
|
||||||
<el-tab-pane label="顶岗计划" name="practicePlan">
|
<el-tab-pane label="顶岗计划" name="practicePlan">
|
||||||
@@ -119,6 +156,24 @@
|
|||||||
</el-table>
|
</el-table>
|
||||||
<div v-if="practicePlanList.length === 0 && !practicePlanLoading" style="text-align: center; padding: 20px; color: #909399">暂无数据</div>
|
<div v-if="practicePlanList.length === 0 && !practicePlanLoading" style="text-align: center; padding: 20px; color: #909399">暂无数据</div>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
|
|
||||||
|
<el-tab-pane label="班级异动" name="turnover">
|
||||||
|
<el-table :data="turnoverList" border style="width: 100%" v-loading="turnoverLoading">
|
||||||
|
<el-table-column type="index" label="序号" width="60" align="center" />
|
||||||
|
<el-table-column prop="realName" label="学生姓名" width="120" />
|
||||||
|
<el-table-column prop="stuNo" label="学号" width="120" />
|
||||||
|
<el-table-column prop="oldClassNo" label="原班级" width="120" />
|
||||||
|
<el-table-column prop="newClassNo" label="现班级" width="120" />
|
||||||
|
<el-table-column prop="turnoverType" label="异动类型" width="100">
|
||||||
|
<template #default="scope">
|
||||||
|
<dict-tag :options="turnover_type" :value="scope.row.turnoverType"></dict-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="turnoverDate" label="异动日期" width="120" />
|
||||||
|
<el-table-column prop="remarks" label="异动原因" show-overflow-tooltip />
|
||||||
|
</el-table>
|
||||||
|
<div v-if="turnoverList.length === 0 && !turnoverLoading" style="text-align: center; padding: 20px; color: #909399">暂无数据</div>
|
||||||
|
</el-tab-pane>
|
||||||
</el-tabs>
|
</el-tabs>
|
||||||
</div>
|
</div>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
@@ -130,7 +185,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts" name="BasicClassDetail">
|
<script setup lang="ts" name="BasicClassDetail">
|
||||||
import { ref } from 'vue';
|
import { ref, computed } from 'vue';
|
||||||
import { queryClassHonorByClassCode } from '/@/api/stuwork/classhonor';
|
import { queryClassHonorByClassCode } from '/@/api/stuwork/classhonor';
|
||||||
import { queryDataByClassCode } from '/@/api/stuwork/classpublicity';
|
import { queryDataByClassCode } from '/@/api/stuwork/classpublicity';
|
||||||
import { getClassRoomByClassCode } from '/@/api/stuwork/teachclassroomassign';
|
import { getClassRoomByClassCode } from '/@/api/stuwork/teachclassroomassign';
|
||||||
@@ -138,60 +193,120 @@ import { queryStuNumByClassCode } from '/@/api/basic/basicstudent';
|
|||||||
import { fearchStuNumByClassCode } from '/@/api/stuwork/dormroomstudent';
|
import { fearchStuNumByClassCode } from '/@/api/stuwork/dormroomstudent';
|
||||||
import { queryPunlishNumByClass } from '/@/api/stuwork/stupunlish';
|
import { queryPunlishNumByClass } from '/@/api/stuwork/stupunlish';
|
||||||
import { fetchList as fetchPracticePlanList } from '/@/api/basic/basicpracticeclassplan';
|
import { fetchList as fetchPracticePlanList } from '/@/api/basic/basicpracticeclassplan';
|
||||||
|
import { queryByClassCode } from '/@/api/stuwork/stuturnover';
|
||||||
|
import { useDict } from "/@/hooks/dict";
|
||||||
|
|
||||||
// 定义变量内容
|
// 定义变量内容
|
||||||
const visible = ref(false);
|
const visible = ref(false);
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
const detailData = ref<any>(null);
|
const detailData = ref<any>(null);
|
||||||
const activeTab = ref('honor');
|
const activeTab = ref('studentOverview');
|
||||||
|
|
||||||
// 六个接口的数据
|
// 各接口的数据
|
||||||
const honorList = ref<any[]>([]);
|
const honorList = ref<any[]>([]);
|
||||||
const honorLoading = ref(false);
|
const honorLoading = ref(false);
|
||||||
const publicityList = ref<any[]>([]);
|
const publicityList = ref<any[]>([]);
|
||||||
const publicityLoading = ref(false);
|
const publicityLoading = ref(false);
|
||||||
const classroomList = ref<any[]>([]);
|
const classroomList = ref<any[]>([]);
|
||||||
const classroomLoading = ref(false);
|
const classroomLoading = ref(false);
|
||||||
const studentNumList = ref<any[]>([]);
|
|
||||||
const studentNumLoading = ref(false);
|
|
||||||
const dormStudentList = ref<any[]>([]);
|
const dormStudentList = ref<any[]>([]);
|
||||||
const dormStudentLoading = ref(false);
|
const dormStudentLoading = ref(false);
|
||||||
const punishList = ref<any[]>([]);
|
|
||||||
|
// 学生概况数据
|
||||||
|
const studentOverviewData = ref<any>(null);
|
||||||
|
const dormOverviewData = ref<any>(null);
|
||||||
|
const studentOverviewLoading = ref(false);
|
||||||
|
|
||||||
|
// 处罚处分数据
|
||||||
|
const punishOverviewData = ref<any>(null);
|
||||||
const punishLoading = ref(false);
|
const punishLoading = ref(false);
|
||||||
|
|
||||||
// 顶岗计划数据
|
// 顶岗计划数据
|
||||||
const practicePlanList = ref<any[]>([]);
|
const practicePlanList = ref<any[]>([]);
|
||||||
const practicePlanLoading = ref(false);
|
const practicePlanLoading = ref(false);
|
||||||
|
|
||||||
|
// 班级异动数据
|
||||||
|
const turnoverList = ref<any[]>([]);
|
||||||
|
const turnoverLoading = ref(false);
|
||||||
|
|
||||||
|
const { turnover_type } = useDict('turnover_type');
|
||||||
|
|
||||||
|
// 教室安排信息(拼接显示)
|
||||||
|
const classroomInfo = computed(() => {
|
||||||
|
if (classroomList.value && classroomList.value.length > 0) {
|
||||||
|
return classroomList.value.map((item: any) => {
|
||||||
|
const parts = [];
|
||||||
|
if (item.buildingNo) parts.push(item.buildingNo);
|
||||||
|
if (item.roomNo) parts.push(item.roomNo);
|
||||||
|
if (item.roomName) parts.push(item.roomName);
|
||||||
|
return parts.join('-');
|
||||||
|
}).join('、');
|
||||||
|
}
|
||||||
|
return '-';
|
||||||
|
});
|
||||||
|
|
||||||
// 打开弹窗
|
// 打开弹窗
|
||||||
const openDialog = async (rowData: any) => {
|
const openDialog = async (rowData: any) => {
|
||||||
visible.value = true;
|
visible.value = true;
|
||||||
detailData.value = rowData || null;
|
detailData.value = rowData || null;
|
||||||
|
activeTab.value = 'studentOverview';
|
||||||
|
|
||||||
// 如果有班级代码,加载六个接口的数据
|
// 如果有班级代码,加载所有接口的数据
|
||||||
if (rowData?.classCode) {
|
if (rowData?.classCode) {
|
||||||
await loadAllData(rowData.classCode);
|
await loadAllData(rowData.classCode);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 加载所有六个接口的数据
|
// 加载所有接口的数据
|
||||||
const loadAllData = async (classCode: string) => {
|
const loadAllData = async (classCode: string) => {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
|
|
||||||
// 并行加载所有接口数据
|
// 并行加载所有接口数据
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
|
loadStudentOverviewData(classCode),
|
||||||
|
loadDormOverviewData(classCode),
|
||||||
|
loadClassroomData(classCode),
|
||||||
loadHonorData(classCode),
|
loadHonorData(classCode),
|
||||||
loadPublicityData(classCode),
|
loadPublicityData(classCode),
|
||||||
loadClassroomData(classCode),
|
|
||||||
loadStudentNumData(classCode),
|
|
||||||
loadDormStudentData(classCode),
|
loadDormStudentData(classCode),
|
||||||
loadPunishData(classCode),
|
loadPunishData(classCode),
|
||||||
loadPracticePlanData(classCode),
|
loadPracticePlanData(classCode),
|
||||||
|
loadTurnoverData(classCode),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 加载学生概况数据
|
||||||
|
const loadStudentOverviewData = async (classCode: string) => {
|
||||||
|
studentOverviewLoading.value = true;
|
||||||
|
try {
|
||||||
|
const res = await queryStuNumByClassCode({ classCode });
|
||||||
|
if (res.data) {
|
||||||
|
// 返回的是对象,直接赋值
|
||||||
|
studentOverviewData.value = res.data;
|
||||||
|
}
|
||||||
|
} catch (err: any) {
|
||||||
|
console.error('获取学生概况失败', err);
|
||||||
|
studentOverviewData.value = null;
|
||||||
|
} finally {
|
||||||
|
studentOverviewLoading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 加载住宿概况数据
|
||||||
|
const loadDormOverviewData = async (classCode: string) => {
|
||||||
|
try {
|
||||||
|
const res = await fearchStuNumByClassCode(classCode);
|
||||||
|
if (res.data) {
|
||||||
|
dormOverviewData.value = res.data;
|
||||||
|
}
|
||||||
|
} catch (err: any) {
|
||||||
|
console.error('获取住宿概况失败', err);
|
||||||
|
dormOverviewData.value = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// 加载班级荣誉数据
|
// 加载班级荣誉数据
|
||||||
const loadHonorData = async (classCode: string) => {
|
const loadHonorData = async (classCode: string) => {
|
||||||
honorLoading.value = true;
|
honorLoading.value = true;
|
||||||
@@ -240,22 +355,6 @@ const loadClassroomData = async (classCode: string) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 加载学生数量数据
|
|
||||||
const loadStudentNumData = async (classCode: string) => {
|
|
||||||
studentNumLoading.value = true;
|
|
||||||
try {
|
|
||||||
const res = await queryStuNumByClassCode({ classCode });
|
|
||||||
if (res.data) {
|
|
||||||
studentNumList.value = Array.isArray(res.data) ? res.data : [];
|
|
||||||
}
|
|
||||||
} catch (err: any) {
|
|
||||||
console.error('获取学生数量失败', err);
|
|
||||||
studentNumList.value = [];
|
|
||||||
} finally {
|
|
||||||
studentNumLoading.value = false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 加载宿舍学生数据
|
// 加载宿舍学生数据
|
||||||
const loadDormStudentData = async (classCode: string) => {
|
const loadDormStudentData = async (classCode: string) => {
|
||||||
dormStudentLoading.value = true;
|
dormStudentLoading.value = true;
|
||||||
@@ -272,17 +371,18 @@ const loadDormStudentData = async (classCode: string) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 加载惩罚记录数据
|
// 加载处罚处分数据
|
||||||
const loadPunishData = async (classCode: string) => {
|
const loadPunishData = async (classCode: string) => {
|
||||||
punishLoading.value = true;
|
punishLoading.value = true;
|
||||||
try {
|
try {
|
||||||
const res = await queryPunlishNumByClass(classCode);
|
const res = await queryPunlishNumByClass(classCode);
|
||||||
if (res.data) {
|
if (res.data) {
|
||||||
punishList.value = Array.isArray(res.data) ? res.data : [];
|
// 返回的是对象,包含各处罚类型的人数
|
||||||
|
punishOverviewData.value = res.data;
|
||||||
}
|
}
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
console.error('获取惩罚记录失败', err);
|
console.error('获取处罚处分失败', err);
|
||||||
punishList.value = [];
|
punishOverviewData.value = null;
|
||||||
} finally {
|
} finally {
|
||||||
punishLoading.value = false;
|
punishLoading.value = false;
|
||||||
}
|
}
|
||||||
@@ -308,6 +408,24 @@ const loadPracticePlanData = async (classCode: string) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 加载班级异动数据
|
||||||
|
const loadTurnoverData = async (classCode: string) => {
|
||||||
|
turnoverLoading.value = true;
|
||||||
|
try {
|
||||||
|
const res = await queryByClassCode(classCode);
|
||||||
|
if (res.data) {
|
||||||
|
turnoverList.value = Array.isArray(res.data) ? res.data : [];
|
||||||
|
} else {
|
||||||
|
turnoverList.value = [];
|
||||||
|
}
|
||||||
|
} catch (err: any) {
|
||||||
|
console.error('获取班级异动失败', err);
|
||||||
|
turnoverList.value = [];
|
||||||
|
} finally {
|
||||||
|
turnoverLoading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// 获取顶岗计划状态标签
|
// 获取顶岗计划状态标签
|
||||||
const getPracticeStatusLabel = (status: string) => {
|
const getPracticeStatusLabel = (status: string) => {
|
||||||
const statusMap: Record<string, string> = {
|
const statusMap: Record<string, string> = {
|
||||||
|
|||||||
@@ -202,7 +202,7 @@
|
|||||||
<el-divider content-position="left">家庭基本信息</el-divider>
|
<el-divider content-position="left">家庭基本信息</el-divider>
|
||||||
<el-row :gutter="20">
|
<el-row :gutter="20">
|
||||||
<el-col :span="24">
|
<el-col :span="24">
|
||||||
<el-form-item label="户口详细地址" prop="householdAddress" :rules="formRules.householdAddress">
|
<el-form-item label="户口详细地址" prop="householdAddress" :rules="formRules.homeHouseholdAddress">
|
||||||
<el-input v-model="homeInfo.householdAddress" placeholder="请输入户口详细地址" clearable />
|
<el-input v-model="homeInfo.householdAddress" placeholder="请输入户口详细地址" clearable />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
@@ -734,7 +734,7 @@ const formRules = {
|
|||||||
// 教育经历必填
|
// 教育经历必填
|
||||||
education: [{ required: true, message: '请选择入学前文化程度', trigger: 'change' }],
|
education: [{ required: true, message: '请选择入学前文化程度', trigger: 'change' }],
|
||||||
// 家庭信息必填
|
// 家庭信息必填
|
||||||
householdAddress: [{ required: true, message: '请输入户口详细地址', trigger: 'blur' }],
|
homeHouseholdAddress: [{ required: true, message: '请输入户口详细地址', trigger: 'blur' }],
|
||||||
householdProperties: [{ required: true, message: '请选择户口性质', trigger: 'change' }],
|
householdProperties: [{ required: true, message: '请选择户口性质', trigger: 'change' }],
|
||||||
isTemp: [{ required: true, message: '请选择是否租住', trigger: 'change' }],
|
isTemp: [{ required: true, message: '请选择是否租住', trigger: 'change' }],
|
||||||
liveAddress: [{ required: true, message: '请输入居住详细地址', trigger: 'blur' }],
|
liveAddress: [{ required: true, message: '请输入居住详细地址', trigger: 'blur' }],
|
||||||
|
|||||||
@@ -37,7 +37,6 @@ const state: BasicTableProps = reactive<BasicTableProps>({
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
createdIsNeed: false,
|
|
||||||
});
|
});
|
||||||
const visible = ref(false);
|
const visible = ref(false);
|
||||||
|
|
||||||
|
|||||||
@@ -98,11 +98,22 @@
|
|||||||
{{ formatStudentStatus(scope.row.stuStatus) }}
|
{{ formatStudentStatus(scope.row.stuStatus) }}
|
||||||
</el-tag>
|
</el-tag>
|
||||||
</template>
|
</template>
|
||||||
<!-- 考勤类型列特殊模板 -->
|
<!-- 考勤类型列 - 可编辑下拉框 -->
|
||||||
<template v-else-if="col.prop === 'attendanceType'" #default="scope">
|
<template v-else-if="col.prop === 'attendanceType'" #default="scope">
|
||||||
<el-tag size="small" type="warning" effect="plain" round>
|
<el-select
|
||||||
{{ formatAttendanceType(scope.row.attendanceType) }}
|
v-model="scope.row.attendanceType"
|
||||||
</el-tag>
|
placeholder="请选择"
|
||||||
|
size="small"
|
||||||
|
style="width: 100%"
|
||||||
|
:disabled="!scope.row.canChangeAttendType"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="item in attendanceTypeList"
|
||||||
|
:key="item.value"
|
||||||
|
:label="item.label"
|
||||||
|
:value="item.value"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
</template>
|
</template>
|
||||||
<!-- 是否住宿列特殊模板 -->
|
<!-- 是否住宿列特殊模板 -->
|
||||||
<template v-else-if="col.prop === 'isRoom'" #default="scope">
|
<template v-else-if="col.prop === 'isRoom'" #default="scope">
|
||||||
@@ -123,27 +134,72 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</template>
|
</template>
|
||||||
<el-table-column label="操作" width="100" align="center" fixed="right">
|
|
||||||
<template #header>
|
|
||||||
<el-icon><Setting /></el-icon>
|
|
||||||
<span style="margin-left: 4px">操作</span>
|
|
||||||
</template>
|
|
||||||
<template #default="scope">
|
|
||||||
<el-button icon="EditPen" link type="primary" @click="handleEdit(scope.row)"> 编辑 </el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<template #empty>
|
<template #empty>
|
||||||
<el-empty description="暂无数据" :image-size="120"> </el-empty>
|
<el-empty description="暂无数据" :image-size="120"> </el-empty>
|
||||||
</template>
|
</template>
|
||||||
</el-table>
|
</el-table>
|
||||||
|
|
||||||
|
<!-- 提交考勤区域 -->
|
||||||
|
<div class="submit-attendance" v-if="dataList.length > 0">
|
||||||
|
<el-divider />
|
||||||
|
<el-form :model="submitForm" label-width="100px" class="submit-form">
|
||||||
|
<el-row :gutter="24">
|
||||||
|
<el-col :span="8">
|
||||||
|
<el-form-item label="考勤照片">
|
||||||
|
<el-upload
|
||||||
|
class="photo-uploader"
|
||||||
|
:action="uploadUrl"
|
||||||
|
:show-file-list="false"
|
||||||
|
:on-success="handlePhotoSuccess"
|
||||||
|
:before-upload="beforePhotoUpload"
|
||||||
|
accept="image/*"
|
||||||
|
>
|
||||||
|
<el-image
|
||||||
|
v-if="submitForm.photo"
|
||||||
|
:src="submitForm.photo"
|
||||||
|
class="photo-preview"
|
||||||
|
fit="cover"
|
||||||
|
/>
|
||||||
|
<el-icon v-else class="photo-uploader-icon"><Plus /></el-icon>
|
||||||
|
</el-upload>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="8">
|
||||||
|
<el-form-item label="班级">
|
||||||
|
<el-input :value="selectedClassName" disabled />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="8">
|
||||||
|
<el-form-item label="点名类型">
|
||||||
|
<el-input :value="selectedOrderTypeName" disabled />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row>
|
||||||
|
<el-col :span="24" style="text-align: center">
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
size="large"
|
||||||
|
:loading="submitLoading"
|
||||||
|
:disabled="!canSubmit"
|
||||||
|
@click="handleSubmit"
|
||||||
|
>
|
||||||
|
<el-icon><CircleCheck /></el-icon>
|
||||||
|
提交考勤
|
||||||
|
</el-button>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
</el-card>
|
</el-card>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts" name="ClassAttendance">
|
<script setup lang="ts" name="ClassAttendance">
|
||||||
import { reactive, ref, onMounted } from 'vue';
|
import { reactive, ref, onMounted, computed } from 'vue';
|
||||||
import { fetchList, queryMyClassList } from '/@/api/stuwork/classattendance';
|
import { fetchList, saveDataBatch } from '/@/api/stuwork/classattendance';
|
||||||
|
import { getClassListByRole } from '/@/api/basic/basicclass';
|
||||||
import { getDicts } from '/@/api/admin/dict';
|
import { getDicts } from '/@/api/admin/dict';
|
||||||
import { useMessage } from '/@/hooks/message';
|
import { useMessage } from '/@/hooks/message';
|
||||||
import TableColumnControl from '/@/components/TableColumnControl/index.vue';
|
import TableColumnControl from '/@/components/TableColumnControl/index.vue';
|
||||||
@@ -156,11 +212,10 @@ import {
|
|||||||
Collection,
|
Collection,
|
||||||
House,
|
House,
|
||||||
Camera,
|
Camera,
|
||||||
Setting,
|
|
||||||
Menu,
|
Menu,
|
||||||
Search,
|
Search,
|
||||||
Document,
|
Document,
|
||||||
EditPen,
|
Plus,
|
||||||
} from '@element-plus/icons-vue';
|
} from '@element-plus/icons-vue';
|
||||||
import { useTableColumnControl } from '/@/hooks/tableColumn';
|
import { useTableColumnControl } from '/@/hooks/tableColumn';
|
||||||
import { defineAsyncComponent } from 'vue';
|
import { defineAsyncComponent } from 'vue';
|
||||||
@@ -171,12 +226,40 @@ const searchFormRef = ref();
|
|||||||
const columnControlRef = ref();
|
const columnControlRef = ref();
|
||||||
const showSearch = ref(true);
|
const showSearch = ref(true);
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
|
const submitLoading = ref(false);
|
||||||
const dataList = ref<any[]>([]);
|
const dataList = ref<any[]>([]);
|
||||||
const classList = ref<any[]>([]);
|
const classList = ref<any[]>([]);
|
||||||
const orderTypeList = ref<any[]>([]);
|
const orderTypeList = ref<any[]>([]);
|
||||||
const studentStatusList = ref<any[]>([]);
|
const studentStatusList = ref<any[]>([]);
|
||||||
const attendanceTypeList = ref<any[]>([]);
|
const attendanceTypeList = ref<any[]>([]);
|
||||||
|
|
||||||
|
// 上传地址
|
||||||
|
const uploadUrl = ref('/admin/sys-file/upload');
|
||||||
|
|
||||||
|
// 提交表单
|
||||||
|
const submitForm = reactive({
|
||||||
|
photo: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
// 选中的班级名称
|
||||||
|
const selectedClassName = computed(() => {
|
||||||
|
if (!searchForm.classCode) return '';
|
||||||
|
const classItem = classList.value.find((item: any) => item.classCode === searchForm.classCode);
|
||||||
|
return classItem ? classItem.classNo : '';
|
||||||
|
});
|
||||||
|
|
||||||
|
// 选中的点名类型名称
|
||||||
|
const selectedOrderTypeName = computed(() => {
|
||||||
|
if (!searchForm.orderType) return '';
|
||||||
|
const orderItem = orderTypeList.value.find((item: any) => item.value === searchForm.orderType);
|
||||||
|
return orderItem ? orderItem.label : '';
|
||||||
|
});
|
||||||
|
|
||||||
|
// 是否可以提交
|
||||||
|
const canSubmit = computed(() => {
|
||||||
|
return searchForm.classCode && searchForm.orderType && dataList.value.length > 0;
|
||||||
|
});
|
||||||
|
|
||||||
// 表格列配置
|
// 表格列配置
|
||||||
const tableColumns = [
|
const tableColumns = [
|
||||||
{ prop: 'stuNo', label: '学号', icon: CreditCard },
|
{ prop: 'stuNo', label: '学号', icon: CreditCard },
|
||||||
@@ -185,7 +268,7 @@ const tableColumns = [
|
|||||||
{ prop: 'parentPhoneA', label: '家长联系电话1', icon: Phone },
|
{ prop: 'parentPhoneA', label: '家长联系电话1', icon: Phone },
|
||||||
{ prop: 'parentPhoneB', label: '家长联系电话2', icon: Phone },
|
{ prop: 'parentPhoneB', label: '家长联系电话2', icon: Phone },
|
||||||
{ prop: 'stuStatus', label: '学生状态', icon: CircleCheck },
|
{ prop: 'stuStatus', label: '学生状态', icon: CircleCheck },
|
||||||
{ prop: 'attendanceType', label: '考勤类型', icon: Collection },
|
{ prop: 'attendanceType', label: '考勤类型', icon: Collection, width: 150 },
|
||||||
{ prop: 'isRoom', label: '是否住宿', icon: House },
|
{ prop: 'isRoom', label: '是否住宿', icon: House },
|
||||||
{ prop: 'roomNo', label: '宿舍号', icon: House },
|
{ prop: 'roomNo', label: '宿舍号', icon: House },
|
||||||
{ prop: 'isDeviceIn', label: '是否扫脸', icon: Camera },
|
{ prop: 'isDeviceIn', label: '是否扫脸', icon: Camera },
|
||||||
@@ -234,21 +317,21 @@ const handleReset = () => {
|
|||||||
searchFormRef.value?.resetFields();
|
searchFormRef.value?.resetFields();
|
||||||
searchForm.classCode = '';
|
searchForm.classCode = '';
|
||||||
searchForm.orderType = '';
|
searchForm.orderType = '';
|
||||||
getDataList();
|
dataList.value = [];
|
||||||
};
|
submitForm.photo = '';
|
||||||
|
|
||||||
// 编辑
|
|
||||||
const handleEdit = (row: any) => {
|
|
||||||
useMessage().info('编辑功能待实现');
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// 获取数据列表
|
// 获取数据列表
|
||||||
const getDataList = async () => {
|
const getDataList = async () => {
|
||||||
|
if (!searchForm.classCode || !searchForm.orderType) {
|
||||||
|
useMessage().warning('请先选择班级和点名类型');
|
||||||
|
return;
|
||||||
|
}
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
try {
|
try {
|
||||||
const params: any = {
|
const params: any = {
|
||||||
classCode: searchForm.classCode || undefined,
|
classCode: searchForm.classCode,
|
||||||
orderType: searchForm.orderType || undefined,
|
orderType: searchForm.orderType,
|
||||||
};
|
};
|
||||||
|
|
||||||
const res = await fetchList(params);
|
const res = await fetchList(params);
|
||||||
@@ -265,16 +348,92 @@ const getDataList = async () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 图片上传成功
|
||||||
|
const handlePhotoSuccess = (response: any) => {
|
||||||
|
if (response.code === 0 && response.data) {
|
||||||
|
submitForm.photo = response.data.url;
|
||||||
|
useMessage().success('图片上传成功');
|
||||||
|
} else {
|
||||||
|
useMessage().error(response.msg || '图片上传失败');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 图片上传前验证
|
||||||
|
const beforePhotoUpload = (file: File) => {
|
||||||
|
const isImage = file.type.startsWith('image/');
|
||||||
|
const isLt5M = file.size / 1024 / 1024 < 5;
|
||||||
|
|
||||||
|
if (!isImage) {
|
||||||
|
useMessage().error('只能上传图片文件!');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!isLt5M) {
|
||||||
|
useMessage().error('图片大小不能超过 5MB!');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 提交考勤
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
if (!searchForm.classCode || !searchForm.orderType) {
|
||||||
|
useMessage().warning('请先选择班级和点名类型');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
submitLoading.value = true;
|
||||||
|
try {
|
||||||
|
const submitData = {
|
||||||
|
photo: submitForm.photo,
|
||||||
|
classCode: searchForm.classCode,
|
||||||
|
orderType: searchForm.orderType,
|
||||||
|
list: dataList.value.map((item: any) => ({
|
||||||
|
stuNo: item.stuNo,
|
||||||
|
realName: item.realName,
|
||||||
|
attendanceType: item.attendanceType,
|
||||||
|
roomNo: item.roomNo,
|
||||||
|
stayDorm: item.stayDorm,
|
||||||
|
leaveReason: item.leaveReason,
|
||||||
|
leaveStartTime: item.leaveStartTime,
|
||||||
|
leaveEndTime: item.leaveEndTime,
|
||||||
|
bedNo: item.bedNo,
|
||||||
|
dealContent: item.dealContent,
|
||||||
|
parentPhoneA: item.parentPhoneA,
|
||||||
|
parentPhoneB: item.parentPhoneB,
|
||||||
|
phone: item.phone,
|
||||||
|
stuStatus: item.stuStatus,
|
||||||
|
isRoom: item.isRoom,
|
||||||
|
canChangeAttendType: item.canChangeAttendType,
|
||||||
|
classCode: item.classCode,
|
||||||
|
truancyNums: item.truancyNums,
|
||||||
|
outContactNums: item.outContactNums,
|
||||||
|
isDeviceIn: item.isDeviceIn,
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
|
||||||
|
await saveDataBatch(submitData);
|
||||||
|
useMessage().success('考勤提交成功');
|
||||||
|
// 清空数据
|
||||||
|
dataList.value = [];
|
||||||
|
submitForm.photo = '';
|
||||||
|
} catch (err: any) {
|
||||||
|
useMessage().error(err.msg || '考勤提交失败');
|
||||||
|
} finally {
|
||||||
|
submitLoading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// 获取班级列表
|
// 获取班级列表
|
||||||
const getClassListData = async () => {
|
const getClassListData = async () => {
|
||||||
try {
|
try {
|
||||||
const res = await queryMyClassList();
|
const res = await getClassListByRole();
|
||||||
if (res.data && Array.isArray(res.data)) {
|
if (res.data) {
|
||||||
classList.value = res.data;
|
classList.value = Array.isArray(res.data) ? res.data : [];
|
||||||
} else {
|
} else {
|
||||||
classList.value = [];
|
classList.value = [];
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
console.error('获取班级列表失败', err);
|
||||||
classList.value = [];
|
classList.value = [];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -282,13 +441,17 @@ const getClassListData = async () => {
|
|||||||
// 获取点名类型字典
|
// 获取点名类型字典
|
||||||
const getOrderTypeDict = async () => {
|
const getOrderTypeDict = async () => {
|
||||||
try {
|
try {
|
||||||
orderTypeList.value = [
|
const res = await getDicts('call_type');
|
||||||
{ label: '早读', value: '1' },
|
if (res.data && Array.isArray(res.data)) {
|
||||||
{ label: '上午', value: '2' },
|
orderTypeList.value = res.data.map((item: any) => ({
|
||||||
{ label: '下午', value: '3' },
|
label: item.label || item.dictLabel || item.name,
|
||||||
{ label: '晚自习', value: '4' },
|
value: item.value || item.dictValue || item.code,
|
||||||
];
|
}));
|
||||||
|
} else {
|
||||||
|
orderTypeList.value = [];
|
||||||
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
console.error('获取点名类型字典失败', err);
|
||||||
orderTypeList.value = [];
|
orderTypeList.value = [];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -338,4 +501,44 @@ onMounted(() => {
|
|||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@import '/@/assets/styles/modern-page.scss';
|
@import '/@/assets/styles/modern-page.scss';
|
||||||
|
|
||||||
|
.submit-attendance {
|
||||||
|
margin-top: 20px;
|
||||||
|
padding: 20px;
|
||||||
|
background: #f5f7fa;
|
||||||
|
border-radius: 8px;
|
||||||
|
|
||||||
|
.submit-form {
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.photo-uploader {
|
||||||
|
:deep(.el-upload) {
|
||||||
|
border: 1px dashed #d9d9d9;
|
||||||
|
border-radius: 6px;
|
||||||
|
cursor: pointer;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
width: 120px;
|
||||||
|
height: 120px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-color: #409eff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.photo-preview {
|
||||||
|
width: 120px;
|
||||||
|
height: 120px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.photo-uploader-icon {
|
||||||
|
font-size: 28px;
|
||||||
|
color: #8c939d;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -91,7 +91,6 @@ const form = reactive({
|
|||||||
// 定义校验规则
|
// 定义校验规则
|
||||||
const dataRules = ref({
|
const dataRules = ref({
|
||||||
classCode: [{ required: true, message: '班号不能为空', trigger: 'change' }],
|
classCode: [{ required: true, message: '班号不能为空', trigger: 'change' }],
|
||||||
stuNo: [{ required: true, message: '学生不能为空', trigger: 'change' }],
|
|
||||||
recordTime: [{ required: true, message: '记录时间不能为空', trigger: 'blur' }],
|
recordTime: [{ required: true, message: '记录时间不能为空', trigger: 'blur' }],
|
||||||
score: [{ required: true, message: '分数不能为空', trigger: 'blur' }],
|
score: [{ required: true, message: '分数不能为空', trigger: 'blur' }],
|
||||||
note: [{ required: true, message: '检查记录不能为空', trigger: 'blur' }],
|
note: [{ required: true, message: '检查记录不能为空', trigger: 'blur' }],
|
||||||
|
|||||||
@@ -159,31 +159,36 @@
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- 学期统计对话框 -->
|
<!-- 学期统计对话框 -->
|
||||||
<el-dialog title="学期统计" v-model="rankDialogVisible" :close-on-click-modal="false" draggable width="800px">
|
<el-dialog title="日常巡检学年学期统计" v-model="rankDialogVisible" :close-on-click-modal="false" draggable width="800px" destroy-on-close>
|
||||||
<el-form :inline="true" :model="rankForm">
|
<el-form :inline="true" :model="rankForm" class="mb10">
|
||||||
<el-form-item label="学年">
|
<el-form-item label="学年">
|
||||||
<el-select v-model="rankForm.schoolYear" placeholder="请选择学年" clearable filterable style="width: 200px">
|
<el-select v-model="rankForm.schoolYear" placeholder="请选择学年" clearable filterable style="width: 180px">
|
||||||
<el-option v-for="item in schoolYearList" :key="item.year" :label="item.year" :value="item.year"> </el-option>
|
<el-option v-for="item in schoolYearList" :key="item.year" :label="item.year" :value="item.year"> </el-option>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="学期">
|
<el-form-item label="学期">
|
||||||
<el-input v-model="rankForm.schoolTerm" placeholder="请输入学期" clearable style="width: 200px" />
|
<el-select v-model="rankForm.schoolTerm" placeholder="请选择学期" clearable style="width: 180px">
|
||||||
|
<el-option v-for="item in schoolTermList" :key="item.value" :label="item.label" :value="item.value" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="学院">
|
||||||
|
<el-select v-model="rankForm.deptCode" placeholder="请选择学院" clearable filterable style="width: 180px">
|
||||||
|
<el-option v-for="item in deptList" :key="item.deptCode" :label="item.deptName" :value="item.deptCode" />
|
||||||
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-button type="primary" icon="Search" @click="getRankData">查询</el-button>
|
<el-button type="primary" icon="Search" @click="getRankData">查询</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
<el-table :data="rankData" v-loading="rankLoading" border style="margin-top: 20px">
|
<el-table :data="rankData" v-loading="rankLoading" stripe max-height="400" :cell-style="{ padding: '8px 0', textAlign: 'center' }" :header-cell-style="{ background: '#f5f7fa', color: '#606266', fontWeight: 'bold', textAlign: 'center' }">
|
||||||
<el-table-column type="index" label="序号" align="center" />
|
<el-table-column type="index" label="序号" width="60" align="center" />
|
||||||
<el-table-column prop="deptName" label="学院" show-overflow-tooltip />
|
<el-table-column prop="deptName" label="学院" min-width="120" align="center" show-overflow-tooltip />
|
||||||
<el-table-column prop="classNo" label="班号" show-overflow-tooltip />
|
<el-table-column prop="className" label="班级" min-width="120" align="center" show-overflow-tooltip />
|
||||||
<el-table-column prop="totalScore" label="总分数" show-overflow-tooltip />
|
<el-table-column prop="totalScore" label="总分数" min-width="100" align="center" />
|
||||||
<el-table-column prop="count" label="记录数" show-overflow-tooltip />
|
<el-table-column prop="recordCount" label="记录数" min-width="100" align="center" />
|
||||||
</el-table>
|
</el-table>
|
||||||
<template #footer>
|
<template v-if="rankData.length === 0 && !rankLoading">
|
||||||
<span class="dialog-footer">
|
<el-empty description="暂无统计数据" :image-size="80" />
|
||||||
<el-button @click="rankDialogVisible = false">关 闭</el-button>
|
|
||||||
</span>
|
|
||||||
</template>
|
</template>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
</div>
|
</div>
|
||||||
@@ -192,12 +197,13 @@
|
|||||||
<script setup lang="ts" name="ClassCheckDaily">
|
<script setup lang="ts" name="ClassCheckDaily">
|
||||||
import { ref, reactive, defineAsyncComponent, computed, onMounted } from 'vue';
|
import { ref, reactive, defineAsyncComponent, computed, onMounted } from 'vue';
|
||||||
import { BasicTableProps, useTable } from '/@/hooks/table';
|
import { BasicTableProps, useTable } from '/@/hooks/table';
|
||||||
import { fetchList, delObjs, exportData, getRank } from '/@/api/stuwork/classcheckdaily';
|
import { fetchList, delObjs, exportData, getRank, dailySummaryByYearTerm } from '/@/api/stuwork/classcheckdaily';
|
||||||
import { makeExportClassCheckDailyTask } from '/@/api/stuwork/file';
|
import { makeExportClassCheckDailyTask } from '/@/api/stuwork/file';
|
||||||
import { useMessage, useMessageBox } from '/@/hooks/message';
|
import { useMessage, useMessageBox } from '/@/hooks/message';
|
||||||
import { getDeptList } from '/@/api/basic/basicclass';
|
import { getDeptList } from '/@/api/basic/basicclass';
|
||||||
import { getClassListByRole } from '/@/api/basic/basicclass';
|
import { getClassListByRole } from '/@/api/basic/basicclass';
|
||||||
import { queryAllSchoolYear } from '/@/api/basic/basicyear';
|
import { queryAllSchoolYear } from '/@/api/basic/basicyear';
|
||||||
|
import { getDicts } from '/@/api/admin/dict';
|
||||||
import TableColumnControl from '/@/components/TableColumnControl/index.vue';
|
import TableColumnControl from '/@/components/TableColumnControl/index.vue';
|
||||||
import {
|
import {
|
||||||
List,
|
List,
|
||||||
@@ -230,6 +236,7 @@ const showSearch = ref(true);
|
|||||||
const deptList = ref<any[]>([]);
|
const deptList = ref<any[]>([]);
|
||||||
const classList = ref<any[]>([]);
|
const classList = ref<any[]>([]);
|
||||||
const schoolYearList = ref<any[]>([]);
|
const schoolYearList = ref<any[]>([]);
|
||||||
|
const schoolTermList = ref<any[]>([]);
|
||||||
// 模板文件URL
|
// 模板文件URL
|
||||||
const templateUrl = ref('/stuwork/classcheckdaily/importTemplate');
|
const templateUrl = ref('/stuwork/classcheckdaily/importTemplate');
|
||||||
|
|
||||||
@@ -284,6 +291,7 @@ const rankData = ref<any[]>([]);
|
|||||||
const rankForm = reactive({
|
const rankForm = reactive({
|
||||||
schoolYear: '',
|
schoolYear: '',
|
||||||
schoolTerm: '',
|
schoolTerm: '',
|
||||||
|
deptCode: '',
|
||||||
});
|
});
|
||||||
|
|
||||||
// 学院选择变化
|
// 学院选择变化
|
||||||
@@ -350,6 +358,7 @@ const handleRank = () => {
|
|||||||
rankDialogVisible.value = true;
|
rankDialogVisible.value = true;
|
||||||
rankForm.schoolYear = '';
|
rankForm.schoolYear = '';
|
||||||
rankForm.schoolTerm = '';
|
rankForm.schoolTerm = '';
|
||||||
|
rankForm.deptCode = '';
|
||||||
rankData.value = [];
|
rankData.value = [];
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -362,9 +371,10 @@ const getRankData = async () => {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
rankLoading.value = true;
|
rankLoading.value = true;
|
||||||
const res = await getRank({
|
const res = await dailySummaryByYearTerm({
|
||||||
schoolYear: rankForm.schoolYear,
|
schoolYear: rankForm.schoolYear,
|
||||||
schoolTerm: rankForm.schoolTerm,
|
schoolTerm: rankForm.schoolTerm,
|
||||||
|
deptCode: rankForm.deptCode,
|
||||||
});
|
});
|
||||||
if (res.data) {
|
if (res.data) {
|
||||||
rankData.value = Array.isArray(res.data) ? res.data : [];
|
rankData.value = Array.isArray(res.data) ? res.data : [];
|
||||||
@@ -415,11 +425,29 @@ const getSchoolYearList = async () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 获取学期字典
|
||||||
|
const getSchoolTermDict = async () => {
|
||||||
|
try {
|
||||||
|
const res = await getDicts('school_term');
|
||||||
|
if (res.data) {
|
||||||
|
schoolTermList.value = Array.isArray(res.data)
|
||||||
|
? res.data.map((item: any) => ({
|
||||||
|
label: item.label || item.dictLabel || item.name,
|
||||||
|
value: item.value || item.dictValue || item.code,
|
||||||
|
}))
|
||||||
|
: [];
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
schoolTermList.value = [];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// 初始化
|
// 初始化
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getDeptListData();
|
getDeptListData();
|
||||||
getClassListData();
|
getClassListData();
|
||||||
getSchoolYearList();
|
getSchoolYearList();
|
||||||
|
getSchoolTermDict();
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -59,6 +59,7 @@
|
|||||||
</span>
|
</span>
|
||||||
<div class="header-actions">
|
<div class="header-actions">
|
||||||
<el-button icon="FolderAdd" type="primary" @click="formDialogRef.openDialog()"> 新增 </el-button>
|
<el-button icon="FolderAdd" type="primary" @click="formDialogRef.openDialog()"> 新增 </el-button>
|
||||||
|
<el-button icon="DataAnalysis" type="info" class="ml10" @click="handleOpenSummary"> 学期统计 </el-button>
|
||||||
<el-button icon="Upload" type="success" class="ml10" @click="handleImport"> 导入 </el-button>
|
<el-button icon="Upload" type="success" class="ml10" @click="handleImport"> 导入 </el-button>
|
||||||
<el-button icon="Download" type="warning" class="ml10" @click="handleExport"> 导出 </el-button>
|
<el-button icon="Download" type="warning" class="ml10" @click="handleExport"> 导出 </el-button>
|
||||||
<right-toolbar v-model:showSearch="showSearch" class="ml10" @queryTable="getDataList">
|
<right-toolbar v-model:showSearch="showSearch" class="ml10" @queryTable="getDataList">
|
||||||
@@ -160,19 +161,55 @@
|
|||||||
:temp-url="templateUrl"
|
:temp-url="templateUrl"
|
||||||
@refreshDataList="getDataList"
|
@refreshDataList="getDataList"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<!-- 学期统计弹出框 -->
|
||||||
|
<el-dialog v-model="summaryVisible" title="日常行为学年学期统计" width="900px" destroy-on-close>
|
||||||
|
<el-form :model="summaryForm" :inline="true" class="mb10">
|
||||||
|
<el-form-item label="学年">
|
||||||
|
<el-select v-model="summaryForm.schoolYear" placeholder="请选择学年" clearable filterable style="width: 180px">
|
||||||
|
<el-option v-for="item in schoolYearList" :key="item.year" :label="item.year" :value="item.year" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="学期">
|
||||||
|
<el-select v-model="summaryForm.schoolTerm" placeholder="请选择学期" clearable style="width: 180px">
|
||||||
|
<el-option v-for="item in schoolTermList" :key="item.value" :label="item.label" :value="item.value" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="学院">
|
||||||
|
<el-select v-model="summaryForm.deptCode" placeholder="请选择学院" clearable filterable style="width: 180px">
|
||||||
|
<el-option v-for="item in deptList" :key="item.deptCode" :label="item.deptName" :value="item.deptCode" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" icon="Search" @click="handleQuerySummary">查询</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<el-table :data="summaryData" v-loading="summaryLoading" stripe max-height="400" :cell-style="{ padding: '8px 0', textAlign: 'center' }" :header-cell-style="{ background: '#f5f7fa', color: '#606266', fontWeight: 'bold', textAlign: 'center' }">
|
||||||
|
<el-table-column type="index" label="序号" width="60" align="center" />
|
||||||
|
<el-table-column prop="deptName" label="学院" min-width="120" align="center" show-overflow-tooltip />
|
||||||
|
<el-table-column prop="className" label="班级" min-width="120" align="center" show-overflow-tooltip />
|
||||||
|
<el-table-column prop="totalScore" label="总分" min-width="100" align="center" />
|
||||||
|
<el-table-column prop="recordCount" label="记录数" min-width="100" align="center" />
|
||||||
|
</el-table>
|
||||||
|
<template v-if="summaryData.length === 0 && !summaryLoading">
|
||||||
|
<el-empty description="暂无统计数据" :image-size="80" />
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts" name="ClassHygieneDaily">
|
<script setup lang="ts" name="ClassHygieneDaily">
|
||||||
import { ref, reactive, defineAsyncComponent, computed, onMounted } from 'vue';
|
import { ref, reactive, defineAsyncComponent, computed, onMounted } from 'vue';
|
||||||
import { BasicTableProps, useTable } from '/@/hooks/table';
|
import { BasicTableProps, useTable } from '/@/hooks/table';
|
||||||
import { fetchList, delObjs } from '/@/api/stuwork/classhygienedaily';
|
import { fetchList, delObjs, dailySummaryByYearTerm } from '/@/api/stuwork/classhygienedaily';
|
||||||
import { downloadClassHygieneDailyTemplate, makeExportClassHygieneDailyTask } from '/@/api/stuwork/file';
|
import { downloadClassHygieneDailyTemplate, makeExportClassHygieneDailyTask } from '/@/api/stuwork/file';
|
||||||
import { useMessage, useMessageBox } from '/@/hooks/message';
|
import { useMessage, useMessageBox } from '/@/hooks/message';
|
||||||
import { getDeptList } from '/@/api/basic/basicclass';
|
import { getDeptList } from '/@/api/basic/basicclass';
|
||||||
import { getClassListByRole } from '/@/api/basic/basicclass';
|
import { getClassListByRole } from '/@/api/basic/basicclass';
|
||||||
|
import { queryAllSchoolYear } from '/@/api/basic/basicyear';
|
||||||
|
import { getDicts } from '/@/api/admin/dict';
|
||||||
import TableColumnControl from '/@/components/TableColumnControl/index.vue';
|
import TableColumnControl from '/@/components/TableColumnControl/index.vue';
|
||||||
import { List, OfficeBuilding, Grid, Calendar, Minus, Document, Setting, Menu, Search, FolderAdd, EditPen } from '@element-plus/icons-vue';
|
import { List, OfficeBuilding, Grid, Calendar, Minus, Document, Setting, Menu, Search, FolderAdd, EditPen, DataAnalysis } from '@element-plus/icons-vue';
|
||||||
import { useTableColumnControl } from '/@/hooks/tableColumn';
|
import { useTableColumnControl } from '/@/hooks/tableColumn';
|
||||||
|
|
||||||
// 引入组件
|
// 引入组件
|
||||||
@@ -187,9 +224,21 @@ const uploadExcelRef = ref();
|
|||||||
const showSearch = ref(true);
|
const showSearch = ref(true);
|
||||||
const deptList = ref<any[]>([]);
|
const deptList = ref<any[]>([]);
|
||||||
const classList = ref<any[]>([]);
|
const classList = ref<any[]>([]);
|
||||||
|
const schoolYearList = ref<any[]>([]);
|
||||||
|
const schoolTermList = ref<any[]>([]);
|
||||||
// 模板文件URL
|
// 模板文件URL
|
||||||
const templateUrl = ref('/stuwork/classhygienedaily/importTemplate');
|
const templateUrl = ref('/stuwork/classhygienedaily/importTemplate');
|
||||||
|
|
||||||
|
// 统计相关变量
|
||||||
|
const summaryVisible = ref(false);
|
||||||
|
const summaryLoading = ref(false);
|
||||||
|
const summaryData = ref<any[]>([]);
|
||||||
|
const summaryForm = reactive({
|
||||||
|
schoolYear: '',
|
||||||
|
schoolTerm: '',
|
||||||
|
deptCode: ''
|
||||||
|
});
|
||||||
|
|
||||||
// 表格列配置
|
// 表格列配置
|
||||||
const tableColumns = [
|
const tableColumns = [
|
||||||
{ prop: 'deptName', label: '学院', icon: OfficeBuilding },
|
{ prop: 'deptName', label: '学院', icon: OfficeBuilding },
|
||||||
@@ -313,10 +362,72 @@ const getClassListData = async () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 获取学年列表
|
||||||
|
const getSchoolYearListData = async () => {
|
||||||
|
try {
|
||||||
|
const res = await queryAllSchoolYear();
|
||||||
|
if (res.data) {
|
||||||
|
schoolYearList.value = Array.isArray(res.data) ? res.data : [];
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
schoolYearList.value = [];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取学期字典
|
||||||
|
const getSchoolTermDict = async () => {
|
||||||
|
try {
|
||||||
|
const res = await getDicts('school_term');
|
||||||
|
if (res.data) {
|
||||||
|
schoolTermList.value = Array.isArray(res.data)
|
||||||
|
? res.data.map((item: any) => ({
|
||||||
|
label: item.label || item.dictLabel || item.name,
|
||||||
|
value: item.value || item.dictValue || item.code,
|
||||||
|
}))
|
||||||
|
: [];
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
schoolTermList.value = [];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 打开统计弹窗
|
||||||
|
const handleOpenSummary = () => {
|
||||||
|
summaryVisible.value = true;
|
||||||
|
summaryForm.schoolYear = '';
|
||||||
|
summaryForm.schoolTerm = '';
|
||||||
|
summaryForm.deptCode = '';
|
||||||
|
summaryData.value = [];
|
||||||
|
};
|
||||||
|
|
||||||
|
// 查询统计数据
|
||||||
|
const handleQuerySummary = async () => {
|
||||||
|
if (!summaryForm.schoolYear || !summaryForm.schoolTerm) {
|
||||||
|
useMessage().warning('请选择学年和学期');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
summaryLoading.value = true;
|
||||||
|
try {
|
||||||
|
const res = await dailySummaryByYearTerm({
|
||||||
|
schoolYear: summaryForm.schoolYear,
|
||||||
|
schoolTerm: summaryForm.schoolTerm,
|
||||||
|
deptCode: summaryForm.deptCode
|
||||||
|
});
|
||||||
|
summaryData.value = Array.isArray(res.data) ? res.data : [];
|
||||||
|
} catch (err: any) {
|
||||||
|
useMessage().error(err.msg || '获取统计数据失败');
|
||||||
|
summaryData.value = [];
|
||||||
|
} finally {
|
||||||
|
summaryLoading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// 初始化
|
// 初始化
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getDeptListData();
|
getDeptListData();
|
||||||
getClassListData();
|
getClassListData();
|
||||||
|
getSchoolYearListData();
|
||||||
|
getSchoolTermDict();
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -38,6 +38,21 @@
|
|||||||
<h3 class="content-title">总结报告</h3>
|
<h3 class="content-title">总结报告</h3>
|
||||||
<div v-html="detailData.summary || '-'"></div>
|
<div v-html="detailData.summary || '-'"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 班级异动情况 -->
|
||||||
|
<div class="turnover-section">
|
||||||
|
<h3 class="content-title">班级异动情况</h3>
|
||||||
|
<el-table :data="turnoverData" v-loading="turnoverLoading" stripe max-height="300" :cell-style="{ padding: '8px 0', textAlign: 'center' }" :header-cell-style="{ background: '#f5f7fa', color: '#606266', fontWeight: 'bold', textAlign: 'center' }">
|
||||||
|
<el-table-column type="index" label="序号" width="60" align="center" />
|
||||||
|
<el-table-column prop="studentName" label="学生姓名" min-width="100" align="center" show-overflow-tooltip />
|
||||||
|
<el-table-column prop="turnoverType" label="异动类型" min-width="100" align="center" show-overflow-tooltip />
|
||||||
|
<el-table-column prop="turnoverDate" label="异动日期" min-width="120" align="center" show-overflow-tooltip />
|
||||||
|
<el-table-column prop="reason" label="异动原因" min-width="150" align="center" show-overflow-tooltip />
|
||||||
|
</el-table>
|
||||||
|
<template v-if="turnoverData.length === 0 && !turnoverLoading">
|
||||||
|
<el-empty description="暂无异动数据" :image-size="60" />
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<span class="dialog-footer">
|
<span class="dialog-footer">
|
||||||
@@ -50,6 +65,7 @@
|
|||||||
<script setup lang="ts" name="ClassSummaryDetailDialog">
|
<script setup lang="ts" name="ClassSummaryDetailDialog">
|
||||||
import { ref, onMounted } from 'vue';
|
import { ref, onMounted } from 'vue';
|
||||||
import { getDetail } from '/@/api/stuwork/classsummary';
|
import { getDetail } from '/@/api/stuwork/classsummary';
|
||||||
|
import { queryByClassCode } from '/@/api/stuwork/stuturnover';
|
||||||
import { getDicts } from '/@/api/admin/dict';
|
import { getDicts } from '/@/api/admin/dict';
|
||||||
|
|
||||||
// 定义变量内容
|
// 定义变量内容
|
||||||
@@ -58,6 +74,8 @@ const loading = ref(false);
|
|||||||
const detailData = ref<any>(null);
|
const detailData = ref<any>(null);
|
||||||
const schoolTermList = ref<any[]>([]);
|
const schoolTermList = ref<any[]>([]);
|
||||||
const statusList = ref<any[]>([]);
|
const statusList = ref<any[]>([]);
|
||||||
|
const turnoverData = ref<any[]>([]);
|
||||||
|
const turnoverLoading = ref(false);
|
||||||
|
|
||||||
// 格式化学期
|
// 格式化学期
|
||||||
const formatSchoolTerm = (value: string | number) => {
|
const formatSchoolTerm = (value: string | number) => {
|
||||||
@@ -79,11 +97,16 @@ const formatStatus = (value: string) => {
|
|||||||
const openDialog = async (id: string) => {
|
const openDialog = async (id: string) => {
|
||||||
visible.value = true;
|
visible.value = true;
|
||||||
detailData.value = null;
|
detailData.value = null;
|
||||||
|
turnoverData.value = [];
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
try {
|
try {
|
||||||
const res = await getDetail(id);
|
const res = await getDetail(id);
|
||||||
if (res.data) {
|
if (res.data) {
|
||||||
detailData.value = res.data;
|
detailData.value = res.data;
|
||||||
|
// 获取班级异动情况
|
||||||
|
if (res.data.classCode) {
|
||||||
|
fetchTurnoverData(res.data.classCode);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
} finally {
|
} finally {
|
||||||
@@ -91,6 +114,23 @@ const openDialog = async (id: string) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 获取班级异动数据
|
||||||
|
const fetchTurnoverData = async (classCode: string) => {
|
||||||
|
turnoverLoading.value = true;
|
||||||
|
try {
|
||||||
|
const res = await queryByClassCode(classCode);
|
||||||
|
if (res.data) {
|
||||||
|
turnoverData.value = Array.isArray(res.data) ? res.data : [];
|
||||||
|
} else {
|
||||||
|
turnoverData.value = [];
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
turnoverData.value = [];
|
||||||
|
} finally {
|
||||||
|
turnoverLoading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// 获取学期字典
|
// 获取学期字典
|
||||||
const getSchoolTermDict = async () => {
|
const getSchoolTermDict = async () => {
|
||||||
try {
|
try {
|
||||||
@@ -225,4 +265,8 @@ defineExpose({
|
|||||||
color: #909399;
|
color: #909399;
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.turnover-section {
|
||||||
|
margin-top: 30px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -54,6 +54,11 @@
|
|||||||
<el-option v-for="item in classList" :key="item.classCode" :label="item.classNo" :value="item.classCode"> </el-option>
|
<el-option v-for="item in classList" :key="item.classCode" :label="item.classNo" :value="item.classCode"> </el-option>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item label="审核状态" prop="auditStatus">
|
||||||
|
<el-select v-model="searchForm.auditStatus" placeholder="请选择审核状态" clearable style="width: 200px">
|
||||||
|
<el-option v-for="item in auditStatusList" :key="item.value" :label="item.label" :value="item.value"> </el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-button type="primary" plain icon="Search" @click="handleSearch">查询</el-button>
|
<el-button type="primary" plain icon="Search" @click="handleSearch">查询</el-button>
|
||||||
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
||||||
@@ -71,6 +76,8 @@
|
|||||||
</span>
|
</span>
|
||||||
<div class="header-actions">
|
<div class="header-actions">
|
||||||
<el-button icon="FolderAdd" type="primary" @click="formDialogRef.openDialog()"> 新 增 </el-button>
|
<el-button icon="FolderAdd" type="primary" @click="formDialogRef.openDialog()"> 新 增 </el-button>
|
||||||
|
<el-button icon="Check" type="success" @click="handleBatchAudit('2')" :disabled="selectedRows.length === 0"> 批量通过 </el-button>
|
||||||
|
<el-button icon="Close" type="danger" @click="handleBatchAudit('3')" :disabled="selectedRows.length === 0"> 批量不通过 </el-button>
|
||||||
<el-button icon="Download" type="success" class="ml10" @click="handleExport"> 导 出 </el-button>
|
<el-button icon="Download" type="success" class="ml10" @click="handleExport"> 导 出 </el-button>
|
||||||
<right-toolbar v-model:showSearch="showSearch" class="ml10" @queryTable="getDataList">
|
<right-toolbar v-model:showSearch="showSearch" class="ml10" @queryTable="getDataList">
|
||||||
<TableColumnControl
|
<TableColumnControl
|
||||||
@@ -100,10 +107,12 @@
|
|||||||
:data="state.dataList"
|
:data="state.dataList"
|
||||||
v-loading="state.loading"
|
v-loading="state.loading"
|
||||||
stripe
|
stripe
|
||||||
|
@selection-change="handleSelectionChange"
|
||||||
:cell-style="tableStyle.cellStyle"
|
:cell-style="tableStyle.cellStyle"
|
||||||
:header-cell-style="tableStyle.headerCellStyle"
|
:header-cell-style="tableStyle.headerCellStyle"
|
||||||
class="modern-table"
|
class="modern-table"
|
||||||
>
|
>
|
||||||
|
<el-table-column type="selection" width="55" align="center" :selectable="checkSelectable" />
|
||||||
<el-table-column type="index" label="序号" width="70" align="center">
|
<el-table-column type="index" label="序号" width="70" align="center">
|
||||||
<template #header>
|
<template #header>
|
||||||
<el-icon><List /></el-icon>
|
<el-icon><List /></el-icon>
|
||||||
@@ -135,16 +144,20 @@
|
|||||||
<span>{{ formatLiveType(scope.row.liveType) }}</span>
|
<span>{{ formatLiveType(scope.row.liveType) }}</span>
|
||||||
</template>
|
</template>
|
||||||
<template #default="scope" v-else-if="col.prop === 'auditStatus'">
|
<template #default="scope" v-else-if="col.prop === 'auditStatus'">
|
||||||
<span>{{ formatAuditStatus(scope.row.auditStatus) }}</span>
|
<el-tag :type="getAuditStatusType(scope.row.auditStatus)" size="small" effect="plain">
|
||||||
|
{{ formatAuditStatus(scope.row.auditStatus) }}
|
||||||
|
</el-tag>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</template>
|
</template>
|
||||||
<el-table-column label="操作" width="150" align="center" fixed="right">
|
<el-table-column label="操作" width="250" align="center" fixed="right">
|
||||||
<template #header>
|
<template #header>
|
||||||
<el-icon><Setting /></el-icon>
|
<el-icon><Setting /></el-icon>
|
||||||
<span style="margin-left: 4px">操作</span>
|
<span style="margin-left: 4px">操作</span>
|
||||||
</template>
|
</template>
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
|
<el-button v-if="scope.row.auditStatus === '1'" icon="Check" link type="success" @click="handleAudit(scope.row, '2')"> 通过 </el-button>
|
||||||
|
<el-button v-if="scope.row.auditStatus === '1'" icon="Close" link type="danger" @click="handleAudit(scope.row, '3')"> 不通过 </el-button>
|
||||||
<el-button icon="Edit" link type="primary" @click="handleEdit(scope.row)"> 编辑 </el-button>
|
<el-button icon="Edit" link type="primary" @click="handleEdit(scope.row)"> 编辑 </el-button>
|
||||||
<el-button icon="Delete" link type="danger" @click="handleDelete(scope.row)"> 删除 </el-button>
|
<el-button icon="Delete" link type="danger" @click="handleDelete(scope.row)"> 删除 </el-button>
|
||||||
</template>
|
</template>
|
||||||
@@ -163,13 +176,31 @@
|
|||||||
|
|
||||||
<!-- 新增/编辑表单弹窗 -->
|
<!-- 新增/编辑表单弹窗 -->
|
||||||
<form-dialog ref="formDialogRef" @refresh="getDataList" />
|
<form-dialog ref="formDialogRef" @refresh="getDataList" />
|
||||||
|
|
||||||
|
<!-- 审批弹窗 -->
|
||||||
|
<el-dialog v-model="auditDialogVisible" :title="auditForm.auditStatus === '2' ? '审批通过' : '审批不通过'" width="500" :close-on-click-modal="false" draggable>
|
||||||
|
<el-form :model="auditForm" label-width="80px">
|
||||||
|
<el-form-item label="审核结果">
|
||||||
|
<el-tag :type="auditForm.auditStatus === '2' ? 'success' : 'danger'" size="large">
|
||||||
|
{{ auditForm.auditStatus === '2' ? '通过' : '不通过' }}
|
||||||
|
</el-tag>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="审核备注">
|
||||||
|
<el-input v-model="auditForm.auditRemark" type="textarea" :rows="3" placeholder="请输入审核备注(选填)" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<el-button @click="auditDialogVisible = false">取 消</el-button>
|
||||||
|
<el-button type="primary" @click="submitAudit" :loading="auditLoading">确 认</el-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts" name="DormLiveApply">
|
<script setup lang="ts" name="DormLiveApply">
|
||||||
import { reactive, ref, onMounted } from 'vue';
|
import { reactive, ref, onMounted } from 'vue';
|
||||||
import { BasicTableProps, useTable } from '/@/hooks/table';
|
import { BasicTableProps, useTable } from '/@/hooks/table';
|
||||||
import { fetchList, delObj, exportData } from '/@/api/stuwork/dormliveapply';
|
import { fetchList, delObj, exportData, auditApply, batchAuditApply } from '/@/api/stuwork/dormliveapply';
|
||||||
import { queryAllSchoolYear } from '/@/api/basic/basicyear';
|
import { queryAllSchoolYear } from '/@/api/basic/basicyear';
|
||||||
import { getDicts } from '/@/api/admin/dict';
|
import { getDicts } from '/@/api/admin/dict';
|
||||||
import { getBuildingList } from '/@/api/stuwork/dormbuilding';
|
import { getBuildingList } from '/@/api/stuwork/dormbuilding';
|
||||||
@@ -177,7 +208,7 @@ import { getClassListByRole } from '/@/api/basic/basicclass';
|
|||||||
import { useMessage, useMessageBox } from '/@/hooks/message';
|
import { useMessage, useMessageBox } from '/@/hooks/message';
|
||||||
import TableColumnControl from '/@/components/TableColumnControl/index.vue';
|
import TableColumnControl from '/@/components/TableColumnControl/index.vue';
|
||||||
import FormDialog from './form.vue';
|
import FormDialog from './form.vue';
|
||||||
import { List, Calendar, Clock, OfficeBuilding, Grid, Menu, Search, Document, Setting, User } from '@element-plus/icons-vue';
|
import { List, Calendar, Clock, OfficeBuilding, Grid, Menu, Search, Document, Setting, User, Check, Close } from '@element-plus/icons-vue';
|
||||||
import { useTableColumnControl } from '/@/hooks/tableColumn';
|
import { useTableColumnControl } from '/@/hooks/tableColumn';
|
||||||
|
|
||||||
// 定义变量内容
|
// 定义变量内容
|
||||||
@@ -191,6 +222,16 @@ const classList = ref<any[]>([]);
|
|||||||
const liveTypeList = ref<any[]>([]);
|
const liveTypeList = ref<any[]>([]);
|
||||||
const auditStatusList = ref<any[]>([]);
|
const auditStatusList = ref<any[]>([]);
|
||||||
const formDialogRef = ref();
|
const formDialogRef = ref();
|
||||||
|
const selectedRows = ref<any[]>([]);
|
||||||
|
const auditDialogVisible = ref(false);
|
||||||
|
const auditLoading = ref(false);
|
||||||
|
const auditForm = reactive({
|
||||||
|
id: '',
|
||||||
|
auditStatus: '',
|
||||||
|
auditRemark: '',
|
||||||
|
isBatch: false,
|
||||||
|
ids: [] as string[],
|
||||||
|
});
|
||||||
|
|
||||||
// 表格列配置
|
// 表格列配置
|
||||||
const tableColumns = [
|
const tableColumns = [
|
||||||
@@ -216,6 +257,7 @@ const searchForm = reactive({
|
|||||||
startTime: '',
|
startTime: '',
|
||||||
endTime: '',
|
endTime: '',
|
||||||
classCode: '',
|
classCode: '',
|
||||||
|
auditStatus: '',
|
||||||
});
|
});
|
||||||
|
|
||||||
// 配置 useTable
|
// 配置 useTable
|
||||||
@@ -255,6 +297,20 @@ const formatAuditStatus = (value: string) => {
|
|||||||
return item ? item.label : value;
|
return item ? item.label : value;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 获取审核状态标签类型
|
||||||
|
const getAuditStatusType = (value: string) => {
|
||||||
|
switch (value) {
|
||||||
|
case '1':
|
||||||
|
return 'warning';
|
||||||
|
case '2':
|
||||||
|
return 'success';
|
||||||
|
case '3':
|
||||||
|
return 'danger';
|
||||||
|
default:
|
||||||
|
return 'info';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// 查询
|
// 查询
|
||||||
const handleSearch = () => {
|
const handleSearch = () => {
|
||||||
getDataList();
|
getDataList();
|
||||||
@@ -269,6 +325,7 @@ const handleReset = () => {
|
|||||||
searchForm.startTime = '';
|
searchForm.startTime = '';
|
||||||
searchForm.endTime = '';
|
searchForm.endTime = '';
|
||||||
searchForm.classCode = '';
|
searchForm.classCode = '';
|
||||||
|
searchForm.auditStatus = '';
|
||||||
getDataList();
|
getDataList();
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -311,6 +368,59 @@ const handleExport = async () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 选择变化
|
||||||
|
const handleSelectionChange = (rows: any[]) => {
|
||||||
|
selectedRows.value = rows;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 判断是否可选(只有待审核状态可选)
|
||||||
|
const checkSelectable = (row: any) => {
|
||||||
|
return row.auditStatus === '1';
|
||||||
|
};
|
||||||
|
|
||||||
|
// 单个审批
|
||||||
|
const handleAudit = (row: any, auditStatus: string) => {
|
||||||
|
auditForm.id = row.id;
|
||||||
|
auditForm.auditStatus = auditStatus;
|
||||||
|
auditForm.auditRemark = '';
|
||||||
|
auditForm.isBatch = false;
|
||||||
|
auditForm.ids = [];
|
||||||
|
auditDialogVisible.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 批量审批
|
||||||
|
const handleBatchAudit = (auditStatus: string) => {
|
||||||
|
if (selectedRows.value.length === 0) {
|
||||||
|
useMessage().warning('请选择要审批的记录');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auditForm.id = '';
|
||||||
|
auditForm.auditStatus = auditStatus;
|
||||||
|
auditForm.auditRemark = '';
|
||||||
|
auditForm.isBatch = true;
|
||||||
|
auditForm.ids = selectedRows.value.map((row) => row.id);
|
||||||
|
auditDialogVisible.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 提交审批
|
||||||
|
const submitAudit = async () => {
|
||||||
|
auditLoading.value = true;
|
||||||
|
try {
|
||||||
|
if (auditForm.isBatch) {
|
||||||
|
await batchAuditApply(auditForm.ids, auditForm.auditStatus, auditForm.auditRemark);
|
||||||
|
} else {
|
||||||
|
await auditApply(auditForm.id, auditForm.auditStatus, auditForm.auditRemark);
|
||||||
|
}
|
||||||
|
useMessage().success('审批成功');
|
||||||
|
auditDialogVisible.value = false;
|
||||||
|
getDataList();
|
||||||
|
} catch (err: any) {
|
||||||
|
useMessage().error(err.msg || '审批失败');
|
||||||
|
} finally {
|
||||||
|
auditLoading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// 获取学年列表
|
// 获取学年列表
|
||||||
const getSchoolYearList = async () => {
|
const getSchoolYearList = async () => {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -104,6 +104,24 @@
|
|||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
|
|
||||||
|
<el-col :span="12" class="mb20">
|
||||||
|
<el-form-item label="校门" prop="schoolDoor">
|
||||||
|
<el-select
|
||||||
|
v-model="form.schoolDoor"
|
||||||
|
placeholder="请选择校门"
|
||||||
|
clearable
|
||||||
|
filterable
|
||||||
|
style="width: 100%">
|
||||||
|
<el-option
|
||||||
|
v-for="item in schoolDoorList"
|
||||||
|
:key="item.value"
|
||||||
|
:label="item.label"
|
||||||
|
:value="item.value">
|
||||||
|
</el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
|
||||||
<el-col :span="24" class="mb20">
|
<el-col :span="24" class="mb20">
|
||||||
<el-form-item label="请假事由" prop="reason">
|
<el-form-item label="请假事由" prop="reason">
|
||||||
<el-input
|
<el-input
|
||||||
@@ -139,8 +157,9 @@ import { ref, reactive, nextTick, onMounted } from 'vue'
|
|||||||
import { useMessage } from '/@/hooks/message'
|
import { useMessage } from '/@/hooks/message'
|
||||||
import { addObj } from '/@/api/stuwork/stutemleaveapply'
|
import { addObj } from '/@/api/stuwork/stutemleaveapply'
|
||||||
import { getDeptListByLevelTwo } from '/@/api/basic/basicdept'
|
import { getDeptListByLevelTwo } from '/@/api/basic/basicdept'
|
||||||
import { list as getClassList } from '/@/api/basic/basicclass'
|
import { getClassListByRole } from '/@/api/basic/basicclass'
|
||||||
import { queryStudentListByClass } from '/@/api/basic/basicstudent'
|
import { queryStudentListByClass, queryAllStudentByClassCode } from '/@/api/basic/basicstudent'
|
||||||
|
import { listAll as getSchoolDoorList } from '/@/api/safety/clouddeviceposition'
|
||||||
|
|
||||||
const emit = defineEmits(['refresh'])
|
const emit = defineEmits(['refresh'])
|
||||||
|
|
||||||
@@ -149,8 +168,10 @@ const dataFormRef = ref()
|
|||||||
const visible = ref(false)
|
const visible = ref(false)
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const deptList = ref<any[]>([])
|
const deptList = ref<any[]>([])
|
||||||
const classList = ref<any[]>([])
|
const allClassList = ref<any[]>([]) // 保存所有班级数据
|
||||||
|
const classList = ref<any[]>([]) // 显示的班级数据(筛选后)
|
||||||
const studentList = ref<any[]>([])
|
const studentList = ref<any[]>([])
|
||||||
|
const schoolDoorList = ref<any[]>([])
|
||||||
|
|
||||||
// 提交表单数据
|
// 提交表单数据
|
||||||
const form = reactive({
|
const form = reactive({
|
||||||
@@ -160,6 +181,7 @@ const form = reactive({
|
|||||||
realName: '',
|
realName: '',
|
||||||
startTime: '',
|
startTime: '',
|
||||||
endTime: '',
|
endTime: '',
|
||||||
|
schoolDoor: '',
|
||||||
reason: '',
|
reason: '',
|
||||||
remarks: ''
|
remarks: ''
|
||||||
})
|
})
|
||||||
@@ -203,9 +225,12 @@ const resetForm = () => {
|
|||||||
form.realName = ''
|
form.realName = ''
|
||||||
form.startTime = ''
|
form.startTime = ''
|
||||||
form.endTime = ''
|
form.endTime = ''
|
||||||
|
form.schoolDoor = ''
|
||||||
form.reason = ''
|
form.reason = ''
|
||||||
form.remarks = ''
|
form.remarks = ''
|
||||||
studentList.value = []
|
studentList.value = []
|
||||||
|
// 重置班级列表为全部
|
||||||
|
classList.value = allClassList.value
|
||||||
}
|
}
|
||||||
|
|
||||||
// 系部变化
|
// 系部变化
|
||||||
@@ -216,9 +241,9 @@ const handleDeptChange = () => {
|
|||||||
studentList.value = []
|
studentList.value = []
|
||||||
// 根据系部筛选班级
|
// 根据系部筛选班级
|
||||||
if (form.deptCode) {
|
if (form.deptCode) {
|
||||||
classList.value = classList.value.filter((item: any) => item.deptCode === form.deptCode)
|
classList.value = allClassList.value.filter((item: any) => item.deptCode === form.deptCode)
|
||||||
} else {
|
} else {
|
||||||
getClassListData()
|
classList.value = allClassList.value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -267,6 +292,7 @@ const onSubmit = async () => {
|
|||||||
stuNo: form.stuNo,
|
stuNo: form.stuNo,
|
||||||
startTime: form.startTime,
|
startTime: form.startTime,
|
||||||
endTime: form.endTime,
|
endTime: form.endTime,
|
||||||
|
schoolDoor: form.schoolDoor,
|
||||||
reason: form.reason,
|
reason: form.reason,
|
||||||
remarks: form.remarks
|
remarks: form.remarks
|
||||||
})
|
})
|
||||||
@@ -296,37 +322,70 @@ const getDeptListData = async () => {
|
|||||||
// 获取班级列表
|
// 获取班级列表
|
||||||
const getClassListData = async () => {
|
const getClassListData = async () => {
|
||||||
try {
|
try {
|
||||||
const res = await getClassList()
|
const res = await getClassListByRole()
|
||||||
if (res.data) {
|
if (res.data) {
|
||||||
classList.value = Array.isArray(res.data) ? res.data : []
|
const list = Array.isArray(res.data) ? res.data : []
|
||||||
|
allClassList.value = list
|
||||||
|
classList.value = list
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
allClassList.value = []
|
||||||
classList.value = []
|
classList.value = []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取学生列表(接口文档:GET 仅 classCode,返回 data 为数组)
|
// 获取学生列表
|
||||||
const getStudentListData = async () => {
|
const getStudentListData = async () => {
|
||||||
if (!form.classCode) return
|
if (!form.classCode) return
|
||||||
try {
|
try {
|
||||||
const res = await queryStudentListByClass({ classCode: form.classCode })
|
// 先尝试 queryStudentListByClass 接口
|
||||||
const data = res?.data
|
let res = await queryStudentListByClass({ classCode: form.classCode })
|
||||||
|
let data = res?.data
|
||||||
|
|
||||||
|
// 如果数据为空或不是数组,尝试 queryAllStudentByClassCode 接口
|
||||||
|
if (!data || (Array.isArray(data) && data.length === 0)) {
|
||||||
|
res = await queryAllStudentByClassCode(form.classCode)
|
||||||
|
data = res?.data
|
||||||
|
}
|
||||||
|
|
||||||
if (Array.isArray(data)) {
|
if (Array.isArray(data)) {
|
||||||
studentList.value = data
|
studentList.value = data
|
||||||
} else if (data?.records && Array.isArray(data.records)) {
|
} else if (data?.records && Array.isArray(data.records)) {
|
||||||
studentList.value = data.records
|
studentList.value = data.records
|
||||||
|
} else if (data && typeof data === 'object') {
|
||||||
|
// 可能返回的是单个对象或包装对象
|
||||||
|
studentList.value = [data]
|
||||||
} else {
|
} else {
|
||||||
studentList.value = []
|
studentList.value = []
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
console.error('获取学生列表失败', err)
|
||||||
studentList.value = []
|
studentList.value = []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取校门列表
|
||||||
|
const getSchoolDoorData = async () => {
|
||||||
|
try {
|
||||||
|
const res = await getSchoolDoorList()
|
||||||
|
if (res.data) {
|
||||||
|
schoolDoorList.value = Array.isArray(res.data)
|
||||||
|
? res.data.map((item: any) => ({
|
||||||
|
label: item.positionName || item.name,
|
||||||
|
value: item.id || item.positionCode
|
||||||
|
}))
|
||||||
|
: []
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
schoolDoorList.value = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 初始化
|
// 初始化
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getDeptListData()
|
getDeptListData()
|
||||||
getClassListData()
|
getClassListData()
|
||||||
|
getSchoolDoorData()
|
||||||
})
|
})
|
||||||
|
|
||||||
// 暴露方法
|
// 暴露方法
|
||||||
|
|||||||
@@ -136,6 +136,9 @@
|
|||||||
</template>
|
</template>
|
||||||
<template #default="scope" v-else-if="col.prop === 'endTime'">
|
<template #default="scope" v-else-if="col.prop === 'endTime'">
|
||||||
<span>{{ scope.row.endTime ? formatDateTime(scope.row.endTime) : '-' }}</span>
|
<span>{{ scope.row.endTime ? formatDateTime(scope.row.endTime) : '-' }}</span>
|
||||||
|
</template>
|
||||||
|
<template #default="scope" v-else-if="col.prop === 'schoolDoor'">
|
||||||
|
<span>{{ formatSchoolDoor(scope.row.schoolDoor) }}</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</template>
|
</template>
|
||||||
@@ -179,6 +182,7 @@ import { fetchList, delObj } from "/@/api/stuwork/stutemleaveapply";
|
|||||||
import { getDicts } from "/@/api/admin/dict";
|
import { getDicts } from "/@/api/admin/dict";
|
||||||
import { getDeptListByLevelTwo } from "/@/api/basic/basicdept";
|
import { getDeptListByLevelTwo } from "/@/api/basic/basicdept";
|
||||||
import { getClassListByRole } from "/@/api/basic/basicclass";
|
import { getClassListByRole } from "/@/api/basic/basicclass";
|
||||||
|
import { listAll as getSchoolDoorListApi } from "/@/api/safety/clouddeviceposition";
|
||||||
import { useMessage, useMessageBox } from "/@/hooks/message";
|
import { useMessage, useMessageBox } from "/@/hooks/message";
|
||||||
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
||||||
import FormDialog from './form.vue'
|
import FormDialog from './form.vue'
|
||||||
@@ -194,6 +198,7 @@ const showSearch = ref(true)
|
|||||||
const deptList = ref<any[]>([])
|
const deptList = ref<any[]>([])
|
||||||
const classList = ref<any[]>([])
|
const classList = ref<any[]>([])
|
||||||
const schoolTermList = ref<any[]>([])
|
const schoolTermList = ref<any[]>([])
|
||||||
|
const schoolDoorList = ref<any[]>([])
|
||||||
const formDialogRef = ref()
|
const formDialogRef = ref()
|
||||||
|
|
||||||
// 表格列配置
|
// 表格列配置
|
||||||
@@ -206,6 +211,7 @@ const tableColumns = [
|
|||||||
{ prop: 'realName', label: '姓名', icon: Avatar },
|
{ prop: 'realName', label: '姓名', icon: Avatar },
|
||||||
{ prop: 'startTime', label: '请假开始时间', icon: Calendar, width: 180 },
|
{ prop: 'startTime', label: '请假开始时间', icon: Calendar, width: 180 },
|
||||||
{ prop: 'endTime', label: '请假结束时间', icon: Calendar, width: 180 },
|
{ prop: 'endTime', label: '请假结束时间', icon: Calendar, width: 180 },
|
||||||
|
{ prop: 'schoolDoor', label: '校门', icon: OfficeBuilding, width: 100 },
|
||||||
{ prop: 'reason', label: '请假事由', icon: Document, minWidth: 150 },
|
{ prop: 'reason', label: '请假事由', icon: Document, minWidth: 150 },
|
||||||
{ prop: 'classTeach', label: '班主任', icon: UserFilled },
|
{ prop: 'classTeach', label: '班主任', icon: UserFilled },
|
||||||
{ prop: 'stuPhote', label: '联系方式', icon: Phone },
|
{ prop: 'stuPhote', label: '联系方式', icon: Phone },
|
||||||
@@ -256,6 +262,15 @@ const formatSchoolTerm = (value: string | number) => {
|
|||||||
return dictItem ? dictItem.label : value
|
return dictItem ? dictItem.label : value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 格式化校门
|
||||||
|
const formatSchoolDoor = (value: string) => {
|
||||||
|
if (value === null || value === undefined || value === '') {
|
||||||
|
return '-'
|
||||||
|
}
|
||||||
|
const dictItem = schoolDoorList.value.find(item => item.value === value)
|
||||||
|
return dictItem ? dictItem.label : value
|
||||||
|
}
|
||||||
|
|
||||||
// 格式化日期时间
|
// 格式化日期时间
|
||||||
const formatDateTime = (dateTime: string) => {
|
const formatDateTime = (dateTime: string) => {
|
||||||
if (!dateTime) return '-'
|
if (!dateTime) return '-'
|
||||||
@@ -331,11 +346,29 @@ const getSchoolTermDict = async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取校门列表
|
||||||
|
const getSchoolDoorData = async () => {
|
||||||
|
try {
|
||||||
|
const res = await getSchoolDoorListApi()
|
||||||
|
if (res.data) {
|
||||||
|
schoolDoorList.value = Array.isArray(res.data)
|
||||||
|
? res.data.map((item: any) => ({
|
||||||
|
label: item.positionName || item.name,
|
||||||
|
value: item.id || item.positionCode
|
||||||
|
}))
|
||||||
|
: []
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
schoolDoorList.value = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 初始化
|
// 初始化
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getDeptListData()
|
getDeptListData()
|
||||||
getClassListData()
|
getClassListData()
|
||||||
getSchoolTermDict()
|
getSchoolTermDict()
|
||||||
|
getSchoolDoorData()
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user