兵马未动 粮草先行
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) => {
|
||||
return request({
|
||||
url: '/basic/basicclass',
|
||||
method: 'put',
|
||||
url: '/basic/basicclass/edit',
|
||||
method: 'post',
|
||||
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
|
||||
|
||||
@@ -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
|
||||
|
||||
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
|
||||
|
||||
@@ -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.onCascaded) await state.onCascaded(state);
|
||||
} catch (err: any) {
|
||||
// 捕获异常并显示错误提示
|
||||
ElMessage.error(err.msg || err.data.msg);
|
||||
// 全局拦截器已展示过错误时不再重复弹窗
|
||||
if (!err?._messageShown) {
|
||||
ElMessage.error(err?.msg || err?.data?.msg || '请求失败');
|
||||
}
|
||||
} finally {
|
||||
// 结束加载数据,设置state.loading为false
|
||||
state.loading = false;
|
||||
|
||||
@@ -97,9 +97,10 @@ service.interceptors.request.use(
|
||||
*/
|
||||
const handleResponse = (response: AxiosResponse<any>) => {
|
||||
if (response.data.code === 1) {
|
||||
// 业务错误,统一弹出错误提示
|
||||
// 业务错误,统一弹出错误提示(标记已展示,避免 hook/页面 catch 再次弹窗)
|
||||
if (response.data.msg) {
|
||||
useMessage().error(response.data.msg);
|
||||
response.data._messageShown = true;
|
||||
}
|
||||
throw response.data;
|
||||
}
|
||||
|
||||
@@ -204,7 +204,8 @@ const dataRules = ref({
|
||||
{ required: true, message: '学院不能为空', trigger: 'change' }
|
||||
],
|
||||
classCode: [
|
||||
{ required: true, message: '班级代码不能为空', trigger: 'blur' }
|
||||
{ required: true, message: '班级代码不能为空', trigger: 'blur' },
|
||||
{ min: 4, message: '班级代码至少4位(班号取后4位)', trigger: 'blur' }
|
||||
],
|
||||
classNo: [
|
||||
{ required: true, message: '班号不能为空', trigger: 'blur' }
|
||||
@@ -406,11 +407,11 @@ watch(() => form.enterDate, (newVal) => {
|
||||
}
|
||||
})
|
||||
|
||||
// 监听班级代码,取后四位为班号
|
||||
// 监听班级代码,取后四位为班号(班级代码至少4位)
|
||||
watch(() => form.classCode, (newVal) => {
|
||||
if (newVal) {
|
||||
const length = newVal.length
|
||||
if (length > 4) {
|
||||
if (length >= 4) {
|
||||
// 只在新增模式下自动填充班号,编辑模式下如果班号为空才填充
|
||||
if (!form.id || !form.classNo) {
|
||||
form.classNo = newVal.substring(length - 4, length)
|
||||
|
||||
@@ -196,7 +196,7 @@
|
||||
</el-tag>
|
||||
</template>
|
||||
</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">
|
||||
<el-button
|
||||
icon="View"
|
||||
|
||||
@@ -4,25 +4,56 @@
|
||||
v-model="visible"
|
||||
:close-on-click-modal="false"
|
||||
draggable
|
||||
width="800px">
|
||||
<div v-loading="loading" class="detail-container" v-if="detailData">
|
||||
<el-descriptions :column="2" border>
|
||||
width="960px">
|
||||
<div class="detail-container">
|
||||
<!-- 活动主信息(来自列表行) -->
|
||||
<el-descriptions v-if="mainInfo.activityTheme" :column="2" border class="mb16">
|
||||
<el-descriptions-item label="活动主题" :span="2">
|
||||
{{ detailData.activityTheme || '-' }}
|
||||
{{ mainInfo.activityTheme || '-' }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="活动说明" :span="2">
|
||||
{{ detailData.remarks || '-' }}
|
||||
{{ mainInfo.remarks || '-' }}
|
||||
</el-descriptions-item>
|
||||
<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 label="开始时间">
|
||||
{{ parseTime(detailData.startTime, '{y}-{m}-{d}') }}
|
||||
{{ mainInfo.startTime ? parseTime(mainInfo.startTime, '{y}-{m}-{d}') : '-' }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="结束时间">
|
||||
{{ parseTime(detailData.endTime, '{y}-{m}-{d}') }}
|
||||
{{ mainInfo.endTime ? parseTime(mainInfo.endTime, '{y}-{m}-{d}') : '-' }}
|
||||
</el-descriptions-item>
|
||||
</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>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
@@ -33,47 +64,40 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="ActivityInfoDetailDialog">
|
||||
import { ref } from 'vue'
|
||||
import { getDetail } from '/@/api/stuwork/activityinfo'
|
||||
import { ref, reactive } from 'vue'
|
||||
import { getActivityInfoSubList } from '/@/api/stuwork/activityinfosub'
|
||||
import { parseTime } from '/@/utils/formatTime'
|
||||
import { useMessage } from '/@/hooks/message'
|
||||
|
||||
// 定义变量内容
|
||||
const visible = 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
|
||||
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 {
|
||||
const res = await getDetail(id)
|
||||
if (res.data) {
|
||||
// 根据接口文档,返回的数据可能是 { records: [...], total: ... } 格式
|
||||
// 如果是列表格式,取第一条;如果是对象,直接使用
|
||||
if (res.data.records && Array.isArray(res.data.records) && res.data.records.length > 0) {
|
||||
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
|
||||
const res = await getActivityInfoSubList(activityInfoId)
|
||||
const data = res.data
|
||||
subList.value = Array.isArray(data) ? data : []
|
||||
} catch (_err) {
|
||||
subList.value = []
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 暴露方法
|
||||
defineExpose({
|
||||
openDialog
|
||||
})
|
||||
@@ -81,7 +105,15 @@ defineExpose({
|
||||
|
||||
<style scoped lang="scss">
|
||||
.detail-container {
|
||||
padding: 20px 0;
|
||||
padding: 8px 0;
|
||||
}
|
||||
.mb16 {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.sub-title {
|
||||
margin-bottom: 8px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
@@ -250,9 +250,9 @@ const {
|
||||
tableStyle: _tableStyle
|
||||
} = useTable(state)
|
||||
|
||||
// 查看详情
|
||||
// 查看详情(接口:getActivityInfoSubList,传入活动ID与行数据用于展示)
|
||||
const handleView = (row: any) => {
|
||||
detailDialogRef.value?.openDialog(row.id)
|
||||
detailDialogRef.value?.openDialog(row.id, row)
|
||||
}
|
||||
|
||||
// 编辑
|
||||
|
||||
@@ -48,6 +48,17 @@
|
||||
style="width: 100%" />
|
||||
</el-form-item>
|
||||
</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-form-item label="备注" prop="remarks">
|
||||
<el-input
|
||||
@@ -86,12 +97,13 @@ const loading = ref(false)
|
||||
const operType = ref('add')
|
||||
const categoryList = ref<any[]>([])
|
||||
|
||||
// 提交表单数据
|
||||
// 提交表单数据(与接口文档一致:edit 含 score 默认扣分值)
|
||||
const form = reactive({
|
||||
id: '',
|
||||
categortyId: '',
|
||||
pointName: '',
|
||||
standard: '',
|
||||
score: undefined as number | undefined,
|
||||
remarks: ''
|
||||
})
|
||||
|
||||
@@ -117,6 +129,7 @@ const openDialog = async (type: string = 'add', row?: any) => {
|
||||
form.categortyId = ''
|
||||
form.pointName = ''
|
||||
form.standard = ''
|
||||
form.score = undefined
|
||||
form.remarks = ''
|
||||
|
||||
// 编辑时填充数据
|
||||
@@ -125,20 +138,21 @@ const openDialog = async (type: string = 'add', row?: any) => {
|
||||
form.categortyId = row.categortyId || ''
|
||||
form.pointName = row.pointName || ''
|
||||
form.standard = row.standard || ''
|
||||
form.score = row.score !== undefined && row.score !== null ? Number(row.score) : undefined
|
||||
form.remarks = row.remarks || ''
|
||||
|
||||
// 如果需要获取详情
|
||||
if (row.id && (!row.pointName || !row.standard)) {
|
||||
// 如果需要获取详情(含 score)
|
||||
if (row.id && (!row.pointName || row.standard === undefined || row.score === undefined)) {
|
||||
loading.value = true
|
||||
getDetail(row.id).then((res: any) => {
|
||||
if (res.data) {
|
||||
form.categortyId = res.data.categortyId || ''
|
||||
form.pointName = res.data.pointName || ''
|
||||
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 || ''
|
||||
}
|
||||
}).catch((err) => {
|
||||
}).finally(() => {
|
||||
}).catch(() => {}).finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
@@ -155,10 +169,12 @@ const onSubmit = async () => {
|
||||
|
||||
loading.value = true
|
||||
try {
|
||||
// 与接口文档一致:categortyId, pointName, standard, score, remarks
|
||||
const submitData = {
|
||||
categortyId: form.categortyId,
|
||||
pointName: form.pointName,
|
||||
standard: form.standard || '',
|
||||
score: form.score !== undefined && form.score !== null ? Number(form.score) : 0,
|
||||
remarks: form.remarks || ''
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,21 @@
|
||||
:inline="true"
|
||||
@keyup.enter="getDataList"
|
||||
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-input
|
||||
v-model="state.queryForm.pointName"
|
||||
@@ -155,9 +170,10 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="AssessmentPoint">
|
||||
import { reactive, ref } from 'vue'
|
||||
import { reactive, ref, onMounted } from 'vue'
|
||||
import { BasicTableProps, useTable } from "/@/hooks/table";
|
||||
import { fetchList, delObj } from "/@/api/stuwork/assessmentpoint";
|
||||
import { getList as getAssessmentCategoryList } from "/@/api/stuwork/assessmentcategory";
|
||||
import { useMessage, useMessageBox } from "/@/hooks/message";
|
||||
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
||||
import FormDialog from './form.vue'
|
||||
@@ -169,6 +185,7 @@ const formDialogRef = ref()
|
||||
const searchFormRef = ref()
|
||||
const columnControlRef = ref()
|
||||
const showSearch = ref(true)
|
||||
const categoryList = ref<any[]>([])
|
||||
|
||||
// 表格列配置
|
||||
const tableColumns = [
|
||||
@@ -194,9 +211,10 @@ const tableStyle = {
|
||||
headerCellStyle: { background: '#f5f7fa', color: '#606266', fontWeight: 'bold' }
|
||||
}
|
||||
|
||||
// 配置 useTable
|
||||
// 配置 useTable(分页接口支持 categortyId、pointName,与考核项对应)
|
||||
const state: BasicTableProps = reactive<BasicTableProps>({
|
||||
queryForm: {
|
||||
categortyId: '',
|
||||
pointName: ''
|
||||
},
|
||||
pageList: fetchList,
|
||||
@@ -215,13 +233,28 @@ const {
|
||||
tableStyle: _tableStyle
|
||||
} = 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 = () => {
|
||||
searchFormRef.value?.resetFields()
|
||||
state.queryForm.categortyId = ''
|
||||
state.queryForm.pointName = ''
|
||||
getDataList()
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getCategoryList()
|
||||
})
|
||||
|
||||
// 编辑
|
||||
const handleEdit = (row: any) => {
|
||||
formDialogRef.value?.openDialog('edit', row)
|
||||
|
||||
@@ -69,8 +69,7 @@
|
||||
v-model="form.score"
|
||||
:precision="0"
|
||||
:step="1"
|
||||
:min="0"
|
||||
placeholder="请输入分数"
|
||||
placeholder="请输入分数(可为负数)"
|
||||
style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
@@ -12,6 +12,53 @@
|
||||
label-width="100px"
|
||||
v-loading="loading">
|
||||
<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-form-item label="标题" prop="title">
|
||||
<el-input
|
||||
@@ -52,9 +99,12 @@
|
||||
</template>
|
||||
|
||||
<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 { 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'
|
||||
|
||||
const emit = defineEmits(['refresh'])
|
||||
@@ -64,10 +114,16 @@ const dataFormRef = ref()
|
||||
const visible = ref(false)
|
||||
const loading = ref(false)
|
||||
const operType = ref('add') // add 或 edit
|
||||
const schoolYearList = ref<any[]>([])
|
||||
const schoolTermList = ref<any[]>([])
|
||||
const classList = ref<any[]>([])
|
||||
|
||||
// 提交表单数据
|
||||
const form = reactive({
|
||||
id: '',
|
||||
schoolYear: '',
|
||||
schoolTerm: '',
|
||||
classCode: '',
|
||||
title: '',
|
||||
content: '',
|
||||
remarks: ''
|
||||
@@ -75,6 +131,15 @@ const form = reactive({
|
||||
|
||||
// 定义校验规则
|
||||
const dataRules = {
|
||||
schoolYear: [
|
||||
{ required: true, message: '请选择学年', trigger: 'change' }
|
||||
],
|
||||
schoolTerm: [
|
||||
{ required: true, message: '请选择学期', trigger: 'change' }
|
||||
],
|
||||
classCode: [
|
||||
{ required: true, message: '请选择班号', trigger: 'change' }
|
||||
],
|
||||
title: [
|
||||
{ required: true, message: '请输入标题', trigger: 'blur' }
|
||||
],
|
||||
@@ -92,6 +157,9 @@ const openDialog = async (type: string = 'add', row?: any) => {
|
||||
nextTick(() => {
|
||||
dataFormRef.value?.resetFields()
|
||||
form.id = ''
|
||||
form.schoolYear = ''
|
||||
form.schoolTerm = ''
|
||||
form.classCode = ''
|
||||
form.title = ''
|
||||
form.content = ''
|
||||
form.remarks = ''
|
||||
@@ -99,6 +167,9 @@ const openDialog = async (type: string = 'add', row?: any) => {
|
||||
// 编辑时填充数据
|
||||
if (type === 'edit' && row) {
|
||||
form.id = row.id
|
||||
form.schoolYear = row.schoolYear || ''
|
||||
form.schoolTerm = row.schoolTerm || ''
|
||||
form.classCode = row.classCode || ''
|
||||
form.title = row.title || ''
|
||||
form.content = row.content || ''
|
||||
form.remarks = row.remarks || ''
|
||||
@@ -108,6 +179,9 @@ const openDialog = async (type: string = 'add', row?: any) => {
|
||||
loading.value = true
|
||||
getDetail(row.id).then((res: any) => {
|
||||
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.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 () => {
|
||||
if (!dataFormRef.value) return
|
||||
@@ -129,6 +240,9 @@ const onSubmit = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const submitData = {
|
||||
schoolYear: form.schoolYear,
|
||||
schoolTerm: form.schoolTerm,
|
||||
classCode: form.classCode,
|
||||
title: form.title,
|
||||
content: form.content,
|
||||
remarks: form.remarks
|
||||
@@ -154,6 +268,13 @@ const onSubmit = async () => {
|
||||
})
|
||||
}
|
||||
|
||||
// 初始化:加载学年、学期、班级
|
||||
onMounted(() => {
|
||||
getSchoolYearList()
|
||||
getSchoolTermDict()
|
||||
getClassListData()
|
||||
})
|
||||
|
||||
// 暴露方法
|
||||
defineExpose({
|
||||
openDialog
|
||||
|
||||
@@ -79,7 +79,7 @@
|
||||
<script setup lang="ts" name="ClassroomBaseArrangeDialog">
|
||||
import { ref, reactive, nextTick, onMounted } from 'vue'
|
||||
import { useMessage } from '/@/hooks/message'
|
||||
import { editObj } from '/@/api/stuwork/classroombase'
|
||||
import { addClassRoomAssign } from '/@/api/stuwork/teachclassroomassign'
|
||||
import { queryAllClass } from '/@/api/basic/basicclass'
|
||||
import { getClassRoomList } from '/@/api/stuwork/teachclassroom'
|
||||
import { getBuildingList } from '/@/api/stuwork/teachbuilding'
|
||||
@@ -150,17 +150,16 @@ const onSubmit = async () => {
|
||||
|
||||
loading.value = true
|
||||
try {
|
||||
await editObj({
|
||||
id: form.id,
|
||||
await addClassRoomAssign({
|
||||
buildingNo: form.buildingNo,
|
||||
classCode: form.classCode,
|
||||
position: form.position
|
||||
position: form.position,
|
||||
classCode: form.classCode
|
||||
})
|
||||
useMessage().success('教室安排成功')
|
||||
visible.value = false
|
||||
emit('refresh')
|
||||
} catch (err: any) {
|
||||
useMessage().error(err.msg || '教室安排失败')
|
||||
} catch (_err) {
|
||||
// 错误由 request 拦截器统一提示
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
@@ -45,40 +45,6 @@
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</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-form>
|
||||
<template #footer>
|
||||
@@ -95,8 +61,6 @@ import { ref, reactive, nextTick, onMounted } from 'vue'
|
||||
import { useMessage } from '/@/hooks/message'
|
||||
import { initObj } from '/@/api/stuwork/classtheme'
|
||||
import { queryAllSchoolYear } from '/@/api/basic/basicyear'
|
||||
import { getClassListByRole } from '/@/api/basic/basicclass'
|
||||
import { getDeptList } from '/@/api/basic/basicclass'
|
||||
import { getDicts } from '/@/api/admin/dict'
|
||||
|
||||
const emit = defineEmits(['refresh'])
|
||||
@@ -107,15 +71,11 @@ const visible = ref(false)
|
||||
const loading = ref(false)
|
||||
const schoolYearList = ref<any[]>([])
|
||||
const schoolTermList = ref<any[]>([])
|
||||
const deptList = ref<any[]>([])
|
||||
const classList = ref<any[]>([])
|
||||
|
||||
// 提交表单数据
|
||||
const form = reactive({
|
||||
schoolYear: '',
|
||||
schoolTerm: '',
|
||||
deptCode: '',
|
||||
classNo: ''
|
||||
schoolTerm: ''
|
||||
})
|
||||
|
||||
// 定义校验规则
|
||||
@@ -125,12 +85,6 @@ const dataRules = {
|
||||
],
|
||||
schoolTerm: [
|
||||
{ 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()
|
||||
form.schoolYear = ''
|
||||
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(() => {
|
||||
getSchoolYearList()
|
||||
getSchoolTermDict()
|
||||
getDeptListData()
|
||||
getClassListData()
|
||||
})
|
||||
|
||||
// 暴露方法
|
||||
|
||||
@@ -89,9 +89,11 @@
|
||||
style="width: 100%">
|
||||
<el-option
|
||||
v-for="item in bedNoList"
|
||||
:key="item"
|
||||
:label="item"
|
||||
:value="item">
|
||||
:key="item.bedNo"
|
||||
:value="item.bedNo">
|
||||
<span :class="{ 'bed-option-occupied': item.haveStudent }">
|
||||
{{ item.bedNo }}{{ item.haveStudent ? ' (有人)' : '' }}
|
||||
</span>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
@@ -138,7 +140,8 @@ const loading = ref(false)
|
||||
const classList = ref<any[]>([])
|
||||
const studentList = ref<any[]>([])
|
||||
const roomList = ref<any[]>([])
|
||||
const bedNoList = ref<string[]>([])
|
||||
// 床位列表:支持 haveStudent 标记(true=有人,false=无人)
|
||||
const bedNoList = ref<Array<{ bedNo: string; haveStudent: boolean }>>([])
|
||||
|
||||
// 提交表单数据
|
||||
const form = reactive({
|
||||
@@ -206,7 +209,7 @@ const handleStudentChange = (stuNo: string) => {
|
||||
}
|
||||
}
|
||||
|
||||
// 房间号变化时获取床位号列表
|
||||
// 房间号变化时获取床位号列表(支持 haveStudent:true=有人,false=无人)
|
||||
const handleRoomChange = async (roomNo: string) => {
|
||||
if (!roomNo) {
|
||||
bedNoList.value = []
|
||||
@@ -214,20 +217,25 @@ const handleRoomChange = async (roomNo: string) => {
|
||||
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 {
|
||||
const res = await fearchRoomStuNum(roomNo)
|
||||
if (res.data) {
|
||||
// 根据返回的数据结构处理床位号列表
|
||||
// 假设返回的是数字数组或对象数组
|
||||
if (Array.isArray(res.data)) {
|
||||
bedNoList.value = res.data.map((item: any) => {
|
||||
if (typeof item === 'number' || typeof item === 'string') {
|
||||
return String(item)
|
||||
}
|
||||
return String(item.bedNo || item.value || item)
|
||||
})
|
||||
bedNoList.value = res.data.map(toBedItem).filter((b) => b.bedNo)
|
||||
} 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 {
|
||||
bedNoList.value = []
|
||||
}
|
||||
@@ -321,3 +329,8 @@ defineExpose({
|
||||
})
|
||||
</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>
|
||||
<span style="margin-left: 4px">{{ col.label }}</span>
|
||||
</template>
|
||||
<!-- 床位号列特殊模板 -->
|
||||
<!-- 床位号列:haveStudent 为 true 时变色标记有人 -->
|
||||
<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 }}
|
||||
</el-tag>
|
||||
<span v-else>-</span>
|
||||
@@ -274,6 +278,12 @@
|
||||
|
||||
<!-- 转宿弹窗 -->
|
||||
<TransferDialog ref="transferDialogRef" @refresh="getDataList" />
|
||||
|
||||
<!-- 宿舍互换弹窗 -->
|
||||
<SwapDialog ref="swapDialogRef" @refresh="getDataList" />
|
||||
|
||||
<!-- 打印宿舍卡弹窗 -->
|
||||
<PrintCardDialog ref="printCardDialogRef" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -281,7 +291,7 @@
|
||||
import { reactive, ref, onMounted, computed, nextTick } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
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 { getBuildingList } from "/@/api/stuwork/dormbuilding";
|
||||
import { fetchDormRoomTreeList } from "/@/api/stuwork/dormroom";
|
||||
@@ -289,6 +299,8 @@ import { useMessage, useMessageBox } from "/@/hooks/message";
|
||||
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
||||
import FormDialog from './form.vue';
|
||||
import TransferDialog from './transfer.vue';
|
||||
import SwapDialog from './swap.vue';
|
||||
import PrintCardDialog from './printCard.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 { useTableColumnControl } from '/@/hooks/tableColumn'
|
||||
@@ -301,6 +313,8 @@ const route = useRoute()
|
||||
const formDialogRef = ref()
|
||||
const columnControlRef = ref<any>()
|
||||
const transferDialogRef = ref()
|
||||
const swapDialogRef = ref()
|
||||
const printCardDialogRef = ref()
|
||||
const searchFormRef = ref()
|
||||
const showSearch = ref(true)
|
||||
const deptList = ref<any[]>([])
|
||||
@@ -425,29 +439,91 @@ const handleDormDataTypeChange = (dormdataType: string) => {
|
||||
getDormRoomTreeListData(dormdataType)
|
||||
}
|
||||
|
||||
// 打印宿舍卡
|
||||
// 打印宿舍卡(按房间号查询后打印)
|
||||
const handlePrintCard = () => {
|
||||
useMessage().warning('功能开发中')
|
||||
printCardDialogRef.value?.openDialog()
|
||||
}
|
||||
|
||||
// 宿舍互换
|
||||
// 宿舍互换(两名学生互换宿舍)
|
||||
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)
|
||||
}
|
||||
|
||||
// 导出
|
||||
const handleExport = () => {
|
||||
useMessage().warning('功能开发中')
|
||||
// 导出:空 n 人宿舍导出(按当前筛选条件传参)
|
||||
const handleExport = async () => {
|
||||
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 || '导出失败')
|
||||
}
|
||||
}
|
||||
|
||||
// 名单导出
|
||||
const handleExportList = () => {
|
||||
useMessage().warning('功能开发中')
|
||||
// 名单导出:与导出共用空 n 人宿舍导出接口,文件名区分
|
||||
const handleExportList = async () => {
|
||||
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) => {
|
||||
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%">
|
||||
<el-option
|
||||
v-for="item in bedNoList"
|
||||
:key="item"
|
||||
:label="item"
|
||||
:value="item">
|
||||
:key="item.bedNo"
|
||||
:value="item.bedNo">
|
||||
<span :class="{ 'bed-option-occupied': item.haveStudent }">
|
||||
{{ item.bedNo }}{{ item.haveStudent ? ' (有人)' : '' }}
|
||||
</span>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
@@ -95,7 +97,8 @@ const dataFormRef = ref()
|
||||
const visible = ref(false)
|
||||
const loading = ref(false)
|
||||
const roomList = ref<any[]>([])
|
||||
const bedNoList = ref<string[]>([])
|
||||
// 床位列表:支持 haveStudent 标记(true=有人,false=无人)
|
||||
const bedNoList = ref<Array<{ bedNo: string; haveStudent: boolean }>>([])
|
||||
|
||||
// 提交表单数据
|
||||
const form = reactive({
|
||||
@@ -119,7 +122,7 @@ const dataRules = {
|
||||
]
|
||||
}
|
||||
|
||||
// 房间号变化时获取床位号列表
|
||||
// 房间号变化时获取床位号列表(支持 haveStudent:true=有人,false=无人)
|
||||
const handleRoomChange = async (roomNo: string) => {
|
||||
if (!roomNo) {
|
||||
bedNoList.value = []
|
||||
@@ -127,18 +130,25 @@ const handleRoomChange = async (roomNo: string) => {
|
||||
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 {
|
||||
const res = await fearchRoomStuNum(roomNo)
|
||||
if (res.data) {
|
||||
if (Array.isArray(res.data)) {
|
||||
bedNoList.value = res.data.map((item: any) => {
|
||||
if (typeof item === 'number' || typeof item === 'string') {
|
||||
return String(item)
|
||||
}
|
||||
return String(item.bedNo || item.value || item)
|
||||
})
|
||||
bedNoList.value = res.data.map(toBedItem).filter((b) => b.bedNo)
|
||||
} 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 {
|
||||
bedNoList.value = []
|
||||
}
|
||||
@@ -157,7 +167,7 @@ const openDialog = async (row: any) => {
|
||||
visible.value = true
|
||||
|
||||
// 重置表单数据
|
||||
nextTick(() => {
|
||||
await nextTick()
|
||||
dataFormRef.value?.resetFields()
|
||||
form.id = row.id || ''
|
||||
form.roomNo = row.roomNo || ''
|
||||
@@ -166,11 +176,11 @@ const openDialog = async (row: any) => {
|
||||
form.isLeader = row.isLeader || '0'
|
||||
bedNoList.value = []
|
||||
|
||||
// 如果有房间号,获取床位号列表
|
||||
// 如果有房间号,先拉取床位列表再回填床位号(避免 handleRoomChange 清空 bedNo)
|
||||
if (form.roomNo) {
|
||||
handleRoomChange(form.roomNo)
|
||||
await handleRoomChange(form.roomNo)
|
||||
form.bedNo = row.bedNo || ''
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 提交表单
|
||||
@@ -224,3 +234,8 @@ defineExpose({
|
||||
})
|
||||
</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-col>
|
||||
<el-col :span="24" class="mb20">
|
||||
<el-form-item label="奖项名称" prop="ruleName">
|
||||
<el-input
|
||||
v-model="form.ruleName"
|
||||
placeholder="请输入奖项名称"
|
||||
<el-form-item label="奖项名称" prop="ruleId">
|
||||
<el-select
|
||||
v-model="form.ruleId"
|
||||
placeholder="请选择奖项名称"
|
||||
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-col>
|
||||
<el-col :span="24" class="mb20">
|
||||
@@ -67,6 +75,7 @@ import { ref, reactive, nextTick, onMounted } from 'vue'
|
||||
import { useMessage } from '/@/hooks/message'
|
||||
import { addObj, editObj, getDetail } from '/@/api/stuwork/rewardclass'
|
||||
import { getClassListByRole } from '/@/api/basic/basicclass'
|
||||
import { getList as getRewardRuleList } from '/@/api/stuwork/rewardrule'
|
||||
|
||||
const emit = defineEmits(['refresh'])
|
||||
|
||||
@@ -76,6 +85,8 @@ const visible = ref(false)
|
||||
const loading = ref(false)
|
||||
const operType = ref('add')
|
||||
const classList = ref<any[]>([])
|
||||
/** 奖项列表(来自接口 /stuwork/rewardrule/list,用于班级的奖项类型可传 ruleType 筛选) */
|
||||
const rewardRuleList = ref<any[]>([])
|
||||
|
||||
// 提交表单数据
|
||||
const form = reactive({
|
||||
@@ -91,8 +102,8 @@ const dataRules = {
|
||||
classCode: [
|
||||
{ required: true, message: '请选择班号', trigger: 'change' }
|
||||
],
|
||||
ruleName: [
|
||||
{ required: true, message: '请输入奖项名称', trigger: 'blur' }
|
||||
ruleId: [
|
||||
{ required: true, message: '请选择奖项名称', trigger: 'change' }
|
||||
]
|
||||
}
|
||||
|
||||
@@ -117,6 +128,8 @@ const openDialog = async (type: string = 'add', row?: any) => {
|
||||
form.ruleId = row.ruleId || ''
|
||||
form.ruleName = row.ruleName || ''
|
||||
form.remarks = row.remarks || ''
|
||||
// 选择奖项后同步 ruleName
|
||||
handleRewardRuleChange(form.ruleId)
|
||||
|
||||
// 如果需要获取详情
|
||||
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(() => {
|
||||
getClassListData()
|
||||
getRewardRuleData()
|
||||
})
|
||||
|
||||
// 暴露方法
|
||||
|
||||
@@ -12,6 +12,39 @@
|
||||
label-width="100px"
|
||||
v-loading="loading">
|
||||
<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-form-item label="宿舍号" prop="roomNo">
|
||||
<el-select
|
||||
@@ -30,12 +63,20 @@
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24" class="mb20">
|
||||
<el-form-item label="奖项名称" prop="ruleName">
|
||||
<el-input
|
||||
v-model="form.ruleName"
|
||||
placeholder="请输入奖项名称"
|
||||
<el-form-item label="奖项名称" prop="ruleId">
|
||||
<el-select
|
||||
v-model="form.ruleId"
|
||||
placeholder="请选择奖项名称"
|
||||
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-col>
|
||||
<el-col :span="24" class="mb20">
|
||||
@@ -66,6 +107,9 @@ import { ref, reactive, nextTick, onMounted } from 'vue'
|
||||
import { useMessage } from '/@/hooks/message'
|
||||
import { addObj, editObj, getDetail } from '/@/api/stuwork/rewarddorm'
|
||||
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'])
|
||||
|
||||
@@ -75,10 +119,16 @@ const visible = ref(false)
|
||||
const loading = ref(false)
|
||||
const operType = ref('add')
|
||||
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({
|
||||
id: '',
|
||||
schoolYear: '',
|
||||
schoolTerm: '',
|
||||
roomNo: '',
|
||||
ruleId: '',
|
||||
ruleName: '',
|
||||
@@ -87,11 +137,17 @@ const form = reactive({
|
||||
|
||||
// 定义校验规则
|
||||
const dataRules = {
|
||||
schoolYear: [
|
||||
{ required: true, message: '请选择学年', trigger: 'change' }
|
||||
],
|
||||
schoolTerm: [
|
||||
{ required: true, message: '请选择学期', trigger: 'change' }
|
||||
],
|
||||
roomNo: [
|
||||
{ required: true, message: '请选择宿舍号', trigger: 'change' }
|
||||
],
|
||||
ruleName: [
|
||||
{ required: true, message: '请输入奖项名称', trigger: 'blur' }
|
||||
ruleId: [
|
||||
{ required: true, message: '请选择奖项名称', trigger: 'change' }
|
||||
]
|
||||
}
|
||||
|
||||
@@ -104,6 +160,8 @@ const openDialog = async (type: string = 'add', row?: any) => {
|
||||
nextTick(() => {
|
||||
dataFormRef.value?.resetFields()
|
||||
form.id = ''
|
||||
form.schoolYear = ''
|
||||
form.schoolTerm = ''
|
||||
form.roomNo = ''
|
||||
form.ruleId = ''
|
||||
form.ruleName = ''
|
||||
@@ -112,23 +170,27 @@ const openDialog = async (type: string = 'add', row?: any) => {
|
||||
// 编辑时填充数据
|
||||
if (type === 'edit' && row) {
|
||||
form.id = row.id
|
||||
form.schoolYear = row.schoolYear || ''
|
||||
form.schoolTerm = row.schoolTerm || ''
|
||||
form.roomNo = row.roomNo || ''
|
||||
form.ruleId = row.ruleId || ''
|
||||
form.ruleName = row.ruleName || ''
|
||||
form.remarks = row.remarks || ''
|
||||
handleRewardRuleChange(form.ruleId)
|
||||
|
||||
// 如果需要获取详情
|
||||
if (row.id && (!row.ruleName || !row.remarks)) {
|
||||
if (row.id && (!row.ruleName || !row.schoolYear)) {
|
||||
loading.value = true
|
||||
getDetail(row.id).then((res: any) => {
|
||||
if (res.data) {
|
||||
form.schoolYear = res.data.schoolYear || ''
|
||||
form.schoolTerm = res.data.schoolTerm || ''
|
||||
form.roomNo = res.data.roomNo || ''
|
||||
form.ruleId = res.data.ruleId || ''
|
||||
form.ruleName = res.data.ruleName || ''
|
||||
form.remarks = res.data.remarks || ''
|
||||
}
|
||||
}).catch((err) => {
|
||||
}).finally(() => {
|
||||
}).catch(() => {}).finally(() => {
|
||||
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 () => {
|
||||
if (!dataFormRef.value) return
|
||||
@@ -145,10 +213,13 @@ const onSubmit = async () => {
|
||||
|
||||
loading.value = true
|
||||
try {
|
||||
// 与接口文档一致:roomNo, ruleId, ruleName, schoolYear, schoolTerm, remarks
|
||||
const submitData = {
|
||||
roomNo: form.roomNo,
|
||||
ruleId: form.ruleId || '', // 如果没有ruleId,传空字符串
|
||||
ruleId: form.ruleId != null && form.ruleId !== '' ? String(form.ruleId) : '',
|
||||
ruleName: form.ruleName,
|
||||
schoolYear: form.schoolYear,
|
||||
schoolTerm: form.schoolTerm,
|
||||
remarks: form.remarks || ''
|
||||
}
|
||||
|
||||
@@ -164,8 +235,8 @@ const onSubmit = async () => {
|
||||
}
|
||||
visible.value = false
|
||||
emit('refresh')
|
||||
} catch (err: any) {
|
||||
useMessage().error(err.msg || (operType.value === 'add' ? '新增失败' : '编辑失败'))
|
||||
} catch (_err) {
|
||||
// 不再在此处弹 error:request 拦截器已统一弹过,避免重复两个 error 弹窗
|
||||
} finally {
|
||||
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(() => {
|
||||
getRoomListData()
|
||||
getRewardRuleData()
|
||||
getSchoolYearData()
|
||||
getSchoolTermData()
|
||||
})
|
||||
|
||||
// 暴露方法
|
||||
|
||||
@@ -104,12 +104,62 @@
|
||||
</el-table>
|
||||
</el-row>
|
||||
</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>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="StuConductTerm">
|
||||
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 { queryAllSchoolYear } from "/@/api/basic/basicyear";
|
||||
import { getDicts } from "/@/api/admin/dict";
|
||||
@@ -129,6 +179,10 @@ const schoolYearList = ref<any[]>([])
|
||||
const schoolTermList = ref<any[]>([])
|
||||
const classList = 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({
|
||||
@@ -263,14 +317,20 @@ const getDataList = async () => {
|
||||
} else {
|
||||
studentList.value = []
|
||||
}
|
||||
} catch (err: any) {
|
||||
useMessage().error(err.msg || '获取数据列表失败')
|
||||
} catch (_err) {
|
||||
studentList.value = []
|
||||
} finally {
|
||||
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 = () => {
|
||||
searchFormRef.value?.resetFields()
|
||||
@@ -280,10 +340,29 @@ const handleReset = () => {
|
||||
studentList.value = []
|
||||
}
|
||||
|
||||
// 查看详情
|
||||
const handleView = (row: any) => {
|
||||
// 可以跳转到详情页面或打开详情弹窗
|
||||
useMessage().info('查看详情功能待实现')
|
||||
// 查看详情(接口:GET /stuwork/stuconduct/queryDataByStuNo,按当前学年+学号拉取后筛本学期记录)
|
||||
const handleView = async (row: any) => {
|
||||
if (!queryForm.schoolYear || !row.stuNo) {
|
||||
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 {
|
||||
height: 100%;
|
||||
|
||||
@@ -90,12 +90,61 @@
|
||||
</el-table>
|
||||
</el-row>
|
||||
</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>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="StuConductYear">
|
||||
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 { queryAllSchoolYear } from "/@/api/basic/basicyear";
|
||||
import { useMessage } from "/@/hooks/message";
|
||||
@@ -113,6 +162,10 @@ const loading = ref(false)
|
||||
const schoolYearList = ref<any[]>([])
|
||||
const classList = 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({
|
||||
@@ -245,8 +298,7 @@ const getDataList = async () => {
|
||||
} else {
|
||||
studentList.value = []
|
||||
}
|
||||
} catch (err: any) {
|
||||
useMessage().error(err.msg || '获取数据列表失败')
|
||||
} catch (_err) {
|
||||
studentList.value = []
|
||||
} finally {
|
||||
loading.value = false
|
||||
@@ -261,10 +313,27 @@ const handleReset = () => {
|
||||
studentList.value = []
|
||||
}
|
||||
|
||||
// 查看详情
|
||||
const handleView = (row: any) => {
|
||||
// 可以跳转到详情页面或打开详情弹窗
|
||||
useMessage().info('查看详情功能待实现')
|
||||
// 查看详情(接口:GET /stuwork/stuconduct/queryDataByStuNo,通过学年学号查看详情)
|
||||
const handleView = async (row: any) => {
|
||||
if (!queryForm.schoolYear || !row.stuNo) {
|
||||
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 {
|
||||
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 {
|
||||
const res = await queryStudentListByClass({ classCode, current: 1, size: 10000 })
|
||||
if (res.data && Array.isArray(res.data.records)) {
|
||||
studentList.value = res.data.records
|
||||
// 接口文档:GET 仅 classCode,返回 data 为数组
|
||||
const res = await queryStudentListByClass({ classCode })
|
||||
const 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 = []
|
||||
}
|
||||
|
||||
@@ -181,9 +181,13 @@ const handleClassChange = async (classCode: string) => {
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await queryStudentListByClass({ classCode, current: 1, size: 10000 })
|
||||
if (res.data && Array.isArray(res.data.records)) {
|
||||
studentList.value = res.data.records
|
||||
// 接口文档:GET,仅 classCode;返回 data 为数组,非 data.records
|
||||
const res = await queryStudentListByClass({ classCode })
|
||||
const 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 = []
|
||||
}
|
||||
|
||||
@@ -305,13 +305,18 @@ const getClassListData = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
// 获取学生列表
|
||||
// 获取学生列表(接口文档:GET 仅 classCode,返回 data 为数组)
|
||||
const getStudentListData = async () => {
|
||||
if (!form.classCode) return
|
||||
try {
|
||||
const res = await queryStudentListByClass(form.classCode)
|
||||
if (res.data) {
|
||||
studentList.value = Array.isArray(res.data) ? res.data : []
|
||||
const res = await queryStudentListByClass({ classCode: form.classCode })
|
||||
const 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) {
|
||||
studentList.value = []
|
||||
|
||||
@@ -207,7 +207,7 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
</template>
|
||||
<el-table-column label="操作" width="150" align="center" fixed="right">
|
||||
<el-table-column label="操作" width="220" align="center" fixed="right">
|
||||
<template #header>
|
||||
<el-icon><Setting /></el-icon>
|
||||
<span style="margin-left: 4px">操作</span>
|
||||
@@ -220,6 +220,13 @@
|
||||
@click="handleEdit(scope.row)">
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button
|
||||
icon="RefreshLeft"
|
||||
link
|
||||
type="warning"
|
||||
@click="handleCancel(scope.row)">
|
||||
异动撤销
|
||||
</el-button>
|
||||
<el-button
|
||||
icon="Delete"
|
||||
link
|
||||
@@ -250,7 +257,7 @@
|
||||
import { reactive, ref, onMounted, computed, nextTick } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
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 { getDicts } from "/@/api/admin/dict";
|
||||
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 TableColumnControl from '/@/components/TableColumnControl/index.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'
|
||||
|
||||
|
||||
@@ -400,6 +407,20 @@ const handleEdit = (row: any) => {
|
||||
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) => {
|
||||
try {
|
||||
|
||||
@@ -58,6 +58,7 @@ import { ref, reactive, nextTick, onMounted } from 'vue'
|
||||
import { useMessage } from '/@/hooks/message'
|
||||
import { addObj } from '/@/api/stuwork/stuworkstudyalternate'
|
||||
import { getClassListByRole } from '/@/api/basic/basicclass'
|
||||
import { queryAllStudentByClassCode } from '/@/api/basic/basicstudent'
|
||||
|
||||
const emit = defineEmits(['refresh'])
|
||||
|
||||
@@ -94,7 +95,7 @@ const openDialog = async () => {
|
||||
})
|
||||
}
|
||||
|
||||
// 提交表单
|
||||
// 提交表单(接口文档要求 classCode、dateRange、stuNoList)
|
||||
const onSubmit = async () => {
|
||||
if (!dataFormRef.value) return
|
||||
|
||||
@@ -108,15 +109,27 @@ const onSubmit = async () => {
|
||||
|
||||
loading.value = true
|
||||
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({
|
||||
classCode: form.classCode,
|
||||
dateRange: form.dateRange
|
||||
dateRange: form.dateRange,
|
||||
stuNoList
|
||||
})
|
||||
useMessage().success('新增成功')
|
||||
visible.value = false
|
||||
emit('refresh')
|
||||
} catch (err: any) {
|
||||
useMessage().error(err.msg || '新增失败')
|
||||
if (!err?._messageShown) {
|
||||
useMessage().error(err?.msg || '新增失败')
|
||||
}
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
@@ -170,7 +170,7 @@
|
||||
:min-width="col.minWidth"
|
||||
:width="col.width">
|
||||
<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>
|
||||
</template>
|
||||
<!-- 学期列特殊模板 -->
|
||||
@@ -290,7 +290,7 @@ const {
|
||||
checkColumnVisible,
|
||||
handleColumnChange,
|
||||
handleColumnOrderChange
|
||||
} = useTableColumnControl(tableColumns, route.path)
|
||||
} = useTableColumnControl(tableColumns, { storageKey: route.path })
|
||||
|
||||
// 搜索表单
|
||||
const searchForm = reactive({
|
||||
@@ -303,10 +303,20 @@ const searchForm = reactive({
|
||||
dateRange: null as [string, string] | null
|
||||
})
|
||||
|
||||
// 配置 useTable
|
||||
// 配置 useTable(接口返回 data.tableData.records / data.tableData.total,需包装以适配 hook 的 res.data[props.item] 取数)
|
||||
const state: BasicTableProps = reactive<BasicTableProps>({
|
||||
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: {
|
||||
item: 'tableData.records',
|
||||
totalCount: 'tableData.total'
|
||||
@@ -397,13 +407,25 @@ const getSchoolTermList = async () => {
|
||||
try {
|
||||
const res = await getDicts('school_term')
|
||||
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) {
|
||||
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(() => {
|
||||
getDeptListData()
|
||||
|
||||
Reference in New Issue
Block a user