食堂问卷调查
This commit is contained in:
98
src/api/basic/basicpracticeclassplan.ts
Normal file
98
src/api/basic/basicpracticeclassplan.ts
Normal file
@@ -0,0 +1,98 @@
|
||||
import request from '/@/utils/request';
|
||||
|
||||
/**
|
||||
* 分页查询顶岗班级计划
|
||||
* @param query 查询参数
|
||||
*/
|
||||
export const fetchList = (query?: any) => {
|
||||
return request({
|
||||
url: '/basic/basicpracticeclassplan/page',
|
||||
method: 'get',
|
||||
params: query,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 通过id查询顶岗班级计划
|
||||
* @param id 主键id
|
||||
*/
|
||||
export const getObj = (id: string) => {
|
||||
return request({
|
||||
url: '/basic/basicpracticeclassplan/detail',
|
||||
method: 'get',
|
||||
params: { id },
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 新增顶岗班级计划
|
||||
* @param obj 数据对象
|
||||
*/
|
||||
export const addObj = (obj: any) => {
|
||||
return request({
|
||||
url: '/basic/basicpracticeclassplan',
|
||||
method: 'post',
|
||||
data: obj,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 修改顶岗班级计划
|
||||
* @param obj 数据对象
|
||||
*/
|
||||
export const putObj = (obj: any) => {
|
||||
return request({
|
||||
url: '/basic/basicpracticeclassplan/edit',
|
||||
method: 'post',
|
||||
data: obj,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 删除顶岗班级计划
|
||||
* @param ids id数组
|
||||
*/
|
||||
export const delObj = (ids: string[]) => {
|
||||
return request({
|
||||
url: '/basic/basicpracticeclassplan/delete',
|
||||
method: 'post',
|
||||
data: ids,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 下载导入模板
|
||||
*/
|
||||
export const downloadImportTemplate = () => {
|
||||
return request({
|
||||
url: '/basic/basicpracticeclassplan/importTemplate',
|
||||
method: 'get',
|
||||
responseType: 'blob',
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 导入顶岗班级计划
|
||||
* @param formData 文件表单数据
|
||||
*/
|
||||
export const importData = (formData: FormData) => {
|
||||
return request({
|
||||
url: '/basic/basicpracticeclassplan/import',
|
||||
method: 'post',
|
||||
data: formData,
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data',
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 导出模板
|
||||
*/
|
||||
export const exportTemplate = () => {
|
||||
return request({
|
||||
url: '/basic/basicpracticeclassplan/exportTemplate',
|
||||
method: 'get',
|
||||
responseType: 'blob',
|
||||
});
|
||||
};
|
||||
61
src/api/stuwork/classmasterjobapply.ts
Normal file
61
src/api/stuwork/classmasterjobapply.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
import request from '/@/utils/request';
|
||||
|
||||
/**
|
||||
* 分页查询班主任任职/调换申请
|
||||
* @param query
|
||||
*/
|
||||
export const fetchList = (query?: any) => {
|
||||
return request({
|
||||
url: '/stuwork/classmasterjobapply/page',
|
||||
method: 'get',
|
||||
params: query,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取详情
|
||||
* @param id
|
||||
*/
|
||||
export const getDetail = (id: string) => {
|
||||
return request({
|
||||
url: '/stuwork/classmasterjobapply/detail',
|
||||
method: 'get',
|
||||
params: { id },
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 新增班主任任职/调换申请
|
||||
* @param data
|
||||
*/
|
||||
export const addObj = (data: any) => {
|
||||
return request({
|
||||
url: '/stuwork/classmasterjobapply/add',
|
||||
method: 'post',
|
||||
data,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 班主任任职/调换申请审批/撤回
|
||||
* @param data
|
||||
*/
|
||||
export const auditObj = (data: any) => {
|
||||
return request({
|
||||
url: '/stuwork/classmasterjobapply/audit',
|
||||
method: 'post',
|
||||
data,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 删除班主任任职/调换申请
|
||||
* @param ids
|
||||
*/
|
||||
export const delObj = (ids: string[]) => {
|
||||
return request({
|
||||
url: '/stuwork/classmasterjobapply/delete',
|
||||
method: 'post',
|
||||
data: ids,
|
||||
});
|
||||
};
|
||||
66
src/api/stuwork/dininghall.ts
Normal file
66
src/api/stuwork/dininghall.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
import request from '/@/utils/request';
|
||||
|
||||
/**
|
||||
* 分页查询食堂列表
|
||||
*/
|
||||
export const fetchList = (query?: any) => {
|
||||
return request({
|
||||
url: '/stuwork/dininghall/page',
|
||||
method: 'get',
|
||||
params: query,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取食堂列表(不分页)
|
||||
*/
|
||||
export const getList = () => {
|
||||
return request({
|
||||
url: '/stuwork/dininghall/list',
|
||||
method: 'get',
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取食堂详情
|
||||
*/
|
||||
export const getObj = (id: string) => {
|
||||
return request({
|
||||
url: '/stuwork/dininghall/detail',
|
||||
method: 'get',
|
||||
params: { id },
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 新增食堂
|
||||
*/
|
||||
export const addObj = (data: any) => {
|
||||
return request({
|
||||
url: '/stuwork/dininghall',
|
||||
method: 'post',
|
||||
data,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 修改食堂
|
||||
*/
|
||||
export const putObj = (data: any) => {
|
||||
return request({
|
||||
url: '/stuwork/dininghall/edit',
|
||||
method: 'post',
|
||||
data,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 删除食堂
|
||||
*/
|
||||
export const delObjs = (ids: string[]) => {
|
||||
return request({
|
||||
url: '/stuwork/dininghall/delete',
|
||||
method: 'post',
|
||||
data: ids,
|
||||
});
|
||||
};
|
||||
66
src/api/stuwork/dininghallvote.ts
Normal file
66
src/api/stuwork/dininghallvote.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
import request from '/@/utils/request';
|
||||
|
||||
/**
|
||||
* 分页查询食堂调查题目列表
|
||||
*/
|
||||
export const fetchList = (query?: any) => {
|
||||
return request({
|
||||
url: '/stuwork/dininghallvote/page',
|
||||
method: 'get',
|
||||
params: query,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取食堂调查题目详情
|
||||
*/
|
||||
export const getObj = (id: string) => {
|
||||
return request({
|
||||
url: '/stuwork/dininghallvote/detail',
|
||||
method: 'get',
|
||||
params: { id },
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 新增食堂调查题目
|
||||
*/
|
||||
export const addObj = (data: any) => {
|
||||
return request({
|
||||
url: '/stuwork/dininghallvote',
|
||||
method: 'post',
|
||||
data,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 修改食堂调查题目
|
||||
*/
|
||||
export const putObj = (data: any) => {
|
||||
return request({
|
||||
url: '/stuwork/dininghallvote/edit',
|
||||
method: 'post',
|
||||
data,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 删除食堂调查题目
|
||||
*/
|
||||
export const delObjs = (ids: string[]) => {
|
||||
return request({
|
||||
url: '/stuwork/dininghallvote/delete',
|
||||
method: 'post',
|
||||
data: ids,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取题目类型字典
|
||||
*/
|
||||
export const getQuestionnaireDict = () => {
|
||||
return request({
|
||||
url: '/admin/dict/type/questionnaire',
|
||||
method: 'get',
|
||||
});
|
||||
};
|
||||
22
src/api/stuwork/dininghallvoteresult.ts
Normal file
22
src/api/stuwork/dininghallvoteresult.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import request from '/@/utils/request';
|
||||
|
||||
/**
|
||||
* 分页查询食堂调查明细
|
||||
*/
|
||||
export const fetchList = (query?: any) => {
|
||||
return request({
|
||||
url: '/stuwork/dininghallvoteresult/page',
|
||||
method: 'get',
|
||||
params: query,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取食堂列表
|
||||
*/
|
||||
export const getDiningHallList = () => {
|
||||
return request({
|
||||
url: '/stuwork/dininghall/list',
|
||||
method: 'get',
|
||||
});
|
||||
};
|
||||
23
src/api/stuwork/dininghallvoteresultanalysis.ts
Normal file
23
src/api/stuwork/dininghallvoteresultanalysis.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import request from '/@/utils/request';
|
||||
|
||||
/**
|
||||
* 获取学生统计列表
|
||||
*/
|
||||
export const getStatisticsList = (query?: any) => {
|
||||
return request({
|
||||
url: '/stuwork/dininghallvoteresultanalysis/getStatisticsList',
|
||||
method: 'get',
|
||||
params: query,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取教职工统计列表
|
||||
*/
|
||||
export const getStatisticsListByTea = (query?: any) => {
|
||||
return request({
|
||||
url: '/stuwork/dininghallvoteresultanalysis/getStatisticsListByTea',
|
||||
method: 'get',
|
||||
params: query,
|
||||
});
|
||||
};
|
||||
44
src/api/stuwork/employmentinformationsurvey.ts
Normal file
44
src/api/stuwork/employmentinformationsurvey.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import request from '/@/utils/request';
|
||||
|
||||
/**
|
||||
* 获取部门统计列表
|
||||
*/
|
||||
export const getStatisticsDept = (query?: any) => {
|
||||
return request({
|
||||
url: '/stuwork/employmentinformationsurvey/getStatisticsDept',
|
||||
method: 'get',
|
||||
params: query,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取班级统计列表
|
||||
*/
|
||||
export const getStatisticsClass = (query?: any) => {
|
||||
return request({
|
||||
url: '/stuwork/employmentinformationsurvey/getStatisticsClass',
|
||||
method: 'get',
|
||||
params: query,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取班级学生信息
|
||||
*/
|
||||
export const getClassStudentInfo = (query?: any) => {
|
||||
return request({
|
||||
url: '/stuwork/employmentinformationsurvey/getClassStudentInfo',
|
||||
method: 'get',
|
||||
params: query,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取班主任班级列表
|
||||
*/
|
||||
export const queryMasterClass = () => {
|
||||
return request({
|
||||
url: '/basic/basicclass/queryMasterClass',
|
||||
method: 'get',
|
||||
});
|
||||
};
|
||||
83
src/api/stuwork/stuturnoverlossconfig.ts
Normal file
83
src/api/stuwork/stuturnoverlossconfig.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
import request from '/@/utils/request';
|
||||
|
||||
/**
|
||||
* 分页查询学籍异动流失配置
|
||||
* @param query
|
||||
*/
|
||||
export const fetchList = (query?: any) => {
|
||||
return request({
|
||||
url: '/stuwork/stuturnoverlossconfig/page',
|
||||
method: 'get',
|
||||
params: query,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取详情
|
||||
* @param id
|
||||
*/
|
||||
export const getDetail = (id: string) => {
|
||||
return request({
|
||||
url: '/stuwork/stuturnoverlossconfig/detail',
|
||||
method: 'get',
|
||||
params: { id },
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 新增学籍异动流失配置
|
||||
* @param data
|
||||
*/
|
||||
export const addObj = (data: any) => {
|
||||
return request({
|
||||
url: '/stuwork/stuturnoverlossconfig',
|
||||
method: 'post',
|
||||
data,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 编辑学籍异动流失配置
|
||||
* @param data
|
||||
*/
|
||||
export const editObj = (data: any) => {
|
||||
return request({
|
||||
url: '/stuwork/stuturnoverlossconfig/edit',
|
||||
method: 'post',
|
||||
data,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 删除学籍异动流失配置
|
||||
* @param ids
|
||||
*/
|
||||
export const delObj = (ids: string[]) => {
|
||||
return request({
|
||||
url: '/stuwork/stuturnoverlossconfig/delete',
|
||||
method: 'post',
|
||||
data: ids,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取所有流失类型的异动配置
|
||||
*/
|
||||
export const getAllLossTurnoverTypes = () => {
|
||||
return request({
|
||||
url: '/stuwork/stuturnoverlossconfig/getAllLossTurnoverTypes',
|
||||
method: 'get',
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 根据异动类型判断是否属于流失
|
||||
* @param turnoverType
|
||||
*/
|
||||
export const isTurnoverTypeLoss = (turnoverType: string) => {
|
||||
return request({
|
||||
url: '/stuwork/stuturnoverlossconfig/isTurnoverTypeLoss',
|
||||
method: 'get',
|
||||
params: { turnoverType },
|
||||
});
|
||||
};
|
||||
@@ -96,6 +96,29 @@
|
||||
</el-table>
|
||||
<div v-if="punishList.length === 0 && !punishLoading" style="text-align: center; padding: 20px; color: #909399">暂无数据</div>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="顶岗计划" name="practicePlan">
|
||||
<el-table :data="practicePlanList" border style="width: 100%" v-loading="practicePlanLoading">
|
||||
<el-table-column type="index" label="序号" width="60" align="center" />
|
||||
<el-table-column prop="practiceStartDate" label="顶岗开始时间" width="120" />
|
||||
<el-table-column prop="practiceEndDate" label="顶岗结束时间" width="120" />
|
||||
<el-table-column prop="status" label="状态" width="100">
|
||||
<template #default="scope">
|
||||
<el-tag :type="getPracticeStatusType(scope.row.status)" size="small">
|
||||
{{ getPracticeStatusLabel(scope.row.status) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="needRemind" label="是否提醒" width="100">
|
||||
<template #default="scope">
|
||||
<span>{{ scope.row.needRemind === '1' ? '是' : '否' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="remindDays" label="提醒天数" width="100" />
|
||||
<el-table-column prop="remarks" label="备注" show-overflow-tooltip />
|
||||
</el-table>
|
||||
<div v-if="practicePlanList.length === 0 && !practicePlanLoading" style="text-align: center; padding: 20px; color: #909399">暂无数据</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
<template #footer>
|
||||
@@ -108,13 +131,13 @@
|
||||
|
||||
<script setup lang="ts" name="BasicClassDetail">
|
||||
import { ref } from 'vue';
|
||||
import { useMessage } from '/@/hooks/message';
|
||||
import { queryClassHonorByClassCode } from '/@/api/stuwork/classhonor';
|
||||
import { queryDataByClassCode } from '/@/api/stuwork/classpublicity';
|
||||
import { getClassRoomByClassCode } from '/@/api/stuwork/teachclassroomassign';
|
||||
import { queryStuNumByClassCode } from '/@/api/basic/basicstudent';
|
||||
import { fearchStuNumByClassCode } from '/@/api/stuwork/dormroomstudent';
|
||||
import { queryPunlishNumByClass } from '/@/api/stuwork/stupunlish';
|
||||
import { fetchList as fetchPracticePlanList } from '/@/api/basic/basicpracticeclassplan';
|
||||
|
||||
// 定义变量内容
|
||||
const visible = ref(false);
|
||||
@@ -136,6 +159,10 @@ const dormStudentLoading = ref(false);
|
||||
const punishList = ref<any[]>([]);
|
||||
const punishLoading = ref(false);
|
||||
|
||||
// 顶岗计划数据
|
||||
const practicePlanList = ref<any[]>([]);
|
||||
const practicePlanLoading = ref(false);
|
||||
|
||||
// 打开弹窗
|
||||
const openDialog = async (rowData: any) => {
|
||||
visible.value = true;
|
||||
@@ -159,6 +186,7 @@ const loadAllData = async (classCode: string) => {
|
||||
loadStudentNumData(classCode),
|
||||
loadDormStudentData(classCode),
|
||||
loadPunishData(classCode),
|
||||
loadPracticePlanData(classCode),
|
||||
]);
|
||||
|
||||
loading.value = false;
|
||||
@@ -260,6 +288,48 @@ const loadPunishData = async (classCode: string) => {
|
||||
}
|
||||
};
|
||||
|
||||
// 加载顶岗计划数据
|
||||
const loadPracticePlanData = async (classCode: string) => {
|
||||
practicePlanLoading.value = true;
|
||||
try {
|
||||
const res = await fetchPracticePlanList({ classCode, current: 1, size: 100 });
|
||||
if (res.data && res.data.records) {
|
||||
practicePlanList.value = res.data.records;
|
||||
} else if (Array.isArray(res.data)) {
|
||||
practicePlanList.value = res.data;
|
||||
} else {
|
||||
practicePlanList.value = [];
|
||||
}
|
||||
} catch (err: any) {
|
||||
console.error('获取顶岗计划失败', err);
|
||||
practicePlanList.value = [];
|
||||
} finally {
|
||||
practicePlanLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 获取顶岗计划状态标签
|
||||
const getPracticeStatusLabel = (status: string) => {
|
||||
const statusMap: Record<string, string> = {
|
||||
'0': '待执行',
|
||||
'1': '执行中',
|
||||
'2': '已完成',
|
||||
'3': '已取消',
|
||||
};
|
||||
return statusMap[status] || '-';
|
||||
};
|
||||
|
||||
// 获取顶岗计划状态类型
|
||||
const getPracticeStatusType = (status: string) => {
|
||||
const typeMap: Record<string, string> = {
|
||||
'0': 'info',
|
||||
'1': 'warning',
|
||||
'2': 'success',
|
||||
'3': 'danger',
|
||||
};
|
||||
return typeMap[status] || 'info';
|
||||
};
|
||||
|
||||
// 暴露方法给父组件
|
||||
defineExpose({
|
||||
openDialog,
|
||||
|
||||
@@ -61,6 +61,7 @@
|
||||
<el-button icon="Link" type="success" class="ml10" @click="handleLinkRule"> 关联门禁规则 </el-button>
|
||||
<el-button icon="Download" type="warning" class="ml10" @click="handleExport"> 导出 </el-button>
|
||||
<el-button icon="DocumentAdd" type="info" class="ml10" @click="handleGenerateAssessment"> 生成考核班级 </el-button>
|
||||
<el-button icon="Upload" type="primary" plain class="ml10" @click="handlePracticePlanImport"> 顶岗计划导入 </el-button>
|
||||
<right-toolbar v-model:showSearch="showSearch" class="ml10" @queryTable="getDataList">
|
||||
<TableColumnControl
|
||||
ref="columnControlRef"
|
||||
@@ -176,6 +177,15 @@
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 顶岗计划导入对话框 -->
|
||||
<upload-excel
|
||||
ref="practicePlanUploadRef"
|
||||
:title="'导入顶岗计划'"
|
||||
:url="'/basic/basicpracticeclassplan/import'"
|
||||
:temp-url="'/basic/basicpracticeclassplan/importTemplate'"
|
||||
@refreshDataList="getDataList"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -210,6 +220,7 @@ const StatusTag = defineStatusTag(() => import('/@/components/StatusTag/index.vu
|
||||
// 引入组件
|
||||
const FormDialog = defineAsyncComponent(() => import('./form.vue'));
|
||||
const DetailDialog = defineAsyncComponent(() => import('./detail.vue'));
|
||||
const UploadExcel = defineAsyncComponent(() => import('/@/components/Upload/Excel.vue'));
|
||||
|
||||
// 定义变量内容
|
||||
const route = useRoute();
|
||||
@@ -217,6 +228,7 @@ const formDialogRef = ref();
|
||||
const detailDialogRef = ref();
|
||||
const searchFormRef = ref();
|
||||
const columnControlRef = ref();
|
||||
const practicePlanUploadRef = ref();
|
||||
// 搜索变量
|
||||
const showSearch = ref(true);
|
||||
const deptList = ref<any[]>([]);
|
||||
@@ -428,6 +440,11 @@ const getRuleListData = async () => {
|
||||
}
|
||||
};
|
||||
|
||||
// 顶岗计划导入
|
||||
const handlePracticePlanImport = () => {
|
||||
practicePlanUploadRef.value.show();
|
||||
};
|
||||
|
||||
// 初始化
|
||||
onMounted(() => {
|
||||
getDeptListData();
|
||||
|
||||
217
src/views/basic/basicpracticeclassplan/form.vue
Normal file
217
src/views/basic/basicpracticeclassplan/form.vue
Normal file
@@ -0,0 +1,217 @@
|
||||
<template>
|
||||
<el-dialog :title="dialogTitle" v-model="visible" :close-on-click-modal="false" draggable width="600px" @close="handleClose">
|
||||
<el-form :model="form" :rules="rules" ref="formRef" label-width="120px" v-loading="loading">
|
||||
<el-form-item label="班级" prop="classCode">
|
||||
<el-select
|
||||
v-model="form.classCode"
|
||||
placeholder="请选择班级"
|
||||
filterable
|
||||
clearable
|
||||
style="width: 100%"
|
||||
@change="handleClassChange"
|
||||
>
|
||||
<el-option v-for="item in classList" :key="item.classCode" :label="`${item.classNo} - ${item.className || ''}`" :value="item.classCode">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="顶岗开始时间" prop="practiceStartDate">
|
||||
<el-date-picker
|
||||
v-model="form.practiceStartDate"
|
||||
type="date"
|
||||
placeholder="请选择顶岗开始时间"
|
||||
style="width: 100%"
|
||||
value-format="YYYY-MM-DD"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="顶岗结束时间" prop="practiceEndDate">
|
||||
<el-date-picker
|
||||
v-model="form.practiceEndDate"
|
||||
type="date"
|
||||
placeholder="请选择顶岗结束时间"
|
||||
style="width: 100%"
|
||||
value-format="YYYY-MM-DD"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-select v-model="form.status" placeholder="请选择状态" style="width: 100%">
|
||||
<el-option label="待执行" value="0" />
|
||||
<el-option label="执行中" value="1" />
|
||||
<el-option label="已完成" value="2" />
|
||||
<el-option label="已取消" value="3" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="是否需要提醒" prop="needRemind">
|
||||
<el-radio-group v-model="form.needRemind">
|
||||
<el-radio label="1">是</el-radio>
|
||||
<el-radio label="0">否</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="提醒提前天数" prop="remindDays" v-if="form.needRemind === '1'">
|
||||
<el-input-number v-model="form.remindDays" :min="1" :max="30" placeholder="请输入提醒天数" style="width: 100%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="remarks">
|
||||
<el-input v-model="form.remarks" type="textarea" :rows="3" placeholder="请输入备注" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="visible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="handleSubmit" :loading="submitLoading">确 定</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="BasicPracticeClassPlanForm">
|
||||
import { ref, reactive, computed } from 'vue';
|
||||
import { getObj, addObj, putObj } from '/@/api/basic/basicpracticeclassplan';
|
||||
import { getClassListByRole } from '/@/api/basic/basicclass';
|
||||
import { useMessage } from '/@/hooks/message';
|
||||
|
||||
// 定义变量内容
|
||||
const visible = ref(false);
|
||||
const loading = ref(false);
|
||||
const submitLoading = ref(false);
|
||||
const formRef = ref();
|
||||
const classList = ref<any[]>([]);
|
||||
|
||||
const form = reactive({
|
||||
id: '',
|
||||
classCode: '',
|
||||
classNo: '',
|
||||
className: '',
|
||||
deptCode: '',
|
||||
deptName: '',
|
||||
teacherNo: '',
|
||||
teacherRealName: '',
|
||||
practiceStartDate: '',
|
||||
practiceEndDate: '',
|
||||
status: '0',
|
||||
remarks: '',
|
||||
needRemind: '0',
|
||||
remindDays: 7,
|
||||
});
|
||||
|
||||
const rules = {
|
||||
classCode: [{ required: true, message: '请选择班级', trigger: 'change' }],
|
||||
practiceStartDate: [{ required: true, message: '请选择顶岗开始时间', trigger: 'change' }],
|
||||
practiceEndDate: [{ required: true, message: '请选择顶岗结束时间', trigger: 'change' }],
|
||||
status: [{ required: true, message: '请选择状态', trigger: 'change' }],
|
||||
};
|
||||
|
||||
// 对话框标题
|
||||
const dialogTitle = computed(() => {
|
||||
return form.id ? '编辑顶岗班级计划' : '新增顶岗班级计划';
|
||||
});
|
||||
|
||||
// 打开弹窗
|
||||
const openDialog = async (id?: string) => {
|
||||
visible.value = true;
|
||||
resetForm();
|
||||
await getClassListData();
|
||||
|
||||
if (id) {
|
||||
loading.value = true;
|
||||
try {
|
||||
const res = await getObj(id);
|
||||
if (res.data) {
|
||||
Object.assign(form, res.data);
|
||||
}
|
||||
} catch (err: any) {
|
||||
useMessage().error(err.msg || '获取详情失败');
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 重置表单
|
||||
const resetForm = () => {
|
||||
Object.assign(form, {
|
||||
id: '',
|
||||
classCode: '',
|
||||
classNo: '',
|
||||
className: '',
|
||||
deptCode: '',
|
||||
deptName: '',
|
||||
teacherNo: '',
|
||||
teacherRealName: '',
|
||||
practiceStartDate: '',
|
||||
practiceEndDate: '',
|
||||
status: '0',
|
||||
remarks: '',
|
||||
needRemind: '0',
|
||||
remindDays: 7,
|
||||
});
|
||||
formRef.value?.resetFields();
|
||||
};
|
||||
|
||||
// 班级选择变化
|
||||
const handleClassChange = (classCode: string) => {
|
||||
const selectedClass = classList.value.find((item) => item.classCode === classCode);
|
||||
if (selectedClass) {
|
||||
form.classNo = selectedClass.classNo || '';
|
||||
form.className = selectedClass.className || '';
|
||||
form.deptCode = selectedClass.deptCode || '';
|
||||
form.deptName = selectedClass.deptName || '';
|
||||
form.teacherNo = selectedClass.teacherNo || '';
|
||||
form.teacherRealName = selectedClass.teacherRealName || '';
|
||||
}
|
||||
};
|
||||
|
||||
// 获取班级列表
|
||||
const getClassListData = async () => {
|
||||
try {
|
||||
const res = await getClassListByRole();
|
||||
if (res.data) {
|
||||
classList.value = Array.isArray(res.data) ? res.data : [];
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('获取班级列表失败', err);
|
||||
classList.value = [];
|
||||
}
|
||||
};
|
||||
|
||||
// 提交表单
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
await formRef.value?.validate();
|
||||
submitLoading.value = true;
|
||||
|
||||
const submitData = { ...form };
|
||||
if (submitData.needRemind !== '1') {
|
||||
submitData.remindDays = null;
|
||||
}
|
||||
|
||||
if (submitData.id) {
|
||||
await putObj(submitData);
|
||||
useMessage().success('修改成功');
|
||||
} else {
|
||||
await addObj(submitData);
|
||||
useMessage().success('新增成功');
|
||||
}
|
||||
|
||||
visible.value = false;
|
||||
emit('refresh');
|
||||
} catch (err: any) {
|
||||
if (err !== false) {
|
||||
useMessage().error(err.msg || '操作失败');
|
||||
}
|
||||
} finally {
|
||||
submitLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 关闭弹窗
|
||||
const handleClose = () => {
|
||||
resetForm();
|
||||
};
|
||||
|
||||
// 定义事件
|
||||
const emit = defineEmits(['refresh']);
|
||||
|
||||
// 暴露方法
|
||||
defineExpose({
|
||||
openDialog,
|
||||
});
|
||||
</script>
|
||||
355
src/views/basic/basicpracticeclassplan/index.vue
Normal file
355
src/views/basic/basicpracticeclassplan/index.vue
Normal file
@@ -0,0 +1,355 @@
|
||||
<template>
|
||||
<div class="modern-page-container">
|
||||
<div class="page-wrapper">
|
||||
<!-- 搜索表单卡片 -->
|
||||
<el-card v-show="showSearch" class="search-card" shadow="never">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span class="card-title">
|
||||
<el-icon class="title-icon"><Search /></el-icon>
|
||||
筛选条件
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
<el-form :model="searchForm" ref="searchFormRef" :inline="true" @keyup.enter="handleSearch" class="search-form">
|
||||
<el-form-item label="学院" prop="deptCode">
|
||||
<el-select v-model="searchForm.deptCode" placeholder="请选择学院" clearable filterable style="width: 200px">
|
||||
<el-option v-for="item in deptList" :key="item.deptCode" :label="item.deptName" :value="item.deptCode"> </el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="班号" prop="classNo">
|
||||
<el-input v-model="searchForm.classNo" placeholder="请输入班号" clearable style="width: 200px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="班主任" prop="teacherRealName">
|
||||
<el-input v-model="searchForm.teacherRealName" placeholder="请输入班主任姓名" clearable style="width: 200px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-select v-model="searchForm.status" placeholder="请选择状态" clearable style="width: 200px">
|
||||
<el-option label="待执行" value="0" />
|
||||
<el-option label="执行中" value="1" />
|
||||
<el-option label="已完成" value="2" />
|
||||
<el-option label="已取消" value="3" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" plain icon="Search" @click="handleSearch">查询</el-button>
|
||||
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<el-row>
|
||||
<div class="mb8" style="width: 100%">
|
||||
<el-button icon="FolderAdd" type="primary" class="ml10" @click="formDialogRef.openDialog()"> 新增 </el-button>
|
||||
<el-button icon="Upload" type="success" class="ml10" @click="handleImport"> 导入 </el-button>
|
||||
<right-toolbar v-model:showSearch="showSearch" class="ml10 mr20" style="float: right" @queryTable="getDataList">
|
||||
<TableColumnControl
|
||||
ref="columnControlRef"
|
||||
:columns="tableColumns"
|
||||
v-model="visibleColumns"
|
||||
trigger-type="default"
|
||||
trigger-circle
|
||||
@change="handleColumnChange"
|
||||
@order-change="handleColumnOrderChange"
|
||||
>
|
||||
<template #trigger>
|
||||
<el-tooltip class="item" effect="dark" content="列设置" placement="top">
|
||||
<el-button circle style="margin-left: 0">
|
||||
<el-icon><Menu /></el-icon>
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</TableColumnControl>
|
||||
</right-toolbar>
|
||||
</div>
|
||||
</el-row>
|
||||
|
||||
<!-- 表格 -->
|
||||
<el-table
|
||||
:data="state.dataList"
|
||||
v-loading="state.loading"
|
||||
border
|
||||
:cell-style="tableStyle.cellStyle"
|
||||
:header-cell-style="tableStyle.headerCellStyle"
|
||||
@sort-change="sortChangeHandle"
|
||||
@selection-change="handleSelectionChange"
|
||||
>
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column type="index" label="序号" align="center" width="70">
|
||||
<template #header>
|
||||
<el-icon><List /></el-icon>
|
||||
<span style="margin-left: 4px">序号</span>
|
||||
</template>
|
||||
<template #default="{ $index }">
|
||||
{{ $index + 1 + ((state.pagination?.current || 1) - 1) * (state.pagination?.size || 10) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
v-for="col in visibleColumnsSorted"
|
||||
:key="col.prop"
|
||||
:prop="col.prop"
|
||||
:label="col.label"
|
||||
:width="col.width"
|
||||
:min-width="col.minWidth"
|
||||
:show-overflow-tooltip="col.showOverflowTooltip !== false"
|
||||
:align="col.align"
|
||||
>
|
||||
<template #header>
|
||||
<el-icon v-if="col.icon"><component :is="col.icon" /></el-icon>
|
||||
<span :style="{ marginLeft: col.icon ? '4px' : '0' }">{{ col.label }}</span>
|
||||
</template>
|
||||
<template #default="scope" v-if="col.prop === 'status'">
|
||||
<el-tag :type="getStatusType(scope.row.status)" size="small" effect="plain">
|
||||
{{ getStatusLabel(scope.row.status) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
<template #default="scope" v-else-if="col.prop === 'needRemind'">
|
||||
<el-tag :type="scope.row.needRemind === '1' ? 'success' : 'info'" size="small" effect="plain">
|
||||
{{ scope.row.needRemind === '1' ? '是' : '否' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
<template #default="scope" v-else-if="col.prop === 'practiceStartDate' || col.prop === 'practiceEndDate'">
|
||||
{{ scope.row[col.prop] || '-' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" fixed="right" width="200">
|
||||
<template #header>
|
||||
<el-icon><Setting /></el-icon>
|
||||
<span style="margin-left: 4px">操作</span>
|
||||
</template>
|
||||
<template #default="scope">
|
||||
<el-button icon="Edit" text type="primary" @click="formDialogRef.openDialog(scope.row.id)"> 编辑 </el-button>
|
||||
<el-button icon="Delete" text type="danger" @click="handleDelete([scope.row.id])"> 删除 </el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<!-- 分页 -->
|
||||
<pagination @size-change="sizeChangeHandle" @current-change="currentChangeHandle" v-bind="state.pagination" />
|
||||
</div>
|
||||
|
||||
<!-- 编辑、新增 -->
|
||||
<FormDialog ref="formDialogRef" @refresh="getDataList(false)" />
|
||||
|
||||
<!-- 导入对话框 -->
|
||||
<upload-excel
|
||||
ref="uploadRef"
|
||||
:title="'导入顶岗班级计划'"
|
||||
:url="'/basic/basicpracticeclassplan/import'"
|
||||
:temp-url="'/basic/basicpracticeclassplan/importTemplate'"
|
||||
@refreshDataList="getDataList"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="BasicPracticeClassPlan">
|
||||
import { ref, reactive, defineAsyncComponent, computed, onMounted } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import {
|
||||
List,
|
||||
OfficeBuilding,
|
||||
Grid,
|
||||
Document,
|
||||
UserFilled,
|
||||
Calendar,
|
||||
CircleCheck,
|
||||
Bell,
|
||||
Setting,
|
||||
Menu,
|
||||
Search,
|
||||
} from '@element-plus/icons-vue';
|
||||
import { BasicTableProps, useTable } from '/@/hooks/table';
|
||||
import { fetchList, delObj } from '/@/api/basic/basicpracticeclassplan';
|
||||
import { getDeptList } from '/@/api/basic/basicclass';
|
||||
import { useMessage, useMessageBox } from '/@/hooks/message';
|
||||
import TableColumnControl from '/@/components/TableColumnControl/index.vue';
|
||||
|
||||
// 引入组件
|
||||
const FormDialog = defineAsyncComponent(() => import('./form.vue'));
|
||||
const UploadExcel = defineAsyncComponent(() => import('/@/components/Upload/Excel.vue'));
|
||||
|
||||
// 定义变量内容
|
||||
const route = useRoute();
|
||||
const formDialogRef = ref();
|
||||
const searchFormRef = ref();
|
||||
const uploadRef = ref();
|
||||
const columnControlRef = ref();
|
||||
const showSearch = ref(true);
|
||||
const deptList = ref<any[]>([]);
|
||||
const selectedRows = ref<any[]>([]);
|
||||
|
||||
// 表格列配置
|
||||
const tableColumns = [
|
||||
{ prop: 'deptName', label: '学院', icon: OfficeBuilding },
|
||||
{ prop: 'classNo', label: '班号', icon: Grid },
|
||||
{ prop: 'className', label: '班级名称', icon: Document },
|
||||
{ prop: 'teacherRealName', label: '班主任', icon: UserFilled },
|
||||
{ prop: 'practiceStartDate', label: '顶岗开始时间', icon: Calendar, width: 120 },
|
||||
{ prop: 'practiceEndDate', label: '顶岗结束时间', icon: Calendar, width: 120 },
|
||||
{ prop: 'status', label: '状态', icon: CircleCheck, width: 100 },
|
||||
{ prop: 'needRemind', label: '是否提醒', icon: Bell, width: 100 },
|
||||
{ prop: 'remindDays', label: '提醒天数', icon: Bell, width: 100 },
|
||||
{ prop: 'remarks', label: '备注', icon: Document },
|
||||
];
|
||||
|
||||
// 当前显示的列
|
||||
const visibleColumns = ref<string[]>([]);
|
||||
// 列排序顺序
|
||||
const columnOrder = ref<string[]>([]);
|
||||
|
||||
// 搜索表单
|
||||
const searchForm = reactive({
|
||||
deptCode: '',
|
||||
classNo: '',
|
||||
teacherRealName: '',
|
||||
status: '',
|
||||
});
|
||||
|
||||
// 配置 useTable
|
||||
const state: BasicTableProps = reactive<BasicTableProps>({
|
||||
queryForm: searchForm,
|
||||
pageList: fetchList,
|
||||
props: {
|
||||
item: 'records',
|
||||
totalCount: 'total',
|
||||
},
|
||||
createdIsNeed: true,
|
||||
});
|
||||
|
||||
// table hook
|
||||
const { getDataList, currentChangeHandle, sizeChangeHandle, sortChangeHandle, tableStyle } = useTable(state);
|
||||
|
||||
// 排序后的表格列
|
||||
const visibleColumnsSorted = computed(() => {
|
||||
const columns = tableColumns.filter((col) => {
|
||||
const key = col.prop || col.label;
|
||||
return visibleColumns.value.includes(key);
|
||||
});
|
||||
|
||||
if (columnOrder.value.length > 0) {
|
||||
const orderedColumns: any[] = [];
|
||||
const unorderedColumns: any[] = [];
|
||||
|
||||
columnOrder.value.forEach((key) => {
|
||||
const col = columns.find((c) => (c.prop || c.label) === key);
|
||||
if (col) {
|
||||
orderedColumns.push(col);
|
||||
}
|
||||
});
|
||||
|
||||
columns.forEach((col) => {
|
||||
const key = col.prop || col.label;
|
||||
if (!columnOrder.value.includes(key)) {
|
||||
unorderedColumns.push(col);
|
||||
}
|
||||
});
|
||||
|
||||
return [...orderedColumns, ...unorderedColumns];
|
||||
}
|
||||
|
||||
return columns;
|
||||
});
|
||||
|
||||
// 初始化列配置
|
||||
const initColumns = () => {
|
||||
visibleColumns.value = tableColumns.map((col) => col.prop || col.label);
|
||||
columnOrder.value = tableColumns.map((col) => col.prop || col.label);
|
||||
};
|
||||
|
||||
// 列变化处理
|
||||
const handleColumnChange = (value: string[]) => {
|
||||
visibleColumns.value = value;
|
||||
};
|
||||
|
||||
// 列排序变化处理
|
||||
const handleColumnOrderChange = (order: string[]) => {
|
||||
columnOrder.value = order;
|
||||
};
|
||||
|
||||
// 查询
|
||||
const handleSearch = () => {
|
||||
getDataList();
|
||||
};
|
||||
|
||||
// 重置
|
||||
const handleReset = () => {
|
||||
searchFormRef.value?.resetFields();
|
||||
Object.assign(searchForm, {
|
||||
deptCode: '',
|
||||
classNo: '',
|
||||
teacherRealName: '',
|
||||
status: '',
|
||||
});
|
||||
getDataList();
|
||||
};
|
||||
|
||||
// 表格选择变化
|
||||
const handleSelectionChange = (selection: any[]) => {
|
||||
selectedRows.value = selection;
|
||||
};
|
||||
|
||||
// 删除
|
||||
const handleDelete = async (ids: string[]) => {
|
||||
try {
|
||||
await useMessageBox().confirm('确定要删除选中的记录吗?');
|
||||
await delObj(ids);
|
||||
useMessage().success('删除成功');
|
||||
getDataList();
|
||||
} catch (err: any) {
|
||||
if (err !== 'cancel') {
|
||||
useMessage().error(err.msg || '删除失败');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 导入
|
||||
const handleImport = () => {
|
||||
uploadRef.value.show();
|
||||
};
|
||||
|
||||
// 获取状态标签
|
||||
const getStatusLabel = (status: string) => {
|
||||
const statusMap: Record<string, string> = {
|
||||
'0': '待执行',
|
||||
'1': '执行中',
|
||||
'2': '已完成',
|
||||
'3': '已取消',
|
||||
};
|
||||
return statusMap[status] || '-';
|
||||
};
|
||||
|
||||
// 获取状态类型
|
||||
const getStatusType = (status: string) => {
|
||||
const typeMap: Record<string, string> = {
|
||||
'0': 'info',
|
||||
'1': 'warning',
|
||||
'2': 'success',
|
||||
'3': 'danger',
|
||||
};
|
||||
return typeMap[status] || 'info';
|
||||
};
|
||||
|
||||
// 获取学院列表
|
||||
const getDeptListData = async () => {
|
||||
try {
|
||||
const res = await getDeptList();
|
||||
if (res.data) {
|
||||
deptList.value = Array.isArray(res.data) ? res.data : [];
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('获取学院列表失败', err);
|
||||
deptList.value = [];
|
||||
}
|
||||
};
|
||||
|
||||
// 初始化
|
||||
initColumns();
|
||||
onMounted(() => {
|
||||
getDeptListData();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import '/@/assets/styles/modern-page.scss';
|
||||
</style>
|
||||
515
src/views/stuwork/classmasterjobapply/index.vue
Normal file
515
src/views/stuwork/classmasterjobapply/index.vue
Normal file
@@ -0,0 +1,515 @@
|
||||
<template>
|
||||
<div class="modern-page-container">
|
||||
<div class="page-wrapper">
|
||||
<!-- 搜索表单卡片 -->
|
||||
<el-card v-show="showSearch" class="search-card" shadow="never">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span class="card-title">
|
||||
<el-icon class="title-icon"><Search /></el-icon>
|
||||
筛选条件
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
<el-form :model="searchForm" ref="searchFormRef" :inline="true" @keyup.enter="handleSearch" class="search-form">
|
||||
<el-form-item label="申请类型" prop="applyType">
|
||||
<el-select
|
||||
v-model="searchForm.applyType"
|
||||
placeholder="请选择申请类型"
|
||||
clearable
|
||||
style="width: 200px">
|
||||
<el-option label="任职" value="1" />
|
||||
<el-option label="调换" value="2" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="申请状态" prop="status">
|
||||
<el-select
|
||||
v-model="searchForm.status"
|
||||
placeholder="请选择状态"
|
||||
clearable
|
||||
style="width: 200px">
|
||||
<el-option label="待审核" value="0" />
|
||||
<el-option label="驳回" value="1" />
|
||||
<el-option label="撤回" value="2" />
|
||||
<el-option label="通过" value="3" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="班级" prop="classCode">
|
||||
<el-select
|
||||
v-model="searchForm.classCode"
|
||||
placeholder="请选择班级"
|
||||
clearable
|
||||
filterable
|
||||
style="width: 200px">
|
||||
<el-option
|
||||
v-for="item in classList"
|
||||
:key="item.classCode"
|
||||
:label="item.classNo"
|
||||
:value="item.classCode">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" plain icon="Search" @click="handleSearch">查询</el-button>
|
||||
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
|
||||
<!-- 内容卡片 -->
|
||||
<el-card class="content-card" shadow="never">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span class="card-title">
|
||||
<el-icon class="title-icon"><Document /></el-icon>
|
||||
班主任调换申请列表
|
||||
</span>
|
||||
<div class="header-actions">
|
||||
<el-button
|
||||
icon="Plus"
|
||||
type="primary"
|
||||
@click="handleAdd">
|
||||
新增申请
|
||||
</el-button>
|
||||
<right-toolbar
|
||||
v-model:showSearch="showSearch"
|
||||
class="ml10"
|
||||
@queryTable="getDataList">
|
||||
</right-toolbar>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- 表格 -->
|
||||
<el-table
|
||||
:data="state.dataList"
|
||||
v-loading="state.loading"
|
||||
stripe
|
||||
:cell-style="tableStyle.cellStyle"
|
||||
:header-cell-style="tableStyle.headerCellStyle"
|
||||
class="modern-table">
|
||||
<el-table-column type="index" label="序号" width="70" align="center">
|
||||
<template #default="{ $index }">
|
||||
{{ $index + 1 + ((state.pagination?.current || 1) - 1) * (state.pagination?.size || 10) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="applyType" label="申请类型" align="center" width="100">
|
||||
<template #default="scope">
|
||||
<el-tag :type="scope.row.applyType === '1' ? 'success' : 'warning'" size="small">
|
||||
{{ scope.row.applyType === '1' ? '任职' : '调换' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="className" label="班级" align="center" show-overflow-tooltip>
|
||||
<template #default="scope">
|
||||
{{ scope.row.classNo }} - {{ scope.row.className }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="fromTeacherName" label="原班主任" align="center" show-overflow-tooltip>
|
||||
<template #default="scope">
|
||||
{{ scope.row.fromTeacherNo }} - {{ scope.row.fromTeacherName || '-' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="toTeacherName" label="拟任班主任" align="center" show-overflow-tooltip>
|
||||
<template #default="scope">
|
||||
{{ scope.row.toTeacherNo }} - {{ scope.row.toTeacherName || '-' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="reason" label="申请原因" align="center" show-overflow-tooltip min-width="150" />
|
||||
<el-table-column prop="effectTime" label="期望生效时间" align="center" width="160">
|
||||
<template #default="scope">
|
||||
{{ formatDateTime(scope.row.effectTime) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="status" label="状态" align="center" width="100">
|
||||
<template #default="scope">
|
||||
<el-tag :type="getStatusType(scope.row.status)" size="small">
|
||||
{{ getStatusLabel(scope.row.status) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="createTime" label="申请时间" align="center" width="160">
|
||||
<template #default="scope">
|
||||
{{ formatDateTime(scope.row.createTime) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="200" align="center" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
v-if="scope.row.status === '0'"
|
||||
icon="Check"
|
||||
link
|
||||
type="success"
|
||||
@click="handleAudit(scope.row, '3')">
|
||||
审批通过
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="scope.row.status === '0'"
|
||||
icon="Close"
|
||||
link
|
||||
type="danger"
|
||||
@click="handleAudit(scope.row, '1')">
|
||||
驳回
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="scope.row.status === '0'"
|
||||
icon="RefreshLeft"
|
||||
link
|
||||
type="warning"
|
||||
@click="handleAudit(scope.row, '2')">
|
||||
撤回
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="scope.row.status === '0'"
|
||||
icon="Delete"
|
||||
link
|
||||
type="danger"
|
||||
@click="handleDelete(scope.row)">
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<!-- 分页 -->
|
||||
<div class="pagination-wrapper">
|
||||
<pagination
|
||||
@size-change="sizeChangeHandle"
|
||||
@current-change="currentChangeHandle"
|
||||
v-bind="state.pagination" />
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
|
||||
<!-- 新增申请弹窗 -->
|
||||
<el-dialog
|
||||
v-model="dialogVisible"
|
||||
title="新增班主任调换申请"
|
||||
width="600px"
|
||||
:close-on-click-modal="false">
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:rules="formRules"
|
||||
label-width="120px">
|
||||
<el-form-item label="申请类型" prop="applyType">
|
||||
<el-radio-group v-model="formData.applyType">
|
||||
<el-radio label="1">任职</el-radio>
|
||||
<el-radio label="2">调换</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="班级" prop="classCode">
|
||||
<el-select
|
||||
v-model="formData.classCode"
|
||||
placeholder="请选择班级"
|
||||
filterable
|
||||
style="width: 100%"
|
||||
@change="handleClassChange">
|
||||
<el-option
|
||||
v-for="item in classList"
|
||||
:key="item.classCode"
|
||||
:label="`${item.classNo} - ${item.className}`"
|
||||
:value="item.classCode">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="拟任班主任" prop="toTeacherNo">
|
||||
<el-select
|
||||
v-model="formData.toTeacherNo"
|
||||
placeholder="请选择拟任班主任"
|
||||
filterable
|
||||
style="width: 100%">
|
||||
<el-option
|
||||
v-for="item in teacherList"
|
||||
:key="item.teacherNo"
|
||||
:label="`${item.teacherNo} - ${item.teacherName}`"
|
||||
:value="item.teacherNo">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="申请原因" prop="reason">
|
||||
<el-input v-model="formData.reason" type="textarea" :rows="3" placeholder="请输入申请原因" />
|
||||
</el-form-item>
|
||||
<el-form-item label="期望生效时间" prop="effectTime">
|
||||
<el-date-picker
|
||||
v-model="formData.effectTime"
|
||||
type="datetime"
|
||||
placeholder="请选择期望生效时间"
|
||||
style="width: 100%"
|
||||
format="YYYY-MM-DD HH:mm:ss"
|
||||
value-format="YYYY-MM-DD HH:mm:ss" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="dialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="handleSubmit" :loading="submitLoading">提交申请</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 审批意见弹窗 -->
|
||||
<el-dialog
|
||||
v-model="auditDialogVisible"
|
||||
:title="auditDialogTitle"
|
||||
width="500px"
|
||||
:close-on-click-modal="false">
|
||||
<el-form
|
||||
ref="auditFormRef"
|
||||
:model="auditFormData"
|
||||
:rules="auditFormRules"
|
||||
label-width="100px">
|
||||
<el-form-item label="审批意见" prop="remark">
|
||||
<el-input v-model="auditFormData.remark" type="textarea" :rows="3" placeholder="请输入审批意见" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="auditDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="handleAuditSubmit" :loading="auditLoading">确定</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="ClassMasterJobApply">
|
||||
import { reactive, ref, onMounted, computed } from 'vue'
|
||||
import { BasicTableProps, useTable } from "/@/hooks/table";
|
||||
import { fetchList, addObj, auditObj, delObj } from "/@/api/stuwork/classmasterjobapply";
|
||||
import { list as getClassList } from "/@/api/basic/basicclass";
|
||||
import { queryAllTeacher } from "/@/api/professional/professionaluser/teacherbase";
|
||||
import { useMessage, useMessageBox } from "/@/hooks/message";
|
||||
import { Search, Document, Plus, Check, Close, RefreshLeft, Delete } from '@element-plus/icons-vue'
|
||||
|
||||
// 定义变量
|
||||
const searchFormRef = ref()
|
||||
const formRef = ref()
|
||||
const auditFormRef = ref()
|
||||
const showSearch = ref(true)
|
||||
const dialogVisible = ref(false)
|
||||
const auditDialogVisible = ref(false)
|
||||
const submitLoading = ref(false)
|
||||
const auditLoading = ref(false)
|
||||
const classList = ref<any[]>([])
|
||||
const teacherList = ref<any[]>([])
|
||||
|
||||
// 搜索表单
|
||||
const searchForm = reactive({
|
||||
applyType: '',
|
||||
status: '',
|
||||
classCode: ''
|
||||
})
|
||||
|
||||
// 表单数据
|
||||
const formData = reactive({
|
||||
applyType: '2',
|
||||
classCode: '',
|
||||
toTeacherNo: '',
|
||||
reason: '',
|
||||
effectTime: ''
|
||||
})
|
||||
|
||||
// 审批表单数据
|
||||
const auditFormData = reactive({
|
||||
id: '',
|
||||
status: '',
|
||||
remark: ''
|
||||
})
|
||||
|
||||
// 表单验证规则
|
||||
const formRules = {
|
||||
applyType: [{ required: true, message: '请选择申请类型', trigger: 'change' }],
|
||||
classCode: [{ required: true, message: '请选择班级', trigger: 'change' }],
|
||||
toTeacherNo: [{ required: true, message: '请选择拟任班主任', trigger: 'change' }],
|
||||
reason: [{ required: true, message: '请输入申请原因', trigger: 'blur' }]
|
||||
}
|
||||
|
||||
// 审批表单验证规则
|
||||
const auditFormRules = {
|
||||
remark: [{ required: false, message: '请输入审批意见', trigger: 'blur' }]
|
||||
}
|
||||
|
||||
// 审批弹窗标题
|
||||
const auditDialogTitle = computed(() => {
|
||||
if (auditFormData.status === '1') return '驳回申请'
|
||||
if (auditFormData.status === '2') return '撤回申请'
|
||||
return '审批通过'
|
||||
})
|
||||
|
||||
// 配置 useTable
|
||||
const state: BasicTableProps = reactive<BasicTableProps>({
|
||||
queryForm: searchForm,
|
||||
pageList: fetchList,
|
||||
props: {
|
||||
item: 'records',
|
||||
totalCount: 'total'
|
||||
},
|
||||
createdIsNeed: true
|
||||
})
|
||||
|
||||
// table hook
|
||||
const {
|
||||
getDataList,
|
||||
currentChangeHandle,
|
||||
sizeChangeHandle,
|
||||
tableStyle
|
||||
} = useTable(state)
|
||||
|
||||
// 获取状态类型
|
||||
const getStatusType = (status: string) => {
|
||||
const map: Record<string, string> = {
|
||||
'0': 'warning',
|
||||
'1': 'danger',
|
||||
'2': 'info',
|
||||
'3': 'success'
|
||||
}
|
||||
return map[status] || 'info'
|
||||
}
|
||||
|
||||
// 获取状态标签
|
||||
const getStatusLabel = (status: string) => {
|
||||
const map: Record<string, string> = {
|
||||
'0': '待审核',
|
||||
'1': '驳回',
|
||||
'2': '撤回',
|
||||
'3': '通过'
|
||||
}
|
||||
return map[status] || status
|
||||
}
|
||||
|
||||
// 格式化日期时间
|
||||
const formatDateTime = (dateTime: string) => {
|
||||
if (!dateTime) return '-'
|
||||
return dateTime
|
||||
}
|
||||
|
||||
// 查询
|
||||
const handleSearch = () => {
|
||||
getDataList()
|
||||
}
|
||||
|
||||
// 重置
|
||||
const handleReset = () => {
|
||||
searchFormRef.value?.resetFields()
|
||||
searchForm.applyType = ''
|
||||
searchForm.status = ''
|
||||
searchForm.classCode = ''
|
||||
getDataList()
|
||||
}
|
||||
|
||||
// 获取班级列表
|
||||
const getClassListData = async () => {
|
||||
try {
|
||||
const res = await getClassList()
|
||||
if (res.data) {
|
||||
classList.value = Array.isArray(res.data) ? res.data : []
|
||||
}
|
||||
} catch (err) {
|
||||
classList.value = []
|
||||
}
|
||||
}
|
||||
|
||||
// 获取教师列表
|
||||
const getTeacherListData = async () => {
|
||||
try {
|
||||
const res = await queryAllTeacher()
|
||||
if (res.data) {
|
||||
teacherList.value = Array.isArray(res.data) ? res.data.map((item: any) => ({
|
||||
teacherNo: item.teacherNo || item.teacherCode,
|
||||
teacherName: item.teacherName || item.realName
|
||||
})) : []
|
||||
}
|
||||
} catch (err) {
|
||||
teacherList.value = []
|
||||
}
|
||||
}
|
||||
|
||||
// 班级选择变更
|
||||
const handleClassChange = (classCode: string) => {
|
||||
// 可以根据班级获取该班级的原班主任信息
|
||||
// 这里暂时清空拟任班主任
|
||||
formData.toTeacherNo = ''
|
||||
// TODO: 获取可选教师列表
|
||||
}
|
||||
|
||||
// 新增
|
||||
const handleAdd = () => {
|
||||
resetForm()
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
// 重置表单
|
||||
const resetForm = () => {
|
||||
formData.applyType = '2'
|
||||
formData.classCode = ''
|
||||
formData.toTeacherNo = ''
|
||||
formData.reason = ''
|
||||
formData.effectTime = ''
|
||||
formRef.value?.resetFields()
|
||||
}
|
||||
|
||||
// 提交
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
await formRef.value?.validate()
|
||||
submitLoading.value = true
|
||||
await addObj(formData)
|
||||
useMessage().success('提交成功')
|
||||
dialogVisible.value = false
|
||||
getDataList()
|
||||
} catch (err: any) {
|
||||
if (err?.msg) {
|
||||
useMessage().error(err.msg)
|
||||
}
|
||||
} finally {
|
||||
submitLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 审批
|
||||
const handleAudit = (row: any, status: string) => {
|
||||
auditFormData.id = row.id
|
||||
auditFormData.status = status
|
||||
auditFormData.remark = ''
|
||||
auditDialogVisible.value = true
|
||||
}
|
||||
|
||||
// 审批提交
|
||||
const handleAuditSubmit = async () => {
|
||||
try {
|
||||
await auditFormRef.value?.validate()
|
||||
auditLoading.value = true
|
||||
await auditObj(auditFormData)
|
||||
useMessage().success('操作成功')
|
||||
auditDialogVisible.value = false
|
||||
getDataList()
|
||||
} catch (err: any) {
|
||||
if (err?.msg) {
|
||||
useMessage().error(err.msg)
|
||||
}
|
||||
} finally {
|
||||
auditLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 删除
|
||||
const handleDelete = async (row: any) => {
|
||||
try {
|
||||
await useMessageBox().confirm('确定要删除该申请吗?')
|
||||
await delObj([row.id])
|
||||
useMessage().success('删除成功')
|
||||
getDataList()
|
||||
} catch (err: any) {
|
||||
if (err !== 'cancel') {
|
||||
useMessage().error(err.msg || '删除失败')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化
|
||||
onMounted(() => {
|
||||
getClassListData()
|
||||
getTeacherListData()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import '/@/assets/styles/modern-page.scss';
|
||||
</style>
|
||||
117
src/views/stuwork/dininghall/form.vue
Normal file
117
src/views/stuwork/dininghall/form.vue
Normal file
@@ -0,0 +1,117 @@
|
||||
<template>
|
||||
<el-dialog :title="form.id ? '编辑' : '新增'" v-model="visible" :width="600" :close-on-click-modal="false" draggable>
|
||||
<el-form ref="dataFormRef" :model="form" :rules="dataRules" label-width="100px" v-loading="loading">
|
||||
<el-form-item label="食堂名称" prop="diningHallName">
|
||||
<el-input v-model="form.diningHallName" placeholder="请输入食堂名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="食堂位置" prop="diningHallPlace">
|
||||
<el-input v-model="form.diningHallPlace" placeholder="请输入食堂位置" />
|
||||
</el-form-item>
|
||||
<el-form-item label="学年" prop="year">
|
||||
<el-input v-model="form.year" placeholder="请输入学年,如:2024-2025" />
|
||||
</el-form-item>
|
||||
<el-form-item label="学期" prop="period">
|
||||
<el-select v-model="form.period" placeholder="请选择学期" style="width: 100%">
|
||||
<el-option label="第一学期" value="1" />
|
||||
<el-option label="第二学期" value="2" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="visible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="onSubmit" :disabled="loading">确 认</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="DiningHallFormDialog">
|
||||
import { ref, reactive, nextTick } from 'vue';
|
||||
import { useMessage } from '/@/hooks/message';
|
||||
import { addObj, putObj } from '/@/api/stuwork/dininghall';
|
||||
|
||||
const emit = defineEmits(['refresh']);
|
||||
|
||||
const dataFormRef = ref();
|
||||
const visible = ref(false);
|
||||
const loading = ref(false);
|
||||
const operType = ref('add');
|
||||
|
||||
const form = reactive({
|
||||
id: '',
|
||||
diningHallName: '',
|
||||
diningHallPlace: '',
|
||||
year: '',
|
||||
period: '',
|
||||
});
|
||||
|
||||
const dataRules = {
|
||||
diningHallName: [{ required: true, message: '请输入食堂名称', trigger: 'blur' }],
|
||||
diningHallPlace: [{ required: true, message: '请输入食堂位置', trigger: 'blur' }],
|
||||
year: [{ required: true, message: '请输入学年', trigger: 'blur' }],
|
||||
period: [{ required: true, message: '请选择学期', trigger: 'change' }],
|
||||
};
|
||||
|
||||
const openDialog = (type: string = 'add', row?: any) => {
|
||||
visible.value = true;
|
||||
operType.value = type;
|
||||
|
||||
nextTick(() => {
|
||||
dataFormRef.value?.resetFields();
|
||||
form.id = '';
|
||||
form.diningHallName = '';
|
||||
form.diningHallPlace = '';
|
||||
form.year = '';
|
||||
form.period = '';
|
||||
|
||||
if (type === 'edit' && row) {
|
||||
form.id = row.id;
|
||||
form.diningHallName = row.diningHallName || '';
|
||||
form.diningHallPlace = row.diningHallPlace || '';
|
||||
form.year = row.year || '';
|
||||
form.period = row.period || '';
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const onSubmit = async () => {
|
||||
if (!dataFormRef.value) return;
|
||||
|
||||
await dataFormRef.value.validate(async (valid: boolean) => {
|
||||
if (!valid) return;
|
||||
|
||||
loading.value = true;
|
||||
try {
|
||||
if (operType.value === 'add') {
|
||||
await addObj({
|
||||
diningHallName: form.diningHallName,
|
||||
diningHallPlace: form.diningHallPlace,
|
||||
year: form.year,
|
||||
period: form.period,
|
||||
});
|
||||
useMessage().success('新增成功');
|
||||
} else {
|
||||
await putObj({
|
||||
id: form.id,
|
||||
diningHallName: form.diningHallName,
|
||||
diningHallPlace: form.diningHallPlace,
|
||||
year: form.year,
|
||||
period: form.period,
|
||||
});
|
||||
useMessage().success('编辑成功');
|
||||
}
|
||||
visible.value = false;
|
||||
emit('refresh');
|
||||
} catch (err: any) {
|
||||
useMessage().error(err.msg || (operType.value === 'add' ? '新增失败' : '编辑失败'));
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
openDialog,
|
||||
});
|
||||
</script>
|
||||
159
src/views/stuwork/dininghall/index.vue
Normal file
159
src/views/stuwork/dininghall/index.vue
Normal file
@@ -0,0 +1,159 @@
|
||||
<template>
|
||||
<div class="modern-page-container">
|
||||
<div class="page-wrapper">
|
||||
<el-card class="content-card" shadow="never">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span class="card-title">
|
||||
<el-icon class="title-icon"><Document /></el-icon>
|
||||
食堂列表
|
||||
</span>
|
||||
<div class="header-actions">
|
||||
<el-button icon="FolderAdd" type="primary" @click="formDialogRef.openDialog()"> 新 增 </el-button>
|
||||
<right-toolbar class="ml10" @queryTable="getDataList">
|
||||
<TableColumnControl
|
||||
ref="columnControlRef"
|
||||
:columns="tableColumns"
|
||||
v-model="visibleColumns"
|
||||
trigger-type="default"
|
||||
trigger-circle
|
||||
@change="handleColumnChange"
|
||||
@order-change="handleColumnOrderChange"
|
||||
>
|
||||
<template #trigger>
|
||||
<el-tooltip class="item" effect="dark" content="列设置" placement="top">
|
||||
<el-button circle style="margin-left: 0">
|
||||
<el-icon><Menu /></el-icon>
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</TableColumnControl>
|
||||
</right-toolbar>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- 搜索表单 -->
|
||||
<el-form :model="searchForm" inline class="search-form">
|
||||
<el-form-item label="食堂名称">
|
||||
<el-input v-model="searchForm.diningHallName" placeholder="请输入食堂名称" clearable style="width: 200px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="学年">
|
||||
<el-input v-model="searchForm.year" placeholder="请输入学年" clearable style="width: 150px" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button icon="Search" type="primary" @click="getDataList(true)"> 搜 索 </el-button>
|
||||
<el-button icon="Refresh" @click="resetSearch"> 重 置 </el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-table
|
||||
:data="state.dataList"
|
||||
v-loading="state.loading"
|
||||
stripe
|
||||
:cell-style="tableStyle.cellStyle"
|
||||
:header-cell-style="tableStyle.headerCellStyle"
|
||||
class="modern-table"
|
||||
>
|
||||
<el-table-column type="index" label="序号" width="70" align="center">
|
||||
<template #default="{ $index }">
|
||||
{{ $index + 1 }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="diningHallName" label="食堂名称" min-width="200" show-overflow-tooltip />
|
||||
<el-table-column prop="diningHallPlace" label="食堂位置" min-width="150" show-overflow-tooltip />
|
||||
<el-table-column prop="year" label="学年" width="120" align="center" />
|
||||
<el-table-column prop="period" label="学期" width="100" align="center">
|
||||
<template #default="{ row }">
|
||||
{{ row.period === '1' ? '第一学期' : row.period === '2' ? '第二学期' : '-' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="150" align="center" fixed="right">
|
||||
<template #default="scope">
|
||||
<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>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<template #empty>
|
||||
<el-empty description="暂无数据" :image-size="120" />
|
||||
</template>
|
||||
</el-table>
|
||||
|
||||
<div class="pagination-wrapper">
|
||||
<pagination @size-change="sizeChangeHandle" @current-change="currentChangeHandle" v-bind="state.pagination" />
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
|
||||
<FormDialog ref="formDialogRef" @refresh="getDataList(false)" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="DiningHall">
|
||||
import { ref, reactive, defineAsyncComponent } from 'vue';
|
||||
import { BasicTableProps, useTable } from '/@/hooks/table';
|
||||
import { fetchList, delObjs } from '/@/api/stuwork/dininghall';
|
||||
import { useMessage, useMessageBox } from '/@/hooks/message';
|
||||
import TableColumnControl from '/@/components/TableColumnControl/index.vue';
|
||||
import { Document, Menu } from '@element-plus/icons-vue';
|
||||
import { useTableColumnControl } from '/@/hooks/tableColumn';
|
||||
|
||||
const FormDialog = defineAsyncComponent(() => import('./form.vue'));
|
||||
|
||||
const formDialogRef = ref();
|
||||
const columnControlRef = ref<any>();
|
||||
|
||||
const tableColumns = [
|
||||
{ prop: 'diningHallName', label: '食堂名称' },
|
||||
{ prop: 'diningHallPlace', label: '食堂位置' },
|
||||
{ prop: 'year', label: '学年' },
|
||||
{ prop: 'period', label: '学期' },
|
||||
];
|
||||
|
||||
const { visibleColumns, visibleColumnsSorted, checkColumnVisible, handleColumnChange, handleColumnOrderChange } = useTableColumnControl(tableColumns);
|
||||
|
||||
const searchForm = reactive({
|
||||
diningHallName: '',
|
||||
year: '',
|
||||
});
|
||||
|
||||
const state: BasicTableProps = reactive<BasicTableProps>({
|
||||
queryForm: searchForm,
|
||||
pageList: fetchList,
|
||||
props: {
|
||||
item: 'records',
|
||||
totalCount: 'total',
|
||||
},
|
||||
});
|
||||
|
||||
const { getDataList, currentChangeHandle, sizeChangeHandle, tableStyle } = useTable(state);
|
||||
|
||||
const resetSearch = () => {
|
||||
searchForm.diningHallName = '';
|
||||
searchForm.year = '';
|
||||
getDataList(true);
|
||||
};
|
||||
|
||||
const handleEdit = (row: any) => {
|
||||
if (formDialogRef.value) {
|
||||
formDialogRef.value.openDialog('edit', row);
|
||||
}
|
||||
};
|
||||
|
||||
const handleDelete = async (row: any) => {
|
||||
try {
|
||||
await useMessageBox().confirm('确定要删除该食堂吗?');
|
||||
await delObjs([row.id]);
|
||||
useMessage().success('删除成功');
|
||||
getDataList();
|
||||
} catch (err: any) {
|
||||
if (err !== 'cancel') {
|
||||
useMessage().error(err.msg || '删除失败');
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import '/@/assets/styles/modern-page.scss';
|
||||
</style>
|
||||
158
src/views/stuwork/dininghallvote/form.vue
Normal file
158
src/views/stuwork/dininghallvote/form.vue
Normal file
@@ -0,0 +1,158 @@
|
||||
<template>
|
||||
<el-dialog :title="form.id ? '编辑' : '新增'" v-model="visible" :width="600" :close-on-click-modal="false" draggable>
|
||||
<el-form ref="dataFormRef" :model="form" :rules="dataRules" label-width="100px" v-loading="loading">
|
||||
<el-form-item label="食堂" prop="diningHallId">
|
||||
<el-select v-model="form.diningHallId" placeholder="请选择食堂" style="width: 100%">
|
||||
<el-option v-for="item in diningHallList" :key="item.id" :label="item.diningHallName" :value="item.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="题目名称" prop="voteTitle">
|
||||
<el-input v-model="form.voteTitle" placeholder="请输入题目名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="题目类型" prop="voteProjectId">
|
||||
<el-select v-model="form.voteProjectId" placeholder="请选择题目类型" style="width: 100%">
|
||||
<el-option v-for="item in questionnaireList" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="学年" prop="year">
|
||||
<el-input v-model="form.year" placeholder="请输入学年,如:2024-2025" />
|
||||
</el-form-item>
|
||||
<el-form-item label="学期" prop="period">
|
||||
<el-select v-model="form.period" placeholder="请选择学期" style="width: 100%">
|
||||
<el-option label="第一学期" value="1" />
|
||||
<el-option label="第二学期" value="2" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="visible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="onSubmit" :disabled="loading">确 认</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="DiningHallVoteFormDialog">
|
||||
import { ref, reactive, nextTick } from 'vue';
|
||||
import { useMessage } from '/@/hooks/message';
|
||||
import { addObj, putObj, getQuestionnaireDict } from '/@/api/stuwork/dininghallvote';
|
||||
import { getList as getDiningHallList } from '/@/api/stuwork/dininghall';
|
||||
|
||||
const props = defineProps<{
|
||||
diningHallList?: any[];
|
||||
}>();
|
||||
|
||||
const emit = defineEmits(['refresh']);
|
||||
|
||||
const dataFormRef = ref();
|
||||
const visible = ref(false);
|
||||
const loading = ref(false);
|
||||
const operType = ref('add');
|
||||
const diningHallList = ref<any[]>([]);
|
||||
const questionnaireList = ref<any[]>([]);
|
||||
|
||||
const form = reactive({
|
||||
id: '',
|
||||
diningHallId: '',
|
||||
voteTitle: '',
|
||||
voteProjectId: '',
|
||||
year: '',
|
||||
period: '',
|
||||
});
|
||||
|
||||
const dataRules = {
|
||||
diningHallId: [{ required: true, message: '请选择食堂', trigger: 'change' }],
|
||||
voteTitle: [{ required: true, message: '请输入题目名称', trigger: 'blur' }],
|
||||
voteProjectId: [{ required: true, message: '请选择题目类型', trigger: 'change' }],
|
||||
year: [{ required: true, message: '请输入学年', trigger: 'blur' }],
|
||||
period: [{ required: true, message: '请选择学期', trigger: 'change' }],
|
||||
};
|
||||
|
||||
const loadDiningHallList = async () => {
|
||||
try {
|
||||
const res = await getDiningHallList();
|
||||
diningHallList.value = res.data || [];
|
||||
} catch (error) {
|
||||
console.error('加载食堂列表失败', error);
|
||||
}
|
||||
};
|
||||
|
||||
const loadQuestionnaireDict = async () => {
|
||||
try {
|
||||
const res = await getQuestionnaireDict();
|
||||
questionnaireList.value = res.data || [];
|
||||
} catch (error) {
|
||||
console.error('加载题目类型失败', error);
|
||||
}
|
||||
};
|
||||
|
||||
const openDialog = (type: string = 'add', row?: any) => {
|
||||
visible.value = true;
|
||||
operType.value = type;
|
||||
|
||||
nextTick(() => {
|
||||
dataFormRef.value?.resetFields();
|
||||
form.id = '';
|
||||
form.diningHallId = '';
|
||||
form.voteTitle = '';
|
||||
form.voteProjectId = '';
|
||||
form.year = '';
|
||||
form.period = '';
|
||||
|
||||
if (type === 'edit' && row) {
|
||||
form.id = row.id;
|
||||
form.diningHallId = row.diningHallId || '';
|
||||
form.voteTitle = row.voteTitle || '';
|
||||
form.voteProjectId = row.voteProjectId || '';
|
||||
form.year = row.year || '';
|
||||
form.period = row.period || '';
|
||||
}
|
||||
});
|
||||
|
||||
loadDiningHallList();
|
||||
loadQuestionnaireDict();
|
||||
};
|
||||
|
||||
const onSubmit = async () => {
|
||||
if (!dataFormRef.value) return;
|
||||
|
||||
await dataFormRef.value.validate(async (valid: boolean) => {
|
||||
if (!valid) return;
|
||||
|
||||
loading.value = true;
|
||||
try {
|
||||
if (operType.value === 'add') {
|
||||
await addObj({
|
||||
diningHallId: form.diningHallId,
|
||||
voteTitle: form.voteTitle,
|
||||
voteProjectId: form.voteProjectId,
|
||||
year: form.year,
|
||||
period: form.period,
|
||||
});
|
||||
useMessage().success('新增成功');
|
||||
} else {
|
||||
await putObj({
|
||||
id: form.id,
|
||||
diningHallId: form.diningHallId,
|
||||
voteTitle: form.voteTitle,
|
||||
voteProjectId: form.voteProjectId,
|
||||
year: form.year,
|
||||
period: form.period,
|
||||
});
|
||||
useMessage().success('编辑成功');
|
||||
}
|
||||
visible.value = false;
|
||||
emit('refresh');
|
||||
} catch (err: any) {
|
||||
useMessage().error(err.msg || (operType.value === 'add' ? '新增失败' : '编辑失败'));
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
openDialog,
|
||||
});
|
||||
</script>
|
||||
173
src/views/stuwork/dininghallvote/index.vue
Normal file
173
src/views/stuwork/dininghallvote/index.vue
Normal file
@@ -0,0 +1,173 @@
|
||||
<template>
|
||||
<div class="modern-page-container">
|
||||
<div class="page-wrapper">
|
||||
<el-card class="content-card" shadow="never">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span class="card-title">
|
||||
<el-icon class="title-icon"><Document /></el-icon>
|
||||
食堂调查题目列表
|
||||
</span>
|
||||
<div class="header-actions">
|
||||
<el-button icon="FolderAdd" type="primary" @click="formDialogRef.openDialog()"> 新 增 </el-button>
|
||||
<right-toolbar class="ml10" @queryTable="getDataList">
|
||||
<TableColumnControl
|
||||
ref="columnControlRef"
|
||||
:columns="tableColumns"
|
||||
v-model="visibleColumns"
|
||||
trigger-type="default"
|
||||
trigger-circle
|
||||
@change="handleColumnChange"
|
||||
@order-change="handleColumnOrderChange"
|
||||
>
|
||||
<template #trigger>
|
||||
<el-tooltip class="item" effect="dark" content="列设置" placement="top">
|
||||
<el-button circle style="margin-left: 0">
|
||||
<el-icon><Menu /></el-icon>
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</TableColumnControl>
|
||||
</right-toolbar>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- 搜索表单 -->
|
||||
<el-form :model="searchForm" inline class="search-form">
|
||||
<el-form-item label="食堂">
|
||||
<el-select v-model="searchForm.diningHallId" placeholder="请选择食堂" clearable style="width: 200px">
|
||||
<el-option v-for="item in diningHallList" :key="item.id" :label="item.diningHallName" :value="item.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button icon="Search" type="primary" @click="getDataList(true)"> 搜 索 </el-button>
|
||||
<el-button icon="Refresh" @click="resetSearch"> 重 置 </el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-table
|
||||
:data="state.dataList"
|
||||
v-loading="state.loading"
|
||||
stripe
|
||||
:cell-style="tableStyle.cellStyle"
|
||||
:header-cell-style="tableStyle.headerCellStyle"
|
||||
class="modern-table"
|
||||
>
|
||||
<el-table-column type="index" label="序号" width="70" align="center">
|
||||
<template #default="{ $index }">
|
||||
{{ $index + 1 }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="voteTitle" label="题目名称" min-width="200" show-overflow-tooltip />
|
||||
<el-table-column prop="diningHallName" label="所属食堂" min-width="150" show-overflow-tooltip />
|
||||
<el-table-column prop="year" label="学年" width="120" align="center" />
|
||||
<el-table-column prop="period" label="学期" width="100" align="center">
|
||||
<template #default="{ row }">
|
||||
{{ row.period === '1' ? '第一学期' : row.period === '2' ? '第二学期' : '-' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="voteProjectName" label="题目类型" width="150" align="center" />
|
||||
<el-table-column label="操作" width="150" align="center" fixed="right">
|
||||
<template #default="scope">
|
||||
<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>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<template #empty>
|
||||
<el-empty description="暂无数据" :image-size="120" />
|
||||
</template>
|
||||
</el-table>
|
||||
|
||||
<div class="pagination-wrapper">
|
||||
<pagination @size-change="sizeChangeHandle" @current-change="currentChangeHandle" v-bind="state.pagination" />
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
|
||||
<FormDialog ref="formDialogRef" @refresh="getDataList(false)" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="DiningHallVote">
|
||||
import { ref, reactive, defineAsyncComponent, onMounted } from 'vue';
|
||||
import { BasicTableProps, useTable } from '/@/hooks/table';
|
||||
import { fetchList, delObjs } from '/@/api/stuwork/dininghallvote';
|
||||
import { getList as getDiningHallList } from '/@/api/stuwork/dininghall';
|
||||
import { useMessage, useMessageBox } from '/@/hooks/message';
|
||||
import TableColumnControl from '/@/components/TableColumnControl/index.vue';
|
||||
import { Document, Menu } from '@element-plus/icons-vue';
|
||||
import { useTableColumnControl } from '/@/hooks/tableColumn';
|
||||
|
||||
const FormDialog = defineAsyncComponent(() => import('./form.vue'));
|
||||
|
||||
const formDialogRef = ref();
|
||||
const columnControlRef = ref<any>();
|
||||
const diningHallList = ref<any[]>([]);
|
||||
|
||||
const tableColumns = [
|
||||
{ prop: 'voteTitle', label: '题目名称' },
|
||||
{ prop: 'diningHallName', label: '所属食堂' },
|
||||
{ prop: 'year', label: '学年' },
|
||||
{ prop: 'period', label: '学期' },
|
||||
{ prop: 'voteProjectName', label: '题目类型' },
|
||||
];
|
||||
|
||||
const { visibleColumns, visibleColumnsSorted, checkColumnVisible, handleColumnChange, handleColumnOrderChange } = useTableColumnControl(tableColumns);
|
||||
|
||||
const searchForm = reactive({
|
||||
diningHallId: '',
|
||||
});
|
||||
|
||||
const state: BasicTableProps = reactive<BasicTableProps>({
|
||||
queryForm: searchForm,
|
||||
pageList: fetchList,
|
||||
props: {
|
||||
item: 'records',
|
||||
totalCount: 'total',
|
||||
},
|
||||
});
|
||||
|
||||
const { getDataList, currentChangeHandle, sizeChangeHandle, tableStyle } = useTable(state);
|
||||
|
||||
const resetSearch = () => {
|
||||
searchForm.diningHallId = '';
|
||||
getDataList(true);
|
||||
};
|
||||
|
||||
const handleEdit = (row: any) => {
|
||||
if (formDialogRef.value) {
|
||||
formDialogRef.value.openDialog('edit', row);
|
||||
}
|
||||
};
|
||||
|
||||
const handleDelete = async (row: any) => {
|
||||
try {
|
||||
await useMessageBox().confirm('确定要删除该题目吗?');
|
||||
await delObjs([row.id]);
|
||||
useMessage().success('删除成功');
|
||||
getDataList();
|
||||
} catch (err: any) {
|
||||
if (err !== 'cancel') {
|
||||
useMessage().error(err.msg || '删除失败');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const loadDiningHallList = async () => {
|
||||
try {
|
||||
const res = await getDiningHallList();
|
||||
diningHallList.value = res.data || [];
|
||||
} catch (error) {
|
||||
console.error('加载食堂列表失败', error);
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
loadDiningHallList();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import '/@/assets/styles/modern-page.scss';
|
||||
</style>
|
||||
236
src/views/stuwork/dininghallvoteStatistics/index.vue
Normal file
236
src/views/stuwork/dininghallvoteStatistics/index.vue
Normal file
@@ -0,0 +1,236 @@
|
||||
<template>
|
||||
<div class="modern-page-container">
|
||||
<div class="page-wrapper">
|
||||
<el-card class="content-card" shadow="never">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span class="card-title">
|
||||
<el-icon class="title-icon"><DataAnalysis /></el-icon>
|
||||
食堂调查完成率统计
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- 标签页切换 -->
|
||||
<el-tabs v-model="activeTab" @tab-change="handleTabChange">
|
||||
<el-tab-pane label="学生统计" name="student">
|
||||
<!-- 搜索表单 -->
|
||||
<el-form :model="searchForm" inline class="search-form">
|
||||
<el-form-item label="学院">
|
||||
<el-select v-model="searchForm.deptCode" placeholder="请选择学院" clearable style="width: 200px">
|
||||
<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 icon="Search" type="primary" @click="getStudentData(true)"> 搜 索 </el-button>
|
||||
<el-button icon="Refresh" @click="resetStudentSearch"> 重 置 </el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-table :data="studentData" v-loading="studentLoading" stripe class="modern-table">
|
||||
<el-table-column type="index" label="序号" width="70" align="center" />
|
||||
<el-table-column prop="deptName" label="学院" min-width="150" show-overflow-tooltip />
|
||||
<el-table-column prop="className" label="班级" min-width="150" show-overflow-tooltip />
|
||||
<el-table-column prop="classMaster" label="班主任" width="100" align="center" />
|
||||
<el-table-column prop="allNum" label="总人数" width="100" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag type="info">{{ row.allNum }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="completed" label="已完成" width="100" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag type="success">{{ row.completed }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="noCompleted" label="未完成" width="100" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag type="danger">{{ row.noCompleted }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="completionRate" label="完成率" width="120" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-progress :percentage="parseFloat(row.completionRate) || 0" :stroke-width="15" :text-inside="true" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<template #empty>
|
||||
<el-empty description="暂无数据" :image-size="120" />
|
||||
</template>
|
||||
</el-table>
|
||||
|
||||
<div class="pagination-wrapper">
|
||||
<pagination @size-change="studentSizeChange" @current-change="studentCurrentChange" v-bind="studentPagination" />
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="教职工统计" name="teacher">
|
||||
<!-- 搜索表单 -->
|
||||
<el-form :model="teacherSearchForm" inline class="search-form">
|
||||
<el-form-item label="学院">
|
||||
<el-select v-model="teacherSearchForm.deptCode" placeholder="请选择学院" clearable style="width: 200px">
|
||||
<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 icon="Search" type="primary" @click="getTeacherData(true)"> 搜 索 </el-button>
|
||||
<el-button icon="Refresh" @click="resetTeacherSearch"> 重 置 </el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-table :data="teacherData" v-loading="teacherLoading" stripe class="modern-table">
|
||||
<el-table-column type="index" label="序号" width="70" align="center" />
|
||||
<el-table-column prop="deptName" label="学院" min-width="150" show-overflow-tooltip />
|
||||
<el-table-column prop="realName" label="姓名" width="100" align="center" />
|
||||
<el-table-column prop="loginName" label="账号" width="120" align="center" />
|
||||
<el-table-column prop="allNum" label="总人数" width="100" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag type="info">{{ row.allNum }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="completed" label="已完成" width="100" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag type="success">{{ row.completed }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="noCompleted" label="未完成" width="100" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag type="danger">{{ row.noCompleted }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="completionRate" label="完成率" width="120" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-progress :percentage="parseFloat(row.completionRate) || 0" :stroke-width="15" :text-inside="true" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<template #empty>
|
||||
<el-empty description="暂无数据" :image-size="120" />
|
||||
</template>
|
||||
</el-table>
|
||||
|
||||
<div class="pagination-wrapper">
|
||||
<pagination @size-change="teacherSizeChange" @current-change="teacherCurrentChange" v-bind="teacherPagination" />
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</el-card>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="DiningHallVoteStatistics">
|
||||
import { ref, reactive, onMounted } from 'vue';
|
||||
import { getStatisticsList, getStatisticsListByTea } from '/@/api/stuwork/dininghallvoteresultanalysis';
|
||||
import { DataAnalysis } from '@element-plus/icons-vue';
|
||||
|
||||
const activeTab = ref('student');
|
||||
const deptList = ref<any[]>([]);
|
||||
|
||||
// 学生统计
|
||||
const searchForm = reactive({
|
||||
deptCode: '',
|
||||
});
|
||||
const studentData = ref<any[]>([]);
|
||||
const studentLoading = ref(false);
|
||||
const studentPagination = reactive({
|
||||
current: 1,
|
||||
size: 10,
|
||||
total: 0,
|
||||
});
|
||||
|
||||
// 教职工统计
|
||||
const teacherSearchForm = reactive({
|
||||
deptCode: '',
|
||||
});
|
||||
const teacherData = ref<any[]>([]);
|
||||
const teacherLoading = ref(false);
|
||||
const teacherPagination = reactive({
|
||||
current: 1,
|
||||
size: 10,
|
||||
total: 0,
|
||||
});
|
||||
|
||||
const getStudentData = async (resetPage = false) => {
|
||||
if (resetPage) {
|
||||
studentPagination.current = 1;
|
||||
}
|
||||
studentLoading.value = true;
|
||||
try {
|
||||
const res = await getStatisticsList({
|
||||
current: studentPagination.current,
|
||||
size: studentPagination.size,
|
||||
deptCode: searchForm.deptCode,
|
||||
});
|
||||
studentData.value = res.data?.records || [];
|
||||
studentPagination.total = res.data?.total || 0;
|
||||
} catch (error) {
|
||||
console.error('获取学生统计数据失败', error);
|
||||
} finally {
|
||||
studentLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const resetStudentSearch = () => {
|
||||
searchForm.deptCode = '';
|
||||
getStudentData(true);
|
||||
};
|
||||
|
||||
const studentSizeChange = (size: number) => {
|
||||
studentPagination.size = size;
|
||||
getStudentData();
|
||||
};
|
||||
|
||||
const studentCurrentChange = (current: number) => {
|
||||
studentPagination.current = current;
|
||||
getStudentData();
|
||||
};
|
||||
|
||||
const getTeacherData = async (resetPage = false) => {
|
||||
if (resetPage) {
|
||||
teacherPagination.current = 1;
|
||||
}
|
||||
teacherLoading.value = true;
|
||||
try {
|
||||
const res = await getStatisticsListByTea({
|
||||
current: teacherPagination.current,
|
||||
size: teacherPagination.size,
|
||||
deptCode: teacherSearchForm.deptCode,
|
||||
});
|
||||
teacherData.value = res.data || [];
|
||||
teacherPagination.total = res.data?.length || 0;
|
||||
} catch (error) {
|
||||
console.error('获取教职工统计数据失败', error);
|
||||
} finally {
|
||||
teacherLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const resetTeacherSearch = () => {
|
||||
teacherSearchForm.deptCode = '';
|
||||
getTeacherData(true);
|
||||
};
|
||||
|
||||
const teacherSizeChange = (size: number) => {
|
||||
teacherPagination.size = size;
|
||||
getTeacherData();
|
||||
};
|
||||
|
||||
const teacherCurrentChange = (current: number) => {
|
||||
teacherPagination.current = current;
|
||||
getTeacherData();
|
||||
};
|
||||
|
||||
const handleTabChange = (tab: string) => {
|
||||
if (tab === 'student' && studentData.value.length === 0) {
|
||||
getStudentData();
|
||||
} else if (tab === 'teacher' && teacherData.value.length === 0) {
|
||||
getTeacherData();
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getStudentData();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import '/@/assets/styles/modern-page.scss';
|
||||
</style>
|
||||
164
src/views/stuwork/dininghallvoteresult/index.vue
Normal file
164
src/views/stuwork/dininghallvoteresult/index.vue
Normal file
@@ -0,0 +1,164 @@
|
||||
<template>
|
||||
<div class="modern-page-container">
|
||||
<div class="page-wrapper">
|
||||
<el-card class="content-card" shadow="never">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span class="card-title">
|
||||
<el-icon class="title-icon"><Document /></el-icon>
|
||||
食堂调查明细列表
|
||||
</span>
|
||||
<div class="header-actions">
|
||||
<right-toolbar class="ml10" @queryTable="getDataList">
|
||||
<TableColumnControl
|
||||
ref="columnControlRef"
|
||||
:columns="tableColumns"
|
||||
v-model="visibleColumns"
|
||||
trigger-type="default"
|
||||
trigger-circle
|
||||
@change="handleColumnChange"
|
||||
@order-change="handleColumnOrderChange"
|
||||
>
|
||||
<template #trigger>
|
||||
<el-tooltip class="item" effect="dark" content="列设置" placement="top">
|
||||
<el-button circle style="margin-left: 0">
|
||||
<el-icon><Menu /></el-icon>
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</TableColumnControl>
|
||||
</right-toolbar>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- 搜索表单 -->
|
||||
<el-form :model="searchForm" inline class="search-form">
|
||||
<el-form-item label="学年">
|
||||
<el-input v-model="searchForm.year" placeholder="请输入学年" clearable style="width: 150px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="学期">
|
||||
<el-select v-model="searchForm.period" placeholder="请选择学期" clearable style="width: 120px">
|
||||
<el-option label="第一学期" value="1" />
|
||||
<el-option label="第二学期" value="2" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button icon="Search" type="primary" @click="getDataList(true)"> 搜 索 </el-button>
|
||||
<el-button icon="Refresh" @click="resetSearch"> 重 置 </el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-table
|
||||
:data="state.dataList"
|
||||
v-loading="state.loading"
|
||||
stripe
|
||||
:cell-style="tableStyle.cellStyle"
|
||||
:header-cell-style="tableStyle.headerCellStyle"
|
||||
class="modern-table"
|
||||
>
|
||||
<el-table-column type="index" label="序号" width="70" align="center">
|
||||
<template #default="{ $index }">
|
||||
{{ $index + 1 }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="loginName" label="填写人账号" width="120" align="center" />
|
||||
<el-table-column prop="year" label="学年" width="120" align="center" />
|
||||
<el-table-column prop="period" label="学期" width="100" align="center">
|
||||
<template #default="{ row }">
|
||||
{{ row.period === '1' ? '第一学期' : row.period === '2' ? '第二学期' : '-' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="diningHallName" label="食堂名称" min-width="150" show-overflow-tooltip />
|
||||
<el-table-column prop="diningHallVoteScore" label="评分" width="80" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag type="success">{{ row.diningHallVoteScore }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="isUnderstand" label="是否了解" width="100" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="row.isUnderstand === 1 ? 'success' : 'info'">
|
||||
{{ row.isUnderstand === 1 ? '是' : '否' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="isStu" label="身份" width="100" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="row.isStu === 1 ? 'primary' : 'warning'">
|
||||
{{ row.isStu === 1 ? '学生' : '教职工' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="mostDissatisfied" label="最不满意食堂" min-width="150" show-overflow-tooltip />
|
||||
<el-table-column prop="mostVist" label="最常去食堂" min-width="150" show-overflow-tooltip />
|
||||
<el-table-column prop="mostDissatisfiedLayer" label="最不满意楼层" width="120" align="center" />
|
||||
<el-table-column prop="mostDissatisfiedWindow" label="最不满意窗口" min-width="150" show-overflow-tooltip />
|
||||
<el-table-column prop="mostVisitLayer" label="最常去楼层" width="120" align="center" />
|
||||
<el-table-column prop="createDate" label="填写时间" width="160" align="center" />
|
||||
<template #empty>
|
||||
<el-empty description="暂无数据" :image-size="120" />
|
||||
</template>
|
||||
</el-table>
|
||||
|
||||
<div class="pagination-wrapper">
|
||||
<pagination @size-change="sizeChangeHandle" @current-change="currentChangeHandle" v-bind="state.pagination" />
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="DiningHallVoteResult">
|
||||
import { ref, reactive } from 'vue';
|
||||
import { BasicTableProps, useTable } from '/@/hooks/table';
|
||||
import { fetchList } from '/@/api/stuwork/dininghallvoteresult';
|
||||
import TableColumnControl from '/@/components/TableColumnControl/index.vue';
|
||||
import { Document, Menu } from '@element-plus/icons-vue';
|
||||
import { useTableColumnControl } from '/@/hooks/tableColumn';
|
||||
|
||||
const columnControlRef = ref<any>();
|
||||
|
||||
const tableColumns = [
|
||||
{ prop: 'loginName', label: '填写人账号' },
|
||||
{ prop: 'year', label: '学年' },
|
||||
{ prop: 'period', label: '学期' },
|
||||
{ prop: 'diningHallName', label: '食堂名称' },
|
||||
{ prop: 'diningHallVoteScore', label: '评分' },
|
||||
{ prop: 'isUnderstand', label: '是否了解' },
|
||||
{ prop: 'isStu', label: '身份' },
|
||||
{ prop: 'mostDissatisfied', label: '最不满意食堂' },
|
||||
{ prop: 'mostVist', label: '最常去食堂' },
|
||||
{ prop: 'mostDissatisfiedLayer', label: '最不满意楼层' },
|
||||
{ prop: 'mostDissatisfiedWindow', label: '最不满意窗口' },
|
||||
{ prop: 'mostVisitLayer', label: '最常去楼层' },
|
||||
{ prop: 'createDate', label: '填写时间' },
|
||||
];
|
||||
|
||||
const { visibleColumns, visibleColumnsSorted, checkColumnVisible, handleColumnChange, handleColumnOrderChange } = useTableColumnControl(tableColumns);
|
||||
|
||||
const searchForm = reactive({
|
||||
year: '',
|
||||
period: '',
|
||||
});
|
||||
|
||||
const state: BasicTableProps = reactive<BasicTableProps>({
|
||||
queryForm: searchForm,
|
||||
pageList: fetchList,
|
||||
props: {
|
||||
item: 'records',
|
||||
totalCount: 'total',
|
||||
},
|
||||
});
|
||||
|
||||
const { getDataList, currentChangeHandle, sizeChangeHandle, tableStyle } = useTable(state);
|
||||
|
||||
const resetSearch = () => {
|
||||
searchForm.year = '';
|
||||
searchForm.period = '';
|
||||
getDataList(true);
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import '/@/assets/styles/modern-page.scss';
|
||||
</style>
|
||||
@@ -0,0 +1,283 @@
|
||||
<template>
|
||||
<div class="modern-page-container">
|
||||
<div class="page-wrapper">
|
||||
<el-card class="content-card" shadow="never">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span class="card-title">
|
||||
<el-icon class="title-icon"><DataAnalysis /></el-icon>
|
||||
毕业调查问卷完成率-部门
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- 搜索表单 -->
|
||||
<el-form :model="searchForm" inline class="search-form">
|
||||
<el-form-item label="毕业年份">
|
||||
<el-input v-model="searchForm.graduationYear" placeholder="请输入毕业年份" clearable style="width: 150px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="学院">
|
||||
<el-select v-model="searchForm.deptCode" placeholder="请选择学院" clearable style="width: 200px">
|
||||
<el-option v-for="item in deptList" :key="item.deptCode" :label="item.deptName" :value="item.deptCode" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="是否联院">
|
||||
<el-select v-model="searchForm.isUnion" placeholder="请选择" clearable style="width: 120px">
|
||||
<el-option label="是" value="1" />
|
||||
<el-option label="否" value="0" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button icon="Search" type="primary" @click="getData(true)"> 搜 索 </el-button>
|
||||
<el-button icon="Refresh" @click="resetSearch"> 重 置 </el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-table :data="dataList" v-loading="loading" stripe class="modern-table">
|
||||
<el-table-column type="index" label="序号" width="70" align="center" />
|
||||
<el-table-column prop="deptName" label="学院" min-width="150" show-overflow-tooltip />
|
||||
<el-table-column prop="shouldFilled" label="应填人数" width="100" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag type="info">{{ row.shouldFilled }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="hasFilled" label="已填人数" width="100" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag type="success">{{ row.hasFilled }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="noFilled" label="未填人数" width="100" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag type="danger">{{ row.noFilled }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="completionRate" label="完成率" width="150" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-progress
|
||||
:percentage="parseFloat(row.completionRate) || 0"
|
||||
:stroke-width="15"
|
||||
:text-inside="true"
|
||||
:color="getProgressColor(parseFloat(row.completionRate) || 0)"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="150" align="center" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-button icon="View" link type="primary" @click="handleViewClass(scope.row)"> 查看班级 </el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<template #empty>
|
||||
<el-empty description="暂无数据" :image-size="120" />
|
||||
</template>
|
||||
</el-table>
|
||||
</el-card>
|
||||
</div>
|
||||
|
||||
<!-- 班级详情对话框 -->
|
||||
<el-dialog v-model="classDialogVisible" title="班级填写情况" :width="900" :close-on-click-modal="false" draggable>
|
||||
<el-table :data="classData" v-loading="classLoading" stripe>
|
||||
<el-table-column type="index" label="序号" width="70" align="center" />
|
||||
<el-table-column prop="className" label="班级名称" min-width="150" show-overflow-tooltip />
|
||||
<el-table-column prop="shouldFilled" label="应填人数" width="100" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag type="info">{{ row.shouldFilled }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="hasFilled" label="已填人数" width="100" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag type="success">{{ row.hasFilled }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="noFilled" label="未填人数" width="100" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag type="danger">{{ row.noFilled }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="completionRate" label="完成率" width="150" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-progress
|
||||
:percentage="parseFloat(row.completionRate) || 0"
|
||||
:stroke-width="15"
|
||||
:text-inside="true"
|
||||
:color="getProgressColor(parseFloat(row.completionRate) || 0)"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="100" align="center">
|
||||
<template #default="scope">
|
||||
<el-button icon="View" link type="primary" @click="handleViewStudent(scope.row)"> 查看学生 </el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 学生详情对话框 -->
|
||||
<el-dialog v-model="studentDialogVisible" title="学生填写情况" :width="1100" :close-on-click-modal="false" draggable>
|
||||
<el-table :data="studentData" v-loading="studentLoading" stripe>
|
||||
<el-table-column type="index" label="序号" width="70" align="center" />
|
||||
<el-table-column prop="stuNo" label="学号" width="120" align="center" />
|
||||
<el-table-column prop="realName" label="姓名" width="100" align="center" />
|
||||
<el-table-column prop="className" label="班级" min-width="150" show-overflow-tooltip />
|
||||
<el-table-column prop="majorName" label="专业" min-width="150" show-overflow-tooltip />
|
||||
<el-table-column prop="phone" label="手机号" width="120" align="center" />
|
||||
<el-table-column prop="isWrite" label="是否填写" width="100" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="row.isWrite === 1 ? 'success' : 'danger'">
|
||||
{{ row.isWrite === 1 ? '已填写' : '未填写' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div class="pagination-wrapper" style="margin-top: 15px">
|
||||
<pagination @size-change="studentSizeChange" @current-change="studentCurrentChange" v-bind="studentPagination" />
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="EmploymentInformationSurveyCompletionRateDept">
|
||||
import { ref, reactive, onMounted } from 'vue';
|
||||
import { getStatisticsDept, getStatisticsClass, getClassStudentInfo } from '/@/api/stuwork/employmentinformationsurvey';
|
||||
import { DataAnalysis } from '@element-plus/icons-vue';
|
||||
import { useMessage } from '/@/hooks/message';
|
||||
|
||||
const searchForm = reactive({
|
||||
graduationYear: '',
|
||||
deptCode: '',
|
||||
isUnion: '',
|
||||
});
|
||||
|
||||
const dataList = ref<any[]>([]);
|
||||
const loading = ref(false);
|
||||
const deptList = ref<any[]>([]);
|
||||
|
||||
// 班级详情
|
||||
const classDialogVisible = ref(false);
|
||||
const classData = ref<any[]>([]);
|
||||
const classLoading = ref(false);
|
||||
const currentDeptCode = ref('');
|
||||
|
||||
// 学生详情
|
||||
const studentDialogVisible = ref(false);
|
||||
const studentData = ref<any[]>([]);
|
||||
const studentLoading = ref(false);
|
||||
const currentClassNo = ref('');
|
||||
const studentPagination = reactive({
|
||||
current: 1,
|
||||
size: 10,
|
||||
total: 0,
|
||||
});
|
||||
|
||||
const getData = async (resetPage = false) => {
|
||||
loading.value = true;
|
||||
try {
|
||||
const res = await getStatisticsDept({
|
||||
graduationYear: searchForm.graduationYear,
|
||||
deptCode: searchForm.deptCode,
|
||||
isUnion: searchForm.isUnion,
|
||||
});
|
||||
dataList.value = res.data || [];
|
||||
} catch (error) {
|
||||
console.error('获取数据失败', error);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const resetSearch = () => {
|
||||
searchForm.graduationYear = '';
|
||||
searchForm.deptCode = '';
|
||||
searchForm.isUnion = '';
|
||||
getData(true);
|
||||
};
|
||||
|
||||
const handleViewClass = async (row: any) => {
|
||||
classDialogVisible.value = true;
|
||||
classLoading.value = true;
|
||||
currentDeptCode.value = row.deptCode;
|
||||
try {
|
||||
const res = await getStatisticsClass({
|
||||
deptCode: row.deptCode,
|
||||
graduationYear: searchForm.graduationYear,
|
||||
});
|
||||
classData.value = res.data || [];
|
||||
} catch (error) {
|
||||
console.error('获取班级数据失败', error);
|
||||
} finally {
|
||||
classLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const handleViewStudent = async (row: any) => {
|
||||
studentDialogVisible.value = true;
|
||||
studentLoading.value = true;
|
||||
currentClassNo.value = row.classNo || row.classCode;
|
||||
studentPagination.current = 1;
|
||||
try {
|
||||
const res = await getClassStudentInfo({
|
||||
classCode: row.classNo || row.classCode,
|
||||
year: searchForm.graduationYear,
|
||||
current: studentPagination.current,
|
||||
size: studentPagination.size,
|
||||
});
|
||||
studentData.value = res.data?.list || [];
|
||||
studentPagination.total = res.data?.list?.length || 0;
|
||||
} catch (error) {
|
||||
console.error('获取学生数据失败', error);
|
||||
} finally {
|
||||
studentLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const studentSizeChange = async (size: number) => {
|
||||
studentPagination.size = size;
|
||||
studentDialogVisible.value = true;
|
||||
studentLoading.value = true;
|
||||
try {
|
||||
const res = await getClassStudentInfo({
|
||||
classCode: currentClassNo.value,
|
||||
year: searchForm.graduationYear,
|
||||
current: studentPagination.current,
|
||||
size: studentPagination.size,
|
||||
});
|
||||
studentData.value = res.data?.list || [];
|
||||
} catch (error) {
|
||||
console.error('获取学生数据失败', error);
|
||||
} finally {
|
||||
studentLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const studentCurrentChange = async (current: number) => {
|
||||
studentPagination.current = current;
|
||||
studentDialogVisible.value = true;
|
||||
studentLoading.value = true;
|
||||
try {
|
||||
const res = await getClassStudentInfo({
|
||||
classCode: currentClassNo.value,
|
||||
year: searchForm.graduationYear,
|
||||
current: studentPagination.current,
|
||||
size: studentPagination.size,
|
||||
});
|
||||
studentData.value = res.data?.list || [];
|
||||
} catch (error) {
|
||||
console.error('获取学生数据失败', error);
|
||||
} finally {
|
||||
studentLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const getProgressColor = (percentage: number) => {
|
||||
if (percentage >= 80) return '#67c23a';
|
||||
if (percentage >= 50) return '#e6a23c';
|
||||
return '#f56c6c';
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getData();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import '/@/assets/styles/modern-page.scss';
|
||||
</style>
|
||||
@@ -1,214 +1,437 @@
|
||||
<template>
|
||||
<div class="modern-page-container">
|
||||
<div class="page-wrapper">
|
||||
<!-- 筛选 -->
|
||||
<el-card class="search-card" shadow="never">
|
||||
<!-- 筛选条件 -->
|
||||
<el-card v-show="showSearch" class="search-card" shadow="never">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span class="card-title">
|
||||
<el-icon class="title-icon"><Search /></el-icon>
|
||||
统计条件
|
||||
筛选条件
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
<el-form :model="queryForm" :inline="true" class="search-form">
|
||||
<el-form :model="searchForm" ref="searchFormRef" :inline="true" class="search-form">
|
||||
<el-form-item label="毕业年份" prop="graduYear">
|
||||
<el-select v-model="queryForm.graduYear" placeholder="请选择毕业年份" clearable style="width: 160px">
|
||||
<el-option v-for="y in graduYearOptions" :key="y" :label="y + '年'" :value="y" />
|
||||
<el-select v-model="searchForm.graduYear" placeholder="请选择毕业年份" clearable filterable style="width: 160px" @change="handleSearch">
|
||||
<el-option v-for="y in graduYearOptions" :key="y" :label="y + '年'" :value="String(y)" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" @click="loadStatistics">查询统计</el-button>
|
||||
<el-button type="primary" icon="Search" @click="handleSearch">查询</el-button>
|
||||
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
|
||||
<!-- 汇总卡片 -->
|
||||
<el-row :gutter="16" class="summary-row">
|
||||
<el-col :span="6">
|
||||
<!-- 统计卡片 -->
|
||||
<el-row :gutter="20" class="stat-cards">
|
||||
<el-col :xs="24" :sm="12" :md="6">
|
||||
<el-card shadow="hover" class="stat-card">
|
||||
<div class="stat-label">毕业生总数</div>
|
||||
<div class="stat-value">{{ summary.total }}</div>
|
||||
<div class="stat-content">
|
||||
<div class="stat-icon" style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%)">
|
||||
<el-icon><UserFilled /></el-icon>
|
||||
</div>
|
||||
<div class="stat-info">
|
||||
<div class="stat-value">{{ statistics.total }}</div>
|
||||
<div class="stat-label">毕业生总数</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-card shadow="hover" class="stat-card stat-success">
|
||||
<div class="stat-label">确认毕业</div>
|
||||
<div class="stat-value">{{ summary.confirmed }}</div>
|
||||
<el-col :xs="24" :sm="12" :md="6">
|
||||
<el-card shadow="hover" class="stat-card">
|
||||
<div class="stat-content">
|
||||
<div class="stat-icon" style="background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%)">
|
||||
<el-icon><CircleCheck /></el-icon>
|
||||
</div>
|
||||
<div class="stat-info">
|
||||
<div class="stat-value">{{ statistics.confirmed }}</div>
|
||||
<div class="stat-label">确认毕业</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-card shadow="hover" class="stat-card stat-warning">
|
||||
<div class="stat-label">待确认</div>
|
||||
<div class="stat-value">{{ summary.pending }}</div>
|
||||
<el-col :xs="24" :sm="12" :md="6">
|
||||
<el-card shadow="hover" class="stat-card">
|
||||
<div class="stat-content">
|
||||
<div class="stat-icon" style="background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%)">
|
||||
<el-icon><Warning /></el-icon>
|
||||
</div>
|
||||
<div class="stat-info">
|
||||
<div class="stat-value">{{ statistics.pending }}</div>
|
||||
<div class="stat-label">待确认</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-card shadow="hover" class="stat-card stat-danger">
|
||||
<div class="stat-label">不可毕业</div>
|
||||
<div class="stat-value">{{ summary.rejected }}</div>
|
||||
<el-col :xs="24" :sm="12" :md="6">
|
||||
<el-card shadow="hover" class="stat-card">
|
||||
<div class="stat-content">
|
||||
<div class="stat-icon" style="background: linear-gradient(135deg, #eb3349 0%, #f45c43 100%)">
|
||||
<el-icon><CircleClose /></el-icon>
|
||||
</div>
|
||||
<div class="stat-info">
|
||||
<div class="stat-value">{{ statistics.rejected }}</div>
|
||||
<div class="stat-label">不可毕业</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 按学院统计表格 -->
|
||||
<el-card class="content-card" shadow="never">
|
||||
<!-- 图表区域 -->
|
||||
<el-row :gutter="20" class="chart-row">
|
||||
<el-col :xs="24" :lg="12">
|
||||
<el-card shadow="never" class="chart-card">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span class="card-title">学院毕业人数分布</span>
|
||||
</div>
|
||||
</template>
|
||||
<div ref="deptChartRef" class="chart-container"></div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :xs="24" :lg="12">
|
||||
<el-card shadow="never" class="chart-card">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span class="card-title">毕业类型分布</span>
|
||||
</div>
|
||||
</template>
|
||||
<div ref="typeChartRef" class="chart-container"></div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 详细数据表格 -->
|
||||
<el-card shadow="never" class="table-card">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span class="card-title">
|
||||
<el-icon class="title-icon"><Document /></el-icon>
|
||||
按学院统计
|
||||
</span>
|
||||
<span class="card-title">学院毕业详情</span>
|
||||
</div>
|
||||
</template>
|
||||
<el-table :data="deptStats" v-loading="loading" stripe border class="modern-table">
|
||||
<el-table-column type="index" label="序号" width="60" align="center" />
|
||||
<el-table-column prop="deptName" label="学院" min-width="140" show-overflow-tooltip />
|
||||
<el-table-column prop="total" label="应毕业人数" width="110" align="center" />
|
||||
<el-table-column prop="pending" label="待确认" width="90" align="center" />
|
||||
<el-table-column prop="confirmed" label="确认毕业" width="100" align="center" />
|
||||
<el-table-column prop="rejected" label="不可毕业" width="100" align="center" />
|
||||
<el-table-column prop="completionRate" label="完成率" width="100" align="center">
|
||||
<el-table :data="deptDetailList" v-loading="loading" stripe border>
|
||||
<el-table-column prop="deptName" label="学院" align="center" />
|
||||
<el-table-column prop="total" label="毕业生总数" align="center" />
|
||||
<el-table-column prop="confirmed" label="确认毕业" align="center">
|
||||
<template #default="scope">
|
||||
<span :class="completionRateClass(scope.row.completionRate)">
|
||||
{{ scope.row.completionRate }}
|
||||
</span>
|
||||
<el-tag type="success" size="small">{{ scope.row.confirmed }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="pending" label="待确认" align="center">
|
||||
<template #default="scope">
|
||||
<el-tag type="warning" size="small">{{ scope.row.pending }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="rejected" label="不可毕业" align="center">
|
||||
<template #default="scope">
|
||||
<el-tag type="danger" size="small">{{ scope.row.rejected }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="rate" label="毕业率" align="center" width="120">
|
||||
<template #default="scope">
|
||||
<el-progress :percentage="scope.row.rate" :stroke-width="10" :text-inside="true" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<template #empty>
|
||||
<el-empty description="请选择毕业年份并点击「查询统计」" :image-size="100" />
|
||||
</template>
|
||||
</el-card>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="GradustuAnalyse">
|
||||
import { reactive, ref, computed, onMounted } from 'vue';
|
||||
import { useMessage } from '/@/hooks/message';
|
||||
import { ref, reactive, computed, onMounted, onUnmounted, nextTick } from 'vue';
|
||||
import { Search, UserFilled, CircleCheck, CircleClose, Warning } from '@element-plus/icons-vue';
|
||||
import { fetchListForAnalyse } from '/@/api/stuwork/gradustu';
|
||||
import { Search, Document } from '@element-plus/icons-vue';
|
||||
import * as echarts from 'echarts';
|
||||
|
||||
const queryForm = reactive({
|
||||
graduYear: '',
|
||||
const searchFormRef = ref();
|
||||
const showSearch = ref(true);
|
||||
const loading = ref(false);
|
||||
const dataList = ref<any[]>([]);
|
||||
const deptChartRef = ref<HTMLElement>();
|
||||
const typeChartRef = ref<HTMLElement>();
|
||||
let deptChart: echarts.ECharts | null = null;
|
||||
let typeChart: echarts.ECharts | null = null;
|
||||
|
||||
// 搜索表单
|
||||
const searchForm = reactive({
|
||||
graduYear: String(new Date().getFullYear()),
|
||||
});
|
||||
|
||||
const loading = ref(false);
|
||||
const rawList = ref<any[]>([]);
|
||||
|
||||
// 毕业年份选项
|
||||
const graduYearOptions = computed(() => {
|
||||
const y = new Date().getFullYear();
|
||||
return Array.from({ length: 11 }, (_, i) => y - 5 + i);
|
||||
});
|
||||
|
||||
// 汇总:总人数、确认毕业、待确认、不可毕业
|
||||
const summary = computed(() => {
|
||||
const list = rawList.value;
|
||||
let pending = 0;
|
||||
let confirmed = 0;
|
||||
let rejected = 0;
|
||||
list.forEach((item: any) => {
|
||||
const s = String(item.status ?? '');
|
||||
if (s === '0') pending++;
|
||||
else if (s === '1') confirmed++;
|
||||
else if (s === '-1') rejected++;
|
||||
});
|
||||
return {
|
||||
total: list.length,
|
||||
pending,
|
||||
confirmed,
|
||||
rejected,
|
||||
};
|
||||
// 统计数据
|
||||
const statistics = computed(() => {
|
||||
const total = dataList.value.length;
|
||||
const confirmed = dataList.value.filter((item) => item.status === '1').length;
|
||||
const pending = dataList.value.filter((item) => item.status === '0').length;
|
||||
const rejected = dataList.value.filter((item) => item.status === '-1').length;
|
||||
return { total, confirmed, pending, rejected };
|
||||
});
|
||||
|
||||
// 按学院聚合
|
||||
const deptStats = computed(() => {
|
||||
const list = rawList.value;
|
||||
const map: Record<string, { deptCode: string; deptName: string; total: number; pending: number; confirmed: number; rejected: number }> = {};
|
||||
list.forEach((item: any) => {
|
||||
const code = item.deptCode || '未知';
|
||||
const name = item.deptName || item.deptCode || '未知';
|
||||
if (!map[code]) {
|
||||
map[code] = { deptCode: code, deptName: name, total: 0, pending: 0, confirmed: 0, rejected: 0 };
|
||||
// 学院详细数据
|
||||
const deptDetailList = computed(() => {
|
||||
const deptMap = new Map<string, { deptName: string; total: number; confirmed: number; pending: number; rejected: number }>();
|
||||
|
||||
dataList.value.forEach((item) => {
|
||||
const deptName = item.deptName || '未知学院';
|
||||
if (!deptMap.has(deptName)) {
|
||||
deptMap.set(deptName, { deptName, total: 0, confirmed: 0, pending: 0, rejected: 0 });
|
||||
}
|
||||
const row = map[code];
|
||||
row.total++;
|
||||
const s = String(item.status ?? '');
|
||||
if (s === '0') row.pending++;
|
||||
else if (s === '1') row.confirmed++;
|
||||
else if (s === '-1') row.rejected++;
|
||||
const dept = deptMap.get(deptName)!;
|
||||
dept.total++;
|
||||
if (item.status === '1') dept.confirmed++;
|
||||
else if (item.status === '0') dept.pending++;
|
||||
else if (item.status === '-1') dept.rejected++;
|
||||
});
|
||||
return Object.values(map).map((row) => ({
|
||||
...row,
|
||||
completionRate: row.total > 0 ? ((row.confirmed / row.total) * 100).toFixed(1) + '%' : '0%',
|
||||
|
||||
return Array.from(deptMap.values()).map((item) => ({
|
||||
...item,
|
||||
rate: item.total > 0 ? Math.round((item.confirmed / item.total) * 100) : 0,
|
||||
}));
|
||||
});
|
||||
|
||||
const completionRateClass = (rate: string) => {
|
||||
const num = parseFloat(rate);
|
||||
if (num >= 100) return 'rate-high';
|
||||
if (num >= 80) return 'rate-mid';
|
||||
return 'rate-low';
|
||||
};
|
||||
|
||||
const loadStatistics = async () => {
|
||||
if (!queryForm.graduYear) {
|
||||
useMessage().warning('请选择毕业年份');
|
||||
// 查询数据
|
||||
const handleSearch = async () => {
|
||||
if (!searchForm.graduYear) {
|
||||
return;
|
||||
}
|
||||
|
||||
loading.value = true;
|
||||
try {
|
||||
const list = await fetchListForAnalyse(queryForm.graduYear);
|
||||
rawList.value = list;
|
||||
} catch (err: any) {
|
||||
useMessage().error(err.msg || '获取数据失败');
|
||||
rawList.value = [];
|
||||
const res = await fetchListForAnalyse(searchForm.graduYear);
|
||||
dataList.value = res;
|
||||
await nextTick();
|
||||
renderCharts();
|
||||
} catch (err) {
|
||||
console.error('获取数据失败', err);
|
||||
dataList.value = [];
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 重置
|
||||
const handleReset = () => {
|
||||
searchFormRef.value?.resetFields();
|
||||
searchForm.graduYear = String(new Date().getFullYear());
|
||||
handleSearch();
|
||||
};
|
||||
|
||||
// 渲染图表
|
||||
const renderCharts = () => {
|
||||
renderDeptChart();
|
||||
renderTypeChart();
|
||||
};
|
||||
|
||||
// 渲染学院分布图表
|
||||
const renderDeptChart = () => {
|
||||
if (!deptChartRef.value) return;
|
||||
|
||||
if (deptChart) {
|
||||
deptChart.dispose();
|
||||
}
|
||||
|
||||
deptChart = echarts.init(deptChartRef.value);
|
||||
|
||||
const chartData = deptDetailList.value.map((item) => ({
|
||||
name: item.deptName,
|
||||
value: item.total,
|
||||
}));
|
||||
|
||||
const option: echarts.EChartsOption = {
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
formatter: '{b}: {c}人 ({d}%)',
|
||||
},
|
||||
legend: {
|
||||
orient: 'vertical',
|
||||
left: 'left',
|
||||
top: 'center',
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '毕业人数',
|
||||
type: 'pie',
|
||||
radius: ['40%', '70%'],
|
||||
center: ['60%', '50%'],
|
||||
avoidLabelOverlap: false,
|
||||
itemStyle: {
|
||||
borderRadius: 10,
|
||||
borderColor: '#fff',
|
||||
borderWidth: 2,
|
||||
},
|
||||
label: {
|
||||
show: false,
|
||||
position: 'center',
|
||||
},
|
||||
emphasis: {
|
||||
label: {
|
||||
show: true,
|
||||
fontSize: 16,
|
||||
fontWeight: 'bold',
|
||||
},
|
||||
},
|
||||
labelLine: {
|
||||
show: false,
|
||||
},
|
||||
data: chartData,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
deptChart.setOption(option);
|
||||
};
|
||||
|
||||
// 渲染毕业类型图表
|
||||
const renderTypeChart = () => {
|
||||
if (!typeChartRef.value) return;
|
||||
|
||||
if (typeChart) {
|
||||
typeChart.dispose();
|
||||
}
|
||||
|
||||
typeChart = echarts.init(typeChartRef.value);
|
||||
|
||||
const typeCount = {
|
||||
段段清: dataList.value.filter((item) => item.type === '1').length,
|
||||
正常毕业: dataList.value.filter((item) => item.type === '2').length,
|
||||
未知: dataList.value.filter((item) => item.type !== '1' && item.type !== '2').length,
|
||||
};
|
||||
|
||||
const chartData = Object.entries(typeCount)
|
||||
.filter(([_, value]) => value > 0)
|
||||
.map(([name, value]) => ({ name, value }));
|
||||
|
||||
const option: echarts.EChartsOption = {
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
formatter: '{b}: {c}人 ({d}%)',
|
||||
},
|
||||
legend: {
|
||||
orient: 'vertical',
|
||||
left: 'left',
|
||||
top: 'center',
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '毕业类型',
|
||||
type: 'pie',
|
||||
radius: '60%',
|
||||
center: ['60%', '50%'],
|
||||
data: chartData,
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
shadowBlur: 10,
|
||||
shadowOffsetX: 0,
|
||||
shadowColor: 'rgba(0, 0, 0, 0.5)',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
typeChart.setOption(option);
|
||||
};
|
||||
|
||||
// 窗口大小变化时重绘图表
|
||||
const handleResize = () => {
|
||||
deptChart?.resize();
|
||||
typeChart?.resize();
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
queryForm.graduYear = String(new Date().getFullYear());
|
||||
handleSearch();
|
||||
window.addEventListener('resize', handleResize);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('resize', handleResize);
|
||||
deptChart?.dispose();
|
||||
typeChart?.dispose();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import '/@/assets/styles/modern-page.scss';
|
||||
|
||||
.summary-row {
|
||||
margin-bottom: 16px;
|
||||
.stat-cards {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.stat-card {
|
||||
text-align: center;
|
||||
.stat-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
.stat-icon {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
border-radius: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 16px;
|
||||
|
||||
.el-icon {
|
||||
font-size: 28px;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.stat-info {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
font-size: 28px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
font-size: 14px;
|
||||
color: var(--el-text-color-secondary);
|
||||
}
|
||||
.stat-value {
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
margin-top: 8px;
|
||||
}
|
||||
&.stat-success .stat-value {
|
||||
color: var(--el-color-success);
|
||||
}
|
||||
&.stat-warning .stat-value {
|
||||
color: var(--el-color-warning);
|
||||
}
|
||||
&.stat-danger .stat-value {
|
||||
color: var(--el-color-danger);
|
||||
color: #909399;
|
||||
margin-top: 4px;
|
||||
}
|
||||
}
|
||||
.rate-high {
|
||||
color: var(--el-color-success);
|
||||
font-weight: 500;
|
||||
|
||||
.chart-row {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.rate-mid {
|
||||
color: var(--el-color-warning);
|
||||
|
||||
.chart-card {
|
||||
.card-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
.rate-low {
|
||||
color: var(--el-color-danger);
|
||||
|
||||
.chart-container {
|
||||
height: 350px;
|
||||
}
|
||||
|
||||
.table-card {
|
||||
.card-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
306
src/views/stuwork/stuturnoverlossconfig/index.vue
Normal file
306
src/views/stuwork/stuturnoverlossconfig/index.vue
Normal file
@@ -0,0 +1,306 @@
|
||||
<template>
|
||||
<div class="modern-page-container">
|
||||
<div class="page-wrapper">
|
||||
<!-- 搜索表单卡片 -->
|
||||
<el-card v-show="showSearch" class="search-card" shadow="never">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span class="card-title">
|
||||
<el-icon class="title-icon"><Search /></el-icon>
|
||||
筛选条件
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
<el-form :model="searchForm" ref="searchFormRef" :inline="true" @keyup.enter="handleSearch" class="search-form">
|
||||
<el-form-item label="异动类型编码" prop="turnoverType">
|
||||
<el-input
|
||||
v-model="searchForm.turnoverType"
|
||||
placeholder="请输入异动类型编码"
|
||||
clearable
|
||||
style="width: 200px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="异动类型名称" prop="turnoverTypeName">
|
||||
<el-input
|
||||
v-model="searchForm.turnoverTypeName"
|
||||
placeholder="请输入异动类型名称"
|
||||
clearable
|
||||
style="width: 200px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="是否流失" prop="isLoss">
|
||||
<el-select
|
||||
v-model="searchForm.isLoss"
|
||||
placeholder="请选择"
|
||||
clearable
|
||||
style="width: 200px">
|
||||
<el-option label="是" value="1" />
|
||||
<el-option label="否" value="0" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" plain icon="Search" @click="handleSearch">查询</el-button>
|
||||
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
|
||||
<!-- 内容卡片 -->
|
||||
<el-card class="content-card" shadow="never">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span class="card-title">
|
||||
<el-icon class="title-icon"><Setting /></el-icon>
|
||||
异动流失配置列表
|
||||
</span>
|
||||
<div class="header-actions">
|
||||
<el-button
|
||||
icon="Plus"
|
||||
type="primary"
|
||||
@click="handleAdd">
|
||||
新增配置
|
||||
</el-button>
|
||||
<right-toolbar
|
||||
v-model:showSearch="showSearch"
|
||||
class="ml10"
|
||||
@queryTable="getDataList">
|
||||
</right-toolbar>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- 表格 -->
|
||||
<el-table
|
||||
:data="state.dataList"
|
||||
v-loading="state.loading"
|
||||
stripe
|
||||
:cell-style="tableStyle.cellStyle"
|
||||
:header-cell-style="tableStyle.headerCellStyle"
|
||||
class="modern-table">
|
||||
<el-table-column type="index" label="序号" width="70" align="center">
|
||||
<template #default="{ $index }">
|
||||
{{ $index + 1 + ((state.pagination?.current || 1) - 1) * (state.pagination?.size || 10) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="turnoverType" label="异动类型编码" align="center" show-overflow-tooltip />
|
||||
<el-table-column prop="turnoverTypeName" label="异动类型名称" align="center" show-overflow-tooltip />
|
||||
<el-table-column prop="isLoss" label="是否流失" align="center" width="120">
|
||||
<template #default="scope">
|
||||
<el-tag :type="scope.row.isLoss === '1' ? 'danger' : 'success'" size="small">
|
||||
{{ scope.row.isLoss === '1' ? '是' : '否' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="remarks" label="备注" align="center" show-overflow-tooltip min-width="150" />
|
||||
<el-table-column prop="createTime" label="创建时间" align="center" width="180">
|
||||
<template #default="scope">
|
||||
{{ scope.row.createTime || '-' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="160" align="center" fixed="right">
|
||||
<template #default="scope">
|
||||
<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>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<!-- 分页 -->
|
||||
<div class="pagination-wrapper">
|
||||
<pagination
|
||||
@size-change="sizeChangeHandle"
|
||||
@current-change="currentChangeHandle"
|
||||
v-bind="state.pagination" />
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
|
||||
<!-- 新增/编辑弹窗 -->
|
||||
<el-dialog
|
||||
v-model="dialogVisible"
|
||||
:title="dialogTitle"
|
||||
width="500px"
|
||||
:close-on-click-modal="false">
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:rules="formRules"
|
||||
label-width="120px">
|
||||
<el-form-item label="异动类型编码" prop="turnoverType">
|
||||
<el-input v-model="formData.turnoverType" placeholder="请输入异动类型编码" />
|
||||
</el-form-item>
|
||||
<el-form-item label="异动类型名称" prop="turnoverTypeName">
|
||||
<el-input v-model="formData.turnoverTypeName" placeholder="请输入异动类型名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="是否流失" prop="isLoss">
|
||||
<el-radio-group v-model="formData.isLoss">
|
||||
<el-radio label="1">是</el-radio>
|
||||
<el-radio label="0">否</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="remarks">
|
||||
<el-input v-model="formData.remarks" type="textarea" :rows="3" placeholder="请输入备注" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="dialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="handleSubmit" :loading="submitLoading">确定</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="StuTurnoverLossConfig">
|
||||
import { reactive, ref } from 'vue'
|
||||
import { BasicTableProps, useTable } from "/@/hooks/table";
|
||||
import { fetchList, addObj, editObj, delObj } from "/@/api/stuwork/stuturnoverlossconfig";
|
||||
import { useMessage, useMessageBox } from "/@/hooks/message";
|
||||
import { Search, Setting, Plus, Edit, Delete } from '@element-plus/icons-vue'
|
||||
|
||||
// 定义变量
|
||||
const searchFormRef = ref()
|
||||
const formRef = ref()
|
||||
const showSearch = ref(true)
|
||||
const dialogVisible = ref(false)
|
||||
const dialogTitle = ref('新增配置')
|
||||
const submitLoading = ref(false)
|
||||
const isEdit = ref(false)
|
||||
|
||||
// 搜索表单
|
||||
const searchForm = reactive({
|
||||
turnoverType: '',
|
||||
turnoverTypeName: '',
|
||||
isLoss: ''
|
||||
})
|
||||
|
||||
// 表单数据
|
||||
const formData = reactive({
|
||||
id: '',
|
||||
turnoverType: '',
|
||||
turnoverTypeName: '',
|
||||
isLoss: '0',
|
||||
remarks: ''
|
||||
})
|
||||
|
||||
// 表单验证规则
|
||||
const formRules = {
|
||||
turnoverType: [{ required: true, message: '请输入异动类型编码', trigger: 'blur' }],
|
||||
turnoverTypeName: [{ required: true, message: '请输入异动类型名称', trigger: 'blur' }],
|
||||
isLoss: [{ required: true, message: '请选择是否流失', trigger: 'change' }]
|
||||
}
|
||||
|
||||
// 配置 useTable
|
||||
const state: BasicTableProps = reactive<BasicTableProps>({
|
||||
queryForm: searchForm,
|
||||
pageList: fetchList,
|
||||
props: {
|
||||
item: 'records',
|
||||
totalCount: 'total'
|
||||
},
|
||||
createdIsNeed: true
|
||||
})
|
||||
|
||||
// table hook
|
||||
const {
|
||||
getDataList,
|
||||
currentChangeHandle,
|
||||
sizeChangeHandle,
|
||||
tableStyle
|
||||
} = useTable(state)
|
||||
|
||||
// 查询
|
||||
const handleSearch = () => {
|
||||
getDataList()
|
||||
}
|
||||
|
||||
// 重置
|
||||
const handleReset = () => {
|
||||
searchFormRef.value?.resetFields()
|
||||
searchForm.turnoverType = ''
|
||||
searchForm.turnoverTypeName = ''
|
||||
searchForm.isLoss = ''
|
||||
getDataList()
|
||||
}
|
||||
|
||||
// 新增
|
||||
const handleAdd = () => {
|
||||
isEdit.value = false
|
||||
dialogTitle.value = '新增配置'
|
||||
resetForm()
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
// 编辑
|
||||
const handleEdit = (row: any) => {
|
||||
isEdit.value = true
|
||||
dialogTitle.value = '编辑配置'
|
||||
resetForm()
|
||||
formData.id = row.id
|
||||
formData.turnoverType = row.turnoverType
|
||||
formData.turnoverTypeName = row.turnoverTypeName
|
||||
formData.isLoss = row.isLoss
|
||||
formData.remarks = row.remarks || ''
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
// 重置表单
|
||||
const resetForm = () => {
|
||||
formData.id = ''
|
||||
formData.turnoverType = ''
|
||||
formData.turnoverTypeName = ''
|
||||
formData.isLoss = '0'
|
||||
formData.remarks = ''
|
||||
formRef.value?.resetFields()
|
||||
}
|
||||
|
||||
// 提交
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
await formRef.value?.validate()
|
||||
submitLoading.value = true
|
||||
if (isEdit.value) {
|
||||
await editObj(formData)
|
||||
useMessage().success('修改成功')
|
||||
} else {
|
||||
await addObj(formData)
|
||||
useMessage().success('新增成功')
|
||||
}
|
||||
dialogVisible.value = false
|
||||
getDataList()
|
||||
} catch (err: any) {
|
||||
if (err?.msg) {
|
||||
useMessage().error(err.msg)
|
||||
}
|
||||
} finally {
|
||||
submitLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 删除
|
||||
const handleDelete = async (row: any) => {
|
||||
try {
|
||||
await useMessageBox().confirm('确定要删除该配置吗?')
|
||||
await delObj([row.id])
|
||||
useMessage().success('删除成功')
|
||||
getDataList()
|
||||
} catch (err: any) {
|
||||
if (err !== 'cancel') {
|
||||
useMessage().error(err.msg || '删除失败')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import '/@/assets/styles/modern-page.scss';
|
||||
</style>
|
||||
Reference in New Issue
Block a user