Merge branch 'developer'
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',
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -460,7 +460,7 @@ export const queryStuBaseByNo = (obj: string | number) => {
|
|||||||
*/
|
*/
|
||||||
export const queryStuindex = (query?: any) => {
|
export const queryStuindex = (query?: any) => {
|
||||||
return request({
|
return request({
|
||||||
url: '/basic/basicstudent/queryStuindex',
|
url: '/basic/basicstudentinfo/queryXJDataByPage',
|
||||||
method: 'get',
|
method: 'get',
|
||||||
params: query,
|
params: query,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import request from '/@/utils/request';
|
|||||||
*/
|
*/
|
||||||
export const fetchList = (query?: any) => {
|
export const fetchList = (query?: any) => {
|
||||||
return request({
|
return request({
|
||||||
url: '/basic/basicstudent/avatar/list',
|
url: '/basic/basicstudent/avatar/page',
|
||||||
method: 'get',
|
method: 'get',
|
||||||
params: query,
|
params: query,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -437,3 +437,12 @@ export function getSupplementFilesByApplyId(applyId: string) {
|
|||||||
params: { purchaseId: applyId },
|
params: { purchaseId: applyId },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function exportPurchaseApply(params?: any) {
|
||||||
|
return request({
|
||||||
|
url: '/purchase/purchasingapply/export',
|
||||||
|
method: 'get',
|
||||||
|
params,
|
||||||
|
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 },
|
||||||
|
});
|
||||||
|
};
|
||||||
83
src/api/stuwork/stuturnoverrule.ts
Normal file
83
src/api/stuwork/stuturnoverrule.ts
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
import request from '/@/utils/request';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页查询异动规则配置列表
|
||||||
|
* @param query
|
||||||
|
*/
|
||||||
|
export const fetchList = (query?: any) => {
|
||||||
|
return request({
|
||||||
|
url: '/stuwork/stuturnoverrule/page',
|
||||||
|
method: 'get',
|
||||||
|
params: query,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增异动规则配置
|
||||||
|
* @param data
|
||||||
|
*/
|
||||||
|
export const addObj = (data: any) => {
|
||||||
|
return request({
|
||||||
|
url: '/stuwork/stuturnoverrule',
|
||||||
|
method: 'post',
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取详情
|
||||||
|
* @param id
|
||||||
|
*/
|
||||||
|
export const getDetail = (id: string) => {
|
||||||
|
return request({
|
||||||
|
url: '/stuwork/stuturnoverrule/detail',
|
||||||
|
method: 'get',
|
||||||
|
params: { id },
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 编辑异动规则配置
|
||||||
|
* @param data
|
||||||
|
*/
|
||||||
|
export const editObj = (data: any) => {
|
||||||
|
return request({
|
||||||
|
url: '/stuwork/stuturnoverrule/edit',
|
||||||
|
method: 'post',
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除异动规则配置
|
||||||
|
* @param ids
|
||||||
|
*/
|
||||||
|
export const delObj = (ids: string[]) => {
|
||||||
|
return request({
|
||||||
|
url: '/stuwork/stuturnoverrule/delete',
|
||||||
|
method: 'post',
|
||||||
|
data: ids,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取所有启用的异动规则
|
||||||
|
*/
|
||||||
|
export const getAllActiveRules = () => {
|
||||||
|
return request({
|
||||||
|
url: '/stuwork/stuturnoverrule/getAllActiveRules',
|
||||||
|
method: 'get',
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据异动类型获取规则
|
||||||
|
* @param turnoverType 异动类型
|
||||||
|
*/
|
||||||
|
export const getRulesByTurnoverType = (turnoverType: string) => {
|
||||||
|
return request({
|
||||||
|
url: '/stuwork/stuturnoverrule/getRulesByTurnoverType',
|
||||||
|
method: 'get',
|
||||||
|
params: { turnoverType },
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -1,14 +1,86 @@
|
|||||||
import request from '/@/utils/request';
|
import request from '/@/utils/request';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查看水电明细
|
* 分页查询宿舍水电月明细
|
||||||
* @param roomNo 宿舍号
|
* @param query
|
||||||
*/
|
*/
|
||||||
export const lookDetails = (roomNo: string) => {
|
export const fetchList = (query?: any) => {
|
||||||
|
return request({
|
||||||
|
url: '/stuwork/watermonthreport/page',
|
||||||
|
method: 'get',
|
||||||
|
params: query,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增宿舍水电月明细
|
||||||
|
* @param data
|
||||||
|
*/
|
||||||
|
export const addObj = (data: any) => {
|
||||||
|
return request({
|
||||||
|
url: '/stuwork/watermonthreport',
|
||||||
|
method: 'post',
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取详情
|
||||||
|
* @param id
|
||||||
|
*/
|
||||||
|
export const getDetail = (id: string) => {
|
||||||
|
return request({
|
||||||
|
url: '/stuwork/watermonthreport/detail',
|
||||||
|
method: 'get',
|
||||||
|
params: { id },
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 编辑宿舍水电月明细
|
||||||
|
* @param data
|
||||||
|
*/
|
||||||
|
export const editObj = (data: any) => {
|
||||||
|
return request({
|
||||||
|
url: '/stuwork/watermonthreport/edit',
|
||||||
|
method: 'post',
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除宿舍水电月明细
|
||||||
|
* @param ids
|
||||||
|
*/
|
||||||
|
export const delObj = (ids: string[]) => {
|
||||||
|
return request({
|
||||||
|
url: '/stuwork/watermonthreport/delete',
|
||||||
|
method: 'post',
|
||||||
|
data: ids,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查看水电明细
|
||||||
|
* @param params 查询参数
|
||||||
|
*/
|
||||||
|
export const lookDetails = (params: any) => {
|
||||||
return request({
|
return request({
|
||||||
url: '/stuwork/watermonthreport/lookDetails',
|
url: '/stuwork/watermonthreport/lookDetails',
|
||||||
method: 'get',
|
method: 'get',
|
||||||
params: { roomNo },
|
params,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据角色查看明细
|
||||||
|
* @param params 查询参数
|
||||||
|
*/
|
||||||
|
export const lookDetail = (params: any) => {
|
||||||
|
return request({
|
||||||
|
url: '/stuwork/watermonthreport/lookDetail',
|
||||||
|
method: 'get',
|
||||||
|
params,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -96,6 +96,29 @@
|
|||||||
</el-table>
|
</el-table>
|
||||||
<div v-if="punishList.length === 0 && !punishLoading" style="text-align: center; padding: 20px; color: #909399">暂无数据</div>
|
<div v-if="punishList.length === 0 && !punishLoading" style="text-align: center; padding: 20px; color: #909399">暂无数据</div>
|
||||||
</el-tab-pane>
|
</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>
|
</el-tabs>
|
||||||
</div>
|
</div>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
@@ -108,13 +131,13 @@
|
|||||||
|
|
||||||
<script setup lang="ts" name="BasicClassDetail">
|
<script setup lang="ts" name="BasicClassDetail">
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
import { useMessage } from '/@/hooks/message';
|
|
||||||
import { queryClassHonorByClassCode } from '/@/api/stuwork/classhonor';
|
import { queryClassHonorByClassCode } from '/@/api/stuwork/classhonor';
|
||||||
import { queryDataByClassCode } from '/@/api/stuwork/classpublicity';
|
import { queryDataByClassCode } from '/@/api/stuwork/classpublicity';
|
||||||
import { getClassRoomByClassCode } from '/@/api/stuwork/teachclassroomassign';
|
import { getClassRoomByClassCode } from '/@/api/stuwork/teachclassroomassign';
|
||||||
import { queryStuNumByClassCode } from '/@/api/basic/basicstudent';
|
import { queryStuNumByClassCode } from '/@/api/basic/basicstudent';
|
||||||
import { fearchStuNumByClassCode } from '/@/api/stuwork/dormroomstudent';
|
import { fearchStuNumByClassCode } from '/@/api/stuwork/dormroomstudent';
|
||||||
import { queryPunlishNumByClass } from '/@/api/stuwork/stupunlish';
|
import { queryPunlishNumByClass } from '/@/api/stuwork/stupunlish';
|
||||||
|
import { fetchList as fetchPracticePlanList } from '/@/api/basic/basicpracticeclassplan';
|
||||||
|
|
||||||
// 定义变量内容
|
// 定义变量内容
|
||||||
const visible = ref(false);
|
const visible = ref(false);
|
||||||
@@ -136,6 +159,10 @@ const dormStudentLoading = ref(false);
|
|||||||
const punishList = ref<any[]>([]);
|
const punishList = ref<any[]>([]);
|
||||||
const punishLoading = ref(false);
|
const punishLoading = ref(false);
|
||||||
|
|
||||||
|
// 顶岗计划数据
|
||||||
|
const practicePlanList = ref<any[]>([]);
|
||||||
|
const practicePlanLoading = ref(false);
|
||||||
|
|
||||||
// 打开弹窗
|
// 打开弹窗
|
||||||
const openDialog = async (rowData: any) => {
|
const openDialog = async (rowData: any) => {
|
||||||
visible.value = true;
|
visible.value = true;
|
||||||
@@ -159,6 +186,7 @@ const loadAllData = async (classCode: string) => {
|
|||||||
loadStudentNumData(classCode),
|
loadStudentNumData(classCode),
|
||||||
loadDormStudentData(classCode),
|
loadDormStudentData(classCode),
|
||||||
loadPunishData(classCode),
|
loadPunishData(classCode),
|
||||||
|
loadPracticePlanData(classCode),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
loading.value = false;
|
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({
|
defineExpose({
|
||||||
openDialog,
|
openDialog,
|
||||||
|
|||||||
@@ -61,6 +61,7 @@
|
|||||||
<el-button icon="Link" type="success" class="ml10" @click="handleLinkRule"> 关联门禁规则 </el-button>
|
<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="Download" type="warning" class="ml10" @click="handleExport"> 导出 </el-button>
|
||||||
<el-button icon="DocumentAdd" type="info" class="ml10" @click="handleGenerateAssessment"> 生成考核班级 </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">
|
<right-toolbar v-model:showSearch="showSearch" class="ml10" @queryTable="getDataList">
|
||||||
<TableColumnControl
|
<TableColumnControl
|
||||||
ref="columnControlRef"
|
ref="columnControlRef"
|
||||||
@@ -176,6 +177,15 @@
|
|||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
|
||||||
|
<!-- 顶岗计划导入对话框 -->
|
||||||
|
<upload-excel
|
||||||
|
ref="practicePlanUploadRef"
|
||||||
|
:title="'导入顶岗计划'"
|
||||||
|
:url="'/basic/basicpracticeclassplan/import'"
|
||||||
|
:temp-url="'/basic/basicpracticeclassplan/importTemplate'"
|
||||||
|
@refreshDataList="getDataList"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -210,6 +220,7 @@ const StatusTag = defineStatusTag(() => import('/@/components/StatusTag/index.vu
|
|||||||
// 引入组件
|
// 引入组件
|
||||||
const FormDialog = defineAsyncComponent(() => import('./form.vue'));
|
const FormDialog = defineAsyncComponent(() => import('./form.vue'));
|
||||||
const DetailDialog = defineAsyncComponent(() => import('./detail.vue'));
|
const DetailDialog = defineAsyncComponent(() => import('./detail.vue'));
|
||||||
|
const UploadExcel = defineAsyncComponent(() => import('/@/components/Upload/Excel.vue'));
|
||||||
|
|
||||||
// 定义变量内容
|
// 定义变量内容
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
@@ -217,6 +228,7 @@ const formDialogRef = ref();
|
|||||||
const detailDialogRef = ref();
|
const detailDialogRef = ref();
|
||||||
const searchFormRef = ref();
|
const searchFormRef = ref();
|
||||||
const columnControlRef = ref();
|
const columnControlRef = ref();
|
||||||
|
const practicePlanUploadRef = ref();
|
||||||
// 搜索变量
|
// 搜索变量
|
||||||
const showSearch = ref(true);
|
const showSearch = ref(true);
|
||||||
const deptList = ref<any[]>([]);
|
const deptList = ref<any[]>([]);
|
||||||
@@ -428,6 +440,11 @@ const getRuleListData = async () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 顶岗计划导入
|
||||||
|
const handlePracticePlanImport = () => {
|
||||||
|
practicePlanUploadRef.value.show();
|
||||||
|
};
|
||||||
|
|
||||||
// 初始化
|
// 初始化
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getDeptListData();
|
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>
|
||||||
@@ -168,7 +168,10 @@
|
|||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
|
|
||||||
<pagination v-show="state.total > 0" :total="state.total" v-model:page="state.page" v-model:limit="state.limit" @pagination="getDataList" />
|
<!-- 分页 -->
|
||||||
|
<div class="pagination-wrapper">
|
||||||
|
<pagination @size-change="sizeChangeHandle" @current-change="currentChangeHandle" v-bind="state.pagination" />
|
||||||
|
</div>
|
||||||
</el-card>
|
</el-card>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -231,16 +234,14 @@ const state: BasicTableProps = reactive<BasicTableProps>({
|
|||||||
createdIsNeed: true,
|
createdIsNeed: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
const { getDataList, sortChangeHandle, tableStyle } = useTable(state);
|
const { getDataList, sortChangeHandle, sizeChangeHandle, currentChangeHandle, tableStyle } = useTable(state);
|
||||||
|
|
||||||
const handleSearch = () => {
|
const handleSearch = () => {
|
||||||
state.page = 1;
|
|
||||||
getDataList();
|
getDataList();
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleReset = () => {
|
const handleReset = () => {
|
||||||
searchFormRef.value?.resetFields();
|
searchFormRef.value?.resetFields();
|
||||||
state.page = 1;
|
|
||||||
getDataList();
|
getDataList();
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
@@ -252,4 +253,9 @@ const handleReset = () => {
|
|||||||
.mb12 {
|
.mb12 {
|
||||||
margin-bottom: 12px;
|
margin-bottom: 12px;
|
||||||
}
|
}
|
||||||
|
.pagination-wrapper {
|
||||||
|
margin-top: 20px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -150,44 +150,10 @@ const searchForm = reactive({
|
|||||||
classCode: '',
|
classCode: '',
|
||||||
});
|
});
|
||||||
|
|
||||||
// 配置 useTable - 接口返回的数据结构是 { classes: [], students: [] }
|
// 配置 useTable - 标准分页查询
|
||||||
const state: BasicTableProps = reactive<BasicTableProps>({
|
const state: BasicTableProps = reactive<BasicTableProps>({
|
||||||
queryForm: searchForm,
|
queryForm: searchForm,
|
||||||
pageList: async (queryParams: any) => {
|
pageList: fetchList,
|
||||||
const res = await fetchList(queryParams);
|
|
||||||
// 接口返回的数据结构是 { classes: [], students: [] }
|
|
||||||
// 需要将 students 数组转换为表格数据,并关联班级信息
|
|
||||||
if (res.data && res.data.students) {
|
|
||||||
const students = res.data.students || [];
|
|
||||||
const classes = res.data.classes || [];
|
|
||||||
const classMap = new Map();
|
|
||||||
classes.forEach((cls: any) => {
|
|
||||||
classMap.set(cls.classCode, cls);
|
|
||||||
});
|
|
||||||
|
|
||||||
// 将学生数据与班级信息合并
|
|
||||||
const dataList = students.map((stu: any) => {
|
|
||||||
const classInfo = classMap.get(stu.classCode);
|
|
||||||
return {
|
|
||||||
...stu,
|
|
||||||
className: classInfo ? classInfo.classNo : stu.className || '',
|
|
||||||
classNo: classInfo ? classInfo.classNo : '',
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
...res,
|
|
||||||
data: {
|
|
||||||
records: dataList,
|
|
||||||
total: dataList.length,
|
|
||||||
current: 1,
|
|
||||||
size: dataList.length,
|
|
||||||
pages: 1,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
},
|
|
||||||
props: {
|
props: {
|
||||||
item: 'records',
|
item: 'records',
|
||||||
totalCount: 'total',
|
totalCount: 'total',
|
||||||
|
|||||||
@@ -75,7 +75,7 @@
|
|||||||
|
|
||||||
<el-divider content-position="left">审核流程</el-divider>
|
<el-divider content-position="left">审核流程</el-divider>
|
||||||
|
|
||||||
<flow-comment-timeline v-if="flowInstId" :flow-inst-id="flowInstId" />
|
<FlowCommentTimeline v-if="flowInstId" :curr-job="{ flowInstId }" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #footer>
|
<template #footer>
|
||||||
@@ -91,11 +91,13 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from 'vue';
|
import { ref, defineAsyncComponent } from 'vue';
|
||||||
import { useMessage } from '/@/hooks/message';
|
import { useMessage } from '/@/hooks/message';
|
||||||
import { getDetail } from '/@/api/purchase/purchasingcontract';
|
import { getDetail } from '/@/api/purchase/purchasingcontract';
|
||||||
import { previewFileById, downloadFileById } from '/@/api/purchase/purchasingrequisition';
|
import { previewFileById, downloadFileById } from '/@/api/purchase/purchasingrequisition';
|
||||||
|
|
||||||
|
const FlowCommentTimeline = defineAsyncComponent(() => import('/@/views/jsonflow/comment/timeline.vue'));
|
||||||
|
|
||||||
interface FileItem {
|
interface FileItem {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
<el-row :gutter="20">
|
<el-row :gutter="20">
|
||||||
<el-col :span="12" class="mb12">
|
<el-col :span="12" class="mb12">
|
||||||
<el-form-item label="合同编号" prop="contractNo">
|
<el-form-item label="合同编号" prop="contractNo">
|
||||||
<el-input v-model="formData.contractNo" placeholder="请输入合同编号" />
|
<el-input v-model="formData.contractNo" placeholder="请输入合同编号" disabled />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="12" class="mb12">
|
<el-col :span="12" class="mb12">
|
||||||
@@ -178,12 +178,17 @@ const open = async (id: string, openMode: 'add' | 'edit' | 'view' = 'add') => {
|
|||||||
const res = await getObj(id);
|
const res = await getObj(id);
|
||||||
applyData.value = res?.data || {};
|
applyData.value = res?.data || {};
|
||||||
|
|
||||||
|
// 新增时,合同编号默认为采购编号
|
||||||
|
if (mode.value === 'add') {
|
||||||
|
formData.value.contractNo = applyData.value.purchaseNo || '';
|
||||||
|
}
|
||||||
|
|
||||||
const contractRes = await getByPurchaseId(id);
|
const contractRes = await getByPurchaseId(id);
|
||||||
const contractData = contractRes?.data;
|
const contractData = contractRes?.data;
|
||||||
if (contractData) {
|
if (contractData) {
|
||||||
formData.value = {
|
formData.value = {
|
||||||
purchaseId: id,
|
purchaseId: id,
|
||||||
contractNo: contractData.contractNo || '',
|
contractNo: contractData.contractNo || applyData.value.purchaseNo || '',
|
||||||
contractName: contractData.contractName || '',
|
contractName: contractData.contractName || '',
|
||||||
money: contractData.money,
|
money: contractData.money,
|
||||||
isBidding: contractData.isBidding || '0',
|
isBidding: contractData.isBidding || '0',
|
||||||
|
|||||||
@@ -33,24 +33,24 @@
|
|||||||
|
|
||||||
<el-form ref="formRef" :model="formData" :rules="formRules" label-width="160px" :disabled="isViewMode">
|
<el-form ref="formRef" :model="formData" :rules="formRules" label-width="160px" :disabled="isViewMode">
|
||||||
<el-row :gutter="20">
|
<el-row :gutter="20">
|
||||||
<el-col :span="12">
|
<el-col :span="12" class="mb12">
|
||||||
<el-form-item label="合同编号" prop="contractNo">
|
<el-form-item label="合同编号" prop="contractNo">
|
||||||
<el-input v-model="formData.contractNo" placeholder="请输入合同编号" />
|
<el-input v-model="formData.contractNo" placeholder="请输入合同编号" disabled />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="12">
|
<el-col :span="12" class="mb12">
|
||||||
<el-form-item label="合同名称" prop="contractName">
|
<el-form-item label="合同名称" prop="contractName">
|
||||||
<el-input v-model="formData.contractName" placeholder="请输入合同名称" />
|
<el-input v-model="formData.contractName" placeholder="请输入合同名称" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
<el-row :gutter="20">
|
<el-row :gutter="20">
|
||||||
<el-col :span="12">
|
<el-col :span="12" class="mb12">
|
||||||
<el-form-item label="合同金额(元)" prop="money">
|
<el-form-item label="合同金额(元)" prop="money">
|
||||||
<el-input-number v-model="formData.money" :precision="2" :min="0" :controls="false" style="width: 100%" />
|
<el-input-number v-model="formData.money" :precision="2" :min="0" :controls="false" style="width: 100%" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="12">
|
<el-col :span="12" class="mb12">
|
||||||
<el-form-item label="是否需要招标" prop="isBidding">
|
<el-form-item label="是否需要招标" prop="isBidding">
|
||||||
<el-radio-group v-model="formData.isBidding">
|
<el-radio-group v-model="formData.isBidding">
|
||||||
<el-radio label="0">否</el-radio>
|
<el-radio label="0">否</el-radio>
|
||||||
@@ -60,7 +60,7 @@
|
|||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
<el-row :gutter="20">
|
<el-row :gutter="20">
|
||||||
<el-col :span="12">
|
<el-col :span="12" class="mb12">
|
||||||
<el-form-item label="是否需要法律顾问" prop="isLegalAdviser">
|
<el-form-item label="是否需要法律顾问" prop="isLegalAdviser">
|
||||||
<el-radio-group v-model="formData.isLegalAdviser">
|
<el-radio-group v-model="formData.isLegalAdviser">
|
||||||
<el-radio label="0">否</el-radio>
|
<el-radio label="0">否</el-radio>
|
||||||
@@ -68,7 +68,7 @@
|
|||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="12">
|
<el-col :span="12" class="mb12">
|
||||||
<el-form-item label="是否涉及多个部门" prop="isDepts">
|
<el-form-item label="是否涉及多个部门" prop="isDepts">
|
||||||
<el-radio-group v-model="formData.isDepts">
|
<el-radio-group v-model="formData.isDepts">
|
||||||
<el-radio label="0">否</el-radio>
|
<el-radio label="0">否</el-radio>
|
||||||
@@ -78,7 +78,7 @@
|
|||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
<el-row :gutter="20">
|
<el-row :gutter="20">
|
||||||
<el-col :span="12">
|
<el-col :span="12" class="mb12">
|
||||||
<el-form-item label="是否全校合同" prop="isSchool">
|
<el-form-item label="是否全校合同" prop="isSchool">
|
||||||
<el-radio-group v-model="formData.isSchool">
|
<el-radio-group v-model="formData.isSchool">
|
||||||
<el-radio label="0">否</el-radio>
|
<el-radio label="0">否</el-radio>
|
||||||
@@ -150,7 +150,7 @@ import { useRoute } from 'vue-router';
|
|||||||
import { Document, Tickets, FolderOpened } from '@element-plus/icons-vue';
|
import { Document, Tickets, FolderOpened } from '@element-plus/icons-vue';
|
||||||
import { ElMessage } from 'element-plus';
|
import { ElMessage } from 'element-plus';
|
||||||
import { getObj } from '/@/api/purchase/purchasingrequisition';
|
import { getObj } from '/@/api/purchase/purchasingrequisition';
|
||||||
import { getByPurchaseId, updateContract } from '/@/api/purchase/purchasingcontract';
|
import { getByPurchaseId, getByFlowInstId, updateContract } from '/@/api/purchase/purchasingcontract';
|
||||||
import { previewFileById, downloadFileById } from '/@/api/purchase/purchasingrequisition';
|
import { previewFileById, downloadFileById } from '/@/api/purchase/purchasingrequisition';
|
||||||
import { currElTabIsSave } from '/@/api/order/order-key-vue';
|
import { currElTabIsSave } from '/@/api/order/order-key-vue';
|
||||||
|
|
||||||
@@ -208,6 +208,13 @@ const previewVisible = ref(false);
|
|||||||
const previewTitle = ref('');
|
const previewTitle = ref('');
|
||||||
const previewUrl = ref('');
|
const previewUrl = ref('');
|
||||||
|
|
||||||
|
const effectiveFlowInstId = computed(() => {
|
||||||
|
if (props.currJob?.flowInstId) {
|
||||||
|
return props.currJob.flowInstId;
|
||||||
|
}
|
||||||
|
return route.query.flowInstId ? Number(route.query.flowInstId) : null;
|
||||||
|
});
|
||||||
|
|
||||||
const effectivePurchaseId = computed(() => {
|
const effectivePurchaseId = computed(() => {
|
||||||
if (props.currJob?.orderId) {
|
if (props.currJob?.orderId) {
|
||||||
return String(props.currJob.orderId);
|
return String(props.currJob.orderId);
|
||||||
@@ -232,41 +239,76 @@ const loadApplyData = async () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const loadContractData = async () => {
|
const loadContractData = async () => {
|
||||||
if (!effectivePurchaseId.value) return;
|
let contractData: any = null;
|
||||||
|
let purchaseId = effectivePurchaseId.value;
|
||||||
|
|
||||||
|
// 优先使用 flowInstId 获取合同数据(审核流程嵌入时)
|
||||||
|
if (effectiveFlowInstId.value) {
|
||||||
try {
|
try {
|
||||||
fileLoading.value = true;
|
fileLoading.value = true;
|
||||||
const res = await getByPurchaseId(effectivePurchaseId.value);
|
const res = await getByFlowInstId(effectiveFlowInstId.value);
|
||||||
if (res.code === 0 && res.data) {
|
if (res.code === 0 && res.data) {
|
||||||
const data = res.data;
|
contractData = res.data;
|
||||||
formData.value = {
|
purchaseId = res.data.purchaseId || purchaseId;
|
||||||
purchaseId: effectivePurchaseId.value,
|
|
||||||
contractNo: data.contractNo || '',
|
|
||||||
contractName: data.contractName || '',
|
|
||||||
money: data.money,
|
|
||||||
isBidding: data.isBidding || '0',
|
|
||||||
isLegalAdviser: data.isLegalAdviser || '0',
|
|
||||||
legalAdviserOpinion: data.legalAdviserOpinion || '',
|
|
||||||
isDepts: data.isDepts || '0',
|
|
||||||
isSchool: data.isSchool || '0',
|
|
||||||
remarks: data.remarks || '',
|
|
||||||
contractFileIds: [],
|
|
||||||
supplementFileIds: [],
|
|
||||||
};
|
|
||||||
(formData.value as any).flowStatus = data.flowStatus;
|
|
||||||
|
|
||||||
if (data.contractFiles && data.contractFiles.length > 0) {
|
|
||||||
contractFiles.value = data.contractFiles;
|
|
||||||
}
|
|
||||||
if (data.supplementFiles && data.supplementFiles.length > 0) {
|
|
||||||
supplementFiles.value = data.supplementFiles;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
ElMessage.error(e?.msg || '加载合同信息失败');
|
ElMessage.error(e?.msg || '加载合同信息失败');
|
||||||
} finally {
|
} finally {
|
||||||
fileLoading.value = false;
|
fileLoading.value = false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果通过 flowInstId 没有获取到数据,则使用 purchaseId 获取
|
||||||
|
if (!contractData && purchaseId) {
|
||||||
|
try {
|
||||||
|
fileLoading.value = true;
|
||||||
|
const res = await getByPurchaseId(purchaseId);
|
||||||
|
if (res.code === 0 && res.data) {
|
||||||
|
contractData = res.data;
|
||||||
|
}
|
||||||
|
} catch (e: any) {
|
||||||
|
ElMessage.error(e?.msg || '加载合同信息失败');
|
||||||
|
} finally {
|
||||||
|
fileLoading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (contractData) {
|
||||||
|
formData.value = {
|
||||||
|
purchaseId: purchaseId,
|
||||||
|
contractNo: contractData.contractNo || '',
|
||||||
|
contractName: contractData.contractName || '',
|
||||||
|
money: contractData.money,
|
||||||
|
isBidding: contractData.isBidding || '0',
|
||||||
|
isLegalAdviser: contractData.isLegalAdviser || '0',
|
||||||
|
legalAdviserOpinion: contractData.legalAdviserOpinion || '',
|
||||||
|
isDepts: contractData.isDepts || '0',
|
||||||
|
isSchool: contractData.isSchool || '0',
|
||||||
|
remarks: contractData.remarks || '',
|
||||||
|
contractFileIds: [],
|
||||||
|
supplementFileIds: [],
|
||||||
|
};
|
||||||
|
(formData.value as any).flowStatus = contractData.flowStatus;
|
||||||
|
|
||||||
|
if (contractData.contractFiles && contractData.contractFiles.length > 0) {
|
||||||
|
contractFiles.value = contractData.contractFiles;
|
||||||
|
}
|
||||||
|
if (contractData.supplementFiles && contractData.supplementFiles.length > 0) {
|
||||||
|
supplementFiles.value = contractData.supplementFiles;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果有 purchaseId,加载采购申请信息
|
||||||
|
if (purchaseId && !applyData.value.id) {
|
||||||
|
try {
|
||||||
|
const applyRes = await getObj(purchaseId);
|
||||||
|
if (applyRes.code === 0 && applyRes.data) {
|
||||||
|
applyData.value = applyRes.data;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('加载采购申请信息失败', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handlePreview = async (row: any) => {
|
const handlePreview = async (row: any) => {
|
||||||
@@ -313,7 +355,7 @@ const handleFlowSave = async () => {
|
|||||||
|
|
||||||
await updateContract({
|
await updateContract({
|
||||||
...formData.value,
|
...formData.value,
|
||||||
purchaseId: effectivePurchaseId.value,
|
purchaseId: formData.value.purchaseId || effectivePurchaseId.value,
|
||||||
contractFileIds,
|
contractFileIds,
|
||||||
supplementFileIds,
|
supplementFileIds,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -39,9 +39,52 @@
|
|||||||
<el-option label="是" value="1" />
|
<el-option label="是" value="1" />
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item label="是否特殊" prop="isSpecial">
|
||||||
|
<el-select v-model="state.queryForm.isSpecial" 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="purchaseMode">
|
||||||
|
<el-select
|
||||||
|
v-model="state.queryForm.purchaseMode"
|
||||||
|
placeholder="请选择采购形式"
|
||||||
|
clearable
|
||||||
|
style="width: 200px"
|
||||||
|
@change="handlePurchaseModeChange"
|
||||||
|
>
|
||||||
|
<el-option label="部门自行采购" value="1" />
|
||||||
|
<el-option label="学校统一采购" value="2" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item v-if="state.queryForm.purchaseMode === '1'" label="采购途径" prop="purchaseChannel">
|
||||||
|
<el-select
|
||||||
|
v-model="state.queryForm.purchaseChannel"
|
||||||
|
placeholder="请选择采购途径"
|
||||||
|
clearable
|
||||||
|
style="width: 200px"
|
||||||
|
@change="handlePurchaseChannelChange"
|
||||||
|
>
|
||||||
|
<el-option label="自行采购" value="1" />
|
||||||
|
<el-option label="委托采购中心采购" value="2" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item v-if="showPurchaseTypeSelect" label="采购方式" prop="purchaseType">
|
||||||
|
<el-select v-model="state.queryForm.purchaseType" placeholder="请选择采购方式" clearable style="width: 200px">
|
||||||
|
<el-option v-for="item in purchaseTypeOptions" :key="item.value" :label="item.label" :value="item.value" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="需求部门" prop="deptId">
|
||||||
|
<el-select v-model="state.queryForm.deptCode" placeholder="请选择需求部门" clearable filterable style="width: 200px">
|
||||||
|
<el-option v-for="item in secondDeptList" :key="item.id" :label="item.name" :value="item.id" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-button type="primary" icon="Search" @click="getDataList">查询</el-button>
|
<el-button type="primary" icon="Search" @click="getDataList">查询</el-button>
|
||||||
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
||||||
|
<el-button type="success" icon="Download" @click="handleExport">导出</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</el-card>
|
</el-card>
|
||||||
@@ -206,7 +249,9 @@
|
|||||||
>委
|
>委
|
||||||
</el-tag>
|
</el-tag>
|
||||||
<el-tooltip
|
<el-tooltip
|
||||||
v-if="scope.row.status === '1' && scope.row.purchaseMode === '1' && scope.row.purchaseChannel === '1' && scope.row.purchaseType !=='6'"
|
v-if="
|
||||||
|
scope.row.status === '1' && scope.row.purchaseMode === '1' && scope.row.purchaseChannel === '1' && scope.row.purchaseType !== '6'
|
||||||
|
"
|
||||||
:content="getSupplementTooltip(scope.row)"
|
:content="getSupplementTooltip(scope.row)"
|
||||||
placement="top"
|
placement="top"
|
||||||
>
|
>
|
||||||
@@ -319,16 +364,34 @@
|
|||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<template v-if="scope.row.status === '1'">
|
<template v-if="scope.row.status === '1'">
|
||||||
<el-tooltip v-if="scope.row.contractFlowStatus" content="点击查看合同详情" placement="top">
|
<el-tooltip v-if="scope.row.contractFlowStatus" content="点击查看合同详情" placement="top">
|
||||||
<el-tag v-if="scope.row.contractFlowStatus === '0'" type="warning" class="status-tag-clickable" @click="handleShowContractDetail(scope.row)"
|
<el-tag
|
||||||
|
v-if="scope.row.contractFlowStatus === '-1'"
|
||||||
|
type="info"
|
||||||
|
class="status-tag-clickable"
|
||||||
|
@click="handleEditContract(scope.row)"
|
||||||
|
>已上传
|
||||||
|
</el-tag>
|
||||||
|
<el-tag
|
||||||
|
v-else-if="scope.row.contractFlowStatus === '0'"
|
||||||
|
type="warning"
|
||||||
|
class="status-tag-clickable"
|
||||||
|
@click="handleShowContractDetail(scope.row)"
|
||||||
>运行中
|
>运行中
|
||||||
</el-tag>
|
</el-tag>
|
||||||
<el-tag v-else-if="scope.row.contractFlowStatus === '1'" type="success" class="status-tag-clickable" @click="handleShowContractDetail(scope.row)"
|
<el-tag
|
||||||
|
v-else-if="scope.row.contractFlowStatus === '1'"
|
||||||
|
type="success"
|
||||||
|
class="status-tag-clickable"
|
||||||
|
@click="handleShowContractDetail(scope.row)"
|
||||||
>已完成
|
>已完成
|
||||||
</el-tag>
|
</el-tag>
|
||||||
<el-tag v-else-if="scope.row.contractFlowStatus === '2'" type="info" class="status-tag-clickable" @click="handleShowContractDetail(scope.row)"
|
<el-tag
|
||||||
|
v-else-if="scope.row.contractFlowStatus === '2'"
|
||||||
|
type="info"
|
||||||
|
class="status-tag-clickable"
|
||||||
|
@click="handleShowContractDetail(scope.row)"
|
||||||
>已作废
|
>已作废
|
||||||
</el-tag>
|
</el-tag>
|
||||||
<el-tag v-else type="info" class="status-tag-clickable" @click="handleShowContractDetail(scope.row)">{{ scope.row.contractFlowStatus }}</el-tag>
|
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
<el-button v-else type="primary" link size="small" @click="handleAddContract(scope.row)">添加合同</el-button>
|
<el-button v-else type="primary" link size="small" @click="handleAddContract(scope.row)">添加合同</el-button>
|
||||||
</template>
|
</template>
|
||||||
@@ -482,6 +545,7 @@ import {
|
|||||||
saveRepresentor,
|
saveRepresentor,
|
||||||
listDownloadUrls,
|
listDownloadUrls,
|
||||||
updateFiles,
|
updateFiles,
|
||||||
|
exportPurchaseApply,
|
||||||
} from '/@/api/purchase/purchasingrequisition';
|
} from '/@/api/purchase/purchasingrequisition';
|
||||||
import { useMessage, useMessageBox } from '/@/hooks/message';
|
import { useMessage, useMessageBox } from '/@/hooks/message';
|
||||||
import { useAuth } from '/@/hooks/auth';
|
import { useAuth } from '/@/hooks/auth';
|
||||||
@@ -515,6 +579,7 @@ import {
|
|||||||
import other from '/@/utils/other';
|
import other from '/@/utils/other';
|
||||||
import { Session } from '/@/utils/storage';
|
import { Session } from '/@/utils/storage';
|
||||||
import { getByPurchaseId } from '/@/api/purchase/purchasingcontract';
|
import { getByPurchaseId } from '/@/api/purchase/purchasingcontract';
|
||||||
|
import { getDeptListByLevelTwo } from '/@/api/basic/basicdept';
|
||||||
|
|
||||||
// 角色常量
|
// 角色常量
|
||||||
const PURCHASE_DEPT_AUDIT_ROLE_CODE = 'PURCHASE_DEPT_AUDIT';
|
const PURCHASE_DEPT_AUDIT_ROLE_CODE = 'PURCHASE_DEPT_AUDIT';
|
||||||
@@ -548,6 +613,53 @@ const dictData = ref({
|
|||||||
categoryTreeData: [] as any[],
|
categoryTreeData: [] as any[],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 搜索条件相关数据
|
||||||
|
const purchaseTypeDeptDelegationList = ref<any[]>([]); // 委托采购中心采购方式字典
|
||||||
|
const secondDeptList = ref<any[]>([]); // 二级部门列表
|
||||||
|
|
||||||
|
// 是否显示采购方式选择框
|
||||||
|
const showPurchaseTypeSelect = computed(() => {
|
||||||
|
const mode = state.queryForm.purchaseMode;
|
||||||
|
if (mode === '2') {
|
||||||
|
// 学校统一采购:显示采购方式
|
||||||
|
return true;
|
||||||
|
} else if (mode === '1') {
|
||||||
|
// 部门自行采购:需要先选择采购途径
|
||||||
|
return !!state.queryForm.purchaseChannel;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 采购方式选项(根据采购形式和采购途径动态变化)
|
||||||
|
const purchaseTypeOptions = computed(() => {
|
||||||
|
const mode = state.queryForm.purchaseMode;
|
||||||
|
if (mode === '2') {
|
||||||
|
// 学校统一采购:使用 UNION_PURCHASE_TYPE 字典
|
||||||
|
return dictData.value.purchaseTypeUnionList;
|
||||||
|
} else if (mode === '1') {
|
||||||
|
const channel = state.queryForm.purchaseChannel;
|
||||||
|
if (channel === '1') {
|
||||||
|
// 自行采购:使用 DEPT_PURCHASE_TYPE 字典
|
||||||
|
return dictData.value.purchaseTypeDeptList;
|
||||||
|
} else if (channel === '2') {
|
||||||
|
// 委托采购中心采购:使用 PURCHASE_TYPE_DEPT_DELEGATION 字典
|
||||||
|
return purchaseTypeDeptDelegationList.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
});
|
||||||
|
|
||||||
|
// 采购形式变化时清空采购途径和采购方式
|
||||||
|
const handlePurchaseModeChange = () => {
|
||||||
|
state.queryForm.purchaseChannel = '';
|
||||||
|
state.queryForm.purchaseType = '';
|
||||||
|
};
|
||||||
|
|
||||||
|
// 采购途径变化时清空采购方式
|
||||||
|
const handlePurchaseChannelChange = () => {
|
||||||
|
state.queryForm.purchaseType = '';
|
||||||
|
};
|
||||||
|
|
||||||
// 定义变量内容
|
// 定义变量内容
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const tableRef = ref();
|
const tableRef = ref();
|
||||||
@@ -639,6 +751,11 @@ const state: BasicTableProps = reactive<BasicTableProps>({
|
|||||||
projectType: '',
|
projectType: '',
|
||||||
status: '',
|
status: '',
|
||||||
isCentralized: '',
|
isCentralized: '',
|
||||||
|
isSpecial: '',
|
||||||
|
purchaseMode: '',
|
||||||
|
purchaseChannel: '',
|
||||||
|
purchaseType: '',
|
||||||
|
deptCode: '',
|
||||||
},
|
},
|
||||||
createdIsNeed: true,
|
createdIsNeed: true,
|
||||||
});
|
});
|
||||||
@@ -747,6 +864,16 @@ const handleAddContract = (row: any) => {
|
|||||||
purchaseContractDialogRef.value?.open(String(id), 'add');
|
purchaseContractDialogRef.value?.open(String(id), 'add');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** 点击已上传合同:打开采购合同编辑弹窗 */
|
||||||
|
const handleEditContract = (row: any) => {
|
||||||
|
const id = row?.id ?? row?.purchaseId;
|
||||||
|
if (!id) {
|
||||||
|
useMessage().warning('无法获取采购申请ID');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
purchaseContractDialogRef.value?.open(String(id), 'edit');
|
||||||
|
};
|
||||||
|
|
||||||
/** 点击采购合同状态:打开采购合同详情弹窗 */
|
/** 点击采购合同状态:打开采购合同详情弹窗 */
|
||||||
const handleShowContractDetail = (row: any) => {
|
const handleShowContractDetail = (row: any) => {
|
||||||
const id = row?.id ?? row?.purchaseId;
|
const id = row?.id ?? row?.purchaseId;
|
||||||
@@ -1072,8 +1199,16 @@ const handleArchive = (row: any) => {
|
|||||||
// 获取字典数据和品目树数据
|
// 获取字典数据和品目树数据
|
||||||
const loadDictData = async () => {
|
const loadDictData = async () => {
|
||||||
try {
|
try {
|
||||||
const [fundSourceRes, isCentralizedRes, isSpecialRes, purchaseTypeDeptRes, purchaseModeSchoolRes, purchaseTypeUnionRes, categoryTreeRes] =
|
const [
|
||||||
await Promise.all([
|
fundSourceRes,
|
||||||
|
isCentralizedRes,
|
||||||
|
isSpecialRes,
|
||||||
|
purchaseTypeDeptRes,
|
||||||
|
purchaseModeSchoolRes,
|
||||||
|
purchaseTypeUnionRes,
|
||||||
|
categoryTreeRes,
|
||||||
|
purchaseTypeDeptDelegationRes,
|
||||||
|
] = await Promise.all([
|
||||||
getDicts('PURCHASE_FUND_SOURCE'),
|
getDicts('PURCHASE_FUND_SOURCE'),
|
||||||
getDicts('PURCHASE_IS_CEN'),
|
getDicts('PURCHASE_IS_CEN'),
|
||||||
getDicts('PURCHASE_IS_SPEC'),
|
getDicts('PURCHASE_IS_SPEC'),
|
||||||
@@ -1081,6 +1216,7 @@ const loadDictData = async () => {
|
|||||||
getDicts('PURCHASE_MODE_SCHOOL'),
|
getDicts('PURCHASE_MODE_SCHOOL'),
|
||||||
getDicts('PURCHASE_TYPE_UNION'),
|
getDicts('PURCHASE_TYPE_UNION'),
|
||||||
getTree(),
|
getTree(),
|
||||||
|
getDicts('PURCHASE_TYPE_DEPT_DELEGATION'),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// 处理资金来源字典
|
// 处理资金来源字典
|
||||||
@@ -1162,6 +1298,14 @@ const loadDictData = async () => {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 处理委托采购中心采购方式字典
|
||||||
|
if (purchaseTypeDeptDelegationRes.data && Array.isArray(purchaseTypeDeptDelegationRes.data)) {
|
||||||
|
purchaseTypeDeptDelegationList.value = purchaseTypeDeptDelegationRes.data.map((item: any) => ({
|
||||||
|
label: item.label || item.dictLabel || item.name,
|
||||||
|
value: item.value || item.dictValue || item.code,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
// 处理品目树数据
|
// 处理品目树数据
|
||||||
if (categoryTreeRes.data && Array.isArray(categoryTreeRes.data)) {
|
if (categoryTreeRes.data && Array.isArray(categoryTreeRes.data)) {
|
||||||
dictData.value.categoryTreeData = categoryTreeRes.data;
|
dictData.value.categoryTreeData = categoryTreeRes.data;
|
||||||
@@ -1202,9 +1346,47 @@ const loadDictData = async () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 获取二级部门列表
|
||||||
|
const loadSecondDeptList = async () => {
|
||||||
|
try {
|
||||||
|
const res = await getDeptListByLevelTwo();
|
||||||
|
if (res.data && Array.isArray(res.data)) {
|
||||||
|
secondDeptList.value = res.data.map((item: any) => ({
|
||||||
|
id: item.id || item.deptId,
|
||||||
|
name: item.name || item.deptName,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error('加载二级部门列表失败', err);
|
||||||
|
secondDeptList.value = [];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 导出功能
|
||||||
|
const handleExport = async () => {
|
||||||
|
try {
|
||||||
|
const res: any = await exportPurchaseApply(state.queryForm);
|
||||||
|
downloadFile(res, '采购申请列表.xlsx');
|
||||||
|
} catch (err: any) {
|
||||||
|
useMessage().error(err.msg || '导出失败');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const downloadFile = (blob: Blob, fileName: string) => {
|
||||||
|
const url = window.URL.createObjectURL(blob);
|
||||||
|
const link = document.createElement('a');
|
||||||
|
link.href = url;
|
||||||
|
link.download = fileName;
|
||||||
|
document.body.appendChild(link);
|
||||||
|
link.click();
|
||||||
|
document.body.removeChild(link);
|
||||||
|
window.URL.revokeObjectURL(url);
|
||||||
|
};
|
||||||
|
|
||||||
// 页面加载时获取字典数据和品目树数据
|
// 页面加载时获取字典数据和品目树数据
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
loadDictData();
|
loadDictData();
|
||||||
|
loadSecondDeptList();
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -140,6 +140,13 @@
|
|||||||
</el-tag>
|
</el-tag>
|
||||||
<span v-else>-</span>
|
<span v-else>-</span>
|
||||||
</template>
|
</template>
|
||||||
|
<!-- 学期班费结余列特殊模板 -->
|
||||||
|
<template v-else-if="col.prop === 'termBalance'" #default="scope">
|
||||||
|
<el-tag v-if="scope.row.termBalance !== null && scope.row.termBalance !== undefined" :type="scope.row.termBalance >= 0 ? 'success' : 'danger'" size="small" effect="plain" round>
|
||||||
|
¥{{ scope.row.termBalance.toFixed(2) }}
|
||||||
|
</el-tag>
|
||||||
|
<span v-else>-</span>
|
||||||
|
</template>
|
||||||
<!-- 附件列特殊模板 -->
|
<!-- 附件列特殊模板 -->
|
||||||
<template v-else-if="col.prop === 'attachment'" #default="scope">
|
<template v-else-if="col.prop === 'attachment'" #default="scope">
|
||||||
<el-button v-if="scope.row.attachment" icon="Document" link type="primary" size="small" @click="handleViewAttachment(scope.row)">
|
<el-button v-if="scope.row.attachment" icon="Document" link type="primary" size="small" @click="handleViewAttachment(scope.row)">
|
||||||
@@ -328,6 +335,7 @@ const tableColumns = [
|
|||||||
{ prop: 'operatTime', label: '发生时间', icon: Calendar, width: 180 },
|
{ prop: 'operatTime', label: '发生时间', icon: Calendar, width: 180 },
|
||||||
{ prop: 'type', label: '类型', icon: Collection },
|
{ prop: 'type', label: '类型', icon: Collection },
|
||||||
{ prop: 'money', label: '金额', icon: Money },
|
{ prop: 'money', label: '金额', icon: Money },
|
||||||
|
{ prop: 'termBalance', label: '学期班费结余', icon: Money, width: 120 },
|
||||||
{ prop: 'operator', label: '经办人', icon: User },
|
{ prop: 'operator', label: '经办人', icon: User },
|
||||||
{ prop: 'purpose', label: '用途', icon: Document, minWidth: 150 },
|
{ prop: 'purpose', label: '用途', icon: Document, minWidth: 150 },
|
||||||
{ prop: 'attachment', label: '附件', width: 100 },
|
{ prop: 'attachment', label: '附件', width: 100 },
|
||||||
@@ -345,7 +353,41 @@ const state: BasicTableProps = reactive<BasicTableProps>({
|
|||||||
classCode: '',
|
classCode: '',
|
||||||
type: '',
|
type: '',
|
||||||
},
|
},
|
||||||
pageList: fetchList,
|
pageList: async (params: any) => {
|
||||||
|
const res = await fetchList(params);
|
||||||
|
// 后端返回数据结构:IPage<ClassFeeLogRelationVO>
|
||||||
|
// ClassFeeLogRelationVO 包含 moneyTotal 和 classFeeLogVOList
|
||||||
|
if (res.data && res.data.records) {
|
||||||
|
// 展开所有班级的班费记录
|
||||||
|
const allRecords: any[] = [];
|
||||||
|
let totalMoney: number = 0;
|
||||||
|
|
||||||
|
res.data.records.forEach((item: any) => {
|
||||||
|
if (item.classFeeLogVOList && Array.isArray(item.classFeeLogVOList)) {
|
||||||
|
item.classFeeLogVOList.forEach((log: any) => {
|
||||||
|
allRecords.push({
|
||||||
|
...log,
|
||||||
|
moneyTotal: item.moneyTotal // 添加班费结余到每条记录
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// 记录总班费结余
|
||||||
|
if (item.moneyTotal !== undefined) {
|
||||||
|
totalMoney = item.moneyTotal;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
...res,
|
||||||
|
data: {
|
||||||
|
records: allRecords,
|
||||||
|
total: allRecords.length,
|
||||||
|
moneyTotal: totalMoney // 保留总结余
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
},
|
||||||
props: {
|
props: {
|
||||||
item: 'records',
|
item: 'records',
|
||||||
totalCount: 'total',
|
totalCount: 'total',
|
||||||
|
|||||||
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>
|
<template>
|
||||||
<div class="modern-page-container">
|
<div class="modern-page-container">
|
||||||
<div class="page-wrapper">
|
<div class="page-wrapper">
|
||||||
<!-- 筛选 -->
|
<!-- 筛选条件 -->
|
||||||
<el-card class="search-card" shadow="never">
|
<el-card v-show="showSearch" class="search-card" shadow="never">
|
||||||
<template #header>
|
<template #header>
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<span class="card-title">
|
<span class="card-title">
|
||||||
<el-icon class="title-icon"><Search /></el-icon>
|
<el-icon class="title-icon"><Search /></el-icon>
|
||||||
统计条件
|
筛选条件
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</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-form-item label="毕业年份" prop="graduYear">
|
||||||
<el-select v-model="queryForm.graduYear" placeholder="请选择毕业年份" clearable style="width: 160px">
|
<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="y" />
|
<el-option v-for="y in graduYearOptions" :key="y" :label="y + '年'" :value="String(y)" />
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<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-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</el-card>
|
</el-card>
|
||||||
|
|
||||||
<!-- 汇总卡片 -->
|
<!-- 统计卡片 -->
|
||||||
<el-row :gutter="16" class="summary-row">
|
<el-row :gutter="20" class="stat-cards">
|
||||||
<el-col :span="6">
|
<el-col :xs="24" :sm="12" :md="6">
|
||||||
<el-card shadow="hover" class="stat-card">
|
<el-card shadow="hover" class="stat-card">
|
||||||
|
<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 class="stat-label">毕业生总数</div>
|
||||||
<div class="stat-value">{{ summary.total }}</div>
|
</div>
|
||||||
|
</div>
|
||||||
</el-card>
|
</el-card>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="6">
|
<el-col :xs="24" :sm="12" :md="6">
|
||||||
<el-card shadow="hover" class="stat-card stat-success">
|
<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 class="stat-label">确认毕业</div>
|
||||||
<div class="stat-value">{{ summary.confirmed }}</div>
|
</div>
|
||||||
|
</div>
|
||||||
</el-card>
|
</el-card>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="6">
|
<el-col :xs="24" :sm="12" :md="6">
|
||||||
<el-card shadow="hover" class="stat-card stat-warning">
|
<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 class="stat-label">待确认</div>
|
||||||
<div class="stat-value">{{ summary.pending }}</div>
|
</div>
|
||||||
|
</div>
|
||||||
</el-card>
|
</el-card>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="6">
|
<el-col :xs="24" :sm="12" :md="6">
|
||||||
<el-card shadow="hover" class="stat-card stat-danger">
|
<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 class="stat-label">不可毕业</div>
|
||||||
<div class="stat-value">{{ summary.rejected }}</div>
|
</div>
|
||||||
|
</div>
|
||||||
</el-card>
|
</el-card>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</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>
|
<template #header>
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<span class="card-title">
|
<span class="card-title">学院毕业人数分布</span>
|
||||||
<el-icon class="title-icon"><Document /></el-icon>
|
|
||||||
按学院统计
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<el-table :data="deptStats" v-loading="loading" stripe border class="modern-table">
|
<div ref="deptChartRef" class="chart-container"></div>
|
||||||
<el-table-column type="index" label="序号" width="60" align="center" />
|
</el-card>
|
||||||
<el-table-column prop="deptName" label="学院" min-width="140" show-overflow-tooltip />
|
</el-col>
|
||||||
<el-table-column prop="total" label="应毕业人数" width="110" align="center" />
|
<el-col :xs="24" :lg="12">
|
||||||
<el-table-column prop="pending" label="待确认" width="90" align="center" />
|
<el-card shadow="never" class="chart-card">
|
||||||
<el-table-column prop="confirmed" label="确认毕业" width="100" align="center" />
|
<template #header>
|
||||||
<el-table-column prop="rejected" label="不可毕业" width="100" align="center" />
|
<div class="card-header">
|
||||||
<el-table-column prop="completionRate" label="完成率" width="100" align="center">
|
<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">学院毕业详情</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<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">
|
<template #default="scope">
|
||||||
<span :class="completionRateClass(scope.row.completionRate)">
|
<el-tag type="success" size="small">{{ scope.row.confirmed }}</el-tag>
|
||||||
{{ scope.row.completionRate }}
|
</template>
|
||||||
</span>
|
</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>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
<template #empty>
|
|
||||||
<el-empty description="请选择毕业年份并点击「查询统计」" :image-size="100" />
|
|
||||||
</template>
|
|
||||||
</el-card>
|
</el-card>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts" name="GradustuAnalyse">
|
<script setup lang="ts" name="GradustuAnalyse">
|
||||||
import { reactive, ref, computed, onMounted } from 'vue';
|
import { ref, reactive, computed, onMounted, onUnmounted, nextTick } from 'vue';
|
||||||
import { useMessage } from '/@/hooks/message';
|
import { Search, UserFilled, CircleCheck, CircleClose, Warning } from '@element-plus/icons-vue';
|
||||||
import { fetchListForAnalyse } from '/@/api/stuwork/gradustu';
|
import { fetchListForAnalyse } from '/@/api/stuwork/gradustu';
|
||||||
import { Search, Document } from '@element-plus/icons-vue';
|
import * as echarts from 'echarts';
|
||||||
|
|
||||||
const queryForm = reactive({
|
const searchFormRef = ref();
|
||||||
graduYear: '',
|
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 graduYearOptions = computed(() => {
|
||||||
const y = new Date().getFullYear();
|
const y = new Date().getFullYear();
|
||||||
return Array.from({ length: 11 }, (_, i) => y - 5 + i);
|
return Array.from({ length: 11 }, (_, i) => y - 5 + i);
|
||||||
});
|
});
|
||||||
|
|
||||||
// 汇总:总人数、确认毕业、待确认、不可毕业
|
// 统计数据
|
||||||
const summary = computed(() => {
|
const statistics = computed(() => {
|
||||||
const list = rawList.value;
|
const total = dataList.value.length;
|
||||||
let pending = 0;
|
const confirmed = dataList.value.filter((item) => item.status === '1').length;
|
||||||
let confirmed = 0;
|
const pending = dataList.value.filter((item) => item.status === '0').length;
|
||||||
let rejected = 0;
|
const rejected = dataList.value.filter((item) => item.status === '-1').length;
|
||||||
list.forEach((item: any) => {
|
return { total, confirmed, pending, rejected };
|
||||||
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 deptStats = computed(() => {
|
const deptDetailList = computed(() => {
|
||||||
const list = rawList.value;
|
const deptMap = new Map<string, { deptName: string; total: number; confirmed: number; pending: number; rejected: number }>();
|
||||||
const map: Record<string, { deptCode: string; deptName: string; total: number; pending: number; confirmed: number; rejected: number }> = {};
|
|
||||||
list.forEach((item: any) => {
|
dataList.value.forEach((item) => {
|
||||||
const code = item.deptCode || '未知';
|
const deptName = item.deptName || '未知学院';
|
||||||
const name = item.deptName || item.deptCode || '未知';
|
if (!deptMap.has(deptName)) {
|
||||||
if (!map[code]) {
|
deptMap.set(deptName, { deptName, total: 0, confirmed: 0, pending: 0, rejected: 0 });
|
||||||
map[code] = { deptCode: code, deptName: name, total: 0, pending: 0, confirmed: 0, rejected: 0 };
|
|
||||||
}
|
}
|
||||||
const row = map[code];
|
const dept = deptMap.get(deptName)!;
|
||||||
row.total++;
|
dept.total++;
|
||||||
const s = String(item.status ?? '');
|
if (item.status === '1') dept.confirmed++;
|
||||||
if (s === '0') row.pending++;
|
else if (item.status === '0') dept.pending++;
|
||||||
else if (s === '1') row.confirmed++;
|
else if (item.status === '-1') dept.rejected++;
|
||||||
else if (s === '-1') row.rejected++;
|
|
||||||
});
|
});
|
||||||
return Object.values(map).map((row) => ({
|
|
||||||
...row,
|
return Array.from(deptMap.values()).map((item) => ({
|
||||||
completionRate: row.total > 0 ? ((row.confirmed / row.total) * 100).toFixed(1) + '%' : '0%',
|
...item,
|
||||||
|
rate: item.total > 0 ? Math.round((item.confirmed / item.total) * 100) : 0,
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
const completionRateClass = (rate: string) => {
|
// 查询数据
|
||||||
const num = parseFloat(rate);
|
const handleSearch = async () => {
|
||||||
if (num >= 100) return 'rate-high';
|
if (!searchForm.graduYear) {
|
||||||
if (num >= 80) return 'rate-mid';
|
|
||||||
return 'rate-low';
|
|
||||||
};
|
|
||||||
|
|
||||||
const loadStatistics = async () => {
|
|
||||||
if (!queryForm.graduYear) {
|
|
||||||
useMessage().warning('请选择毕业年份');
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
try {
|
try {
|
||||||
const list = await fetchListForAnalyse(queryForm.graduYear);
|
const res = await fetchListForAnalyse(searchForm.graduYear);
|
||||||
rawList.value = list;
|
dataList.value = res;
|
||||||
} catch (err: any) {
|
await nextTick();
|
||||||
useMessage().error(err.msg || '获取数据失败');
|
renderCharts();
|
||||||
rawList.value = [];
|
} catch (err) {
|
||||||
|
console.error('获取数据失败', err);
|
||||||
|
dataList.value = [];
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false;
|
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(() => {
|
onMounted(() => {
|
||||||
queryForm.graduYear = String(new Date().getFullYear());
|
handleSearch();
|
||||||
|
window.addEventListener('resize', handleResize);
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
window.removeEventListener('resize', handleResize);
|
||||||
|
deptChart?.dispose();
|
||||||
|
typeChart?.dispose();
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@import '/@/assets/styles/modern-page.scss';
|
.stat-cards {
|
||||||
|
margin-bottom: 20px;
|
||||||
.summary-row {
|
|
||||||
margin-bottom: 16px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.stat-card {
|
.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 {
|
.stat-label {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: var(--el-text-color-secondary);
|
color: #909399;
|
||||||
}
|
margin-top: 4px;
|
||||||
.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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.rate-high {
|
|
||||||
color: var(--el-color-success);
|
.chart-row {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart-card {
|
||||||
|
.card-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-title {
|
||||||
|
font-size: 16px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
.rate-mid {
|
|
||||||
color: var(--el-color-warning);
|
|
||||||
}
|
}
|
||||||
.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>
|
</style>
|
||||||
@@ -1,9 +1,17 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="layout-padding">
|
<div class="modern-page-container">
|
||||||
<div class="layout-padding-auto layout-padding-view">
|
<div class="page-wrapper">
|
||||||
<!-- 搜索表单 -->
|
<!-- 搜索表单卡片 -->
|
||||||
<el-row v-show="showSearch">
|
<el-card v-show="showSearch" class="search-card" shadow="never">
|
||||||
<el-form :model="queryForm" ref="searchFormRef" :inline="true" @keyup.enter="getDataList">
|
<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" ref="searchFormRef" :inline="true" @keyup.enter="getDataList" class="search-form">
|
||||||
<el-form-item label="学年" prop="schoolYear">
|
<el-form-item label="学年" prop="schoolYear">
|
||||||
<el-select
|
<el-select
|
||||||
v-model="queryForm.schoolYear"
|
v-model="queryForm.schoolYear"
|
||||||
@@ -54,15 +62,25 @@
|
|||||||
<el-button type="warning" icon="Bell" @click="handleSendWarning" :loading="warningLoading">发送预警</el-button>
|
<el-button type="warning" icon="Bell" @click="handleSendWarning" :loading="warningLoading">发送预警</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</el-row>
|
</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>
|
||||||
|
</template>
|
||||||
|
|
||||||
<!-- 统计表格 -->
|
<!-- 统计表格 -->
|
||||||
<el-row style="margin-bottom: 20px">
|
|
||||||
<el-table
|
<el-table
|
||||||
:data="statisticsData"
|
:data="statisticsData"
|
||||||
v-loading="loading"
|
v-loading="loading"
|
||||||
border
|
stripe
|
||||||
style="width: 100%"
|
style="width: 100%; margin-bottom: 20px"
|
||||||
:cell-style="tableStyle.cellStyle"
|
:cell-style="tableStyle.cellStyle"
|
||||||
:header-cell-style="tableStyle.headerCellStyle"
|
:header-cell-style="tableStyle.headerCellStyle"
|
||||||
>
|
>
|
||||||
@@ -73,50 +91,52 @@
|
|||||||
<el-table-column prop="pass" label="及格" min-width="100" align="center" />
|
<el-table-column prop="pass" label="及格" min-width="100" align="center" />
|
||||||
<el-table-column prop="fail" label="不及格" min-width="100" align="center" />
|
<el-table-column prop="fail" label="不及格" min-width="100" align="center" />
|
||||||
</el-table>
|
</el-table>
|
||||||
</el-row>
|
|
||||||
|
|
||||||
<!-- 学生列表表格 -->
|
<!-- 学生列表表格 -->
|
||||||
<el-row>
|
|
||||||
<el-table
|
<el-table
|
||||||
:data="studentList"
|
:data="studentList"
|
||||||
v-loading="loading"
|
v-loading="loading"
|
||||||
border
|
stripe
|
||||||
:max-height="600"
|
style="width: 100%"
|
||||||
:cell-style="tableStyle.cellStyle"
|
:cell-style="tableStyle.cellStyle"
|
||||||
:header-cell-style="tableStyle.headerCellStyle"
|
:header-cell-style="tableStyle.headerCellStyle"
|
||||||
>
|
>
|
||||||
<el-table-column type="index" label="序号" width="60" align="center" />
|
<el-table-column type="index" label="序号" width="70" align="center" />
|
||||||
<el-table-column prop="stuNo" label="学号" show-overflow-tooltip align="center" />
|
<el-table-column prop="stuNo" label="学号" min-width="120" show-overflow-tooltip align="center" />
|
||||||
<el-table-column prop="realName" label="姓名" show-overflow-tooltip align="center" />
|
<el-table-column prop="realName" label="姓名" min-width="100" show-overflow-tooltip align="center" />
|
||||||
<el-table-column prop="score" label="学期总评" show-overflow-tooltip align="center">
|
<!-- 各月份分数 -->
|
||||||
|
<el-table-column v-for="(month, index) in monthColumns" :key="index" :label="month.label" min-width="70" align="center">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<span>{{ scope.row.score !== null && scope.row.score !== undefined ? scope.row.score.toFixed(2) : '-' }}</span>
|
<span>{{ formatScore(scope.row[month.prop]) }}</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="操作" width="150" align="center" fixed="right">
|
<!-- 学期总评 -->
|
||||||
|
<el-table-column prop="scoreOneTerm" label="学期总评" min-width="100" align="center">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-button icon="View" text type="primary" @click="handleView(scope.row)"> 查看 </el-button>
|
<el-tag :type="getScoreType(scope.row.scoreOneTerm)" size="small" effect="plain">
|
||||||
|
{{ formatScore(scope.row.scoreOneTerm) }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="操作" width="100" align="center" fixed="right">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button icon="View" link type="primary" @click="handleView(scope.row)">查看</el-button>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
</el-row>
|
</el-card>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 查看详情弹窗(接口:queryDataByStuNo 通过学年学号查看详情,按当前学期筛选) -->
|
<!-- 查看详情弹窗 -->
|
||||||
<el-dialog
|
<el-dialog v-model="viewDialogVisible" title="学期操行考核详情" width="900px" destroy-on-close @close="viewDetailList = []">
|
||||||
v-model="viewDialogVisible"
|
|
||||||
title="学期操行考核详情"
|
|
||||||
width="800px"
|
|
||||||
destroy-on-close
|
|
||||||
@close="viewDetailList = []">
|
|
||||||
<div v-if="viewRow" class="view-summary">
|
<div v-if="viewRow" class="view-summary">
|
||||||
<el-descriptions :column="2" border size="small">
|
<el-descriptions :column="3" border size="small">
|
||||||
<el-descriptions-item label="学号">{{ viewRow.stuNo }}</el-descriptions-item>
|
<el-descriptions-item label="学号">{{ viewRow.stuNo }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="姓名">{{ viewRow.realName }}</el-descriptions-item>
|
<el-descriptions-item label="姓名">{{ viewRow.realName }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="学年">{{ queryForm.schoolYear }}</el-descriptions-item>
|
<el-descriptions-item label="学期总评">
|
||||||
<el-descriptions-item label="学期">{{ formatSchoolTerm(queryForm.schoolTerm) }}</el-descriptions-item>
|
<el-tag :type="getScoreType(viewRow.scoreOneTerm)" size="small">
|
||||||
<el-descriptions-item label="学期总评" :span="2">
|
{{ formatScore(viewRow.scoreOneTerm) }}
|
||||||
{{ viewRow.score != null && viewRow.score !== undefined ? Number(viewRow.score).toFixed(2) : '-' }}
|
</el-tag>
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
</el-descriptions>
|
</el-descriptions>
|
||||||
</div>
|
</div>
|
||||||
@@ -124,14 +144,13 @@
|
|||||||
<el-table
|
<el-table
|
||||||
:data="viewDetailList"
|
:data="viewDetailList"
|
||||||
v-loading="viewLoading"
|
v-loading="viewLoading"
|
||||||
border
|
stripe
|
||||||
size="small"
|
size="small"
|
||||||
max-height="400"
|
max-height="400"
|
||||||
:cell-style="tableStyle.cellStyle"
|
:cell-style="tableStyle.cellStyle"
|
||||||
:header-cell-style="tableStyle.headerCellStyle">
|
:header-cell-style="tableStyle.headerCellStyle">
|
||||||
<el-table-column type="index" label="序号" width="60" align="center" />
|
<el-table-column type="index" label="序号" width="60" align="center" />
|
||||||
<el-table-column prop="schoolTerm" label="学期" width="80" align="center" show-overflow-tooltip />
|
<el-table-column prop="recordDate" label="考核日期" width="120" align="center" show-overflow-tooltip />
|
||||||
<el-table-column prop="recordDate" label="考核日期" width="110" align="center" show-overflow-tooltip />
|
|
||||||
<el-table-column prop="conductType" label="类型" width="80" align="center">
|
<el-table-column prop="conductType" label="类型" width="80" align="center">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-tag :type="scope.row.conductType === '1' ? 'success' : 'danger'" size="small">
|
<el-tag :type="scope.row.conductType === '1' ? 'success' : 'danger'" size="small">
|
||||||
@@ -144,8 +163,8 @@
|
|||||||
{{ scope.row.score != null && scope.row.score !== undefined ? Number(scope.row.score) : '-' }}
|
{{ scope.row.score != null && scope.row.score !== undefined ? Number(scope.row.score) : '-' }}
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="description" label="情况记录" min-width="140" show-overflow-tooltip />
|
<el-table-column prop="description" label="情况记录" min-width="150" show-overflow-tooltip />
|
||||||
<el-table-column prop="remarks" label="备注" min-width="100" show-overflow-tooltip />
|
<el-table-column prop="remarks" label="备注" min-width="120" show-overflow-tooltip />
|
||||||
</el-table>
|
</el-table>
|
||||||
<template v-if="viewDetailList.length === 0 && !viewLoading">
|
<template v-if="viewDetailList.length === 0 && !viewLoading">
|
||||||
<el-empty description="暂无考核记录" :image-size="80" />
|
<el-empty description="暂无考核记录" :image-size="80" />
|
||||||
@@ -156,16 +175,17 @@
|
|||||||
|
|
||||||
<script setup lang="ts" name="StuConductTerm">
|
<script setup lang="ts" name="StuConductTerm">
|
||||||
import { reactive, ref, onMounted, computed } from 'vue'
|
import { reactive, ref, onMounted, computed } from 'vue'
|
||||||
import { getStuConductTerm, queryDataByStuNo, sendConductWarning } from "/@/api/stuwork/stuconduct";
|
import { getStuConductTerm, sendConductWarning, queryDataByStuNo } from "/@/api/stuwork/stuconduct";
|
||||||
import { getClassListByRole } from "/@/api/basic/basicclass";
|
import { getClassListByRole } from "/@/api/basic/basicclass";
|
||||||
import { queryAllSchoolYear } from "/@/api/basic/basicyear";
|
import { queryAllSchoolYear } from "/@/api/basic/basicyear";
|
||||||
import { getDicts } from "/@/api/admin/dict";
|
import { getDicts } from "/@/api/admin/dict";
|
||||||
import { useMessage, useMessageBox } from "/@/hooks/message";
|
import { useMessage, useMessageBox } from "/@/hooks/message";
|
||||||
|
import { Search, Document } from '@element-plus/icons-vue';
|
||||||
|
|
||||||
// 表格样式 - 在组件内部定义,不从外部导入
|
// 表格样式
|
||||||
const tableStyle = {
|
const tableStyle = {
|
||||||
cellStyle: { padding: '8px 0' },
|
cellStyle: { padding: '8px 0', textAlign: 'center' },
|
||||||
headerCellStyle: { background: '#f5f7fa', color: '#606266', fontWeight: 'bold' },
|
headerCellStyle: { background: '#f5f7fa', color: '#606266', fontWeight: 'bold', textAlign: 'center' },
|
||||||
};
|
};
|
||||||
|
|
||||||
// 定义变量内容
|
// 定义变量内容
|
||||||
@@ -189,28 +209,73 @@ const queryForm = reactive({
|
|||||||
classCode: '',
|
classCode: '',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 根据学期动态生成月份列
|
||||||
|
const monthColumns = computed(() => {
|
||||||
|
// 第一学期:9月、10月、11月、12月、1月
|
||||||
|
// 第二学期:2月、3月、4月、5月、6月
|
||||||
|
if (queryForm.schoolTerm === '1') {
|
||||||
|
return [
|
||||||
|
{ label: '9月', prop: 'scoreOneMonth' },
|
||||||
|
{ label: '10月', prop: 'scoreTwoMonth' },
|
||||||
|
{ label: '11月', prop: 'scoreThreeMonth' },
|
||||||
|
{ label: '12月', prop: 'scoreFourMonth' },
|
||||||
|
{ label: '1月', prop: 'scoreFiveMonth' },
|
||||||
|
];
|
||||||
|
} else if (queryForm.schoolTerm === '2') {
|
||||||
|
return [
|
||||||
|
{ label: '2月', prop: 'scoreSixMonth' },
|
||||||
|
{ label: '3月', prop: 'scoreSevenMonth' },
|
||||||
|
{ label: '4月', prop: 'scoreEightMonth' },
|
||||||
|
{ label: '5月', prop: 'scoreNineMonth' },
|
||||||
|
{ label: '6月', prop: 'scoreTenMonth' },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
// 默认返回第一学期
|
||||||
|
return [
|
||||||
|
{ label: '9月', prop: 'scoreOneMonth' },
|
||||||
|
{ label: '10月', prop: 'scoreTwoMonth' },
|
||||||
|
{ label: '11月', prop: 'scoreThreeMonth' },
|
||||||
|
{ label: '12月', prop: 'scoreFourMonth' },
|
||||||
|
{ label: '1月', prop: 'scoreFiveMonth' },
|
||||||
|
];
|
||||||
|
});
|
||||||
|
|
||||||
|
// 格式化分数
|
||||||
|
const formatScore = (score: any) => {
|
||||||
|
if (score === null || score === undefined || score === '') return '-';
|
||||||
|
return Number(score).toFixed(2);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 根据分数获取标签类型
|
||||||
|
const getScoreType = (score: any) => {
|
||||||
|
if (score === null || score === undefined) return 'info';
|
||||||
|
const num = Number(score);
|
||||||
|
if (num >= 90) return 'success';
|
||||||
|
if (num >= 80) return 'primary';
|
||||||
|
if (num >= 60) return 'warning';
|
||||||
|
return 'danger';
|
||||||
|
};
|
||||||
|
|
||||||
// 统计表格数据
|
// 统计表格数据
|
||||||
const statisticsData = computed(() => {
|
const statisticsData = computed(() => {
|
||||||
if (studentList.value.length === 0) {
|
if (studentList.value.length === 0) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
// 计算各等级人数
|
let excellent = 0;
|
||||||
// 优秀:>=90,良好:80-89,及格:60-79,不及格:<60
|
let good = 0;
|
||||||
let excellent = 0; // 优秀
|
let pass = 0;
|
||||||
let good = 0; // 良好
|
let fail = 0;
|
||||||
let pass = 0; // 及格
|
|
||||||
let fail = 0; // 不及格
|
|
||||||
const total = studentList.value.length;
|
const total = studentList.value.length;
|
||||||
|
|
||||||
studentList.value.forEach((student: any) => {
|
studentList.value.forEach((student: any) => {
|
||||||
const score = student.score;
|
const score = student.scoreOneTerm;
|
||||||
if (score !== null && score !== undefined) {
|
if (score !== null && score !== undefined) {
|
||||||
if (score >= 90) {
|
if (Number(score) >= 90) {
|
||||||
excellent++;
|
excellent++;
|
||||||
} else if (score >= 80) {
|
} else if (Number(score) >= 80) {
|
||||||
good++;
|
good++;
|
||||||
} else if (score >= 60) {
|
} else if (Number(score) >= 60) {
|
||||||
pass++;
|
pass++;
|
||||||
} else {
|
} else {
|
||||||
fail++;
|
fail++;
|
||||||
@@ -218,52 +283,19 @@ const statisticsData = computed(() => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 计算比率
|
|
||||||
const excellentRate = total > 0 ? ((excellent / total) * 100).toFixed(2) + '%' : '0%';
|
const excellentRate = total > 0 ? ((excellent / total) * 100).toFixed(2) + '%' : '0%';
|
||||||
const goodRate = total > 0 ? ((good / total) * 100).toFixed(2) + '%' : '0%';
|
const goodRate = total > 0 ? ((good / total) * 100).toFixed(2) + '%' : '0%';
|
||||||
const passRate = total > 0 ? ((pass / total) * 100).toFixed(2) + '%' : '0%';
|
const passRate = total > 0 ? ((pass / total) * 100).toFixed(2) + '%' : '0%';
|
||||||
const failRate = total > 0 ? ((fail / total) * 100).toFixed(2) + '%' : '0%';
|
const failRate = total > 0 ? ((fail / total) * 100).toFixed(2) + '%' : '0%';
|
||||||
|
const excellentGoodRate = total > 0 ? (((excellent + good) / total) * 100).toFixed(2) + '%' : '0%';
|
||||||
|
|
||||||
// 优良率 = (优秀 + 良好) / 总人数
|
|
||||||
const excellentGoodCount = excellent + good;
|
|
||||||
const excellentGoodRate = total > 0 ? ((excellentGoodCount / total) * 100).toFixed(2) + '%' : '0%';
|
|
||||||
|
|
||||||
// 获取班级名称
|
|
||||||
const classNo = studentList.value.length > 0 ? studentList.value[0].classNo || '-' : '-';
|
const classNo = studentList.value.length > 0 ? studentList.value[0].classNo || '-' : '-';
|
||||||
|
|
||||||
return [
|
return [
|
||||||
{
|
{ label: '人数', classNo, excellent, good, pass, fail },
|
||||||
label: '人数',
|
{ label: '比率', classNo, excellent: excellentRate, good: goodRate, pass: passRate, fail: failRate },
|
||||||
classNo: classNo,
|
{ label: '优良率', classNo, excellent: excellentGoodRate, good: '-', pass: '-', fail: '-' },
|
||||||
excellent: excellent,
|
{ label: '备注', classNo, excellent: '-', good: '-', pass: '-', fail: '-' },
|
||||||
good: good,
|
|
||||||
pass: pass,
|
|
||||||
fail: fail,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '比率',
|
|
||||||
classNo: classNo,
|
|
||||||
excellent: excellentRate,
|
|
||||||
good: goodRate,
|
|
||||||
pass: passRate,
|
|
||||||
fail: failRate,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '优良率',
|
|
||||||
classNo: classNo,
|
|
||||||
excellent: excellentGoodRate,
|
|
||||||
good: '-',
|
|
||||||
pass: '-',
|
|
||||||
fail: '-',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '备注',
|
|
||||||
classNo: classNo,
|
|
||||||
excellent: '-',
|
|
||||||
good: '-',
|
|
||||||
pass: '-',
|
|
||||||
fail: '-',
|
|
||||||
},
|
|
||||||
];
|
];
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -283,34 +315,19 @@ const getDataList = async () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (res.data && Array.isArray(res.data)) {
|
if (res.data && Array.isArray(res.data)) {
|
||||||
// 处理返回的数据,提取学生列表
|
// 从返回数据中提取学生列表
|
||||||
// 根据API文档,返回的是StuConductTermVO数组
|
|
||||||
const tempList: any[] = [];
|
const tempList: any[] = [];
|
||||||
|
|
||||||
res.data.forEach((item: any) => {
|
res.data.forEach((item: any) => {
|
||||||
// 如果返回的数据结构中有basicStudentVOList,需要展开
|
if (item.basicStudentVOList && Array.isArray(item.basicStudentVOList)) {
|
||||||
if (item.basicStudentVOList && Array.isArray(item.basicStudentVOList) && item.basicStudentVOList.length > 0) {
|
|
||||||
item.basicStudentVOList.forEach((student: any) => {
|
item.basicStudentVOList.forEach((student: any) => {
|
||||||
tempList.push({
|
tempList.push({
|
||||||
stuNo: student.stuNo || item.stuNo,
|
...student,
|
||||||
realName: student.realName || item.realName,
|
|
||||||
score: item.score, // 学期总评分数
|
|
||||||
classNo: item.classNo,
|
classNo: item.classNo,
|
||||||
classCode: item.classCode,
|
classCode: item.classCode,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
// 直接使用item作为学生信息
|
|
||||||
tempList.push({
|
|
||||||
stuNo: item.stuNo,
|
|
||||||
realName: item.realName,
|
|
||||||
score: item.score,
|
|
||||||
classNo: item.classNo,
|
|
||||||
classCode: item.classCode,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
studentList.value = tempList;
|
studentList.value = tempList;
|
||||||
} else {
|
} else {
|
||||||
studentList.value = [];
|
studentList.value = [];
|
||||||
@@ -338,7 +355,7 @@ const handleReset = () => {
|
|||||||
studentList.value = [];
|
studentList.value = [];
|
||||||
};
|
};
|
||||||
|
|
||||||
// 查看详情(接口:GET /stuwork/stuconduct/queryDataByStuNo,按当前学年+学号拉取后筛本学期记录)
|
// 查看详情
|
||||||
const handleView = async (row: any) => {
|
const handleView = async (row: any) => {
|
||||||
if (!queryForm.schoolYear || !row.stuNo) {
|
if (!queryForm.schoolYear || !row.stuNo) {
|
||||||
useMessage().warning('缺少学年或学号');
|
useMessage().warning('缺少学年或学号');
|
||||||
@@ -354,6 +371,7 @@ const handleView = async (row: any) => {
|
|||||||
stuNo: row.stuNo,
|
stuNo: row.stuNo,
|
||||||
});
|
});
|
||||||
const list = Array.isArray(res.data) ? res.data : [];
|
const list = Array.isArray(res.data) ? res.data : [];
|
||||||
|
// 筛选当前学期的记录
|
||||||
const term = queryForm.schoolTerm;
|
const term = queryForm.schoolTerm;
|
||||||
viewDetailList.value = term ? list.filter((r: any) => String(r.schoolTerm) === String(term)) : list;
|
viewDetailList.value = term ? list.filter((r: any) => String(r.schoolTerm) === String(term)) : list;
|
||||||
} catch (_err) {
|
} catch (_err) {
|
||||||
@@ -373,9 +391,7 @@ const handleSendWarning = async () => {
|
|||||||
const { confirm } = useMessageBox();
|
const { confirm } = useMessageBox();
|
||||||
try {
|
try {
|
||||||
await confirm(
|
await confirm(
|
||||||
`确定要发送${queryForm.schoolYear}学年第${
|
`确定要发送${queryForm.schoolYear}学年第${queryForm.schoolTerm === '1' ? '一' : '二'}学期操行考核预警吗?将向班主任推送不及格学生(低于60分)的预警通知。`
|
||||||
queryForm.schoolTerm === '1' ? '一' : '二'
|
|
||||||
}学期操行考核预警吗?将向班主任推送不及格学生(低于60分)的预警通知。`
|
|
||||||
);
|
);
|
||||||
|
|
||||||
warningLoading.value = true;
|
warningLoading.value = true;
|
||||||
@@ -394,11 +410,7 @@ const handleSendWarning = async () => {
|
|||||||
const getSchoolYearList = async () => {
|
const getSchoolYearList = async () => {
|
||||||
try {
|
try {
|
||||||
const res = await queryAllSchoolYear();
|
const res = await queryAllSchoolYear();
|
||||||
if (res.data && Array.isArray(res.data)) {
|
schoolYearList.value = res.data && Array.isArray(res.data) ? res.data : [];
|
||||||
schoolYearList.value = res.data;
|
|
||||||
} else {
|
|
||||||
schoolYearList.value = [];
|
|
||||||
}
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
schoolYearList.value = [];
|
schoolYearList.value = [];
|
||||||
}
|
}
|
||||||
@@ -425,11 +437,7 @@ const getSchoolTermDict = async () => {
|
|||||||
const getClassListData = async () => {
|
const getClassListData = async () => {
|
||||||
try {
|
try {
|
||||||
const res = await getClassListByRole();
|
const res = await getClassListByRole();
|
||||||
if (res.data && Array.isArray(res.data)) {
|
classList.value = res.data && Array.isArray(res.data) ? res.data : [];
|
||||||
classList.value = res.data;
|
|
||||||
} else {
|
|
||||||
classList.value = [];
|
|
||||||
}
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
classList.value = [];
|
classList.value = [];
|
||||||
}
|
}
|
||||||
@@ -444,15 +452,7 @@ onMounted(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.layout-padding {
|
@import '/@/assets/styles/modern-page.scss';
|
||||||
.layout-padding-auto {
|
|
||||||
.layout-padding-view {
|
|
||||||
.el-row {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.view-summary {
|
.view-summary {
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
@@ -462,19 +462,4 @@ onMounted(() => {
|
|||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: #303133;
|
color: #303133;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 确保页面可以滚动
|
|
||||||
.layout-padding {
|
|
||||||
height: 100%;
|
|
||||||
overflow-y: auto;
|
|
||||||
|
|
||||||
.layout-padding-auto {
|
|
||||||
height: 100%;
|
|
||||||
|
|
||||||
.layout-padding-view {
|
|
||||||
height: 100%;
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
@@ -1,9 +1,17 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="layout-padding">
|
<div class="modern-page-container">
|
||||||
<div class="layout-padding-auto layout-padding-view">
|
<div class="page-wrapper">
|
||||||
<!-- 搜索表单 -->
|
<!-- 搜索表单卡片 -->
|
||||||
<el-row v-show="showSearch">
|
<el-card v-show="showSearch" class="search-card" shadow="never">
|
||||||
<el-form :model="queryForm" ref="searchFormRef" :inline="true" @keyup.enter="getDataList">
|
<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" ref="searchFormRef" :inline="true" @keyup.enter="getDataList" class="search-form">
|
||||||
<el-form-item label="学年" prop="schoolYear">
|
<el-form-item label="学年" prop="schoolYear">
|
||||||
<el-select v-model="queryForm.schoolYear" placeholder="请选择学年" clearable filterable style="width: 200px">
|
<el-select v-model="queryForm.schoolYear" placeholder="请选择学年" clearable filterable style="width: 200px">
|
||||||
<el-option v-for="item in schoolYearList" :key="item.year" :label="item.year" :value="item.year"> </el-option>
|
<el-option v-for="item in schoolYearList" :key="item.year" :label="item.year" :value="item.year"> </el-option>
|
||||||
@@ -19,15 +27,25 @@
|
|||||||
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</el-row>
|
</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>
|
||||||
|
</template>
|
||||||
|
|
||||||
<!-- 统计表格 -->
|
<!-- 统计表格 -->
|
||||||
<el-row style="margin-bottom: 20px">
|
|
||||||
<el-table
|
<el-table
|
||||||
:data="statisticsData"
|
:data="statisticsData"
|
||||||
v-loading="loading"
|
v-loading="loading"
|
||||||
border
|
stripe
|
||||||
style="width: 100%"
|
style="width: 100%; margin-bottom: 20px"
|
||||||
:cell-style="tableStyle.cellStyle"
|
:cell-style="tableStyle.cellStyle"
|
||||||
:header-cell-style="tableStyle.headerCellStyle"
|
:header-cell-style="tableStyle.headerCellStyle"
|
||||||
>
|
>
|
||||||
@@ -38,60 +56,106 @@
|
|||||||
<el-table-column prop="pass" label="及格" min-width="100" align="center" />
|
<el-table-column prop="pass" label="及格" min-width="100" align="center" />
|
||||||
<el-table-column prop="fail" label="不及格" min-width="100" align="center" />
|
<el-table-column prop="fail" label="不及格" min-width="100" align="center" />
|
||||||
</el-table>
|
</el-table>
|
||||||
</el-row>
|
|
||||||
|
|
||||||
<!-- 学生列表表格 -->
|
<!-- 学生列表表格 -->
|
||||||
<el-row>
|
|
||||||
<el-table
|
<el-table
|
||||||
:data="studentList"
|
:data="studentList"
|
||||||
v-loading="loading"
|
v-loading="loading"
|
||||||
border
|
stripe
|
||||||
:max-height="600"
|
style="width: 100%"
|
||||||
:cell-style="tableStyle.cellStyle"
|
:cell-style="tableStyle.cellStyle"
|
||||||
:header-cell-style="tableStyle.headerCellStyle"
|
:header-cell-style="tableStyle.headerCellStyle"
|
||||||
>
|
>
|
||||||
<el-table-column type="index" label="序号" width="60" align="center" />
|
<el-table-column type="index" label="序号" width="70" align="center" fixed="left" />
|
||||||
<el-table-column prop="stuNo" label="学号" show-overflow-tooltip align="center" />
|
<el-table-column prop="stuNo" label="学号" min-width="120" show-overflow-tooltip align="center" fixed="left" />
|
||||||
<el-table-column prop="realName" label="姓名" show-overflow-tooltip align="center" />
|
<el-table-column prop="realName" label="姓名" min-width="80" show-overflow-tooltip align="center" fixed="left" />
|
||||||
<el-table-column prop="score" label="学年总评" show-overflow-tooltip align="center">
|
<!-- 第一学期月份 -->
|
||||||
|
<el-table-column label="第一学期" align="center">
|
||||||
|
<el-table-column v-for="(month, index) in firstTermMonths" :key="'first-' + index" :label="month.label" min-width="60" align="center">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<span>{{ scope.row.score !== null && scope.row.score !== undefined ? scope.row.score.toFixed(2) : '-' }}</span>
|
<span>{{ formatScore(scope.row[month.prop]) }}</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="操作" width="150" align="center" fixed="right">
|
<el-table-column label="学期评" min-width="70" align="center">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-button icon="View" text type="primary" @click="handleView(scope.row)"> 查看 </el-button>
|
<el-tag :type="getScoreType(scope.row.scoreOneTerm)" size="small" effect="plain">
|
||||||
|
{{ formatScore(scope.row.scoreOneTerm) }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table-column>
|
||||||
|
<!-- 第二学期月份 -->
|
||||||
|
<el-table-column label="第二学期" align="center">
|
||||||
|
<el-table-column v-for="(month, index) in secondTermMonths" :key="'second-' + index" :label="month.label" min-width="60" align="center">
|
||||||
|
<template #default="scope">
|
||||||
|
<span>{{ formatScore(scope.row[month.prop]) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="学期评" min-width="70" align="center">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-tag :type="getScoreType(scope.row.scoreTwoTerm)" size="small" effect="plain">
|
||||||
|
{{ formatScore(scope.row.scoreTwoTerm) }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table-column>
|
||||||
|
<!-- 学年总评 -->
|
||||||
|
<el-table-column prop="scoreYear" label="学年总评" min-width="100" align="center" fixed="right">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-tag :type="getScoreType(scope.row.scoreYear)" size="small" effect="dark">
|
||||||
|
{{ formatScore(scope.row.scoreYear) }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="操作" width="100" align="center" fixed="right">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button icon="View" link type="primary" @click="handleView(scope.row)">查看</el-button>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
</el-row>
|
</el-card>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 查看详情弹窗(接口:queryDataByStuNo 通过学年学号查看详情) -->
|
<!-- 查看详情弹窗 -->
|
||||||
<el-dialog v-model="viewDialogVisible" title="学年操行考核详情" width="800px" destroy-on-close @close="viewDetailList = []">
|
<el-dialog v-model="viewDialogVisible" title="学年操行考核详情" width="900px" destroy-on-close @close="viewDetailList = []">
|
||||||
<div v-if="viewRow" class="view-summary">
|
<div v-if="viewRow" class="view-summary">
|
||||||
<el-descriptions :column="2" border size="small">
|
<el-descriptions :column="3" border size="small">
|
||||||
<el-descriptions-item label="学号">{{ viewRow.stuNo }}</el-descriptions-item>
|
<el-descriptions-item label="学号">{{ viewRow.stuNo }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="姓名">{{ viewRow.realName }}</el-descriptions-item>
|
<el-descriptions-item label="姓名">{{ viewRow.realName }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="学年">{{ queryForm.schoolYear }}</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="学年总评">
|
<el-descriptions-item label="学年总评">
|
||||||
{{ viewRow.score != null && viewRow.score !== undefined ? Number(viewRow.score).toFixed(2) : '-' }}
|
<el-tag :type="getScoreType(viewRow.scoreYear)" size="small">
|
||||||
|
{{ formatScore(viewRow.scoreYear) }}
|
||||||
|
</el-tag>
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="第一学期">
|
||||||
|
<el-tag :type="getScoreType(viewRow.scoreOneTerm)" size="small" effect="plain">
|
||||||
|
{{ formatScore(viewRow.scoreOneTerm) }}
|
||||||
|
</el-tag>
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="第二学期">
|
||||||
|
<el-tag :type="getScoreType(viewRow.scoreTwoTerm)" size="small" effect="plain">
|
||||||
|
{{ formatScore(viewRow.scoreTwoTerm) }}
|
||||||
|
</el-tag>
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="学年">{{ queryForm.schoolYear }}</el-descriptions-item>
|
||||||
</el-descriptions>
|
</el-descriptions>
|
||||||
</div>
|
</div>
|
||||||
<div class="view-detail-title">考核记录</div>
|
<div class="view-detail-title">考核记录</div>
|
||||||
<el-table
|
<el-table
|
||||||
:data="viewDetailList"
|
:data="viewDetailList"
|
||||||
v-loading="viewLoading"
|
v-loading="viewLoading"
|
||||||
border
|
stripe
|
||||||
size="small"
|
size="small"
|
||||||
max-height="400"
|
max-height="400"
|
||||||
:cell-style="tableStyle.cellStyle"
|
:cell-style="tableStyle.cellStyle"
|
||||||
:header-cell-style="tableStyle.headerCellStyle"
|
:header-cell-style="tableStyle.headerCellStyle">
|
||||||
>
|
|
||||||
<el-table-column type="index" label="序号" width="60" align="center" />
|
<el-table-column type="index" label="序号" width="60" align="center" />
|
||||||
<el-table-column prop="schoolTerm" label="学期" width="80" align="center" show-overflow-tooltip />
|
<el-table-column prop="schoolTerm" label="学期" width="80" align="center">
|
||||||
<el-table-column prop="recordDate" label="考核日期" width="110" align="center" show-overflow-tooltip />
|
<template #default="scope">
|
||||||
|
<el-tag size="small" effect="plain">{{ scope.row.schoolTerm === '1' ? '第一学期' : '第二学期' }}</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="recordDate" label="考核日期" width="120" align="center" show-overflow-tooltip />
|
||||||
<el-table-column prop="conductType" label="类型" width="80" align="center">
|
<el-table-column prop="conductType" label="类型" width="80" align="center">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-tag :type="scope.row.conductType === '1' ? 'success' : 'danger'" size="small">
|
<el-tag :type="scope.row.conductType === '1' ? 'success' : 'danger'" size="small">
|
||||||
@@ -104,8 +168,8 @@
|
|||||||
{{ scope.row.score != null && scope.row.score !== undefined ? Number(scope.row.score) : '-' }}
|
{{ scope.row.score != null && scope.row.score !== undefined ? Number(scope.row.score) : '-' }}
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="description" label="情况记录" min-width="140" show-overflow-tooltip />
|
<el-table-column prop="description" label="情况记录" min-width="150" show-overflow-tooltip />
|
||||||
<el-table-column prop="remarks" label="备注" min-width="100" show-overflow-tooltip />
|
<el-table-column prop="remarks" label="备注" min-width="120" show-overflow-tooltip />
|
||||||
</el-table>
|
</el-table>
|
||||||
<template v-if="viewDetailList.length === 0 && !viewLoading">
|
<template v-if="viewDetailList.length === 0 && !viewLoading">
|
||||||
<el-empty description="暂无考核记录" :image-size="80" />
|
<el-empty description="暂无考核记录" :image-size="80" />
|
||||||
@@ -120,11 +184,12 @@ import { getStuConductYear, queryDataByStuNo } from '/@/api/stuwork/stuconduct';
|
|||||||
import { getClassListByRole } from '/@/api/basic/basicclass';
|
import { getClassListByRole } from '/@/api/basic/basicclass';
|
||||||
import { queryAllSchoolYear } from '/@/api/basic/basicyear';
|
import { queryAllSchoolYear } from '/@/api/basic/basicyear';
|
||||||
import { useMessage } from '/@/hooks/message';
|
import { useMessage } from '/@/hooks/message';
|
||||||
|
import { Search, Document } from '@element-plus/icons-vue';
|
||||||
|
|
||||||
// 表格样式 - 在组件内部定义,不从外部导入
|
// 表格样式
|
||||||
const tableStyle = {
|
const tableStyle = {
|
||||||
cellStyle: { padding: '8px 0' },
|
cellStyle: { padding: '8px 0', textAlign: 'center' },
|
||||||
headerCellStyle: { background: '#f5f7fa', color: '#606266', fontWeight: 'bold' },
|
headerCellStyle: { background: '#f5f7fa', color: '#606266', fontWeight: 'bold', textAlign: 'center' },
|
||||||
};
|
};
|
||||||
|
|
||||||
// 定义变量内容
|
// 定义变量内容
|
||||||
@@ -145,28 +210,60 @@ const queryForm = reactive({
|
|||||||
classCode: '',
|
classCode: '',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 第一学期月份列(9月-1月)
|
||||||
|
const firstTermMonths = [
|
||||||
|
{ label: '9月', prop: 'scoreOneMonth' },
|
||||||
|
{ label: '10月', prop: 'scoreTwoMonth' },
|
||||||
|
{ label: '11月', prop: 'scoreThreeMonth' },
|
||||||
|
{ label: '12月', prop: 'scoreFourMonth' },
|
||||||
|
{ label: '1月', prop: 'scoreFiveMonth' },
|
||||||
|
];
|
||||||
|
|
||||||
|
// 第二学期月份列(2月-6月)
|
||||||
|
const secondTermMonths = [
|
||||||
|
{ label: '2月', prop: 'scoreSixMonth' },
|
||||||
|
{ label: '3月', prop: 'scoreSevenMonth' },
|
||||||
|
{ label: '4月', prop: 'scoreEightMonth' },
|
||||||
|
{ label: '5月', prop: 'scoreNineMonth' },
|
||||||
|
{ label: '6月', prop: 'scoreTenMonth' },
|
||||||
|
];
|
||||||
|
|
||||||
|
// 格式化分数
|
||||||
|
const formatScore = (score: any) => {
|
||||||
|
if (score === null || score === undefined || score === '') return '-';
|
||||||
|
return Number(score).toFixed(2);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 根据分数获取标签类型
|
||||||
|
const getScoreType = (score: any) => {
|
||||||
|
if (score === null || score === undefined) return 'info';
|
||||||
|
const num = Number(score);
|
||||||
|
if (num >= 90) return 'success';
|
||||||
|
if (num >= 80) return 'primary';
|
||||||
|
if (num >= 60) return 'warning';
|
||||||
|
return 'danger';
|
||||||
|
};
|
||||||
|
|
||||||
// 统计表格数据
|
// 统计表格数据
|
||||||
const statisticsData = computed(() => {
|
const statisticsData = computed(() => {
|
||||||
if (studentList.value.length === 0) {
|
if (studentList.value.length === 0) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
// 计算各等级人数
|
let excellent = 0;
|
||||||
// 优秀:>=90,良好:80-89,及格:60-79,不及格:<60
|
let good = 0;
|
||||||
let excellent = 0; // 优秀
|
let pass = 0;
|
||||||
let good = 0; // 良好
|
let fail = 0;
|
||||||
let pass = 0; // 及格
|
|
||||||
let fail = 0; // 不及格
|
|
||||||
const total = studentList.value.length;
|
const total = studentList.value.length;
|
||||||
|
|
||||||
studentList.value.forEach((student: any) => {
|
studentList.value.forEach((student: any) => {
|
||||||
const score = student.score;
|
const score = student.scoreYear;
|
||||||
if (score !== null && score !== undefined) {
|
if (score !== null && score !== undefined) {
|
||||||
if (score >= 90) {
|
if (Number(score) >= 90) {
|
||||||
excellent++;
|
excellent++;
|
||||||
} else if (score >= 80) {
|
} else if (Number(score) >= 80) {
|
||||||
good++;
|
good++;
|
||||||
} else if (score >= 60) {
|
} else if (Number(score) >= 60) {
|
||||||
pass++;
|
pass++;
|
||||||
} else {
|
} else {
|
||||||
fail++;
|
fail++;
|
||||||
@@ -174,52 +271,19 @@ const statisticsData = computed(() => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 计算比率
|
|
||||||
const excellentRate = total > 0 ? ((excellent / total) * 100).toFixed(2) + '%' : '0%';
|
const excellentRate = total > 0 ? ((excellent / total) * 100).toFixed(2) + '%' : '0%';
|
||||||
const goodRate = total > 0 ? ((good / total) * 100).toFixed(2) + '%' : '0%';
|
const goodRate = total > 0 ? ((good / total) * 100).toFixed(2) + '%' : '0%';
|
||||||
const passRate = total > 0 ? ((pass / total) * 100).toFixed(2) + '%' : '0%';
|
const passRate = total > 0 ? ((pass / total) * 100).toFixed(2) + '%' : '0%';
|
||||||
const failRate = total > 0 ? ((fail / total) * 100).toFixed(2) + '%' : '0%';
|
const failRate = total > 0 ? ((fail / total) * 100).toFixed(2) + '%' : '0%';
|
||||||
|
const excellentGoodRate = total > 0 ? (((excellent + good) / total) * 100).toFixed(2) + '%' : '0%';
|
||||||
|
|
||||||
// 优良率 = (优秀 + 良好) / 总人数
|
|
||||||
const excellentGoodCount = excellent + good;
|
|
||||||
const excellentGoodRate = total > 0 ? ((excellentGoodCount / total) * 100).toFixed(2) + '%' : '0%';
|
|
||||||
|
|
||||||
// 获取班级名称
|
|
||||||
const classNo = studentList.value.length > 0 ? studentList.value[0].classNo || '-' : '-';
|
const classNo = studentList.value.length > 0 ? studentList.value[0].classNo || '-' : '-';
|
||||||
|
|
||||||
return [
|
return [
|
||||||
{
|
{ label: '人数', classNo, excellent, good, pass, fail },
|
||||||
label: '人数',
|
{ label: '比率', classNo, excellent: excellentRate, good: goodRate, pass: passRate, fail: failRate },
|
||||||
classNo: classNo,
|
{ label: '优良率', classNo, excellent: excellentGoodRate, good: '-', pass: '-', fail: '-' },
|
||||||
excellent: excellent,
|
{ label: '备注', classNo, excellent: '-', good: '-', pass: '-', fail: '-' },
|
||||||
good: good,
|
|
||||||
pass: pass,
|
|
||||||
fail: fail,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '比率',
|
|
||||||
classNo: classNo,
|
|
||||||
excellent: excellentRate,
|
|
||||||
good: goodRate,
|
|
||||||
pass: passRate,
|
|
||||||
fail: failRate,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '优良率',
|
|
||||||
classNo: classNo,
|
|
||||||
excellent: excellentGoodRate,
|
|
||||||
good: '-',
|
|
||||||
pass: '-',
|
|
||||||
fail: '-',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '备注',
|
|
||||||
classNo: classNo,
|
|
||||||
excellent: '-',
|
|
||||||
good: '-',
|
|
||||||
pass: '-',
|
|
||||||
fail: '-',
|
|
||||||
},
|
|
||||||
];
|
];
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -238,34 +302,18 @@ const getDataList = async () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (res.data && Array.isArray(res.data)) {
|
if (res.data && Array.isArray(res.data)) {
|
||||||
// 处理返回的数据,提取学生列表
|
|
||||||
// 根据API文档,返回的是StuConductYearVO数组
|
|
||||||
const tempList: any[] = [];
|
const tempList: any[] = [];
|
||||||
|
|
||||||
res.data.forEach((item: any) => {
|
res.data.forEach((item: any) => {
|
||||||
// 如果返回的数据结构中有basicStudentVOList,需要展开
|
if (item.basicStudentVOList && Array.isArray(item.basicStudentVOList)) {
|
||||||
if (item.basicStudentVOList && Array.isArray(item.basicStudentVOList) && item.basicStudentVOList.length > 0) {
|
|
||||||
item.basicStudentVOList.forEach((student: any) => {
|
item.basicStudentVOList.forEach((student: any) => {
|
||||||
tempList.push({
|
tempList.push({
|
||||||
stuNo: student.stuNo || item.stuNo,
|
...student,
|
||||||
realName: student.realName || item.realName,
|
|
||||||
score: item.score, // 学年总评分数
|
|
||||||
classNo: item.classNo,
|
classNo: item.classNo,
|
||||||
classCode: item.classCode,
|
classCode: item.classCode,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
// 直接使用item作为学生信息
|
|
||||||
tempList.push({
|
|
||||||
stuNo: item.stuNo,
|
|
||||||
realName: item.realName,
|
|
||||||
score: item.score,
|
|
||||||
classNo: item.classNo,
|
|
||||||
classCode: item.classCode,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
studentList.value = tempList;
|
studentList.value = tempList;
|
||||||
} else {
|
} else {
|
||||||
studentList.value = [];
|
studentList.value = [];
|
||||||
@@ -285,7 +333,7 @@ const handleReset = () => {
|
|||||||
studentList.value = [];
|
studentList.value = [];
|
||||||
};
|
};
|
||||||
|
|
||||||
// 查看详情(接口:GET /stuwork/stuconduct/queryDataByStuNo,通过学年学号查看详情)
|
// 查看详情
|
||||||
const handleView = async (row: any) => {
|
const handleView = async (row: any) => {
|
||||||
if (!queryForm.schoolYear || !row.stuNo) {
|
if (!queryForm.schoolYear || !row.stuNo) {
|
||||||
useMessage().warning('缺少学年或学号');
|
useMessage().warning('缺少学年或学号');
|
||||||
@@ -312,11 +360,7 @@ const handleView = async (row: any) => {
|
|||||||
const getSchoolYearList = async () => {
|
const getSchoolYearList = async () => {
|
||||||
try {
|
try {
|
||||||
const res = await queryAllSchoolYear();
|
const res = await queryAllSchoolYear();
|
||||||
if (res.data && Array.isArray(res.data)) {
|
schoolYearList.value = res.data && Array.isArray(res.data) ? res.data : [];
|
||||||
schoolYearList.value = res.data;
|
|
||||||
} else {
|
|
||||||
schoolYearList.value = [];
|
|
||||||
}
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
schoolYearList.value = [];
|
schoolYearList.value = [];
|
||||||
}
|
}
|
||||||
@@ -326,11 +370,7 @@ const getSchoolYearList = async () => {
|
|||||||
const getClassListData = async () => {
|
const getClassListData = async () => {
|
||||||
try {
|
try {
|
||||||
const res = await getClassListByRole();
|
const res = await getClassListByRole();
|
||||||
if (res.data && Array.isArray(res.data)) {
|
classList.value = res.data && Array.isArray(res.data) ? res.data : [];
|
||||||
classList.value = res.data;
|
|
||||||
} else {
|
|
||||||
classList.value = [];
|
|
||||||
}
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
classList.value = [];
|
classList.value = [];
|
||||||
}
|
}
|
||||||
@@ -344,15 +384,7 @@ onMounted(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.layout-padding {
|
@import '/@/assets/styles/modern-page.scss';
|
||||||
.layout-padding-auto {
|
|
||||||
.layout-padding-view {
|
|
||||||
.el-row {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.view-summary {
|
.view-summary {
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
@@ -362,19 +394,4 @@ onMounted(() => {
|
|||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: #303133;
|
color: #303133;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 确保页面可以滚动
|
|
||||||
.layout-padding {
|
|
||||||
height: 100%;
|
|
||||||
overflow-y: auto;
|
|
||||||
|
|
||||||
.layout-padding-auto {
|
|
||||||
height: 100%;
|
|
||||||
|
|
||||||
.layout-padding-view {
|
|
||||||
height: 100%;
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</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>
|
||||||
271
src/views/stuwork/stuturnoverrule/form.vue
Normal file
271
src/views/stuwork/stuturnoverrule/form.vue
Normal file
@@ -0,0 +1,271 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog
|
||||||
|
:title="form.id ? '编辑异动规则' : '新增异动规则'"
|
||||||
|
v-model="visible"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
draggable
|
||||||
|
width="600px">
|
||||||
|
<el-form
|
||||||
|
ref="dataFormRef"
|
||||||
|
:model="form"
|
||||||
|
:rules="dataRules"
|
||||||
|
label-width="120px"
|
||||||
|
v-loading="loading">
|
||||||
|
<el-row :gutter="24">
|
||||||
|
<el-col :span="24" class="mb20">
|
||||||
|
<el-form-item label="规则名称" prop="ruleName">
|
||||||
|
<el-input
|
||||||
|
v-model="form.ruleName"
|
||||||
|
placeholder="请输入规则名称"
|
||||||
|
clearable
|
||||||
|
maxlength="100" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
|
||||||
|
<el-col :span="24" class="mb20">
|
||||||
|
<el-form-item label="异动类型" prop="turnoverType">
|
||||||
|
<el-select
|
||||||
|
v-model="form.turnoverType"
|
||||||
|
placeholder="请选择异动类型"
|
||||||
|
clearable
|
||||||
|
style="width: 100%">
|
||||||
|
<el-option
|
||||||
|
v-for="item in turnoverTypeList"
|
||||||
|
:key="item.value"
|
||||||
|
:label="item.label"
|
||||||
|
:value="item.value">
|
||||||
|
</el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
|
||||||
|
<el-col :span="12" class="mb20">
|
||||||
|
<el-form-item label="最短间隔" prop="minIntervalDays">
|
||||||
|
<el-input-number
|
||||||
|
v-model="form.minIntervalDays"
|
||||||
|
:min="0"
|
||||||
|
:max="365"
|
||||||
|
placeholder="天"
|
||||||
|
style="width: 100%" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
|
||||||
|
<el-col :span="12" class="mb20">
|
||||||
|
<el-form-item label="最长持续" prop="maxDurationDays">
|
||||||
|
<el-input-number
|
||||||
|
v-model="form.maxDurationDays"
|
||||||
|
:min="0"
|
||||||
|
:max="3650"
|
||||||
|
placeholder="天"
|
||||||
|
style="width: 100%" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
|
||||||
|
<el-col :span="12" class="mb20">
|
||||||
|
<el-form-item label="提醒天数" prop="remindDays">
|
||||||
|
<el-input-number
|
||||||
|
v-model="form.remindDays"
|
||||||
|
:min="0"
|
||||||
|
:max="365"
|
||||||
|
placeholder="天"
|
||||||
|
style="width: 100%" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
|
||||||
|
<el-col :span="12" class="mb20">
|
||||||
|
<el-form-item label="排序" prop="sort">
|
||||||
|
<el-input-number
|
||||||
|
v-model="form.sort"
|
||||||
|
:min="0"
|
||||||
|
:max="999"
|
||||||
|
style="width: 100%" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
|
||||||
|
<el-col :span="12" class="mb20">
|
||||||
|
<el-form-item label="是否启用" prop="isActive">
|
||||||
|
<el-switch
|
||||||
|
v-model="form.isActive"
|
||||||
|
active-value="1"
|
||||||
|
inactive-value="0"
|
||||||
|
active-text="启用"
|
||||||
|
inactive-text="禁用" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
|
||||||
|
<el-col :span="24" class="mb20">
|
||||||
|
<el-form-item label="规则描述" prop="ruleDescription">
|
||||||
|
<el-input
|
||||||
|
v-model="form.ruleDescription"
|
||||||
|
type="textarea"
|
||||||
|
:rows="3"
|
||||||
|
placeholder="请输入规则描述"
|
||||||
|
maxlength="500"
|
||||||
|
show-word-limit />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
|
||||||
|
<el-col :span="24" class="mb20">
|
||||||
|
<el-form-item label="备注" prop="remarks">
|
||||||
|
<el-input
|
||||||
|
v-model="form.remarks"
|
||||||
|
type="textarea"
|
||||||
|
:rows="2"
|
||||||
|
placeholder="请输入备注"
|
||||||
|
maxlength="200" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</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="StuTurnoverRuleFormDialog">
|
||||||
|
import { ref, reactive, nextTick, onMounted } from 'vue'
|
||||||
|
import { useMessage } from '/@/hooks/message'
|
||||||
|
import { addObj, editObj, getDetail } from '/@/api/stuwork/stuturnoverrule'
|
||||||
|
import { getDicts } from '/@/api/admin/dict'
|
||||||
|
|
||||||
|
const emit = defineEmits(['refresh'])
|
||||||
|
|
||||||
|
// 定义变量内容
|
||||||
|
const dataFormRef = ref()
|
||||||
|
const visible = ref(false)
|
||||||
|
const loading = ref(false)
|
||||||
|
const turnoverTypeList = ref<any[]>([])
|
||||||
|
|
||||||
|
// 提交表单数据
|
||||||
|
const form = reactive({
|
||||||
|
id: '',
|
||||||
|
ruleName: '',
|
||||||
|
turnoverType: '',
|
||||||
|
minIntervalDays: 0,
|
||||||
|
maxDurationDays: 0,
|
||||||
|
remindDays: 0,
|
||||||
|
ruleDescription: '',
|
||||||
|
isActive: '1',
|
||||||
|
sort: 0,
|
||||||
|
remarks: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
// 定义校验规则
|
||||||
|
const dataRules = reactive({
|
||||||
|
ruleName: [
|
||||||
|
{ required: true, message: '请输入规则名称', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
turnoverType: [
|
||||||
|
{ required: true, message: '请选择异动类型', trigger: 'change' }
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
// 打开弹窗
|
||||||
|
const openDialog = async (row?: any) => {
|
||||||
|
visible.value = true
|
||||||
|
|
||||||
|
// 重置表单数据
|
||||||
|
nextTick(() => {
|
||||||
|
dataFormRef.value?.resetFields()
|
||||||
|
form.id = ''
|
||||||
|
form.ruleName = ''
|
||||||
|
form.turnoverType = ''
|
||||||
|
form.minIntervalDays = 0
|
||||||
|
form.maxDurationDays = 0
|
||||||
|
form.remindDays = 0
|
||||||
|
form.ruleDescription = ''
|
||||||
|
form.isActive = '1'
|
||||||
|
form.sort = 0
|
||||||
|
form.remarks = ''
|
||||||
|
|
||||||
|
// 编辑时填充数据
|
||||||
|
if (row?.id) {
|
||||||
|
loading.value = true
|
||||||
|
getDetail(row.id).then((res: any) => {
|
||||||
|
if (res.data) {
|
||||||
|
form.id = res.data.id
|
||||||
|
form.ruleName = res.data.ruleName || ''
|
||||||
|
form.turnoverType = res.data.turnoverType || ''
|
||||||
|
form.minIntervalDays = res.data.minIntervalDays || 0
|
||||||
|
form.maxDurationDays = res.data.maxDurationDays || 0
|
||||||
|
form.remindDays = res.data.remindDays || 0
|
||||||
|
form.ruleDescription = res.data.ruleDescription || ''
|
||||||
|
form.isActive = res.data.isActive || '1'
|
||||||
|
form.sort = res.data.sort || 0
|
||||||
|
form.remarks = res.data.remarks || ''
|
||||||
|
}
|
||||||
|
}).finally(() => {
|
||||||
|
loading.value = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提交表单
|
||||||
|
const onSubmit = async () => {
|
||||||
|
if (!dataFormRef.value) return
|
||||||
|
|
||||||
|
await dataFormRef.value.validate(async (valid: boolean) => {
|
||||||
|
if (!valid) return
|
||||||
|
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
const submitData = {
|
||||||
|
id: form.id || undefined,
|
||||||
|
ruleName: form.ruleName,
|
||||||
|
turnoverType: form.turnoverType,
|
||||||
|
minIntervalDays: form.minIntervalDays,
|
||||||
|
maxDurationDays: form.maxDurationDays,
|
||||||
|
remindDays: form.remindDays,
|
||||||
|
ruleDescription: form.ruleDescription,
|
||||||
|
isActive: form.isActive,
|
||||||
|
sort: form.sort,
|
||||||
|
remarks: form.remarks
|
||||||
|
}
|
||||||
|
|
||||||
|
if (form.id) {
|
||||||
|
await editObj(submitData)
|
||||||
|
useMessage().success('编辑成功')
|
||||||
|
} else {
|
||||||
|
await addObj(submitData)
|
||||||
|
useMessage().success('新增成功')
|
||||||
|
}
|
||||||
|
visible.value = false
|
||||||
|
emit('refresh')
|
||||||
|
} catch (err: any) {
|
||||||
|
useMessage().error(err.msg || (form.id ? '编辑失败' : '新增失败'))
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取异动类型字典
|
||||||
|
const getTurnoverTypeDict = async () => {
|
||||||
|
try {
|
||||||
|
const res = await getDicts('turnover_type')
|
||||||
|
if (res.data) {
|
||||||
|
turnoverTypeList.value = Array.isArray(res.data) ? res.data.map((item: any) => ({
|
||||||
|
label: item.label || item.dictLabel || item.name,
|
||||||
|
value: item.value || item.dictValue || item.code
|
||||||
|
})) : []
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
turnoverTypeList.value = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化
|
||||||
|
onMounted(() => {
|
||||||
|
getTurnoverTypeDict()
|
||||||
|
})
|
||||||
|
|
||||||
|
// 暴露方法给父组件
|
||||||
|
defineExpose({
|
||||||
|
openDialog
|
||||||
|
})
|
||||||
|
</script>
|
||||||
337
src/views/stuwork/stuturnoverrule/index.vue
Normal file
337
src/views/stuwork/stuturnoverrule/index.vue
Normal file
@@ -0,0 +1,337 @@
|
|||||||
|
<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="ruleName">
|
||||||
|
<el-input
|
||||||
|
v-model="searchForm.ruleName"
|
||||||
|
placeholder="请输入规则名称"
|
||||||
|
clearable
|
||||||
|
style="width: 200px" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="异动类型" prop="turnoverType">
|
||||||
|
<el-select
|
||||||
|
v-model="searchForm.turnoverType"
|
||||||
|
placeholder="请选择异动类型"
|
||||||
|
clearable
|
||||||
|
style="width: 200px">
|
||||||
|
<el-option
|
||||||
|
v-for="item in turnoverTypeList"
|
||||||
|
:key="item.value"
|
||||||
|
:label="item.label"
|
||||||
|
:value="item.value">
|
||||||
|
</el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="是否启用" prop="isActive">
|
||||||
|
<el-select
|
||||||
|
v-model="searchForm.isActive"
|
||||||
|
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"
|
||||||
|
v-auth="'stuwork_stuturnoverrule_add'"
|
||||||
|
@click="formDialogRef.openDialog()">
|
||||||
|
新增规则
|
||||||
|
</el-button>
|
||||||
|
<right-toolbar
|
||||||
|
v-model:showSearch="showSearch"
|
||||||
|
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-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 #header>
|
||||||
|
<el-icon><List /></el-icon>
|
||||||
|
</template>
|
||||||
|
<template #default="{ $index }">
|
||||||
|
{{ $index + 1 + ((state.pagination?.current || 1) - 1) * (state.pagination?.size || 10) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<template v-for="col in visibleColumnsSorted" :key="col.prop || col.label">
|
||||||
|
<el-table-column
|
||||||
|
v-if="checkColumnVisible(col.prop || '') && col.prop !== '操作'"
|
||||||
|
:prop="col.prop"
|
||||||
|
:label="col.label"
|
||||||
|
show-overflow-tooltip
|
||||||
|
align="center"
|
||||||
|
:min-width="col.minWidth"
|
||||||
|
:width="col.width">
|
||||||
|
<template #header>
|
||||||
|
<el-icon><component :is="columnConfigMap[col.prop]?.icon || Document" /></el-icon>
|
||||||
|
<span style="margin-left: 4px">{{ col.label }}</span>
|
||||||
|
</template>
|
||||||
|
<!-- 异动类型列特殊模板 -->
|
||||||
|
<template v-if="col.prop === 'turnoverType'" #default="scope">
|
||||||
|
<el-tag size="small" type="warning" effect="plain">
|
||||||
|
{{ formatTurnoverType(scope.row.turnoverType) }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
<!-- 是否启用列特殊模板 -->
|
||||||
|
<template v-else-if="col.prop === 'isActive'" #default="scope">
|
||||||
|
<el-tag size="small" :type="scope.row.isActive === '1' ? 'success' : 'danger'" effect="plain">
|
||||||
|
{{ scope.row.isActive === '1' ? '启用' : '禁用' }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
<!-- 天数相关列 -->
|
||||||
|
<template v-else-if="col.prop === 'minIntervalDays'" #default="scope">
|
||||||
|
<span>{{ scope.row.minIntervalDays ?? '-' }} 天</span>
|
||||||
|
</template>
|
||||||
|
<template v-else-if="col.prop === 'maxDurationDays'" #default="scope">
|
||||||
|
<span>{{ scope.row.maxDurationDays ?? '-' }} 天</span>
|
||||||
|
</template>
|
||||||
|
<template v-else-if="col.prop === 'remindDays'" #default="scope">
|
||||||
|
<span>{{ scope.row.remindDays ?? '-' }} 天</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</template>
|
||||||
|
<el-table-column label="操作" width="160" align="center" fixed="right">
|
||||||
|
<template #header>
|
||||||
|
<el-icon><Setting /></el-icon>
|
||||||
|
<span style="margin-left: 4px">操作</span>
|
||||||
|
</template>
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button
|
||||||
|
icon="Edit"
|
||||||
|
link
|
||||||
|
type="primary"
|
||||||
|
v-auth="'stuwork_stuturnoverrule_edit'"
|
||||||
|
@click="handleEdit(scope.row)">
|
||||||
|
编辑
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
icon="Delete"
|
||||||
|
link
|
||||||
|
type="danger"
|
||||||
|
v-auth="'stuwork_stuturnoverrule_del'"
|
||||||
|
@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>
|
||||||
|
|
||||||
|
<!-- 新增/编辑表单弹窗 -->
|
||||||
|
<form-dialog ref="formDialogRef" @refresh="getDataList" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts" name="StuTurnoverRule">
|
||||||
|
import { reactive, ref, onMounted, nextTick } from 'vue'
|
||||||
|
import { useRoute } from 'vue-router'
|
||||||
|
import { BasicTableProps, useTable } from "/@/hooks/table";
|
||||||
|
import { fetchList, delObj } from "/@/api/stuwork/stuturnoverrule";
|
||||||
|
import { getDicts } from "/@/api/admin/dict";
|
||||||
|
import { useMessage, useMessageBox } from "/@/hooks/message";
|
||||||
|
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
||||||
|
import FormDialog from './form.vue'
|
||||||
|
import { List, Setting, Document, Collection, Menu, Search, Clock } from '@element-plus/icons-vue'
|
||||||
|
import { useTableColumnControl } from '/@/hooks/tableColumn'
|
||||||
|
|
||||||
|
// 定义变量内容
|
||||||
|
const route = useRoute()
|
||||||
|
const searchFormRef = ref()
|
||||||
|
const columnControlRef = ref<any>()
|
||||||
|
const showSearch = ref(true)
|
||||||
|
const turnoverTypeList = ref<any[]>([])
|
||||||
|
const formDialogRef = ref()
|
||||||
|
|
||||||
|
// 表格列配置
|
||||||
|
const tableColumns = [
|
||||||
|
{ prop: 'ruleName', label: '规则名称', minWidth: 150 },
|
||||||
|
{ prop: 'turnoverType', label: '异动类型', width: 120 },
|
||||||
|
{ prop: 'minIntervalDays', label: '最短间隔', width: 100 },
|
||||||
|
{ prop: 'maxDurationDays', label: '最长持续', width: 100 },
|
||||||
|
{ prop: 'remindDays', label: '提醒天数', width: 100 },
|
||||||
|
{ prop: 'sort', label: '排序', width: 80 },
|
||||||
|
{ prop: 'isActive', label: '状态', width: 80 },
|
||||||
|
{ prop: 'ruleDescription', label: '规则描述', minWidth: 200 },
|
||||||
|
{ prop: 'remarks', label: '备注', minWidth: 150 }
|
||||||
|
]
|
||||||
|
|
||||||
|
// 列配置映射(用于图标显示)
|
||||||
|
const columnConfigMap: Record<string, { icon: any }> = {
|
||||||
|
ruleName: { icon: Document },
|
||||||
|
turnoverType: { icon: Collection },
|
||||||
|
minIntervalDays: { icon: Clock },
|
||||||
|
maxDurationDays: { icon: Clock },
|
||||||
|
remindDays: { icon: Clock },
|
||||||
|
sort: { icon: Setting },
|
||||||
|
isActive: { icon: Setting },
|
||||||
|
ruleDescription: { icon: Document },
|
||||||
|
remarks: { icon: Document }
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用表格列控制hook
|
||||||
|
const {
|
||||||
|
visibleColumns,
|
||||||
|
visibleColumnsSorted,
|
||||||
|
checkColumnVisible,
|
||||||
|
handleColumnChange,
|
||||||
|
handleColumnOrderChange
|
||||||
|
} = useTableColumnControl(tableColumns, route.path)
|
||||||
|
|
||||||
|
// 搜索表单
|
||||||
|
const searchForm = reactive({
|
||||||
|
ruleName: '',
|
||||||
|
turnoverType: '',
|
||||||
|
isActive: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
// 配置 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 formatTurnoverType = (value: string) => {
|
||||||
|
if (!value) return '-'
|
||||||
|
const item = turnoverTypeList.value.find((item: any) => item.value === value)
|
||||||
|
return item ? item.label : value
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询
|
||||||
|
const handleSearch = () => {
|
||||||
|
getDataList()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重置
|
||||||
|
const handleReset = () => {
|
||||||
|
searchFormRef.value?.resetFields()
|
||||||
|
searchForm.ruleName = ''
|
||||||
|
searchForm.turnoverType = ''
|
||||||
|
searchForm.isActive = ''
|
||||||
|
getDataList()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 编辑
|
||||||
|
const handleEdit = (row: any) => {
|
||||||
|
formDialogRef.value?.openDialog(row)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除
|
||||||
|
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 || '删除失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取异动类型字典
|
||||||
|
const getTurnoverTypeDict = async () => {
|
||||||
|
try {
|
||||||
|
const res = await getDicts('turnover_type')
|
||||||
|
if (res.data) {
|
||||||
|
turnoverTypeList.value = Array.isArray(res.data) ? res.data.map((item: any) => ({
|
||||||
|
label: item.label || item.dictLabel || item.name,
|
||||||
|
value: item.value || item.dictValue || item.code
|
||||||
|
})) : []
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
turnoverTypeList.value = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化
|
||||||
|
onMounted(() => {
|
||||||
|
getTurnoverTypeDict()
|
||||||
|
nextTick(() => {
|
||||||
|
if (visibleColumns.value.length === 0) {
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
@import '/@/assets/styles/modern-page.scss';
|
||||||
|
</style>
|
||||||
@@ -20,10 +20,10 @@
|
|||||||
filterable
|
filterable
|
||||||
style="width: 200px">
|
style="width: 200px">
|
||||||
<el-option
|
<el-option
|
||||||
v-for="item in schoolYearList"
|
v-for="(item, index) in schoolYearList"
|
||||||
:key="item.year"
|
:key="item.year || item.id || index"
|
||||||
:label="item.year"
|
:label="item.year || item.schoolYear || item.name || item"
|
||||||
:value="item.year">
|
:value="item.year || item.schoolYear || item.name || item">
|
||||||
</el-option>
|
</el-option>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
@@ -303,23 +303,38 @@ const searchForm = reactive({
|
|||||||
dateRange: null as [string, string] | null
|
dateRange: null as [string, string] | null
|
||||||
})
|
})
|
||||||
|
|
||||||
// 配置 useTable(接口返回 data.tableData.records / data.tableData.total,需包装以适配 hook 的 res.data[props.item] 取数)
|
// 配置 useTable(接口返回 data.tableData.records / data.tableData.total)
|
||||||
const state: BasicTableProps = reactive<BasicTableProps>({
|
const state: BasicTableProps = reactive<BasicTableProps>({
|
||||||
queryForm: searchForm,
|
queryForm: searchForm,
|
||||||
pageList: async (params: any) => {
|
pageList: async (params: any) => {
|
||||||
const res = await fetchList(params)
|
// 处理查询参数
|
||||||
const data = res?.data
|
const queryParams: any = { ...params }
|
||||||
const tableData = data?.tableData
|
// 处理日期范围
|
||||||
|
if (searchForm.dateRange && searchForm.dateRange.length === 2) {
|
||||||
|
queryParams.dateRangeStr = `${searchForm.dateRange[0]},${searchForm.dateRange[1]}`
|
||||||
|
}
|
||||||
|
delete queryParams.dateRange
|
||||||
|
// 处理学年数组
|
||||||
|
if (queryParams.schoolYear) {
|
||||||
|
queryParams.schoolYear = [queryParams.schoolYear]
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await fetchList(queryParams)
|
||||||
|
// 将嵌套的 tableData 提升到 data 层级,适配 useTable hook
|
||||||
|
if (res.data && res.data.tableData) {
|
||||||
return {
|
return {
|
||||||
|
...res,
|
||||||
data: {
|
data: {
|
||||||
'tableData.records': tableData?.records ?? [],
|
records: res.data.tableData.records || [],
|
||||||
'tableData.total': tableData?.total ?? data?.total ?? 0
|
total: res.data.tableData.total || 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
return res
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
item: 'tableData.records',
|
item: 'records',
|
||||||
totalCount: 'tableData.total'
|
totalCount: 'total'
|
||||||
},
|
},
|
||||||
createdIsNeed: true
|
createdIsNeed: true
|
||||||
})
|
})
|
||||||
@@ -334,20 +349,6 @@ const {
|
|||||||
|
|
||||||
// 查询
|
// 查询
|
||||||
const handleSearch = () => {
|
const handleSearch = () => {
|
||||||
// 处理日期范围
|
|
||||||
const params: any = { ...searchForm }
|
|
||||||
if (searchForm.dateRange && searchForm.dateRange.length === 2) {
|
|
||||||
params.dateRangeStr = `${searchForm.dateRange[0]},${searchForm.dateRange[1]}`
|
|
||||||
}
|
|
||||||
delete params.dateRange
|
|
||||||
|
|
||||||
// 处理学年数组
|
|
||||||
if (params.schoolYear) {
|
|
||||||
params.schoolYear = [params.schoolYear]
|
|
||||||
}
|
|
||||||
|
|
||||||
// 更新查询参数
|
|
||||||
Object.assign(searchForm, params)
|
|
||||||
getDataList()
|
getDataList()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -394,10 +395,20 @@ const getDeptListData = async () => {
|
|||||||
const getSchoolYearList = async () => {
|
const getSchoolYearList = async () => {
|
||||||
try {
|
try {
|
||||||
const res = await queryAllSchoolYear()
|
const res = await queryAllSchoolYear()
|
||||||
if (res.data && Array.isArray(res.data)) {
|
if (res.data) {
|
||||||
|
// 兼容多种数据格式
|
||||||
|
if (Array.isArray(res.data)) {
|
||||||
schoolYearList.value = res.data
|
schoolYearList.value = res.data
|
||||||
|
} else if (res.data.records && Array.isArray(res.data.records)) {
|
||||||
|
schoolYearList.value = res.data.records
|
||||||
|
} else {
|
||||||
|
schoolYearList.value = []
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
schoolYearList.value = []
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
console.error('获取学年列表失败', err)
|
||||||
schoolYearList.value = []
|
schoolYearList.value = []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,10 +19,10 @@
|
|||||||
filterable
|
filterable
|
||||||
style="width: 100%">
|
style="width: 100%">
|
||||||
<el-option
|
<el-option
|
||||||
v-for="item in schoolYearList"
|
v-for="(item, index) in schoolYearList"
|
||||||
:key="item.year"
|
:key="item.year || item.id || index"
|
||||||
:label="item.year"
|
:label="item.year || item.schoolYear || item.name || item"
|
||||||
:value="item.year">
|
:value="item.year || item.schoolYear || item.name || item">
|
||||||
</el-option>
|
</el-option>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
@@ -34,9 +34,9 @@
|
|||||||
style="width: 100%">
|
style="width: 100%">
|
||||||
<el-option
|
<el-option
|
||||||
v-for="item in schoolTermList"
|
v-for="item in schoolTermList"
|
||||||
:key="item.value"
|
:key="item.value || item.dictValue"
|
||||||
:label="item.label"
|
:label="item.label || item.dictLabel || item.name"
|
||||||
:value="item.value">
|
:value="item.value || item.dictValue">
|
||||||
</el-option>
|
</el-option>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
@@ -50,16 +50,18 @@
|
|||||||
<el-form-item label="教师" prop="teacherNo">
|
<el-form-item label="教师" prop="teacherNo">
|
||||||
<el-select
|
<el-select
|
||||||
v-model="form.teacherNo"
|
v-model="form.teacherNo"
|
||||||
placeholder="请选择教师"
|
placeholder="请输入姓名或工号搜索"
|
||||||
clearable
|
clearable
|
||||||
filterable
|
filterable
|
||||||
style="width: 100%"
|
remote
|
||||||
@search="handleTeacherSearch">
|
:remote-method="handleTeacherSearch"
|
||||||
|
:loading="teacherLoading"
|
||||||
|
style="width: 100%">
|
||||||
<el-option
|
<el-option
|
||||||
v-for="item in teacherList"
|
v-for="item in teacherList"
|
||||||
:key="item.teacherNo"
|
:key="item.teacherNo || item.id"
|
||||||
:label="`${item.realName}(${item.teacherNo})`"
|
:label="`${item.realName || item.name}(${item.teacherNo || item.id})`"
|
||||||
:value="item.teacherNo">
|
:value="item.teacherNo || item.id">
|
||||||
</el-option>
|
</el-option>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
@@ -87,6 +89,7 @@ const emit = defineEmits(['refresh'])
|
|||||||
const dataFormRef = ref()
|
const dataFormRef = ref()
|
||||||
const visible = ref(false)
|
const visible = ref(false)
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
|
const teacherLoading = ref(false)
|
||||||
const schoolYearList = ref<any[]>([])
|
const schoolYearList = ref<any[]>([])
|
||||||
const schoolTermList = ref<any[]>([])
|
const schoolTermList = ref<any[]>([])
|
||||||
const teacherList = ref<any[]>([])
|
const teacherList = ref<any[]>([])
|
||||||
@@ -117,17 +120,29 @@ const dataRules = {
|
|||||||
|
|
||||||
// 教师搜索
|
// 教师搜索
|
||||||
const handleTeacherSearch = async (keyword: string) => {
|
const handleTeacherSearch = async (keyword: string) => {
|
||||||
if (!keyword) {
|
if (!keyword || keyword.length < 1) {
|
||||||
teacherList.value = []
|
teacherList.value = []
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
teacherLoading.value = true
|
||||||
try {
|
try {
|
||||||
const res = await getTeacherInfoCommon({ searchKeywords: keyword })
|
const res = await getTeacherInfoCommon({ searchKeywords: keyword })
|
||||||
if (res.data && Array.isArray(res.data)) {
|
if (res.data) {
|
||||||
|
if (Array.isArray(res.data)) {
|
||||||
teacherList.value = res.data
|
teacherList.value = res.data
|
||||||
|
} else if (res.data.records && Array.isArray(res.data.records)) {
|
||||||
|
teacherList.value = res.data.records
|
||||||
|
} else {
|
||||||
|
teacherList.value = []
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
teacherList.value = []
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
console.error('获取教师列表失败', err)
|
||||||
teacherList.value = []
|
teacherList.value = []
|
||||||
|
} finally {
|
||||||
|
teacherLoading.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -174,10 +189,19 @@ const onSubmit = async () => {
|
|||||||
const getSchoolYearList = async () => {
|
const getSchoolYearList = async () => {
|
||||||
try {
|
try {
|
||||||
const res = await queryAllSchoolYear()
|
const res = await queryAllSchoolYear()
|
||||||
if (res.data && Array.isArray(res.data)) {
|
if (res.data) {
|
||||||
|
if (Array.isArray(res.data)) {
|
||||||
schoolYearList.value = res.data
|
schoolYearList.value = res.data
|
||||||
|
} else if (res.data.records && Array.isArray(res.data.records)) {
|
||||||
|
schoolYearList.value = res.data.records
|
||||||
|
} else {
|
||||||
|
schoolYearList.value = []
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
schoolYearList.value = []
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
console.error('获取学年列表失败', err)
|
||||||
schoolYearList.value = []
|
schoolYearList.value = []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -187,9 +211,15 @@ const getSchoolTermList = async () => {
|
|||||||
try {
|
try {
|
||||||
const res = await getDicts('school_term')
|
const res = await getDicts('school_term')
|
||||||
if (res.data && Array.isArray(res.data)) {
|
if (res.data && Array.isArray(res.data)) {
|
||||||
schoolTermList.value = res.data
|
schoolTermList.value = res.data.map((item: any) => ({
|
||||||
|
label: item.label ?? item.dictLabel ?? item.name,
|
||||||
|
value: item.value ?? item.dictValue
|
||||||
|
}))
|
||||||
|
} else {
|
||||||
|
schoolTermList.value = []
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
console.error('获取学期列表失败', err)
|
||||||
schoolTermList.value = []
|
schoolTermList.value = []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
262
src/views/stuwork/watermonthreport/form.vue
Normal file
262
src/views/stuwork/watermonthreport/form.vue
Normal file
@@ -0,0 +1,262 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog
|
||||||
|
:title="form.id ? '编辑水电月明细' : '新增水电月明细'"
|
||||||
|
v-model="visible"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
draggable
|
||||||
|
width="550px">
|
||||||
|
<el-form
|
||||||
|
ref="dataFormRef"
|
||||||
|
:model="form"
|
||||||
|
:rules="dataRules"
|
||||||
|
label-width="100px"
|
||||||
|
v-loading="loading">
|
||||||
|
<el-row :gutter="24">
|
||||||
|
<el-col :span="24" class="mb20">
|
||||||
|
<el-form-item label="房间号" prop="roomNo">
|
||||||
|
<el-input
|
||||||
|
v-model="form.roomNo"
|
||||||
|
placeholder="请输入房间号"
|
||||||
|
clearable
|
||||||
|
maxlength="50" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
|
||||||
|
<el-col :span="12" class="mb20">
|
||||||
|
<el-form-item label="年份" prop="year">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="form.year"
|
||||||
|
type="year"
|
||||||
|
placeholder="选择年份"
|
||||||
|
value-format="YYYY"
|
||||||
|
style="width: 100%" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
|
||||||
|
<el-col :span="12" class="mb20">
|
||||||
|
<el-form-item label="月份" prop="month">
|
||||||
|
<el-select
|
||||||
|
v-model="form.month"
|
||||||
|
placeholder="请选择月份"
|
||||||
|
style="width: 100%">
|
||||||
|
<el-option v-for="m in 12" :key="m" :label="m + '月'" :value="m" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
|
||||||
|
<el-col :span="12" class="mb20">
|
||||||
|
<el-form-item label="类型" prop="flag">
|
||||||
|
<el-select
|
||||||
|
v-model="form.flag"
|
||||||
|
placeholder="请选择类型"
|
||||||
|
style="width: 100%"
|
||||||
|
@change="handleFlagChange">
|
||||||
|
<el-option label="用电" :value="2" />
|
||||||
|
<el-option label="用水" :value="4" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
|
||||||
|
<el-col :span="12" class="mb20">
|
||||||
|
<el-form-item label="水表类型" prop="meterNum">
|
||||||
|
<el-select
|
||||||
|
v-model="form.meterNum"
|
||||||
|
placeholder="请选择水表类型"
|
||||||
|
style="width: 100%"
|
||||||
|
:disabled="form.flag !== 4">
|
||||||
|
<el-option label="冷水" :value="10" />
|
||||||
|
<el-option label="热水" :value="11" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
|
||||||
|
<el-col :span="12" class="mb20">
|
||||||
|
<el-form-item label="用量" prop="subiMonthSum">
|
||||||
|
<el-input-number
|
||||||
|
v-model="form.subiMonthSum"
|
||||||
|
:min="0"
|
||||||
|
:precision="2"
|
||||||
|
:controls="false"
|
||||||
|
placeholder="请输入用量"
|
||||||
|
style="width: 100%" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
|
||||||
|
<el-col :span="12" class="mb20">
|
||||||
|
<el-form-item label="费用" prop="subWatFlagSum">
|
||||||
|
<el-input-number
|
||||||
|
v-model="form.subWatFlagSum"
|
||||||
|
:min="0"
|
||||||
|
:precision="2"
|
||||||
|
:controls="false"
|
||||||
|
placeholder="请输入费用"
|
||||||
|
style="width: 100%" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
|
||||||
|
<el-col :span="24" class="mb20">
|
||||||
|
<el-form-item label="日期" prop="waterDate">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="form.waterDate"
|
||||||
|
type="date"
|
||||||
|
placeholder="选择日期"
|
||||||
|
value-format="YYYY-MM-DD"
|
||||||
|
style="width: 100%" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
|
||||||
|
<el-col :span="24" class="mb20">
|
||||||
|
<el-form-item label="备注" prop="remarks">
|
||||||
|
<el-input
|
||||||
|
v-model="form.remarks"
|
||||||
|
type="textarea"
|
||||||
|
:rows="3"
|
||||||
|
placeholder="请输入备注"
|
||||||
|
maxlength="200" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</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="WaterMonthReportFormDialog">
|
||||||
|
import { ref, reactive, nextTick } from 'vue'
|
||||||
|
import { useMessage } from '/@/hooks/message'
|
||||||
|
import { addObj, editObj, getDetail } from '/@/api/stuwork/watermonthreport'
|
||||||
|
|
||||||
|
const emit = defineEmits(['refresh'])
|
||||||
|
|
||||||
|
// 定义变量内容
|
||||||
|
const dataFormRef = ref()
|
||||||
|
const visible = ref(false)
|
||||||
|
const loading = ref(false)
|
||||||
|
|
||||||
|
// 提交表单数据
|
||||||
|
const form = reactive({
|
||||||
|
id: '',
|
||||||
|
roomNo: '',
|
||||||
|
year: '',
|
||||||
|
month: '',
|
||||||
|
flag: undefined as number | undefined,
|
||||||
|
meterNum: undefined as number | undefined,
|
||||||
|
subiMonthSum: undefined as number | undefined,
|
||||||
|
subWatFlagSum: undefined as number | undefined,
|
||||||
|
waterDate: '',
|
||||||
|
remarks: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
// 定义校验规则
|
||||||
|
const dataRules = reactive({
|
||||||
|
roomNo: [
|
||||||
|
{ required: true, message: '请输入房间号', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
year: [
|
||||||
|
{ required: true, message: '请选择年份', trigger: 'change' }
|
||||||
|
],
|
||||||
|
month: [
|
||||||
|
{ required: true, message: '请选择月份', trigger: 'change' }
|
||||||
|
],
|
||||||
|
flag: [
|
||||||
|
{ required: true, message: '请选择类型', trigger: 'change' }
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
// 类型变化处理
|
||||||
|
const handleFlagChange = () => {
|
||||||
|
if (form.flag !== 4) {
|
||||||
|
form.meterNum = undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 打开弹窗
|
||||||
|
const openDialog = async (row?: any) => {
|
||||||
|
visible.value = true
|
||||||
|
|
||||||
|
// 重置表单数据
|
||||||
|
nextTick(() => {
|
||||||
|
dataFormRef.value?.resetFields()
|
||||||
|
form.id = ''
|
||||||
|
form.roomNo = ''
|
||||||
|
form.year = ''
|
||||||
|
form.month = ''
|
||||||
|
form.flag = undefined
|
||||||
|
form.meterNum = undefined
|
||||||
|
form.subiMonthSum = undefined
|
||||||
|
form.subWatFlagSum = undefined
|
||||||
|
form.waterDate = ''
|
||||||
|
form.remarks = ''
|
||||||
|
|
||||||
|
// 编辑时填充数据
|
||||||
|
if (row?.id) {
|
||||||
|
loading.value = true
|
||||||
|
getDetail(row.id).then((res: any) => {
|
||||||
|
if (res.data) {
|
||||||
|
form.id = res.data.id
|
||||||
|
form.roomNo = res.data.roomNo || ''
|
||||||
|
form.year = res.data.year ? String(res.data.year) : ''
|
||||||
|
form.month = res.data.month
|
||||||
|
form.flag = res.data.flag
|
||||||
|
form.meterNum = res.data.meterNum
|
||||||
|
form.subiMonthSum = res.data.subiMonthSum
|
||||||
|
form.subWatFlagSum = res.data.subWatFlagSum
|
||||||
|
form.waterDate = res.data.waterDate || ''
|
||||||
|
form.remarks = res.data.remarks || ''
|
||||||
|
}
|
||||||
|
}).finally(() => {
|
||||||
|
loading.value = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提交表单
|
||||||
|
const onSubmit = async () => {
|
||||||
|
if (!dataFormRef.value) return
|
||||||
|
|
||||||
|
await dataFormRef.value.validate(async (valid: boolean) => {
|
||||||
|
if (!valid) return
|
||||||
|
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
const submitData = {
|
||||||
|
id: form.id || undefined,
|
||||||
|
roomNo: form.roomNo,
|
||||||
|
year: form.year ? parseInt(form.year) : undefined,
|
||||||
|
month: form.month,
|
||||||
|
flag: form.flag,
|
||||||
|
meterNum: form.meterNum,
|
||||||
|
subiMonthSum: form.subiMonthSum,
|
||||||
|
subWatFlagSum: form.subWatFlagSum,
|
||||||
|
waterDate: form.waterDate,
|
||||||
|
remarks: form.remarks
|
||||||
|
}
|
||||||
|
|
||||||
|
if (form.id) {
|
||||||
|
await editObj(submitData)
|
||||||
|
useMessage().success('编辑成功')
|
||||||
|
} else {
|
||||||
|
await addObj(submitData)
|
||||||
|
useMessage().success('新增成功')
|
||||||
|
}
|
||||||
|
visible.value = false
|
||||||
|
emit('refresh')
|
||||||
|
} catch (err: any) {
|
||||||
|
useMessage().error(err.msg || (form.id ? '编辑失败' : '新增失败'))
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 暴露方法给父组件
|
||||||
|
defineExpose({
|
||||||
|
openDialog
|
||||||
|
})
|
||||||
|
</script>
|
||||||
328
src/views/stuwork/watermonthreport/index.vue
Normal file
328
src/views/stuwork/watermonthreport/index.vue
Normal file
@@ -0,0 +1,328 @@
|
|||||||
|
<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="roomNo">
|
||||||
|
<el-input
|
||||||
|
v-model="searchForm.roomNo"
|
||||||
|
placeholder="请输入房间号"
|
||||||
|
clearable
|
||||||
|
style="width: 200px" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="年份" prop="year">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="searchForm.year"
|
||||||
|
type="year"
|
||||||
|
placeholder="选择年份"
|
||||||
|
value-format="YYYY"
|
||||||
|
style="width: 200px" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="月份" prop="month">
|
||||||
|
<el-select
|
||||||
|
v-model="searchForm.month"
|
||||||
|
placeholder="请选择月份"
|
||||||
|
clearable
|
||||||
|
style="width: 200px">
|
||||||
|
<el-option v-for="m in 12" :key="m" :label="m + '月'" :value="m" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="类型" prop="flag">
|
||||||
|
<el-select
|
||||||
|
v-model="searchForm.flag"
|
||||||
|
placeholder="请选择类型"
|
||||||
|
clearable
|
||||||
|
style="width: 200px">
|
||||||
|
<el-option label="用电" :value="2" />
|
||||||
|
<el-option label="用水" :value="4" />
|
||||||
|
</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"
|
||||||
|
v-auth="'stuwork_watermonthreport_add'"
|
||||||
|
@click="formDialogRef.openDialog()">
|
||||||
|
新增
|
||||||
|
</el-button>
|
||||||
|
<right-toolbar
|
||||||
|
v-model:showSearch="showSearch"
|
||||||
|
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-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 #header>
|
||||||
|
<el-icon><List /></el-icon>
|
||||||
|
</template>
|
||||||
|
<template #default="{ $index }">
|
||||||
|
{{ $index + 1 + ((state.pagination?.current || 1) - 1) * (state.pagination?.size || 10) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<template v-for="col in visibleColumnsSorted" :key="col.prop || col.label">
|
||||||
|
<el-table-column
|
||||||
|
v-if="checkColumnVisible(col.prop || '') && col.prop !== '操作'"
|
||||||
|
:prop="col.prop"
|
||||||
|
:label="col.label"
|
||||||
|
show-overflow-tooltip
|
||||||
|
align="center"
|
||||||
|
:min-width="col.minWidth"
|
||||||
|
:width="col.width">
|
||||||
|
<template #header>
|
||||||
|
<el-icon><component :is="columnConfigMap[col.prop]?.icon || Document" /></el-icon>
|
||||||
|
<span style="margin-left: 4px">{{ col.label }}</span>
|
||||||
|
</template>
|
||||||
|
<!-- 类型列 -->
|
||||||
|
<template v-if="col.prop === 'flag'" #default="scope">
|
||||||
|
<el-tag size="small" :type="scope.row.flag === 2 ? 'warning' : 'primary'" effect="plain">
|
||||||
|
{{ scope.row.flag === 2 ? '用电' : '用水' }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
<!-- 水表类型列 -->
|
||||||
|
<template v-else-if="col.prop === 'meterNum'" #default="scope">
|
||||||
|
<span>{{ formatMeterNum(scope.row.meterNum) }}</span>
|
||||||
|
</template>
|
||||||
|
<!-- 用量列 -->
|
||||||
|
<template v-else-if="col.prop === 'subiMonthSum'" #default="scope">
|
||||||
|
<span>{{ scope.row.subiMonthSum ?? '-' }} {{ scope.row.flag === 2 ? '度' : '吨' }}</span>
|
||||||
|
</template>
|
||||||
|
<!-- 费用列 -->
|
||||||
|
<template v-else-if="col.prop === 'subWatFlagSum'" #default="scope">
|
||||||
|
<span class="text-primary">¥{{ scope.row.subWatFlagSum ?? '0' }}</span>
|
||||||
|
</template>
|
||||||
|
<!-- 月份列 -->
|
||||||
|
<template v-else-if="col.prop === 'month'" #default="scope">
|
||||||
|
<span>{{ scope.row.month }}月</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</template>
|
||||||
|
<el-table-column label="操作" width="160" align="center" fixed="right">
|
||||||
|
<template #header>
|
||||||
|
<el-icon><Setting /></el-icon>
|
||||||
|
<span style="margin-left: 4px">操作</span>
|
||||||
|
</template>
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button
|
||||||
|
icon="Edit"
|
||||||
|
link
|
||||||
|
type="primary"
|
||||||
|
v-auth="'stuwork_watermonthreport_edit'"
|
||||||
|
@click="handleEdit(scope.row)">
|
||||||
|
编辑
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
icon="Delete"
|
||||||
|
link
|
||||||
|
type="danger"
|
||||||
|
v-auth="'stuwork_watermonthreport_del'"
|
||||||
|
@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>
|
||||||
|
|
||||||
|
<!-- 新增/编辑表单弹窗 -->
|
||||||
|
<form-dialog ref="formDialogRef" @refresh="getDataList" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts" name="WaterMonthReport">
|
||||||
|
import { reactive, ref, onMounted, nextTick } from 'vue'
|
||||||
|
import { useRoute } from 'vue-router'
|
||||||
|
import { BasicTableProps, useTable } from "/@/hooks/table";
|
||||||
|
import { fetchList, delObj } from "/@/api/stuwork/watermonthreport";
|
||||||
|
import { useMessage, useMessageBox } from "/@/hooks/message";
|
||||||
|
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
||||||
|
import FormDialog from './form.vue'
|
||||||
|
import { List, Setting, Document, HomeFilled, Calendar, Clock, Coin, Menu, Search } from '@element-plus/icons-vue'
|
||||||
|
import { useTableColumnControl } from '/@/hooks/tableColumn'
|
||||||
|
|
||||||
|
// 定义变量内容
|
||||||
|
const route = useRoute()
|
||||||
|
const searchFormRef = ref()
|
||||||
|
const columnControlRef = ref<any>()
|
||||||
|
const showSearch = ref(true)
|
||||||
|
const formDialogRef = ref()
|
||||||
|
|
||||||
|
// 表格列配置
|
||||||
|
const tableColumns = [
|
||||||
|
{ prop: 'roomNo', label: '房间号', width: 120 },
|
||||||
|
{ prop: 'year', label: '年份', width: 100 },
|
||||||
|
{ prop: 'month', label: '月份', width: 100 },
|
||||||
|
{ prop: 'flag', label: '类型', width: 100 },
|
||||||
|
{ prop: 'meterNum', label: '水表类型', width: 100 },
|
||||||
|
{ prop: 'subiMonthSum', label: '用量', width: 120 },
|
||||||
|
{ prop: 'subWatFlagSum', label: '费用', width: 120 },
|
||||||
|
{ prop: 'waterDate', label: '日期', width: 120 },
|
||||||
|
{ prop: 'remarks', label: '备注', minWidth: 150 }
|
||||||
|
]
|
||||||
|
|
||||||
|
// 列配置映射(用于图标显示)
|
||||||
|
const columnConfigMap: Record<string, { icon: any }> = {
|
||||||
|
roomNo: { icon: HomeFilled },
|
||||||
|
year: { icon: Calendar },
|
||||||
|
month: { icon: Calendar },
|
||||||
|
flag: { icon: Clock },
|
||||||
|
meterNum: { icon: Clock },
|
||||||
|
subiMonthSum: { icon: Coin },
|
||||||
|
subWatFlagSum: { icon: Coin },
|
||||||
|
waterDate: { icon: Calendar },
|
||||||
|
remarks: { icon: Document }
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用表格列控制hook
|
||||||
|
const {
|
||||||
|
visibleColumns,
|
||||||
|
visibleColumnsSorted,
|
||||||
|
checkColumnVisible,
|
||||||
|
handleColumnChange,
|
||||||
|
handleColumnOrderChange
|
||||||
|
} = useTableColumnControl(tableColumns, route.path)
|
||||||
|
|
||||||
|
// 搜索表单
|
||||||
|
const searchForm = reactive({
|
||||||
|
roomNo: '',
|
||||||
|
year: '',
|
||||||
|
month: '',
|
||||||
|
flag: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
// 格式化水表类型
|
||||||
|
const formatMeterNum = (value: number) => {
|
||||||
|
if (value === 10) return '冷水'
|
||||||
|
if (value === 11) 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 handleSearch = () => {
|
||||||
|
getDataList()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重置
|
||||||
|
const handleReset = () => {
|
||||||
|
searchFormRef.value?.resetFields()
|
||||||
|
searchForm.roomNo = ''
|
||||||
|
searchForm.year = ''
|
||||||
|
searchForm.month = ''
|
||||||
|
searchForm.flag = ''
|
||||||
|
getDataList()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 编辑
|
||||||
|
const handleEdit = (row: any) => {
|
||||||
|
formDialogRef.value?.openDialog(row)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除
|
||||||
|
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(() => {
|
||||||
|
nextTick(() => {
|
||||||
|
if (visibleColumns.value.length === 0) {
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
@import '/@/assets/styles/modern-page.scss';
|
||||||
|
.text-primary {
|
||||||
|
color: #409eff;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Reference in New Issue
Block a user