Merge remote-tracking branch 'origin/developer' into developer
This commit is contained in:
@@ -76,13 +76,13 @@ export const delObj = (id: string | number) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新
|
* 更新(编辑)
|
||||||
* @param obj
|
* @param obj 含 id 及需修改字段,走接口文档 /edit 接口
|
||||||
*/
|
*/
|
||||||
export const putObj = (obj: any) => {
|
export const putObj = (obj: any) => {
|
||||||
return request({
|
return request({
|
||||||
url: '/basic/basicclass',
|
url: '/basic/basicclass/edit',
|
||||||
method: 'put',
|
method: 'post',
|
||||||
data: obj,
|
data: obj,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -22,6 +22,19 @@ export const getActivityInfoList = () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查看详情 - 根据活动ID获取活动子项目列表
|
||||||
|
* 接口文档:GET /api/stuwork/activityinfosub/getActivityInfoSubList
|
||||||
|
* @param activityInfoId 活动信息ID
|
||||||
|
*/
|
||||||
|
export const getActivityInfoSubList = (activityInfoId: string) => {
|
||||||
|
return request({
|
||||||
|
url: '/stuwork/activityinfosub/getActivityInfoSubList',
|
||||||
|
method: 'get',
|
||||||
|
params: { activityInfoId }
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 删除活动子项目
|
* 删除活动子项目
|
||||||
* @param ids
|
* @param ids
|
||||||
|
|||||||
@@ -103,3 +103,37 @@ export const fearchRoomStuNum = (roomNo: string) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 互换宿舍(文档:sourceSutNo / targetStuNO)
|
||||||
|
*/
|
||||||
|
export const exchangeRoom = (data: { sourceSutNo: string; targetStuNO: string }) => {
|
||||||
|
return request({
|
||||||
|
url: '/stuwork/dormroomstudent/exchangeRoom',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 打印宿舍卡(按房间号获取打印数据)
|
||||||
|
*/
|
||||||
|
export const printDormRoomData = (roomNo: string) => {
|
||||||
|
return request({
|
||||||
|
url: '/stuwork/dormroomstudent/printDormRoomData',
|
||||||
|
method: 'get',
|
||||||
|
params: { roomNo }
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 空 n 人宿舍导出
|
||||||
|
*/
|
||||||
|
export const exportEmptyPeopleRoomExcel = (data?: any) => {
|
||||||
|
return request({
|
||||||
|
url: '/stuwork/dormroomstudent/exportEmptyPeopleRoomExcel',
|
||||||
|
method: 'post',
|
||||||
|
data: data || {},
|
||||||
|
responseType: 'blob'
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|||||||
61
src/api/stuwork/gradustu.ts
Normal file
61
src/api/stuwork/gradustu.ts
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
import request from '/@/utils/request'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页查询毕业学生(毕业审核列表)
|
||||||
|
* 后端返回 data.dataList.records / data.dataList.total,此处归一为 data.records / data.total 供 useTable 使用
|
||||||
|
* @param query current, size, graduYear, status, type, stuNo, realName, classCode, deptCode, ...
|
||||||
|
*/
|
||||||
|
export const fetchList = (query?: any) => {
|
||||||
|
return request({
|
||||||
|
url: '/stuwork/stugraducheck/page',
|
||||||
|
method: 'get',
|
||||||
|
params: query
|
||||||
|
}).then((res: any) => {
|
||||||
|
const raw = res.data || {}
|
||||||
|
const dataList = raw.dataList || {}
|
||||||
|
return {
|
||||||
|
...res,
|
||||||
|
data: {
|
||||||
|
records: dataList.records || [],
|
||||||
|
total: dataList.total ?? 0,
|
||||||
|
canExamConduct: raw.canExamConduct,
|
||||||
|
canExamScore: raw.canExamScore,
|
||||||
|
canExamSkill: raw.canExamSkill,
|
||||||
|
canExamStuPunish: raw.canExamStuPunish,
|
||||||
|
canExamBaseInfo: raw.canExamBaseInfo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成毕业生信息
|
||||||
|
* @param data 如 { type: '0' }
|
||||||
|
*/
|
||||||
|
export const makeGraduStu = (data?: { type?: string }) => {
|
||||||
|
return request({
|
||||||
|
url: '/stuwork/stugraducheck/makeGraduStu',
|
||||||
|
method: 'post',
|
||||||
|
data: data || { type: '0' }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取某年毕业生列表(用于统计页前端汇总,大 size 拉取)
|
||||||
|
* @param graduYear 毕业年份
|
||||||
|
*/
|
||||||
|
export const fetchListForAnalyse = (graduYear: string | number) => {
|
||||||
|
return request({
|
||||||
|
url: '/stuwork/stugraducheck/page',
|
||||||
|
method: 'get',
|
||||||
|
params: {
|
||||||
|
graduYear,
|
||||||
|
current: 1,
|
||||||
|
size: 9999
|
||||||
|
}
|
||||||
|
}).then((res: any) => {
|
||||||
|
const raw = res.data || {}
|
||||||
|
const dataList = raw.dataList || {}
|
||||||
|
return (dataList.records || []) as any[]
|
||||||
|
})
|
||||||
|
}
|
||||||
73
src/api/stuwork/psychologicalcounselingduty.ts
Normal file
73
src/api/stuwork/psychologicalcounselingduty.ts
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
import request from '/@/utils/request'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 按月份返回值班表(列表)
|
||||||
|
* @param params year, month
|
||||||
|
*/
|
||||||
|
export const listByMonth = (params: { year: string | number; month: string | number }) => {
|
||||||
|
return request({
|
||||||
|
url: '/stuwork/psychologicalcounselingduty/listByMonth',
|
||||||
|
method: 'get',
|
||||||
|
params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 后台获取某年某月值班表,回显到日历/列表
|
||||||
|
* @param params year, month
|
||||||
|
*/
|
||||||
|
export const getDutyByMonth = (params: { year: string | number; month: string | number }) => {
|
||||||
|
return request({
|
||||||
|
url: '/stuwork/psychologicalcounselingduty/getDutyByMonth',
|
||||||
|
method: 'get',
|
||||||
|
params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过 id 查询值班详情
|
||||||
|
* @param id
|
||||||
|
*/
|
||||||
|
export const getDetail = (id: string) => {
|
||||||
|
return request({
|
||||||
|
url: '/stuwork/psychologicalcounselingduty/detail',
|
||||||
|
method: 'get',
|
||||||
|
params: { id }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增/批量新增值班
|
||||||
|
* @param list 每项 { date: 'YYYY-MM-DD', teacherUserName: '工号', weekType?: 'single'|'double' }
|
||||||
|
*/
|
||||||
|
export const saveDuty = (list: Array<{ date: string; teacherUserName: string; weekType?: string }>) => {
|
||||||
|
return request({
|
||||||
|
url: '/stuwork/psychologicalcounselingduty/saveDuty',
|
||||||
|
method: 'post',
|
||||||
|
data: list
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 一键清空某月值班
|
||||||
|
* @param data { year, month }
|
||||||
|
*/
|
||||||
|
export const clearDuty = (data: { year: number; month: number }) => {
|
||||||
|
return request({
|
||||||
|
url: '/stuwork/psychologicalcounselingduty/clearDuty',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清除单个值班(按日期)
|
||||||
|
* @param data { days: 'YYYY-MM-DD' }
|
||||||
|
*/
|
||||||
|
export const clearOneDuty = (data: { days: string }) => {
|
||||||
|
return request({
|
||||||
|
url: '/stuwork/psychologicalcounselingduty/clearOneDuty',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
61
src/api/stuwork/psychologicalcounselingreservation.ts
Normal file
61
src/api/stuwork/psychologicalcounselingreservation.ts
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
import request from '/@/utils/request'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页查询预约记录
|
||||||
|
* @param query current, size, stuNo, classNo, reservationTime, isHandle
|
||||||
|
*/
|
||||||
|
export const fetchList = (query?: any) => {
|
||||||
|
return request({
|
||||||
|
url: '/stuwork/psychologicalcounselingreservation/page',
|
||||||
|
method: 'get',
|
||||||
|
params: query
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过 id 查询预约记录详情
|
||||||
|
* @param id
|
||||||
|
*/
|
||||||
|
export const getDetail = (id: string) => {
|
||||||
|
return request({
|
||||||
|
url: '/stuwork/psychologicalcounselingreservation/detail',
|
||||||
|
method: 'get',
|
||||||
|
params: { id }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增预约记录
|
||||||
|
* @param data teacherNo, reservationTime, classNo, stuNo, stuName, phone, remarks, realName?, isHandle?
|
||||||
|
*/
|
||||||
|
export const addObj = (data: any) => {
|
||||||
|
return request({
|
||||||
|
url: '/stuwork/psychologicalcounselingreservation',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改预约记录
|
||||||
|
* @param data 含 id 及需修改字段
|
||||||
|
*/
|
||||||
|
export const editObj = (data: any) => {
|
||||||
|
return request({
|
||||||
|
url: '/stuwork/psychologicalcounselingreservation/edit',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过 id 删除预约记录
|
||||||
|
* @param ids id 数组
|
||||||
|
*/
|
||||||
|
export const delObj = (ids: string[]) => {
|
||||||
|
return request({
|
||||||
|
url: '/stuwork/psychologicalcounselingreservation/delete',
|
||||||
|
method: 'post',
|
||||||
|
data: ids
|
||||||
|
})
|
||||||
|
}
|
||||||
71
src/api/stuwork/psychologicalcounselingteacher.ts
Normal file
71
src/api/stuwork/psychologicalcounselingteacher.ts
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
import request from '/@/utils/request'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页查询心理咨询预约师
|
||||||
|
* @param query current, size, realName
|
||||||
|
*/
|
||||||
|
export const fetchList = (query?: any) => {
|
||||||
|
return request({
|
||||||
|
url: '/stuwork/psychologicalcounselingteacher/page',
|
||||||
|
method: 'get',
|
||||||
|
params: query
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取预约师列表(不分页)
|
||||||
|
*/
|
||||||
|
export const getList = () => {
|
||||||
|
return request({
|
||||||
|
url: '/stuwork/psychologicalcounselingteacher/list',
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过 id 查询详情
|
||||||
|
* @param id
|
||||||
|
*/
|
||||||
|
export const getDetail = (id: string) => {
|
||||||
|
return request({
|
||||||
|
url: '/stuwork/psychologicalcounselingteacher/detail',
|
||||||
|
method: 'get',
|
||||||
|
params: { id }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增心理咨询预约师
|
||||||
|
* @param data userName, realName, phone, remarks
|
||||||
|
*/
|
||||||
|
export const addObj = (data: any) => {
|
||||||
|
return request({
|
||||||
|
url: '/stuwork/psychologicalcounselingteacher',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改心理咨询预约师
|
||||||
|
* @param data id, userName, realName, phone, remarks
|
||||||
|
*/
|
||||||
|
export const editObj = (data: any) => {
|
||||||
|
return request({
|
||||||
|
url: '/stuwork/psychologicalcounselingteacher/edit',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过 id 删除心理咨询预约师
|
||||||
|
* @param ids id 数组
|
||||||
|
*/
|
||||||
|
export const delObj = (ids: string[]) => {
|
||||||
|
return request({
|
||||||
|
url: '/stuwork/psychologicalcounselingteacher/delete',
|
||||||
|
method: 'post',
|
||||||
|
data: ids
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -36,6 +36,18 @@ export const getDetail = (id: string) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过学年学号查看详情(接口文档:GET /stuwork/stuconduct/queryDataByStuNo)
|
||||||
|
* @param params stuNo 学号, schoolYear 学年
|
||||||
|
*/
|
||||||
|
export const queryDataByStuNo = (params: { stuNo: string; schoolYear: string }) => {
|
||||||
|
return request({
|
||||||
|
url: '/stuwork/stuconduct/queryDataByStuNo',
|
||||||
|
method: 'get',
|
||||||
|
params
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 编辑操行考核
|
* 编辑操行考核
|
||||||
* @param data
|
* @param data
|
||||||
|
|||||||
25
src/api/stuwork/stugraducheck.ts
Normal file
25
src/api/stuwork/stugraducheck.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import request from '/@/utils/request'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 毕业学生名单 - 分页查询
|
||||||
|
* 接口文档:GET /api/stuwork/stugraducheck/page
|
||||||
|
* 参数:current, size, graduYear, status, type, stuNo, realName, classCode, deptCode 等
|
||||||
|
* 返回归一为 data.records / data.total 供 useTable 使用
|
||||||
|
*/
|
||||||
|
export const fetchList = (query?: any) => {
|
||||||
|
return request({
|
||||||
|
url: '/stuwork/stugraducheck/page',
|
||||||
|
method: 'get',
|
||||||
|
params: query
|
||||||
|
}).then((res: any) => {
|
||||||
|
const raw = res.data || {}
|
||||||
|
const dataList = raw.dataList || {}
|
||||||
|
return {
|
||||||
|
...res,
|
||||||
|
data: {
|
||||||
|
records: dataList.records || [],
|
||||||
|
total: dataList.total ?? 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -60,6 +60,18 @@ export const delObj = (ids: string[]) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 撤销学籍异动
|
||||||
|
* @param ids 异动记录ID列表
|
||||||
|
*/
|
||||||
|
export const cancelObj = (ids: string[]) => {
|
||||||
|
return request({
|
||||||
|
url: '/stuwork/stuturnover/cancel',
|
||||||
|
method: 'post',
|
||||||
|
data: ids
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 导出学籍异动
|
* 导出学籍异动
|
||||||
* @param query
|
* @param query
|
||||||
|
|||||||
@@ -12,3 +12,16 @@ export const getClassRoomByClassCode = (classCode: string | number) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 教室安排
|
||||||
|
* 接口文档:POST /api/stuwork/teachclassroomassign/addClassRoomAssign
|
||||||
|
* @param data buildingNo 楼号, position 位置, classCode 班级代码
|
||||||
|
*/
|
||||||
|
export const addClassRoomAssign = (data: { buildingNo?: string | number; position?: string; classCode?: string }) => {
|
||||||
|
return request({
|
||||||
|
url: '/stuwork/teachclassroomassign/addClassRoomAssign',
|
||||||
|
method: 'post',
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -156,8 +156,10 @@ export function useTable(options?: BasicTableProps) {
|
|||||||
if (state.onLoaded) await state.onLoaded(state);
|
if (state.onLoaded) await state.onLoaded(state);
|
||||||
if (state.onCascaded) await state.onCascaded(state);
|
if (state.onCascaded) await state.onCascaded(state);
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
// 捕获异常并显示错误提示
|
// 全局拦截器已展示过错误时不再重复弹窗
|
||||||
ElMessage.error(err.msg || err.data.msg);
|
if (!err?._messageShown) {
|
||||||
|
ElMessage.error(err?.msg || err?.data?.msg || '请求失败');
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
// 结束加载数据,设置state.loading为false
|
// 结束加载数据,设置state.loading为false
|
||||||
state.loading = false;
|
state.loading = false;
|
||||||
|
|||||||
@@ -97,9 +97,10 @@ service.interceptors.request.use(
|
|||||||
*/
|
*/
|
||||||
const handleResponse = (response: AxiosResponse<any>) => {
|
const handleResponse = (response: AxiosResponse<any>) => {
|
||||||
if (response.data.code === 1) {
|
if (response.data.code === 1) {
|
||||||
// 业务错误,统一弹出错误提示
|
// 业务错误,统一弹出错误提示(标记已展示,避免 hook/页面 catch 再次弹窗)
|
||||||
if (response.data.msg) {
|
if (response.data.msg) {
|
||||||
useMessage().error(response.data.msg);
|
useMessage().error(response.data.msg);
|
||||||
|
response.data._messageShown = true;
|
||||||
}
|
}
|
||||||
throw response.data;
|
throw response.data;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -204,7 +204,8 @@ const dataRules = ref({
|
|||||||
{ required: true, message: '学院不能为空', trigger: 'change' }
|
{ required: true, message: '学院不能为空', trigger: 'change' }
|
||||||
],
|
],
|
||||||
classCode: [
|
classCode: [
|
||||||
{ required: true, message: '班级代码不能为空', trigger: 'blur' }
|
{ required: true, message: '班级代码不能为空', trigger: 'blur' },
|
||||||
|
{ min: 4, message: '班级代码至少4位(班号取后4位)', trigger: 'blur' }
|
||||||
],
|
],
|
||||||
classNo: [
|
classNo: [
|
||||||
{ required: true, message: '班号不能为空', trigger: 'blur' }
|
{ required: true, message: '班号不能为空', trigger: 'blur' }
|
||||||
@@ -406,11 +407,11 @@ watch(() => form.enterDate, (newVal) => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// 监听班级代码,取后四位为班号
|
// 监听班级代码,取后四位为班号(班级代码至少4位)
|
||||||
watch(() => form.classCode, (newVal) => {
|
watch(() => form.classCode, (newVal) => {
|
||||||
if (newVal) {
|
if (newVal) {
|
||||||
const length = newVal.length
|
const length = newVal.length
|
||||||
if (length > 4) {
|
if (length >= 4) {
|
||||||
// 只在新增模式下自动填充班号,编辑模式下如果班号为空才填充
|
// 只在新增模式下自动填充班号,编辑模式下如果班号为空才填充
|
||||||
if (!form.id || !form.classNo) {
|
if (!form.id || !form.classNo) {
|
||||||
form.classNo = newVal.substring(length - 4, length)
|
form.classNo = newVal.substring(length - 4, length)
|
||||||
|
|||||||
@@ -196,7 +196,7 @@
|
|||||||
</el-tag>
|
</el-tag>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="操作" align="center" fixed="right" width="200">
|
<el-table-column label="操作" align="center" fixed="right" width="260">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-button
|
<el-button
|
||||||
icon="View"
|
icon="View"
|
||||||
|
|||||||
@@ -4,25 +4,56 @@
|
|||||||
v-model="visible"
|
v-model="visible"
|
||||||
:close-on-click-modal="false"
|
:close-on-click-modal="false"
|
||||||
draggable
|
draggable
|
||||||
width="800px">
|
width="960px">
|
||||||
<div v-loading="loading" class="detail-container" v-if="detailData">
|
<div class="detail-container">
|
||||||
<el-descriptions :column="2" border>
|
<!-- 活动主信息(来自列表行) -->
|
||||||
|
<el-descriptions v-if="mainInfo.activityTheme" :column="2" border class="mb16">
|
||||||
<el-descriptions-item label="活动主题" :span="2">
|
<el-descriptions-item label="活动主题" :span="2">
|
||||||
{{ detailData.activityTheme || '-' }}
|
{{ mainInfo.activityTheme || '-' }}
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
<el-descriptions-item label="活动说明" :span="2">
|
<el-descriptions-item label="活动说明" :span="2">
|
||||||
{{ detailData.remarks || '-' }}
|
{{ mainInfo.remarks || '-' }}
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
<el-descriptions-item label="活动兼报数">
|
<el-descriptions-item label="活动兼报数">
|
||||||
{{ detailData.maxSub !== undefined && detailData.maxSub !== null ? detailData.maxSub : '-' }}
|
{{ mainInfo.maxSub !== undefined && mainInfo.maxSub !== null ? mainInfo.maxSub : '-' }}
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
<el-descriptions-item label="开始时间">
|
<el-descriptions-item label="开始时间">
|
||||||
{{ parseTime(detailData.startTime, '{y}-{m}-{d}') }}
|
{{ mainInfo.startTime ? parseTime(mainInfo.startTime, '{y}-{m}-{d}') : '-' }}
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
<el-descriptions-item label="结束时间">
|
<el-descriptions-item label="结束时间">
|
||||||
{{ parseTime(detailData.endTime, '{y}-{m}-{d}') }}
|
{{ mainInfo.endTime ? parseTime(mainInfo.endTime, '{y}-{m}-{d}') : '-' }}
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
</el-descriptions>
|
</el-descriptions>
|
||||||
|
<!-- 子项目列表(接口:getActivityInfoSubList) -->
|
||||||
|
<div class="sub-title">子项目列表</div>
|
||||||
|
<el-table
|
||||||
|
:data="subList"
|
||||||
|
v-loading="loading"
|
||||||
|
border
|
||||||
|
size="small"
|
||||||
|
max-height="400"
|
||||||
|
style="width: 100%">
|
||||||
|
<el-table-column type="index" label="序号" width="60" align="center" />
|
||||||
|
<el-table-column prop="subTitle" label="子项目名称" min-width="140" show-overflow-tooltip />
|
||||||
|
<el-table-column prop="deptName" label="学院" width="110" show-overflow-tooltip />
|
||||||
|
<el-table-column prop="classNo" label="班号" width="80" align="center" />
|
||||||
|
<el-table-column prop="classMasterName" label="班主任" width="90" show-overflow-tooltip />
|
||||||
|
<el-table-column prop="startTime" label="开始时间" width="155" align="center">
|
||||||
|
<template #default="scope">
|
||||||
|
{{ scope.row.startTime ? parseTime(scope.row.startTime, '{y}-{m}-{d} {h}:{i}') : '-' }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="endTime" label="结束时间" width="155" align="center">
|
||||||
|
<template #default="scope">
|
||||||
|
{{ scope.row.endTime ? parseTime(scope.row.endTime, '{y}-{m}-{d} {h}:{i}') : '-' }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="maxNum" label="人数限制" width="88" align="center" />
|
||||||
|
<el-table-column prop="applyNums" label="已报名" width="78" align="center" />
|
||||||
|
<el-table-column prop="position" label="地点" min-width="100" show-overflow-tooltip />
|
||||||
|
<el-table-column prop="projectDescription" label="项目描述" min-width="180" show-overflow-tooltip />
|
||||||
|
</el-table>
|
||||||
|
<el-empty v-if="!loading && subList.length === 0" description="暂无子项目" :image-size="80" />
|
||||||
</div>
|
</div>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<span class="dialog-footer">
|
<span class="dialog-footer">
|
||||||
@@ -33,47 +64,40 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts" name="ActivityInfoDetailDialog">
|
<script setup lang="ts" name="ActivityInfoDetailDialog">
|
||||||
import { ref } from 'vue'
|
import { ref, reactive } from 'vue'
|
||||||
import { getDetail } from '/@/api/stuwork/activityinfo'
|
import { getActivityInfoSubList } from '/@/api/stuwork/activityinfosub'
|
||||||
import { parseTime } from '/@/utils/formatTime'
|
import { parseTime } from '/@/utils/formatTime'
|
||||||
import { useMessage } from '/@/hooks/message'
|
|
||||||
|
|
||||||
// 定义变量内容
|
|
||||||
const visible = ref(false)
|
const visible = ref(false)
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const detailData = ref<any>({})
|
const mainInfo = reactive<Record<string, any>>({})
|
||||||
|
const subList = ref<any[]>([])
|
||||||
|
|
||||||
// 打开弹窗
|
/**
|
||||||
const openDialog = async (id: string) => {
|
* 打开弹窗:使用接口 getActivityInfoSubList(activityInfoId) 获取详情子项目列表
|
||||||
|
* @param activityInfoId 活动信息ID
|
||||||
|
* @param row 列表行数据,用于展示活动主题等主信息
|
||||||
|
*/
|
||||||
|
const openDialog = async (activityInfoId: string, row?: any) => {
|
||||||
visible.value = true
|
visible.value = true
|
||||||
loading.value = true
|
loading.value = true
|
||||||
detailData.value = {}
|
subList.value = []
|
||||||
|
Object.keys(mainInfo).forEach((k) => delete mainInfo[k])
|
||||||
|
if (row && typeof row === 'object') {
|
||||||
|
Object.assign(mainInfo, row)
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await getDetail(id)
|
const res = await getActivityInfoSubList(activityInfoId)
|
||||||
if (res.data) {
|
const data = res.data
|
||||||
// 根据接口文档,返回的数据可能是 { records: [...], total: ... } 格式
|
subList.value = Array.isArray(data) ? data : []
|
||||||
// 如果是列表格式,取第一条;如果是对象,直接使用
|
} catch (_err) {
|
||||||
if (res.data.records && Array.isArray(res.data.records) && res.data.records.length > 0) {
|
subList.value = []
|
||||||
detailData.value = res.data.records[0]
|
|
||||||
} else if (res.data.records && Array.isArray(res.data.records)) {
|
|
||||||
// 列表为空
|
|
||||||
useMessage().warning('未找到详情数据')
|
|
||||||
visible.value = false
|
|
||||||
} else {
|
|
||||||
// 直接是对象
|
|
||||||
detailData.value = res.data
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (err: any) {
|
|
||||||
useMessage().error(err.msg || '获取详情失败')
|
|
||||||
visible.value = false
|
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false
|
loading.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 暴露方法
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
openDialog
|
openDialog
|
||||||
})
|
})
|
||||||
@@ -81,7 +105,15 @@ defineExpose({
|
|||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.detail-container {
|
.detail-container {
|
||||||
padding: 20px 0;
|
padding: 8px 0;
|
||||||
|
}
|
||||||
|
.mb16 {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
.sub-title {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #303133;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -250,9 +250,9 @@ const {
|
|||||||
tableStyle: _tableStyle
|
tableStyle: _tableStyle
|
||||||
} = useTable(state)
|
} = useTable(state)
|
||||||
|
|
||||||
// 查看详情
|
// 查看详情(接口:getActivityInfoSubList,传入活动ID与行数据用于展示)
|
||||||
const handleView = (row: any) => {
|
const handleView = (row: any) => {
|
||||||
detailDialogRef.value?.openDialog(row.id)
|
detailDialogRef.value?.openDialog(row.id, row)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 编辑
|
// 编辑
|
||||||
|
|||||||
@@ -48,6 +48,17 @@
|
|||||||
style="width: 100%" />
|
style="width: 100%" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
|
<el-col :span="24" class="mb20">
|
||||||
|
<el-form-item label="默认扣分值" prop="score">
|
||||||
|
<el-input-number
|
||||||
|
v-model="form.score"
|
||||||
|
:min="0"
|
||||||
|
:max="999"
|
||||||
|
:precision="0"
|
||||||
|
placeholder="请输入默认扣分值"
|
||||||
|
style="width: 100%" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
<el-col :span="24" class="mb20">
|
<el-col :span="24" class="mb20">
|
||||||
<el-form-item label="备注" prop="remarks">
|
<el-form-item label="备注" prop="remarks">
|
||||||
<el-input
|
<el-input
|
||||||
@@ -86,12 +97,13 @@ const loading = ref(false)
|
|||||||
const operType = ref('add')
|
const operType = ref('add')
|
||||||
const categoryList = ref<any[]>([])
|
const categoryList = ref<any[]>([])
|
||||||
|
|
||||||
// 提交表单数据
|
// 提交表单数据(与接口文档一致:edit 含 score 默认扣分值)
|
||||||
const form = reactive({
|
const form = reactive({
|
||||||
id: '',
|
id: '',
|
||||||
categortyId: '',
|
categortyId: '',
|
||||||
pointName: '',
|
pointName: '',
|
||||||
standard: '',
|
standard: '',
|
||||||
|
score: undefined as number | undefined,
|
||||||
remarks: ''
|
remarks: ''
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -117,6 +129,7 @@ const openDialog = async (type: string = 'add', row?: any) => {
|
|||||||
form.categortyId = ''
|
form.categortyId = ''
|
||||||
form.pointName = ''
|
form.pointName = ''
|
||||||
form.standard = ''
|
form.standard = ''
|
||||||
|
form.score = undefined
|
||||||
form.remarks = ''
|
form.remarks = ''
|
||||||
|
|
||||||
// 编辑时填充数据
|
// 编辑时填充数据
|
||||||
@@ -125,20 +138,21 @@ const openDialog = async (type: string = 'add', row?: any) => {
|
|||||||
form.categortyId = row.categortyId || ''
|
form.categortyId = row.categortyId || ''
|
||||||
form.pointName = row.pointName || ''
|
form.pointName = row.pointName || ''
|
||||||
form.standard = row.standard || ''
|
form.standard = row.standard || ''
|
||||||
|
form.score = row.score !== undefined && row.score !== null ? Number(row.score) : undefined
|
||||||
form.remarks = row.remarks || ''
|
form.remarks = row.remarks || ''
|
||||||
|
|
||||||
// 如果需要获取详情
|
// 如果需要获取详情(含 score)
|
||||||
if (row.id && (!row.pointName || !row.standard)) {
|
if (row.id && (!row.pointName || row.standard === undefined || row.score === undefined)) {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
getDetail(row.id).then((res: any) => {
|
getDetail(row.id).then((res: any) => {
|
||||||
if (res.data) {
|
if (res.data) {
|
||||||
form.categortyId = res.data.categortyId || ''
|
form.categortyId = res.data.categortyId || ''
|
||||||
form.pointName = res.data.pointName || ''
|
form.pointName = res.data.pointName || ''
|
||||||
form.standard = res.data.standard || ''
|
form.standard = res.data.standard || ''
|
||||||
|
form.score = res.data.score !== undefined && res.data.score !== null ? Number(res.data.score) : undefined
|
||||||
form.remarks = res.data.remarks || ''
|
form.remarks = res.data.remarks || ''
|
||||||
}
|
}
|
||||||
}).catch((err) => {
|
}).catch(() => {}).finally(() => {
|
||||||
}).finally(() => {
|
|
||||||
loading.value = false
|
loading.value = false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -155,10 +169,12 @@ const onSubmit = async () => {
|
|||||||
|
|
||||||
loading.value = true
|
loading.value = true
|
||||||
try {
|
try {
|
||||||
|
// 与接口文档一致:categortyId, pointName, standard, score, remarks
|
||||||
const submitData = {
|
const submitData = {
|
||||||
categortyId: form.categortyId,
|
categortyId: form.categortyId,
|
||||||
pointName: form.pointName,
|
pointName: form.pointName,
|
||||||
standard: form.standard || '',
|
standard: form.standard || '',
|
||||||
|
score: form.score !== undefined && form.score !== null ? Number(form.score) : 0,
|
||||||
remarks: form.remarks || ''
|
remarks: form.remarks || ''
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,21 @@
|
|||||||
:inline="true"
|
:inline="true"
|
||||||
@keyup.enter="getDataList"
|
@keyup.enter="getDataList"
|
||||||
class="search-form">
|
class="search-form">
|
||||||
|
<el-form-item label="考核项" prop="categortyId">
|
||||||
|
<el-select
|
||||||
|
v-model="state.queryForm.categortyId"
|
||||||
|
placeholder="请选择考核项"
|
||||||
|
clearable
|
||||||
|
filterable
|
||||||
|
style="width: 200px">
|
||||||
|
<el-option
|
||||||
|
v-for="item in categoryList"
|
||||||
|
:key="item.id"
|
||||||
|
:label="item.category"
|
||||||
|
:value="item.id">
|
||||||
|
</el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
<el-form-item label="指标名称" prop="pointName">
|
<el-form-item label="指标名称" prop="pointName">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="state.queryForm.pointName"
|
v-model="state.queryForm.pointName"
|
||||||
@@ -155,9 +170,10 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts" name="AssessmentPoint">
|
<script setup lang="ts" name="AssessmentPoint">
|
||||||
import { reactive, ref } from 'vue'
|
import { reactive, ref, onMounted } from 'vue'
|
||||||
import { BasicTableProps, useTable } from "/@/hooks/table";
|
import { BasicTableProps, useTable } from "/@/hooks/table";
|
||||||
import { fetchList, delObj } from "/@/api/stuwork/assessmentpoint";
|
import { fetchList, delObj } from "/@/api/stuwork/assessmentpoint";
|
||||||
|
import { getList as getAssessmentCategoryList } from "/@/api/stuwork/assessmentcategory";
|
||||||
import { useMessage, useMessageBox } from "/@/hooks/message";
|
import { useMessage, useMessageBox } from "/@/hooks/message";
|
||||||
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
||||||
import FormDialog from './form.vue'
|
import FormDialog from './form.vue'
|
||||||
@@ -169,6 +185,7 @@ const formDialogRef = ref()
|
|||||||
const searchFormRef = ref()
|
const searchFormRef = ref()
|
||||||
const columnControlRef = ref()
|
const columnControlRef = ref()
|
||||||
const showSearch = ref(true)
|
const showSearch = ref(true)
|
||||||
|
const categoryList = ref<any[]>([])
|
||||||
|
|
||||||
// 表格列配置
|
// 表格列配置
|
||||||
const tableColumns = [
|
const tableColumns = [
|
||||||
@@ -194,9 +211,10 @@ const tableStyle = {
|
|||||||
headerCellStyle: { background: '#f5f7fa', color: '#606266', fontWeight: 'bold' }
|
headerCellStyle: { background: '#f5f7fa', color: '#606266', fontWeight: 'bold' }
|
||||||
}
|
}
|
||||||
|
|
||||||
// 配置 useTable
|
// 配置 useTable(分页接口支持 categortyId、pointName,与考核项对应)
|
||||||
const state: BasicTableProps = reactive<BasicTableProps>({
|
const state: BasicTableProps = reactive<BasicTableProps>({
|
||||||
queryForm: {
|
queryForm: {
|
||||||
|
categortyId: '',
|
||||||
pointName: ''
|
pointName: ''
|
||||||
},
|
},
|
||||||
pageList: fetchList,
|
pageList: fetchList,
|
||||||
@@ -215,13 +233,28 @@ const {
|
|||||||
tableStyle: _tableStyle
|
tableStyle: _tableStyle
|
||||||
} = useTable(state)
|
} = useTable(state)
|
||||||
|
|
||||||
|
// 获取考核项列表(与表单考核项对应,接口:assessmentcategory/list)
|
||||||
|
const getCategoryList = async () => {
|
||||||
|
try {
|
||||||
|
const res = await getAssessmentCategoryList()
|
||||||
|
categoryList.value = res.data && Array.isArray(res.data) ? res.data : []
|
||||||
|
} catch (err) {
|
||||||
|
categoryList.value = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 重置
|
// 重置
|
||||||
const handleReset = () => {
|
const handleReset = () => {
|
||||||
searchFormRef.value?.resetFields()
|
searchFormRef.value?.resetFields()
|
||||||
|
state.queryForm.categortyId = ''
|
||||||
state.queryForm.pointName = ''
|
state.queryForm.pointName = ''
|
||||||
getDataList()
|
getDataList()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getCategoryList()
|
||||||
|
})
|
||||||
|
|
||||||
// 编辑
|
// 编辑
|
||||||
const handleEdit = (row: any) => {
|
const handleEdit = (row: any) => {
|
||||||
formDialogRef.value?.openDialog('edit', row)
|
formDialogRef.value?.openDialog('edit', row)
|
||||||
|
|||||||
@@ -69,8 +69,7 @@
|
|||||||
v-model="form.score"
|
v-model="form.score"
|
||||||
:precision="0"
|
:precision="0"
|
||||||
:step="1"
|
:step="1"
|
||||||
:min="0"
|
placeholder="请输入分数(可为负数)"
|
||||||
placeholder="请输入分数"
|
|
||||||
style="width: 100%" />
|
style="width: 100%" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
|
|||||||
@@ -12,6 +12,53 @@
|
|||||||
label-width="100px"
|
label-width="100px"
|
||||||
v-loading="loading">
|
v-loading="loading">
|
||||||
<el-row :gutter="24">
|
<el-row :gutter="24">
|
||||||
|
<el-col :span="12" class="mb20">
|
||||||
|
<el-form-item label="学年" prop="schoolYear">
|
||||||
|
<el-select
|
||||||
|
v-model="form.schoolYear"
|
||||||
|
placeholder="请选择学年"
|
||||||
|
clearable
|
||||||
|
filterable
|
||||||
|
style="width: 100%">
|
||||||
|
<el-option
|
||||||
|
v-for="item in schoolYearList"
|
||||||
|
:key="item.year"
|
||||||
|
:label="item.year"
|
||||||
|
:value="item.year" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12" class="mb20">
|
||||||
|
<el-form-item label="学期" prop="schoolTerm">
|
||||||
|
<el-select
|
||||||
|
v-model="form.schoolTerm"
|
||||||
|
placeholder="请选择学期"
|
||||||
|
clearable
|
||||||
|
style="width: 100%">
|
||||||
|
<el-option
|
||||||
|
v-for="item in schoolTermList"
|
||||||
|
:key="item.value"
|
||||||
|
:label="item.label"
|
||||||
|
:value="item.value" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12" class="mb20">
|
||||||
|
<el-form-item label="班号" prop="classCode">
|
||||||
|
<el-select
|
||||||
|
v-model="form.classCode"
|
||||||
|
placeholder="请选择班号"
|
||||||
|
clearable
|
||||||
|
filterable
|
||||||
|
style="width: 100%">
|
||||||
|
<el-option
|
||||||
|
v-for="item in classList"
|
||||||
|
:key="item.classCode"
|
||||||
|
:label="item.classNo"
|
||||||
|
:value="item.classCode" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
<el-col :span="24" class="mb20">
|
<el-col :span="24" class="mb20">
|
||||||
<el-form-item label="标题" prop="title">
|
<el-form-item label="标题" prop="title">
|
||||||
<el-input
|
<el-input
|
||||||
@@ -52,9 +99,12 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts" name="ClassPlanFormDialog">
|
<script setup lang="ts" name="ClassPlanFormDialog">
|
||||||
import { ref, reactive, nextTick } from 'vue'
|
import { ref, reactive, nextTick, onMounted } from 'vue'
|
||||||
import { useMessage } from '/@/hooks/message'
|
import { useMessage } from '/@/hooks/message'
|
||||||
import { addObj, editObj, getDetail } from '/@/api/stuwork/classplan'
|
import { addObj, editObj, getDetail } from '/@/api/stuwork/classplan'
|
||||||
|
import { queryAllSchoolYear } from '/@/api/basic/basicyear'
|
||||||
|
import { getClassListByRole } from '/@/api/basic/basicclass'
|
||||||
|
import { getDicts } from '/@/api/admin/dict'
|
||||||
import Editor from '/@/components/Editor/index.vue'
|
import Editor from '/@/components/Editor/index.vue'
|
||||||
|
|
||||||
const emit = defineEmits(['refresh'])
|
const emit = defineEmits(['refresh'])
|
||||||
@@ -64,10 +114,16 @@ const dataFormRef = ref()
|
|||||||
const visible = ref(false)
|
const visible = ref(false)
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const operType = ref('add') // add 或 edit
|
const operType = ref('add') // add 或 edit
|
||||||
|
const schoolYearList = ref<any[]>([])
|
||||||
|
const schoolTermList = ref<any[]>([])
|
||||||
|
const classList = ref<any[]>([])
|
||||||
|
|
||||||
// 提交表单数据
|
// 提交表单数据
|
||||||
const form = reactive({
|
const form = reactive({
|
||||||
id: '',
|
id: '',
|
||||||
|
schoolYear: '',
|
||||||
|
schoolTerm: '',
|
||||||
|
classCode: '',
|
||||||
title: '',
|
title: '',
|
||||||
content: '',
|
content: '',
|
||||||
remarks: ''
|
remarks: ''
|
||||||
@@ -75,6 +131,15 @@ const form = reactive({
|
|||||||
|
|
||||||
// 定义校验规则
|
// 定义校验规则
|
||||||
const dataRules = {
|
const dataRules = {
|
||||||
|
schoolYear: [
|
||||||
|
{ required: true, message: '请选择学年', trigger: 'change' }
|
||||||
|
],
|
||||||
|
schoolTerm: [
|
||||||
|
{ required: true, message: '请选择学期', trigger: 'change' }
|
||||||
|
],
|
||||||
|
classCode: [
|
||||||
|
{ required: true, message: '请选择班号', trigger: 'change' }
|
||||||
|
],
|
||||||
title: [
|
title: [
|
||||||
{ required: true, message: '请输入标题', trigger: 'blur' }
|
{ required: true, message: '请输入标题', trigger: 'blur' }
|
||||||
],
|
],
|
||||||
@@ -92,6 +157,9 @@ const openDialog = async (type: string = 'add', row?: any) => {
|
|||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
dataFormRef.value?.resetFields()
|
dataFormRef.value?.resetFields()
|
||||||
form.id = ''
|
form.id = ''
|
||||||
|
form.schoolYear = ''
|
||||||
|
form.schoolTerm = ''
|
||||||
|
form.classCode = ''
|
||||||
form.title = ''
|
form.title = ''
|
||||||
form.content = ''
|
form.content = ''
|
||||||
form.remarks = ''
|
form.remarks = ''
|
||||||
@@ -99,6 +167,9 @@ const openDialog = async (type: string = 'add', row?: any) => {
|
|||||||
// 编辑时填充数据
|
// 编辑时填充数据
|
||||||
if (type === 'edit' && row) {
|
if (type === 'edit' && row) {
|
||||||
form.id = row.id
|
form.id = row.id
|
||||||
|
form.schoolYear = row.schoolYear || ''
|
||||||
|
form.schoolTerm = row.schoolTerm || ''
|
||||||
|
form.classCode = row.classCode || ''
|
||||||
form.title = row.title || ''
|
form.title = row.title || ''
|
||||||
form.content = row.content || ''
|
form.content = row.content || ''
|
||||||
form.remarks = row.remarks || ''
|
form.remarks = row.remarks || ''
|
||||||
@@ -108,6 +179,9 @@ const openDialog = async (type: string = 'add', row?: any) => {
|
|||||||
loading.value = true
|
loading.value = true
|
||||||
getDetail(row.id).then((res: any) => {
|
getDetail(row.id).then((res: any) => {
|
||||||
if (res.data) {
|
if (res.data) {
|
||||||
|
form.schoolYear = res.data.schoolYear || form.schoolYear
|
||||||
|
form.schoolTerm = res.data.schoolTerm || form.schoolTerm
|
||||||
|
form.classCode = res.data.classCode || form.classCode
|
||||||
form.content = res.data.content || ''
|
form.content = res.data.content || ''
|
||||||
form.remarks = res.data.remarks || ''
|
form.remarks = res.data.remarks || ''
|
||||||
}
|
}
|
||||||
@@ -119,6 +193,43 @@ const openDialog = async (type: string = 'add', row?: any) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 学年列表(班级管理-学年接口)
|
||||||
|
const getSchoolYearList = async () => {
|
||||||
|
try {
|
||||||
|
const res = await queryAllSchoolYear()
|
||||||
|
schoolYearList.value = res?.data && Array.isArray(res.data) ? res.data : []
|
||||||
|
} catch (err) {
|
||||||
|
schoolYearList.value = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 学期字典(系统通用)
|
||||||
|
const getSchoolTermDict = async () => {
|
||||||
|
try {
|
||||||
|
const res = await getDicts('school_term')
|
||||||
|
if (res?.data && Array.isArray(res.data)) {
|
||||||
|
schoolTermList.value = res.data.map((item: any) => ({
|
||||||
|
label: item.label ?? item.dictLabel ?? item.name,
|
||||||
|
value: item.value ?? item.dictValue ?? item.code
|
||||||
|
}))
|
||||||
|
} else {
|
||||||
|
schoolTermList.value = []
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
schoolTermList.value = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 班级列表(班级管理目录下)
|
||||||
|
const getClassListData = async () => {
|
||||||
|
try {
|
||||||
|
const res = await getClassListByRole()
|
||||||
|
classList.value = res?.data && Array.isArray(res.data) ? res.data : []
|
||||||
|
} catch (err) {
|
||||||
|
classList.value = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 提交表单
|
// 提交表单
|
||||||
const onSubmit = async () => {
|
const onSubmit = async () => {
|
||||||
if (!dataFormRef.value) return
|
if (!dataFormRef.value) return
|
||||||
@@ -129,6 +240,9 @@ const onSubmit = async () => {
|
|||||||
loading.value = true
|
loading.value = true
|
||||||
try {
|
try {
|
||||||
const submitData = {
|
const submitData = {
|
||||||
|
schoolYear: form.schoolYear,
|
||||||
|
schoolTerm: form.schoolTerm,
|
||||||
|
classCode: form.classCode,
|
||||||
title: form.title,
|
title: form.title,
|
||||||
content: form.content,
|
content: form.content,
|
||||||
remarks: form.remarks
|
remarks: form.remarks
|
||||||
@@ -154,6 +268,13 @@ const onSubmit = async () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 初始化:加载学年、学期、班级
|
||||||
|
onMounted(() => {
|
||||||
|
getSchoolYearList()
|
||||||
|
getSchoolTermDict()
|
||||||
|
getClassListData()
|
||||||
|
})
|
||||||
|
|
||||||
// 暴露方法
|
// 暴露方法
|
||||||
defineExpose({
|
defineExpose({
|
||||||
openDialog
|
openDialog
|
||||||
|
|||||||
@@ -79,7 +79,7 @@
|
|||||||
<script setup lang="ts" name="ClassroomBaseArrangeDialog">
|
<script setup lang="ts" name="ClassroomBaseArrangeDialog">
|
||||||
import { ref, reactive, nextTick, onMounted } from 'vue'
|
import { ref, reactive, nextTick, onMounted } from 'vue'
|
||||||
import { useMessage } from '/@/hooks/message'
|
import { useMessage } from '/@/hooks/message'
|
||||||
import { editObj } from '/@/api/stuwork/classroombase'
|
import { addClassRoomAssign } from '/@/api/stuwork/teachclassroomassign'
|
||||||
import { queryAllClass } from '/@/api/basic/basicclass'
|
import { queryAllClass } from '/@/api/basic/basicclass'
|
||||||
import { getClassRoomList } from '/@/api/stuwork/teachclassroom'
|
import { getClassRoomList } from '/@/api/stuwork/teachclassroom'
|
||||||
import { getBuildingList } from '/@/api/stuwork/teachbuilding'
|
import { getBuildingList } from '/@/api/stuwork/teachbuilding'
|
||||||
@@ -150,17 +150,16 @@ const onSubmit = async () => {
|
|||||||
|
|
||||||
loading.value = true
|
loading.value = true
|
||||||
try {
|
try {
|
||||||
await editObj({
|
await addClassRoomAssign({
|
||||||
id: form.id,
|
|
||||||
buildingNo: form.buildingNo,
|
buildingNo: form.buildingNo,
|
||||||
classCode: form.classCode,
|
position: form.position,
|
||||||
position: form.position
|
classCode: form.classCode
|
||||||
})
|
})
|
||||||
useMessage().success('教室安排成功')
|
useMessage().success('教室安排成功')
|
||||||
visible.value = false
|
visible.value = false
|
||||||
emit('refresh')
|
emit('refresh')
|
||||||
} catch (err: any) {
|
} catch (_err) {
|
||||||
useMessage().error(err.msg || '教室安排失败')
|
// 错误由 request 拦截器统一提示
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false
|
loading.value = false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,40 +45,6 @@
|
|||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="24" class="mb20">
|
|
||||||
<el-form-item label="学院" prop="deptCode">
|
|
||||||
<el-select
|
|
||||||
v-model="form.deptCode"
|
|
||||||
placeholder="请选择学院"
|
|
||||||
clearable
|
|
||||||
filterable
|
|
||||||
style="width: 100%">
|
|
||||||
<el-option
|
|
||||||
v-for="item in deptList"
|
|
||||||
:key="item.deptCode"
|
|
||||||
:label="item.deptName"
|
|
||||||
:value="item.deptCode">
|
|
||||||
</el-option>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="24" class="mb20">
|
|
||||||
<el-form-item label="班级" prop="classNo">
|
|
||||||
<el-select
|
|
||||||
v-model="form.classNo"
|
|
||||||
placeholder="请选择班级"
|
|
||||||
clearable
|
|
||||||
filterable
|
|
||||||
style="width: 100%">
|
|
||||||
<el-option
|
|
||||||
v-for="item in classList"
|
|
||||||
:key="item.classCode"
|
|
||||||
:label="item.classNo"
|
|
||||||
:value="item.classNo">
|
|
||||||
</el-option>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
</el-row>
|
||||||
</el-form>
|
</el-form>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
@@ -95,8 +61,6 @@ import { ref, reactive, nextTick, onMounted } from 'vue'
|
|||||||
import { useMessage } from '/@/hooks/message'
|
import { useMessage } from '/@/hooks/message'
|
||||||
import { initObj } from '/@/api/stuwork/classtheme'
|
import { initObj } from '/@/api/stuwork/classtheme'
|
||||||
import { queryAllSchoolYear } from '/@/api/basic/basicyear'
|
import { queryAllSchoolYear } from '/@/api/basic/basicyear'
|
||||||
import { getClassListByRole } from '/@/api/basic/basicclass'
|
|
||||||
import { getDeptList } from '/@/api/basic/basicclass'
|
|
||||||
import { getDicts } from '/@/api/admin/dict'
|
import { getDicts } from '/@/api/admin/dict'
|
||||||
|
|
||||||
const emit = defineEmits(['refresh'])
|
const emit = defineEmits(['refresh'])
|
||||||
@@ -107,15 +71,11 @@ const visible = ref(false)
|
|||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const schoolYearList = ref<any[]>([])
|
const schoolYearList = ref<any[]>([])
|
||||||
const schoolTermList = ref<any[]>([])
|
const schoolTermList = ref<any[]>([])
|
||||||
const deptList = ref<any[]>([])
|
|
||||||
const classList = ref<any[]>([])
|
|
||||||
|
|
||||||
// 提交表单数据
|
// 提交表单数据
|
||||||
const form = reactive({
|
const form = reactive({
|
||||||
schoolYear: '',
|
schoolYear: '',
|
||||||
schoolTerm: '',
|
schoolTerm: ''
|
||||||
deptCode: '',
|
|
||||||
classNo: ''
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// 定义校验规则
|
// 定义校验规则
|
||||||
@@ -125,12 +85,6 @@ const dataRules = {
|
|||||||
],
|
],
|
||||||
schoolTerm: [
|
schoolTerm: [
|
||||||
{ required: true, message: '请选择学期', trigger: 'change' }
|
{ required: true, message: '请选择学期', trigger: 'change' }
|
||||||
],
|
|
||||||
deptCode: [
|
|
||||||
{ required: true, message: '请选择学院', trigger: 'change' }
|
|
||||||
],
|
|
||||||
classNo: [
|
|
||||||
{ required: true, message: '请选择班级', trigger: 'change' }
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -141,8 +95,6 @@ const openDialog = () => {
|
|||||||
dataFormRef.value?.resetFields()
|
dataFormRef.value?.resetFields()
|
||||||
form.schoolYear = ''
|
form.schoolYear = ''
|
||||||
form.schoolTerm = ''
|
form.schoolTerm = ''
|
||||||
form.deptCode = ''
|
|
||||||
form.classNo = ''
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -192,34 +144,10 @@ const getSchoolTermDict = async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取学院列表
|
|
||||||
const getDeptListData = async () => {
|
|
||||||
try {
|
|
||||||
const res = await getDeptList()
|
|
||||||
if (res.data && Array.isArray(res.data)) {
|
|
||||||
deptList.value = res.data
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取班级列表
|
|
||||||
const getClassListData = async () => {
|
|
||||||
try {
|
|
||||||
const res = await getClassListByRole()
|
|
||||||
if (res.data && Array.isArray(res.data)) {
|
|
||||||
classList.value = res.data
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 初始化
|
// 初始化
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getSchoolYearList()
|
getSchoolYearList()
|
||||||
getSchoolTermDict()
|
getSchoolTermDict()
|
||||||
getDeptListData()
|
|
||||||
getClassListData()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// 暴露方法
|
// 暴露方法
|
||||||
|
|||||||
@@ -89,9 +89,11 @@
|
|||||||
style="width: 100%">
|
style="width: 100%">
|
||||||
<el-option
|
<el-option
|
||||||
v-for="item in bedNoList"
|
v-for="item in bedNoList"
|
||||||
:key="item"
|
:key="item.bedNo"
|
||||||
:label="item"
|
:value="item.bedNo">
|
||||||
:value="item">
|
<span :class="{ 'bed-option-occupied': item.haveStudent }">
|
||||||
|
{{ item.bedNo }}{{ item.haveStudent ? ' (有人)' : '' }}
|
||||||
|
</span>
|
||||||
</el-option>
|
</el-option>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
@@ -138,7 +140,8 @@ const loading = ref(false)
|
|||||||
const classList = ref<any[]>([])
|
const classList = ref<any[]>([])
|
||||||
const studentList = ref<any[]>([])
|
const studentList = ref<any[]>([])
|
||||||
const roomList = ref<any[]>([])
|
const roomList = ref<any[]>([])
|
||||||
const bedNoList = ref<string[]>([])
|
// 床位列表:支持 haveStudent 标记(true=有人,false=无人)
|
||||||
|
const bedNoList = ref<Array<{ bedNo: string; haveStudent: boolean }>>([])
|
||||||
|
|
||||||
// 提交表单数据
|
// 提交表单数据
|
||||||
const form = reactive({
|
const form = reactive({
|
||||||
@@ -206,7 +209,7 @@ const handleStudentChange = (stuNo: string) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 房间号变化时获取床位号列表
|
// 房间号变化时获取床位号列表(支持 haveStudent:true=有人,false=无人)
|
||||||
const handleRoomChange = async (roomNo: string) => {
|
const handleRoomChange = async (roomNo: string) => {
|
||||||
if (!roomNo) {
|
if (!roomNo) {
|
||||||
bedNoList.value = []
|
bedNoList.value = []
|
||||||
@@ -214,20 +217,25 @@ const handleRoomChange = async (roomNo: string) => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const toBedItem = (item: any): { bedNo: string; haveStudent: boolean } => {
|
||||||
|
if (typeof item === 'number' || typeof item === 'string') {
|
||||||
|
return { bedNo: String(item), haveStudent: false }
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
bedNo: String(item?.bedNo ?? item?.value ?? item ?? ''),
|
||||||
|
haveStudent: !!(item && item.haveStudent === true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await fearchRoomStuNum(roomNo)
|
const res = await fearchRoomStuNum(roomNo)
|
||||||
if (res.data) {
|
if (res.data) {
|
||||||
// 根据返回的数据结构处理床位号列表
|
|
||||||
// 假设返回的是数字数组或对象数组
|
|
||||||
if (Array.isArray(res.data)) {
|
if (Array.isArray(res.data)) {
|
||||||
bedNoList.value = res.data.map((item: any) => {
|
bedNoList.value = res.data.map(toBedItem).filter((b) => b.bedNo)
|
||||||
if (typeof item === 'number' || typeof item === 'string') {
|
|
||||||
return String(item)
|
|
||||||
}
|
|
||||||
return String(item.bedNo || item.value || item)
|
|
||||||
})
|
|
||||||
} else if (res.data.bedNos && Array.isArray(res.data.bedNos)) {
|
} else if (res.data.bedNos && Array.isArray(res.data.bedNos)) {
|
||||||
bedNoList.value = res.data.bedNos.map((item: any) => String(item))
|
bedNoList.value = res.data.bedNos.map((item: any) =>
|
||||||
|
toBedItem(typeof item === 'object' ? item : { bedNo: String(item), haveStudent: false })
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
bedNoList.value = []
|
bedNoList.value = []
|
||||||
}
|
}
|
||||||
@@ -321,3 +329,8 @@ defineExpose({
|
|||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.bed-option-occupied {
|
||||||
|
color: var(--el-color-warning);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -211,9 +211,13 @@
|
|||||||
<el-icon><component :is="columnConfigMap[col.prop || '']?.icon || OfficeBuilding" /></el-icon>
|
<el-icon><component :is="columnConfigMap[col.prop || '']?.icon || OfficeBuilding" /></el-icon>
|
||||||
<span style="margin-left: 4px">{{ col.label }}</span>
|
<span style="margin-left: 4px">{{ col.label }}</span>
|
||||||
</template>
|
</template>
|
||||||
<!-- 床位号列特殊模板 -->
|
<!-- 床位号列:haveStudent 为 true 时变色标记有人 -->
|
||||||
<template v-if="col.prop === 'bedNo'" #default="scope">
|
<template v-if="col.prop === 'bedNo'" #default="scope">
|
||||||
<el-tag v-if="scope.row.bedNo" size="small" type="info" effect="plain">
|
<el-tag
|
||||||
|
v-if="scope.row.bedNo"
|
||||||
|
size="small"
|
||||||
|
:type="scope.row.haveStudent ? 'warning' : 'info'"
|
||||||
|
effect="plain">
|
||||||
{{ scope.row.bedNo }}
|
{{ scope.row.bedNo }}
|
||||||
</el-tag>
|
</el-tag>
|
||||||
<span v-else>-</span>
|
<span v-else>-</span>
|
||||||
@@ -274,6 +278,12 @@
|
|||||||
|
|
||||||
<!-- 转宿弹窗 -->
|
<!-- 转宿弹窗 -->
|
||||||
<TransferDialog ref="transferDialogRef" @refresh="getDataList" />
|
<TransferDialog ref="transferDialogRef" @refresh="getDataList" />
|
||||||
|
|
||||||
|
<!-- 宿舍互换弹窗 -->
|
||||||
|
<SwapDialog ref="swapDialogRef" @refresh="getDataList" />
|
||||||
|
|
||||||
|
<!-- 打印宿舍卡弹窗 -->
|
||||||
|
<PrintCardDialog ref="printCardDialogRef" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -281,7 +291,7 @@
|
|||||||
import { reactive, ref, onMounted, computed, nextTick } from 'vue'
|
import { reactive, ref, onMounted, computed, nextTick } from 'vue'
|
||||||
import { useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
import { BasicTableProps, useTable } from "/@/hooks/table";
|
import { BasicTableProps, useTable } from "/@/hooks/table";
|
||||||
import { fetchList, delObjs } from "/@/api/stuwork/dormroomstudent";
|
import { fetchList, delObjs, exportEmptyPeopleRoomExcel } from "/@/api/stuwork/dormroomstudent";
|
||||||
import { getDeptList } from "/@/api/basic/basicclass";
|
import { getDeptList } from "/@/api/basic/basicclass";
|
||||||
import { getBuildingList } from "/@/api/stuwork/dormbuilding";
|
import { getBuildingList } from "/@/api/stuwork/dormbuilding";
|
||||||
import { fetchDormRoomTreeList } from "/@/api/stuwork/dormroom";
|
import { fetchDormRoomTreeList } from "/@/api/stuwork/dormroom";
|
||||||
@@ -289,6 +299,8 @@ import { useMessage, useMessageBox } from "/@/hooks/message";
|
|||||||
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
||||||
import FormDialog from './form.vue';
|
import FormDialog from './form.vue';
|
||||||
import TransferDialog from './transfer.vue';
|
import TransferDialog from './transfer.vue';
|
||||||
|
import SwapDialog from './swap.vue';
|
||||||
|
import PrintCardDialog from './printCard.vue';
|
||||||
import TreeSelect from '/@/components/TreeSelect/index.vue';
|
import TreeSelect from '/@/components/TreeSelect/index.vue';
|
||||||
import { List, OfficeBuilding, House, Grid, UserFilled, Phone, CreditCard, Avatar, User, Setting, Menu, Search, Document } from '@element-plus/icons-vue'
|
import { List, OfficeBuilding, House, Grid, UserFilled, Phone, CreditCard, Avatar, User, Setting, Menu, Search, Document } from '@element-plus/icons-vue'
|
||||||
import { useTableColumnControl } from '/@/hooks/tableColumn'
|
import { useTableColumnControl } from '/@/hooks/tableColumn'
|
||||||
@@ -301,6 +313,8 @@ const route = useRoute()
|
|||||||
const formDialogRef = ref()
|
const formDialogRef = ref()
|
||||||
const columnControlRef = ref<any>()
|
const columnControlRef = ref<any>()
|
||||||
const transferDialogRef = ref()
|
const transferDialogRef = ref()
|
||||||
|
const swapDialogRef = ref()
|
||||||
|
const printCardDialogRef = ref()
|
||||||
const searchFormRef = ref()
|
const searchFormRef = ref()
|
||||||
const showSearch = ref(true)
|
const showSearch = ref(true)
|
||||||
const deptList = ref<any[]>([])
|
const deptList = ref<any[]>([])
|
||||||
@@ -425,29 +439,91 @@ const handleDormDataTypeChange = (dormdataType: string) => {
|
|||||||
getDormRoomTreeListData(dormdataType)
|
getDormRoomTreeListData(dormdataType)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 打印宿舍卡
|
// 打印宿舍卡(按房间号查询后打印)
|
||||||
const handlePrintCard = () => {
|
const handlePrintCard = () => {
|
||||||
useMessage().warning('功能开发中')
|
printCardDialogRef.value?.openDialog()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 宿舍互换
|
// 宿舍互换(两名学生互换宿舍)
|
||||||
const handleRoomSwap = () => {
|
const handleRoomSwap = () => {
|
||||||
useMessage().warning('功能开发中')
|
const query = {
|
||||||
|
deptCode: searchForm.deptCode,
|
||||||
|
buildingNo: searchForm.buildingNo,
|
||||||
|
gender: searchForm.gender,
|
||||||
|
roomNo: searchForm.roomNo || searchForm.roomNoInput,
|
||||||
|
classNo: searchForm.classNo,
|
||||||
|
stuNo: searchForm.stuNo,
|
||||||
|
realName: searchForm.realName
|
||||||
|
}
|
||||||
|
swapDialogRef.value?.openDialog(query)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 导出
|
// 导出:空 n 人宿舍导出(按当前筛选条件传参)
|
||||||
const handleExport = () => {
|
const handleExport = async () => {
|
||||||
useMessage().warning('功能开发中')
|
try {
|
||||||
|
const params = {
|
||||||
|
deptCode: searchForm.deptCode,
|
||||||
|
buildingNo: searchForm.buildingNo,
|
||||||
|
gender: searchForm.gender,
|
||||||
|
dormdataType: searchForm.dormdataType,
|
||||||
|
roomNo: searchForm.roomNo || searchForm.roomNoInput,
|
||||||
|
classNo: searchForm.classNo,
|
||||||
|
stuNo: searchForm.stuNo,
|
||||||
|
realName: searchForm.realName
|
||||||
|
}
|
||||||
|
const res = await exportEmptyPeopleRoomExcel(params)
|
||||||
|
const blob = res instanceof Blob ? res : new Blob([res as BlobPart], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' })
|
||||||
|
const url = window.URL.createObjectURL(blob)
|
||||||
|
const link = document.createElement('a')
|
||||||
|
link.href = url
|
||||||
|
link.download = `空宿舍导出_${Date.now()}.xlsx`
|
||||||
|
document.body.appendChild(link)
|
||||||
|
link.click()
|
||||||
|
document.body.removeChild(link)
|
||||||
|
window.URL.revokeObjectURL(url)
|
||||||
|
useMessage().success('导出成功')
|
||||||
|
} catch (err: any) {
|
||||||
|
useMessage().error(err?.msg || '导出失败')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 名单导出
|
// 名单导出:与导出共用空 n 人宿舍导出接口,文件名区分
|
||||||
const handleExportList = () => {
|
const handleExportList = async () => {
|
||||||
useMessage().warning('功能开发中')
|
try {
|
||||||
|
const params = {
|
||||||
|
deptCode: searchForm.deptCode,
|
||||||
|
buildingNo: searchForm.buildingNo,
|
||||||
|
gender: searchForm.gender,
|
||||||
|
dormdataType: searchForm.dormdataType,
|
||||||
|
roomNo: searchForm.roomNo || searchForm.roomNoInput,
|
||||||
|
classNo: searchForm.classNo,
|
||||||
|
stuNo: searchForm.stuNo,
|
||||||
|
realName: searchForm.realName
|
||||||
|
}
|
||||||
|
const res = await exportEmptyPeopleRoomExcel(params)
|
||||||
|
const blob = res instanceof Blob ? res : new Blob([res as BlobPart], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' })
|
||||||
|
const url = window.URL.createObjectURL(blob)
|
||||||
|
const link = document.createElement('a')
|
||||||
|
link.href = url
|
||||||
|
link.download = `住宿学生名单_${Date.now()}.xlsx`
|
||||||
|
document.body.appendChild(link)
|
||||||
|
link.click()
|
||||||
|
document.body.removeChild(link)
|
||||||
|
window.URL.revokeObjectURL(url)
|
||||||
|
useMessage().success('导出成功')
|
||||||
|
} catch (err: any) {
|
||||||
|
useMessage().error(err?.msg || '导出失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 编辑(与转宿共用接口 edit,修改房间/床位/是否舍长)
|
||||||
|
const handleEdit = (row: any) => {
|
||||||
|
transferDialogRef.value?.openDialog(row)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 转宿
|
// 转宿
|
||||||
const handleTransfer = (row: any) => {
|
const handleTransfer = (row: any) => {
|
||||||
transferDialogRef.value.openDialog(row)
|
transferDialogRef.value?.openDialog(row)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 退宿
|
// 退宿
|
||||||
|
|||||||
152
src/views/stuwork/dormroomstudent/printCard.vue
Normal file
152
src/views/stuwork/dormroomstudent/printCard.vue
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog
|
||||||
|
title="打印宿舍卡"
|
||||||
|
v-model="visible"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
width="640px"
|
||||||
|
destroy-on-close
|
||||||
|
@closed="onClosed">
|
||||||
|
<div v-loading="loading">
|
||||||
|
<el-form :inline="true" class="mb16">
|
||||||
|
<el-form-item label="房间号">
|
||||||
|
<el-input v-model="roomNo" placeholder="请输入房间号" clearable style="width: 180px" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" @click="handleFetch">查询并预览</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<div v-if="printData.length" ref="printAreaRef" class="print-area">
|
||||||
|
<div v-for="(room, idx) in printData" :key="idx" class="room-card mb16">
|
||||||
|
<div class="room-title">房间号:{{ room.roomNo }}</div>
|
||||||
|
<table class="print-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>姓名</th>
|
||||||
|
<th>学号</th>
|
||||||
|
<th>床位</th>
|
||||||
|
<th>是否舍长</th>
|
||||||
|
<th>班级</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr v-for="(s, i) in (room.dormRoomStudentVOList || [])" :key="i">
|
||||||
|
<td>{{ s.realName }}</td>
|
||||||
|
<td>{{ s.stuNo }}</td>
|
||||||
|
<td>{{ s.bedNo }}</td>
|
||||||
|
<td>{{ s.isLeader === '1' ? '是' : '否' }}</td>
|
||||||
|
<td>{{ s.className || s.classNo }}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<template #footer>
|
||||||
|
<span class="dialog-footer">
|
||||||
|
<el-button @click="visible = false">关 闭</el-button>
|
||||||
|
<el-button type="primary" @click="handlePrint" :disabled="!printData.length">打 印</el-button>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts" name="DormRoomStudentPrintCard">
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { useMessage } from '/@/hooks/message'
|
||||||
|
import { printDormRoomData } from '/@/api/stuwork/dormroomstudent'
|
||||||
|
|
||||||
|
const visible = ref(false)
|
||||||
|
const loading = ref(false)
|
||||||
|
const roomNo = ref('')
|
||||||
|
const printData = ref<any[]>([])
|
||||||
|
const printAreaRef = ref<HTMLElement>()
|
||||||
|
|
||||||
|
const openDialog = (initialRoomNo?: string) => {
|
||||||
|
visible.value = true
|
||||||
|
roomNo.value = initialRoomNo || ''
|
||||||
|
printData.value = []
|
||||||
|
}
|
||||||
|
|
||||||
|
const onClosed = () => {
|
||||||
|
printData.value = []
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleFetch = async () => {
|
||||||
|
const no = (roomNo.value || '').trim()
|
||||||
|
if (!no) {
|
||||||
|
useMessage().warning('请输入房间号')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
const res = await printDormRoomData(no)
|
||||||
|
const data = res?.data ?? res
|
||||||
|
printData.value = Array.isArray(data) ? data : (data ? [data] : [])
|
||||||
|
if (!printData.value.length) {
|
||||||
|
useMessage().info('该房间暂无数据')
|
||||||
|
}
|
||||||
|
} catch (err: any) {
|
||||||
|
useMessage().error(err?.msg || '查询失败')
|
||||||
|
printData.value = []
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handlePrint = () => {
|
||||||
|
if (!printData.value.length) return
|
||||||
|
const win = window.open('', '_blank')
|
||||||
|
if (!win) {
|
||||||
|
useMessage().error('无法打开打印窗口')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const el = printAreaRef.value
|
||||||
|
if (el) {
|
||||||
|
win.document.write(`
|
||||||
|
<!DOCTYPE html><html><head><meta charset="utf-8"><title>宿舍卡</title>
|
||||||
|
<style>
|
||||||
|
table { border-collapse: collapse; width: 100%; }
|
||||||
|
th, td { border: 1px solid #333; padding: 6px 8px; text-align: center; }
|
||||||
|
.room-title { font-weight: bold; margin-bottom: 8px; }
|
||||||
|
</style>
|
||||||
|
</head><body>${el.innerHTML}</body></html>
|
||||||
|
`)
|
||||||
|
win.document.close()
|
||||||
|
win.focus()
|
||||||
|
setTimeout(() => {
|
||||||
|
win.print()
|
||||||
|
win.close()
|
||||||
|
}, 300)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({ openDialog })
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.print-area {
|
||||||
|
max-height: 60vh;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
.room-card {
|
||||||
|
padding: 12px;
|
||||||
|
border: 1px solid var(--el-border-color);
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
.room-title {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
.print-table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
th,
|
||||||
|
td {
|
||||||
|
border: 1px solid var(--el-border-color);
|
||||||
|
padding: 6px 8px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.mb16 {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
136
src/views/stuwork/dormroomstudent/swap.vue
Normal file
136
src/views/stuwork/dormroomstudent/swap.vue
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog
|
||||||
|
title="宿舍互换"
|
||||||
|
v-model="visible"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
draggable
|
||||||
|
width="520px">
|
||||||
|
<el-form
|
||||||
|
ref="formRef"
|
||||||
|
:model="form"
|
||||||
|
:rules="rules"
|
||||||
|
label-width="100px"
|
||||||
|
:validate-on-rule-change="false"
|
||||||
|
v-loading="loading">
|
||||||
|
<el-form-item label="学生一" prop="sourceSutNo">
|
||||||
|
<el-select
|
||||||
|
v-model="form.sourceSutNo"
|
||||||
|
placeholder="请选择学生(学号)"
|
||||||
|
clearable
|
||||||
|
filterable
|
||||||
|
style="width: 100%"
|
||||||
|
@change="onSourceChange">
|
||||||
|
<el-option
|
||||||
|
v-for="item in studentOptions"
|
||||||
|
:key="item.stuNo"
|
||||||
|
:label="`${item.realName}(${item.stuNo})${item.roomNo || ''}`"
|
||||||
|
:value="item.stuNo" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="学生二" prop="targetStuNO">
|
||||||
|
<el-select
|
||||||
|
v-model="form.targetStuNO"
|
||||||
|
placeholder="请选择学生(学号)"
|
||||||
|
clearable
|
||||||
|
filterable
|
||||||
|
style="width: 100%"
|
||||||
|
@change="onTargetChange">
|
||||||
|
<el-option
|
||||||
|
v-for="item in studentOptions"
|
||||||
|
:key="item.stuNo"
|
||||||
|
:label="`${item.realName}(${item.stuNo})${item.roomNo || ''}`"
|
||||||
|
:value="item.stuNo" />
|
||||||
|
</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" :loading="submitting">确 认 互 换</el-button>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts" name="DormRoomStudentSwapDialog">
|
||||||
|
import { ref, reactive } from 'vue'
|
||||||
|
import { useMessage } from '/@/hooks/message'
|
||||||
|
import { exchangeRoom } from '/@/api/stuwork/dormroomstudent'
|
||||||
|
import { fetchList } from '/@/api/stuwork/dormroomstudent'
|
||||||
|
|
||||||
|
const emit = defineEmits(['refresh'])
|
||||||
|
|
||||||
|
const formRef = ref()
|
||||||
|
const visible = ref(false)
|
||||||
|
const loading = ref(false)
|
||||||
|
const submitting = ref(false)
|
||||||
|
const studentOptions = ref<any[]>([])
|
||||||
|
|
||||||
|
const form = reactive({
|
||||||
|
sourceSutNo: '',
|
||||||
|
targetStuNO: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
const rules = {
|
||||||
|
sourceSutNo: [{ required: true, message: '请选择学生一', trigger: 'change' }],
|
||||||
|
targetStuNO: [{ required: true, message: '请选择学生二', trigger: 'change' }]
|
||||||
|
}
|
||||||
|
|
||||||
|
const onSourceChange = () => {
|
||||||
|
if (form.sourceSutNo && form.targetStuNO && form.sourceSutNo === form.targetStuNO) {
|
||||||
|
form.targetStuNO = ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const onTargetChange = () => {
|
||||||
|
if (form.sourceSutNo && form.targetStuNO && form.sourceSutNo === form.targetStuNO) {
|
||||||
|
form.sourceSutNo = ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const openDialog = async (queryParams?: any) => {
|
||||||
|
visible.value = true
|
||||||
|
form.sourceSutNo = ''
|
||||||
|
form.targetStuNO = ''
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
const res = await fetchList({
|
||||||
|
current: 1,
|
||||||
|
size: 500,
|
||||||
|
...queryParams
|
||||||
|
})
|
||||||
|
const list = res?.data?.records ?? res?.records ?? []
|
||||||
|
studentOptions.value = Array.isArray(list) ? list : []
|
||||||
|
} catch (err) {
|
||||||
|
studentOptions.value = []
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const onSubmit = async () => {
|
||||||
|
if (!formRef.value) return
|
||||||
|
await formRef.value.validate(async (valid: boolean) => {
|
||||||
|
if (!valid) return
|
||||||
|
if (form.sourceSutNo === form.targetStuNO) {
|
||||||
|
useMessage().warning('请选择两名不同学生')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
submitting.value = true
|
||||||
|
try {
|
||||||
|
await exchangeRoom({
|
||||||
|
sourceSutNo: form.sourceSutNo,
|
||||||
|
targetStuNO: form.targetStuNO
|
||||||
|
})
|
||||||
|
useMessage().success('互换成功')
|
||||||
|
visible.value = false
|
||||||
|
emit('refresh')
|
||||||
|
} catch (err: any) {
|
||||||
|
console.error(err)
|
||||||
|
} finally {
|
||||||
|
submitting.value = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({ openDialog })
|
||||||
|
</script>
|
||||||
@@ -41,9 +41,11 @@
|
|||||||
style="width: 100%">
|
style="width: 100%">
|
||||||
<el-option
|
<el-option
|
||||||
v-for="item in bedNoList"
|
v-for="item in bedNoList"
|
||||||
:key="item"
|
:key="item.bedNo"
|
||||||
:label="item"
|
:value="item.bedNo">
|
||||||
:value="item">
|
<span :class="{ 'bed-option-occupied': item.haveStudent }">
|
||||||
|
{{ item.bedNo }}{{ item.haveStudent ? ' (有人)' : '' }}
|
||||||
|
</span>
|
||||||
</el-option>
|
</el-option>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
@@ -95,7 +97,8 @@ const dataFormRef = ref()
|
|||||||
const visible = ref(false)
|
const visible = ref(false)
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const roomList = ref<any[]>([])
|
const roomList = ref<any[]>([])
|
||||||
const bedNoList = ref<string[]>([])
|
// 床位列表:支持 haveStudent 标记(true=有人,false=无人)
|
||||||
|
const bedNoList = ref<Array<{ bedNo: string; haveStudent: boolean }>>([])
|
||||||
|
|
||||||
// 提交表单数据
|
// 提交表单数据
|
||||||
const form = reactive({
|
const form = reactive({
|
||||||
@@ -119,7 +122,7 @@ const dataRules = {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
// 房间号变化时获取床位号列表
|
// 房间号变化时获取床位号列表(支持 haveStudent:true=有人,false=无人)
|
||||||
const handleRoomChange = async (roomNo: string) => {
|
const handleRoomChange = async (roomNo: string) => {
|
||||||
if (!roomNo) {
|
if (!roomNo) {
|
||||||
bedNoList.value = []
|
bedNoList.value = []
|
||||||
@@ -127,18 +130,25 @@ const handleRoomChange = async (roomNo: string) => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const toBedItem = (item: any): { bedNo: string; haveStudent: boolean } => {
|
||||||
|
if (typeof item === 'number' || typeof item === 'string') {
|
||||||
|
return { bedNo: String(item), haveStudent: false }
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
bedNo: String(item?.bedNo ?? item?.value ?? item ?? ''),
|
||||||
|
haveStudent: !!(item && item.haveStudent === true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await fearchRoomStuNum(roomNo)
|
const res = await fearchRoomStuNum(roomNo)
|
||||||
if (res.data) {
|
if (res.data) {
|
||||||
if (Array.isArray(res.data)) {
|
if (Array.isArray(res.data)) {
|
||||||
bedNoList.value = res.data.map((item: any) => {
|
bedNoList.value = res.data.map(toBedItem).filter((b) => b.bedNo)
|
||||||
if (typeof item === 'number' || typeof item === 'string') {
|
|
||||||
return String(item)
|
|
||||||
}
|
|
||||||
return String(item.bedNo || item.value || item)
|
|
||||||
})
|
|
||||||
} else if (res.data.bedNos && Array.isArray(res.data.bedNos)) {
|
} else if (res.data.bedNos && Array.isArray(res.data.bedNos)) {
|
||||||
bedNoList.value = res.data.bedNos.map((item: any) => String(item))
|
bedNoList.value = res.data.bedNos.map((item: any) =>
|
||||||
|
toBedItem(typeof item === 'object' ? item : { bedNo: String(item), haveStudent: false })
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
bedNoList.value = []
|
bedNoList.value = []
|
||||||
}
|
}
|
||||||
@@ -157,20 +167,20 @@ const openDialog = async (row: any) => {
|
|||||||
visible.value = true
|
visible.value = true
|
||||||
|
|
||||||
// 重置表单数据
|
// 重置表单数据
|
||||||
nextTick(() => {
|
await nextTick()
|
||||||
dataFormRef.value?.resetFields()
|
dataFormRef.value?.resetFields()
|
||||||
form.id = row.id || ''
|
form.id = row.id || ''
|
||||||
form.roomNo = row.roomNo || ''
|
form.roomNo = row.roomNo || ''
|
||||||
form.bedNo = row.bedNo || ''
|
form.bedNo = row.bedNo || ''
|
||||||
form.stuNo = row.stuNo || ''
|
form.stuNo = row.stuNo || ''
|
||||||
form.isLeader = row.isLeader || '0'
|
form.isLeader = row.isLeader || '0'
|
||||||
bedNoList.value = []
|
bedNoList.value = []
|
||||||
|
|
||||||
// 如果有房间号,获取床位号列表
|
// 如果有房间号,先拉取床位列表再回填床位号(避免 handleRoomChange 清空 bedNo)
|
||||||
if (form.roomNo) {
|
if (form.roomNo) {
|
||||||
handleRoomChange(form.roomNo)
|
await handleRoomChange(form.roomNo)
|
||||||
}
|
form.bedNo = row.bedNo || ''
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 提交表单
|
// 提交表单
|
||||||
@@ -224,3 +234,8 @@ defineExpose({
|
|||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.bed-option-occupied {
|
||||||
|
color: var(--el-color-warning);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
227
src/views/stuwork/gradustu/analyse.vue
Normal file
227
src/views/stuwork/gradustu/analyse.vue
Normal file
@@ -0,0 +1,227 @@
|
|||||||
|
<template>
|
||||||
|
<div class="modern-page-container">
|
||||||
|
<div class="page-wrapper">
|
||||||
|
<!-- 筛选 -->
|
||||||
|
<el-card class="search-card" shadow="never">
|
||||||
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="card-title">
|
||||||
|
<el-icon class="title-icon"><Search /></el-icon>
|
||||||
|
统计条件
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-form :model="queryForm" :inline="true" class="search-form">
|
||||||
|
<el-form-item label="毕业年份" prop="graduYear">
|
||||||
|
<el-select
|
||||||
|
v-model="queryForm.graduYear"
|
||||||
|
placeholder="请选择毕业年份"
|
||||||
|
clearable
|
||||||
|
style="width: 160px">
|
||||||
|
<el-option
|
||||||
|
v-for="y in graduYearOptions"
|
||||||
|
:key="y"
|
||||||
|
:label="y + '年'"
|
||||||
|
:value="y" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" icon="Search" @click="loadStatistics">查询统计</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<!-- 汇总卡片 -->
|
||||||
|
<el-row :gutter="16" class="summary-row">
|
||||||
|
<el-col :span="6">
|
||||||
|
<el-card shadow="hover" class="stat-card">
|
||||||
|
<div class="stat-label">毕业生总数</div>
|
||||||
|
<div class="stat-value">{{ summary.total }}</div>
|
||||||
|
</el-card>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="6">
|
||||||
|
<el-card shadow="hover" class="stat-card stat-success">
|
||||||
|
<div class="stat-label">确认毕业</div>
|
||||||
|
<div class="stat-value">{{ summary.confirmed }}</div>
|
||||||
|
</el-card>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="6">
|
||||||
|
<el-card shadow="hover" class="stat-card stat-warning">
|
||||||
|
<div class="stat-label">待确认</div>
|
||||||
|
<div class="stat-value">{{ summary.pending }}</div>
|
||||||
|
</el-card>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="6">
|
||||||
|
<el-card shadow="hover" class="stat-card stat-danger">
|
||||||
|
<div class="stat-label">不可毕业</div>
|
||||||
|
<div class="stat-value">{{ summary.rejected }}</div>
|
||||||
|
</el-card>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<!-- 按学院统计表格 -->
|
||||||
|
<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-table
|
||||||
|
:data="deptStats"
|
||||||
|
v-loading="loading"
|
||||||
|
stripe
|
||||||
|
border
|
||||||
|
class="modern-table">
|
||||||
|
<el-table-column type="index" label="序号" width="60" align="center" />
|
||||||
|
<el-table-column prop="deptName" label="学院" min-width="140" show-overflow-tooltip />
|
||||||
|
<el-table-column prop="total" label="应毕业人数" width="110" align="center" />
|
||||||
|
<el-table-column prop="pending" label="待确认" width="90" align="center" />
|
||||||
|
<el-table-column prop="confirmed" label="确认毕业" width="100" align="center" />
|
||||||
|
<el-table-column prop="rejected" label="不可毕业" width="100" align="center" />
|
||||||
|
<el-table-column prop="completionRate" label="完成率" width="100" align="center">
|
||||||
|
<template #default="scope">
|
||||||
|
<span :class="completionRateClass(scope.row.completionRate)">
|
||||||
|
{{ scope.row.completionRate }}
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<template #empty>
|
||||||
|
<el-empty description="请选择毕业年份并点击「查询统计」" :image-size="100" />
|
||||||
|
</template>
|
||||||
|
</el-card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts" name="GradustuAnalyse">
|
||||||
|
import { reactive, ref, computed, onMounted } from 'vue'
|
||||||
|
import { useMessage } from '/@/hooks/message'
|
||||||
|
import { fetchListForAnalyse } from '/@/api/stuwork/gradustu'
|
||||||
|
import { Search, Document } from '@element-plus/icons-vue'
|
||||||
|
|
||||||
|
const queryForm = reactive({
|
||||||
|
graduYear: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
const loading = ref(false)
|
||||||
|
const rawList = ref<any[]>([])
|
||||||
|
|
||||||
|
const graduYearOptions = computed(() => {
|
||||||
|
const y = new Date().getFullYear()
|
||||||
|
return Array.from({ length: 11 }, (_, i) => y - 5 + i)
|
||||||
|
})
|
||||||
|
|
||||||
|
// 汇总:总人数、确认毕业、待确认、不可毕业
|
||||||
|
const summary = computed(() => {
|
||||||
|
const list = rawList.value
|
||||||
|
let pending = 0
|
||||||
|
let confirmed = 0
|
||||||
|
let rejected = 0
|
||||||
|
list.forEach((item: any) => {
|
||||||
|
const s = String(item.status ?? '')
|
||||||
|
if (s === '0') pending++
|
||||||
|
else if (s === '1') confirmed++
|
||||||
|
else if (s === '-1') rejected++
|
||||||
|
})
|
||||||
|
return {
|
||||||
|
total: list.length,
|
||||||
|
pending,
|
||||||
|
confirmed,
|
||||||
|
rejected
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 按学院聚合
|
||||||
|
const deptStats = computed(() => {
|
||||||
|
const list = rawList.value
|
||||||
|
const map: Record<string, { deptCode: string; deptName: string; total: number; pending: number; confirmed: number; rejected: number }> = {}
|
||||||
|
list.forEach((item: any) => {
|
||||||
|
const code = item.deptCode || '未知'
|
||||||
|
const name = item.deptName || item.deptCode || '未知'
|
||||||
|
if (!map[code]) {
|
||||||
|
map[code] = { deptCode: code, deptName: name, total: 0, pending: 0, confirmed: 0, rejected: 0 }
|
||||||
|
}
|
||||||
|
const row = map[code]
|
||||||
|
row.total++
|
||||||
|
const s = String(item.status ?? '')
|
||||||
|
if (s === '0') row.pending++
|
||||||
|
else if (s === '1') row.confirmed++
|
||||||
|
else if (s === '-1') row.rejected++
|
||||||
|
})
|
||||||
|
return Object.values(map).map((row) => ({
|
||||||
|
...row,
|
||||||
|
completionRate: row.total > 0 ? ((row.confirmed / row.total) * 100).toFixed(1) + '%' : '0%'
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
|
||||||
|
const completionRateClass = (rate: string) => {
|
||||||
|
const num = parseFloat(rate)
|
||||||
|
if (num >= 100) return 'rate-high'
|
||||||
|
if (num >= 80) return 'rate-mid'
|
||||||
|
return 'rate-low'
|
||||||
|
}
|
||||||
|
|
||||||
|
const loadStatistics = async () => {
|
||||||
|
if (!queryForm.graduYear) {
|
||||||
|
useMessage().warning('请选择毕业年份')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
const list = await fetchListForAnalyse(queryForm.graduYear)
|
||||||
|
rawList.value = list
|
||||||
|
} catch (err: any) {
|
||||||
|
useMessage().error(err.msg || '获取数据失败')
|
||||||
|
rawList.value = []
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
queryForm.graduYear = String(new Date().getFullYear())
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
@import '/@/assets/styles/modern-page.scss';
|
||||||
|
|
||||||
|
.summary-row {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
.stat-card {
|
||||||
|
text-align: center;
|
||||||
|
.stat-label {
|
||||||
|
font-size: 14px;
|
||||||
|
color: var(--el-text-color-secondary);
|
||||||
|
}
|
||||||
|
.stat-value {
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
&.stat-success .stat-value {
|
||||||
|
color: var(--el-color-success);
|
||||||
|
}
|
||||||
|
&.stat-warning .stat-value {
|
||||||
|
color: var(--el-color-warning);
|
||||||
|
}
|
||||||
|
&.stat-danger .stat-value {
|
||||||
|
color: var(--el-color-danger);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.rate-high {
|
||||||
|
color: var(--el-color-success);
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
.rate-mid {
|
||||||
|
color: var(--el-color-warning);
|
||||||
|
}
|
||||||
|
.rate-low {
|
||||||
|
color: var(--el-color-danger);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
342
src/views/stuwork/gradustu/index.vue
Normal file
342
src/views/stuwork/gradustu/index.vue
Normal file
@@ -0,0 +1,342 @@
|
|||||||
|
<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="graduYear">
|
||||||
|
<el-select
|
||||||
|
v-model="searchForm.graduYear"
|
||||||
|
placeholder="请选择毕业年份"
|
||||||
|
clearable
|
||||||
|
filterable
|
||||||
|
style="width: 140px">
|
||||||
|
<el-option
|
||||||
|
v-for="y in graduYearOptions"
|
||||||
|
:key="y"
|
||||||
|
:label="y + '年'"
|
||||||
|
:value="y" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="毕业状态" prop="status">
|
||||||
|
<el-select
|
||||||
|
v-model="searchForm.status"
|
||||||
|
placeholder="请选择"
|
||||||
|
clearable
|
||||||
|
style="width: 140px">
|
||||||
|
<el-option label="待确认" value="0" />
|
||||||
|
<el-option label="确认毕业" value="1" />
|
||||||
|
<el-option label="不可毕业" value="-1" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="毕业类型" prop="type">
|
||||||
|
<el-select
|
||||||
|
v-model="searchForm.type"
|
||||||
|
placeholder="请选择"
|
||||||
|
clearable
|
||||||
|
style="width: 120px">
|
||||||
|
<el-option label="段段清" value="1" />
|
||||||
|
<el-option label="正常毕业" value="2" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="学号" prop="stuNo">
|
||||||
|
<el-input
|
||||||
|
v-model="searchForm.stuNo"
|
||||||
|
placeholder="请输入学号"
|
||||||
|
clearable
|
||||||
|
style="width: 140px" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="姓名" prop="realName">
|
||||||
|
<el-input
|
||||||
|
v-model="searchForm.realName"
|
||||||
|
placeholder="请输入姓名"
|
||||||
|
clearable
|
||||||
|
style="width: 120px" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="学院" prop="deptCode">
|
||||||
|
<el-select
|
||||||
|
v-model="searchForm.deptCode"
|
||||||
|
placeholder="请选择学院"
|
||||||
|
clearable
|
||||||
|
filterable
|
||||||
|
style="width: 160px">
|
||||||
|
<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="班号" prop="classNo">
|
||||||
|
<el-input
|
||||||
|
v-model="searchForm.classNo"
|
||||||
|
placeholder="请输入班号"
|
||||||
|
clearable
|
||||||
|
style="width: 120px" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" icon="Search" @click="handleSearch">查询</el-button>
|
||||||
|
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<!-- 内容卡片 -->
|
||||||
|
<el-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"
|
||||||
|
:loading="makeLoading"
|
||||||
|
@click="handleMakeGraduStu">
|
||||||
|
生成毕业生信息
|
||||||
|
</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"
|
||||||
|
:min-width="col.minWidth"
|
||||||
|
:width="col.width"
|
||||||
|
show-overflow-tooltip
|
||||||
|
align="center">
|
||||||
|
<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 v-if="col.prop === 'status'" #default="scope">
|
||||||
|
<el-tag :type="statusTagType(scope.row.status)" size="small">
|
||||||
|
{{ formatStatus(scope.row.status) }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
<template v-else-if="col.prop === 'type'" #default="scope">
|
||||||
|
{{ scope.row.type === '1' ? '段段清' : scope.row.type === '2' ? '正常毕业' : scope.row.type || '-' }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</template>
|
||||||
|
<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="Gradustu">
|
||||||
|
import { reactive, ref, computed, onMounted } from 'vue'
|
||||||
|
import { useRoute } from 'vue-router'
|
||||||
|
import { BasicTableProps, useTable } from '/@/hooks/table'
|
||||||
|
import { fetchList, makeGraduStu } from '/@/api/stuwork/gradustu'
|
||||||
|
import { useMessage, useMessageBox } from '/@/hooks/message'
|
||||||
|
import { getDeptList } from '/@/api/basic/basicclass'
|
||||||
|
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
||||||
|
import {
|
||||||
|
List,
|
||||||
|
Calendar,
|
||||||
|
UserFilled,
|
||||||
|
Document,
|
||||||
|
Setting,
|
||||||
|
Menu,
|
||||||
|
Search,
|
||||||
|
Grid
|
||||||
|
} 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 makeLoading = ref(false)
|
||||||
|
const deptList = ref<any[]>([])
|
||||||
|
|
||||||
|
// 毕业年份:当前年前后各 5 年
|
||||||
|
const graduYearOptions = computed(() => {
|
||||||
|
const y = new Date().getFullYear()
|
||||||
|
return Array.from({ length: 11 }, (_, i) => y - 5 + i)
|
||||||
|
})
|
||||||
|
|
||||||
|
// 表格列配置(与其它页面一致,icon 写在列上)
|
||||||
|
const tableColumns = [
|
||||||
|
{ prop: 'stuNo', label: '学号', icon: UserFilled },
|
||||||
|
{ prop: 'realName', label: '姓名', icon: UserFilled },
|
||||||
|
{ prop: 'classNo', label: '班号', icon: Grid },
|
||||||
|
{ prop: 'majorName', label: '专业名称', icon: Document, minWidth: 140 },
|
||||||
|
{ prop: 'graduYear', label: '毕业年份', icon: Calendar },
|
||||||
|
{ prop: 'status', label: '毕业状态', icon: Document },
|
||||||
|
{ prop: 'type', label: '毕业类型', icon: Document },
|
||||||
|
{ prop: 'scoreCondition', label: '学分情况', icon: Document, minWidth: 100 },
|
||||||
|
{ prop: 'skillCondition', label: '技能情况', icon: Document, minWidth: 100 },
|
||||||
|
{ prop: 'conductCondition', label: '操行情况', icon: Document, minWidth: 100 },
|
||||||
|
{ prop: 'specialGradu', label: '特殊毕业', icon: Document, minWidth: 100 }
|
||||||
|
]
|
||||||
|
|
||||||
|
const {
|
||||||
|
visibleColumns,
|
||||||
|
visibleColumnsSorted,
|
||||||
|
checkColumnVisible,
|
||||||
|
handleColumnChange,
|
||||||
|
handleColumnOrderChange
|
||||||
|
} = useTableColumnControl(tableColumns, route.path)
|
||||||
|
|
||||||
|
// 搜索表单
|
||||||
|
const searchForm = reactive({
|
||||||
|
graduYear: '',
|
||||||
|
status: '',
|
||||||
|
type: '',
|
||||||
|
stuNo: '',
|
||||||
|
realName: '',
|
||||||
|
deptCode: '',
|
||||||
|
classNo: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
const state: BasicTableProps = reactive<BasicTableProps>({
|
||||||
|
queryForm: searchForm,
|
||||||
|
pageList: fetchList,
|
||||||
|
props: {
|
||||||
|
item: 'records',
|
||||||
|
totalCount: 'total'
|
||||||
|
},
|
||||||
|
createdIsNeed: true
|
||||||
|
})
|
||||||
|
|
||||||
|
const {
|
||||||
|
getDataList,
|
||||||
|
currentChangeHandle,
|
||||||
|
sizeChangeHandle,
|
||||||
|
tableStyle
|
||||||
|
} = useTable(state)
|
||||||
|
|
||||||
|
const formatStatus = (status: string) => {
|
||||||
|
const map: Record<string, string> = { '0': '待确认', '1': '确认毕业', '-1': '不可毕业' }
|
||||||
|
return map[status] || status || '-'
|
||||||
|
}
|
||||||
|
|
||||||
|
const statusTagType = (status: string) => {
|
||||||
|
const map: Record<string, string> = { '0': 'warning', '1': 'success', '-1': 'danger' }
|
||||||
|
return map[status] || 'info'
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSearch = () => {
|
||||||
|
getDataList()
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleReset = () => {
|
||||||
|
searchFormRef.value?.resetFields()
|
||||||
|
searchForm.graduYear = ''
|
||||||
|
searchForm.status = ''
|
||||||
|
searchForm.type = ''
|
||||||
|
searchForm.stuNo = ''
|
||||||
|
searchForm.realName = ''
|
||||||
|
searchForm.deptCode = ''
|
||||||
|
searchForm.classNo = ''
|
||||||
|
getDataList()
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleMakeGraduStu = async () => {
|
||||||
|
try {
|
||||||
|
await useMessageBox().confirm('确定要生成毕业生信息吗?', '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
})
|
||||||
|
} catch {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
makeLoading.value = true
|
||||||
|
try {
|
||||||
|
await makeGraduStu({ type: '0' })
|
||||||
|
useMessage().success('生成成功')
|
||||||
|
getDataList()
|
||||||
|
} catch (err: any) {
|
||||||
|
useMessage().error(err.msg || '生成失败')
|
||||||
|
} finally {
|
||||||
|
makeLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const loadDeptList = async () => {
|
||||||
|
try {
|
||||||
|
const res = await getDeptList()
|
||||||
|
deptList.value = Array.isArray(res.data) ? res.data : []
|
||||||
|
} catch (err) {
|
||||||
|
deptList.value = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
loadDeptList()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
@import '/@/assets/styles/modern-page.scss';
|
||||||
|
</style>
|
||||||
154
src/views/stuwork/psychologicalcounselingduty/form.vue
Normal file
154
src/views/stuwork/psychologicalcounselingduty/form.vue
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog
|
||||||
|
title="新增排班"
|
||||||
|
v-model="visible"
|
||||||
|
:width="500"
|
||||||
|
: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="date">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="form.date"
|
||||||
|
type="date"
|
||||||
|
placeholder="选择值班日期"
|
||||||
|
format="YYYY-MM-DD"
|
||||||
|
value-format="YYYY-MM-DD"
|
||||||
|
:disabled-date="disabledDate"
|
||||||
|
style="width: 100%" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="预约师" prop="teacherUserName">
|
||||||
|
<el-select
|
||||||
|
v-model="form.teacherUserName"
|
||||||
|
placeholder="请选择预约师"
|
||||||
|
filterable
|
||||||
|
clearable
|
||||||
|
style="width: 100%">
|
||||||
|
<el-option
|
||||||
|
v-for="item in teacherList"
|
||||||
|
:key="item.userName"
|
||||||
|
:label="(item.realName || '') + (item.userName ? ` (${item.userName})` : '')"
|
||||||
|
:value="item.userName" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="周类型" prop="weekType">
|
||||||
|
<el-select
|
||||||
|
v-model="form.weekType"
|
||||||
|
placeholder="请选择"
|
||||||
|
clearable
|
||||||
|
style="width: 100%">
|
||||||
|
<el-option label="单周" value="single" />
|
||||||
|
<el-option label="双周" value="double" />
|
||||||
|
</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="PsychologicalCounselingDutyFormDialog">
|
||||||
|
import { ref, reactive, nextTick, watch } from 'vue'
|
||||||
|
import { useMessage } from '/@/hooks/message'
|
||||||
|
import { saveDuty } from '/@/api/stuwork/psychologicalcounselingduty'
|
||||||
|
import { getList } from '/@/api/stuwork/psychologicalcounselingteacher'
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
year?: number
|
||||||
|
month?: number
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const emit = defineEmits(['refresh'])
|
||||||
|
|
||||||
|
const dataFormRef = ref()
|
||||||
|
const visible = ref(false)
|
||||||
|
const loading = ref(false)
|
||||||
|
const teacherList = ref<any[]>([])
|
||||||
|
|
||||||
|
const form = reactive({
|
||||||
|
date: '',
|
||||||
|
teacherUserName: '',
|
||||||
|
weekType: 'single'
|
||||||
|
})
|
||||||
|
|
||||||
|
const dataRules = {
|
||||||
|
date: [{ required: true, message: '请选择值班日期', trigger: 'change' }],
|
||||||
|
teacherUserName: [{ required: true, message: '请选择预约师', trigger: 'change' }]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 限制只能选当前年月的日期
|
||||||
|
const disabledDate = (time: Date) => {
|
||||||
|
if (!props.year || !props.month) return false
|
||||||
|
const y = time.getFullYear()
|
||||||
|
const m = time.getMonth() + 1
|
||||||
|
if (y !== props.year) return true
|
||||||
|
if (m !== props.month) return true
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
const loadTeacherList = async () => {
|
||||||
|
try {
|
||||||
|
const res = await getList()
|
||||||
|
if (res.data && Array.isArray(res.data)) {
|
||||||
|
teacherList.value = res.data
|
||||||
|
} else {
|
||||||
|
teacherList.value = []
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
teacherList.value = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const openDialog = () => {
|
||||||
|
visible.value = true
|
||||||
|
nextTick(() => {
|
||||||
|
dataFormRef.value?.resetFields()
|
||||||
|
form.date = ''
|
||||||
|
form.teacherUserName = ''
|
||||||
|
form.weekType = 'single'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const onSubmit = async () => {
|
||||||
|
if (!dataFormRef.value) return
|
||||||
|
await dataFormRef.value.validate(async (valid: boolean) => {
|
||||||
|
if (!valid) return
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
await saveDuty([
|
||||||
|
{
|
||||||
|
date: form.date,
|
||||||
|
teacherUserName: form.teacherUserName,
|
||||||
|
weekType: form.weekType || 'single'
|
||||||
|
}
|
||||||
|
])
|
||||||
|
useMessage().success('排班成功')
|
||||||
|
visible.value = false
|
||||||
|
emit('refresh')
|
||||||
|
} catch (err: any) {
|
||||||
|
useMessage().error(err.msg || '排班失败')
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(visible, (v) => {
|
||||||
|
if (v) loadTeacherList()
|
||||||
|
})
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
openDialog
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
</style>
|
||||||
310
src/views/stuwork/psychologicalcounselingduty/index.vue
Normal file
310
src/views/stuwork/psychologicalcounselingduty/index.vue
Normal file
@@ -0,0 +1,310 @@
|
|||||||
|
<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="getDataList"
|
||||||
|
class="search-form">
|
||||||
|
<el-form-item label="年份" prop="year">
|
||||||
|
<el-select
|
||||||
|
v-model="searchForm.year"
|
||||||
|
placeholder="请选择年份"
|
||||||
|
clearable
|
||||||
|
style="width: 120px">
|
||||||
|
<el-option
|
||||||
|
v-for="y in yearOptions"
|
||||||
|
:key="y"
|
||||||
|
:label="y + '年'"
|
||||||
|
:value="y" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="月份" prop="month">
|
||||||
|
<el-select
|
||||||
|
v-model="searchForm.month"
|
||||||
|
placeholder="请选择月份"
|
||||||
|
clearable
|
||||||
|
style="width: 120px">
|
||||||
|
<el-option
|
||||||
|
v-for="m in 12"
|
||||||
|
:key="m"
|
||||||
|
:label="m + '月'"
|
||||||
|
:value="m" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" icon="Search" @click="getDataList">查询</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"><Calendar /></el-icon>
|
||||||
|
值班管理列表
|
||||||
|
</span>
|
||||||
|
<div class="header-actions">
|
||||||
|
<el-button
|
||||||
|
icon="FolderAdd"
|
||||||
|
type="primary"
|
||||||
|
@click="formDialogRef?.openDialog()">
|
||||||
|
新增排班
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
icon="Delete"
|
||||||
|
type="danger"
|
||||||
|
plain
|
||||||
|
:disabled="!dataList.length"
|
||||||
|
@click="handleClearMonth">
|
||||||
|
一键清空本月
|
||||||
|
</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="dataList"
|
||||||
|
v-loading="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 }}
|
||||||
|
</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"
|
||||||
|
:min-width="col.minWidth"
|
||||||
|
:width="col.width"
|
||||||
|
show-overflow-tooltip
|
||||||
|
align="center">
|
||||||
|
<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 v-if="col.prop === 'reservation'" #default="scope">
|
||||||
|
<el-tag :type="scope.row.reservation === '1' ? 'success' : 'info'" size="small">
|
||||||
|
{{ scope.row.reservation === '1' ? '是' : '否' }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</template>
|
||||||
|
<el-table-column label="操作" width="100" align="center" fixed="right">
|
||||||
|
<template #header>
|
||||||
|
<el-icon><Setting /></el-icon>
|
||||||
|
<span style="margin-left: 4px">操作</span>
|
||||||
|
</template>
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button
|
||||||
|
icon="Delete"
|
||||||
|
link
|
||||||
|
type="danger"
|
||||||
|
@click="handleClearOne(scope.row)">
|
||||||
|
清除
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<template #empty>
|
||||||
|
<el-empty description="暂无值班数据,请选择年月查询或新增排班" :image-size="120">
|
||||||
|
<el-button type="primary" icon="FolderAdd" @click="formDialogRef?.openDialog()">新增排班</el-button>
|
||||||
|
</el-empty>
|
||||||
|
</template>
|
||||||
|
</el-table>
|
||||||
|
</el-card>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 新增排班弹窗 -->
|
||||||
|
<FormDialog
|
||||||
|
ref="formDialogRef"
|
||||||
|
:year="searchForm.year"
|
||||||
|
:month="searchForm.month"
|
||||||
|
@refresh="getDataList" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts" name="PsychologicalCounselingDuty">
|
||||||
|
import { reactive, ref, computed, onMounted, defineAsyncComponent } from 'vue'
|
||||||
|
import { useRoute } from 'vue-router'
|
||||||
|
import { useMessage, useMessageBox } from '/@/hooks/message'
|
||||||
|
import { getDutyByMonth, clearDuty, clearOneDuty } from '/@/api/stuwork/psychologicalcounselingduty'
|
||||||
|
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
||||||
|
import { List, Calendar, UserFilled, Phone, Document, Setting, Menu, Search } from '@element-plus/icons-vue'
|
||||||
|
import { useTableColumnControl } from '/@/hooks/tableColumn'
|
||||||
|
|
||||||
|
const FormDialog = defineAsyncComponent(() => import('./form.vue'))
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
const formDialogRef = ref()
|
||||||
|
const searchFormRef = ref()
|
||||||
|
const columnControlRef = ref<any>()
|
||||||
|
const showSearch = ref(true)
|
||||||
|
const loading = ref(false)
|
||||||
|
const dataList = ref<any[]>([])
|
||||||
|
|
||||||
|
// 年份选项:当前年前后各 5 年
|
||||||
|
const yearOptions = computed(() => {
|
||||||
|
const y = new Date().getFullYear()
|
||||||
|
return Array.from({ length: 11 }, (_, i) => y - 5 + i)
|
||||||
|
})
|
||||||
|
|
||||||
|
// 表格列配置(与其它页面一致,icon 写在列上)
|
||||||
|
const tableColumns = [
|
||||||
|
{ prop: 'date', label: '值班日期', icon: Calendar },
|
||||||
|
{ prop: 'dutyTime', label: '值班时间', icon: Calendar },
|
||||||
|
{ prop: 'realName', label: '教师姓名', icon: UserFilled },
|
||||||
|
{ prop: 'userName', label: '用户名', icon: UserFilled },
|
||||||
|
{ prop: 'phone', label: '联系方式', icon: Phone, minWidth: 120 },
|
||||||
|
{ prop: 'reservation', label: '是否预约', icon: Document }
|
||||||
|
]
|
||||||
|
|
||||||
|
const {
|
||||||
|
visibleColumns,
|
||||||
|
visibleColumnsSorted,
|
||||||
|
checkColumnVisible,
|
||||||
|
handleColumnChange,
|
||||||
|
handleColumnOrderChange
|
||||||
|
} = useTableColumnControl(tableColumns, route.path)
|
||||||
|
|
||||||
|
// 表格样式(与 useTable 一致)
|
||||||
|
const tableStyle = {
|
||||||
|
cellStyle: {},
|
||||||
|
headerCellStyle: {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 搜索表单
|
||||||
|
const searchForm = reactive({
|
||||||
|
year: new Date().getFullYear(),
|
||||||
|
month: new Date().getMonth() + 1
|
||||||
|
})
|
||||||
|
|
||||||
|
const getDataList = async () => {
|
||||||
|
if (!searchForm.year || !searchForm.month) {
|
||||||
|
useMessage().warning('请选择年份和月份')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
const res = await getDutyByMonth({
|
||||||
|
year: searchForm.year,
|
||||||
|
month: searchForm.month
|
||||||
|
})
|
||||||
|
dataList.value = Array.isArray(res.data) ? res.data : []
|
||||||
|
} catch (err: any) {
|
||||||
|
useMessage().error(err.msg || '获取值班表失败')
|
||||||
|
dataList.value = []
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleReset = () => {
|
||||||
|
const now = new Date()
|
||||||
|
searchForm.year = now.getFullYear()
|
||||||
|
searchForm.month = now.getMonth() + 1
|
||||||
|
getDataList()
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleClearMonth = async () => {
|
||||||
|
if (!searchForm.year || !searchForm.month) {
|
||||||
|
useMessage().warning('请先选择年份和月份')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await useMessageBox().confirm(
|
||||||
|
`确定要清空 ${searchForm.year}年${searchForm.month}月 的全部值班吗?`,
|
||||||
|
'提示',
|
||||||
|
{ confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }
|
||||||
|
)
|
||||||
|
} catch {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await clearDuty({
|
||||||
|
year: Number(searchForm.year),
|
||||||
|
month: Number(searchForm.month)
|
||||||
|
})
|
||||||
|
useMessage().success('清空成功')
|
||||||
|
getDataList()
|
||||||
|
} catch (err: any) {
|
||||||
|
useMessage().error(err.msg || '清空失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleClearOne = async (row: any) => {
|
||||||
|
const dateStr = row.date || row.dutyTime || ''
|
||||||
|
if (!dateStr) {
|
||||||
|
useMessage().warning('无法获取日期')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const day = dateStr.split(' ')[0] || dateStr
|
||||||
|
try {
|
||||||
|
await useMessageBox().confirm(`确定要清除 ${day} 的值班吗?`, '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
})
|
||||||
|
} catch {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await clearOneDuty({ days: day })
|
||||||
|
useMessage().success('清除成功')
|
||||||
|
getDataList()
|
||||||
|
} catch (err: any) {
|
||||||
|
useMessage().error(err.msg || '清除失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getDataList()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
@import '/@/assets/styles/modern-page.scss';
|
||||||
|
</style>
|
||||||
309
src/views/stuwork/psychologicalcounselingreservation/form.vue
Normal file
309
src/views/stuwork/psychologicalcounselingreservation/form.vue
Normal file
@@ -0,0 +1,309 @@
|
|||||||
|
<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="teacherNo">
|
||||||
|
<el-select
|
||||||
|
v-model="form.teacherNo"
|
||||||
|
placeholder="请选择值班教师"
|
||||||
|
filterable
|
||||||
|
clearable
|
||||||
|
style="width: 100%"
|
||||||
|
:disabled="!!form.id"
|
||||||
|
@change="handleTeacherChange">
|
||||||
|
<el-option
|
||||||
|
v-for="item in teacherList"
|
||||||
|
:key="item.userName"
|
||||||
|
:label="(item.realName || '') + (item.userName ? ` (${item.userName})` : '')"
|
||||||
|
:value="item.userName" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="预约时间" prop="reservationTime">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="form.reservationTime"
|
||||||
|
type="datetime"
|
||||||
|
placeholder="选择预约时间"
|
||||||
|
format="YYYY-MM-DD HH:mm"
|
||||||
|
value-format="YYYY-MM-DD HH:mm:ss"
|
||||||
|
style="width: 100%"
|
||||||
|
:disabled="!!form.id" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="班号" prop="classCode">
|
||||||
|
<el-select
|
||||||
|
v-model="form.classCode"
|
||||||
|
placeholder="请选择班号"
|
||||||
|
clearable
|
||||||
|
filterable
|
||||||
|
style="width: 100%"
|
||||||
|
:disabled="!!form.id"
|
||||||
|
@change="handleClassChange">
|
||||||
|
<el-option
|
||||||
|
v-for="item in classList"
|
||||||
|
:key="item.classCode"
|
||||||
|
:label="item.classNo"
|
||||||
|
:value="item.classCode" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="学号" prop="stuNo">
|
||||||
|
<el-select
|
||||||
|
v-model="form.stuNo"
|
||||||
|
:placeholder="form.classCode ? (studentLoading ? '加载中...' : '请选择学生') : '请先选择班号'"
|
||||||
|
clearable
|
||||||
|
filterable
|
||||||
|
style="width: 100%"
|
||||||
|
:disabled="!form.classCode || !!form.id || studentLoading"
|
||||||
|
:loading="studentLoading"
|
||||||
|
@change="handleStudentChange">
|
||||||
|
<el-option
|
||||||
|
v-for="item in studentList"
|
||||||
|
:key="item.stuNo"
|
||||||
|
:label="`${item.realName || item.stuName || ''} (${item.stuNo})`"
|
||||||
|
:value="item.stuNo" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="学生姓名" prop="stuName">
|
||||||
|
<el-input v-model="form.stuName" placeholder="选学生后自动带出" disabled />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="联系方式" prop="phone">
|
||||||
|
<el-input v-model="form.phone" placeholder="选学生后自动带出" disabled />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="学生留言" prop="remarks">
|
||||||
|
<el-input
|
||||||
|
v-model="form.remarks"
|
||||||
|
type="textarea"
|
||||||
|
:rows="3"
|
||||||
|
placeholder="请输入学生留言"
|
||||||
|
maxlength="250"
|
||||||
|
show-word-limit
|
||||||
|
:disabled="!!form.id" />
|
||||||
|
</el-form-item>
|
||||||
|
<template v-if="form.id">
|
||||||
|
<el-form-item label="是否处理" prop="isHandle">
|
||||||
|
<el-select
|
||||||
|
v-model="form.isHandle"
|
||||||
|
placeholder="请选择"
|
||||||
|
clearable
|
||||||
|
style="width: 100%">
|
||||||
|
<el-option label="是" value="1" />
|
||||||
|
<el-option label="否" value="0" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="老师备注" prop="teaRemark">
|
||||||
|
<el-input
|
||||||
|
v-model="form.teaRemark"
|
||||||
|
type="textarea"
|
||||||
|
:rows="3"
|
||||||
|
placeholder="请输入老师备注"
|
||||||
|
maxlength="250"
|
||||||
|
show-word-limit />
|
||||||
|
</el-form-item>
|
||||||
|
</template>
|
||||||
|
</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="PsychologicalCounselingReservationFormDialog">
|
||||||
|
import { ref, reactive, nextTick, onMounted } from 'vue'
|
||||||
|
import { useMessage } from '/@/hooks/message'
|
||||||
|
import { addObj, editObj, getDetail } from '/@/api/stuwork/psychologicalcounselingreservation'
|
||||||
|
import { getList } from '/@/api/stuwork/psychologicalcounselingteacher'
|
||||||
|
import { getClassListByRole } from '/@/api/basic/basicclass'
|
||||||
|
import { queryAllStudentByClassCode } from '/@/api/basic/basicstudent'
|
||||||
|
|
||||||
|
const emit = defineEmits(['refresh'])
|
||||||
|
|
||||||
|
const dataFormRef = ref()
|
||||||
|
const visible = ref(false)
|
||||||
|
const loading = ref(false)
|
||||||
|
const studentLoading = ref(false)
|
||||||
|
const operType = ref('add')
|
||||||
|
const teacherList = ref<any[]>([])
|
||||||
|
const classList = ref<any[]>([])
|
||||||
|
const studentList = ref<any[]>([])
|
||||||
|
|
||||||
|
const form = reactive({
|
||||||
|
id: '',
|
||||||
|
teacherNo: '',
|
||||||
|
realName: '',
|
||||||
|
reservationTime: '',
|
||||||
|
classCode: '',
|
||||||
|
classNo: '',
|
||||||
|
stuNo: '',
|
||||||
|
stuName: '',
|
||||||
|
phone: '',
|
||||||
|
remarks: '',
|
||||||
|
isHandle: '0',
|
||||||
|
teaRemark: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
const dataRules = {
|
||||||
|
teacherNo: [{ required: true, message: '请选择值班教师', trigger: 'change' }],
|
||||||
|
reservationTime: [{ required: true, message: '请选择预约时间', trigger: 'change' }],
|
||||||
|
classCode: [{ required: true, message: '请选择班号', trigger: 'change' }],
|
||||||
|
stuNo: [{ required: true, message: '请选择学生', trigger: 'change' }]
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleTeacherChange = (val: string) => {
|
||||||
|
const item = teacherList.value.find((t: any) => t.userName === val)
|
||||||
|
form.realName = item ? (item.realName || '') : ''
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleClassChange = async () => {
|
||||||
|
form.stuNo = ''
|
||||||
|
form.stuName = ''
|
||||||
|
form.phone = ''
|
||||||
|
studentList.value = []
|
||||||
|
if (!form.classCode) return
|
||||||
|
studentLoading.value = true
|
||||||
|
try {
|
||||||
|
const res = await queryAllStudentByClassCode(form.classCode)
|
||||||
|
studentList.value = Array.isArray(res.data) ? res.data : []
|
||||||
|
} catch (err) {
|
||||||
|
studentList.value = []
|
||||||
|
} finally {
|
||||||
|
studentLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleStudentChange = (val: string) => {
|
||||||
|
const item = studentList.value.find((s: any) => s.stuNo === val)
|
||||||
|
if (item) {
|
||||||
|
form.stuName = item.realName || item.stuName || ''
|
||||||
|
form.phone = item.phone || item.mobile || ''
|
||||||
|
} else {
|
||||||
|
form.stuName = ''
|
||||||
|
form.phone = ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const openDialog = (type: string = 'add', row?: any) => {
|
||||||
|
operType.value = type
|
||||||
|
visible.value = true
|
||||||
|
|
||||||
|
nextTick(() => {
|
||||||
|
dataFormRef.value?.resetFields()
|
||||||
|
form.id = ''
|
||||||
|
form.teacherNo = ''
|
||||||
|
form.realName = ''
|
||||||
|
form.reservationTime = ''
|
||||||
|
form.classCode = ''
|
||||||
|
form.classNo = ''
|
||||||
|
form.stuNo = ''
|
||||||
|
form.stuName = ''
|
||||||
|
form.phone = ''
|
||||||
|
form.remarks = ''
|
||||||
|
form.isHandle = '0'
|
||||||
|
form.teaRemark = ''
|
||||||
|
studentList.value = []
|
||||||
|
|
||||||
|
if (type === 'edit' && row) {
|
||||||
|
form.id = row.id
|
||||||
|
form.teacherNo = row.teacherNo || ''
|
||||||
|
form.realName = row.realName || ''
|
||||||
|
form.reservationTime = row.reservationTime || ''
|
||||||
|
form.classNo = row.classNo || ''
|
||||||
|
const cls = classList.value.find((c: any) => c.classNo === row.classNo)
|
||||||
|
form.classCode = cls ? cls.classCode : (row.classCode || '')
|
||||||
|
form.stuNo = row.stuNo || ''
|
||||||
|
form.stuName = row.stuName || ''
|
||||||
|
form.phone = row.phone || ''
|
||||||
|
form.remarks = row.remarks || ''
|
||||||
|
form.isHandle = row.isHandle !== undefined && row.isHandle !== null ? String(row.isHandle) : '0'
|
||||||
|
form.teaRemark = row.teaRemark || ''
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const onSubmit = async () => {
|
||||||
|
if (!dataFormRef.value) return
|
||||||
|
await dataFormRef.value.validate(async (valid: boolean) => {
|
||||||
|
if (!valid) return
|
||||||
|
|
||||||
|
const selectedClass = classList.value.find((c: any) => c.classCode === form.classCode)
|
||||||
|
const classNo = selectedClass ? selectedClass.classNo : form.classNo || form.classCode
|
||||||
|
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
if (operType.value === 'add') {
|
||||||
|
await addObj({
|
||||||
|
teacherNo: form.teacherNo,
|
||||||
|
realName: form.realName,
|
||||||
|
reservationTime: form.reservationTime ? (form.reservationTime.length === 10 ? form.reservationTime + ' 00:00:00' : form.reservationTime) : '',
|
||||||
|
classNo,
|
||||||
|
stuNo: form.stuNo,
|
||||||
|
stuName: form.stuName,
|
||||||
|
phone: form.phone,
|
||||||
|
remarks: form.remarks
|
||||||
|
})
|
||||||
|
useMessage().success('新增成功')
|
||||||
|
} else {
|
||||||
|
await editObj({
|
||||||
|
id: form.id,
|
||||||
|
teacherNo: form.teacherNo,
|
||||||
|
realName: form.realName,
|
||||||
|
reservationTime: form.reservationTime,
|
||||||
|
classNo: form.classNo || classNo,
|
||||||
|
stuNo: form.stuNo,
|
||||||
|
stuName: form.stuName,
|
||||||
|
phone: form.phone,
|
||||||
|
remarks: form.remarks,
|
||||||
|
isHandle: form.isHandle,
|
||||||
|
teaRemark: form.teaRemark || ''
|
||||||
|
})
|
||||||
|
useMessage().success('编辑成功')
|
||||||
|
}
|
||||||
|
visible.value = false
|
||||||
|
emit('refresh')
|
||||||
|
} catch (err: any) {
|
||||||
|
useMessage().error(err.msg || (operType.value === 'add' ? '新增失败' : '编辑失败'))
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const loadTeacherList = async () => {
|
||||||
|
try {
|
||||||
|
const res = await getList()
|
||||||
|
teacherList.value = Array.isArray(res.data) ? res.data : []
|
||||||
|
} catch (err) {
|
||||||
|
teacherList.value = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const loadClassList = async () => {
|
||||||
|
try {
|
||||||
|
const res = await getClassListByRole()
|
||||||
|
classList.value = Array.isArray(res.data) ? res.data : []
|
||||||
|
} catch (err) {
|
||||||
|
classList.value = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
loadTeacherList()
|
||||||
|
loadClassList()
|
||||||
|
})
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
openDialog
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
</style>
|
||||||
296
src/views/stuwork/psychologicalcounselingreservation/index.vue
Normal file
296
src/views/stuwork/psychologicalcounselingreservation/index.vue
Normal file
@@ -0,0 +1,296 @@
|
|||||||
|
<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="stuNo">
|
||||||
|
<el-input
|
||||||
|
v-model="searchForm.stuNo"
|
||||||
|
placeholder="请输入学号"
|
||||||
|
clearable
|
||||||
|
style="width: 160px" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="班号" prop="classNo">
|
||||||
|
<el-input
|
||||||
|
v-model="searchForm.classNo"
|
||||||
|
placeholder="请输入班号"
|
||||||
|
clearable
|
||||||
|
style="width: 160px" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="预约时间" prop="reservationTime">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="searchForm.reservationTime"
|
||||||
|
type="date"
|
||||||
|
placeholder="选择预约时间"
|
||||||
|
format="YYYY-MM-DD"
|
||||||
|
value-format="YYYY-MM-DD"
|
||||||
|
clearable
|
||||||
|
style="width: 180px" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="是否处理" prop="isHandle">
|
||||||
|
<el-select
|
||||||
|
v-model="searchForm.isHandle"
|
||||||
|
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 type="primary" 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="FolderAdd"
|
||||||
|
type="primary"
|
||||||
|
@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"
|
||||||
|
:min-width="col.minWidth"
|
||||||
|
:width="col.width"
|
||||||
|
show-overflow-tooltip
|
||||||
|
align="center">
|
||||||
|
<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 v-if="col.prop === 'isHandle'" #default="scope">
|
||||||
|
<el-tag :type="scope.row.isHandle === '1' ? 'success' : 'info'" size="small">
|
||||||
|
{{ scope.row.isHandle === '1' ? '是' : '否' }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</template>
|
||||||
|
<el-table-column label="操作" width="150" 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"
|
||||||
|
@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">
|
||||||
|
<el-button type="primary" icon="FolderAdd" @click="formDialogRef?.openDialog()">新增预约记录</el-button>
|
||||||
|
</el-empty>
|
||||||
|
</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" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts" name="PsychologicalCounselingReservation">
|
||||||
|
import { reactive, ref, onMounted, defineAsyncComponent } from 'vue'
|
||||||
|
import { useRoute } from 'vue-router'
|
||||||
|
import { BasicTableProps, useTable } from '/@/hooks/table'
|
||||||
|
import { fetchList, delObj } from '/@/api/stuwork/psychologicalcounselingreservation'
|
||||||
|
import { useMessage, useMessageBox } from '/@/hooks/message'
|
||||||
|
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
||||||
|
import {
|
||||||
|
List,
|
||||||
|
Calendar,
|
||||||
|
UserFilled,
|
||||||
|
Phone,
|
||||||
|
EditPen,
|
||||||
|
Setting,
|
||||||
|
Menu,
|
||||||
|
Search,
|
||||||
|
Document,
|
||||||
|
FolderAdd
|
||||||
|
} from '@element-plus/icons-vue'
|
||||||
|
import { useTableColumnControl } from '/@/hooks/tableColumn'
|
||||||
|
|
||||||
|
const FormDialog = defineAsyncComponent(() => import('./form.vue'))
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
const formDialogRef = ref()
|
||||||
|
const searchFormRef = ref()
|
||||||
|
const columnControlRef = ref<any>()
|
||||||
|
const showSearch = ref(true)
|
||||||
|
|
||||||
|
// 表格列配置(与其它页面一致,icon 写在列上)
|
||||||
|
const tableColumns = [
|
||||||
|
{ prop: 'reservationTime', label: '预约时间', icon: Calendar, minWidth: 160 },
|
||||||
|
{ prop: 'realName', label: '值班老师', icon: UserFilled },
|
||||||
|
{ prop: 'classNo', label: '班号', icon: Document },
|
||||||
|
{ prop: 'stuNo', label: '学号', icon: UserFilled },
|
||||||
|
{ prop: 'stuName', label: '学生姓名', icon: UserFilled },
|
||||||
|
{ prop: 'phone', label: '联系方式', icon: Phone, minWidth: 120 },
|
||||||
|
{ prop: 'isHandle', label: '是否处理', icon: Document },
|
||||||
|
{ prop: 'teaRemark', label: '老师备注', icon: EditPen, minWidth: 120 },
|
||||||
|
{ prop: 'remarks', label: '学生留言', icon: EditPen, minWidth: 120 }
|
||||||
|
]
|
||||||
|
|
||||||
|
const {
|
||||||
|
visibleColumns,
|
||||||
|
visibleColumnsSorted,
|
||||||
|
checkColumnVisible,
|
||||||
|
handleColumnChange,
|
||||||
|
handleColumnOrderChange
|
||||||
|
} = useTableColumnControl(tableColumns, route.path)
|
||||||
|
|
||||||
|
// 搜索表单
|
||||||
|
const searchForm = reactive({
|
||||||
|
stuNo: '',
|
||||||
|
classNo: '',
|
||||||
|
reservationTime: '',
|
||||||
|
isHandle: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
const state: BasicTableProps = reactive<BasicTableProps>({
|
||||||
|
queryForm: searchForm,
|
||||||
|
pageList: fetchList,
|
||||||
|
props: {
|
||||||
|
item: 'records',
|
||||||
|
totalCount: 'total'
|
||||||
|
},
|
||||||
|
createdIsNeed: true
|
||||||
|
})
|
||||||
|
|
||||||
|
const {
|
||||||
|
getDataList,
|
||||||
|
currentChangeHandle,
|
||||||
|
sizeChangeHandle,
|
||||||
|
tableStyle
|
||||||
|
} = useTable(state)
|
||||||
|
|
||||||
|
const handleSearch = () => {
|
||||||
|
getDataList()
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleReset = () => {
|
||||||
|
searchFormRef.value?.resetFields()
|
||||||
|
searchForm.stuNo = ''
|
||||||
|
searchForm.classNo = ''
|
||||||
|
searchForm.reservationTime = ''
|
||||||
|
searchForm.isHandle = ''
|
||||||
|
getDataList()
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleEdit = (row: any) => {
|
||||||
|
formDialogRef.value?.openDialog('edit', row)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleDelete = async (row: any) => {
|
||||||
|
try {
|
||||||
|
await useMessageBox().confirm('确定要删除该预约记录吗?', '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
})
|
||||||
|
} catch {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await delObj([row.id])
|
||||||
|
useMessage().success('删除成功')
|
||||||
|
getDataList()
|
||||||
|
} catch (err: any) {
|
||||||
|
useMessage().error(err.msg || '删除失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
@import '/@/assets/styles/modern-page.scss';
|
||||||
|
</style>
|
||||||
186
src/views/stuwork/psychologicalcounselingteacher/form.vue
Normal file
186
src/views/stuwork/psychologicalcounselingteacher/form.vue
Normal file
@@ -0,0 +1,186 @@
|
|||||||
|
<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="userName">
|
||||||
|
<el-select
|
||||||
|
v-model="form.userName"
|
||||||
|
placeholder="请选择教师"
|
||||||
|
filterable
|
||||||
|
clearable
|
||||||
|
style="width: 100%"
|
||||||
|
:disabled="!!form.id"
|
||||||
|
@change="handleTeacherChange">
|
||||||
|
<el-option
|
||||||
|
v-for="item in teacherList"
|
||||||
|
:key="item.teacherNo || item.userName"
|
||||||
|
:label="(item.realName || item.name) + (item.teacherNo ? ` (${item.teacherNo})` : '')"
|
||||||
|
:value="item.teacherNo || item.userName">
|
||||||
|
</el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="真实姓名" prop="realName">
|
||||||
|
<el-input
|
||||||
|
v-model="form.realName"
|
||||||
|
placeholder="选教师后自动带出,可修改"
|
||||||
|
clearable />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="联系方式" prop="phone">
|
||||||
|
<el-input
|
||||||
|
v-model="form.phone"
|
||||||
|
placeholder="请输入联系方式"
|
||||||
|
clearable />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="备注" prop="remarks">
|
||||||
|
<el-input
|
||||||
|
v-model="form.remarks"
|
||||||
|
type="textarea"
|
||||||
|
:rows="4"
|
||||||
|
placeholder="请输入备注"
|
||||||
|
maxlength="250"
|
||||||
|
show-word-limit />
|
||||||
|
</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="PsychologicalCounselingTeacherFormDialog">
|
||||||
|
import { ref, reactive, nextTick, onMounted } from 'vue'
|
||||||
|
import { useMessage } from '/@/hooks/message'
|
||||||
|
import { addObj, editObj, getDetail } from '/@/api/stuwork/psychologicalcounselingteacher'
|
||||||
|
import { getTeacherBaseList } from '/@/api/professional/professionaluser/teacherbase'
|
||||||
|
|
||||||
|
const emit = defineEmits(['refresh'])
|
||||||
|
|
||||||
|
const dataFormRef = ref()
|
||||||
|
const visible = ref(false)
|
||||||
|
const loading = ref(false)
|
||||||
|
const operType = ref('add')
|
||||||
|
const teacherList = ref<any[]>([])
|
||||||
|
|
||||||
|
const form = reactive({
|
||||||
|
id: '',
|
||||||
|
userName: '',
|
||||||
|
realName: '',
|
||||||
|
phone: '',
|
||||||
|
remarks: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
const dataRules = {
|
||||||
|
userName: [
|
||||||
|
{ required: true, message: '请选择教师', trigger: 'change' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleTeacherChange = (val: string) => {
|
||||||
|
const item = teacherList.value.find(
|
||||||
|
(t: any) => (t.teacherNo || t.userName) === val
|
||||||
|
)
|
||||||
|
if (item) {
|
||||||
|
form.realName = item.realName || item.name || ''
|
||||||
|
form.phone = item.phone || item.tel || ''
|
||||||
|
} else {
|
||||||
|
form.realName = ''
|
||||||
|
form.phone = ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const openDialog = (type: string = 'add', row?: any) => {
|
||||||
|
operType.value = type
|
||||||
|
visible.value = true
|
||||||
|
|
||||||
|
nextTick(() => {
|
||||||
|
dataFormRef.value?.resetFields()
|
||||||
|
form.id = ''
|
||||||
|
form.userName = ''
|
||||||
|
form.realName = ''
|
||||||
|
form.phone = ''
|
||||||
|
form.remarks = ''
|
||||||
|
|
||||||
|
if (type === 'edit' && row) {
|
||||||
|
form.id = row.id
|
||||||
|
form.userName = row.userName || ''
|
||||||
|
form.realName = row.realName || ''
|
||||||
|
form.phone = row.phone || ''
|
||||||
|
form.remarks = row.remarks || ''
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
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({
|
||||||
|
userName: form.userName,
|
||||||
|
realName: form.realName,
|
||||||
|
phone: form.phone,
|
||||||
|
remarks: form.remarks
|
||||||
|
})
|
||||||
|
useMessage().success('新增成功')
|
||||||
|
} else {
|
||||||
|
await editObj({
|
||||||
|
id: form.id,
|
||||||
|
userName: form.userName,
|
||||||
|
realName: form.realName,
|
||||||
|
phone: form.phone,
|
||||||
|
remarks: form.remarks
|
||||||
|
})
|
||||||
|
useMessage().success('编辑成功')
|
||||||
|
}
|
||||||
|
visible.value = false
|
||||||
|
emit('refresh')
|
||||||
|
} catch (err: any) {
|
||||||
|
useMessage().error(err.msg || (operType.value === 'add' ? '新增失败' : '编辑失败'))
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const loadTeacherList = async () => {
|
||||||
|
try {
|
||||||
|
const res = await getTeacherBaseList()
|
||||||
|
if (res.data && Array.isArray(res.data)) {
|
||||||
|
teacherList.value = res.data
|
||||||
|
} else {
|
||||||
|
teacherList.value = []
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
teacherList.value = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
loadTeacherList()
|
||||||
|
})
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
openDialog
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.mb20 {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
244
src/views/stuwork/psychologicalcounselingteacher/index.vue
Normal file
244
src/views/stuwork/psychologicalcounselingteacher/index.vue
Normal file
@@ -0,0 +1,244 @@
|
|||||||
|
<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="realName">
|
||||||
|
<el-input
|
||||||
|
v-model="searchForm.realName"
|
||||||
|
placeholder="请输入教师姓名"
|
||||||
|
clearable
|
||||||
|
style="width: 200px" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" icon="Search" @click="handleSearch">查询</el-button>
|
||||||
|
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<!-- 内容卡片 -->
|
||||||
|
<el-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
|
||||||
|
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"
|
||||||
|
:min-width="col.minWidth"
|
||||||
|
:width="col.width"
|
||||||
|
show-overflow-tooltip
|
||||||
|
align="center">
|
||||||
|
<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>
|
||||||
|
</el-table-column>
|
||||||
|
</template>
|
||||||
|
<el-table-column label="操作" width="150" 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"
|
||||||
|
@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">
|
||||||
|
<el-button type="primary" icon="FolderAdd" @click="formDialogRef?.openDialog()">新增预约师</el-button>
|
||||||
|
</el-empty>
|
||||||
|
</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" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts" name="PsychologicalCounselingTeacher">
|
||||||
|
import { reactive, ref, onMounted, defineAsyncComponent } from 'vue'
|
||||||
|
import { useRoute } from 'vue-router'
|
||||||
|
import { BasicTableProps, useTable } from '/@/hooks/table'
|
||||||
|
import { fetchList, delObj } from '/@/api/stuwork/psychologicalcounselingteacher'
|
||||||
|
import { useMessage, useMessageBox } from '/@/hooks/message'
|
||||||
|
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
||||||
|
import { List, UserFilled, Phone, EditPen, Setting, Menu, Search, Document, FolderAdd } from '@element-plus/icons-vue'
|
||||||
|
import { useTableColumnControl } from '/@/hooks/tableColumn'
|
||||||
|
|
||||||
|
const FormDialog = defineAsyncComponent(() => import('./form.vue'))
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
const formDialogRef = ref()
|
||||||
|
const searchFormRef = ref()
|
||||||
|
const columnControlRef = ref<any>()
|
||||||
|
const showSearch = ref(true)
|
||||||
|
|
||||||
|
// 表格列配置(与其它页面一致,icon 写在列上)
|
||||||
|
const tableColumns = [
|
||||||
|
{ prop: 'userName', label: '用户名', icon: UserFilled },
|
||||||
|
{ prop: 'realName', label: '教师姓名', icon: UserFilled },
|
||||||
|
{ prop: 'phone', label: '联系方式', icon: Phone, minWidth: 120 },
|
||||||
|
{ prop: 'remarks', label: '备注', icon: EditPen, minWidth: 150 }
|
||||||
|
]
|
||||||
|
|
||||||
|
const {
|
||||||
|
visibleColumns,
|
||||||
|
visibleColumnsSorted,
|
||||||
|
checkColumnVisible,
|
||||||
|
handleColumnChange,
|
||||||
|
handleColumnOrderChange
|
||||||
|
} = useTableColumnControl(tableColumns, route.path)
|
||||||
|
|
||||||
|
// 搜索表单
|
||||||
|
const searchForm = reactive({
|
||||||
|
realName: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
// 配置 useTable
|
||||||
|
const state: BasicTableProps = reactive<BasicTableProps>({
|
||||||
|
queryForm: searchForm,
|
||||||
|
pageList: fetchList,
|
||||||
|
props: {
|
||||||
|
item: 'records',
|
||||||
|
totalCount: 'total'
|
||||||
|
},
|
||||||
|
createdIsNeed: true
|
||||||
|
})
|
||||||
|
|
||||||
|
const {
|
||||||
|
getDataList,
|
||||||
|
currentChangeHandle,
|
||||||
|
sizeChangeHandle,
|
||||||
|
tableStyle
|
||||||
|
} = useTable(state)
|
||||||
|
|
||||||
|
const handleSearch = () => {
|
||||||
|
getDataList()
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleReset = () => {
|
||||||
|
searchFormRef.value?.resetFields()
|
||||||
|
searchForm.realName = ''
|
||||||
|
getDataList()
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleEdit = (row: any) => {
|
||||||
|
formDialogRef.value?.openDialog('edit', row)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleDelete = async (row: any) => {
|
||||||
|
try {
|
||||||
|
await useMessageBox().confirm('确定要删除该预约师吗?', '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
})
|
||||||
|
} catch {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await delObj([row.id])
|
||||||
|
useMessage().success('删除成功')
|
||||||
|
getDataList()
|
||||||
|
} catch (err: any) {
|
||||||
|
useMessage().error(err.msg || '删除失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
@import '/@/assets/styles/modern-page.scss';
|
||||||
|
</style>
|
||||||
@@ -31,12 +31,20 @@
|
|||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="24" class="mb20">
|
<el-col :span="24" class="mb20">
|
||||||
<el-form-item label="奖项名称" prop="ruleName">
|
<el-form-item label="奖项名称" prop="ruleId">
|
||||||
<el-input
|
<el-select
|
||||||
v-model="form.ruleName"
|
v-model="form.ruleId"
|
||||||
placeholder="请输入奖项名称"
|
placeholder="请选择奖项名称"
|
||||||
clearable
|
clearable
|
||||||
style="width: 100%" />
|
filterable
|
||||||
|
style="width: 100%"
|
||||||
|
@change="handleRewardRuleChange">
|
||||||
|
<el-option
|
||||||
|
v-for="item in rewardRuleList"
|
||||||
|
:key="item.id"
|
||||||
|
:label="item.ruleName"
|
||||||
|
:value="item.id" />
|
||||||
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="24" class="mb20">
|
<el-col :span="24" class="mb20">
|
||||||
@@ -67,6 +75,7 @@ import { ref, reactive, nextTick, onMounted } from 'vue'
|
|||||||
import { useMessage } from '/@/hooks/message'
|
import { useMessage } from '/@/hooks/message'
|
||||||
import { addObj, editObj, getDetail } from '/@/api/stuwork/rewardclass'
|
import { addObj, editObj, getDetail } from '/@/api/stuwork/rewardclass'
|
||||||
import { getClassListByRole } from '/@/api/basic/basicclass'
|
import { getClassListByRole } from '/@/api/basic/basicclass'
|
||||||
|
import { getList as getRewardRuleList } from '/@/api/stuwork/rewardrule'
|
||||||
|
|
||||||
const emit = defineEmits(['refresh'])
|
const emit = defineEmits(['refresh'])
|
||||||
|
|
||||||
@@ -76,6 +85,8 @@ const visible = ref(false)
|
|||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const operType = ref('add')
|
const operType = ref('add')
|
||||||
const classList = ref<any[]>([])
|
const classList = ref<any[]>([])
|
||||||
|
/** 奖项列表(来自接口 /stuwork/rewardrule/list,用于班级的奖项类型可传 ruleType 筛选) */
|
||||||
|
const rewardRuleList = ref<any[]>([])
|
||||||
|
|
||||||
// 提交表单数据
|
// 提交表单数据
|
||||||
const form = reactive({
|
const form = reactive({
|
||||||
@@ -91,8 +102,8 @@ const dataRules = {
|
|||||||
classCode: [
|
classCode: [
|
||||||
{ required: true, message: '请选择班号', trigger: 'change' }
|
{ required: true, message: '请选择班号', trigger: 'change' }
|
||||||
],
|
],
|
||||||
ruleName: [
|
ruleId: [
|
||||||
{ required: true, message: '请输入奖项名称', trigger: 'blur' }
|
{ required: true, message: '请选择奖项名称', trigger: 'change' }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,6 +128,8 @@ const openDialog = async (type: string = 'add', row?: any) => {
|
|||||||
form.ruleId = row.ruleId || ''
|
form.ruleId = row.ruleId || ''
|
||||||
form.ruleName = row.ruleName || ''
|
form.ruleName = row.ruleName || ''
|
||||||
form.remarks = row.remarks || ''
|
form.remarks = row.remarks || ''
|
||||||
|
// 选择奖项后同步 ruleName
|
||||||
|
handleRewardRuleChange(form.ruleId)
|
||||||
|
|
||||||
// 如果需要获取详情
|
// 如果需要获取详情
|
||||||
if (row.id && (!row.ruleName || !row.remarks)) {
|
if (row.id && (!row.ruleName || !row.remarks)) {
|
||||||
@@ -138,8 +151,12 @@ const openDialog = async (type: string = 'add', row?: any) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 班级变化
|
// 班级变化
|
||||||
const handleClassChange = () => {
|
const handleClassChange = () => {}
|
||||||
// 可以根据班级获取相关信息,这里暂时不实现
|
|
||||||
|
// 选择奖项时同步 ruleName(提交时需要)
|
||||||
|
const handleRewardRuleChange = (id: string) => {
|
||||||
|
const item = rewardRuleList.value.find((r) => r.id === id)
|
||||||
|
form.ruleName = item ? item.ruleName : ''
|
||||||
}
|
}
|
||||||
|
|
||||||
// 提交表单
|
// 提交表单
|
||||||
@@ -192,9 +209,21 @@ const getClassListData = async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取奖项列表(接口:GET /stuwork/rewardrule/list)
|
||||||
|
const getRewardRuleData = async () => {
|
||||||
|
try {
|
||||||
|
const res = await getRewardRuleList()
|
||||||
|
const list = res.data && Array.isArray(res.data) ? res.data : []
|
||||||
|
rewardRuleList.value = list
|
||||||
|
} catch (err) {
|
||||||
|
rewardRuleList.value = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 初始化
|
// 初始化
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getClassListData()
|
getClassListData()
|
||||||
|
getRewardRuleData()
|
||||||
})
|
})
|
||||||
|
|
||||||
// 暴露方法
|
// 暴露方法
|
||||||
|
|||||||
@@ -12,6 +12,39 @@
|
|||||||
label-width="100px"
|
label-width="100px"
|
||||||
v-loading="loading">
|
v-loading="loading">
|
||||||
<el-row :gutter="24">
|
<el-row :gutter="24">
|
||||||
|
<el-col :span="24" class="mb20">
|
||||||
|
<el-form-item label="学年" prop="schoolYear">
|
||||||
|
<el-select
|
||||||
|
v-model="form.schoolYear"
|
||||||
|
placeholder="请选择学年"
|
||||||
|
clearable
|
||||||
|
filterable
|
||||||
|
style="width: 100%">
|
||||||
|
<el-option
|
||||||
|
v-for="item in schoolYearList"
|
||||||
|
:key="item.year"
|
||||||
|
:label="item.year"
|
||||||
|
:value="item.year">
|
||||||
|
</el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="24" class="mb20">
|
||||||
|
<el-form-item label="学期" prop="schoolTerm">
|
||||||
|
<el-select
|
||||||
|
v-model="form.schoolTerm"
|
||||||
|
placeholder="请选择学期"
|
||||||
|
clearable
|
||||||
|
style="width: 100%">
|
||||||
|
<el-option
|
||||||
|
v-for="item in schoolTermList"
|
||||||
|
:key="item.value"
|
||||||
|
:label="item.label"
|
||||||
|
:value="item.value">
|
||||||
|
</el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
<el-col :span="24" class="mb20">
|
<el-col :span="24" class="mb20">
|
||||||
<el-form-item label="宿舍号" prop="roomNo">
|
<el-form-item label="宿舍号" prop="roomNo">
|
||||||
<el-select
|
<el-select
|
||||||
@@ -30,12 +63,20 @@
|
|||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="24" class="mb20">
|
<el-col :span="24" class="mb20">
|
||||||
<el-form-item label="奖项名称" prop="ruleName">
|
<el-form-item label="奖项名称" prop="ruleId">
|
||||||
<el-input
|
<el-select
|
||||||
v-model="form.ruleName"
|
v-model="form.ruleId"
|
||||||
placeholder="请输入奖项名称"
|
placeholder="请选择奖项名称"
|
||||||
clearable
|
clearable
|
||||||
style="width: 100%" />
|
filterable
|
||||||
|
style="width: 100%"
|
||||||
|
@change="handleRewardRuleChange">
|
||||||
|
<el-option
|
||||||
|
v-for="item in rewardRuleList"
|
||||||
|
:key="item.id"
|
||||||
|
:label="item.ruleName"
|
||||||
|
:value="item.id" />
|
||||||
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="24" class="mb20">
|
<el-col :span="24" class="mb20">
|
||||||
@@ -66,6 +107,9 @@ import { ref, reactive, nextTick, onMounted } from 'vue'
|
|||||||
import { useMessage } from '/@/hooks/message'
|
import { useMessage } from '/@/hooks/message'
|
||||||
import { addObj, editObj, getDetail } from '/@/api/stuwork/rewarddorm'
|
import { addObj, editObj, getDetail } from '/@/api/stuwork/rewarddorm'
|
||||||
import { dormRoomList } from '/@/api/stuwork/dormroom'
|
import { dormRoomList } from '/@/api/stuwork/dormroom'
|
||||||
|
import { getList as getRewardRuleList } from '/@/api/stuwork/rewardrule'
|
||||||
|
import { queryAllSchoolYear } from '/@/api/basic/basicyear'
|
||||||
|
import { getDicts } from '/@/api/admin/dict'
|
||||||
|
|
||||||
const emit = defineEmits(['refresh'])
|
const emit = defineEmits(['refresh'])
|
||||||
|
|
||||||
@@ -75,10 +119,16 @@ const visible = ref(false)
|
|||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const operType = ref('add')
|
const operType = ref('add')
|
||||||
const roomList = ref<any[]>([])
|
const roomList = ref<any[]>([])
|
||||||
|
const schoolYearList = ref<any[]>([])
|
||||||
|
const schoolTermList = ref<any[]>([])
|
||||||
|
/** 奖项列表(来自接口 /stuwork/rewardrule/list) */
|
||||||
|
const rewardRuleList = ref<any[]>([])
|
||||||
|
|
||||||
// 提交表单数据
|
// 提交表单数据(与接口文档一致:roomNo, ruleId, ruleName, schoolYear, schoolTerm, remarks)
|
||||||
const form = reactive({
|
const form = reactive({
|
||||||
id: '',
|
id: '',
|
||||||
|
schoolYear: '',
|
||||||
|
schoolTerm: '',
|
||||||
roomNo: '',
|
roomNo: '',
|
||||||
ruleId: '',
|
ruleId: '',
|
||||||
ruleName: '',
|
ruleName: '',
|
||||||
@@ -87,11 +137,17 @@ const form = reactive({
|
|||||||
|
|
||||||
// 定义校验规则
|
// 定义校验规则
|
||||||
const dataRules = {
|
const dataRules = {
|
||||||
|
schoolYear: [
|
||||||
|
{ required: true, message: '请选择学年', trigger: 'change' }
|
||||||
|
],
|
||||||
|
schoolTerm: [
|
||||||
|
{ required: true, message: '请选择学期', trigger: 'change' }
|
||||||
|
],
|
||||||
roomNo: [
|
roomNo: [
|
||||||
{ required: true, message: '请选择宿舍号', trigger: 'change' }
|
{ required: true, message: '请选择宿舍号', trigger: 'change' }
|
||||||
],
|
],
|
||||||
ruleName: [
|
ruleId: [
|
||||||
{ required: true, message: '请输入奖项名称', trigger: 'blur' }
|
{ required: true, message: '请选择奖项名称', trigger: 'change' }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,6 +160,8 @@ const openDialog = async (type: string = 'add', row?: any) => {
|
|||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
dataFormRef.value?.resetFields()
|
dataFormRef.value?.resetFields()
|
||||||
form.id = ''
|
form.id = ''
|
||||||
|
form.schoolYear = ''
|
||||||
|
form.schoolTerm = ''
|
||||||
form.roomNo = ''
|
form.roomNo = ''
|
||||||
form.ruleId = ''
|
form.ruleId = ''
|
||||||
form.ruleName = ''
|
form.ruleName = ''
|
||||||
@@ -112,23 +170,27 @@ const openDialog = async (type: string = 'add', row?: any) => {
|
|||||||
// 编辑时填充数据
|
// 编辑时填充数据
|
||||||
if (type === 'edit' && row) {
|
if (type === 'edit' && row) {
|
||||||
form.id = row.id
|
form.id = row.id
|
||||||
|
form.schoolYear = row.schoolYear || ''
|
||||||
|
form.schoolTerm = row.schoolTerm || ''
|
||||||
form.roomNo = row.roomNo || ''
|
form.roomNo = row.roomNo || ''
|
||||||
form.ruleId = row.ruleId || ''
|
form.ruleId = row.ruleId || ''
|
||||||
form.ruleName = row.ruleName || ''
|
form.ruleName = row.ruleName || ''
|
||||||
form.remarks = row.remarks || ''
|
form.remarks = row.remarks || ''
|
||||||
|
handleRewardRuleChange(form.ruleId)
|
||||||
|
|
||||||
// 如果需要获取详情
|
// 如果需要获取详情
|
||||||
if (row.id && (!row.ruleName || !row.remarks)) {
|
if (row.id && (!row.ruleName || !row.schoolYear)) {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
getDetail(row.id).then((res: any) => {
|
getDetail(row.id).then((res: any) => {
|
||||||
if (res.data) {
|
if (res.data) {
|
||||||
|
form.schoolYear = res.data.schoolYear || ''
|
||||||
|
form.schoolTerm = res.data.schoolTerm || ''
|
||||||
form.roomNo = res.data.roomNo || ''
|
form.roomNo = res.data.roomNo || ''
|
||||||
form.ruleId = res.data.ruleId || ''
|
form.ruleId = res.data.ruleId || ''
|
||||||
form.ruleName = res.data.ruleName || ''
|
form.ruleName = res.data.ruleName || ''
|
||||||
form.remarks = res.data.remarks || ''
|
form.remarks = res.data.remarks || ''
|
||||||
}
|
}
|
||||||
}).catch((err) => {
|
}).catch(() => {}).finally(() => {
|
||||||
}).finally(() => {
|
|
||||||
loading.value = false
|
loading.value = false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -136,6 +198,12 @@ const openDialog = async (type: string = 'add', row?: any) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 选择奖项时同步 ruleName(提交时需要)
|
||||||
|
const handleRewardRuleChange = (id: string) => {
|
||||||
|
const item = rewardRuleList.value.find((r) => r.id === id)
|
||||||
|
form.ruleName = item ? item.ruleName : ''
|
||||||
|
}
|
||||||
|
|
||||||
// 提交表单
|
// 提交表单
|
||||||
const onSubmit = async () => {
|
const onSubmit = async () => {
|
||||||
if (!dataFormRef.value) return
|
if (!dataFormRef.value) return
|
||||||
@@ -145,10 +213,13 @@ const onSubmit = async () => {
|
|||||||
|
|
||||||
loading.value = true
|
loading.value = true
|
||||||
try {
|
try {
|
||||||
|
// 与接口文档一致:roomNo, ruleId, ruleName, schoolYear, schoolTerm, remarks
|
||||||
const submitData = {
|
const submitData = {
|
||||||
roomNo: form.roomNo,
|
roomNo: form.roomNo,
|
||||||
ruleId: form.ruleId || '', // 如果没有ruleId,传空字符串
|
ruleId: form.ruleId != null && form.ruleId !== '' ? String(form.ruleId) : '',
|
||||||
ruleName: form.ruleName,
|
ruleName: form.ruleName,
|
||||||
|
schoolYear: form.schoolYear,
|
||||||
|
schoolTerm: form.schoolTerm,
|
||||||
remarks: form.remarks || ''
|
remarks: form.remarks || ''
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -164,8 +235,8 @@ const onSubmit = async () => {
|
|||||||
}
|
}
|
||||||
visible.value = false
|
visible.value = false
|
||||||
emit('refresh')
|
emit('refresh')
|
||||||
} catch (err: any) {
|
} catch (_err) {
|
||||||
useMessage().error(err.msg || (operType.value === 'add' ? '新增失败' : '编辑失败'))
|
// 不再在此处弹 error:request 拦截器已统一弹过,避免重复两个 error 弹窗
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false
|
loading.value = false
|
||||||
}
|
}
|
||||||
@@ -186,9 +257,50 @@ const getRoomListData = async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取奖项列表(接口:GET /stuwork/rewardrule/list)
|
||||||
|
const getRewardRuleData = async () => {
|
||||||
|
try {
|
||||||
|
const res = await getRewardRuleList()
|
||||||
|
const list = res.data && Array.isArray(res.data) ? res.data : []
|
||||||
|
rewardRuleList.value = list
|
||||||
|
} catch (err) {
|
||||||
|
rewardRuleList.value = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取学年列表
|
||||||
|
const getSchoolYearData = async () => {
|
||||||
|
try {
|
||||||
|
const res = await queryAllSchoolYear()
|
||||||
|
schoolYearList.value = res.data && Array.isArray(res.data) ? res.data : []
|
||||||
|
} catch (err) {
|
||||||
|
schoolYearList.value = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取学期字典
|
||||||
|
const getSchoolTermData = async () => {
|
||||||
|
try {
|
||||||
|
const res = await getDicts('school_term')
|
||||||
|
if (res.data && Array.isArray(res.data)) {
|
||||||
|
schoolTermList.value = res.data.map((item: any) => ({
|
||||||
|
label: item.label || item.dictLabel || item.name,
|
||||||
|
value: item.value ?? item.dictValue ?? item.code
|
||||||
|
}))
|
||||||
|
} else {
|
||||||
|
schoolTermList.value = []
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
schoolTermList.value = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 初始化
|
// 初始化
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getRoomListData()
|
getRoomListData()
|
||||||
|
getRewardRuleData()
|
||||||
|
getSchoolYearData()
|
||||||
|
getSchoolTermData()
|
||||||
})
|
})
|
||||||
|
|
||||||
// 暴露方法
|
// 暴露方法
|
||||||
|
|||||||
@@ -104,12 +104,62 @@
|
|||||||
</el-table>
|
</el-table>
|
||||||
</el-row>
|
</el-row>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 查看详情弹窗(接口:queryDataByStuNo 通过学年学号查看详情,按当前学期筛选) -->
|
||||||
|
<el-dialog
|
||||||
|
v-model="viewDialogVisible"
|
||||||
|
title="学期操行考核详情"
|
||||||
|
width="800px"
|
||||||
|
destroy-on-close
|
||||||
|
@close="viewDetailList = []">
|
||||||
|
<div v-if="viewRow" class="view-summary">
|
||||||
|
<el-descriptions :column="2" border size="small">
|
||||||
|
<el-descriptions-item label="学号">{{ viewRow.stuNo }}</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="学期">{{ formatSchoolTerm(queryForm.schoolTerm) }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="学期总评" :span="2">
|
||||||
|
{{ viewRow.score != null && viewRow.score !== undefined ? Number(viewRow.score).toFixed(2) : '-' }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
</el-descriptions>
|
||||||
|
</div>
|
||||||
|
<div class="view-detail-title">考核记录</div>
|
||||||
|
<el-table
|
||||||
|
:data="viewDetailList"
|
||||||
|
v-loading="viewLoading"
|
||||||
|
border
|
||||||
|
size="small"
|
||||||
|
max-height="400"
|
||||||
|
:cell-style="tableStyle.cellStyle"
|
||||||
|
:header-cell-style="tableStyle.headerCellStyle">
|
||||||
|
<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="110" align="center" show-overflow-tooltip />
|
||||||
|
<el-table-column prop="conductType" label="类型" width="80" align="center">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-tag :type="scope.row.conductType === '1' ? 'success' : 'danger'" size="small">
|
||||||
|
{{ scope.row.conductType === '1' ? '加分' : scope.row.conductType === '0' ? '扣分' : scope.row.conductType || '-' }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="score" label="分数" width="80" align="center">
|
||||||
|
<template #default="scope">
|
||||||
|
{{ scope.row.score != null && scope.row.score !== undefined ? Number(scope.row.score) : '-' }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="description" label="情况记录" min-width="140" show-overflow-tooltip />
|
||||||
|
<el-table-column prop="remarks" label="备注" min-width="100" show-overflow-tooltip />
|
||||||
|
</el-table>
|
||||||
|
<template v-if="viewDetailList.length === 0 && !viewLoading">
|
||||||
|
<el-empty description="暂无考核记录" :image-size="80" />
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<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 } from "/@/api/stuwork/stuconduct";
|
import { getStuConductTerm, 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";
|
||||||
@@ -129,6 +179,10 @@ const schoolYearList = ref<any[]>([])
|
|||||||
const schoolTermList = ref<any[]>([])
|
const schoolTermList = ref<any[]>([])
|
||||||
const classList = ref<any[]>([])
|
const classList = ref<any[]>([])
|
||||||
const studentList = ref<any[]>([])
|
const studentList = ref<any[]>([])
|
||||||
|
const viewDialogVisible = ref(false)
|
||||||
|
const viewLoading = ref(false)
|
||||||
|
const viewRow = ref<any>(null)
|
||||||
|
const viewDetailList = ref<any[]>([])
|
||||||
|
|
||||||
// 查询表单
|
// 查询表单
|
||||||
const queryForm = reactive({
|
const queryForm = reactive({
|
||||||
@@ -263,14 +317,20 @@ const getDataList = async () => {
|
|||||||
} else {
|
} else {
|
||||||
studentList.value = []
|
studentList.value = []
|
||||||
}
|
}
|
||||||
} catch (err: any) {
|
} catch (_err) {
|
||||||
useMessage().error(err.msg || '获取数据列表失败')
|
|
||||||
studentList.value = []
|
studentList.value = []
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false
|
loading.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 格式化学期显示
|
||||||
|
const formatSchoolTerm = (value: string | number) => {
|
||||||
|
if (value === null || value === undefined || value === '') return '-'
|
||||||
|
const dictItem = schoolTermList.value.find((item: any) => item.value == value)
|
||||||
|
return dictItem ? dictItem.label : value
|
||||||
|
}
|
||||||
|
|
||||||
// 重置
|
// 重置
|
||||||
const handleReset = () => {
|
const handleReset = () => {
|
||||||
searchFormRef.value?.resetFields()
|
searchFormRef.value?.resetFields()
|
||||||
@@ -280,10 +340,29 @@ const handleReset = () => {
|
|||||||
studentList.value = []
|
studentList.value = []
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查看详情
|
// 查看详情(接口:GET /stuwork/stuconduct/queryDataByStuNo,按当前学年+学号拉取后筛本学期记录)
|
||||||
const handleView = (row: any) => {
|
const handleView = async (row: any) => {
|
||||||
// 可以跳转到详情页面或打开详情弹窗
|
if (!queryForm.schoolYear || !row.stuNo) {
|
||||||
useMessage().info('查看详情功能待实现')
|
useMessage().warning('缺少学年或学号')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
viewRow.value = row
|
||||||
|
viewDialogVisible.value = true
|
||||||
|
viewDetailList.value = []
|
||||||
|
viewLoading.value = true
|
||||||
|
try {
|
||||||
|
const res = await queryDataByStuNo({
|
||||||
|
schoolYear: queryForm.schoolYear,
|
||||||
|
stuNo: row.stuNo
|
||||||
|
})
|
||||||
|
const list = Array.isArray(res.data) ? res.data : []
|
||||||
|
const term = queryForm.schoolTerm
|
||||||
|
viewDetailList.value = term ? list.filter((r: any) => String(r.schoolTerm) === String(term)) : list
|
||||||
|
} catch (_err) {
|
||||||
|
viewDetailList.value = []
|
||||||
|
} finally {
|
||||||
|
viewLoading.value = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取学年列表
|
// 获取学年列表
|
||||||
@@ -350,6 +429,15 @@ onMounted(() => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.view-summary {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
.view-detail-title {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #303133;
|
||||||
|
}
|
||||||
|
|
||||||
// 确保页面可以滚动
|
// 确保页面可以滚动
|
||||||
.layout-padding {
|
.layout-padding {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|||||||
@@ -90,12 +90,61 @@
|
|||||||
</el-table>
|
</el-table>
|
||||||
</el-row>
|
</el-row>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 查看详情弹窗(接口:queryDataByStuNo 通过学年学号查看详情) -->
|
||||||
|
<el-dialog
|
||||||
|
v-model="viewDialogVisible"
|
||||||
|
title="学年操行考核详情"
|
||||||
|
width="800px"
|
||||||
|
destroy-on-close
|
||||||
|
@close="viewDetailList = []">
|
||||||
|
<div v-if="viewRow" class="view-summary">
|
||||||
|
<el-descriptions :column="2" border size="small">
|
||||||
|
<el-descriptions-item label="学号">{{ viewRow.stuNo }}</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="学年总评">
|
||||||
|
{{ viewRow.score != null && viewRow.score !== undefined ? Number(viewRow.score).toFixed(2) : '-' }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
</el-descriptions>
|
||||||
|
</div>
|
||||||
|
<div class="view-detail-title">考核记录</div>
|
||||||
|
<el-table
|
||||||
|
:data="viewDetailList"
|
||||||
|
v-loading="viewLoading"
|
||||||
|
border
|
||||||
|
size="small"
|
||||||
|
max-height="400"
|
||||||
|
:cell-style="tableStyle.cellStyle"
|
||||||
|
:header-cell-style="tableStyle.headerCellStyle">
|
||||||
|
<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="110" align="center" show-overflow-tooltip />
|
||||||
|
<el-table-column prop="conductType" label="类型" width="80" align="center">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-tag :type="scope.row.conductType === '1' ? 'success' : 'danger'" size="small">
|
||||||
|
{{ scope.row.conductType === '1' ? '加分' : scope.row.conductType === '0' ? '扣分' : scope.row.conductType || '-' }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="score" label="分数" width="80" align="center">
|
||||||
|
<template #default="scope">
|
||||||
|
{{ scope.row.score != null && scope.row.score !== undefined ? Number(scope.row.score) : '-' }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="description" label="情况记录" min-width="140" show-overflow-tooltip />
|
||||||
|
<el-table-column prop="remarks" label="备注" min-width="100" show-overflow-tooltip />
|
||||||
|
</el-table>
|
||||||
|
<template v-if="viewDetailList.length === 0 && !viewLoading">
|
||||||
|
<el-empty description="暂无考核记录" :image-size="80" />
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts" name="StuConductYear">
|
<script setup lang="ts" name="StuConductYear">
|
||||||
import { reactive, ref, onMounted, computed } from 'vue'
|
import { reactive, ref, onMounted, computed } from 'vue'
|
||||||
import { getStuConductYear } from "/@/api/stuwork/stuconduct";
|
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";
|
||||||
@@ -113,6 +162,10 @@ const loading = ref(false)
|
|||||||
const schoolYearList = ref<any[]>([])
|
const schoolYearList = ref<any[]>([])
|
||||||
const classList = ref<any[]>([])
|
const classList = ref<any[]>([])
|
||||||
const studentList = ref<any[]>([])
|
const studentList = ref<any[]>([])
|
||||||
|
const viewDialogVisible = ref(false)
|
||||||
|
const viewLoading = ref(false)
|
||||||
|
const viewRow = ref<any>(null)
|
||||||
|
const viewDetailList = ref<any[]>([])
|
||||||
|
|
||||||
// 查询表单
|
// 查询表单
|
||||||
const queryForm = reactive({
|
const queryForm = reactive({
|
||||||
@@ -245,8 +298,7 @@ const getDataList = async () => {
|
|||||||
} else {
|
} else {
|
||||||
studentList.value = []
|
studentList.value = []
|
||||||
}
|
}
|
||||||
} catch (err: any) {
|
} catch (_err) {
|
||||||
useMessage().error(err.msg || '获取数据列表失败')
|
|
||||||
studentList.value = []
|
studentList.value = []
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false
|
loading.value = false
|
||||||
@@ -261,10 +313,27 @@ const handleReset = () => {
|
|||||||
studentList.value = []
|
studentList.value = []
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查看详情
|
// 查看详情(接口:GET /stuwork/stuconduct/queryDataByStuNo,通过学年学号查看详情)
|
||||||
const handleView = (row: any) => {
|
const handleView = async (row: any) => {
|
||||||
// 可以跳转到详情页面或打开详情弹窗
|
if (!queryForm.schoolYear || !row.stuNo) {
|
||||||
useMessage().info('查看详情功能待实现')
|
useMessage().warning('缺少学年或学号')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
viewRow.value = row
|
||||||
|
viewDialogVisible.value = true
|
||||||
|
viewDetailList.value = []
|
||||||
|
viewLoading.value = true
|
||||||
|
try {
|
||||||
|
const res = await queryDataByStuNo({
|
||||||
|
schoolYear: queryForm.schoolYear,
|
||||||
|
stuNo: row.stuNo
|
||||||
|
})
|
||||||
|
viewDetailList.value = Array.isArray(res.data) ? res.data : []
|
||||||
|
} catch (_err) {
|
||||||
|
viewDetailList.value = []
|
||||||
|
} finally {
|
||||||
|
viewLoading.value = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取学年列表
|
// 获取学年列表
|
||||||
@@ -313,6 +382,15 @@ onMounted(() => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.view-summary {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
.view-detail-title {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #303133;
|
||||||
|
}
|
||||||
|
|
||||||
// 确保页面可以滚动
|
// 确保页面可以滚动
|
||||||
.layout-padding {
|
.layout-padding {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|||||||
309
src/views/stuwork/stugraducheck/index.vue
Normal file
309
src/views/stuwork/stugraducheck/index.vue
Normal file
@@ -0,0 +1,309 @@
|
|||||||
|
<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="graduYear">
|
||||||
|
<el-select
|
||||||
|
v-model="searchForm.graduYear"
|
||||||
|
placeholder="请选择毕业年份"
|
||||||
|
clearable
|
||||||
|
filterable
|
||||||
|
style="width: 140px">
|
||||||
|
<el-option
|
||||||
|
v-for="y in graduYearOptions"
|
||||||
|
:key="y"
|
||||||
|
:label="y + '年'"
|
||||||
|
:value="y" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="毕业状态" prop="status">
|
||||||
|
<el-select
|
||||||
|
v-model="searchForm.status"
|
||||||
|
placeholder="请选择"
|
||||||
|
clearable
|
||||||
|
style="width: 140px">
|
||||||
|
<el-option label="待确认" value="0" />
|
||||||
|
<el-option label="确认毕业" value="1" />
|
||||||
|
<el-option label="不可毕业" value="-1" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="毕业类型" prop="type">
|
||||||
|
<el-select
|
||||||
|
v-model="searchForm.type"
|
||||||
|
placeholder="请选择"
|
||||||
|
clearable
|
||||||
|
style="width: 120px">
|
||||||
|
<el-option label="段段清" value="1" />
|
||||||
|
<el-option label="正常毕业" value="2" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="学号" prop="stuNo">
|
||||||
|
<el-input
|
||||||
|
v-model="searchForm.stuNo"
|
||||||
|
placeholder="请输入学号"
|
||||||
|
clearable
|
||||||
|
style="width: 140px" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="姓名" prop="realName">
|
||||||
|
<el-input
|
||||||
|
v-model="searchForm.realName"
|
||||||
|
placeholder="请输入姓名"
|
||||||
|
clearable
|
||||||
|
style="width: 120px" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="学院" prop="deptCode">
|
||||||
|
<el-select
|
||||||
|
v-model="searchForm.deptCode"
|
||||||
|
placeholder="请选择学院"
|
||||||
|
clearable
|
||||||
|
filterable
|
||||||
|
style="width: 160px">
|
||||||
|
<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="班号" prop="classNo">
|
||||||
|
<el-input
|
||||||
|
v-model="searchForm.classNo"
|
||||||
|
placeholder="请输入班号"
|
||||||
|
clearable
|
||||||
|
style="width: 120px" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" icon="Search" @click="handleSearch">查询</el-button>
|
||||||
|
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<!-- 内容卡片 -->
|
||||||
|
<el-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
|
||||||
|
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"
|
||||||
|
:min-width="col.minWidth"
|
||||||
|
:width="col.width"
|
||||||
|
show-overflow-tooltip
|
||||||
|
align="center">
|
||||||
|
<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 v-if="col.prop === 'status'" #default="scope">
|
||||||
|
<el-tag :type="statusTagType(scope.row.status)" size="small">
|
||||||
|
{{ formatStatus(scope.row.status) }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
<template v-else-if="col.prop === 'type'" #default="scope">
|
||||||
|
{{ scope.row.type === '1' ? '段段清' : scope.row.type === '2' ? '正常毕业' : scope.row.type || '-' }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</template>
|
||||||
|
<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="Stugraducheck">
|
||||||
|
import { reactive, ref, computed, onMounted } from 'vue'
|
||||||
|
import { useRoute } from 'vue-router'
|
||||||
|
import { BasicTableProps, useTable } from '/@/hooks/table'
|
||||||
|
import { fetchList } from '/@/api/stuwork/stugraducheck'
|
||||||
|
import { getDeptList } from '/@/api/basic/basicclass'
|
||||||
|
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
||||||
|
import {
|
||||||
|
List,
|
||||||
|
Calendar,
|
||||||
|
UserFilled,
|
||||||
|
Document,
|
||||||
|
Menu,
|
||||||
|
Search,
|
||||||
|
Grid
|
||||||
|
} 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 deptList = ref<any[]>([])
|
||||||
|
|
||||||
|
// 毕业年份:当前年前后各 5 年
|
||||||
|
const graduYearOptions = computed(() => {
|
||||||
|
const y = new Date().getFullYear()
|
||||||
|
return Array.from({ length: 11 }, (_, i) => y - 5 + i)
|
||||||
|
})
|
||||||
|
|
||||||
|
// 表格列配置(与其它页面一致,icon 写在列上)
|
||||||
|
const tableColumns = [
|
||||||
|
{ prop: 'stuNo', label: '学号', icon: UserFilled },
|
||||||
|
{ prop: 'realName', label: '姓名', icon: UserFilled },
|
||||||
|
{ prop: 'classNo', label: '班号', icon: Grid },
|
||||||
|
{ prop: 'majorName', label: '专业名称', icon: Document, minWidth: 140 },
|
||||||
|
{ prop: 'graduYear', label: '毕业年份', icon: Calendar },
|
||||||
|
{ prop: 'status', label: '毕业状态', icon: Document },
|
||||||
|
{ prop: 'type', label: '毕业类型', icon: Document },
|
||||||
|
{ prop: 'scoreCondition', label: '学分情况', icon: Document, minWidth: 100 },
|
||||||
|
{ prop: 'skillCondition', label: '技能情况', icon: Document, minWidth: 100 },
|
||||||
|
{ prop: 'conductCondition', label: '操行情况', icon: Document, minWidth: 100 }
|
||||||
|
]
|
||||||
|
|
||||||
|
const {
|
||||||
|
visibleColumns,
|
||||||
|
visibleColumnsSorted,
|
||||||
|
checkColumnVisible,
|
||||||
|
handleColumnChange,
|
||||||
|
handleColumnOrderChange
|
||||||
|
} = useTableColumnControl(tableColumns, route.path)
|
||||||
|
|
||||||
|
// 搜索表单(与接口文档一致:graduYear, status, type, stuNo, realName, deptCode, classCode 等)
|
||||||
|
const searchForm = reactive({
|
||||||
|
graduYear: '',
|
||||||
|
status: '',
|
||||||
|
type: '',
|
||||||
|
stuNo: '',
|
||||||
|
realName: '',
|
||||||
|
deptCode: '',
|
||||||
|
classNo: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
const state: BasicTableProps = reactive<BasicTableProps>({
|
||||||
|
queryForm: searchForm,
|
||||||
|
pageList: fetchList,
|
||||||
|
props: {
|
||||||
|
item: 'records',
|
||||||
|
totalCount: 'total'
|
||||||
|
},
|
||||||
|
createdIsNeed: true
|
||||||
|
})
|
||||||
|
|
||||||
|
const {
|
||||||
|
getDataList,
|
||||||
|
currentChangeHandle,
|
||||||
|
sizeChangeHandle,
|
||||||
|
tableStyle
|
||||||
|
} = useTable(state)
|
||||||
|
|
||||||
|
const formatStatus = (status: string) => {
|
||||||
|
const map: Record<string, string> = { '0': '待确认', '1': '确认毕业', '-1': '不可毕业' }
|
||||||
|
return map[status] || status || '-'
|
||||||
|
}
|
||||||
|
|
||||||
|
const statusTagType = (status: string) => {
|
||||||
|
const map: Record<string, string> = { '0': 'warning', '1': 'success', '-1': 'danger' }
|
||||||
|
return map[status] || 'info'
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSearch = () => {
|
||||||
|
getDataList()
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleReset = () => {
|
||||||
|
searchFormRef.value?.resetFields()
|
||||||
|
searchForm.graduYear = ''
|
||||||
|
searchForm.status = ''
|
||||||
|
searchForm.type = ''
|
||||||
|
searchForm.stuNo = ''
|
||||||
|
searchForm.realName = ''
|
||||||
|
searchForm.deptCode = ''
|
||||||
|
searchForm.classNo = ''
|
||||||
|
getDataList()
|
||||||
|
}
|
||||||
|
|
||||||
|
const loadDeptList = async () => {
|
||||||
|
try {
|
||||||
|
const res = await getDeptList()
|
||||||
|
deptList.value = Array.isArray(res.data) ? res.data : []
|
||||||
|
} catch (err) {
|
||||||
|
deptList.value = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
loadDeptList()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
@import '/@/assets/styles/modern-page.scss';
|
||||||
|
</style>
|
||||||
@@ -188,9 +188,13 @@ const handleClassChange = async (classCode: string) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await queryStudentListByClass({ classCode, current: 1, size: 10000 })
|
// 接口文档:GET 仅 classCode,返回 data 为数组
|
||||||
if (res.data && Array.isArray(res.data.records)) {
|
const res = await queryStudentListByClass({ classCode })
|
||||||
studentList.value = res.data.records
|
const data = res?.data
|
||||||
|
if (Array.isArray(data)) {
|
||||||
|
studentList.value = data
|
||||||
|
} else if (data?.records && Array.isArray(data.records)) {
|
||||||
|
studentList.value = data.records
|
||||||
} else {
|
} else {
|
||||||
studentList.value = []
|
studentList.value = []
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -181,9 +181,13 @@ const handleClassChange = async (classCode: string) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await queryStudentListByClass({ classCode, current: 1, size: 10000 })
|
// 接口文档:GET,仅 classCode;返回 data 为数组,非 data.records
|
||||||
if (res.data && Array.isArray(res.data.records)) {
|
const res = await queryStudentListByClass({ classCode })
|
||||||
studentList.value = res.data.records
|
const data = res?.data
|
||||||
|
if (Array.isArray(data)) {
|
||||||
|
studentList.value = data
|
||||||
|
} else if (data?.records && Array.isArray(data.records)) {
|
||||||
|
studentList.value = data.records
|
||||||
} else {
|
} else {
|
||||||
studentList.value = []
|
studentList.value = []
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -305,13 +305,18 @@ const getClassListData = async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取学生列表
|
// 获取学生列表(接口文档:GET 仅 classCode,返回 data 为数组)
|
||||||
const getStudentListData = async () => {
|
const getStudentListData = async () => {
|
||||||
if (!form.classCode) return
|
if (!form.classCode) return
|
||||||
try {
|
try {
|
||||||
const res = await queryStudentListByClass(form.classCode)
|
const res = await queryStudentListByClass({ classCode: form.classCode })
|
||||||
if (res.data) {
|
const data = res?.data
|
||||||
studentList.value = Array.isArray(res.data) ? res.data : []
|
if (Array.isArray(data)) {
|
||||||
|
studentList.value = data
|
||||||
|
} else if (data?.records && Array.isArray(data.records)) {
|
||||||
|
studentList.value = data.records
|
||||||
|
} else {
|
||||||
|
studentList.value = []
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
studentList.value = []
|
studentList.value = []
|
||||||
|
|||||||
@@ -207,7 +207,7 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</template>
|
</template>
|
||||||
<el-table-column label="操作" width="150" align="center" fixed="right">
|
<el-table-column label="操作" width="220" align="center" fixed="right">
|
||||||
<template #header>
|
<template #header>
|
||||||
<el-icon><Setting /></el-icon>
|
<el-icon><Setting /></el-icon>
|
||||||
<span style="margin-left: 4px">操作</span>
|
<span style="margin-left: 4px">操作</span>
|
||||||
@@ -220,6 +220,13 @@
|
|||||||
@click="handleEdit(scope.row)">
|
@click="handleEdit(scope.row)">
|
||||||
编辑
|
编辑
|
||||||
</el-button>
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
icon="RefreshLeft"
|
||||||
|
link
|
||||||
|
type="warning"
|
||||||
|
@click="handleCancel(scope.row)">
|
||||||
|
异动撤销
|
||||||
|
</el-button>
|
||||||
<el-button
|
<el-button
|
||||||
icon="Delete"
|
icon="Delete"
|
||||||
link
|
link
|
||||||
@@ -250,7 +257,7 @@
|
|||||||
import { reactive, ref, onMounted, computed, nextTick } from 'vue'
|
import { reactive, ref, onMounted, computed, nextTick } from 'vue'
|
||||||
import { useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
import { BasicTableProps, useTable } from "/@/hooks/table";
|
import { BasicTableProps, useTable } from "/@/hooks/table";
|
||||||
import { fetchList, delObj, exportData } from "/@/api/stuwork/stuturnover";
|
import { fetchList, delObj, exportData, cancelObj } from "/@/api/stuwork/stuturnover";
|
||||||
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 { getDeptListByLevelTwo } from "/@/api/basic/basicdept";
|
import { getDeptListByLevelTwo } from "/@/api/basic/basicdept";
|
||||||
@@ -258,7 +265,7 @@ import { list as getClassList } from "/@/api/basic/basicclass";
|
|||||||
import { useMessage, useMessageBox } from "/@/hooks/message";
|
import { useMessage, useMessageBox } from "/@/hooks/message";
|
||||||
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
||||||
import FormDialog from './form.vue'
|
import FormDialog from './form.vue'
|
||||||
import { List, Calendar, Clock, OfficeBuilding, Grid, CreditCard, Avatar, Collection, Document, Setting, Menu, Search } from '@element-plus/icons-vue'
|
import { List, Calendar, Clock, OfficeBuilding, Grid, CreditCard, Avatar, Collection, Document, Setting, Menu, Search, RefreshLeft } from '@element-plus/icons-vue'
|
||||||
import { useTableColumnControl } from '/@/hooks/tableColumn'
|
import { useTableColumnControl } from '/@/hooks/tableColumn'
|
||||||
|
|
||||||
|
|
||||||
@@ -400,6 +407,20 @@ const handleEdit = (row: any) => {
|
|||||||
formDialogRef.value?.openDialog('edit', row)
|
formDialogRef.value?.openDialog('edit', row)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 异动撤销
|
||||||
|
const handleCancel = async (row: any) => {
|
||||||
|
try {
|
||||||
|
await useMessageBox().confirm('确定要撤销该条学籍异动吗?')
|
||||||
|
await cancelObj([row.id])
|
||||||
|
useMessage().success('撤销成功')
|
||||||
|
getDataList()
|
||||||
|
} catch (err: any) {
|
||||||
|
if (err !== 'cancel') {
|
||||||
|
useMessage().error(err?.msg || '撤销失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 删除
|
// 删除
|
||||||
const handleDelete = async (row: any) => {
|
const handleDelete = async (row: any) => {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ import { ref, reactive, nextTick, onMounted } from 'vue'
|
|||||||
import { useMessage } from '/@/hooks/message'
|
import { useMessage } from '/@/hooks/message'
|
||||||
import { addObj } from '/@/api/stuwork/stuworkstudyalternate'
|
import { addObj } from '/@/api/stuwork/stuworkstudyalternate'
|
||||||
import { getClassListByRole } from '/@/api/basic/basicclass'
|
import { getClassListByRole } from '/@/api/basic/basicclass'
|
||||||
|
import { queryAllStudentByClassCode } from '/@/api/basic/basicstudent'
|
||||||
|
|
||||||
const emit = defineEmits(['refresh'])
|
const emit = defineEmits(['refresh'])
|
||||||
|
|
||||||
@@ -94,7 +95,7 @@ const openDialog = async () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 提交表单
|
// 提交表单(接口文档要求 classCode、dateRange、stuNoList)
|
||||||
const onSubmit = async () => {
|
const onSubmit = async () => {
|
||||||
if (!dataFormRef.value) return
|
if (!dataFormRef.value) return
|
||||||
|
|
||||||
@@ -108,15 +109,27 @@ const onSubmit = async () => {
|
|||||||
|
|
||||||
loading.value = true
|
loading.value = true
|
||||||
try {
|
try {
|
||||||
|
// 按班级获取学生列表,提取学号作为 stuNoList(接口必传)
|
||||||
|
const stuRes = await queryAllStudentByClassCode(form.classCode)
|
||||||
|
const list = stuRes?.data && Array.isArray(stuRes.data) ? stuRes.data : []
|
||||||
|
const stuNoList = list.map((s: any) => s.stuNo).filter(Boolean)
|
||||||
|
if (stuNoList.length === 0) {
|
||||||
|
useMessage().warning('该班级暂无学生,无法新增工学交替')
|
||||||
|
loading.value = false
|
||||||
|
return
|
||||||
|
}
|
||||||
await addObj({
|
await addObj({
|
||||||
classCode: form.classCode,
|
classCode: form.classCode,
|
||||||
dateRange: form.dateRange
|
dateRange: form.dateRange,
|
||||||
|
stuNoList
|
||||||
})
|
})
|
||||||
useMessage().success('新增成功')
|
useMessage().success('新增成功')
|
||||||
visible.value = false
|
visible.value = false
|
||||||
emit('refresh')
|
emit('refresh')
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
useMessage().error(err.msg || '新增失败')
|
if (!err?._messageShown) {
|
||||||
|
useMessage().error(err?.msg || '新增失败')
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false
|
loading.value = false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -170,7 +170,7 @@
|
|||||||
:min-width="col.minWidth"
|
:min-width="col.minWidth"
|
||||||
:width="col.width">
|
:width="col.width">
|
||||||
<template #header>
|
<template #header>
|
||||||
<el-icon><component :is="columnConfigMap[col.prop]?.icon || Calendar" /></el-icon>
|
<el-icon><component :is="columnConfigMap[col.prop || '']?.icon || Calendar" /></el-icon>
|
||||||
<span style="margin-left: 4px">{{ col.label }}</span>
|
<span style="margin-left: 4px">{{ col.label }}</span>
|
||||||
</template>
|
</template>
|
||||||
<!-- 学期列特殊模板 -->
|
<!-- 学期列特殊模板 -->
|
||||||
@@ -290,7 +290,7 @@ const {
|
|||||||
checkColumnVisible,
|
checkColumnVisible,
|
||||||
handleColumnChange,
|
handleColumnChange,
|
||||||
handleColumnOrderChange
|
handleColumnOrderChange
|
||||||
} = useTableColumnControl(tableColumns, route.path)
|
} = useTableColumnControl(tableColumns, { storageKey: route.path })
|
||||||
|
|
||||||
// 搜索表单
|
// 搜索表单
|
||||||
const searchForm = reactive({
|
const searchForm = reactive({
|
||||||
@@ -303,10 +303,20 @@ const searchForm = reactive({
|
|||||||
dateRange: null as [string, string] | null
|
dateRange: null as [string, string] | null
|
||||||
})
|
})
|
||||||
|
|
||||||
// 配置 useTable
|
// 配置 useTable(接口返回 data.tableData.records / data.tableData.total,需包装以适配 hook 的 res.data[props.item] 取数)
|
||||||
const state: BasicTableProps = reactive<BasicTableProps>({
|
const state: BasicTableProps = reactive<BasicTableProps>({
|
||||||
queryForm: searchForm,
|
queryForm: searchForm,
|
||||||
pageList: fetchList,
|
pageList: async (params: any) => {
|
||||||
|
const res = await fetchList(params)
|
||||||
|
const data = res?.data
|
||||||
|
const tableData = data?.tableData
|
||||||
|
return {
|
||||||
|
data: {
|
||||||
|
'tableData.records': tableData?.records ?? [],
|
||||||
|
'tableData.total': tableData?.total ?? data?.total ?? 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
props: {
|
props: {
|
||||||
item: 'tableData.records',
|
item: 'tableData.records',
|
||||||
totalCount: 'tableData.total'
|
totalCount: 'tableData.total'
|
||||||
@@ -397,13 +407,25 @@ 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 ?? item.code
|
||||||
|
}))
|
||||||
|
} else {
|
||||||
|
schoolTermList.value = []
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
schoolTermList.value = []
|
schoolTermList.value = []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 学期列展示:按字典转成文案
|
||||||
|
const formatSchoolTerm = (value: string | number) => {
|
||||||
|
if (value === '' || value == null) return '-'
|
||||||
|
const item = schoolTermList.value.find((t: any) => String(t.value) === String(value))
|
||||||
|
return item ? item.label : value
|
||||||
|
}
|
||||||
|
|
||||||
// 初始化
|
// 初始化
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getDeptListData()
|
getDeptListData()
|
||||||
|
|||||||
Reference in New Issue
Block a user