Merge branch 'master' of http://47.99.164.66:3000/hzdyjs-xg/all-project
This commit is contained in:
commit
eeb79de050
29 files changed
+214
-13980
No files matched your search
@@ -1,396 +0,0 @@
|
||||
import { BaseEntity, PageQuery } from './types';
|
||||
|
||||
/**
|
||||
* 学年管理 VO
|
||||
*/
|
||||
export interface AcademicYearVO {
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
id: string | number;
|
||||
|
||||
/**
|
||||
* 租户编号
|
||||
*/
|
||||
tenantId: string;
|
||||
|
||||
/**
|
||||
* 学年编码
|
||||
*/
|
||||
yearCode: string;
|
||||
|
||||
/**
|
||||
* 学年名称
|
||||
*/
|
||||
yearName: string;
|
||||
|
||||
/**
|
||||
* 开始日期
|
||||
*/
|
||||
startDate: string;
|
||||
|
||||
/**
|
||||
* 结束日期
|
||||
*/
|
||||
endDate: string;
|
||||
|
||||
/**
|
||||
* 是否当前学年:0-否,1-是
|
||||
*/
|
||||
isCurrent: boolean;
|
||||
|
||||
/**
|
||||
* 学年状态:1-启用,0-停用
|
||||
*/
|
||||
yearStatus: string;
|
||||
|
||||
/**
|
||||
* 学年描述
|
||||
*/
|
||||
description?: string;
|
||||
|
||||
/**
|
||||
* 排序序号
|
||||
*/
|
||||
sortOrder: number;
|
||||
|
||||
/**
|
||||
* 备注信息
|
||||
*/
|
||||
remark?: string;
|
||||
|
||||
/**
|
||||
* 创建者
|
||||
*/
|
||||
createBy: string | number;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
createTime: string;
|
||||
|
||||
/**
|
||||
* 更新者
|
||||
*/
|
||||
updateBy?: string | number;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
updateTime?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 学年管理 Form
|
||||
*/
|
||||
export interface AcademicYearForm extends BaseEntity {
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
id?: string | number;
|
||||
|
||||
/**
|
||||
* 学年编码
|
||||
*/
|
||||
yearCode?: string;
|
||||
|
||||
/**
|
||||
* 学年名称
|
||||
*/
|
||||
yearName?: string;
|
||||
|
||||
/**
|
||||
* 开始日期
|
||||
*/
|
||||
startDate?: string;
|
||||
|
||||
/**
|
||||
* 结束日期
|
||||
*/
|
||||
endDate?: string;
|
||||
|
||||
/**
|
||||
* 是否当前学年
|
||||
*/
|
||||
isCurrent?: boolean;
|
||||
|
||||
/**
|
||||
* 学年状态
|
||||
*/
|
||||
yearStatus?: string;
|
||||
|
||||
/**
|
||||
* 学年描述
|
||||
*/
|
||||
description?: string;
|
||||
|
||||
/**
|
||||
* 排序序号
|
||||
*/
|
||||
sortOrder?: number;
|
||||
|
||||
/**
|
||||
* 备注信息
|
||||
*/
|
||||
remark?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 学年管理 Query
|
||||
*/
|
||||
export interface AcademicYearQuery extends PageQuery {
|
||||
/**
|
||||
* 学年编码
|
||||
*/
|
||||
yearCode?: string;
|
||||
|
||||
/**
|
||||
* 学年名称
|
||||
*/
|
||||
yearName?: string;
|
||||
|
||||
/**
|
||||
* 是否当前学年
|
||||
*/
|
||||
isCurrent?: boolean;
|
||||
|
||||
/**
|
||||
* 学年状态
|
||||
*/
|
||||
yearStatus?: string;
|
||||
|
||||
/**
|
||||
* 开始日期范围
|
||||
*/
|
||||
startDateRange?: string[];
|
||||
|
||||
/**
|
||||
* 结束日期范围
|
||||
*/
|
||||
endDateRange?: string[];
|
||||
|
||||
/**
|
||||
* 创建时间范围
|
||||
*/
|
||||
createTimeRange?: string[];
|
||||
|
||||
/**
|
||||
* 日期范围参数
|
||||
*/
|
||||
params?: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* 学期管理 VO
|
||||
*/
|
||||
export interface SemesterVO {
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
id: string | number;
|
||||
|
||||
/**
|
||||
* 租户编号
|
||||
*/
|
||||
tenantId: string;
|
||||
|
||||
/**
|
||||
* 学年ID
|
||||
*/
|
||||
academicYearId: string | number;
|
||||
|
||||
/**
|
||||
* 学年名称
|
||||
*/
|
||||
academicYearName?: string;
|
||||
|
||||
/**
|
||||
* 学期编码
|
||||
*/
|
||||
semesterCode: string;
|
||||
|
||||
/**
|
||||
* 学期名称
|
||||
*/
|
||||
semesterName: string;
|
||||
|
||||
/**
|
||||
* 学期类型:1-春季学期,2-秋季学期,3-夏季学期,4-短学期
|
||||
*/
|
||||
semesterType: string;
|
||||
|
||||
/**
|
||||
* 开始日期
|
||||
*/
|
||||
startDate: string;
|
||||
|
||||
/**
|
||||
* 结束日期
|
||||
*/
|
||||
endDate: string;
|
||||
|
||||
/**
|
||||
* 是否当前学期:0-否,1-是
|
||||
*/
|
||||
isCurrent: boolean;
|
||||
|
||||
/**
|
||||
* 学期状态:1-启用,0-停用
|
||||
*/
|
||||
semesterStatus: string;
|
||||
|
||||
/**
|
||||
* 学期描述
|
||||
*/
|
||||
description?: string;
|
||||
|
||||
/**
|
||||
* 排序序号
|
||||
*/
|
||||
sortOrder: number;
|
||||
|
||||
/**
|
||||
* 备注信息
|
||||
*/
|
||||
remark?: string;
|
||||
|
||||
/**
|
||||
* 创建者
|
||||
*/
|
||||
createBy: string | number;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
createTime: string;
|
||||
|
||||
/**
|
||||
* 更新者
|
||||
*/
|
||||
updateBy?: string | number;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
updateTime?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 学期管理 Form
|
||||
*/
|
||||
export interface SemesterForm extends BaseEntity {
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
id?: string | number;
|
||||
|
||||
/**
|
||||
* 学年ID
|
||||
*/
|
||||
academicYearId?: string | number;
|
||||
|
||||
/**
|
||||
* 学期编码
|
||||
*/
|
||||
semesterCode?: string;
|
||||
|
||||
/**
|
||||
* 学期名称
|
||||
*/
|
||||
semesterName?: string;
|
||||
|
||||
/**
|
||||
* 学期类型
|
||||
*/
|
||||
semesterType?: string;
|
||||
|
||||
/**
|
||||
* 开始日期
|
||||
*/
|
||||
startDate?: string;
|
||||
|
||||
/**
|
||||
* 结束日期
|
||||
*/
|
||||
endDate?: string;
|
||||
|
||||
/**
|
||||
* 是否当前学期
|
||||
*/
|
||||
isCurrent?: boolean;
|
||||
|
||||
/**
|
||||
* 学期状态
|
||||
*/
|
||||
semesterStatus?: string;
|
||||
|
||||
/**
|
||||
* 学期描述
|
||||
*/
|
||||
description?: string;
|
||||
|
||||
/**
|
||||
* 排序序号
|
||||
*/
|
||||
sortOrder?: number;
|
||||
|
||||
/**
|
||||
* 备注信息
|
||||
*/
|
||||
remark?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 学期管理 Query
|
||||
*/
|
||||
export interface SemesterQuery extends PageQuery {
|
||||
/**
|
||||
* 学年ID
|
||||
*/
|
||||
academicYearId?: string | number;
|
||||
|
||||
/**
|
||||
* 学期编码
|
||||
*/
|
||||
semesterCode?: string;
|
||||
|
||||
/**
|
||||
* 学期名称
|
||||
*/
|
||||
semesterName?: string;
|
||||
|
||||
/**
|
||||
* 学期类型
|
||||
*/
|
||||
semesterType?: string;
|
||||
|
||||
/**
|
||||
* 是否当前学期
|
||||
*/
|
||||
isCurrent?: boolean;
|
||||
|
||||
/**
|
||||
* 学期状态
|
||||
*/
|
||||
semesterStatus?: string;
|
||||
|
||||
/**
|
||||
* 开始日期范围
|
||||
*/
|
||||
startDateRange?: string[];
|
||||
|
||||
/**
|
||||
* 结束日期范围
|
||||
*/
|
||||
endDateRange?: string[];
|
||||
|
||||
/**
|
||||
* 创建时间范围
|
||||
*/
|
||||
createTimeRange?: string[];
|
||||
|
||||
/**
|
||||
* 日期范围参数
|
||||
*/
|
||||
params?: any;
|
||||
}
|
||||
@@ -1,152 +0,0 @@
|
||||
import request from '@/utils/request';
|
||||
import { AxiosPromise } from 'axios';
|
||||
import { AcademicYearVO, AcademicYearForm, AcademicYearQuery } from './academicTypes';
|
||||
|
||||
/**
|
||||
* 查询学年管理列表
|
||||
* @param query 查询参数
|
||||
*/
|
||||
export const listAcademicYear = (query?: AcademicYearQuery): AxiosPromise<AcademicYearVO[]> => {
|
||||
return request({
|
||||
url: '/xg-xx/academicYear/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 查询学年管理详细信息
|
||||
* @param id 学年ID
|
||||
*/
|
||||
export const getAcademicYear = (id: string | number): AxiosPromise<AcademicYearVO> => {
|
||||
return request({
|
||||
url: `/xg-xx/academicYear/${id}`,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 新增学年管理
|
||||
* @param data 学年管理数据
|
||||
*/
|
||||
export const addAcademicYear = (data: AcademicYearForm) => {
|
||||
return request({
|
||||
url: '/xg-xx/academicYear',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 修改学年管理
|
||||
* @param data 学年管理数据
|
||||
*/
|
||||
export const updateAcademicYear = (data: AcademicYearForm) => {
|
||||
return request({
|
||||
url: '/xg-xx/academicYear',
|
||||
method: 'put',
|
||||
data: data
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 删除学年管理
|
||||
* @param id 学年ID
|
||||
*/
|
||||
export const delAcademicYear = (id: string | number | Array<string | number>) => {
|
||||
return request({
|
||||
url: `/xg-xx/academicYear/${id}`,
|
||||
method: 'delete'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 导出学年管理列表
|
||||
* @param query 查询参数
|
||||
*/
|
||||
export const exportAcademicYear = (query?: AcademicYearQuery) => {
|
||||
return request({
|
||||
url: '/xg-xx/academicYear/export',
|
||||
method: 'post',
|
||||
data: query
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 查询当前学年
|
||||
*/
|
||||
export const getCurrentAcademicYear = (): AxiosPromise<AcademicYearVO> => {
|
||||
return request({
|
||||
url: '/xg-xx/academicYear/current',
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 设置当前学年
|
||||
* @param id 学年ID
|
||||
*/
|
||||
export const setCurrentAcademicYear = (id: string | number) => {
|
||||
return request({
|
||||
url: `/xg-xx/academicYear/setCurrent/${id}`,
|
||||
method: 'put'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 查询可用的学年选项
|
||||
*/
|
||||
export const getAvailableAcademicYearOptions = (): AxiosPromise<AcademicYearVO[]> => {
|
||||
return request({
|
||||
url: '/xg-xx/academicYear/availableOptions',
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 校验学年名称是否唯一
|
||||
* @param yearName 学年名称
|
||||
* @param id 学年ID
|
||||
*/
|
||||
export const checkAcademicYearName = (yearName: string, id?: string | number): AxiosPromise<boolean> => {
|
||||
return request({
|
||||
url: '/xg-xx/academicYear/checkYearName',
|
||||
method: 'get',
|
||||
params: { yearName, id }
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 校验学年编码是否唯一
|
||||
* @param yearCode 学年编码
|
||||
* @param id 学年ID
|
||||
*/
|
||||
export const checkAcademicYearCode = (yearCode: string, id?: string | number): AxiosPromise<boolean> => {
|
||||
return request({
|
||||
url: '/xg-xx/academicYear/checkYearCode',
|
||||
method: 'get',
|
||||
params: { yearCode, id }
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 激活学年
|
||||
* @param id 学年ID
|
||||
*/
|
||||
export const activateAcademicYear = (id: string | number) => {
|
||||
return request({
|
||||
url: `/xg-xx/academicYear/activate/${id}`,
|
||||
method: 'put'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 停用学年
|
||||
* @param id 学年ID
|
||||
*/
|
||||
export const deactivateAcademicYear = (id: string | number) => {
|
||||
return request({
|
||||
url: `/xg-xx/academicYear/deactivate/${id}`,
|
||||
method: 'put'
|
||||
});
|
||||
};
|
||||
@@ -1,194 +0,0 @@
|
||||
import request from '@/utils/request';
|
||||
import { AxiosPromise } from 'axios';
|
||||
|
||||
/**
|
||||
* 业务期间查询参数
|
||||
*/
|
||||
export interface XgBusinessPeriodQuery {
|
||||
id?: number;
|
||||
batchId?: number;
|
||||
periodName?: string;
|
||||
periodType?: string;
|
||||
targetRole?: string;
|
||||
startTime?: string;
|
||||
endTime?: string;
|
||||
status?: string;
|
||||
enabled?: boolean;
|
||||
keyword?: string;
|
||||
batchName?: string;
|
||||
pageNum?: number;
|
||||
pageSize?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 业务期间表单
|
||||
*/
|
||||
export interface XgBusinessPeriodForm {
|
||||
id?: number;
|
||||
batchId?: number;
|
||||
periodName?: string;
|
||||
periodType?: string;
|
||||
targetRole?: string;
|
||||
startTime?: string;
|
||||
endTime?: string;
|
||||
status?: string;
|
||||
description?: string;
|
||||
sortOrder?: number;
|
||||
enabled?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* 业务期间视图对象
|
||||
*/
|
||||
export interface XgBusinessPeriodVO {
|
||||
id?: number;
|
||||
batchId?: number;
|
||||
batchName?: string;
|
||||
periodName?: string;
|
||||
periodType?: string;
|
||||
targetRole?: string;
|
||||
startTime?: string;
|
||||
endTime?: string;
|
||||
status?: string;
|
||||
description?: string;
|
||||
sortOrder?: number;
|
||||
enabled?: boolean;
|
||||
createTime?: string;
|
||||
updateTime?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询业务期间列表
|
||||
*/
|
||||
export const listBusinessPeriod = (query: XgBusinessPeriodQuery): AxiosPromise<XgBusinessPeriodVO[]> => {
|
||||
return request({
|
||||
url: '/xg/businessPeriod/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 查询所有业务期间列表(不分页)
|
||||
*/
|
||||
export const listAllBusinessPeriod = (query: XgBusinessPeriodQuery): AxiosPromise<XgBusinessPeriodVO[]> => {
|
||||
return request({
|
||||
url: '/xg/businessPeriod/listAll',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取业务期间详细信息
|
||||
*/
|
||||
export const getBusinessPeriod = (id: string | number): AxiosPromise<XgBusinessPeriodVO> => {
|
||||
return request({
|
||||
url: '/xg/businessPeriod/' + id,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 新增业务期间
|
||||
*/
|
||||
export const addBusinessPeriod = (data: XgBusinessPeriodForm) => {
|
||||
return request({
|
||||
url: '/xg/businessPeriod',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 修改业务期间
|
||||
*/
|
||||
export const updateBusinessPeriod = (data: XgBusinessPeriodForm) => {
|
||||
return request({
|
||||
url: '/xg/businessPeriod',
|
||||
method: 'put',
|
||||
data: data
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 删除业务期间
|
||||
*/
|
||||
export const delBusinessPeriod = (id: string | number | Array<string | number>) => {
|
||||
return request({
|
||||
url: '/xg/businessPeriod/' + id,
|
||||
method: 'delete'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 导出业务期间
|
||||
*/
|
||||
export const exportBusinessPeriod = (query: XgBusinessPeriodQuery) => {
|
||||
return request({
|
||||
url: '/xg/businessPeriod/export',
|
||||
method: 'post',
|
||||
data: query
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 根据批次ID查询业务期间列表
|
||||
*/
|
||||
export const queryByBatchId = (batchId: number): AxiosPromise<XgBusinessPeriodVO[]> => {
|
||||
return request({
|
||||
url: '/xg/businessPeriod/byBatch/' + batchId,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 查询当前活跃的业务期间
|
||||
*/
|
||||
export const queryActivePeriods = (): AxiosPromise<XgBusinessPeriodVO[]> => {
|
||||
return request({
|
||||
url: '/xg/businessPeriod/active',
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 检查业务期间是否开放
|
||||
*/
|
||||
export const checkPeriodOpen = (batchId: number): AxiosPromise<boolean> => {
|
||||
return request({
|
||||
url: '/xg/businessPeriod/checkOpen/' + batchId,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 开启业务期间
|
||||
*/
|
||||
export const openPeriod = (id: string | number) => {
|
||||
return request({
|
||||
url: '/xg/businessPeriod/open/' + id,
|
||||
method: 'post'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 关闭业务期间
|
||||
*/
|
||||
export const closePeriod = (id: string | number) => {
|
||||
return request({
|
||||
url: '/xg/businessPeriod/close/' + id,
|
||||
method: 'post'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 校验业务期间时间是否冲突
|
||||
*/
|
||||
export const checkPeriodTimeConflict = (data: XgBusinessPeriodForm): AxiosPromise<boolean> => {
|
||||
return request({
|
||||
url: '/xg/businessPeriod/checkTimeConflict',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
};
|
||||
@@ -1,265 +0,0 @@
|
||||
import request from '@/utils/request';
|
||||
import { AxiosPromise } from 'axios';
|
||||
import { XgClassCadreVO, XgClassCadreForm, XgClassCadreQuery } from '@/api/xg-xx/types';
|
||||
|
||||
// 查询班级干部信息列表
|
||||
export const listClassCadre = (query: XgClassCadreQuery): AxiosPromise<XgClassCadreVO[]> => {
|
||||
return request({
|
||||
url: '/xg/classCadre/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
};
|
||||
|
||||
// 查询班级干部信息详细
|
||||
export const getClassCadre = (id: string | number): AxiosPromise<XgClassCadreVO> => {
|
||||
return request({
|
||||
url: '/xg/classCadre/' + id,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
// 新增班级干部信息
|
||||
export const addClassCadre = (data: XgClassCadreForm) => {
|
||||
return request({
|
||||
url: '/xg/classCadre',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
};
|
||||
|
||||
// 修改班级干部信息
|
||||
export const updateClassCadre = (data: XgClassCadreForm) => {
|
||||
return request({
|
||||
url: '/xg/classCadre',
|
||||
method: 'put',
|
||||
data: data
|
||||
});
|
||||
};
|
||||
|
||||
// 删除班级干部信息
|
||||
export const delClassCadre = (ids: string | number | Array<string | number>) => {
|
||||
return request({
|
||||
url: '/xg/classCadre/' + ids,
|
||||
method: 'delete'
|
||||
});
|
||||
};
|
||||
|
||||
// 导出班级干部信息
|
||||
export const exportClassCadre = (query: XgClassCadreQuery) => {
|
||||
return request({
|
||||
url: '/xg/classCadre/export',
|
||||
method: 'post',
|
||||
data: query
|
||||
});
|
||||
};
|
||||
|
||||
// ==================== 业务功能接口 ====================
|
||||
|
||||
// 根据班级ID查询班干部列表
|
||||
export const getCadresByClass = (classId: string | number): AxiosPromise<XgClassCadreVO[]> => {
|
||||
return request({
|
||||
url: '/xg/classCadre/class/' + classId,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
// 根据学生ID查询班干部信息
|
||||
export const getCadresByStudent = (studentId: string | number): AxiosPromise<XgClassCadreVO[]> => {
|
||||
return request({
|
||||
url: '/xg/classCadre/student/' + studentId,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
// 查询当前在任班干部列表
|
||||
export const getCurrentCadres = (classId?: string | number): AxiosPromise<XgClassCadreVO[]> => {
|
||||
return request({
|
||||
url: '/xg/classCadre/current',
|
||||
method: 'get',
|
||||
params: { classId }
|
||||
});
|
||||
};
|
||||
|
||||
// 根据职务查询班干部
|
||||
export const getCadreByPosition = (classId: string | number, positionCode: string): AxiosPromise<XgClassCadreVO> => {
|
||||
return request({
|
||||
url: '/xg/classCadre/position',
|
||||
method: 'get',
|
||||
params: { classId, positionCode }
|
||||
});
|
||||
};
|
||||
|
||||
// 任命班干部
|
||||
export const appointCadre = (data: XgClassCadreForm) => {
|
||||
return request({
|
||||
url: '/xg/classCadre/appoint',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
};
|
||||
|
||||
// 免职班干部
|
||||
export const dismissCadre = (cadreId: string | number, dismissalReason: string, dismissalDate: string) => {
|
||||
return request({
|
||||
url: '/xg/classCadre/dismiss/' + cadreId,
|
||||
method: 'put',
|
||||
params: { dismissalReason, dismissalDate }
|
||||
});
|
||||
};
|
||||
|
||||
// 调任班干部
|
||||
export const transferCadre = (
|
||||
cadreId: string | number,
|
||||
newPositionCode: string,
|
||||
newPositionName: string,
|
||||
transferReason: string,
|
||||
effectiveDate: string
|
||||
) => {
|
||||
return request({
|
||||
url: '/xg/classCadre/transfer/' + cadreId,
|
||||
method: 'put',
|
||||
params: { newPositionCode, newPositionName, transferReason, effectiveDate }
|
||||
});
|
||||
};
|
||||
|
||||
// 续任班干部
|
||||
export const renewCadre = (cadreId: string | number, newTermDuration: number, renewalReason: string) => {
|
||||
return request({
|
||||
url: '/xg/classCadre/renew/' + cadreId,
|
||||
method: 'put',
|
||||
params: { newTermDuration, renewalReason }
|
||||
});
|
||||
};
|
||||
|
||||
// 班干部辞职
|
||||
export const resignCadre = (cadreId: string | number, resignationReason: string, resignationDate: string) => {
|
||||
return request({
|
||||
url: '/xg/classCadre/resign/' + cadreId,
|
||||
method: 'put',
|
||||
params: { resignationReason, resignationDate }
|
||||
});
|
||||
};
|
||||
|
||||
// ==================== 统计查询接口 ====================
|
||||
|
||||
// 查询即将到期的班干部列表
|
||||
export const getExpiringCadres = (days: number = 30): AxiosPromise<XgClassCadreVO[]> => {
|
||||
return request({
|
||||
url: '/xg/classCadre/expiring',
|
||||
method: 'get',
|
||||
params: { days }
|
||||
});
|
||||
};
|
||||
|
||||
// 查询已到期的班干部列表
|
||||
export const getExpiredCadres = (): AxiosPromise<XgClassCadreVO[]> => {
|
||||
return request({
|
||||
url: '/xg/classCadre/expired',
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
// 根据学院查询班干部统计
|
||||
export const getCadresByCollege = (collegeName: string): AxiosPromise<XgClassCadreVO[]> => {
|
||||
return request({
|
||||
url: '/xg/classCadre/college/' + collegeName,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
// 根据年级查询班干部统计
|
||||
export const getCadresByGrade = (grade: string): AxiosPromise<XgClassCadreVO[]> => {
|
||||
return request({
|
||||
url: '/xg/classCadre/grade/' + grade,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
// 查询班干部任期统计
|
||||
export const getCadreTermStatistics = (startDate: string, endDate: string): AxiosPromise<XgClassCadreVO[]> => {
|
||||
return request({
|
||||
url: '/xg/classCadre/term-statistics',
|
||||
method: 'get',
|
||||
params: { startDate, endDate }
|
||||
});
|
||||
};
|
||||
|
||||
// 根据职务级别查询班干部
|
||||
export const getCadresByLevel = (positionLevel: number): AxiosPromise<XgClassCadreVO[]> => {
|
||||
return request({
|
||||
url: '/xg/classCadre/level/' + positionLevel,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
// 查询班干部工作表现统计
|
||||
export const getCadresByPerformance = (performance: string): AxiosPromise<XgClassCadreVO[]> => {
|
||||
return request({
|
||||
url: '/xg/classCadre/performance/' + performance,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
// ==================== 辅助功能接口 ====================
|
||||
|
||||
// 检查职务是否已被占用
|
||||
export const checkPositionOccupied = (classId: string | number, positionCode: string, excludeId?: string | number): AxiosPromise<boolean> => {
|
||||
return request({
|
||||
url: '/xg/classCadre/check-position',
|
||||
method: 'get',
|
||||
params: { classId, positionCode, excludeId }
|
||||
});
|
||||
};
|
||||
|
||||
// 检查学生是否已有其他职务
|
||||
export const checkStudentHasOtherPosition = (studentId: string | number, excludeId?: string | number): AxiosPromise<boolean> => {
|
||||
return request({
|
||||
url: '/xg/classCadre/check-student',
|
||||
method: 'get',
|
||||
params: { studentId, excludeId }
|
||||
});
|
||||
};
|
||||
|
||||
// 批量更新班干部状态
|
||||
export const batchUpdateCadreStatus = (cadreIds: Array<string | number>, status: string) => {
|
||||
return request({
|
||||
url: '/xg/classCadre/batch-status',
|
||||
method: 'put',
|
||||
params: { cadreIds: cadreIds.join(','), status }
|
||||
});
|
||||
};
|
||||
|
||||
// 自动处理到期班干部
|
||||
export const autoProcessExpiredCadres = () => {
|
||||
return request({
|
||||
url: '/xg/classCadre/auto-process-expired',
|
||||
method: 'post'
|
||||
});
|
||||
};
|
||||
|
||||
// 发送到期提醒通知
|
||||
export const sendExpirationReminder = (days: number = 30) => {
|
||||
return request({
|
||||
url: '/xg/classCadre/send-reminder',
|
||||
method: 'post',
|
||||
params: { days }
|
||||
});
|
||||
};
|
||||
|
||||
// 导入班干部信息
|
||||
export const importClassCadre = (data: FormData) => {
|
||||
return request({
|
||||
url: '/xg/classCadre/import',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
};
|
||||
|
||||
// 获取导入模板
|
||||
export const importTemplate = () => {
|
||||
return request({
|
||||
url: '/xg/classCadre/import-template',
|
||||
method: 'post'
|
||||
});
|
||||
};
|
||||
@@ -1,205 +0,0 @@
|
||||
import request from '@/utils/request';
|
||||
import { AxiosPromise } from 'axios';
|
||||
import { ClassInfoVO, ClassInfoForm, ClassInfoQuery, ClassOption, TeacherOption } from './types';
|
||||
|
||||
/**
|
||||
* 查询班级信息列表
|
||||
* @param query 查询参数
|
||||
*/
|
||||
export const listClassInfo = (query?: ClassInfoQuery): AxiosPromise<ClassInfoVO[]> => {
|
||||
return request({
|
||||
url: '/xg/classInfo/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 查询班级信息详细信息
|
||||
* @param id 班级ID
|
||||
*/
|
||||
export const getClassInfo = (id: string | number): AxiosPromise<ClassInfoVO> => {
|
||||
return request({
|
||||
url: `/xg/classInfo/${id}`,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 新增班级信息
|
||||
* @param data 班级信息数据
|
||||
*/
|
||||
export const addClassInfo = (data: ClassInfoForm) => {
|
||||
return request({
|
||||
url: '/xg/classInfo',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 修改班级信息
|
||||
* @param data 班级信息数据
|
||||
*/
|
||||
export const updateClassInfo = (data: ClassInfoForm) => {
|
||||
return request({
|
||||
url: '/xg/classInfo',
|
||||
method: 'put',
|
||||
data: data
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 删除班级信息
|
||||
* @param id 班级ID
|
||||
*/
|
||||
export const delClassInfo = (id: string | number | Array<string | number>) => {
|
||||
return request({
|
||||
url: `/xg/classInfo/${id}`,
|
||||
method: 'delete'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 根据班级代码查询班级信息
|
||||
* @param classCode 班级代码
|
||||
*/
|
||||
export const getClassInfoByCode = (classCode: string): AxiosPromise<ClassInfoVO> => {
|
||||
return request({
|
||||
url: `/xg/classInfo/byCode/${classCode}`,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 查询学院班级列表
|
||||
* @param collegeCode 学院代码
|
||||
*/
|
||||
export const getClassInfoByCollege = (collegeCode: string): AxiosPromise<ClassInfoVO[]> => {
|
||||
return request({
|
||||
url: `/xg/classInfo/byCollege/${collegeCode}`,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取班级选项列表
|
||||
*/
|
||||
export const getClassInfoOptions = (): AxiosPromise<ClassOption[]> => {
|
||||
return request({
|
||||
url: '/xg/classInfo/options',
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 检查班级代码是否存在
|
||||
* @param classCode 班级代码
|
||||
*/
|
||||
export const checkClassCode = (classCode: string): AxiosPromise<boolean> => {
|
||||
return request({
|
||||
url: '/xg/classInfo/checkClassCode',
|
||||
method: 'get',
|
||||
params: { classCode }
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 更新班级人数统计
|
||||
* @param classId 班级ID
|
||||
*/
|
||||
export const updateClassStudentCount = (classId: string | number) => {
|
||||
return request({
|
||||
url: `/xg/classInfo/${classId}/updateCount`,
|
||||
method: 'put'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取辅导员选项列表
|
||||
* @param collegeCode 学院代码(可选)
|
||||
*/
|
||||
export const getCounselorOptions = (collegeCode?: string): AxiosPromise<TeacherOption[]> => {
|
||||
return request({
|
||||
url: '/xg/classInfo/counselorOptions',
|
||||
method: 'get',
|
||||
params: { collegeCode }
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取班主任选项列表
|
||||
* @param collegeCode 学院代码(可选)
|
||||
*/
|
||||
export const getClassTeacherOptions = (collegeCode?: string): AxiosPromise<TeacherOption[]> => {
|
||||
return request({
|
||||
url: '/xg/classInfo/classTeacherOptions',
|
||||
method: 'get',
|
||||
params: { collegeCode }
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 导出班级信息
|
||||
* @param query 查询参数
|
||||
*/
|
||||
export const exportClassInfo = (query?: ClassInfoQuery) => {
|
||||
return request({
|
||||
url: '/xg/classInfo/export',
|
||||
method: 'post',
|
||||
data: query
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 批量生成班级代码
|
||||
* @param params 生成参数
|
||||
*/
|
||||
export const generateClassCode = (params: {
|
||||
collegeCode: string;
|
||||
majorCode: string;
|
||||
enrollmentYear: number;
|
||||
classCount: number;
|
||||
}): AxiosPromise<string[]> => {
|
||||
return request({
|
||||
url: '/xg/classInfo/generateClassCode',
|
||||
method: 'post',
|
||||
data: params
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 查询班级学生统计
|
||||
* @param classId 班级ID
|
||||
*/
|
||||
export const getClassStudentStatistics = (
|
||||
classId: string | number
|
||||
): AxiosPromise<{
|
||||
totalCount: number;
|
||||
maleCount: number;
|
||||
femaleCount: number;
|
||||
activeCount: number;
|
||||
suspendedCount: number;
|
||||
graduatedCount: number;
|
||||
}> => {
|
||||
return request({
|
||||
url: `/xg/classInfo/${classId}/statistics`,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取班级信息总体统计
|
||||
*/
|
||||
export const getClassInfoStatistics = (): AxiosPromise<{
|
||||
totalCount: number;
|
||||
activeCount: number;
|
||||
suspendedCount: number;
|
||||
graduatedCount: number;
|
||||
averageStudentCount: number;
|
||||
}> => {
|
||||
return request({
|
||||
url: '/xg/classInfo/statistics',
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
@@ -1,162 +0,0 @@
|
||||
import request from '@/utils/request';
|
||||
import { AxiosPromise } from 'axios';
|
||||
import { XgCollectionBatchVO, XgCollectionBatchForm, XgCollectionBatchQuery } from './types';
|
||||
|
||||
/**
|
||||
* 查询信息采集批次列表
|
||||
*/
|
||||
export const listCollectionBatch = (query: XgCollectionBatchQuery): AxiosPromise<XgCollectionBatchVO[]> => {
|
||||
return request({
|
||||
url: '/xg/collectionBatch/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 查询所有信息采集批次列表(不分页)
|
||||
*/
|
||||
export const listAllCollectionBatch = (query: XgCollectionBatchQuery): AxiosPromise<XgCollectionBatchVO[]> => {
|
||||
return request({
|
||||
url: '/xg/collectionBatch/listAll',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 查询信息采集批次详细
|
||||
*/
|
||||
export const getCollectionBatch = (id: string | number): AxiosPromise<XgCollectionBatchVO> => {
|
||||
return request({
|
||||
url: '/xg/collectionBatch/' + id,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 新增信息采集批次
|
||||
*/
|
||||
export const addCollectionBatch = (data: XgCollectionBatchForm) => {
|
||||
return request({
|
||||
url: '/xg/collectionBatch',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 修改信息采集批次
|
||||
*/
|
||||
export const updateCollectionBatch = (data: XgCollectionBatchForm) => {
|
||||
return request({
|
||||
url: '/xg/collectionBatch',
|
||||
method: 'put',
|
||||
data: data
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 删除信息采集批次
|
||||
*/
|
||||
export const delCollectionBatch = (id: string | number | Array<string | number>) => {
|
||||
return request({
|
||||
url: '/xg/collectionBatch/' + id,
|
||||
method: 'delete'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 导出信息采集批次
|
||||
*/
|
||||
export const exportCollectionBatch = (query: XgCollectionBatchQuery) => {
|
||||
return request({
|
||||
url: '/xg/collectionBatch/export',
|
||||
method: 'post',
|
||||
data: query
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取当前活跃的批次列表
|
||||
*/
|
||||
export const getActiveBatches = (): AxiosPromise<XgCollectionBatchVO[]> => {
|
||||
return request({
|
||||
url: '/xg/collectionBatch/active',
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 发布批次
|
||||
*/
|
||||
export const publishBatch = (id: string | number) => {
|
||||
return request({
|
||||
url: '/xg/collectionBatch/publish/' + id,
|
||||
method: 'post'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 取消发布批次
|
||||
*/
|
||||
export const unpublishBatch = (id: string | number) => {
|
||||
return request({
|
||||
url: '/xg/collectionBatch/unpublish/' + id,
|
||||
method: 'post'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 结束批次
|
||||
*/
|
||||
export const completeBatch = (id: string | number) => {
|
||||
return request({
|
||||
url: '/xg/collectionBatch/complete/' + id,
|
||||
method: 'post'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 复制批次
|
||||
*/
|
||||
export const copyBatch = (id: string | number, newBatchName: string, newBatchCode: string) => {
|
||||
return request({
|
||||
url: '/xg/collectionBatch/copy/' + id,
|
||||
method: 'post',
|
||||
params: {
|
||||
newBatchName,
|
||||
newBatchCode
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取批次统计信息
|
||||
*/
|
||||
export const getBatchStatistics = (id: string | number) => {
|
||||
return request({
|
||||
url: '/xg/collectionBatch/statistics/' + id,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 更新批次统计信息
|
||||
*/
|
||||
export const updateBatchStatistics = (id: string | number) => {
|
||||
return request({
|
||||
url: '/xg/collectionBatch/updateStatistics/' + id,
|
||||
method: 'post'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取批次选项列表(用于下拉框)
|
||||
*/
|
||||
export const getBatchOptions = () => {
|
||||
return request({
|
||||
url: '/xg/collectionBatch/options',
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
@@ -1,18 +0,0 @@
|
||||
/**
|
||||
* 学工管理模块 API 入口
|
||||
*/
|
||||
|
||||
// 学生信息管理
|
||||
export * from './studentInfo';
|
||||
// 班级信息管理
|
||||
export * from './classInfo';
|
||||
// 班级干部管理
|
||||
export * from './classCadre';
|
||||
// 学年管理
|
||||
export * from './academicYear';
|
||||
// 学期管理
|
||||
export * from './semester';
|
||||
// 类型定义
|
||||
export * from './types';
|
||||
export * from './academicTypes';
|
||||
export * from './collectionBatch';
|
||||
@@ -1,128 +0,0 @@
|
||||
import request from '@/utils/request';
|
||||
import { AxiosPromise } from 'axios';
|
||||
import { SemesterVO, SemesterForm, SemesterQuery } from './academicTypes';
|
||||
|
||||
/**
|
||||
* 查询学期管理列表
|
||||
* @param query 查询参数
|
||||
*/
|
||||
export const listSemester = (query?: SemesterQuery): AxiosPromise<SemesterVO[]> => {
|
||||
return request({
|
||||
url: '/xg-xx/semester/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 查询学期管理详细信息
|
||||
* @param id 学期ID
|
||||
*/
|
||||
export const getSemester = (id: string | number): AxiosPromise<SemesterVO> => {
|
||||
return request({
|
||||
url: `/xg-xx/semester/${id}`,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 新增学期管理
|
||||
* @param data 学期管理数据
|
||||
*/
|
||||
export const addSemester = (data: SemesterForm) => {
|
||||
return request({
|
||||
url: '/xg-xx/semester',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 修改学期管理
|
||||
* @param data 学期管理数据
|
||||
*/
|
||||
export const updateSemester = (data: SemesterForm) => {
|
||||
return request({
|
||||
url: '/xg-xx/semester',
|
||||
method: 'put',
|
||||
data: data
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 删除学期管理
|
||||
* @param id 学期ID
|
||||
*/
|
||||
export const delSemester = (id: string | number | Array<string | number>) => {
|
||||
return request({
|
||||
url: `/xg-xx/semester/${id}`,
|
||||
method: 'delete'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 导出学期管理列表
|
||||
* @param query 查询参数
|
||||
*/
|
||||
export const exportSemester = (query?: SemesterQuery) => {
|
||||
return request({
|
||||
url: '/xg-xx/semester/export',
|
||||
method: 'post',
|
||||
data: query
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 根据学年ID查询学期列表
|
||||
* @param academicYearId 学年ID
|
||||
*/
|
||||
export const getSemestersByAcademicYear = (academicYearId: string | number): AxiosPromise<SemesterVO[]> => {
|
||||
return request({
|
||||
url: `/xg-xx/semester/getByAcademicYear/${academicYearId}`,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 查询当前学期
|
||||
*/
|
||||
export const getCurrentSemester = (): AxiosPromise<SemesterVO> => {
|
||||
return request({
|
||||
url: '/xg-xx/semester/current',
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 设置当前学期
|
||||
* @param id 学期ID
|
||||
*/
|
||||
export const setCurrentSemester = (id: string | number) => {
|
||||
return request({
|
||||
url: `/xg-xx/semester/setCurrent/${id}`,
|
||||
method: 'put'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 查询可用的学期选项
|
||||
*/
|
||||
export const getAvailableSemesterOptions = (): AxiosPromise<SemesterVO[]> => {
|
||||
return request({
|
||||
url: '/xg-xx/semester/availableOptions',
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 校验学期名称是否唯一
|
||||
* @param semesterName 学期名称
|
||||
* @param id 学期ID
|
||||
*/
|
||||
export const checkSemesterName = (semesterName: string, id?: string | number): AxiosPromise<boolean> => {
|
||||
return request({
|
||||
url: '/xg-xx/semester/checkSemesterName',
|
||||
method: 'get',
|
||||
params: { semesterName, id }
|
||||
});
|
||||
};
|
||||
@@ -1,478 +0,0 @@
|
||||
import request from '@/utils/request';
|
||||
import { AxiosPromise } from 'axios';
|
||||
import {
|
||||
StudentInfoVO,
|
||||
StudentInfoForm,
|
||||
StudentInfoQuery,
|
||||
StudentStatisticsVO,
|
||||
BatchUpdateStatusForm,
|
||||
StudentAuditForm,
|
||||
SubmitAuditForm,
|
||||
ClassOption,
|
||||
CollegeOption,
|
||||
MajorOption,
|
||||
ImportResult
|
||||
} from './types';
|
||||
|
||||
/**
|
||||
* 查询学生信息列表
|
||||
* @param query 查询参数
|
||||
*/
|
||||
export const listStudentInfo = (query?: StudentInfoQuery): AxiosPromise<StudentInfoVO[]> => {
|
||||
return request({
|
||||
url: '/xg/studentInfo/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 查询学生信息详细信息
|
||||
* @param id 学生ID
|
||||
*/
|
||||
export const getStudentInfo = (id: string | number): AxiosPromise<StudentInfoVO> => {
|
||||
return request({
|
||||
url: `/xg/studentInfo/${id}`,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 根据学号查询学生信息
|
||||
* @param studentNo 学号
|
||||
*/
|
||||
export const getStudentInfoByStudentNo = (studentNo: string): AxiosPromise<StudentInfoVO> => {
|
||||
return request({
|
||||
url: `/xg/studentInfo/byStudentNo/${studentNo}`,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 根据身份证号查询学生信息
|
||||
* @param idCard 身份证号
|
||||
*/
|
||||
export const getStudentInfoByIdCard = (idCard: string): AxiosPromise<StudentInfoVO> => {
|
||||
return request({
|
||||
url: `/xg/studentInfo/byIdCard/${idCard}`,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 查询班级学生列表
|
||||
* @param classCode 班级代码
|
||||
*/
|
||||
export const getStudentInfoByClass = (classCode: string): AxiosPromise<StudentInfoVO[]> => {
|
||||
return request({
|
||||
url: `/xg/studentInfo/byClass/${classCode}`,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 查询学院学生列表
|
||||
* @param collegeCode 学院代码
|
||||
*/
|
||||
export const getStudentInfoByCollege = (collegeCode: string): AxiosPromise<StudentInfoVO[]> => {
|
||||
return request({
|
||||
url: `/xg/studentInfo/byCollege/${collegeCode}`,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 新增学生信息
|
||||
* @param data 学生信息数据
|
||||
*/
|
||||
export const addStudentInfo = (data: StudentInfoForm) => {
|
||||
return request({
|
||||
url: '/xg/studentInfo',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 修改学生信息
|
||||
* @param data 学生信息数据
|
||||
*/
|
||||
export const updateStudentInfo = (data: StudentInfoForm) => {
|
||||
return request({
|
||||
url: '/xg/studentInfo',
|
||||
method: 'put',
|
||||
data: data
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 删除学生信息
|
||||
* @param id 学生ID
|
||||
*/
|
||||
export const delStudentInfo = (id: string | number | Array<string | number>) => {
|
||||
return request({
|
||||
url: `/xg/studentInfo/${id}`,
|
||||
method: 'delete'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 导出学生信息
|
||||
* @param query 查询参数
|
||||
*/
|
||||
export const exportStudentInfo = (query?: StudentInfoQuery) => {
|
||||
return request({
|
||||
url: '/xg/studentInfo/export',
|
||||
method: 'post',
|
||||
data: query
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 批量导入学生信息
|
||||
* @param file 文件
|
||||
* @param updateSupport 是否更新支持
|
||||
*/
|
||||
export const importStudentInfo = (file: File, updateSupport: boolean = false): AxiosPromise<ImportResult> => {
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
formData.append('updateSupport', updateSupport.toString());
|
||||
|
||||
return request({
|
||||
url: '/xg/studentInfo/importData',
|
||||
method: 'post',
|
||||
data: formData,
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data'
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取导入模板
|
||||
*/
|
||||
export const importTemplate = () => {
|
||||
return request({
|
||||
url: '/xg/studentInfo/importTemplate',
|
||||
method: 'post'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 批量更新学生信息状态
|
||||
* @param data 批量更新数据
|
||||
*/
|
||||
export const batchUpdateStudentInfoStatus = (data: BatchUpdateStatusForm) => {
|
||||
return request({
|
||||
url: '/xg/studentInfo/batchUpdateStatus',
|
||||
method: 'put',
|
||||
data: data
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 提交学生信息审核
|
||||
* @param data 提交审核数据
|
||||
*/
|
||||
export const submitStudentInfoAudit = (data: SubmitAuditForm) => {
|
||||
return request({
|
||||
url: '/xg/studentInfo/submitAudit',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 审核学生信息
|
||||
* @param data 审核数据
|
||||
*/
|
||||
export const auditStudentInfo = (data: StudentAuditForm) => {
|
||||
return request({
|
||||
url: '/xg/studentInfo/audit',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 查询学生统计信息
|
||||
* @param query 查询参数
|
||||
*/
|
||||
export const getStudentStatistics = (query?: {
|
||||
collegeCode?: string;
|
||||
classCode?: string;
|
||||
enrollmentYear?: number;
|
||||
studentStatus?: string;
|
||||
}): AxiosPromise<StudentStatisticsVO[]> => {
|
||||
return request({
|
||||
url: '/xg/studentInfo/statistics',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 检查学号是否存在
|
||||
* @param studentNo 学号
|
||||
* @param id 学生ID(编辑时传入)
|
||||
*/
|
||||
export const checkStudentNo = (studentNo: string, id?: string | number): AxiosPromise<boolean> => {
|
||||
return request({
|
||||
url: '/xg/studentInfo/checkStudentNo',
|
||||
method: 'get',
|
||||
params: { studentNo, id }
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 检查身份证号是否存在
|
||||
* @param idCard 身份证号
|
||||
* @param id 学生ID(编辑时传入)
|
||||
*/
|
||||
export const checkIdCard = (idCard: string, id?: string | number): AxiosPromise<boolean> => {
|
||||
return request({
|
||||
url: '/xg/studentInfo/checkIdCard',
|
||||
method: 'get',
|
||||
params: { idCard, id }
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取班级选项列表
|
||||
* @param collegeCode 学院代码(可选)
|
||||
*/
|
||||
export const getClassOptions = (collegeCode?: string): AxiosPromise<ClassOption[]> => {
|
||||
return request({
|
||||
url: '/xg/studentInfo/classOptions',
|
||||
method: 'get',
|
||||
params: { collegeCode }
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取学院选项列表
|
||||
*/
|
||||
export const getCollegeOptions = (): AxiosPromise<CollegeOption[]> => {
|
||||
return request({
|
||||
url: '/xg/studentInfo/collegeOptions',
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取专业选项列表
|
||||
* @param collegeCode 学院代码(可选)
|
||||
*/
|
||||
export const getMajorOptions = (collegeCode?: string): AxiosPromise<MajorOption[]> => {
|
||||
return request({
|
||||
url: '/xg/studentInfo/majorOptions',
|
||||
method: 'get',
|
||||
params: { collegeCode }
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取学生选项列表(用于下拉选择)
|
||||
* @param query 查询参数
|
||||
*/
|
||||
export const getStudentOptions = (query?: {
|
||||
name?: string;
|
||||
classCode?: string;
|
||||
collegeCode?: string;
|
||||
}): AxiosPromise<Array<{ value: string | number; label: string; studentNo?: string; className?: string }>> => {
|
||||
return request({
|
||||
url: '/xg/studentInfo/studentOptions',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 同步学生信息
|
||||
* @param source 同步来源:1-教务系统,2-招生系统
|
||||
*/
|
||||
export const syncStudentInfo = (source: string) => {
|
||||
return request({
|
||||
url: '/xg/studentInfo/sync',
|
||||
method: 'post',
|
||||
data: { source }
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 生成学生学号
|
||||
* @param collegeCode 学院代码
|
||||
* @param enrollmentYear 入学年份
|
||||
*/
|
||||
export const generateStudentNo = (collegeCode: string, enrollmentYear: number): AxiosPromise<string> => {
|
||||
return request({
|
||||
url: '/xg/studentInfo/generateStudentNo',
|
||||
method: 'get',
|
||||
params: { collegeCode, enrollmentYear }
|
||||
});
|
||||
};
|
||||
|
||||
// ==================== 批次相关接口 ====================
|
||||
|
||||
/**
|
||||
* 根据批次ID查询学生信息
|
||||
* @param batchId 批次ID
|
||||
* @param query 查询参数
|
||||
*/
|
||||
export const getStudentsByBatch = (batchId: string | number, query?: StudentInfoQuery): AxiosPromise<StudentInfoVO[]> => {
|
||||
return request({
|
||||
url: `/xg/studentInfo/batch/${batchId}`,
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 关联学生到批次
|
||||
* @param studentId 学生ID
|
||||
* @param batchId 批次ID
|
||||
*/
|
||||
export const associateStudentToBatch = (studentId: string | number, batchId: string | number) => {
|
||||
return request({
|
||||
url: '/xg/studentInfo/associateToBatch',
|
||||
method: 'post',
|
||||
data: { studentId, batchId }
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 批量关联学生到批次
|
||||
* @param studentIds 学生ID数组
|
||||
* @param batchId 批次ID
|
||||
*/
|
||||
export const batchAssociateStudentsToBatch = (studentIds: (string | number)[], batchId: string | number) => {
|
||||
return request({
|
||||
url: '/xg/studentInfo/batchAssociateToBatch',
|
||||
method: 'post',
|
||||
data: { studentIds, batchId }
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 提交学生信息到批次
|
||||
* @param studentId 学生ID
|
||||
* @param batchId 批次ID
|
||||
*/
|
||||
export const submitStudentInfo = (studentId: string | number, batchId: string | number) => {
|
||||
return request({
|
||||
url: '/xg/studentInfo/submit',
|
||||
method: 'post',
|
||||
data: { studentId, batchId }
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 撤回学生信息提交
|
||||
* @param studentId 学生ID
|
||||
* @param batchId 批次ID
|
||||
*/
|
||||
export const withdrawStudentInfo = (studentId: string | number, batchId: string | number) => {
|
||||
return request({
|
||||
url: '/xg/studentInfo/withdraw',
|
||||
method: 'post',
|
||||
data: { studentId, batchId }
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取学生批次进度
|
||||
* @param studentId 学生ID
|
||||
* @param batchId 批次ID
|
||||
*/
|
||||
export const getStudentBatchProgress = (studentId: string | number, batchId: string | number) => {
|
||||
return request({
|
||||
url: `/xg/studentInfo/batchProgress/${studentId}/${batchId}`,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 批量审核学生信息
|
||||
* @param studentIds 学生ID数组
|
||||
* @param batchId 批次ID
|
||||
* @param approvalStatus 审核状态
|
||||
* @param approvalComments 审核意见
|
||||
*/
|
||||
export const batchAuditStudentInfo = (data: {
|
||||
studentIds: (string | number)[];
|
||||
batchId: string | number;
|
||||
approvalStatus: string;
|
||||
approvalComments?: string;
|
||||
}) => {
|
||||
return request({
|
||||
url: '/xg/studentInfo/batchAudit',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 检查学生编辑权限
|
||||
* @param studentId 学生ID
|
||||
* @param batchId 批次ID
|
||||
*/
|
||||
export const checkEditPermission = (studentId: string | number, batchId: string | number) => {
|
||||
return request({
|
||||
url: `/xg/studentInfo/checkEditPermission/${studentId}/${batchId}`,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取学生批次统计信息
|
||||
* @param batchId 批次ID
|
||||
*/
|
||||
export const getStudentBatchStatistics = (batchId: string | number) => {
|
||||
return request({
|
||||
url: `/xg/studentInfo/batchStatistics/${batchId}`,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 批量提交学生信息
|
||||
* @param studentIds 学生ID数组
|
||||
* @param batchId 批次ID
|
||||
*/
|
||||
export const batchSubmitStudentInfo = (studentIds: (string | number)[], batchId: string | number) => {
|
||||
return request({
|
||||
url: '/xg/studentInfo/batchSubmit',
|
||||
method: 'post',
|
||||
data: { studentIds, batchId }
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 批量撤回学生信息
|
||||
* @param studentIds 学生ID数组
|
||||
* @param batchId 批次ID
|
||||
*/
|
||||
export const batchWithdrawStudentInfo = (studentIds: (string | number)[], batchId: string | number) => {
|
||||
return request({
|
||||
url: '/xg/studentInfo/batchWithdraw',
|
||||
method: 'post',
|
||||
data: { studentIds, batchId }
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取所有学生选项(用于批次关联)
|
||||
* @param query 查询参数
|
||||
*/
|
||||
export const getAllStudentOptions = (query?: {
|
||||
name?: string;
|
||||
studentNo?: string;
|
||||
classCode?: string;
|
||||
collegeCode?: string;
|
||||
}): AxiosPromise<Array<{ id: string | number; name: string; studentNo: string; className?: string; collegeName?: string }>> => {
|
||||
return request({
|
||||
url: '/xg/studentInfo/allOptions',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
};
|
||||
@@ -1,182 +0,0 @@
|
||||
import request from '@/utils/request';
|
||||
import { AxiosPromise } from 'axios';
|
||||
|
||||
/**
|
||||
* 学生信息审核查询参数
|
||||
*/
|
||||
export interface XgStudentInfoApprovalQuery {
|
||||
batchId?: number;
|
||||
studentId?: number;
|
||||
studentName?: string;
|
||||
studentNo?: string;
|
||||
classCode?: string;
|
||||
className?: string;
|
||||
collegeCode?: string;
|
||||
collegeName?: string;
|
||||
submissionStatus?: string;
|
||||
approvalStatus?: string;
|
||||
submissionTimeStart?: string;
|
||||
submissionTimeEnd?: string;
|
||||
approvalTimeStart?: string;
|
||||
approvalTimeEnd?: string;
|
||||
approvalBy?: number;
|
||||
approvalByName?: string;
|
||||
keyword?: string;
|
||||
pageNum?: number;
|
||||
pageSize?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 学生信息审核视图对象
|
||||
*/
|
||||
export interface XgStudentInfoApprovalVO {
|
||||
id?: number;
|
||||
batchId?: number;
|
||||
batchName?: string;
|
||||
studentId?: number;
|
||||
studentName?: string;
|
||||
studentNo?: string;
|
||||
classCode?: string;
|
||||
className?: string;
|
||||
collegeCode?: string;
|
||||
collegeName?: string;
|
||||
majorCode?: string;
|
||||
majorName?: string;
|
||||
submissionStatus?: string;
|
||||
approvalStatus?: string;
|
||||
submissionTime?: string;
|
||||
approvalTime?: string;
|
||||
approvalBy?: number;
|
||||
approvalByName?: string;
|
||||
approvalComments?: string;
|
||||
phone?: string;
|
||||
idCard?: string;
|
||||
createTime?: string;
|
||||
updateTime?: string;
|
||||
totalCount?: number;
|
||||
pendingCount?: number;
|
||||
approvedCount?: number;
|
||||
rejectedCount?: number;
|
||||
submittedCount?: number;
|
||||
draftCount?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询待审核的学生信息列表
|
||||
*/
|
||||
export const queryPendingApprovalList = (query: XgStudentInfoApprovalQuery): AxiosPromise<XgStudentInfoApprovalVO[]> => {
|
||||
return request({
|
||||
url: '/xg/studentInfoApproval/pending',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 查询已审核的学生信息列表
|
||||
*/
|
||||
export const queryApprovedList = (query: XgStudentInfoApprovalQuery): AxiosPromise<XgStudentInfoApprovalVO[]> => {
|
||||
return request({
|
||||
url: '/xg/studentInfoApproval/approved',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 审核学生信息
|
||||
*/
|
||||
export const approveStudentInfo = (studentId: number, batchId: number, approved: boolean, comments?: string) => {
|
||||
return request({
|
||||
url: '/xg/studentInfoApproval/approve',
|
||||
method: 'post',
|
||||
params: {
|
||||
studentId,
|
||||
batchId,
|
||||
approved,
|
||||
comments
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 批量审核学生信息
|
||||
*/
|
||||
export const batchApproveStudentInfo = (studentIds: number[], batchId: number, approved: boolean, comments?: string) => {
|
||||
return request({
|
||||
url: '/xg/studentInfoApproval/batchApprove',
|
||||
method: 'post',
|
||||
data: studentIds,
|
||||
params: {
|
||||
batchId,
|
||||
approved,
|
||||
comments
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 撤销审核
|
||||
*/
|
||||
export const revokeApproval = (studentId: number, batchId: number, reason?: string) => {
|
||||
return request({
|
||||
url: '/xg/studentInfoApproval/revoke',
|
||||
method: 'post',
|
||||
params: {
|
||||
studentId,
|
||||
batchId,
|
||||
reason
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 查询学生信息审核历史
|
||||
*/
|
||||
export const queryApprovalHistory = (studentId: number, batchId: number): AxiosPromise<XgStudentInfoApprovalVO[]> => {
|
||||
return request({
|
||||
url: '/xg/studentInfoApproval/history',
|
||||
method: 'get',
|
||||
params: {
|
||||
studentId,
|
||||
batchId
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取审核统计信息
|
||||
*/
|
||||
export const getApprovalStatistics = (batchId: number): AxiosPromise<XgStudentInfoApprovalVO> => {
|
||||
return request({
|
||||
url: '/xg/studentInfoApproval/statistics',
|
||||
method: 'get',
|
||||
params: {
|
||||
batchId
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 检查审核权限
|
||||
*/
|
||||
export const checkApprovalPermission = (batchId: number): AxiosPromise<boolean> => {
|
||||
return request({
|
||||
url: '/xg/studentInfoApproval/checkPermission',
|
||||
method: 'get',
|
||||
params: {
|
||||
batchId
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 导出审核结果
|
||||
*/
|
||||
export const exportApprovalResults = (query: XgStudentInfoApprovalQuery) => {
|
||||
return request({
|
||||
url: '/xg/studentInfoApproval/export',
|
||||
method: 'post',
|
||||
data: query
|
||||
});
|
||||
};
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,306 +0,0 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-form :model="queryParams" ref="queryFormRef" :inline="true" v-show="showSearch" label-width="68px">
|
||||
<el-form-item label="学年编码" prop="yearCode">
|
||||
<el-input v-model="queryParams.yearCode" placeholder="请输入学年编码" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="学年名称" prop="yearName">
|
||||
<el-input v-model="queryParams.yearName" placeholder="请输入学年名称" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="学年状态" prop="yearStatus">
|
||||
<el-select v-model="queryParams.yearStatus" placeholder="请选择学年状态" clearable>
|
||||
<el-option label="启用" value="1" />
|
||||
<el-option label="停用" value="0" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="是否当前" prop="isCurrent">
|
||||
<el-select v-model="queryParams.isCurrent" placeholder="请选择是否当前学年" clearable>
|
||||
<el-option label="是" :value="true" />
|
||||
<el-option label="否" :value="false" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['xg:academicYear:add']">新增</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['xg:academicYear:edit']">修改</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['xg:academicYear:remove']"
|
||||
>删除</el-button
|
||||
>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['xg:academicYear:export']">导出</el-button>
|
||||
</el-col>
|
||||
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
|
||||
<el-table v-loading="loading" :data="academicYearList" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column label="学年编码" align="center" prop="yearCode" />
|
||||
<el-table-column label="学年名称" align="center" prop="yearName" />
|
||||
<el-table-column label="开始日期" align="center" prop="startDate" width="180">
|
||||
<template #default="scope">
|
||||
<span>{{ parseTime(scope.row.startDate, '{y}-{m}-{d}') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="结束日期" align="center" prop="endDate" width="180">
|
||||
<template #default="scope">
|
||||
<span>{{ parseTime(scope.row.endDate, '{y}-{m}-{d}') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="是否当前" align="center" prop="isCurrent">
|
||||
<template #default="scope">
|
||||
<dict-tag :options="sys_yes_no" :value="scope.row.isCurrent" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="学年状态" align="center" prop="yearStatus">
|
||||
<template #default="scope">
|
||||
<dict-tag :options="sys_normal_disable" :value="scope.row.yearStatus" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="排序" align="center" prop="sortOrder" />
|
||||
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
|
||||
<template #default="scope">
|
||||
<span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d}') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template #default="scope">
|
||||
<el-tooltip content="修改" placement="top">
|
||||
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['xg:academicYear:edit']"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="删除" placement="top">
|
||||
<el-button link type="danger" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['xg:academicYear:remove']"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="设为当前" placement="top" v-if="!scope.row.isCurrent">
|
||||
<el-button link type="success" icon="Check" @click="handleSetCurrent(scope.row)" v-hasPermi="['xg:academicYear:edit']"></el-button>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
|
||||
|
||||
<!-- 添加或修改学年管理对话框 -->
|
||||
<el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
|
||||
<el-form ref="academicYearFormRef" :model="form" :rules="rules" label-width="80px">
|
||||
<el-form-item label="学年编码" prop="yearCode">
|
||||
<el-input v-model="form.yearCode" placeholder="请输入学年编码" />
|
||||
</el-form-item>
|
||||
<el-form-item label="学年名称" prop="yearName">
|
||||
<el-input v-model="form.yearName" placeholder="请输入学年名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="开始日期" prop="startDate">
|
||||
<el-date-picker clearable v-model="form.startDate" type="date" value-format="YYYY-MM-DD" placeholder="请选择开始日期" />
|
||||
</el-form-item>
|
||||
<el-form-item label="结束日期" prop="endDate">
|
||||
<el-date-picker clearable v-model="form.endDate" type="date" value-format="YYYY-MM-DD" placeholder="请选择结束日期" />
|
||||
</el-form-item>
|
||||
<el-form-item label="是否当前" prop="isCurrent">
|
||||
<el-radio-group v-model="form.isCurrent">
|
||||
<el-radio :value="true">是</el-radio>
|
||||
<el-radio :value="false">否</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="学年状态" prop="yearStatus">
|
||||
<el-radio-group v-model="form.yearStatus">
|
||||
<el-radio value="1">启用</el-radio>
|
||||
<el-radio value="0">停用</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="排序序号" prop="sortOrder">
|
||||
<el-input-number v-model="form.sortOrder" :min="0" />
|
||||
</el-form-item>
|
||||
<el-form-item label="学年描述" prop="description">
|
||||
<el-input v-model="form.description" type="textarea" placeholder="请输入学年描述" />
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="form.remark" type="textarea" placeholder="请输入备注" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="cancel">取 消</el-button>
|
||||
<el-button type="primary" @click="submitForm">确 定</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="AcademicYear" lang="ts">
|
||||
import {
|
||||
listAcademicYear,
|
||||
getAcademicYear,
|
||||
delAcademicYear,
|
||||
addAcademicYear,
|
||||
updateAcademicYear,
|
||||
setCurrentAcademicYear,
|
||||
exportAcademicYear
|
||||
} from '@/api/xg-xx/academicYear';
|
||||
import { AcademicYearVO, AcademicYearQuery, AcademicYearForm } from '@/api/xg-xx/academicTypes';
|
||||
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
const { sys_yes_no, sys_normal_disable } = proxy?.useDict('sys_yes_no', 'sys_normal_disable');
|
||||
|
||||
const academicYearList = ref<AcademicYearVO[]>([]);
|
||||
const loading = ref(true);
|
||||
const showSearch = ref(true);
|
||||
const ids = ref<Array<string | number>>([]);
|
||||
const single = ref(true);
|
||||
const multiple = ref(true);
|
||||
const total = ref(0);
|
||||
|
||||
const queryFormRef = ref<ElFormInstance>();
|
||||
const academicYearFormRef = ref<ElFormInstance>();
|
||||
|
||||
const dialog = reactive<DialogOption>({
|
||||
visible: false,
|
||||
title: ''
|
||||
});
|
||||
|
||||
const initFormData: AcademicYearForm = {
|
||||
id: undefined,
|
||||
yearCode: '',
|
||||
yearName: '',
|
||||
startDate: '',
|
||||
endDate: '',
|
||||
isCurrent: false,
|
||||
yearStatus: '1',
|
||||
sortOrder: 0,
|
||||
description: '',
|
||||
remark: ''
|
||||
};
|
||||
|
||||
const data = reactive<PageData<AcademicYearForm, AcademicYearQuery>>({
|
||||
form: { ...initFormData },
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
yearCode: '',
|
||||
yearName: '',
|
||||
yearStatus: '',
|
||||
isCurrent: undefined,
|
||||
params: {}
|
||||
},
|
||||
rules: {
|
||||
yearCode: [{ required: true, message: '学年编码不能为空', trigger: 'blur' }],
|
||||
yearName: [{ required: true, message: '学年名称不能为空', trigger: 'blur' }],
|
||||
startDate: [{ required: true, message: '开始日期不能为空', trigger: 'blur' }],
|
||||
endDate: [{ required: true, message: '结束日期不能为空', trigger: 'blur' }],
|
||||
yearStatus: [{ required: true, message: '学年状态不能为空', trigger: 'change' }],
|
||||
sortOrder: [{ required: true, message: '排序序号不能为空', trigger: 'blur' }]
|
||||
}
|
||||
});
|
||||
|
||||
const { queryParams, form, rules } = toRefs(data);
|
||||
|
||||
/** 查询学年管理列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true;
|
||||
const res = await listAcademicYear(queryParams.value);
|
||||
academicYearList.value = res.rows;
|
||||
total.value = res.total;
|
||||
loading.value = false;
|
||||
};
|
||||
|
||||
/** 取消按钮 */
|
||||
const cancel = () => {
|
||||
reset();
|
||||
dialog.visible = false;
|
||||
};
|
||||
|
||||
/** 表单重置 */
|
||||
const reset = () => {
|
||||
form.value = { ...initFormData };
|
||||
academicYearFormRef.value?.resetFields();
|
||||
};
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.value.pageNum = 1;
|
||||
getList();
|
||||
};
|
||||
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value?.resetFields();
|
||||
handleQuery();
|
||||
};
|
||||
|
||||
/** 多选框选中数据 */
|
||||
const handleSelectionChange = (selection: AcademicYearVO[]) => {
|
||||
ids.value = selection.map((item) => item.id);
|
||||
single.value = selection.length !== 1;
|
||||
multiple.value = !selection.length;
|
||||
};
|
||||
|
||||
/** 新增按钮操作 */
|
||||
const handleAdd = () => {
|
||||
reset();
|
||||
dialog.visible = true;
|
||||
dialog.title = '添加学年管理';
|
||||
};
|
||||
|
||||
/** 修改按钮操作 */
|
||||
const handleUpdate = async (row?: AcademicYearVO) => {
|
||||
reset();
|
||||
const _id = row?.id || ids.value[0];
|
||||
const res = await getAcademicYear(_id);
|
||||
Object.assign(form.value, res.data);
|
||||
dialog.visible = true;
|
||||
dialog.title = '修改学年管理';
|
||||
};
|
||||
|
||||
/** 提交按钮 */
|
||||
const submitForm = () => {
|
||||
academicYearFormRef.value?.validate(async (valid: boolean) => {
|
||||
if (valid) {
|
||||
if (form.value.id) {
|
||||
await updateAcademicYear(form.value);
|
||||
proxy?.$modal.msgSuccess('修改成功');
|
||||
} else {
|
||||
await addAcademicYear(form.value);
|
||||
proxy?.$modal.msgSuccess('新增成功');
|
||||
}
|
||||
dialog.visible = false;
|
||||
await getList();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = async (row?: AcademicYearVO) => {
|
||||
const _ids = row?.id || ids.value;
|
||||
await proxy?.$modal.confirm('是否确认删除学年管理编号为"' + _ids + '"的数据项?');
|
||||
await delAcademicYear(_ids);
|
||||
proxy?.$modal.msgSuccess('删除成功');
|
||||
await getList();
|
||||
};
|
||||
|
||||
/** 设为当前学年 */
|
||||
const handleSetCurrent = async (row: AcademicYearVO) => {
|
||||
await proxy?.$modal.confirm('是否确认设置"' + row.yearName + '"为当前学年?');
|
||||
await setCurrentAcademicYear(row.id);
|
||||
proxy?.$modal.msgSuccess('设置成功');
|
||||
await getList();
|
||||
};
|
||||
|
||||
/** 导出按钮操作 */
|
||||
const handleExport = () => {
|
||||
proxy?.$download.excel(exportAcademicYear, queryParams.value, `学年管理_${new Date().getTime()}.xlsx`);
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getList();
|
||||
});
|
||||
</script>
|
||||
@@ -1,697 +0,0 @@
|
||||
<template>
|
||||
<div class="p-2 page-container">
|
||||
<!-- 搜索区域 -->
|
||||
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
|
||||
<div v-show="showSearch" class="mb-[10px]">
|
||||
<el-card shadow="hover">
|
||||
<el-form ref="queryFormRef" :model="queryParams" label-width="100px">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="6">
|
||||
<el-form-item label="批次选择" prop="batchId">
|
||||
<el-select v-model="queryParams.batchId" placeholder="请选择批次" clearable style="width: 100%">
|
||||
<el-option v-for="batch in batchOptions" :key="batch.id" :label="batch.batchName" :value="batch.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="期间名称" prop="periodName">
|
||||
<el-input v-model="queryParams.periodName" placeholder="请输入期间名称" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="期间类型" prop="periodType">
|
||||
<el-select v-model="queryParams.periodType" placeholder="请选择期间类型" clearable style="width: 100%">
|
||||
<el-option label="学生填报" value="STUDENT_SUBMIT" />
|
||||
<el-option label="辅导员审核" value="COUNSELOR_REVIEW" />
|
||||
<el-option label="学院审核" value="COLLEGE_REVIEW" />
|
||||
<el-option label="学校审核" value="SCHOOL_REVIEW" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="目标角色" prop="targetRole">
|
||||
<el-select v-model="queryParams.targetRole" placeholder="请选择目标角色" clearable style="width: 100%">
|
||||
<el-option label="学生" value="STUDENT" />
|
||||
<el-option label="辅导员" value="COUNSELOR" />
|
||||
<el-option label="班主任" value="CLASS_TEACHER" />
|
||||
<el-option label="学院管理员" value="COLLEGE_ADMIN" />
|
||||
<el-option label="学校管理员" value="SCHOOL_ADMIN" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="6">
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-select v-model="queryParams.status" placeholder="请选择状态" clearable style="width: 100%">
|
||||
<el-option label="未开始" value="PENDING" />
|
||||
<el-option label="进行中" value="ACTIVE" />
|
||||
<el-option label="已结束" value="CLOSED" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="启用状态" prop="enabled">
|
||||
<el-select v-model="queryParams.enabled" placeholder="请选择启用状态" clearable style="width: 100%">
|
||||
<el-option label="启用" :value="true" />
|
||||
<el-option label="禁用" :value="false" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</el-card>
|
||||
</div>
|
||||
</transition>
|
||||
|
||||
<!-- 主要内容区域 -->
|
||||
<el-card shadow="hover">
|
||||
<!-- 操作按钮区域 -->
|
||||
<div class="toolbar-wrapper">
|
||||
<div class="toolbar-left">
|
||||
<el-button type="primary" icon="Plus" @click="handleAdd" v-hasPermi="['xg:businessPeriod:add']">
|
||||
新增期间
|
||||
</el-button>
|
||||
<el-button type="success" icon="Edit" :disabled="single" @click="handleUpdate" v-hasPermi="['xg:businessPeriod:edit']">
|
||||
修改
|
||||
</el-button>
|
||||
<el-button type="danger" icon="Delete" :disabled="multiple" @click="handleDelete" v-hasPermi="['xg:businessPeriod:remove']">
|
||||
删除
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<div class="toolbar-right">
|
||||
<el-button type="warning" icon="Download" @click="handleExport" v-hasPermi="['xg:businessPeriod:export']">
|
||||
导出
|
||||
</el-button>
|
||||
<el-button type="info" icon="Refresh" @click="handleRefresh">
|
||||
刷新
|
||||
</el-button>
|
||||
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 表格区域 -->
|
||||
<div class="table-wrapper">
|
||||
<el-table
|
||||
v-loading="loading"
|
||||
:data="periodList"
|
||||
@selection-change="handleSelectionChange"
|
||||
@sort-change="handleSortChange"
|
||||
row-key="id"
|
||||
border
|
||||
stripe
|
||||
size="small"
|
||||
>
|
||||
<el-table-column type="selection" width="50" align="center" />
|
||||
<el-table-column label="序号" type="index" width="60" align="center" />
|
||||
<el-table-column label="期间名称" prop="periodName" width="150" align="center" show-overflow-tooltip />
|
||||
<el-table-column label="批次名称" prop="batchName" width="150" align="center" show-overflow-tooltip />
|
||||
<el-table-column label="期间类型" prop="periodType" width="120" align="center">
|
||||
<template #default="scope">
|
||||
<el-tag :type="getPeriodTypeTagType(scope.row.periodType)" size="small">
|
||||
{{ getPeriodTypeText(scope.row.periodType) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="目标角色" prop="targetRole" width="120" align="center">
|
||||
<template #default="scope">
|
||||
<el-tag :type="getTargetRoleTagType(scope.row.targetRole)" size="small">
|
||||
{{ getTargetRoleText(scope.row.targetRole) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="开始时间" prop="startTime" width="160" align="center" sortable="custom">
|
||||
<template #default="scope">
|
||||
<span>{{ proxy.parseTime(scope.row.startTime, '{y}-{m}-{d} {h}:{i}') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="结束时间" prop="endTime" width="160" align="center" sortable="custom">
|
||||
<template #default="scope">
|
||||
<span>{{ proxy.parseTime(scope.row.endTime, '{y}-{m}-{d} {h}:{i}') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" prop="status" width="100" align="center">
|
||||
<template #default="scope">
|
||||
<el-tag :type="getStatusTagType(scope.row.status)" size="small">
|
||||
{{ getStatusText(scope.row.status) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="启用状态" prop="enabled" width="100" align="center">
|
||||
<template #default="scope">
|
||||
<el-switch
|
||||
v-model="scope.row.enabled"
|
||||
@change="handleToggleEnabled(scope.row)"
|
||||
:disabled="!hasPermission(['xg:businessPeriod:edit'])"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="排序" prop="sortOrder" width="80" align="center" />
|
||||
<el-table-column label="描述" prop="description" width="200" align="center" show-overflow-tooltip />
|
||||
<el-table-column label="操作" align="center" width="200" fixed="right" class-name="small-padding fixed-width">
|
||||
<template #default="scope">
|
||||
<div class="action-buttons">
|
||||
<el-tooltip content="查看" placement="top">
|
||||
<el-button link type="info" icon="View" size="small" @click="handleView(scope.row)" />
|
||||
</el-tooltip>
|
||||
<el-tooltip content="修改" placement="top">
|
||||
<el-button link type="primary" icon="Edit" size="small" @click="handleUpdate(scope.row)" v-hasPermi="['xg:businessPeriod:edit']" />
|
||||
</el-tooltip>
|
||||
<el-tooltip content="删除" placement="top">
|
||||
<el-button link type="danger" icon="Delete" size="small" @click="handleDelete(scope.row)" v-hasPermi="['xg:businessPeriod:remove']" />
|
||||
</el-tooltip>
|
||||
<template v-if="scope.row.status === 'PENDING'">
|
||||
<el-tooltip content="开启" placement="top">
|
||||
<el-button link type="success" icon="VideoPlay" size="small" @click="handleOpen(scope.row)" v-hasPermi="['xg:businessPeriod:manage']" />
|
||||
</el-tooltip>
|
||||
</template>
|
||||
<template v-else-if="scope.row.status === 'ACTIVE'">
|
||||
<el-tooltip content="关闭" placement="top">
|
||||
<el-button link type="warning" icon="VideoPause" size="small" @click="handleClose(scope.row)" v-hasPermi="['xg:businessPeriod:manage']" />
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<div class="pagination-container">
|
||||
<pagination v-show="total > 0" :total="total" :page="queryParams.pageNum" :limit="queryParams.pageSize" @pagination="getList" />
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<!-- 新增/修改对话框 -->
|
||||
<el-dialog :title="dialog.title" v-model="dialog.visible" width="800px" append-to-body>
|
||||
<el-form ref="periodFormRef" :model="form" :rules="rules" label-width="120px">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="批次选择" prop="batchId">
|
||||
<el-select v-model="form.batchId" placeholder="请选择批次" style="width: 100%">
|
||||
<el-option v-for="batch in batchOptions" :key="batch.id" :label="batch.batchName" :value="batch.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="期间名称" prop="periodName">
|
||||
<el-input v-model="form.periodName" placeholder="请输入期间名称" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="期间类型" prop="periodType">
|
||||
<el-select v-model="form.periodType" placeholder="请选择期间类型" style="width: 100%">
|
||||
<el-option label="学生填报" value="STUDENT_SUBMIT" />
|
||||
<el-option label="辅导员审核" value="COUNSELOR_REVIEW" />
|
||||
<el-option label="学院审核" value="COLLEGE_REVIEW" />
|
||||
<el-option label="学校审核" value="SCHOOL_REVIEW" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="目标角色" prop="targetRole">
|
||||
<el-select v-model="form.targetRole" placeholder="请选择目标角色" style="width: 100%">
|
||||
<el-option label="学生" value="STUDENT" />
|
||||
<el-option label="辅导员" value="COUNSELOR" />
|
||||
<el-option label="班主任" value="CLASS_TEACHER" />
|
||||
<el-option label="学院管理员" value="COLLEGE_ADMIN" />
|
||||
<el-option label="学校管理员" value="SCHOOL_ADMIN" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="开始时间" prop="startTime">
|
||||
<el-date-picker
|
||||
v-model="form.startTime"
|
||||
type="datetime"
|
||||
placeholder="请选择开始时间"
|
||||
style="width: 100%"
|
||||
format="YYYY-MM-DD HH:mm:ss"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="结束时间" prop="endTime">
|
||||
<el-date-picker
|
||||
v-model="form.endTime"
|
||||
type="datetime"
|
||||
placeholder="请选择结束时间"
|
||||
style="width: 100%"
|
||||
format="YYYY-MM-DD HH:mm:ss"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="排序" prop="sortOrder">
|
||||
<el-input-number v-model="form.sortOrder" :min="0" :max="9999" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="启用状态" prop="enabled">
|
||||
<el-switch v-model="form.enabled" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="描述" prop="description">
|
||||
<el-input
|
||||
v-model="form.description"
|
||||
type="textarea"
|
||||
:rows="3"
|
||||
placeholder="请输入期间描述"
|
||||
maxlength="500"
|
||||
show-word-limit
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="cancel">取消</el-button>
|
||||
<el-button type="primary" @click="submitForm">确定</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, onMounted, getCurrentInstance } from 'vue';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import {
|
||||
listBusinessPeriod,
|
||||
getBusinessPeriod,
|
||||
addBusinessPeriod,
|
||||
updateBusinessPeriod,
|
||||
delBusinessPeriod,
|
||||
openPeriod,
|
||||
closePeriod,
|
||||
checkPeriodTimeConflict,
|
||||
exportBusinessPeriod,
|
||||
type XgBusinessPeriodQuery,
|
||||
type XgBusinessPeriodForm,
|
||||
type XgBusinessPeriodVO
|
||||
} from '@/api/xg-xx/businessPeriod';
|
||||
import { listAllCollectionBatch } from '@/api/xg-xx/collectionBatch';
|
||||
|
||||
const { proxy } = getCurrentInstance() as any;
|
||||
|
||||
// 响应式数据
|
||||
const loading = ref(false);
|
||||
const showSearch = ref(true);
|
||||
const ids = ref<number[]>([]);
|
||||
const single = ref(true);
|
||||
const multiple = ref(true);
|
||||
const total = ref(0);
|
||||
const periodList = ref<XgBusinessPeriodVO[]>([]);
|
||||
const batchOptions = ref<any[]>([]);
|
||||
|
||||
// 查询参数
|
||||
const queryParams = reactive<XgBusinessPeriodQuery>({
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
batchId: undefined,
|
||||
periodName: undefined,
|
||||
periodType: undefined,
|
||||
targetRole: undefined,
|
||||
status: undefined,
|
||||
enabled: undefined
|
||||
});
|
||||
|
||||
// 对话框
|
||||
const dialog = reactive({
|
||||
visible: false,
|
||||
title: '',
|
||||
type: 'add' as 'add' | 'edit'
|
||||
});
|
||||
|
||||
// 表单数据
|
||||
const form = reactive<XgBusinessPeriodForm>({
|
||||
id: undefined,
|
||||
batchId: undefined,
|
||||
periodName: '',
|
||||
periodType: '',
|
||||
targetRole: '',
|
||||
startTime: '',
|
||||
endTime: '',
|
||||
status: 'PENDING',
|
||||
description: '',
|
||||
sortOrder: 0,
|
||||
enabled: true
|
||||
});
|
||||
|
||||
// 表单校验规则
|
||||
const rules = {
|
||||
batchId: [{ required: true, message: '请选择批次', trigger: 'change' }],
|
||||
periodName: [{ required: true, message: '请输入期间名称', trigger: 'blur' }],
|
||||
periodType: [{ required: true, message: '请选择期间类型', trigger: 'change' }],
|
||||
targetRole: [{ required: true, message: '请选择目标角色', trigger: 'change' }],
|
||||
startTime: [{ required: true, message: '请选择开始时间', trigger: 'change' }],
|
||||
endTime: [{ required: true, message: '请选择结束时间', trigger: 'change' }]
|
||||
};
|
||||
|
||||
// 获取列表数据
|
||||
const getList = async () => {
|
||||
loading.value = true;
|
||||
try {
|
||||
const response = await listBusinessPeriod(queryParams);
|
||||
periodList.value = response.rows;
|
||||
total.value = response.total;
|
||||
} catch (error) {
|
||||
console.error('获取列表失败:', error);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 获取批次选项
|
||||
const getBatchOptions = async () => {
|
||||
try {
|
||||
const response = await listAllCollectionBatch({});
|
||||
batchOptions.value = response.data;
|
||||
} catch (error) {
|
||||
console.error('获取批次选项失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 搜索
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNum = 1;
|
||||
getList();
|
||||
};
|
||||
|
||||
// 重置搜索
|
||||
const resetQuery = () => {
|
||||
proxy.resetForm('queryFormRef');
|
||||
handleQuery();
|
||||
};
|
||||
|
||||
// 多选框选中数据
|
||||
const handleSelectionChange = (selection: XgBusinessPeriodVO[]) => {
|
||||
ids.value = selection.map(item => item.id!);
|
||||
single.value = selection.length !== 1;
|
||||
multiple.value = !selection.length;
|
||||
};
|
||||
|
||||
// 排序变化
|
||||
const handleSortChange = (column: any) => {
|
||||
// 处理排序逻辑
|
||||
getList();
|
||||
};
|
||||
|
||||
// 新增
|
||||
const handleAdd = () => {
|
||||
reset();
|
||||
dialog.visible = true;
|
||||
dialog.title = '新增业务期间';
|
||||
dialog.type = 'add';
|
||||
};
|
||||
|
||||
// 修改
|
||||
const handleUpdate = async (row?: XgBusinessPeriodVO) => {
|
||||
reset();
|
||||
const id = row?.id || ids.value[0];
|
||||
|
||||
try {
|
||||
const response = await getBusinessPeriod(id);
|
||||
Object.assign(form, response.data);
|
||||
dialog.visible = true;
|
||||
dialog.title = '修改业务期间';
|
||||
dialog.type = 'edit';
|
||||
} catch (error) {
|
||||
console.error('获取详情失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 查看
|
||||
const handleView = (row: XgBusinessPeriodVO) => {
|
||||
// 实现查看逻辑
|
||||
ElMessage.info('查看功能待实现');
|
||||
};
|
||||
|
||||
// 删除
|
||||
const handleDelete = async (row?: XgBusinessPeriodVO) => {
|
||||
const periodIds = row?.id ? [row.id] : ids.value;
|
||||
|
||||
try {
|
||||
await ElMessageBox.confirm('确定要删除选中的业务期间吗?', '系统提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
});
|
||||
|
||||
await delBusinessPeriod(periodIds);
|
||||
ElMessage.success('删除成功');
|
||||
getList();
|
||||
} catch (error) {
|
||||
console.error('删除失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 开启期间
|
||||
const handleOpen = async (row: XgBusinessPeriodVO) => {
|
||||
try {
|
||||
await ElMessageBox.confirm(`确定要开启期间"${row.periodName}"吗?`, '系统提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
});
|
||||
|
||||
await openPeriod(row.id!);
|
||||
ElMessage.success('开启成功');
|
||||
getList();
|
||||
} catch (error) {
|
||||
console.error('开启失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 关闭期间
|
||||
const handleClose = async (row: XgBusinessPeriodVO) => {
|
||||
try {
|
||||
await ElMessageBox.confirm(`确定要关闭期间"${row.periodName}"吗?`, '系统提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
});
|
||||
|
||||
await closePeriod(row.id!);
|
||||
ElMessage.success('关闭成功');
|
||||
getList();
|
||||
} catch (error) {
|
||||
console.error('关闭失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 切换启用状态
|
||||
const handleToggleEnabled = async (row: XgBusinessPeriodVO) => {
|
||||
try {
|
||||
await updateBusinessPeriod({
|
||||
id: row.id,
|
||||
enabled: row.enabled
|
||||
});
|
||||
ElMessage.success('状态更新成功');
|
||||
} catch (error) {
|
||||
console.error('状态更新失败:', error);
|
||||
// 恢复原状态
|
||||
row.enabled = !row.enabled;
|
||||
}
|
||||
};
|
||||
|
||||
// 提交表单
|
||||
const submitForm = async () => {
|
||||
try {
|
||||
// 时间冲突检查
|
||||
const conflictResponse = await checkPeriodTimeConflict(form);
|
||||
if (conflictResponse.data) {
|
||||
ElMessage.error('时间范围与其他期间冲突,请重新选择');
|
||||
return;
|
||||
}
|
||||
|
||||
if (dialog.type === 'add') {
|
||||
await addBusinessPeriod(form);
|
||||
ElMessage.success('新增成功');
|
||||
} else {
|
||||
await updateBusinessPeriod(form);
|
||||
ElMessage.success('修改成功');
|
||||
}
|
||||
|
||||
dialog.visible = false;
|
||||
getList();
|
||||
} catch (error) {
|
||||
console.error('提交失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 取消
|
||||
const cancel = () => {
|
||||
dialog.visible = false;
|
||||
reset();
|
||||
};
|
||||
|
||||
// 重置表单
|
||||
const reset = () => {
|
||||
Object.assign(form, {
|
||||
id: undefined,
|
||||
batchId: undefined,
|
||||
periodName: '',
|
||||
periodType: '',
|
||||
targetRole: '',
|
||||
startTime: '',
|
||||
endTime: '',
|
||||
status: 'PENDING',
|
||||
description: '',
|
||||
sortOrder: 0,
|
||||
enabled: true
|
||||
});
|
||||
proxy.resetForm('periodFormRef');
|
||||
};
|
||||
|
||||
// 导出
|
||||
const handleExport = () => {
|
||||
proxy?.download(
|
||||
'xg/businessPeriod/export',
|
||||
queryParams,
|
||||
`业务期间_${proxy.parseTime(new Date(), '{y}{m}{d}{h}{i}{s}')}.xlsx`
|
||||
);
|
||||
};
|
||||
|
||||
// 刷新
|
||||
const handleRefresh = () => {
|
||||
getList();
|
||||
};
|
||||
|
||||
// 权限检查
|
||||
const hasPermission = (permissions: string[]) => {
|
||||
return proxy.hasPermi(permissions);
|
||||
};
|
||||
|
||||
// 获取期间类型标签类型
|
||||
const getPeriodTypeTagType = (type: string) => {
|
||||
const typeMap: Record<string, string> = {
|
||||
'STUDENT_SUBMIT': 'primary',
|
||||
'COUNSELOR_REVIEW': 'success',
|
||||
'COLLEGE_REVIEW': 'warning',
|
||||
'SCHOOL_REVIEW': 'danger'
|
||||
};
|
||||
return typeMap[type] || 'info';
|
||||
};
|
||||
|
||||
// 获取期间类型文本
|
||||
const getPeriodTypeText = (type: string) => {
|
||||
const typeMap: Record<string, string> = {
|
||||
'STUDENT_SUBMIT': '学生填报',
|
||||
'COUNSELOR_REVIEW': '辅导员审核',
|
||||
'COLLEGE_REVIEW': '学院审核',
|
||||
'SCHOOL_REVIEW': '学校审核'
|
||||
};
|
||||
return typeMap[type] || '未知';
|
||||
};
|
||||
|
||||
// 获取目标角色标签类型
|
||||
const getTargetRoleTagType = (role: string) => {
|
||||
const roleMap: Record<string, string> = {
|
||||
'STUDENT': 'primary',
|
||||
'COUNSELOR': 'success',
|
||||
'CLASS_TEACHER': 'warning',
|
||||
'COLLEGE_ADMIN': 'danger',
|
||||
'SCHOOL_ADMIN': 'info'
|
||||
};
|
||||
return roleMap[role] || 'info';
|
||||
};
|
||||
|
||||
// 获取目标角色文本
|
||||
const getTargetRoleText = (role: string) => {
|
||||
const roleMap: Record<string, string> = {
|
||||
'STUDENT': '学生',
|
||||
'COUNSELOR': '辅导员',
|
||||
'CLASS_TEACHER': '班主任',
|
||||
'COLLEGE_ADMIN': '学院管理员',
|
||||
'SCHOOL_ADMIN': '学校管理员'
|
||||
};
|
||||
return roleMap[role] || '未知';
|
||||
};
|
||||
|
||||
// 获取状态标签类型
|
||||
const getStatusTagType = (status: string) => {
|
||||
const statusMap: Record<string, string> = {
|
||||
'PENDING': 'info',
|
||||
'ACTIVE': 'success',
|
||||
'CLOSED': 'warning'
|
||||
};
|
||||
return statusMap[status] || 'info';
|
||||
};
|
||||
|
||||
// 获取状态文本
|
||||
const getStatusText = (status: string) => {
|
||||
const statusMap: Record<string, string> = {
|
||||
'PENDING': '未开始',
|
||||
'ACTIVE': '进行中',
|
||||
'CLOSED': '已结束'
|
||||
};
|
||||
return statusMap[status] || '未知';
|
||||
};
|
||||
|
||||
// 初始化
|
||||
onMounted(() => {
|
||||
getBatchOptions();
|
||||
getList();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.page-container {
|
||||
background-color: #f5f5f5;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.toolbar-wrapper {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.toolbar-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.toolbar-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.table-wrapper {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.pagination-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-top: 16px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,276 +0,0 @@
|
||||
<template>
|
||||
<el-dialog :title="dialogTitle" v-model="dialogVisible" width="600px" append-to-body @close="handleClose">
|
||||
<el-form ref="formRef" :model="form" :rules="rules" label-width="120px">
|
||||
<el-form-item label="学生信息">
|
||||
<el-input :value="`${cadreData.studentNo} - ${cadreData.studentName}`" disabled />
|
||||
</el-form-item>
|
||||
<el-form-item label="当前职务">
|
||||
<el-input :value="cadreData.positionName" disabled />
|
||||
</el-form-item>
|
||||
|
||||
<!-- 调任操作专用字段 -->
|
||||
<template v-if="operationType === 'transfer'">
|
||||
<el-form-item label="新职务" prop="newPositionCode">
|
||||
<el-select v-model="form.newPositionCode" placeholder="请选择新职务" style="width: 100%" @change="handlePositionChange">
|
||||
<el-option v-for="item in positionOptions" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="生效日期" prop="effectiveDate">
|
||||
<el-date-picker v-model="form.effectiveDate" type="date" placeholder="选择生效日期" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</template>
|
||||
|
||||
<!-- 续任操作专用字段 -->
|
||||
<template v-if="operationType === 'renew'">
|
||||
<el-form-item label="新任期时长" prop="newTermDuration">
|
||||
<el-input-number v-model="form.newTermDuration" :min="1" :max="48" placeholder="请输入新任期时长(月)" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</template>
|
||||
|
||||
<!-- 重新任命操作专用字段 -->
|
||||
<template v-if="operationType === 'reappoint'">
|
||||
<el-form-item label="新职务" prop="newPositionCode">
|
||||
<el-select v-model="form.newPositionCode" placeholder="请选择新职务" style="width: 100%" @change="handlePositionChange">
|
||||
<el-option v-for="item in positionOptions" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="任职日期" prop="appointmentDate">
|
||||
<el-date-picker v-model="form.appointmentDate" type="date" placeholder="选择任职日期" style="width: 100%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="任期开始日期" prop="termStartDate">
|
||||
<el-date-picker v-model="form.termStartDate" type="date" placeholder="选择任期开始日期" style="width: 100%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="任期时长(月)" prop="termDuration">
|
||||
<el-input-number v-model="form.termDuration" :min="1" :max="48" placeholder="请输入任期时长" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</template>
|
||||
|
||||
<el-form-item label="操作日期" prop="operationDate">
|
||||
<el-date-picker v-model="form.operationDate" type="date" placeholder="选择操作日期" style="width: 100%" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="reasonLabel" prop="reason">
|
||||
<el-input v-model="form.reason" type="textarea" :rows="3" :placeholder="reasonPlaceholder" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="操作备注" prop="operationRemark">
|
||||
<el-input v-model="form.operationRemark" type="textarea" :rows="2" placeholder="请输入操作备注" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="handleClose">取 消</el-button>
|
||||
<el-button type="primary" @click="handleConfirm">确 定</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, computed, watch } from 'vue';
|
||||
import type { FormInstance, FormRules } from 'element-plus';
|
||||
import type { XgClassCadreVO } from '@/api/xg-xx/types';
|
||||
|
||||
interface Props {
|
||||
visible: boolean;
|
||||
operationType: string;
|
||||
cadreData: XgClassCadreVO;
|
||||
}
|
||||
|
||||
interface Emits {
|
||||
(e: 'update:visible', value: boolean): void;
|
||||
(e: 'confirm', data: any): void;
|
||||
}
|
||||
|
||||
const props = defineProps<Props>();
|
||||
const emit = defineEmits<Emits>();
|
||||
|
||||
const formRef = ref<FormInstance>();
|
||||
|
||||
const form = reactive({
|
||||
operationDate: new Date().toISOString().split('T')[0],
|
||||
reason: '',
|
||||
operationRemark: '',
|
||||
newPositionCode: '',
|
||||
newPositionName: '',
|
||||
newTermDuration: 12,
|
||||
effectiveDate: '',
|
||||
appointmentDate: '',
|
||||
termStartDate: '',
|
||||
termDuration: 12
|
||||
});
|
||||
|
||||
const positionOptions = ref([
|
||||
{ value: 'class_monitor', label: '班长' },
|
||||
{ value: 'deputy_monitor', label: '副班长' },
|
||||
{ value: 'study_committee', label: '学习委员' },
|
||||
{ value: 'organization_committee', label: '组织委员' },
|
||||
{ value: 'publicity_committee', label: '宣传委员' },
|
||||
{ value: 'sports_committee', label: '体育委员' },
|
||||
{ value: 'life_committee', label: '生活委员' },
|
||||
{ value: 'literary_committee', label: '文艺委员' },
|
||||
{ value: 'discipline_committee', label: '纪律委员' },
|
||||
{ value: 'psychology_committee', label: '心理委员' }
|
||||
]);
|
||||
|
||||
const dialogVisible = computed({
|
||||
get: () => props.visible,
|
||||
set: (value) => emit('update:visible', value)
|
||||
});
|
||||
|
||||
const dialogTitle = computed(() => {
|
||||
switch (props.operationType) {
|
||||
case 'dismiss':
|
||||
return '免职操作';
|
||||
case 'transfer':
|
||||
return '调任操作';
|
||||
case 'renew':
|
||||
return '续任操作';
|
||||
case 'resign':
|
||||
return '辞职操作';
|
||||
case 'reappoint':
|
||||
return '重新任命';
|
||||
default:
|
||||
return '操作';
|
||||
}
|
||||
});
|
||||
|
||||
const reasonLabel = computed(() => {
|
||||
switch (props.operationType) {
|
||||
case 'dismiss':
|
||||
return '免职原因';
|
||||
case 'transfer':
|
||||
return '调任原因';
|
||||
case 'renew':
|
||||
return '续任原因';
|
||||
case 'resign':
|
||||
return '辞职原因';
|
||||
case 'reappoint':
|
||||
return '任命原因';
|
||||
default:
|
||||
return '操作原因';
|
||||
}
|
||||
});
|
||||
|
||||
const reasonPlaceholder = computed(() => {
|
||||
switch (props.operationType) {
|
||||
case 'dismiss':
|
||||
return '请输入免职原因';
|
||||
case 'transfer':
|
||||
return '请输入调任原因';
|
||||
case 'renew':
|
||||
return '请输入续任原因';
|
||||
case 'resign':
|
||||
return '请输入辞职原因';
|
||||
case 'reappoint':
|
||||
return '请输入任命原因';
|
||||
default:
|
||||
return '请输入操作原因';
|
||||
}
|
||||
});
|
||||
|
||||
const rules = computed<FormRules>(() => {
|
||||
const baseRules = {
|
||||
operationDate: [{ required: true, message: '操作日期不能为空', trigger: 'change' }],
|
||||
reason: [{ required: true, message: `${reasonLabel.value}不能为空`, trigger: 'blur' }]
|
||||
};
|
||||
|
||||
if (props.operationType === 'transfer') {
|
||||
baseRules.newPositionCode = [{ required: true, message: '新职务不能为空', trigger: 'change' }];
|
||||
baseRules.effectiveDate = [{ required: true, message: '生效日期不能为空', trigger: 'change' }];
|
||||
}
|
||||
|
||||
if (props.operationType === 'renew') {
|
||||
baseRules.newTermDuration = [{ required: true, message: '新任期时长不能为空', trigger: 'blur' }];
|
||||
}
|
||||
|
||||
if (props.operationType === 'reappoint') {
|
||||
baseRules.newPositionCode = [{ required: true, message: '新职务不能为空', trigger: 'change' }];
|
||||
baseRules.appointmentDate = [{ required: true, message: '任职日期不能为空', trigger: 'change' }];
|
||||
baseRules.termStartDate = [{ required: true, message: '任期开始日期不能为空', trigger: 'change' }];
|
||||
baseRules.termDuration = [{ required: true, message: '任期时长不能为空', trigger: 'blur' }];
|
||||
}
|
||||
|
||||
return baseRules;
|
||||
});
|
||||
|
||||
const handlePositionChange = (positionCode: string) => {
|
||||
const position = positionOptions.value.find((item) => item.value === positionCode);
|
||||
if (position) {
|
||||
form.newPositionName = position.label;
|
||||
}
|
||||
};
|
||||
|
||||
const handleConfirm = () => {
|
||||
formRef.value?.validate((valid) => {
|
||||
if (valid) {
|
||||
const operationData = {
|
||||
type: props.operationType,
|
||||
cadreId: props.cadreData.id,
|
||||
operationDate: form.operationDate,
|
||||
reason: form.reason,
|
||||
operationRemark: form.operationRemark
|
||||
};
|
||||
|
||||
if (props.operationType === 'transfer') {
|
||||
operationData.newPositionCode = form.newPositionCode;
|
||||
operationData.newPositionName = form.newPositionName;
|
||||
operationData.effectiveDate = form.effectiveDate;
|
||||
}
|
||||
|
||||
if (props.operationType === 'renew') {
|
||||
operationData.newTermDuration = form.newTermDuration;
|
||||
}
|
||||
|
||||
if (props.operationType === 'reappoint') {
|
||||
operationData.newPositionCode = form.newPositionCode;
|
||||
operationData.newPositionName = form.newPositionName;
|
||||
operationData.appointmentDate = form.appointmentDate;
|
||||
operationData.termStartDate = form.termStartDate;
|
||||
operationData.termDuration = form.termDuration;
|
||||
}
|
||||
|
||||
emit('confirm', operationData);
|
||||
handleClose();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
dialogVisible.value = false;
|
||||
resetForm();
|
||||
};
|
||||
|
||||
const resetForm = () => {
|
||||
Object.assign(form, {
|
||||
operationDate: new Date().toISOString().split('T')[0],
|
||||
reason: '',
|
||||
operationRemark: '',
|
||||
newPositionCode: '',
|
||||
newPositionName: '',
|
||||
newTermDuration: 12,
|
||||
effectiveDate: '',
|
||||
appointmentDate: '',
|
||||
termStartDate: '',
|
||||
termDuration: 12
|
||||
});
|
||||
formRef.value?.resetFields();
|
||||
};
|
||||
|
||||
// 监听对话框打开,重置表单
|
||||
watch(
|
||||
() => props.visible,
|
||||
(newVal) => {
|
||||
if (newVal) {
|
||||
resetForm();
|
||||
}
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.dialog-footer {
|
||||
text-align: right;
|
||||
}
|
||||
</style>
|
||||
@@ -1,771 +0,0 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<!-- 搜索区域 -->
|
||||
<el-form v-show="showSearch" ref="queryFormRef" :model="queryParams" :inline="true" label-width="68px">
|
||||
<el-form-item label="班级" prop="classId">
|
||||
<el-select v-model="queryParams.classId" placeholder="请选择班级" clearable style="width: 200px">
|
||||
<el-option v-for="item in classOptions" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="学号" prop="studentNo">
|
||||
<el-input v-model="queryParams.studentNo" placeholder="请输入学号" clearable style="width: 200px" @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="姓名" prop="studentName">
|
||||
<el-input v-model="queryParams.studentName" placeholder="请输入学生姓名" clearable style="width: 200px" @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="职务" prop="positionCode">
|
||||
<el-select v-model="queryParams.positionCode" placeholder="请选择职务" clearable style="width: 200px">
|
||||
<el-option v-for="item in positionOptions" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="任职状态" prop="cadreStatus">
|
||||
<el-select v-model="queryParams.cadreStatus" placeholder="请选择任职状态" clearable style="width: 200px">
|
||||
<el-option label="在任" value="1" />
|
||||
<el-option label="到期" value="2" />
|
||||
<el-option label="辞职" value="3" />
|
||||
<el-option label="免职" value="4" />
|
||||
<el-option label="调任" value="5" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="是否现任" prop="isCurrent">
|
||||
<el-select v-model="queryParams.isCurrent" placeholder="请选择是否现任" clearable style="width: 200px">
|
||||
<el-option label="是" :value="true" />
|
||||
<el-option label="否" :value="false" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<!-- 工具栏 -->
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['xg:classCadre:add']">新增</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['xg:classCadre:edit']">修改</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['xg:classCadre:remove']"
|
||||
>删除</el-button
|
||||
>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['xg:classCadre:export']">导出</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="info" plain icon="Upload" @click="handleImport" v-hasPermi="['xg:classCadre:import']">导入</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="primary" plain icon="Clock" @click="handleExpiring">到期提醒</el-button>
|
||||
</el-col>
|
||||
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
|
||||
<!-- 表格数据 -->
|
||||
<el-table v-loading="loading" :data="classCadreList" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column label="序号" type="index" width="50" align="center" />
|
||||
<el-table-column label="学号" align="center" prop="studentNo" min-width="120" />
|
||||
<el-table-column label="姓名" align="center" prop="studentName" min-width="100" />
|
||||
<el-table-column label="班级" align="center" prop="className" min-width="150" />
|
||||
<el-table-column label="职务" align="center" prop="positionName" min-width="120">
|
||||
<template #default="scope">
|
||||
<el-tag :type="getPositionTagType(scope.row.positionLevel)">
|
||||
{{ scope.row.positionName }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="任职状态" align="center" prop="cadreStatus" min-width="100">
|
||||
<template #default="scope">
|
||||
<el-tag :type="getCadreStatusTagType(scope.row.cadreStatus)">
|
||||
{{ getCadreStatusText(scope.row.cadreStatus) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="是否现任" align="center" prop="isCurrent" min-width="100">
|
||||
<template #default="scope">
|
||||
<el-tag :type="scope.row.isCurrent ? 'success' : 'info'">
|
||||
{{ scope.row.isCurrent ? '是' : '否' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="任职日期" align="center" prop="appointmentDate" min-width="120" />
|
||||
<el-table-column label="任期结束" align="center" prop="termEndDate" min-width="120">
|
||||
<template #default="scope">
|
||||
<span v-if="scope.row.termEndDate">
|
||||
{{ scope.row.termEndDate }}
|
||||
<el-tag v-if="scope.row.daysRemaining <= 30 && scope.row.daysRemaining > 0" type="warning" size="small">
|
||||
{{ scope.row.daysRemaining }}天到期
|
||||
</el-tag>
|
||||
<el-tag v-else-if="scope.row.daysRemaining <= 0" type="danger" size="small"> 已到期 </el-tag>
|
||||
</span>
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="工作表现" align="center" prop="workPerformance" min-width="100">
|
||||
<template #default="scope">
|
||||
<el-tag v-if="scope.row.workPerformance" :type="getPerformanceTagType(scope.row.workPerformance)">
|
||||
{{ getPerformanceText(scope.row.workPerformance) }}
|
||||
</el-tag>
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" min-width="280">
|
||||
<template #default="scope">
|
||||
<el-tooltip content="查看详情" placement="top">
|
||||
<el-button link type="primary" icon="View" @click="handleView(scope.row)" v-hasPermi="['xg:classCadre:query']" />
|
||||
</el-tooltip>
|
||||
<el-tooltip content="编辑" placement="top">
|
||||
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['xg:classCadre:edit']" />
|
||||
</el-tooltip>
|
||||
<el-tooltip content="删除" placement="top">
|
||||
<el-button link type="danger" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['xg:classCadre:remove']" />
|
||||
</el-tooltip>
|
||||
|
||||
<!-- 任免操作按钮 -->
|
||||
<el-dropdown v-if="scope.row.isCurrent" @command="(command) => handleCadreOperation(command, scope.row)">
|
||||
<el-button link type="warning" icon="Operation">
|
||||
任免 <el-icon class="el-icon--right"><arrow-down /></el-icon>
|
||||
</el-button>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item command="dismiss" v-hasPermi="['xg:classCadre:dismiss']">免职</el-dropdown-item>
|
||||
<el-dropdown-item command="transfer" v-hasPermi="['xg:classCadre:transfer']">调任</el-dropdown-item>
|
||||
<el-dropdown-item command="renew" v-hasPermi="['xg:classCadre:renew']">续任</el-dropdown-item>
|
||||
<el-dropdown-item command="resign" v-hasPermi="['xg:classCadre:resign']">辞职</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
|
||||
<!-- 重新任命按钮 -->
|
||||
<el-tooltip v-if="!scope.row.isCurrent" content="重新任命" placement="top">
|
||||
<el-button link type="success" icon="UserFilled" @click="handleReappoint(scope.row)" v-hasPermi="['xg:classCadre:appoint']" />
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<!-- 分页 -->
|
||||
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
|
||||
|
||||
<!-- 添加或修改班级干部信息对话框 -->
|
||||
<el-dialog :title="dialog.title" v-model="dialog.visible" width="800px" append-to-body>
|
||||
<el-form ref="classCadreFormRef" :model="form" :rules="rules" label-width="120px">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="班级" prop="classId">
|
||||
<el-select v-model="form.classId" placeholder="请选择班级" style="width: 100%">
|
||||
<el-option v-for="item in classOptions" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="学生" prop="studentId">
|
||||
<el-select
|
||||
v-model="form.studentId"
|
||||
placeholder="请选择学生"
|
||||
filterable
|
||||
remote
|
||||
:remote-method="remoteStudentMethod"
|
||||
:loading="studentLoading"
|
||||
style="width: 100%"
|
||||
@change="handleStudentChange"
|
||||
>
|
||||
<el-option v-for="item in studentOptions" :key="item.value" :label="`${item.studentNo} - ${item.label}`" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="职务" prop="positionCode">
|
||||
<el-select v-model="form.positionCode" placeholder="请选择职务" style="width: 100%" @change="handlePositionChange">
|
||||
<el-option v-for="item in positionOptions" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="任职日期" prop="appointmentDate">
|
||||
<el-date-picker v-model="form.appointmentDate" type="date" placeholder="选择任职日期" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="任期开始日期" prop="termStartDate">
|
||||
<el-date-picker v-model="form.termStartDate" type="date" placeholder="选择任期开始日期" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="任期时长(月)" prop="termDuration">
|
||||
<el-input-number v-model="form.termDuration" :min="1" :max="48" placeholder="请输入任期时长" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="是否兼职" prop="isConcurrent">
|
||||
<el-radio-group v-model="form.isConcurrent">
|
||||
<el-radio :value="false">否</el-radio>
|
||||
<el-radio :value="true">是</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="任命原因" prop="appointmentReason">
|
||||
<el-input v-model="form.appointmentReason" placeholder="请输入任命原因" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="联系电话" prop="contactPhone">
|
||||
<el-input v-model="form.contactPhone" placeholder="请输入联系电话" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="联系邮箱" prop="contactEmail">
|
||||
<el-input v-model="form.contactEmail" placeholder="请输入联系邮箱" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-form-item label="工作计划" prop="workPlan">
|
||||
<el-input v-model="form.workPlan" type="textarea" :rows="3" placeholder="请输入工作计划" />
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="form.remark" type="textarea" :rows="2" placeholder="请输入备注" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="cancel">取 消</el-button>
|
||||
<el-button type="primary" @click="submitForm">确 定</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 查看详情对话框 -->
|
||||
<el-dialog title="班干部详情" v-model="viewDialog.visible" width="900px" append-to-body>
|
||||
<el-descriptions :column="2" border>
|
||||
<el-descriptions-item label="学号">{{ viewData.studentNo }}</el-descriptions-item>
|
||||
<el-descriptions-item label="姓名">{{ viewData.studentName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="班级">{{ viewData.className }}</el-descriptions-item>
|
||||
<el-descriptions-item label="职务">{{ viewData.positionName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="任职日期">{{ viewData.appointmentDate }}</el-descriptions-item>
|
||||
<el-descriptions-item label="任期开始">{{ viewData.termStartDate }}</el-descriptions-item>
|
||||
<el-descriptions-item label="任期结束">{{ viewData.termEndDate || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="任期时长">{{ viewData.termDuration }}个月</el-descriptions-item>
|
||||
<el-descriptions-item label="是否现任">
|
||||
<el-tag :type="viewData.isCurrent ? 'success' : 'info'">
|
||||
{{ viewData.isCurrent ? '是' : '否' }}
|
||||
</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="任职状态">
|
||||
<el-tag :type="getCadreStatusTagType(viewData.cadreStatus)">
|
||||
{{ getCadreStatusText(viewData.cadreStatus) }}
|
||||
</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="是否兼职">
|
||||
{{ viewData.isConcurrent ? '是' : '否' }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="工作表现">
|
||||
<el-tag v-if="viewData.workPerformance" :type="getPerformanceTagType(viewData.workPerformance)">
|
||||
{{ getPerformanceText(viewData.workPerformance) }}
|
||||
</el-tag>
|
||||
<span v-else>-</span>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="任命原因" :span="2">{{ viewData.appointmentReason || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="联系电话">{{ viewData.contactPhone || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="联系邮箱">{{ viewData.contactEmail || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="工作计划" :span="2">{{ viewData.workPlan || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="主要成就" :span="2">{{ viewData.achievements || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="备注" :span="2">{{ viewData.remark || '-' }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 任免操作对话框 -->
|
||||
<CadreOperationDialog
|
||||
v-model:visible="operationDialog.visible"
|
||||
:operation-type="operationDialog.type"
|
||||
:cadre-data="operationDialog.cadreData"
|
||||
@confirm="handleOperationConfirm"
|
||||
/>
|
||||
|
||||
<!-- 导入对话框 -->
|
||||
<ImportDialog
|
||||
v-model:visible="importDialog.visible"
|
||||
:upload-url="'/xg/classCadre/import'"
|
||||
:template-url="'/xg/classCadre/import-template'"
|
||||
@success="handleImportSuccess"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="ClassCadre" lang="ts">
|
||||
import { ref, reactive, onMounted, getCurrentInstance } from 'vue';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import type { FormInstance, FormRules } from 'element-plus';
|
||||
import {
|
||||
listClassCadre,
|
||||
getClassCadre,
|
||||
addClassCadre,
|
||||
updateClassCadre,
|
||||
delClassCadre,
|
||||
exportClassCadre,
|
||||
dismissCadre,
|
||||
transferCadre,
|
||||
renewCadre,
|
||||
resignCadre,
|
||||
getExpiringCadres
|
||||
} from '@/api/xg-xx/classCadre';
|
||||
import { listClassInfo } from '@/api/xg-xx/classInfo';
|
||||
import { listStudentInfo } from '@/api/xg-xx/studentInfo';
|
||||
import type { XgClassCadreVO, XgClassCadreForm, XgClassCadreQuery } from '@/api/xg-xx/types';
|
||||
import CadreOperationDialog from './components/CadreOperationDialog.vue';
|
||||
import ImportDialog from '@/components/ImportDialog/index.vue';
|
||||
|
||||
const { proxy } = getCurrentInstance()!;
|
||||
|
||||
// 数据
|
||||
const classCadreList = ref<XgClassCadreVO[]>([]);
|
||||
const loading = ref(true);
|
||||
const showSearch = ref(true);
|
||||
const ids = ref<Array<string | number>>([]);
|
||||
const single = ref(true);
|
||||
const multiple = ref(true);
|
||||
const total = ref(0);
|
||||
|
||||
// 查询参数
|
||||
const queryParams = reactive<XgClassCadreQuery>({
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
classId: undefined,
|
||||
studentNo: undefined,
|
||||
studentName: undefined,
|
||||
positionCode: undefined,
|
||||
cadreStatus: undefined,
|
||||
isCurrent: undefined
|
||||
});
|
||||
|
||||
// 表单相关
|
||||
const classCadreFormRef = ref<FormInstance>();
|
||||
const dialog = reactive({
|
||||
visible: false,
|
||||
title: ''
|
||||
});
|
||||
|
||||
const form = reactive<XgClassCadreForm>({
|
||||
id: undefined,
|
||||
classId: undefined,
|
||||
studentId: undefined,
|
||||
positionCode: undefined,
|
||||
appointmentDate: undefined,
|
||||
termStartDate: undefined,
|
||||
termDuration: 12,
|
||||
isConcurrent: false,
|
||||
appointmentReason: undefined,
|
||||
contactPhone: undefined,
|
||||
contactEmail: undefined,
|
||||
workPlan: undefined,
|
||||
remark: undefined
|
||||
});
|
||||
|
||||
const rules = reactive<FormRules<XgClassCadreForm>>({
|
||||
classId: [{ required: true, message: '班级不能为空', trigger: 'change' }],
|
||||
studentId: [{ required: true, message: '学生不能为空', trigger: 'change' }],
|
||||
positionCode: [{ required: true, message: '职务不能为空', trigger: 'change' }],
|
||||
appointmentDate: [{ required: true, message: '任职日期不能为空', trigger: 'blur' }],
|
||||
termStartDate: [{ required: true, message: '任期开始日期不能为空', trigger: 'blur' }],
|
||||
termDuration: [{ required: true, message: '任期时长不能为空', trigger: 'blur' }]
|
||||
});
|
||||
|
||||
// 选项数据
|
||||
const classOptions = ref<Array<{ value: string | number; label: string }>>([]);
|
||||
const studentOptions = ref<Array<{ value: string | number; label: string; studentNo: string }>>([]);
|
||||
const positionOptions = ref([
|
||||
{ value: 'class_monitor', label: '班长' },
|
||||
{ value: 'deputy_monitor', label: '副班长' },
|
||||
{ value: 'study_committee', label: '学习委员' },
|
||||
{ value: 'organization_committee', label: '组织委员' },
|
||||
{ value: 'publicity_committee', label: '宣传委员' },
|
||||
{ value: 'sports_committee', label: '体育委员' },
|
||||
{ value: 'life_committee', label: '生活委员' },
|
||||
{ value: 'literary_committee', label: '文艺委员' },
|
||||
{ value: 'discipline_committee', label: '纪律委员' },
|
||||
{ value: 'psychology_committee', label: '心理委员' }
|
||||
]);
|
||||
|
||||
const studentLoading = ref(false);
|
||||
|
||||
// 查看详情
|
||||
const viewDialog = reactive({
|
||||
visible: false
|
||||
});
|
||||
const viewData = ref<XgClassCadreVO>({} as XgClassCadreVO);
|
||||
|
||||
// 任免操作
|
||||
const operationDialog = reactive({
|
||||
visible: false,
|
||||
type: '',
|
||||
cadreData: {} as XgClassCadreVO
|
||||
});
|
||||
|
||||
// 导入
|
||||
const importDialog = reactive({
|
||||
visible: false
|
||||
});
|
||||
|
||||
// 查询表单引用
|
||||
const queryFormRef = ref<FormInstance>();
|
||||
|
||||
/** 查询班级干部信息列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true;
|
||||
try {
|
||||
const res = await listClassCadre(queryParams);
|
||||
classCadreList.value = res.rows;
|
||||
total.value = res.total;
|
||||
} catch (error) {
|
||||
console.error('获取班级干部信息列表失败:', error);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNum = 1;
|
||||
getList();
|
||||
};
|
||||
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value?.resetFields();
|
||||
handleQuery();
|
||||
};
|
||||
|
||||
/** 多选框选中数据 */
|
||||
const handleSelectionChange = (selection: XgClassCadreVO[]) => {
|
||||
ids.value = selection.map((item) => item.id);
|
||||
single.value = selection.length !== 1;
|
||||
multiple.value = !selection.length;
|
||||
};
|
||||
|
||||
/** 新增按钮操作 */
|
||||
const handleAdd = () => {
|
||||
reset();
|
||||
dialog.visible = true;
|
||||
dialog.title = '添加班级干部信息';
|
||||
};
|
||||
|
||||
/** 修改按钮操作 */
|
||||
const handleUpdate = async (row?: XgClassCadreVO) => {
|
||||
reset();
|
||||
const id = row?.id || ids.value[0];
|
||||
try {
|
||||
const res = await getClassCadre(id);
|
||||
Object.assign(form, res.data);
|
||||
dialog.visible = true;
|
||||
dialog.title = '修改班级干部信息';
|
||||
} catch (error) {
|
||||
console.error('获取班级干部信息失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
/** 查看详情 */
|
||||
const handleView = async (row: XgClassCadreVO) => {
|
||||
try {
|
||||
const res = await getClassCadre(row.id);
|
||||
viewData.value = res.data;
|
||||
viewDialog.visible = true;
|
||||
} catch (error) {
|
||||
console.error('获取班级干部信息失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
/** 提交按钮 */
|
||||
const submitForm = () => {
|
||||
classCadreFormRef.value?.validate(async (valid) => {
|
||||
if (valid) {
|
||||
try {
|
||||
if (form.id) {
|
||||
await updateClassCadre(form);
|
||||
ElMessage.success('修改成功');
|
||||
} else {
|
||||
await addClassCadre(form);
|
||||
ElMessage.success('新增成功');
|
||||
}
|
||||
dialog.visible = false;
|
||||
getList();
|
||||
} catch (error) {
|
||||
console.error('提交失败:', error);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = async (row?: XgClassCadreVO) => {
|
||||
const deleteIds = row?.id ? [row.id] : ids.value;
|
||||
try {
|
||||
await ElMessageBox.confirm('是否确认删除班级干部信息编号为"' + deleteIds + '"的数据项?', '警告', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
});
|
||||
await delClassCadre(deleteIds);
|
||||
ElMessage.success('删除成功');
|
||||
getList();
|
||||
} catch (error) {
|
||||
console.error('删除失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
/** 导出按钮操作 */
|
||||
const handleExport = () => {
|
||||
proxy?.download(
|
||||
'/xg/classCadre/export',
|
||||
{
|
||||
...queryParams
|
||||
},
|
||||
`class_cadre_${new Date().getTime()}.xlsx`
|
||||
);
|
||||
};
|
||||
|
||||
/** 导入按钮操作 */
|
||||
const handleImport = () => {
|
||||
importDialog.visible = true;
|
||||
};
|
||||
|
||||
/** 导入成功回调 */
|
||||
const handleImportSuccess = () => {
|
||||
getList();
|
||||
};
|
||||
|
||||
/** 到期提醒 */
|
||||
const handleExpiring = async () => {
|
||||
try {
|
||||
const res = await getExpiringCadres(30);
|
||||
if (res.data.length > 0) {
|
||||
ElMessage.warning(`有 ${res.data.length} 名班干部即将到期,请及时处理`);
|
||||
} else {
|
||||
ElMessage.success('暂无即将到期的班干部');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取到期提醒失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
/** 任免操作 */
|
||||
const handleCadreOperation = (command: string, row: XgClassCadreVO) => {
|
||||
operationDialog.type = command;
|
||||
operationDialog.cadreData = row;
|
||||
operationDialog.visible = true;
|
||||
};
|
||||
|
||||
/** 重新任命 */
|
||||
const handleReappoint = (row: XgClassCadreVO) => {
|
||||
handleCadreOperation('reappoint', row);
|
||||
};
|
||||
|
||||
/** 任免操作确认 */
|
||||
const handleOperationConfirm = async (operationData: any) => {
|
||||
try {
|
||||
switch (operationData.type) {
|
||||
case 'dismiss':
|
||||
await dismissCadre(operationData.cadreId, operationData.reason, operationData.operationDate);
|
||||
ElMessage.success('免职成功');
|
||||
break;
|
||||
case 'transfer':
|
||||
await transferCadre(
|
||||
operationData.cadreId,
|
||||
operationData.newPositionCode,
|
||||
operationData.newPositionName,
|
||||
operationData.reason,
|
||||
operationData.effectiveDate
|
||||
);
|
||||
ElMessage.success('调任成功');
|
||||
break;
|
||||
case 'renew':
|
||||
await renewCadre(operationData.cadreId, operationData.newTermDuration, operationData.reason);
|
||||
ElMessage.success('续任成功');
|
||||
break;
|
||||
case 'resign':
|
||||
await resignCadre(operationData.cadreId, operationData.reason, operationData.operationDate);
|
||||
ElMessage.success('辞职成功');
|
||||
break;
|
||||
}
|
||||
getList();
|
||||
} catch (error) {
|
||||
console.error('操作失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
/** 表单重置 */
|
||||
const reset = () => {
|
||||
Object.assign(form, {
|
||||
id: undefined,
|
||||
classId: undefined,
|
||||
studentId: undefined,
|
||||
positionCode: undefined,
|
||||
appointmentDate: undefined,
|
||||
termStartDate: undefined,
|
||||
termDuration: 12,
|
||||
isConcurrent: false,
|
||||
appointmentReason: undefined,
|
||||
contactPhone: undefined,
|
||||
contactEmail: undefined,
|
||||
workPlan: undefined,
|
||||
remark: undefined
|
||||
});
|
||||
classCadreFormRef.value?.resetFields();
|
||||
};
|
||||
|
||||
/** 取消按钮 */
|
||||
const cancel = () => {
|
||||
dialog.visible = false;
|
||||
reset();
|
||||
};
|
||||
|
||||
/** 远程搜索学生 */
|
||||
const remoteStudentMethod = async (query: string) => {
|
||||
if (query) {
|
||||
studentLoading.value = true;
|
||||
try {
|
||||
const res = await listStudentInfo({ studentNo: query, name: query, pageSize: 20 });
|
||||
studentOptions.value = res.rows.map((item) => ({
|
||||
value: item.id,
|
||||
label: item.name,
|
||||
studentNo: item.studentNo
|
||||
}));
|
||||
} catch (error) {
|
||||
console.error('搜索学生失败:', error);
|
||||
} finally {
|
||||
studentLoading.value = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/** 学生选择变化 */
|
||||
const handleStudentChange = (studentId: string | number) => {
|
||||
const student = studentOptions.value.find((item) => item.value === studentId);
|
||||
if (student) {
|
||||
form.studentNo = student.studentNo;
|
||||
form.studentName = student.label;
|
||||
}
|
||||
};
|
||||
|
||||
/** 职务选择变化 */
|
||||
const handlePositionChange = (positionCode: string) => {
|
||||
const position = positionOptions.value.find((item) => item.value === positionCode);
|
||||
if (position) {
|
||||
form.positionName = position.label;
|
||||
}
|
||||
};
|
||||
|
||||
/** 获取班级选项 */
|
||||
const getClassOptions = async () => {
|
||||
try {
|
||||
const res = await listClassInfo({ pageSize: 1000 });
|
||||
classOptions.value = res.rows.map((item) => ({
|
||||
value: item.id,
|
||||
label: `${item.classCode} - ${item.className}`
|
||||
}));
|
||||
} catch (error) {
|
||||
console.error('获取班级选项失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 辅助方法
|
||||
const getPositionTagType = (level: number) => {
|
||||
switch (level) {
|
||||
case 1:
|
||||
return 'danger'; // 班长
|
||||
case 2:
|
||||
return 'warning'; // 副班长
|
||||
case 3:
|
||||
return 'success'; // 委员
|
||||
default:
|
||||
return 'info'; // 普通成员
|
||||
}
|
||||
};
|
||||
|
||||
const getCadreStatusTagType = (status: string) => {
|
||||
switch (status) {
|
||||
case '1':
|
||||
return 'success'; // 在任
|
||||
case '2':
|
||||
return 'warning'; // 到期
|
||||
case '3':
|
||||
return 'info'; // 辞职
|
||||
case '4':
|
||||
return 'danger'; // 免职
|
||||
case '5':
|
||||
return 'primary'; // 调任
|
||||
default:
|
||||
return 'info';
|
||||
}
|
||||
};
|
||||
|
||||
const getCadreStatusText = (status: string) => {
|
||||
switch (status) {
|
||||
case '1':
|
||||
return '在任';
|
||||
case '2':
|
||||
return '到期';
|
||||
case '3':
|
||||
return '辞职';
|
||||
case '4':
|
||||
return '免职';
|
||||
case '5':
|
||||
return '调任';
|
||||
default:
|
||||
return '未知';
|
||||
}
|
||||
};
|
||||
|
||||
const getPerformanceTagType = (performance: string) => {
|
||||
switch (performance) {
|
||||
case 'excellent':
|
||||
return 'success';
|
||||
case 'good':
|
||||
return 'primary';
|
||||
case 'average':
|
||||
return 'warning';
|
||||
case 'poor':
|
||||
return 'danger';
|
||||
default:
|
||||
return 'info';
|
||||
}
|
||||
};
|
||||
|
||||
const getPerformanceText = (performance: string) => {
|
||||
switch (performance) {
|
||||
case 'excellent':
|
||||
return '优秀';
|
||||
case 'good':
|
||||
return '良好';
|
||||
case 'average':
|
||||
return '一般';
|
||||
case 'poor':
|
||||
return '较差';
|
||||
default:
|
||||
return '未知';
|
||||
}
|
||||
};
|
||||
|
||||
// 初始化
|
||||
onMounted(() => {
|
||||
getList();
|
||||
getClassOptions();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.app-container {
|
||||
padding: 20px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,504 +0,0 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-row :gutter="20">
|
||||
<!-- 统计卡片 -->
|
||||
<el-col :span="6" v-for="item in statisticsCards" :key="item.key">
|
||||
<el-card class="statistics-card">
|
||||
<div class="statistics-content">
|
||||
<div class="statistics-icon" :style="{ backgroundColor: item.color }">
|
||||
<el-icon :size="24">
|
||||
<component :is="item.icon" />
|
||||
</el-icon>
|
||||
</div>
|
||||
<div class="statistics-info">
|
||||
<div class="statistics-title">{{ item.title }}</div>
|
||||
<div class="statistics-value">{{ item.value }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20" class="mt-4">
|
||||
<!-- 职务分布图 -->
|
||||
<el-col :span="12">
|
||||
<el-card>
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>职务分布</span>
|
||||
</div>
|
||||
</template>
|
||||
<div ref="positionChartRef" style="height: 300px"></div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
|
||||
<!-- 任职状态分布图 -->
|
||||
<el-col :span="12">
|
||||
<el-card>
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>任职状态分布</span>
|
||||
</div>
|
||||
</template>
|
||||
<div ref="statusChartRef" style="height: 300px"></div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20" class="mt-4">
|
||||
<!-- 学院统计 -->
|
||||
<el-col :span="12">
|
||||
<el-card>
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>各学院班干部数量</span>
|
||||
</div>
|
||||
</template>
|
||||
<div ref="collegeChartRef" style="height: 300px"></div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
|
||||
<!-- 工作表现分布 -->
|
||||
<el-col :span="12">
|
||||
<el-card>
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>工作表现分布</span>
|
||||
</div>
|
||||
</template>
|
||||
<div ref="performanceChartRef" style="height: 300px"></div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20" class="mt-4">
|
||||
<!-- 到期提醒列表 -->
|
||||
<el-col :span="24">
|
||||
<el-card>
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>即将到期的班干部</span>
|
||||
<el-button type="primary" size="small" @click="refreshExpiringList">刷新</el-button>
|
||||
</div>
|
||||
</template>
|
||||
<el-table :data="expiringList" style="width: 100%">
|
||||
<el-table-column prop="studentNo" label="学号" width="120" />
|
||||
<el-table-column prop="studentName" label="姓名" width="100" />
|
||||
<el-table-column prop="className" label="班级" width="150" />
|
||||
<el-table-column prop="positionName" label="职务" width="120" />
|
||||
<el-table-column prop="termEndDate" label="任期结束日期" width="120" />
|
||||
<el-table-column prop="daysRemaining" label="剩余天数" width="100">
|
||||
<template #default="scope">
|
||||
<el-tag :type="scope.row.daysRemaining <= 7 ? 'danger' : 'warning'"> {{ scope.row.daysRemaining }}天 </el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="contactPhone" label="联系电话" width="120" />
|
||||
<el-table-column label="操作" width="120">
|
||||
<template #default="scope">
|
||||
<el-button type="primary" size="small" @click="handleContact(scope.row)"> 联系 </el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="ClassCadreStatistics" lang="ts">
|
||||
import { ref, reactive, onMounted, nextTick } from 'vue';
|
||||
import * as echarts from 'echarts';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { User, UserFilled, Trophy, Clock } from '@element-plus/icons-vue';
|
||||
import { listClassCadre, getExpiringCadres, getCadresByCollege, getCadresByPerformance } from '@/api/xg-xx/classCadre';
|
||||
import type { XgClassCadreVO } from '@/api/xg-xx/types';
|
||||
|
||||
// 统计卡片数据
|
||||
const statisticsCards = ref([
|
||||
{
|
||||
key: 'total',
|
||||
title: '总班干部数',
|
||||
value: 0,
|
||||
icon: User,
|
||||
color: '#409EFF'
|
||||
},
|
||||
{
|
||||
key: 'current',
|
||||
title: '在任班干部',
|
||||
value: 0,
|
||||
icon: UserFilled,
|
||||
color: '#67C23A'
|
||||
},
|
||||
{
|
||||
key: 'excellent',
|
||||
title: '优秀班干部',
|
||||
value: 0,
|
||||
icon: Trophy,
|
||||
color: '#E6A23C'
|
||||
},
|
||||
{
|
||||
key: 'expiring',
|
||||
title: '即将到期',
|
||||
value: 0,
|
||||
icon: Clock,
|
||||
color: '#F56C6C'
|
||||
}
|
||||
]);
|
||||
|
||||
// 图表引用
|
||||
const positionChartRef = ref<HTMLDivElement>();
|
||||
const statusChartRef = ref<HTMLDivElement>();
|
||||
const collegeChartRef = ref<HTMLDivElement>();
|
||||
const performanceChartRef = ref<HTMLDivElement>();
|
||||
|
||||
// 到期提醒列表
|
||||
const expiringList = ref<XgClassCadreVO[]>([]);
|
||||
|
||||
// 图表实例
|
||||
let positionChart: echarts.ECharts;
|
||||
let statusChart: echarts.ECharts;
|
||||
let collegeChart: echarts.ECharts;
|
||||
let performanceChart: echarts.ECharts;
|
||||
|
||||
/** 加载统计数据 */
|
||||
const loadStatistics = async () => {
|
||||
try {
|
||||
// 获取总班干部数据
|
||||
const totalRes = await listClassCadre({ pageSize: 10000 });
|
||||
const totalData = totalRes.rows;
|
||||
|
||||
// 统计总数
|
||||
statisticsCards.value[0].value = totalData.length;
|
||||
|
||||
// 统计在任班干部
|
||||
const currentCadres = totalData.filter((item) => item.isCurrent);
|
||||
statisticsCards.value[1].value = currentCadres.length;
|
||||
|
||||
// 统计优秀班干部
|
||||
const excellentCadres = totalData.filter((item) => item.workPerformance === 'excellent');
|
||||
statisticsCards.value[2].value = excellentCadres.length;
|
||||
|
||||
// 统计即将到期
|
||||
const expiringRes = await getExpiringCadres(30);
|
||||
statisticsCards.value[3].value = expiringRes.data.length;
|
||||
expiringList.value = expiringRes.data;
|
||||
|
||||
// 初始化图表
|
||||
await nextTick();
|
||||
initPositionChart(totalData);
|
||||
initStatusChart(totalData);
|
||||
initCollegeChart(totalData);
|
||||
initPerformanceChart(totalData);
|
||||
} catch (error) {
|
||||
console.error('加载统计数据失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
/** 初始化职务分布图 */
|
||||
const initPositionChart = (data: XgClassCadreVO[]) => {
|
||||
if (!positionChartRef.value) return;
|
||||
|
||||
positionChart = echarts.init(positionChartRef.value);
|
||||
|
||||
// 统计职务分布
|
||||
const positionMap = new Map();
|
||||
data.forEach((item) => {
|
||||
const count = positionMap.get(item.positionName) || 0;
|
||||
positionMap.set(item.positionName, count + 1);
|
||||
});
|
||||
|
||||
const positionData = Array.from(positionMap.entries()).map(([name, value]) => ({
|
||||
name,
|
||||
value
|
||||
}));
|
||||
|
||||
const option = {
|
||||
tooltip: {
|
||||
trigger: 'item'
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '职务分布',
|
||||
type: 'pie',
|
||||
radius: '50%',
|
||||
data: positionData,
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
shadowBlur: 10,
|
||||
shadowOffsetX: 0,
|
||||
shadowColor: 'rgba(0, 0, 0, 0.5)'
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
positionChart.setOption(option);
|
||||
};
|
||||
|
||||
/** 初始化任职状态分布图 */
|
||||
const initStatusChart = (data: XgClassCadreVO[]) => {
|
||||
if (!statusChartRef.value) return;
|
||||
|
||||
statusChart = echarts.init(statusChartRef.value);
|
||||
|
||||
// 统计任职状态分布
|
||||
const statusMap = new Map();
|
||||
data.forEach((item) => {
|
||||
const statusText = getCadreStatusText(item.cadreStatus);
|
||||
const count = statusMap.get(statusText) || 0;
|
||||
statusMap.set(statusText, count + 1);
|
||||
});
|
||||
|
||||
const statusData = Array.from(statusMap.entries()).map(([name, value]) => ({
|
||||
name,
|
||||
value
|
||||
}));
|
||||
|
||||
const option = {
|
||||
tooltip: {
|
||||
trigger: 'item'
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '任职状态',
|
||||
type: 'pie',
|
||||
radius: ['40%', '70%'],
|
||||
data: statusData,
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
shadowBlur: 10,
|
||||
shadowOffsetX: 0,
|
||||
shadowColor: 'rgba(0, 0, 0, 0.5)'
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
statusChart.setOption(option);
|
||||
};
|
||||
|
||||
/** 初始化学院统计图 */
|
||||
const initCollegeChart = (data: XgClassCadreVO[]) => {
|
||||
if (!collegeChartRef.value) return;
|
||||
|
||||
collegeChart = echarts.init(collegeChartRef.value);
|
||||
|
||||
// 统计学院分布
|
||||
const collegeMap = new Map();
|
||||
data.forEach((item) => {
|
||||
const college = item.collegeName || '未知学院';
|
||||
const count = collegeMap.get(college) || 0;
|
||||
collegeMap.set(college, count + 1);
|
||||
});
|
||||
|
||||
const colleges = Array.from(collegeMap.keys());
|
||||
const values = Array.from(collegeMap.values());
|
||||
|
||||
const option = {
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'shadow'
|
||||
}
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: colleges,
|
||||
axisLabel: {
|
||||
interval: 0,
|
||||
rotate: 45
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value'
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '班干部数量',
|
||||
type: 'bar',
|
||||
data: values,
|
||||
itemStyle: {
|
||||
color: '#409EFF'
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
collegeChart.setOption(option);
|
||||
};
|
||||
|
||||
/** 初始化工作表现分布图 */
|
||||
const initPerformanceChart = (data: XgClassCadreVO[]) => {
|
||||
if (!performanceChartRef.value) return;
|
||||
|
||||
performanceChart = echarts.init(performanceChartRef.value);
|
||||
|
||||
// 统计工作表现分布
|
||||
const performanceMap = new Map();
|
||||
data.forEach((item) => {
|
||||
if (item.workPerformance) {
|
||||
const performanceText = getPerformanceText(item.workPerformance);
|
||||
const count = performanceMap.get(performanceText) || 0;
|
||||
performanceMap.set(performanceText, count + 1);
|
||||
}
|
||||
});
|
||||
|
||||
const performanceData = Array.from(performanceMap.entries()).map(([name, value]) => ({
|
||||
name,
|
||||
value
|
||||
}));
|
||||
|
||||
const option = {
|
||||
tooltip: {
|
||||
trigger: 'item'
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '工作表现',
|
||||
type: 'pie',
|
||||
radius: '50%',
|
||||
data: performanceData,
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
shadowBlur: 10,
|
||||
shadowOffsetX: 0,
|
||||
shadowColor: 'rgba(0, 0, 0, 0.5)'
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
performanceChart.setOption(option);
|
||||
};
|
||||
|
||||
/** 刷新即将到期列表 */
|
||||
const refreshExpiringList = async () => {
|
||||
try {
|
||||
const res = await getExpiringCadres(30);
|
||||
expiringList.value = res.data;
|
||||
statisticsCards.value[3].value = res.data.length;
|
||||
ElMessage.success('刷新成功');
|
||||
} catch (error) {
|
||||
console.error('刷新失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
/** 联系班干部 */
|
||||
const handleContact = (row: XgClassCadreVO) => {
|
||||
if (row.contactPhone) {
|
||||
window.open(`tel:${row.contactPhone}`);
|
||||
} else {
|
||||
ElMessage.warning('该班干部未设置联系电话');
|
||||
}
|
||||
};
|
||||
|
||||
// 辅助方法
|
||||
const getCadreStatusText = (status: string) => {
|
||||
switch (status) {
|
||||
case '1':
|
||||
return '在任';
|
||||
case '2':
|
||||
return '到期';
|
||||
case '3':
|
||||
return '辞职';
|
||||
case '4':
|
||||
return '免职';
|
||||
case '5':
|
||||
return '调任';
|
||||
default:
|
||||
return '未知';
|
||||
}
|
||||
};
|
||||
|
||||
const getPerformanceText = (performance: string) => {
|
||||
switch (performance) {
|
||||
case 'excellent':
|
||||
return '优秀';
|
||||
case 'good':
|
||||
return '良好';
|
||||
case 'average':
|
||||
return '一般';
|
||||
case 'poor':
|
||||
return '较差';
|
||||
default:
|
||||
return '未知';
|
||||
}
|
||||
};
|
||||
|
||||
// 响应式处理
|
||||
const handleResize = () => {
|
||||
positionChart?.resize();
|
||||
statusChart?.resize();
|
||||
collegeChart?.resize();
|
||||
performanceChart?.resize();
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
loadStatistics();
|
||||
window.addEventListener('resize', handleResize);
|
||||
});
|
||||
|
||||
// 清理
|
||||
onBeforeUnmount(() => {
|
||||
window.removeEventListener('resize', handleResize);
|
||||
positionChart?.dispose();
|
||||
statusChart?.dispose();
|
||||
collegeChart?.dispose();
|
||||
performanceChart?.dispose();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.app-container {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.statistics-card {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.statistics-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.statistics-icon {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: white;
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
.statistics-info {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.statistics-title {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.statistics-value {
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.mt-4 {
|
||||
margin-top: 20px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,277 +0,0 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-card class="box-card">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>班级信息导入</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<el-steps :active="activeStep" finish-status="success" align-center>
|
||||
<el-step title="下载模板" description="下载Excel导入模板"></el-step>
|
||||
<el-step title="填写数据" description="按模板格式填写班级数据"></el-step>
|
||||
<el-step title="上传文件" description="上传填写好的Excel文件"></el-step>
|
||||
<el-step title="导入完成" description="查看导入结果"></el-step>
|
||||
</el-steps>
|
||||
|
||||
<div class="import-content" v-if="activeStep === 0">
|
||||
<div class="step-content">
|
||||
<el-alert title="使用说明" type="info" :closable="false" show-icon>
|
||||
<p>1. 请先下载班级信息导入模板</p>
|
||||
<p>2. 按照模板格式填写班级信息</p>
|
||||
<p>3. 班级代码、班级名称为必填项</p>
|
||||
<p>4. 确保学院代码、专业代码在系统中存在</p>
|
||||
</el-alert>
|
||||
|
||||
<div class="step-actions">
|
||||
<el-button type="primary" @click="downloadTemplate">
|
||||
<el-icon><Download /></el-icon>
|
||||
下载导入模板
|
||||
</el-button>
|
||||
<el-button @click="nextStep">下一步</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="import-content" v-if="activeStep === 1">
|
||||
<div class="step-content">
|
||||
<el-alert title="填写要求" type="warning" :closable="false" show-icon>
|
||||
<p>• 班级代码必须唯一,建议格式:学院代码+年级+专业+序号</p>
|
||||
<p>• 班级名称不能重复</p>
|
||||
<p>• 入学年份和毕业年份必须为4位数字</p>
|
||||
<p>• 最大学生人数建议在20-60人之间</p>
|
||||
</el-alert>
|
||||
|
||||
<div class="step-actions">
|
||||
<el-button @click="prevStep">上一步</el-button>
|
||||
<el-button type="primary" @click="nextStep">下一步</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="import-content" v-if="activeStep === 2">
|
||||
<div class="step-content">
|
||||
<el-upload
|
||||
ref="uploadRef"
|
||||
class="upload-demo"
|
||||
drag
|
||||
:action="uploadAction"
|
||||
:headers="uploadHeaders"
|
||||
:data="uploadData"
|
||||
:before-upload="beforeUpload"
|
||||
:on-success="handleUploadSuccess"
|
||||
:on-error="handleUploadError"
|
||||
:file-list="fileList"
|
||||
:limit="1"
|
||||
accept=".xlsx,.xls"
|
||||
>
|
||||
<el-icon class="el-icon--upload"><upload-filled /></el-icon>
|
||||
<div class="el-upload__text">将Excel文件拖到此处,或<em>点击上传</em></div>
|
||||
<template #tip>
|
||||
<div class="el-upload__tip">只能上传xlsx/xls文件,且不超过10MB</div>
|
||||
</template>
|
||||
</el-upload>
|
||||
|
||||
<div class="upload-options">
|
||||
<el-checkbox v-model="updateSupport"> 是否更新已经存在的班级数据 </el-checkbox>
|
||||
</div>
|
||||
|
||||
<div class="step-actions">
|
||||
<el-button @click="prevStep">上一步</el-button>
|
||||
<el-button type="primary" @click="startImport" :loading="uploading"> 开始导入 </el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="import-content" v-if="activeStep === 3">
|
||||
<div class="step-content">
|
||||
<el-result :icon="importResult.success ? 'success' : 'error'" :title="importResult.title" :sub-title="importResult.message">
|
||||
<template #extra>
|
||||
<el-button type="primary" @click="resetImport">重新导入</el-button>
|
||||
<el-button @click="goToList">查看班级列表</el-button>
|
||||
</template>
|
||||
</el-result>
|
||||
|
||||
<div v-if="importResult.detail" class="import-detail">
|
||||
<el-card>
|
||||
<template #header>
|
||||
<span>导入详情</span>
|
||||
</template>
|
||||
<div v-html="importResult.detail"></div>
|
||||
</el-card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="ClassInfoImport" lang="ts">
|
||||
import { ref, reactive } from 'vue';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { getToken } from '@/utils/auth';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
// 组件状态
|
||||
const activeStep = ref(0);
|
||||
const uploading = ref(false);
|
||||
const fileList = ref([]);
|
||||
const updateSupport = ref(false);
|
||||
|
||||
// 上传配置
|
||||
const uploadAction = ref(import.meta.env.VITE_APP_BASE_API + '/xg/classInfo/importData');
|
||||
const uploadHeaders = ref({
|
||||
Authorization: 'Bearer ' + getToken()
|
||||
});
|
||||
const uploadData = reactive({
|
||||
updateSupport: false
|
||||
});
|
||||
|
||||
// 导入结果
|
||||
const importResult = reactive({
|
||||
success: false,
|
||||
title: '',
|
||||
message: '',
|
||||
detail: ''
|
||||
});
|
||||
|
||||
/** 下载模板 */
|
||||
const downloadTemplate = () => {
|
||||
// 这里应该调用下载模板的API
|
||||
window.open(import.meta.env.VITE_APP_BASE_API + '/xg/classInfo/importTemplate', '_blank');
|
||||
ElMessage.success('模板下载已开始');
|
||||
};
|
||||
|
||||
/** 下一步 */
|
||||
const nextStep = () => {
|
||||
if (activeStep.value < 3) {
|
||||
activeStep.value++;
|
||||
}
|
||||
};
|
||||
|
||||
/** 上一步 */
|
||||
const prevStep = () => {
|
||||
if (activeStep.value > 0) {
|
||||
activeStep.value--;
|
||||
}
|
||||
};
|
||||
|
||||
/** 上传前检查 */
|
||||
const beforeUpload = (file: any) => {
|
||||
const isExcel = file.type === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' || file.type === 'application/vnd.ms-excel';
|
||||
const isLt10M = file.size / 1024 / 1024 < 10;
|
||||
|
||||
if (!isExcel) {
|
||||
ElMessage.error('上传文件只能是 Excel 格式!');
|
||||
return false;
|
||||
}
|
||||
if (!isLt10M) {
|
||||
ElMessage.error('上传文件大小不能超过 10MB!');
|
||||
return false;
|
||||
}
|
||||
|
||||
uploadData.updateSupport = updateSupport.value;
|
||||
return true;
|
||||
};
|
||||
|
||||
/** 开始导入 */
|
||||
const startImport = () => {
|
||||
if (fileList.value.length === 0) {
|
||||
ElMessage.warning('请先选择要上传的文件');
|
||||
return;
|
||||
}
|
||||
|
||||
uploading.value = true;
|
||||
// 文件会自动上传,这里只需要更新状态
|
||||
};
|
||||
|
||||
/** 上传成功处理 */
|
||||
const handleUploadSuccess = (response: any) => {
|
||||
uploading.value = false;
|
||||
|
||||
if (response.code === 200) {
|
||||
importResult.success = true;
|
||||
importResult.title = '导入成功';
|
||||
importResult.message = response.msg;
|
||||
importResult.detail = response.data || '';
|
||||
activeStep.value = 3;
|
||||
} else {
|
||||
importResult.success = false;
|
||||
importResult.title = '导入失败';
|
||||
importResult.message = response.msg || '导入过程中发生错误';
|
||||
importResult.detail = response.data || '';
|
||||
activeStep.value = 3;
|
||||
}
|
||||
};
|
||||
|
||||
/** 上传失败处理 */
|
||||
const handleUploadError = () => {
|
||||
uploading.value = false;
|
||||
importResult.success = false;
|
||||
importResult.title = '导入失败';
|
||||
importResult.message = '文件上传失败,请检查网络连接或文件格式';
|
||||
importResult.detail = '';
|
||||
activeStep.value = 3;
|
||||
};
|
||||
|
||||
/** 重新导入 */
|
||||
const resetImport = () => {
|
||||
activeStep.value = 0;
|
||||
fileList.value = [];
|
||||
updateSupport.value = false;
|
||||
importResult.success = false;
|
||||
importResult.title = '';
|
||||
importResult.message = '';
|
||||
importResult.detail = '';
|
||||
};
|
||||
|
||||
/** 跳转到班级列表 */
|
||||
const goToList = () => {
|
||||
router.push('/xg-xx/classInfo');
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.import-content {
|
||||
margin: 40px 0;
|
||||
}
|
||||
|
||||
.step-content {
|
||||
text-align: center;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.step-actions {
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
.step-actions .el-button {
|
||||
margin: 0 10px;
|
||||
}
|
||||
|
||||
.upload-demo {
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.upload-options {
|
||||
margin: 20px 0;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.import-detail {
|
||||
margin-top: 20px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.el-alert p {
|
||||
margin: 5px 0;
|
||||
}
|
||||
</style>
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,916 +0,0 @@
|
||||
<template>
|
||||
<div class="p-2 page-container">
|
||||
<!-- 搜索区域 -->
|
||||
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
|
||||
<div v-show="showSearch" class="mb-[10px]">
|
||||
<el-card shadow="hover">
|
||||
<el-form ref="queryFormRef" :model="queryParams" label-width="100px">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="6">
|
||||
<el-form-item label="批次编码" prop="batchCode">
|
||||
<el-input v-model="queryParams.batchCode" placeholder="请输入批次编码" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="批次名称" prop="batchName">
|
||||
<el-input v-model="queryParams.batchName" placeholder="请输入批次名称" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="批次类型" prop="batchType">
|
||||
<el-select v-model="queryParams.batchType" placeholder="请选择批次类型" clearable style="width: 100%">
|
||||
<el-option label="常规批次" value="regular" />
|
||||
<el-option label="专项批次" value="special" />
|
||||
<el-option label="紧急批次" value="urgent" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="批次状态" prop="status">
|
||||
<el-select v-model="queryParams.status" placeholder="请选择批次状态" clearable style="width: 100%">
|
||||
<el-option label="草稿" value="draft" />
|
||||
<el-option label="进行中" value="active" />
|
||||
<el-option label="已完成" value="completed" />
|
||||
<el-option label="已取消" value="cancelled" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="6">
|
||||
<el-form-item label="学年" prop="academicYearId">
|
||||
<el-select v-model="queryParams.academicYearId" placeholder="请选择学年" clearable style="width: 100%">
|
||||
<el-option v-for="year in academicYearOptions" :key="year.value" :label="year.label" :value="year.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="是否发布" prop="isPublished">
|
||||
<el-select v-model="queryParams.isPublished" placeholder="请选择发布状态" clearable style="width: 100%">
|
||||
<el-option label="已发布" :value="true" />
|
||||
<el-option label="未发布" :value="false" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</el-card>
|
||||
</div>
|
||||
</transition>
|
||||
|
||||
<!-- 主要内容区域 -->
|
||||
<el-card shadow="never" class="button-card table-card">
|
||||
<template #header>
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['xg:collectionBatch:add']">新增批次</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['xg:collectionBatch:edit']">修改</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['xg:collectionBatch:remove']">删除</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button icon="Download" @click="handleExport" v-hasPermi="['xg:collectionBatch:export']">导出</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button icon="Promotion" :disabled="single" @click="handlePublish" v-hasPermi="['xg:collectionBatch:publish']">发布</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button icon="Finished" :disabled="single" @click="handleComplete" v-hasPermi="['xg:collectionBatch:complete']">结束</el-button>
|
||||
</el-col>
|
||||
<right-toolbar v-model="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<div class="table-container">
|
||||
<el-table v-loading="loading" border :data="collectionBatchList" @selection-change="handleSelectionChange" style="width: 100%" class="scroll-table">
|
||||
<el-table-column type="selection" width="55" align="center" fixed="left" />
|
||||
<el-table-column label="批次编码" align="center" prop="batchCode" width="120" show-overflow-tooltip fixed="left" />
|
||||
<el-table-column label="批次名称" align="center" prop="batchName" width="180" show-overflow-tooltip fixed="left" />
|
||||
<el-table-column label="批次类型" align="center" prop="batchType" width="100">
|
||||
<template #default="scope">
|
||||
<el-tag :type="getBatchTypeTagType(scope.row.batchType)" size="small">
|
||||
{{ getBatchTypeText(scope.row.batchType) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="学年" align="center" prop="academicYearName" width="120" show-overflow-tooltip />
|
||||
<el-table-column label="学期" align="center" prop="semesterName" width="120" show-overflow-tooltip />
|
||||
<el-table-column label="目标范围" align="center" prop="targetScope" width="100">
|
||||
<template #default="scope">
|
||||
<el-tag :type="getTargetScopeTagType(scope.row.targetScope)" size="small">
|
||||
{{ getTargetScopeText(scope.row.targetScope) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="开始时间" align="center" prop="startTime" width="160">
|
||||
<template #default="scope">
|
||||
<span>{{ proxy.parseTime(scope.row.startTime, '{y}-{m}-{d} {h}:{i}') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="结束时间" align="center" prop="endTime" width="160">
|
||||
<template #default="scope">
|
||||
<span>{{ proxy.parseTime(scope.row.endTime, '{y}-{m}-{d} {h}:{i}') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="批次状态" align="center" prop="status" width="100">
|
||||
<template #default="scope">
|
||||
<el-tag :type="getStatusTagType(scope.row.status)" size="small">
|
||||
{{ getStatusText(scope.row.status) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="发布状态" align="center" prop="isPublished" width="100">
|
||||
<template #default="scope">
|
||||
<el-tag :type="scope.row.isPublished ? 'success' : 'info'" size="small">
|
||||
{{ scope.row.isPublished ? '已发布' : '未发布' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="完成进度" align="center" width="120">
|
||||
<template #default="scope">
|
||||
<el-progress :percentage="scope.row.completionRate" :stroke-width="6" :show-text="false" />
|
||||
<div class="text-xs text-gray-500 mt-1">
|
||||
{{ scope.row.completedCount }}/{{ scope.row.totalCount }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="创建时间" align="center" prop="createTime" width="160">
|
||||
<template #default="scope">
|
||||
<span>{{ proxy.parseTime(scope.row.createTime, '{y}-{m}-{d} {h}:{i}') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" width="200" fixed="right" class-name="small-padding fixed-width">
|
||||
<template #default="scope">
|
||||
<div class="action-buttons">
|
||||
<el-tooltip content="查看" placement="top">
|
||||
<el-button link type="info" icon="View" size="small" @click="handleView(scope.row)" v-hasPermi="['xg:collectionBatch:query']"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="修改" placement="top">
|
||||
<el-button link type="primary" icon="Edit" size="small" @click="handleUpdate(scope.row)" v-hasPermi="['xg:collectionBatch:edit']"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="删除" placement="top">
|
||||
<el-button link type="danger" icon="Delete" size="small" @click="handleDelete(scope.row)" v-hasPermi="['xg:collectionBatch:remove']"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="统计" placement="top">
|
||||
<el-button link type="warning" icon="DataAnalysis" size="small" @click="handleStatistics(scope.row)" v-hasPermi="['xg:collectionBatch:statistics']"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="复制" placement="top">
|
||||
<el-button link type="success" icon="DocumentCopy" size="small" @click="handleCopy(scope.row)" v-hasPermi="['xg:collectionBatch:add']"></el-button>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<div class="pagination-container">
|
||||
<pagination v-show="total > 0" :total="total" :page="queryParams.pageNum" :limit="queryParams.pageSize" @pagination="getList" />
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<!-- 新增/修改批次对话框 -->
|
||||
<el-dialog :title="dialog.title" v-model="dialog.visible" width="900px" append-to-body>
|
||||
<el-form ref="collectionBatchFormRef" :model="form" :rules="rules" label-width="120px">
|
||||
<el-tabs v-model="activeTab" type="border-card">
|
||||
<el-tab-pane label="基本信息" name="basic">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="批次编码" prop="batchCode">
|
||||
<el-input v-model="form.batchCode" placeholder="请输入批次编码" :disabled="dialog.type === 'edit'" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="批次名称" prop="batchName">
|
||||
<el-input v-model="form.batchName" placeholder="请输入批次名称" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="批次类型" prop="batchType">
|
||||
<el-select v-model="form.batchType" placeholder="请选择批次类型" style="width: 100%">
|
||||
<el-option label="常规批次" value="regular" />
|
||||
<el-option label="专项批次" value="special" />
|
||||
<el-option label="紧急批次" value="urgent" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="目标范围" prop="targetScope">
|
||||
<el-select v-model="form.targetScope" placeholder="请选择目标范围" style="width: 100%">
|
||||
<el-option label="全部学生" value="all" />
|
||||
<el-option label="指定学院" value="college" />
|
||||
<el-option label="指定班级" value="class" />
|
||||
<el-option label="指定学生" value="student" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="关联学年" prop="academicYearId">
|
||||
<el-select v-model="form.academicYearId" placeholder="请选择学年" style="width: 100%">
|
||||
<el-option v-for="year in academicYearOptions" :key="year.value" :label="year.label" :value="year.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="关联学期" prop="semesterId">
|
||||
<el-select v-model="form.semesterId" placeholder="请选择学期" style="width: 100%">
|
||||
<el-option v-for="semester in semesterOptions" :key="semester.value" :label="semester.label" :value="semester.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="开始时间" prop="startTime">
|
||||
<el-date-picker v-model="form.startTime" type="datetime" placeholder="选择开始时间" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="结束时间" prop="endTime">
|
||||
<el-date-picker v-model="form.endTime" type="datetime" placeholder="选择结束时间" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="批次状态" prop="status">
|
||||
<el-select v-model="form.status" placeholder="请选择批次状态" style="width: 100%">
|
||||
<el-option label="草稿" value="draft" />
|
||||
<el-option label="进行中" value="active" />
|
||||
<el-option label="已完成" value="completed" />
|
||||
<el-option label="已取消" value="cancelled" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="排序序号" prop="sortOrder">
|
||||
<el-input-number v-model="form.sortOrder" :min="0" :max="9999" placeholder="排序序号" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="高级设置" name="advanced">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="自动提醒" prop="autoRemind">
|
||||
<el-switch v-model="form.autoRemind" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="提醒天数" prop="remindDays">
|
||||
<el-input-number v-model="form.remindDays" :min="1" :max="30" placeholder="提醒天数" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="24">
|
||||
<el-form-item label="批次描述" prop="description">
|
||||
<el-input v-model="form.description" type="textarea" :rows="4" placeholder="请输入批次描述" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="24">
|
||||
<el-form-item label="通知内容" prop="noticeContent">
|
||||
<el-input v-model="form.noticeContent" type="textarea" :rows="4" placeholder="请输入通知内容" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="24">
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="form.remark" type="textarea" :rows="3" placeholder="请输入备注" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="cancelForm">取 消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 查看详情对话框 -->
|
||||
<el-dialog title="批次详情" v-model="detailDialog.visible" width="800px" append-to-body>
|
||||
<el-descriptions :column="2" border>
|
||||
<el-descriptions-item label="批次编码">{{ detailForm.batchCode }}</el-descriptions-item>
|
||||
<el-descriptions-item label="批次名称">{{ detailForm.batchName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="批次类型">
|
||||
<el-tag :type="getBatchTypeTagType(detailForm.batchType)" size="small">
|
||||
{{ getBatchTypeText(detailForm.batchType) }}
|
||||
</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="目标范围">
|
||||
<el-tag :type="getTargetScopeTagType(detailForm.targetScope)" size="small">
|
||||
{{ getTargetScopeText(detailForm.targetScope) }}
|
||||
</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="关联学年">{{ detailForm.academicYearName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="关联学期">{{ detailForm.semesterName || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="开始时间">{{ proxy.parseTime(detailForm.startTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</el-descriptions-item>
|
||||
<el-descriptions-item label="结束时间">{{ proxy.parseTime(detailForm.endTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</el-descriptions-item>
|
||||
<el-descriptions-item label="批次状态">
|
||||
<el-tag :type="getStatusTagType(detailForm.status)" size="small">
|
||||
{{ getStatusText(detailForm.status) }}
|
||||
</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="发布状态">
|
||||
<el-tag :type="detailForm.isPublished ? 'success' : 'info'" size="small">
|
||||
{{ detailForm.isPublished ? '已发布' : '未发布' }}
|
||||
</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="完成进度">
|
||||
<el-progress :percentage="detailForm.completionRate" :stroke-width="6" />
|
||||
<div class="text-xs text-gray-500 mt-1">
|
||||
{{ detailForm.completedCount }}/{{ detailForm.totalCount }}
|
||||
</div>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="自动提醒">
|
||||
<el-tag :type="detailForm.autoRemind ? 'success' : 'info'" size="small">
|
||||
{{ detailForm.autoRemind ? '开启' : '关闭' }}
|
||||
</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="提醒天数">{{ detailForm.remindDays }}天</el-descriptions-item>
|
||||
<el-descriptions-item label="排序序号">{{ detailForm.sortOrder }}</el-descriptions-item>
|
||||
<el-descriptions-item label="创建时间">{{ proxy.parseTime(detailForm.createTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</el-descriptions-item>
|
||||
<el-descriptions-item label="更新时间">{{ proxy.parseTime(detailForm.updateTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</el-descriptions-item>
|
||||
<el-descriptions-item label="批次描述" :span="2">{{ detailForm.description || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="通知内容" :span="2">{{ detailForm.noticeContent || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="备注" :span="2">{{ detailForm.remark || '-' }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="detailDialog.visible = false">关 闭</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 复制批次对话框 -->
|
||||
<el-dialog title="复制批次" v-model="copyDialog.visible" width="500px" append-to-body>
|
||||
<el-form ref="copyFormRef" :model="copyForm" :rules="copyRules" label-width="120px">
|
||||
<el-form-item label="新批次编码" prop="batchCode">
|
||||
<el-input v-model="copyForm.batchCode" placeholder="请输入新批次编码" />
|
||||
</el-form-item>
|
||||
<el-form-item label="新批次名称" prop="batchName">
|
||||
<el-input v-model="copyForm.batchName" placeholder="请输入新批次名称" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button type="primary" @click="submitCopyForm">确 定</el-button>
|
||||
<el-button @click="copyDialog.visible = false">取 消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="CollectionBatch">
|
||||
// 组件导入和基本设置
|
||||
import { nextTick, onMounted, reactive, ref, computed } from 'vue';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import { getCurrentInstance } from 'vue';
|
||||
import {
|
||||
listCollectionBatch,
|
||||
getCollectionBatch,
|
||||
addCollectionBatch,
|
||||
updateCollectionBatch,
|
||||
delCollectionBatch,
|
||||
publishBatch,
|
||||
unpublishBatch,
|
||||
completeBatch,
|
||||
copyBatch,
|
||||
getBatchStatistics,
|
||||
updateBatchStatistics
|
||||
} from '@/api/xg-xx/collectionBatch';
|
||||
import { listAcademicYear } from '@/api/xg-xx/academicYear';
|
||||
import { listSemester } from '@/api/xg-xx/semester';
|
||||
import type { XgCollectionBatchVO, XgCollectionBatchForm } from '@/api/xg-xx/types';
|
||||
|
||||
const { proxy } = getCurrentInstance() as any;
|
||||
|
||||
// 响应式数据
|
||||
const collectionBatchList = ref<XgCollectionBatchVO[]>([]);
|
||||
const loading = ref(true);
|
||||
const showSearch = ref(true);
|
||||
const ids = ref<Array<string | number>>([]);
|
||||
const single = ref(true);
|
||||
const multiple = ref(true);
|
||||
const total = ref(0);
|
||||
const queryFormRef = ref();
|
||||
const collectionBatchFormRef = ref();
|
||||
const copyFormRef = ref();
|
||||
const activeTab = ref('basic');
|
||||
|
||||
// 选项数据
|
||||
const academicYearOptions = ref([]);
|
||||
const semesterOptions = ref([]);
|
||||
|
||||
// 查询参数
|
||||
const queryParams = ref({
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
batchCode: undefined,
|
||||
batchName: undefined,
|
||||
batchType: undefined,
|
||||
status: undefined,
|
||||
academicYearId: undefined,
|
||||
isPublished: undefined
|
||||
});
|
||||
|
||||
// 表单参数
|
||||
const form = ref<XgCollectionBatchForm>({});
|
||||
|
||||
// 详情表单
|
||||
const detailForm = ref<XgCollectionBatchVO>({});
|
||||
|
||||
// 复制表单
|
||||
const copyForm = ref({
|
||||
batchCode: '',
|
||||
batchName: ''
|
||||
});
|
||||
|
||||
// 当前复制的批次ID
|
||||
const copyBatchId = ref<string | number>('');
|
||||
|
||||
// 表单校验
|
||||
const rules = ref({
|
||||
batchCode: [{ required: true, message: '批次编码不能为空', trigger: 'blur' }],
|
||||
batchName: [{ required: true, message: '批次名称不能为空', trigger: 'blur' }],
|
||||
batchType: [{ required: true, message: '批次类型不能为空', trigger: 'change' }],
|
||||
targetScope: [{ required: true, message: '目标范围不能为空', trigger: 'change' }],
|
||||
academicYearId: [{ required: true, message: '关联学年不能为空', trigger: 'change' }],
|
||||
startTime: [{ required: true, message: '开始时间不能为空', trigger: 'change' }],
|
||||
endTime: [{ required: true, message: '结束时间不能为空', trigger: 'change' }],
|
||||
status: [{ required: true, message: '批次状态不能为空', trigger: 'change' }]
|
||||
});
|
||||
|
||||
// 复制表单校验
|
||||
const copyRules = ref({
|
||||
batchCode: [{ required: true, message: '批次编码不能为空', trigger: 'blur' }],
|
||||
batchName: [{ required: true, message: '批次名称不能为空', trigger: 'blur' }]
|
||||
});
|
||||
|
||||
// 对话框
|
||||
const dialog = reactive({
|
||||
visible: false,
|
||||
title: '',
|
||||
type: 'add'
|
||||
});
|
||||
|
||||
const detailDialog = reactive({
|
||||
visible: false
|
||||
});
|
||||
|
||||
const copyDialog = reactive({
|
||||
visible: false
|
||||
});
|
||||
|
||||
// 生命周期钩子
|
||||
onMounted(() => {
|
||||
getList();
|
||||
getAcademicYearOptions();
|
||||
getSemesterOptions();
|
||||
});
|
||||
|
||||
// 获取批次列表
|
||||
const getList = async () => {
|
||||
loading.value = true;
|
||||
try {
|
||||
const res = await listCollectionBatch(queryParams.value);
|
||||
collectionBatchList.value = res.rows;
|
||||
total.value = res.total;
|
||||
} catch (error) {
|
||||
console.error('获取批次列表失败:', error);
|
||||
ElMessage.error('获取批次列表失败,请稍后重试');
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 获取学年选项
|
||||
const getAcademicYearOptions = async () => {
|
||||
try {
|
||||
const res = await listAcademicYear({ pageNum: 1, pageSize: 1000 });
|
||||
academicYearOptions.value = res.rows.map(item => ({
|
||||
value: item.id,
|
||||
label: item.yearName
|
||||
}));
|
||||
} catch (error) {
|
||||
console.error('获取学年选项失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 获取学期选项
|
||||
const getSemesterOptions = async () => {
|
||||
try {
|
||||
const res = await listSemester({ pageNum: 1, pageSize: 1000 });
|
||||
semesterOptions.value = res.rows.map(item => ({
|
||||
value: item.id,
|
||||
label: item.semesterName
|
||||
}));
|
||||
} catch (error) {
|
||||
console.error('获取学期选项失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 搜索按钮操作
|
||||
const handleQuery = () => {
|
||||
queryParams.value.pageNum = 1;
|
||||
getList();
|
||||
};
|
||||
|
||||
// 重置按钮操作
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value?.resetFields();
|
||||
handleQuery();
|
||||
};
|
||||
|
||||
// 多选框选中数据
|
||||
const handleSelectionChange = (selection: XgCollectionBatchVO[]) => {
|
||||
ids.value = selection.map(item => item.id);
|
||||
single.value = selection.length !== 1;
|
||||
multiple.value = !selection.length;
|
||||
};
|
||||
|
||||
// 新增按钮操作
|
||||
const handleAdd = () => {
|
||||
reset();
|
||||
dialog.visible = true;
|
||||
dialog.title = '新增信息采集批次';
|
||||
dialog.type = 'add';
|
||||
};
|
||||
|
||||
// 修改按钮操作
|
||||
const handleUpdate = async (row?: XgCollectionBatchVO) => {
|
||||
reset();
|
||||
const batchId = row?.id || ids.value[0];
|
||||
try {
|
||||
const res = await getCollectionBatch(batchId);
|
||||
Object.assign(form.value, res.data);
|
||||
dialog.visible = true;
|
||||
dialog.title = '修改信息采集批次';
|
||||
dialog.type = 'edit';
|
||||
} catch (error) {
|
||||
console.error('获取批次详情失败:', error);
|
||||
ElMessage.error('获取批次详情失败,请稍后重试');
|
||||
}
|
||||
};
|
||||
|
||||
// 查看详情
|
||||
const handleView = async (row: XgCollectionBatchVO) => {
|
||||
try {
|
||||
const res = await getCollectionBatch(row.id);
|
||||
Object.assign(detailForm.value, res.data);
|
||||
detailDialog.visible = true;
|
||||
} catch (error) {
|
||||
console.error('获取批次详情失败:', error);
|
||||
ElMessage.error('获取批次详情失败,请稍后重试');
|
||||
}
|
||||
};
|
||||
|
||||
// 删除按钮操作
|
||||
const handleDelete = async (row?: XgCollectionBatchVO) => {
|
||||
const batchIds = row?.id || ids.value;
|
||||
let confirmMessage = '';
|
||||
|
||||
if (row) {
|
||||
confirmMessage = `是否确认删除批次"${row.batchName}"?`;
|
||||
} else {
|
||||
const selectedBatches = collectionBatchList.value.filter(item => ids.value.includes(item.id));
|
||||
if (selectedBatches.length === 1) {
|
||||
confirmMessage = `是否确认删除批次"${selectedBatches[0].batchName}"?`;
|
||||
} else {
|
||||
confirmMessage = `是否确认删除选中的${selectedBatches.length}个批次?`;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
await ElMessageBox.confirm(confirmMessage, '系统提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
});
|
||||
|
||||
await delCollectionBatch(batchIds);
|
||||
ElMessage.success('删除成功');
|
||||
await getList();
|
||||
} catch (error) {
|
||||
console.error('删除失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 发布按钮操作
|
||||
const handlePublish = async () => {
|
||||
const batchId = ids.value[0];
|
||||
const selectedBatch = collectionBatchList.value.find(item => item.id === batchId);
|
||||
|
||||
try {
|
||||
await ElMessageBox.confirm(`是否确认发布批次"${selectedBatch?.batchName}"?`, '系统提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
});
|
||||
|
||||
await publishBatch(batchId);
|
||||
ElMessage.success('发布成功');
|
||||
await getList();
|
||||
} catch (error) {
|
||||
console.error('发布失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 结束按钮操作
|
||||
const handleComplete = async () => {
|
||||
const batchId = ids.value[0];
|
||||
const selectedBatch = collectionBatchList.value.find(item => item.id === batchId);
|
||||
|
||||
try {
|
||||
await ElMessageBox.confirm(`是否确认结束批次"${selectedBatch?.batchName}"?`, '系统提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
});
|
||||
|
||||
await completeBatch(batchId);
|
||||
ElMessage.success('结束成功');
|
||||
await getList();
|
||||
} catch (error) {
|
||||
console.error('结束失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 复制按钮操作
|
||||
const handleCopy = (row: XgCollectionBatchVO) => {
|
||||
copyBatchId.value = row.id;
|
||||
copyForm.value = {
|
||||
batchCode: `${row.batchCode}_copy`,
|
||||
batchName: `${row.batchName}_复制`
|
||||
};
|
||||
copyDialog.visible = true;
|
||||
};
|
||||
|
||||
// 统计按钮操作
|
||||
const handleStatistics = async (row: XgCollectionBatchVO) => {
|
||||
try {
|
||||
// 更新统计信息
|
||||
await updateBatchStatistics(row.id);
|
||||
// 获取统计数据
|
||||
const res = await getBatchStatistics(row.id);
|
||||
|
||||
ElMessageBox.alert(`
|
||||
<div style="text-align: left;">
|
||||
<h4>批次统计信息</h4>
|
||||
<p>批次名称:${row.batchName}</p>
|
||||
<p>总人数:${res.data.totalCount}</p>
|
||||
<p>已完成:${res.data.completedCount}</p>
|
||||
<p>完成率:${res.data.completionRate}%</p>
|
||||
<p>更新时间:${proxy.parseTime(new Date(), '{y}-{m}-{d} {h}:{i}:{s}')}</p>
|
||||
</div>
|
||||
`, '统计信息', {
|
||||
dangerouslyUseHTMLString: true,
|
||||
type: 'info'
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('获取统计信息失败:', error);
|
||||
ElMessage.error('获取统计信息失败,请稍后重试');
|
||||
}
|
||||
};
|
||||
|
||||
// 导出按钮操作
|
||||
const handleExport = () => {
|
||||
proxy?.download(
|
||||
'xg/collectionBatch/export',
|
||||
{
|
||||
...queryParams.value
|
||||
},
|
||||
`信息采集批次_${proxy.parseTime(new Date(), '{y}{m}{d}{h}{i}{s}')}.xlsx`
|
||||
);
|
||||
};
|
||||
|
||||
// 提交表单
|
||||
const submitForm = async () => {
|
||||
if (!collectionBatchFormRef.value) return;
|
||||
|
||||
try {
|
||||
await collectionBatchFormRef.value.validate();
|
||||
|
||||
if (dialog.type === 'add') {
|
||||
await addCollectionBatch(form.value);
|
||||
ElMessage.success('新增成功');
|
||||
} else if (dialog.type === 'edit') {
|
||||
await updateCollectionBatch(form.value);
|
||||
ElMessage.success('修改成功');
|
||||
}
|
||||
|
||||
dialog.visible = false;
|
||||
await getList();
|
||||
} catch (error) {
|
||||
console.error('提交失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 提交复制表单
|
||||
const submitCopyForm = async () => {
|
||||
if (!copyFormRef.value) return;
|
||||
|
||||
try {
|
||||
await copyFormRef.value.validate();
|
||||
await copyBatch(copyBatchId.value, copyForm.value);
|
||||
ElMessage.success('复制成功');
|
||||
copyDialog.visible = false;
|
||||
await getList();
|
||||
} catch (error) {
|
||||
console.error('复制失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 取消表单
|
||||
const cancelForm = () => {
|
||||
dialog.visible = false;
|
||||
reset();
|
||||
};
|
||||
|
||||
// 表单重置
|
||||
const reset = () => {
|
||||
form.value = {
|
||||
batchCode: undefined,
|
||||
batchName: undefined,
|
||||
batchType: 'regular',
|
||||
targetScope: 'all',
|
||||
academicYearId: undefined,
|
||||
semesterId: undefined,
|
||||
startTime: undefined,
|
||||
endTime: undefined,
|
||||
status: 'draft',
|
||||
autoRemind: true,
|
||||
remindDays: 3,
|
||||
sortOrder: 0,
|
||||
description: undefined,
|
||||
noticeContent: undefined,
|
||||
remark: undefined
|
||||
};
|
||||
collectionBatchFormRef.value?.resetFields();
|
||||
};
|
||||
|
||||
// 获取批次类型标签类型
|
||||
const getBatchTypeTagType = (batchType: string) => {
|
||||
switch (batchType) {
|
||||
case 'regular':
|
||||
return '';
|
||||
case 'special':
|
||||
return 'warning';
|
||||
case 'urgent':
|
||||
return 'danger';
|
||||
default:
|
||||
return 'info';
|
||||
}
|
||||
};
|
||||
|
||||
// 获取批次类型文本
|
||||
const getBatchTypeText = (batchType: string) => {
|
||||
switch (batchType) {
|
||||
case 'regular':
|
||||
return '常规';
|
||||
case 'special':
|
||||
return '专项';
|
||||
case 'urgent':
|
||||
return '紧急';
|
||||
default:
|
||||
return '未知';
|
||||
}
|
||||
};
|
||||
|
||||
// 获取目标范围标签类型
|
||||
const getTargetScopeTagType = (targetScope: string) => {
|
||||
switch (targetScope) {
|
||||
case 'all':
|
||||
return 'success';
|
||||
case 'college':
|
||||
return 'warning';
|
||||
case 'class':
|
||||
return '';
|
||||
case 'student':
|
||||
return 'info';
|
||||
default:
|
||||
return 'info';
|
||||
}
|
||||
};
|
||||
|
||||
// 获取目标范围文本
|
||||
const getTargetScopeText = (targetScope: string) => {
|
||||
switch (targetScope) {
|
||||
case 'all':
|
||||
return '全部';
|
||||
case 'college':
|
||||
return '学院';
|
||||
case 'class':
|
||||
return '班级';
|
||||
case 'student':
|
||||
return '学生';
|
||||
default:
|
||||
return '未知';
|
||||
}
|
||||
};
|
||||
|
||||
// 获取状态标签类型
|
||||
const getStatusTagType = (status: string) => {
|
||||
switch (status) {
|
||||
case 'draft':
|
||||
return 'info';
|
||||
case 'active':
|
||||
return 'success';
|
||||
case 'completed':
|
||||
return '';
|
||||
case 'cancelled':
|
||||
return 'danger';
|
||||
default:
|
||||
return 'info';
|
||||
}
|
||||
};
|
||||
|
||||
// 获取状态文本
|
||||
const getStatusText = (status: string) => {
|
||||
switch (status) {
|
||||
case 'draft':
|
||||
return '草稿';
|
||||
case 'active':
|
||||
return '进行中';
|
||||
case 'completed':
|
||||
return '已完成';
|
||||
case 'cancelled':
|
||||
return '已取消';
|
||||
default:
|
||||
return '未知';
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 页面容器样式 */
|
||||
.page-container {
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
/* 表格卡片布局 */
|
||||
.table-card {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.table-card :deep(.el-card__body) {
|
||||
padding: 0px 20px 20px 20px !important;
|
||||
}
|
||||
|
||||
/* 移除按钮栏底部边框 */
|
||||
.button-card :deep(.el-card__header) {
|
||||
border-bottom: none !important;
|
||||
}
|
||||
|
||||
/* 分页容器 */
|
||||
.pagination-container {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
padding: 16px 20px 0 0 !important;
|
||||
background-color: #fff;
|
||||
margin-top: 0px !important;
|
||||
}
|
||||
|
||||
/* 操作按钮样式 */
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/* 对话框底部样式 */
|
||||
.dialog-footer {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
/* 表格容器 */
|
||||
.table-container {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
/* 滚动表格 */
|
||||
.scroll-table {
|
||||
overflow-x: auto;
|
||||
}
|
||||
</style>
|
||||
@@ -1,348 +0,0 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-form :model="queryParams" ref="queryFormRef" :inline="true" v-show="showSearch" label-width="68px">
|
||||
<el-form-item label="学年" prop="academicYearId">
|
||||
<el-select v-model="queryParams.academicYearId" placeholder="请选择学年" clearable>
|
||||
<el-option v-for="item in academicYearOptions" :key="item.id" :label="item.yearName" :value="item.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="学期编码" prop="semesterCode">
|
||||
<el-input v-model="queryParams.semesterCode" placeholder="请输入学期编码" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="学期名称" prop="semesterName">
|
||||
<el-input v-model="queryParams.semesterName" placeholder="请输入学期名称" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="学期类型" prop="semesterType">
|
||||
<el-select v-model="queryParams.semesterType" placeholder="请选择学期类型" clearable>
|
||||
<el-option label="春季学期" value="1" />
|
||||
<el-option label="秋季学期" value="2" />
|
||||
<el-option label="夏季学期" value="3" />
|
||||
<el-option label="短学期" value="4" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="学期状态" prop="semesterStatus">
|
||||
<el-select v-model="queryParams.semesterStatus" placeholder="请选择学期状态" clearable>
|
||||
<el-option label="启用" value="1" />
|
||||
<el-option label="停用" value="0" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="是否当前" prop="isCurrent">
|
||||
<el-select v-model="queryParams.isCurrent" placeholder="请选择是否当前学期" clearable>
|
||||
<el-option label="是" :value="true" />
|
||||
<el-option label="否" :value="false" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['xg:semester:add']">新增</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['xg:semester:edit']">修改</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['xg:semester:remove']">删除</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['xg:semester:export']">导出</el-button>
|
||||
</el-col>
|
||||
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
|
||||
<el-table v-loading="loading" :data="semesterList" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column label="学年名称" align="center" prop="academicYearName" />
|
||||
<el-table-column label="学期编码" align="center" prop="semesterCode" />
|
||||
<el-table-column label="学期名称" align="center" prop="semesterName" />
|
||||
<el-table-column label="学期类型" align="center" prop="semesterType">
|
||||
<template #default="scope">
|
||||
<dict-tag :options="semester_type" :value="scope.row.semesterType" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="开始日期" align="center" prop="startDate" width="180">
|
||||
<template #default="scope">
|
||||
<span>{{ parseTime(scope.row.startDate, '{y}-{m}-{d}') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="结束日期" align="center" prop="endDate" width="180">
|
||||
<template #default="scope">
|
||||
<span>{{ parseTime(scope.row.endDate, '{y}-{m}-{d}') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="是否当前" align="center" prop="isCurrent">
|
||||
<template #default="scope">
|
||||
<dict-tag :options="sys_yes_no" :value="scope.row.isCurrent" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="学期状态" align="center" prop="semesterStatus">
|
||||
<template #default="scope">
|
||||
<dict-tag :options="sys_normal_disable" :value="scope.row.semesterStatus" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="排序" align="center" prop="sortOrder" />
|
||||
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
|
||||
<template #default="scope">
|
||||
<span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d}') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template #default="scope">
|
||||
<el-tooltip content="修改" placement="top">
|
||||
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['xg:semester:edit']"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="删除" placement="top">
|
||||
<el-button link type="danger" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['xg:semester:remove']"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="设为当前" placement="top" v-if="!scope.row.isCurrent">
|
||||
<el-button link type="success" icon="Check" @click="handleSetCurrent(scope.row)" v-hasPermi="['xg:semester:edit']"></el-button>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
|
||||
|
||||
<!-- 添加或修改学期管理对话框 -->
|
||||
<el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
|
||||
<el-form ref="semesterFormRef" :model="form" :rules="rules" label-width="80px">
|
||||
<el-form-item label="学年" prop="academicYearId">
|
||||
<el-select v-model="form.academicYearId" placeholder="请选择学年" @change="handleAcademicYearChange">
|
||||
<el-option v-for="item in academicYearOptions" :key="item.id" :label="item.yearName" :value="item.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="学期编码" prop="semesterCode">
|
||||
<el-input v-model="form.semesterCode" placeholder="请输入学期编码" />
|
||||
</el-form-item>
|
||||
<el-form-item label="学期名称" prop="semesterName">
|
||||
<el-input v-model="form.semesterName" placeholder="请输入学期名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="学期类型" prop="semesterType">
|
||||
<el-select v-model="form.semesterType" placeholder="请选择学期类型">
|
||||
<el-option label="春季学期" value="1" />
|
||||
<el-option label="秋季学期" value="2" />
|
||||
<el-option label="夏季学期" value="3" />
|
||||
<el-option label="短学期" value="4" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="开始日期" prop="startDate">
|
||||
<el-date-picker clearable v-model="form.startDate" type="date" value-format="YYYY-MM-DD" placeholder="请选择开始日期" />
|
||||
</el-form-item>
|
||||
<el-form-item label="结束日期" prop="endDate">
|
||||
<el-date-picker clearable v-model="form.endDate" type="date" value-format="YYYY-MM-DD" placeholder="请选择结束日期" />
|
||||
</el-form-item>
|
||||
<el-form-item label="是否当前" prop="isCurrent">
|
||||
<el-radio-group v-model="form.isCurrent">
|
||||
<el-radio :value="true">是</el-radio>
|
||||
<el-radio :value="false">否</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="学期状态" prop="semesterStatus">
|
||||
<el-radio-group v-model="form.semesterStatus">
|
||||
<el-radio value="1">启用</el-radio>
|
||||
<el-radio value="0">停用</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="排序序号" prop="sortOrder">
|
||||
<el-input-number v-model="form.sortOrder" :min="0" />
|
||||
</el-form-item>
|
||||
<el-form-item label="学期描述" prop="description">
|
||||
<el-input v-model="form.description" type="textarea" placeholder="请输入学期描述" />
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="form.remark" type="textarea" placeholder="请输入备注" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="cancel">取 消</el-button>
|
||||
<el-button type="primary" @click="submitForm">确 定</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="Semester" lang="ts">
|
||||
import { listSemester, getSemester, delSemester, addSemester, updateSemester, setCurrentSemester, exportSemester } from '@/api/xg-xx/semester';
|
||||
import { getAvailableAcademicYearOptions } from '@/api/xg-xx/academicYear';
|
||||
import { SemesterVO, SemesterQuery, SemesterForm, AcademicYearVO } from '@/api/xg-xx/academicTypes';
|
||||
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
const { sys_yes_no, sys_normal_disable, semester_type } = proxy?.useDict('sys_yes_no', 'sys_normal_disable', 'semester_type');
|
||||
|
||||
const semesterList = ref<SemesterVO[]>([]);
|
||||
const academicYearOptions = ref<AcademicYearVO[]>([]);
|
||||
const loading = ref(true);
|
||||
const showSearch = ref(true);
|
||||
const ids = ref<Array<string | number>>([]);
|
||||
const single = ref(true);
|
||||
const multiple = ref(true);
|
||||
const total = ref(0);
|
||||
|
||||
const queryFormRef = ref<ElFormInstance>();
|
||||
const semesterFormRef = ref<ElFormInstance>();
|
||||
|
||||
const dialog = reactive<DialogOption>({
|
||||
visible: false,
|
||||
title: ''
|
||||
});
|
||||
|
||||
const initFormData: SemesterForm = {
|
||||
id: undefined,
|
||||
academicYearId: undefined,
|
||||
semesterCode: '',
|
||||
semesterName: '',
|
||||
semesterType: '',
|
||||
startDate: '',
|
||||
endDate: '',
|
||||
isCurrent: false,
|
||||
semesterStatus: '1',
|
||||
sortOrder: 0,
|
||||
description: '',
|
||||
remark: ''
|
||||
};
|
||||
|
||||
const data = reactive<PageData<SemesterForm, SemesterQuery>>({
|
||||
form: { ...initFormData },
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
academicYearId: undefined,
|
||||
semesterCode: '',
|
||||
semesterName: '',
|
||||
semesterType: '',
|
||||
semesterStatus: '',
|
||||
isCurrent: undefined,
|
||||
params: {}
|
||||
},
|
||||
rules: {
|
||||
academicYearId: [{ required: true, message: '学年不能为空', trigger: 'change' }],
|
||||
semesterCode: [{ required: true, message: '学期编码不能为空', trigger: 'blur' }],
|
||||
semesterName: [{ required: true, message: '学期名称不能为空', trigger: 'blur' }],
|
||||
semesterType: [{ required: true, message: '学期类型不能为空', trigger: 'change' }],
|
||||
startDate: [{ required: true, message: '开始日期不能为空', trigger: 'blur' }],
|
||||
endDate: [{ required: true, message: '结束日期不能为空', trigger: 'blur' }],
|
||||
semesterStatus: [{ required: true, message: '学期状态不能为空', trigger: 'change' }],
|
||||
sortOrder: [{ required: true, message: '排序序号不能为空', trigger: 'blur' }]
|
||||
}
|
||||
});
|
||||
|
||||
const { queryParams, form, rules } = toRefs(data);
|
||||
|
||||
/** 查询学期管理列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true;
|
||||
const res = await listSemester(queryParams.value);
|
||||
semesterList.value = res.rows;
|
||||
total.value = res.total;
|
||||
loading.value = false;
|
||||
};
|
||||
|
||||
/** 获取学年选项 */
|
||||
const getAcademicYearOptions = async () => {
|
||||
const res = await getAvailableAcademicYearOptions();
|
||||
academicYearOptions.value = res.data;
|
||||
};
|
||||
|
||||
/** 取消按钮 */
|
||||
const cancel = () => {
|
||||
reset();
|
||||
dialog.visible = false;
|
||||
};
|
||||
|
||||
/** 表单重置 */
|
||||
const reset = () => {
|
||||
form.value = { ...initFormData };
|
||||
semesterFormRef.value?.resetFields();
|
||||
};
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.value.pageNum = 1;
|
||||
getList();
|
||||
};
|
||||
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value?.resetFields();
|
||||
handleQuery();
|
||||
};
|
||||
|
||||
/** 多选框选中数据 */
|
||||
const handleSelectionChange = (selection: SemesterVO[]) => {
|
||||
ids.value = selection.map((item) => item.id);
|
||||
single.value = selection.length !== 1;
|
||||
multiple.value = !selection.length;
|
||||
};
|
||||
|
||||
/** 新增按钮操作 */
|
||||
const handleAdd = () => {
|
||||
reset();
|
||||
dialog.visible = true;
|
||||
dialog.title = '添加学期管理';
|
||||
};
|
||||
|
||||
/** 修改按钮操作 */
|
||||
const handleUpdate = async (row?: SemesterVO) => {
|
||||
reset();
|
||||
const _id = row?.id || ids.value[0];
|
||||
const res = await getSemester(_id);
|
||||
Object.assign(form.value, res.data);
|
||||
dialog.visible = true;
|
||||
dialog.title = '修改学期管理';
|
||||
};
|
||||
|
||||
/** 学年变化处理 */
|
||||
const handleAcademicYearChange = () => {
|
||||
// 可以根据学年自动填充一些默认值
|
||||
};
|
||||
|
||||
/** 提交按钮 */
|
||||
const submitForm = () => {
|
||||
semesterFormRef.value?.validate(async (valid: boolean) => {
|
||||
if (valid) {
|
||||
if (form.value.id) {
|
||||
await updateSemester(form.value);
|
||||
proxy?.$modal.msgSuccess('修改成功');
|
||||
} else {
|
||||
await addSemester(form.value);
|
||||
proxy?.$modal.msgSuccess('新增成功');
|
||||
}
|
||||
dialog.visible = false;
|
||||
await getList();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = async (row?: SemesterVO) => {
|
||||
const _ids = row?.id || ids.value;
|
||||
await proxy?.$modal.confirm('是否确认删除学期管理编号为"' + _ids + '"的数据项?');
|
||||
await delSemester(_ids);
|
||||
proxy?.$modal.msgSuccess('删除成功');
|
||||
await getList();
|
||||
};
|
||||
|
||||
/** 设为当前学期 */
|
||||
const handleSetCurrent = async (row: SemesterVO) => {
|
||||
await proxy?.$modal.confirm('是否确认设置"' + row.semesterName + '"为当前学期?');
|
||||
await setCurrentSemester(row.id);
|
||||
proxy?.$modal.msgSuccess('设置成功');
|
||||
await getList();
|
||||
};
|
||||
|
||||
/** 导出按钮操作 */
|
||||
const handleExport = () => {
|
||||
proxy?.$download.excel(exportSemester, queryParams.value, `学期管理_${new Date().getTime()}.xlsx`);
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getList();
|
||||
getAcademicYearOptions();
|
||||
});
|
||||
</script>
|
||||
@@ -1,774 +0,0 @@
|
||||
<template>
|
||||
<div class="p-2 page-container">
|
||||
<!-- 批次选择区域 -->
|
||||
<el-card shadow="hover" class="mb-4">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>批次管理</span>
|
||||
<div class="header-actions">
|
||||
<el-select v-model="selectedBatchId" placeholder="请选择批次" @change="handleBatchChange" style="width: 200px; margin-right: 10px;">
|
||||
<el-option v-for="batch in batchOptions" :key="batch.id" :label="batch.batchName" :value="batch.id" />
|
||||
</el-select>
|
||||
<el-button type="primary" icon="Refresh" @click="refreshBatchInfo">刷新</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- 批次信息展示 -->
|
||||
<div v-if="currentBatch" class="batch-info">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="6">
|
||||
<el-statistic title="批次名称" :value="currentBatch.batchName" />
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-statistic title="批次状态" :value="getBatchStatusText(currentBatch.status)" />
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-statistic title="总学生数" :value="batchStatistics.totalCount" />
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-statistic title="已提交数" :value="batchStatistics.submittedCount" />
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20" class="mt-4">
|
||||
<el-col :span="6">
|
||||
<el-statistic title="已通过数" :value="batchStatistics.approvedCount" />
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-statistic title="已拒绝数" :value="batchStatistics.rejectedCount" />
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-statistic title="完成率" :value="batchStatistics.completionRate" suffix="%" />
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<div class="progress-info">
|
||||
<el-progress :percentage="batchStatistics.completionRate" :stroke-width="8" />
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<!-- 搜索区域 -->
|
||||
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
|
||||
<div v-show="showSearch" class="mb-[10px]">
|
||||
<el-card shadow="hover">
|
||||
<el-form ref="queryFormRef" :model="queryParams" label-width="100px">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="6">
|
||||
<el-form-item label="学生姓名" prop="name">
|
||||
<el-input v-model="queryParams.name" placeholder="请输入学生姓名" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="学号" prop="studentNo">
|
||||
<el-input v-model="queryParams.studentNo" placeholder="请输入学号" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="班级" prop="className">
|
||||
<el-input v-model="queryParams.className" placeholder="请输入班级名称" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="提交状态" prop="submissionStatus">
|
||||
<el-select v-model="queryParams.submissionStatus" placeholder="请选择提交状态" clearable style="width: 100%">
|
||||
<el-option label="草稿" value="DRAFT" />
|
||||
<el-option label="已提交" value="SUBMITTED" />
|
||||
<el-option label="已通过" value="APPROVED" />
|
||||
<el-option label="已拒绝" value="REJECTED" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="6">
|
||||
<el-form-item label="学院" prop="collegeName">
|
||||
<el-input v-model="queryParams.collegeName" placeholder="请输入学院名称" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="18">
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</el-card>
|
||||
</div>
|
||||
</transition>
|
||||
|
||||
<!-- 主要内容区域 -->
|
||||
<el-card shadow="hover">
|
||||
<!-- 操作按钮区域 -->
|
||||
<div class="toolbar-wrapper">
|
||||
<div class="toolbar-left">
|
||||
<el-button type="primary" icon="Plus" @click="handleAssociate" :disabled="!selectedBatchId">
|
||||
关联学生到批次
|
||||
</el-button>
|
||||
<el-button type="success" icon="Select" :disabled="!multiple || ids.length === 0" @click="handleBatchAssociate">
|
||||
批量关联
|
||||
</el-button>
|
||||
<el-button type="warning" icon="Upload" :disabled="!multiple || ids.length === 0" @click="handleBatchSubmit">
|
||||
批量提交
|
||||
</el-button>
|
||||
<el-button type="info" icon="Download" :disabled="!multiple || ids.length === 0" @click="handleBatchWithdraw">
|
||||
批量撤回
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<div class="toolbar-right">
|
||||
<el-button type="danger" icon="Download" @click="handleExport">
|
||||
导出
|
||||
</el-button>
|
||||
<el-button type="info" icon="Refresh" @click="handleRefresh">
|
||||
刷新
|
||||
</el-button>
|
||||
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 表格区域 -->
|
||||
<div class="table-wrapper">
|
||||
<el-table
|
||||
v-loading="loading"
|
||||
:data="studentList"
|
||||
@selection-change="handleSelectionChange"
|
||||
@sort-change="handleSortChange"
|
||||
row-key="id"
|
||||
border
|
||||
stripe
|
||||
size="small"
|
||||
>
|
||||
<el-table-column type="selection" width="50" align="center" />
|
||||
<el-table-column label="序号" type="index" width="60" align="center" />
|
||||
<el-table-column label="学生姓名" prop="name" width="100" align="center" show-overflow-tooltip />
|
||||
<el-table-column label="学号" prop="studentNo" width="120" align="center" show-overflow-tooltip />
|
||||
<el-table-column label="班级" prop="className" width="120" align="center" show-overflow-tooltip />
|
||||
<el-table-column label="学院" prop="collegeName" width="120" align="center" show-overflow-tooltip />
|
||||
<el-table-column label="专业" prop="majorName" width="120" align="center" show-overflow-tooltip />
|
||||
<el-table-column label="联系电话" prop="phone" width="120" align="center" show-overflow-tooltip />
|
||||
<el-table-column label="提交状态" prop="submissionStatus" width="100" align="center">
|
||||
<template #default="scope">
|
||||
<el-tag :type="getSubmissionStatusTagType(scope.row.submissionStatus)" size="small">
|
||||
{{ getSubmissionStatusText(scope.row.submissionStatus) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="提交时间" prop="submissionTime" width="160" align="center" sortable="custom">
|
||||
<template #default="scope">
|
||||
<span>{{ scope.row.submissionTime ? proxy.parseTime(scope.row.submissionTime, '{y}-{m}-{d} {h}:{i}') : '-' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="审核状态" prop="approvalStatus" width="100" align="center">
|
||||
<template #default="scope">
|
||||
<el-tag :type="getApprovalStatusTagType(scope.row.approvalStatus)" size="small">
|
||||
{{ getApprovalStatusText(scope.row.approvalStatus) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" width="200" fixed="right" class-name="small-padding fixed-width">
|
||||
<template #default="scope">
|
||||
<div class="action-buttons">
|
||||
<el-tooltip content="查看详情" placement="top">
|
||||
<el-button link type="info" icon="View" size="small" @click="handleView(scope.row)" />
|
||||
</el-tooltip>
|
||||
<el-tooltip content="编辑" placement="top">
|
||||
<el-button link type="primary" icon="Edit" size="small" @click="handleEdit(scope.row)" />
|
||||
</el-tooltip>
|
||||
<el-tooltip content="查看进度" placement="top">
|
||||
<el-button link type="warning" icon="DataAnalysis" size="small" @click="handleProgress(scope.row)" />
|
||||
</el-tooltip>
|
||||
<template v-if="scope.row.submissionStatus === 'DRAFT'">
|
||||
<el-tooltip content="提交" placement="top">
|
||||
<el-button link type="success" icon="Upload" size="small" @click="handleSubmit(scope.row)" />
|
||||
</el-tooltip>
|
||||
</template>
|
||||
<template v-else-if="scope.row.submissionStatus === 'SUBMITTED'">
|
||||
<el-tooltip content="撤回" placement="top">
|
||||
<el-button link type="danger" icon="Download" size="small" @click="handleWithdraw(scope.row)" />
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<div class="pagination-container">
|
||||
<pagination v-show="total > 0" :total="total" :page="queryParams.pageNum" :limit="queryParams.pageSize" @pagination="getList" />
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<!-- 关联学生到批次对话框 -->
|
||||
<el-dialog title="关联学生到批次" v-model="associateDialog.visible" width="600px" append-to-body>
|
||||
<el-form ref="associateFormRef" :model="associateForm" :rules="associateRules" label-width="100px">
|
||||
<el-form-item label="选择批次" prop="batchId">
|
||||
<el-select v-model="associateForm.batchId" placeholder="请选择批次" style="width: 100%">
|
||||
<el-option v-for="batch in batchOptions" :key="batch.id" :label="batch.batchName" :value="batch.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="选择学生" prop="studentIds">
|
||||
<el-select v-model="associateForm.studentIds" multiple placeholder="请选择学生" style="width: 100%">
|
||||
<el-option v-for="student in allStudents" :key="student.id" :label="`${student.name}(${student.studentNo})`" :value="student.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="associateDialog.visible = false">取消</el-button>
|
||||
<el-button type="primary" @click="submitAssociate">确定</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 学生进度对话框 -->
|
||||
<el-dialog title="学生批次进度" v-model="progressDialog.visible" width="800px" append-to-body>
|
||||
<div v-if="progressInfo" class="progress-content">
|
||||
<el-descriptions :column="2" border>
|
||||
<el-descriptions-item label="学生姓名">{{ progressInfo.studentName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="学号">{{ progressInfo.studentNo }}</el-descriptions-item>
|
||||
<el-descriptions-item label="批次名称">{{ progressInfo.batchName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="提交状态">
|
||||
<el-tag :type="getSubmissionStatusTagType(progressInfo.submissionStatus)">
|
||||
{{ getSubmissionStatusText(progressInfo.submissionStatus) }}
|
||||
</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="审核状态">
|
||||
<el-tag :type="getApprovalStatusTagType(progressInfo.approvalStatus)">
|
||||
{{ getApprovalStatusText(progressInfo.approvalStatus) }}
|
||||
</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="提交时间">
|
||||
{{ progressInfo.submissionTime ? proxy.parseTime(progressInfo.submissionTime, '{y}-{m}-{d} {h}:{i}:{s}') : '-' }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="审核时间">
|
||||
{{ progressInfo.approvalTime ? proxy.parseTime(progressInfo.approvalTime, '{y}-{m}-{d} {h}:{i}:{s}') : '-' }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="审核意见" :span="2">
|
||||
{{ progressInfo.approvalComments || '-' }}
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, onMounted, getCurrentInstance } from 'vue';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import { listAllCollectionBatch } from '@/api/xg-xx/collectionBatch';
|
||||
import {
|
||||
getStudentsByBatch,
|
||||
associateStudentToBatch,
|
||||
batchAssociateStudentsToBatch,
|
||||
submitStudentInfo,
|
||||
withdrawStudentInfo,
|
||||
getStudentBatchProgress,
|
||||
batchAuditStudentInfo,
|
||||
checkEditPermission,
|
||||
getStudentBatchStatistics,
|
||||
batchSubmitStudentInfo,
|
||||
batchWithdrawStudentInfo,
|
||||
getAllStudentOptions
|
||||
} from '@/api/xg-xx/studentInfo';
|
||||
|
||||
const { proxy } = getCurrentInstance() as any;
|
||||
|
||||
// 响应式数据
|
||||
const loading = ref(false);
|
||||
const showSearch = ref(true);
|
||||
const ids = ref<number[]>([]);
|
||||
const single = ref(true);
|
||||
const multiple = ref(true);
|
||||
const total = ref(0);
|
||||
const studentList = ref<any[]>([]);
|
||||
const batchOptions = ref<any[]>([]);
|
||||
const allStudents = ref<any[]>([]);
|
||||
const selectedBatchId = ref<number>();
|
||||
const currentBatch = ref<any>();
|
||||
|
||||
// 批次统计信息
|
||||
const batchStatistics = reactive({
|
||||
totalCount: 0,
|
||||
submittedCount: 0,
|
||||
approvedCount: 0,
|
||||
rejectedCount: 0,
|
||||
completionRate: 0
|
||||
});
|
||||
|
||||
// 查询参数
|
||||
const queryParams = reactive({
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
batchId: undefined as number | undefined,
|
||||
name: undefined,
|
||||
studentNo: undefined,
|
||||
className: undefined,
|
||||
submissionStatus: undefined,
|
||||
collegeName: undefined
|
||||
});
|
||||
|
||||
// 关联对话框
|
||||
const associateDialog = reactive({
|
||||
visible: false
|
||||
});
|
||||
|
||||
const associateForm = reactive({
|
||||
batchId: undefined as number | undefined,
|
||||
studentIds: [] as number[]
|
||||
});
|
||||
|
||||
const associateRules = {
|
||||
batchId: [{ required: true, message: '请选择批次', trigger: 'change' }],
|
||||
studentIds: [{ required: true, message: '请选择学生', trigger: 'change' }]
|
||||
};
|
||||
|
||||
// 进度对话框
|
||||
const progressDialog = reactive({
|
||||
visible: false
|
||||
});
|
||||
|
||||
const progressInfo = ref<any>();
|
||||
|
||||
// 获取列表数据
|
||||
const getList = async () => {
|
||||
if (!selectedBatchId.value) {
|
||||
studentList.value = [];
|
||||
total.value = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
loading.value = true;
|
||||
try {
|
||||
const response = await getStudentsByBatch(selectedBatchId.value, queryParams);
|
||||
studentList.value = response.rows;
|
||||
total.value = response.total;
|
||||
} catch (error) {
|
||||
console.error('获取列表失败:', error);
|
||||
ElMessage.error('获取学生列表失败');
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 获取批次选项
|
||||
const getBatchOptions = async () => {
|
||||
try {
|
||||
const response = await listAllCollectionBatch({});
|
||||
batchOptions.value = response.data;
|
||||
} catch (error) {
|
||||
console.error('获取批次选项失败:', error);
|
||||
ElMessage.error('获取批次选项失败');
|
||||
}
|
||||
};
|
||||
|
||||
// 获取所有学生选项
|
||||
const getAllStudentsList = async () => {
|
||||
try {
|
||||
const response = await getAllStudentOptions();
|
||||
allStudents.value = response.data;
|
||||
} catch (error) {
|
||||
console.error('获取学生选项失败:', error);
|
||||
ElMessage.error('获取学生选项失败');
|
||||
}
|
||||
};
|
||||
|
||||
// 批次改变处理
|
||||
const handleBatchChange = (batchId: number) => {
|
||||
selectedBatchId.value = batchId;
|
||||
queryParams.batchId = batchId;
|
||||
currentBatch.value = batchOptions.value.find(batch => batch.id === batchId);
|
||||
|
||||
// 获取批次统计信息
|
||||
getBatchStatistics();
|
||||
|
||||
// 刷新列表
|
||||
getList();
|
||||
};
|
||||
|
||||
// 获取批次统计信息
|
||||
const getBatchStatistics = async () => {
|
||||
if (!selectedBatchId.value) return;
|
||||
|
||||
try {
|
||||
const response = await getStudentBatchStatistics(selectedBatchId.value);
|
||||
Object.assign(batchStatistics, response.data);
|
||||
} catch (error) {
|
||||
console.error('获取批次统计失败:', error);
|
||||
ElMessage.error('获取批次统计失败');
|
||||
}
|
||||
};
|
||||
|
||||
// 刷新批次信息
|
||||
const refreshBatchInfo = () => {
|
||||
getBatchStatistics();
|
||||
getList();
|
||||
};
|
||||
|
||||
// 搜索
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNum = 1;
|
||||
getList();
|
||||
};
|
||||
|
||||
// 重置搜索
|
||||
const resetQuery = () => {
|
||||
proxy.resetForm('queryFormRef');
|
||||
handleQuery();
|
||||
};
|
||||
|
||||
// 多选框选中数据
|
||||
const handleSelectionChange = (selection: any[]) => {
|
||||
ids.value = selection.map(item => item.id);
|
||||
single.value = selection.length !== 1;
|
||||
multiple.value = !selection.length;
|
||||
};
|
||||
|
||||
// 排序变化
|
||||
const handleSortChange = (column: any) => {
|
||||
getList();
|
||||
};
|
||||
|
||||
// 关联学生到批次
|
||||
const handleAssociate = () => {
|
||||
associateDialog.visible = true;
|
||||
associateForm.batchId = selectedBatchId.value;
|
||||
associateForm.studentIds = [];
|
||||
|
||||
// 获取所有学生选项
|
||||
getAllStudentsList();
|
||||
};
|
||||
|
||||
// 批量关联
|
||||
const handleBatchAssociate = () => {
|
||||
if (ids.value.length === 0) {
|
||||
ElMessage.warning('请选择要关联的学生');
|
||||
return;
|
||||
}
|
||||
|
||||
associateDialog.visible = true;
|
||||
associateForm.batchId = selectedBatchId.value;
|
||||
associateForm.studentIds = ids.value;
|
||||
|
||||
// 获取所有学生选项
|
||||
getAllStudentsList();
|
||||
};
|
||||
|
||||
// 提交关联
|
||||
const submitAssociate = async () => {
|
||||
if (!associateForm.batchId || associateForm.studentIds.length === 0) {
|
||||
ElMessage.warning('请选择批次和学生');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await batchAssociateStudentsToBatch(associateForm.studentIds, associateForm.batchId);
|
||||
|
||||
ElMessage.success('关联成功');
|
||||
associateDialog.visible = false;
|
||||
|
||||
// 刷新统计信息和列表
|
||||
getBatchStatistics();
|
||||
getList();
|
||||
} catch (error) {
|
||||
console.error('关联失败:', error);
|
||||
ElMessage.error('关联失败');
|
||||
}
|
||||
};
|
||||
|
||||
// 批量提交
|
||||
const handleBatchSubmit = async () => {
|
||||
if (ids.value.length === 0) {
|
||||
ElMessage.warning('请选择要提交的学生');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!selectedBatchId.value) {
|
||||
ElMessage.warning('请先选择批次');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await ElMessageBox.confirm('确定要批量提交选中的学生信息吗?', '系统提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
});
|
||||
|
||||
await batchSubmitStudentInfo(ids.value, selectedBatchId.value);
|
||||
|
||||
ElMessage.success('批量提交成功');
|
||||
|
||||
// 刷新统计信息和列表
|
||||
getBatchStatistics();
|
||||
getList();
|
||||
} catch (error) {
|
||||
if (error !== 'cancel') {
|
||||
console.error('批量提交失败:', error);
|
||||
ElMessage.error('批量提交失败');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 批量撤回
|
||||
const handleBatchWithdraw = async () => {
|
||||
if (ids.value.length === 0) {
|
||||
ElMessage.warning('请选择要撤回的学生');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!selectedBatchId.value) {
|
||||
ElMessage.warning('请先选择批次');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await ElMessageBox.confirm('确定要批量撤回选中的学生信息吗?', '系统提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
});
|
||||
|
||||
await batchWithdrawStudentInfo(ids.value, selectedBatchId.value);
|
||||
|
||||
ElMessage.success('批量撤回成功');
|
||||
|
||||
// 刷新统计信息和列表
|
||||
getBatchStatistics();
|
||||
getList();
|
||||
} catch (error) {
|
||||
if (error !== 'cancel') {
|
||||
console.error('批量撤回失败:', error);
|
||||
ElMessage.error('批量撤回失败');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 查看详情
|
||||
const handleView = (row: any) => {
|
||||
proxy.$router.push(`/xg-xx/studentInfo/detail/${row.id}`);
|
||||
};
|
||||
|
||||
// 编辑
|
||||
const handleEdit = (row: any) => {
|
||||
proxy.$router.push(`/xg-xx/studentInfo/edit/${row.id}`);
|
||||
};
|
||||
|
||||
// 提交
|
||||
const handleSubmit = async (row: any) => {
|
||||
if (!selectedBatchId.value) {
|
||||
ElMessage.warning('请先选择批次');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await ElMessageBox.confirm('确定要提交该学生信息吗?', '系统提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
});
|
||||
|
||||
await submitStudentInfo(row.id, selectedBatchId.value);
|
||||
|
||||
ElMessage.success('提交成功');
|
||||
|
||||
// 刷新统计信息和列表
|
||||
getBatchStatistics();
|
||||
getList();
|
||||
} catch (error) {
|
||||
if (error !== 'cancel') {
|
||||
console.error('提交失败:', error);
|
||||
ElMessage.error('提交失败');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 撤回
|
||||
const handleWithdraw = async (row: any) => {
|
||||
if (!selectedBatchId.value) {
|
||||
ElMessage.warning('请先选择批次');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await ElMessageBox.confirm('确定要撤回该学生信息吗?', '系统提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
});
|
||||
|
||||
await withdrawStudentInfo(row.id, selectedBatchId.value);
|
||||
|
||||
ElMessage.success('撤回成功');
|
||||
|
||||
// 刷新统计信息和列表
|
||||
getBatchStatistics();
|
||||
getList();
|
||||
} catch (error) {
|
||||
if (error !== 'cancel') {
|
||||
console.error('撤回失败:', error);
|
||||
ElMessage.error('撤回失败');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 查看进度
|
||||
const handleProgress = async (row: any) => {
|
||||
if (!selectedBatchId.value) {
|
||||
ElMessage.warning('请先选择批次');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await getStudentBatchProgress(row.id, selectedBatchId.value);
|
||||
progressInfo.value = response.data;
|
||||
progressDialog.visible = true;
|
||||
} catch (error) {
|
||||
console.error('获取进度失败:', error);
|
||||
ElMessage.error('获取进度失败');
|
||||
}
|
||||
};
|
||||
|
||||
// 导出
|
||||
const handleExport = () => {
|
||||
proxy?.download(
|
||||
'xg/studentInfo/export',
|
||||
queryParams,
|
||||
`学生信息_${proxy.parseTime(new Date(), '{y}{m}{d}{h}{i}{s}')}.xlsx`
|
||||
);
|
||||
};
|
||||
|
||||
// 刷新
|
||||
const handleRefresh = () => {
|
||||
getList();
|
||||
};
|
||||
|
||||
// 获取批次状态文本
|
||||
const getBatchStatusText = (status: string) => {
|
||||
const statusMap: Record<string, string> = {
|
||||
'DRAFT': '草稿',
|
||||
'ACTIVE': '进行中',
|
||||
'COMPLETED': '已完成',
|
||||
'CANCELLED': '已取消'
|
||||
};
|
||||
return statusMap[status] || '未知';
|
||||
};
|
||||
|
||||
// 获取提交状态标签类型
|
||||
const getSubmissionStatusTagType = (status: string): 'success' | 'warning' | 'info' | 'primary' | 'danger' => {
|
||||
const statusMap: Record<string, 'success' | 'warning' | 'info' | 'primary' | 'danger'> = {
|
||||
'DRAFT': 'info',
|
||||
'SUBMITTED': 'warning',
|
||||
'APPROVED': 'success',
|
||||
'REJECTED': 'danger'
|
||||
};
|
||||
return statusMap[status] || 'info';
|
||||
};
|
||||
|
||||
// 获取提交状态文本
|
||||
const getSubmissionStatusText = (status: string) => {
|
||||
const statusMap: Record<string, string> = {
|
||||
'DRAFT': '草稿',
|
||||
'SUBMITTED': '已提交',
|
||||
'APPROVED': '已通过',
|
||||
'REJECTED': '已拒绝'
|
||||
};
|
||||
return statusMap[status] || '未知';
|
||||
};
|
||||
|
||||
// 获取审核状态标签类型
|
||||
const getApprovalStatusTagType = (status: string): 'success' | 'warning' | 'info' | 'primary' | 'danger' => {
|
||||
const statusMap: Record<string, 'success' | 'warning' | 'info' | 'primary' | 'danger'> = {
|
||||
'PENDING': 'warning',
|
||||
'APPROVED': 'success',
|
||||
'REJECTED': 'danger'
|
||||
};
|
||||
return statusMap[status] || 'info';
|
||||
};
|
||||
|
||||
// 获取审核状态文本
|
||||
const getApprovalStatusText = (status: string) => {
|
||||
const statusMap: Record<string, string> = {
|
||||
'PENDING': '待审核',
|
||||
'APPROVED': '已通过',
|
||||
'REJECTED': '已拒绝'
|
||||
};
|
||||
return statusMap[status] || '未知';
|
||||
};
|
||||
|
||||
// 初始化
|
||||
onMounted(() => {
|
||||
getBatchOptions();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.page-container {
|
||||
background-color: #f5f5f5;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.header-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.batch-info {
|
||||
padding: 16px 0;
|
||||
}
|
||||
|
||||
.toolbar-wrapper {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.toolbar-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.toolbar-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.table-wrapper {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.pagination-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.progress-info {
|
||||
padding: 8px 0;
|
||||
}
|
||||
|
||||
.progress-content {
|
||||
padding: 16px 0;
|
||||
}
|
||||
|
||||
.mt-4 {
|
||||
margin-top: 16px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,779 +0,0 @@
|
||||
<template>
|
||||
<div class="batch-management-container">
|
||||
<!-- 搜索区域 -->
|
||||
<el-card shadow="hover" class="mb-4">
|
||||
<el-form ref="queryFormRef" :model="queryParams" label-width="100px">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="6">
|
||||
<el-form-item label="批次名称" prop="batchName">
|
||||
<el-input v-model="queryParams.batchName" placeholder="请输入批次名称" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="批次类型" prop="batchType">
|
||||
<el-select v-model="queryParams.batchType" placeholder="请选择批次类型" clearable style="width: 100%">
|
||||
<el-option label="常规批次" value="regular" />
|
||||
<el-option label="专项批次" value="special" />
|
||||
<el-option label="紧急批次" value="urgent" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="批次状态" prop="status">
|
||||
<el-select v-model="queryParams.status" placeholder="请选择批次状态" clearable style="width: 100%">
|
||||
<el-option label="草稿" value="DRAFT" />
|
||||
<el-option label="活跃" value="ACTIVE" />
|
||||
<el-option label="已完成" value="COMPLETED" />
|
||||
<el-option label="已取消" value="CANCELLED" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</el-card>
|
||||
|
||||
<!-- 主要内容区域 -->
|
||||
<el-card shadow="hover">
|
||||
<!-- 操作按钮区域 -->
|
||||
<div class="toolbar-wrapper">
|
||||
<div class="toolbar-left">
|
||||
<el-button type="primary" icon="Plus" @click="handleAdd">
|
||||
新增批次
|
||||
</el-button>
|
||||
<el-button type="success" icon="Upload" :disabled="!multiple || ids.length === 0" @click="handleBatchPublish">
|
||||
批量发布
|
||||
</el-button>
|
||||
<el-button type="warning" icon="Download" :disabled="!multiple || ids.length === 0" @click="handleBatchUnpublish">
|
||||
批量取消发布
|
||||
</el-button>
|
||||
<el-button type="danger" icon="Delete" :disabled="!multiple || ids.length === 0" @click="handleDelete">
|
||||
批量删除
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<div class="toolbar-right">
|
||||
<el-button type="primary" icon="Download" @click="handleExport">
|
||||
导出
|
||||
</el-button>
|
||||
<el-button type="info" icon="Refresh" @click="handleRefresh">
|
||||
刷新
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 表格区域 -->
|
||||
<div class="table-wrapper">
|
||||
<el-table
|
||||
v-loading="loading"
|
||||
:data="batchList"
|
||||
@selection-change="handleSelectionChange"
|
||||
@sort-change="handleSortChange"
|
||||
row-key="id"
|
||||
border
|
||||
stripe
|
||||
size="small"
|
||||
>
|
||||
<el-table-column type="selection" width="50" align="center" />
|
||||
<el-table-column label="序号" type="index" width="60" align="center" />
|
||||
<el-table-column label="批次名称" prop="batchName" width="150" align="center" show-overflow-tooltip />
|
||||
<el-table-column label="批次编码" prop="batchCode" width="120" align="center" show-overflow-tooltip />
|
||||
<el-table-column label="批次类型" prop="batchType" width="100" align="center">
|
||||
<template #default="scope">
|
||||
<el-tag :type="getBatchTypeTagType(scope.row.batchType)" size="small">
|
||||
{{ getBatchTypeText(scope.row.batchType) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" prop="status" width="100" align="center">
|
||||
<template #default="scope">
|
||||
<el-tag :type="getStatusTagType(scope.row.status)" size="small">
|
||||
{{ getStatusText(scope.row.status) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="发布状态" prop="isPublished" width="100" align="center">
|
||||
<template #default="scope">
|
||||
<el-tag :type="scope.row.isPublished ? 'success' : 'info'" size="small">
|
||||
{{ scope.row.isPublished ? '已发布' : '未发布' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="开始时间" prop="startTime" width="160" align="center" sortable="custom">
|
||||
<template #default="scope">
|
||||
<span>{{ scope.row.startTime ? parseTime(scope.row.startTime, '{y}-{m}-{d} {h}:{i}') : '-' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="结束时间" prop="endTime" width="160" align="center" sortable="custom">
|
||||
<template #default="scope">
|
||||
<span>{{ scope.row.endTime ? parseTime(scope.row.endTime, '{y}-{m}-{d} {h}:{i}') : '-' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="总人数" prop="totalCount" width="80" align="center" />
|
||||
<el-table-column label="完成率" prop="completionRate" width="100" align="center">
|
||||
<template #default="scope">
|
||||
<el-progress :percentage="scope.row.completionRate" :stroke-width="6" :show-text="false" />
|
||||
<span class="ml-2">{{ scope.row.completionRate }}%</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" width="200" fixed="right" class-name="small-padding fixed-width">
|
||||
<template #default="scope">
|
||||
<div class="action-buttons">
|
||||
<el-tooltip content="查看详情" placement="top">
|
||||
<el-button link type="info" icon="View" size="small" @click="handleView(scope.row)" />
|
||||
</el-tooltip>
|
||||
<el-tooltip content="编辑" placement="top">
|
||||
<el-button link type="primary" icon="Edit" size="small" @click="handleEdit(scope.row)" />
|
||||
</el-tooltip>
|
||||
<el-tooltip content="统计" placement="top">
|
||||
<el-button link type="warning" icon="DataAnalysis" size="small" @click="handleStatistics(scope.row)" />
|
||||
</el-tooltip>
|
||||
<template v-if="!scope.row.isPublished">
|
||||
<el-tooltip content="发布" placement="top">
|
||||
<el-button link type="success" icon="Upload" size="small" @click="handlePublish(scope.row)" />
|
||||
</el-tooltip>
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-tooltip content="取消发布" placement="top">
|
||||
<el-button link type="danger" icon="Download" size="small" @click="handleUnpublish(scope.row)" />
|
||||
</el-tooltip>
|
||||
</template>
|
||||
<el-tooltip content="复制" placement="top">
|
||||
<el-button link type="info" icon="CopyDocument" size="small" @click="handleCopy(scope.row)" />
|
||||
</el-tooltip>
|
||||
<el-tooltip content="删除" placement="top">
|
||||
<el-button link type="danger" icon="Delete" size="small" @click="handleDelete(scope.row)" />
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<div class="pagination-container">
|
||||
<el-pagination
|
||||
v-show="total > 0"
|
||||
:total="total"
|
||||
:page-size="queryParams.pageSize"
|
||||
:current-page="queryParams.pageNum"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<!-- 批次详情对话框 -->
|
||||
<el-dialog
|
||||
:title="dialogTitle"
|
||||
v-model="batchDialog.visible"
|
||||
width="800px"
|
||||
append-to-body
|
||||
>
|
||||
<el-form ref="batchFormRef" :model="batchForm" :rules="batchRules" label-width="120px">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="批次名称" prop="batchName">
|
||||
<el-input v-model="batchForm.batchName" placeholder="请输入批次名称" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="批次编码" prop="batchCode">
|
||||
<el-input v-model="batchForm.batchCode" placeholder="请输入批次编码" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="批次类型" prop="batchType">
|
||||
<el-select v-model="batchForm.batchType" placeholder="请选择批次类型" style="width: 100%">
|
||||
<el-option label="常规批次" value="regular" />
|
||||
<el-option label="专项批次" value="special" />
|
||||
<el-option label="紧急批次" value="urgent" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="目标范围" prop="targetScope">
|
||||
<el-select v-model="batchForm.targetScope" placeholder="请选择目标范围" style="width: 100%">
|
||||
<el-option label="全部学生" value="all" />
|
||||
<el-option label="按学院" value="college" />
|
||||
<el-option label="按班级" value="class" />
|
||||
<el-option label="按学生" value="student" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="开始时间" prop="startTime">
|
||||
<el-date-picker
|
||||
v-model="batchForm.startTime"
|
||||
type="datetime"
|
||||
placeholder="请选择开始时间"
|
||||
format="YYYY-MM-DD HH:mm"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="结束时间" prop="endTime">
|
||||
<el-date-picker
|
||||
v-model="batchForm.endTime"
|
||||
type="datetime"
|
||||
placeholder="请选择结束时间"
|
||||
format="YYYY-MM-DD HH:mm"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="自动提醒" prop="autoRemind">
|
||||
<el-switch v-model="batchForm.autoRemind" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="提醒天数" prop="remindDays">
|
||||
<el-input-number v-model="batchForm.remindDays" :min="1" :max="30" placeholder="结束前几天提醒" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-form-item label="批次描述" prop="description">
|
||||
<el-input v-model="batchForm.description" type="textarea" :rows="3" placeholder="请输入批次描述" />
|
||||
</el-form-item>
|
||||
<el-form-item label="通知内容" prop="noticeContent">
|
||||
<el-input v-model="batchForm.noticeContent" type="textarea" :rows="4" placeholder="请输入通知内容" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="batchDialog.visible = false">取消</el-button>
|
||||
<el-button type="primary" @click="submitBatch">确定</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 批次统计对话框 -->
|
||||
<el-dialog title="批次统计" v-model="statisticsDialog.visible" width="900px" append-to-body>
|
||||
<div v-if="statisticsInfo" class="statistics-content">
|
||||
<el-row :gutter="20" class="mb-4">
|
||||
<el-col :span="6">
|
||||
<el-statistic title="总人数" :value="statisticsInfo.totalCount" />
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-statistic title="已提交" :value="statisticsInfo.submittedCount" />
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-statistic title="已通过" :value="statisticsInfo.approvedCount" />
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-statistic title="完成率" :value="statisticsInfo.completionRate" suffix="%" />
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-progress :percentage="statisticsInfo.completionRate" :stroke-width="12" />
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<div class="statistics-chart">
|
||||
<!-- 这里可以集成图表组件 -->
|
||||
<p>统计图表区域</p>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, onMounted, getCurrentInstance, computed } from 'vue';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import { parseTime } from '@/utils/ruoyi';
|
||||
import {
|
||||
listCollectionBatch,
|
||||
getCollectionBatch,
|
||||
addCollectionBatch,
|
||||
updateCollectionBatch,
|
||||
delCollectionBatch,
|
||||
publishCollectionBatch,
|
||||
unpublishCollectionBatch,
|
||||
getCollectionBatchStatistics,
|
||||
copyCollectionBatch
|
||||
} from '@/api/xg-xx/collectionBatch';
|
||||
|
||||
const { proxy } = getCurrentInstance() as any;
|
||||
|
||||
// 定义 emit
|
||||
const emit = defineEmits(['batch-created', 'batch-updated', 'batch-deleted']);
|
||||
|
||||
// 响应式数据
|
||||
const loading = ref(false);
|
||||
const ids = ref<number[]>([]);
|
||||
const single = ref(true);
|
||||
const multiple = ref(true);
|
||||
const total = ref(0);
|
||||
const batchList = ref<any[]>([]);
|
||||
|
||||
// 查询参数
|
||||
const queryParams = reactive({
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
batchName: undefined,
|
||||
batchType: undefined,
|
||||
status: undefined
|
||||
});
|
||||
|
||||
// 批次对话框
|
||||
const batchDialog = reactive({
|
||||
visible: false,
|
||||
type: 'add'
|
||||
});
|
||||
|
||||
const batchForm = reactive({
|
||||
id: undefined,
|
||||
batchName: '',
|
||||
batchCode: '',
|
||||
batchType: 'regular',
|
||||
targetScope: 'all',
|
||||
startTime: '',
|
||||
endTime: '',
|
||||
autoRemind: true,
|
||||
remindDays: 3,
|
||||
description: '',
|
||||
noticeContent: ''
|
||||
});
|
||||
|
||||
const batchRules = {
|
||||
batchName: [{ required: true, message: '请输入批次名称', trigger: 'blur' }],
|
||||
batchCode: [{ required: true, message: '请输入批次编码', trigger: 'blur' }],
|
||||
batchType: [{ required: true, message: '请选择批次类型', trigger: 'change' }],
|
||||
targetScope: [{ required: true, message: '请选择目标范围', trigger: 'change' }],
|
||||
startTime: [{ required: true, message: '请选择开始时间', trigger: 'change' }],
|
||||
endTime: [{ required: true, message: '请选择结束时间', trigger: 'change' }]
|
||||
};
|
||||
|
||||
// 统计对话框
|
||||
const statisticsDialog = reactive({
|
||||
visible: false
|
||||
});
|
||||
|
||||
const statisticsInfo = ref<any>();
|
||||
|
||||
// 计算属性
|
||||
const dialogTitle = computed(() => {
|
||||
return batchDialog.type === 'add' ? '新增批次' : '编辑批次';
|
||||
});
|
||||
|
||||
// 获取列表数据
|
||||
const getList = async () => {
|
||||
loading.value = true;
|
||||
try {
|
||||
const response = await listCollectionBatch(queryParams);
|
||||
batchList.value = response.rows;
|
||||
total.value = response.total;
|
||||
} catch (error) {
|
||||
console.error('获取批次列表失败:', error);
|
||||
ElMessage.error('获取批次列表失败');
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 搜索
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNum = 1;
|
||||
getList();
|
||||
};
|
||||
|
||||
// 重置搜索
|
||||
const resetQuery = () => {
|
||||
proxy.resetForm('queryFormRef');
|
||||
handleQuery();
|
||||
};
|
||||
|
||||
// 多选框选中数据
|
||||
const handleSelectionChange = (selection: any[]) => {
|
||||
ids.value = selection.map(item => item.id);
|
||||
single.value = selection.length !== 1;
|
||||
multiple.value = !selection.length;
|
||||
};
|
||||
|
||||
// 排序变化
|
||||
const handleSortChange = (column: any) => {
|
||||
getList();
|
||||
};
|
||||
|
||||
// 分页大小变化
|
||||
const handleSizeChange = (val: number) => {
|
||||
queryParams.pageSize = val;
|
||||
getList();
|
||||
};
|
||||
|
||||
// 当前页变化
|
||||
const handleCurrentChange = (val: number) => {
|
||||
queryParams.pageNum = val;
|
||||
getList();
|
||||
};
|
||||
|
||||
// 新增批次
|
||||
const handleAdd = () => {
|
||||
resetBatchForm();
|
||||
batchDialog.type = 'add';
|
||||
batchDialog.visible = true;
|
||||
};
|
||||
|
||||
// 编辑批次
|
||||
const handleEdit = async (row: any) => {
|
||||
try {
|
||||
const response = await getCollectionBatch(row.id);
|
||||
Object.assign(batchForm, response.data);
|
||||
batchDialog.type = 'edit';
|
||||
batchDialog.visible = true;
|
||||
} catch (error) {
|
||||
console.error('获取批次详情失败:', error);
|
||||
ElMessage.error('获取批次详情失败');
|
||||
}
|
||||
};
|
||||
|
||||
// 查看详情
|
||||
const handleView = async (row: any) => {
|
||||
try {
|
||||
const response = await getCollectionBatch(row.id);
|
||||
Object.assign(batchForm, response.data);
|
||||
batchDialog.type = 'view';
|
||||
batchDialog.visible = true;
|
||||
} catch (error) {
|
||||
console.error('获取批次详情失败:', error);
|
||||
ElMessage.error('获取批次详情失败');
|
||||
}
|
||||
};
|
||||
|
||||
// 批次统计
|
||||
const handleStatistics = async (row: any) => {
|
||||
try {
|
||||
const response = await getCollectionBatchStatistics(row.id);
|
||||
statisticsInfo.value = response.data;
|
||||
statisticsDialog.visible = true;
|
||||
} catch (error) {
|
||||
console.error('获取批次统计失败:', error);
|
||||
ElMessage.error('获取批次统计失败');
|
||||
}
|
||||
};
|
||||
|
||||
// 发布批次
|
||||
const handlePublish = async (row: any) => {
|
||||
try {
|
||||
await ElMessageBox.confirm('确定要发布该批次吗?', '系统提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
});
|
||||
|
||||
await publishCollectionBatch(row.id);
|
||||
ElMessage.success('发布成功');
|
||||
getList();
|
||||
emit('batch-updated');
|
||||
} catch (error) {
|
||||
if (error !== 'cancel') {
|
||||
console.error('发布失败:', error);
|
||||
ElMessage.error('发布失败');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 取消发布
|
||||
const handleUnpublish = async (row: any) => {
|
||||
try {
|
||||
await ElMessageBox.confirm('确定要取消发布该批次吗?', '系统提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
});
|
||||
|
||||
await unpublishCollectionBatch(row.id);
|
||||
ElMessage.success('取消发布成功');
|
||||
getList();
|
||||
emit('batch-updated');
|
||||
} catch (error) {
|
||||
if (error !== 'cancel') {
|
||||
console.error('取消发布失败:', error);
|
||||
ElMessage.error('取消发布失败');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 复制批次
|
||||
const handleCopy = async (row: any) => {
|
||||
try {
|
||||
await ElMessageBox.confirm('确定要复制该批次吗?', '系统提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'info'
|
||||
});
|
||||
|
||||
await copyCollectionBatch(row.id, {
|
||||
batchName: row.batchName + '_副本',
|
||||
batchCode: row.batchCode + '_copy'
|
||||
});
|
||||
|
||||
ElMessage.success('复制成功');
|
||||
getList();
|
||||
emit('batch-created');
|
||||
} catch (error) {
|
||||
if (error !== 'cancel') {
|
||||
console.error('复制失败:', error);
|
||||
ElMessage.error('复制失败');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 批量发布
|
||||
const handleBatchPublish = async () => {
|
||||
if (ids.value.length === 0) {
|
||||
ElMessage.warning('请选择要发布的批次');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await ElMessageBox.confirm('确定要批量发布选中的批次吗?', '系统提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
});
|
||||
|
||||
// 批量发布逻辑
|
||||
for (const id of ids.value) {
|
||||
await publishCollectionBatch(id);
|
||||
}
|
||||
|
||||
ElMessage.success('批量发布成功');
|
||||
getList();
|
||||
emit('batch-updated');
|
||||
} catch (error) {
|
||||
if (error !== 'cancel') {
|
||||
console.error('批量发布失败:', error);
|
||||
ElMessage.error('批量发布失败');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 批量取消发布
|
||||
const handleBatchUnpublish = async () => {
|
||||
if (ids.value.length === 0) {
|
||||
ElMessage.warning('请选择要取消发布的批次');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await ElMessageBox.confirm('确定要批量取消发布选中的批次吗?', '系统提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
});
|
||||
|
||||
// 批量取消发布逻辑
|
||||
for (const id of ids.value) {
|
||||
await unpublishCollectionBatch(id);
|
||||
}
|
||||
|
||||
ElMessage.success('批量取消发布成功');
|
||||
getList();
|
||||
emit('batch-updated');
|
||||
} catch (error) {
|
||||
if (error !== 'cancel') {
|
||||
console.error('批量取消发布失败:', error);
|
||||
ElMessage.error('批量取消发布失败');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 删除批次
|
||||
const handleDelete = async (row?: any) => {
|
||||
const batchIds = row ? [row.id] : ids.value;
|
||||
if (batchIds.length === 0) {
|
||||
ElMessage.warning('请选择要删除的批次');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await ElMessageBox.confirm('确定要删除选中的批次吗?', '系统提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
});
|
||||
|
||||
await delCollectionBatch(batchIds);
|
||||
ElMessage.success('删除成功');
|
||||
getList();
|
||||
emit('batch-deleted');
|
||||
} catch (error) {
|
||||
if (error !== 'cancel') {
|
||||
console.error('删除失败:', error);
|
||||
ElMessage.error('删除失败');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 导出
|
||||
const handleExport = () => {
|
||||
proxy?.download(
|
||||
'xg/collectionBatch/export',
|
||||
queryParams,
|
||||
`批次信息_${parseTime(new Date(), '{y}{m}{d}{h}{i}{s}')}.xlsx`
|
||||
);
|
||||
};
|
||||
|
||||
// 刷新
|
||||
const handleRefresh = () => {
|
||||
getList();
|
||||
};
|
||||
|
||||
// 提交批次
|
||||
const submitBatch = async () => {
|
||||
try {
|
||||
const valid = await proxy.$refs.batchFormRef.validate();
|
||||
if (!valid) return;
|
||||
|
||||
if (batchDialog.type === 'add') {
|
||||
await addCollectionBatch(batchForm);
|
||||
ElMessage.success('新增成功');
|
||||
emit('batch-created');
|
||||
} else {
|
||||
await updateCollectionBatch(batchForm);
|
||||
ElMessage.success('修改成功');
|
||||
emit('batch-updated');
|
||||
}
|
||||
|
||||
batchDialog.visible = false;
|
||||
getList();
|
||||
} catch (error) {
|
||||
console.error('提交失败:', error);
|
||||
ElMessage.error('提交失败');
|
||||
}
|
||||
};
|
||||
|
||||
// 重置表单
|
||||
const resetBatchForm = () => {
|
||||
Object.assign(batchForm, {
|
||||
id: undefined,
|
||||
batchName: '',
|
||||
batchCode: '',
|
||||
batchType: 'regular',
|
||||
targetScope: 'all',
|
||||
startTime: '',
|
||||
endTime: '',
|
||||
autoRemind: true,
|
||||
remindDays: 3,
|
||||
description: '',
|
||||
noticeContent: ''
|
||||
});
|
||||
};
|
||||
|
||||
// 获取批次类型标签类型
|
||||
const getBatchTypeTagType = (type: string) => {
|
||||
const typeMap: Record<string, string> = {
|
||||
'regular': '',
|
||||
'special': 'warning',
|
||||
'urgent': 'danger'
|
||||
};
|
||||
return typeMap[type] || '';
|
||||
};
|
||||
|
||||
// 获取批次类型文本
|
||||
const getBatchTypeText = (type: string) => {
|
||||
const typeMap: Record<string, string> = {
|
||||
'regular': '常规',
|
||||
'special': '专项',
|
||||
'urgent': '紧急'
|
||||
};
|
||||
return typeMap[type] || '未知';
|
||||
};
|
||||
|
||||
// 获取状态标签类型
|
||||
const getStatusTagType = (status: string) => {
|
||||
const statusMap: Record<string, string> = {
|
||||
'DRAFT': 'info',
|
||||
'ACTIVE': 'success',
|
||||
'COMPLETED': 'warning',
|
||||
'CANCELLED': 'danger'
|
||||
};
|
||||
return statusMap[status] || '';
|
||||
};
|
||||
|
||||
// 获取状态文本
|
||||
const getStatusText = (status: string) => {
|
||||
const statusMap: Record<string, string> = {
|
||||
'DRAFT': '草稿',
|
||||
'ACTIVE': '活跃',
|
||||
'COMPLETED': '已完成',
|
||||
'CANCELLED': '已取消'
|
||||
};
|
||||
return statusMap[status] || '未知';
|
||||
};
|
||||
|
||||
// 初始化
|
||||
onMounted(() => {
|
||||
getList();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.batch-management-container {
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.toolbar-wrapper {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.toolbar-left {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.toolbar-right {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.table-wrapper {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.pagination-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.statistics-content {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.statistics-chart {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 200px;
|
||||
background: #f5f5f5;
|
||||
border-radius: 4px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,925 +0,0 @@
|
||||
<template>
|
||||
<div class="p-2 page-container">
|
||||
<!-- 页面标题 -->
|
||||
<div class="page-header mb-4">
|
||||
<h2 class="page-title">学生信息管理</h2>
|
||||
<p class="page-description">管理学生基本信息、信息采集批次以及审核流程</p>
|
||||
</div>
|
||||
|
||||
<!-- 标签页结构 -->
|
||||
<el-tabs v-model="activeTab" type="card" class="main-tabs">
|
||||
<!-- 学生信息列表标签页 -->
|
||||
<el-tab-pane label="学生信息列表" name="studentList">
|
||||
<div class="tab-content">
|
||||
<!-- 批次选择区域 -->
|
||||
<el-card shadow="hover" class="mb-4">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>批次选择</span>
|
||||
<div class="header-actions">
|
||||
<el-select v-model="selectedBatchId" placeholder="选择批次" @change="handleBatchChange" style="width: 200px; margin-right: 10px;">
|
||||
<el-option label="全部学生" :value="null" />
|
||||
<el-option v-for="batch in batchOptions" :key="batch.id" :label="batch.batchName" :value="batch.id" />
|
||||
</el-select>
|
||||
<el-button type="primary" icon="Refresh" @click="refreshBatchInfo">刷新</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- 批次统计信息 -->
|
||||
<div v-if="currentBatch" class="batch-info">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="6">
|
||||
<el-statistic title="批次名称" :value="currentBatch.batchName" />
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-statistic title="批次状态" :value="getBatchStatusText(currentBatch.status)" />
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-statistic title="总学生数" :value="batchStatistics.totalCount" />
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-statistic title="已提交数" :value="batchStatistics.submittedCount" />
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20" class="mt-4">
|
||||
<el-col :span="6">
|
||||
<el-statistic title="已通过数" :value="batchStatistics.approvedCount" />
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-statistic title="已拒绝数" :value="batchStatistics.rejectedCount" />
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-statistic title="完成率" :value="batchStatistics.completionRate" suffix="%" />
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<div class="progress-info">
|
||||
<el-progress :percentage="batchStatistics.completionRate" :stroke-width="8" />
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<!-- 搜索区域 -->
|
||||
<el-card shadow="hover" class="mb-4">
|
||||
<el-form ref="queryFormRef" :model="queryParams" label-width="100px">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="6">
|
||||
<el-form-item label="学生姓名" prop="name">
|
||||
<el-input v-model="queryParams.name" placeholder="请输入学生姓名" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="学号" prop="studentNo">
|
||||
<el-input v-model="queryParams.studentNo" placeholder="请输入学号" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="班级" prop="className">
|
||||
<el-input v-model="queryParams.className" placeholder="请输入班级名称" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="提交状态" prop="submissionStatus">
|
||||
<el-select v-model="queryParams.submissionStatus" placeholder="请选择提交状态" clearable style="width: 100%">
|
||||
<el-option label="草稿" value="DRAFT" />
|
||||
<el-option label="已提交" value="SUBMITTED" />
|
||||
<el-option label="已通过" value="APPROVED" />
|
||||
<el-option label="已拒绝" value="REJECTED" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="6">
|
||||
<el-form-item label="学院" prop="collegeName">
|
||||
<el-input v-model="queryParams.collegeName" placeholder="请输入学院名称" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="18">
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</el-card>
|
||||
|
||||
<!-- 主要内容区域 -->
|
||||
<el-card shadow="hover">
|
||||
<!-- 操作按钮区域 -->
|
||||
<div class="toolbar-wrapper">
|
||||
<div class="toolbar-left">
|
||||
<el-button type="primary" icon="Plus" @click="handleAdd">
|
||||
新增学生
|
||||
</el-button>
|
||||
<el-button type="success" icon="Select" :disabled="!multiple || ids.length === 0" @click="handleBatchAssociate">
|
||||
批量关联批次
|
||||
</el-button>
|
||||
<el-button type="warning" icon="Upload" :disabled="!multiple || ids.length === 0" @click="handleBatchSubmit">
|
||||
批量提交
|
||||
</el-button>
|
||||
<el-button type="info" icon="Download" :disabled="!multiple || ids.length === 0" @click="handleBatchWithdraw">
|
||||
批量撤回
|
||||
</el-button>
|
||||
<el-button type="danger" icon="Delete" :disabled="!multiple || ids.length === 0" @click="handleDelete">
|
||||
批量删除
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<div class="toolbar-right">
|
||||
<el-button type="primary" icon="Upload" @click="handleImport">
|
||||
导入
|
||||
</el-button>
|
||||
<el-button type="danger" icon="Download" @click="handleExport">
|
||||
导出
|
||||
</el-button>
|
||||
<el-button type="info" icon="Refresh" @click="handleRefresh">
|
||||
刷新
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 表格区域 -->
|
||||
<div class="table-wrapper">
|
||||
<el-table
|
||||
v-loading="loading"
|
||||
:data="studentList"
|
||||
@selection-change="handleSelectionChange"
|
||||
@sort-change="handleSortChange"
|
||||
row-key="id"
|
||||
border
|
||||
stripe
|
||||
size="small"
|
||||
>
|
||||
<el-table-column type="selection" width="50" align="center" />
|
||||
<el-table-column label="序号" type="index" width="60" align="center" />
|
||||
<el-table-column label="学生姓名" prop="name" width="100" align="center" show-overflow-tooltip />
|
||||
<el-table-column label="学号" prop="studentNo" width="120" align="center" show-overflow-tooltip />
|
||||
<el-table-column label="班级" prop="className" width="120" align="center" show-overflow-tooltip />
|
||||
<el-table-column label="学院" prop="collegeName" width="120" align="center" show-overflow-tooltip />
|
||||
<el-table-column label="专业" prop="majorName" width="120" align="center" show-overflow-tooltip />
|
||||
<el-table-column label="联系电话" prop="phone" width="120" align="center" show-overflow-tooltip />
|
||||
<el-table-column label="提交状态" prop="submissionStatus" width="100" align="center">
|
||||
<template #default="scope">
|
||||
<el-tag :type="getSubmissionStatusTagType(scope.row.submissionStatus)" size="small">
|
||||
{{ getSubmissionStatusText(scope.row.submissionStatus) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="提交时间" prop="submissionTime" width="160" align="center" sortable="custom">
|
||||
<template #default="scope">
|
||||
<span>{{ scope.row.submissionTime ? parseTime(scope.row.submissionTime, '{y}-{m}-{d} {h}:{i}') : '-' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="审核状态" prop="approvalStatus" width="100" align="center">
|
||||
<template #default="scope">
|
||||
<el-tag :type="getApprovalStatusTagType(scope.row.approvalStatus)" size="small">
|
||||
{{ getApprovalStatusText(scope.row.approvalStatus) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" width="200" fixed="right" class-name="small-padding fixed-width">
|
||||
<template #default="scope">
|
||||
<div class="action-buttons">
|
||||
<el-tooltip content="查看详情" placement="top">
|
||||
<el-button link type="info" icon="View" size="small" @click="handleView(scope.row)" />
|
||||
</el-tooltip>
|
||||
<el-tooltip content="编辑" placement="top">
|
||||
<el-button link type="primary" icon="Edit" size="small" @click="handleEdit(scope.row)" />
|
||||
</el-tooltip>
|
||||
<el-tooltip content="查看进度" placement="top">
|
||||
<el-button link type="warning" icon="DataAnalysis" size="small" @click="handleProgress(scope.row)" />
|
||||
</el-tooltip>
|
||||
<template v-if="scope.row.submissionStatus === 'DRAFT'">
|
||||
<el-tooltip content="提交" placement="top">
|
||||
<el-button link type="success" icon="Upload" size="small" @click="handleSubmit(scope.row)" />
|
||||
</el-tooltip>
|
||||
</template>
|
||||
<template v-else-if="scope.row.submissionStatus === 'SUBMITTED'">
|
||||
<el-tooltip content="撤回" placement="top">
|
||||
<el-button link type="danger" icon="Download" size="small" @click="handleWithdraw(scope.row)" />
|
||||
</el-tooltip>
|
||||
</template>
|
||||
<el-tooltip content="删除" placement="top">
|
||||
<el-button link type="danger" icon="Delete" size="small" @click="handleDelete(scope.row)" />
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<div class="pagination-container">
|
||||
<el-pagination
|
||||
v-show="total > 0"
|
||||
:total="total"
|
||||
:page-size="queryParams.pageSize"
|
||||
:current-page="queryParams.pageNum"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
|
||||
<!-- 信息采集批次标签页 -->
|
||||
<el-tab-pane label="信息采集批次" name="batchManagement">
|
||||
<div class="tab-content">
|
||||
<!-- 批次管理组件 -->
|
||||
<BatchManagementComponent
|
||||
@batch-created="handleBatchCreated"
|
||||
@batch-updated="handleBatchUpdated"
|
||||
@batch-deleted="handleBatchDeleted"
|
||||
/>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
|
||||
<!-- 关联批次对话框 -->
|
||||
<el-dialog title="关联学生到批次" v-model="associateDialog.visible" width="600px" append-to-body>
|
||||
<el-form ref="associateFormRef" :model="associateForm" :rules="associateRules" label-width="100px">
|
||||
<el-form-item label="选择批次" prop="batchId">
|
||||
<el-select v-model="associateForm.batchId" placeholder="请选择批次" style="width: 100%">
|
||||
<el-option v-for="batch in batchOptions" :key="batch.id" :label="batch.batchName" :value="batch.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="已选学生" prop="studentIds">
|
||||
<div class="selected-students">
|
||||
<el-tag v-for="student in selectedStudents" :key="student.id" closable @close="removeStudent(student.id)">
|
||||
{{ student.name }}({{ student.studentNo }})
|
||||
</el-tag>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="associateDialog.visible = false">取消</el-button>
|
||||
<el-button type="primary" @click="submitAssociate">确定</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 学生进度对话框 -->
|
||||
<el-dialog title="学生批次进度" v-model="progressDialog.visible" width="800px" append-to-body>
|
||||
<div v-if="progressInfo" class="progress-content">
|
||||
<el-descriptions :column="2" border>
|
||||
<el-descriptions-item label="学生姓名">{{ progressInfo.studentName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="学号">{{ progressInfo.studentNo }}</el-descriptions-item>
|
||||
<el-descriptions-item label="批次名称">{{ progressInfo.batchName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="提交状态">
|
||||
<el-tag :type="getSubmissionStatusTagType(progressInfo.submissionStatus)">
|
||||
{{ getSubmissionStatusText(progressInfo.submissionStatus) }}
|
||||
</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="审核状态">
|
||||
<el-tag :type="getApprovalStatusTagType(progressInfo.approvalStatus)">
|
||||
{{ getApprovalStatusText(progressInfo.approvalStatus) }}
|
||||
</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="提交时间">
|
||||
{{ progressInfo.submissionTime ? parseTime(progressInfo.submissionTime, '{y}-{m}-{d} {h}:{i}:{s}') : '-' }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="审核时间">
|
||||
{{ progressInfo.approvalTime ? parseTime(progressInfo.approvalTime, '{y}-{m}-{d} {h}:{i}:{s}') : '-' }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="审核意见" :span="2">
|
||||
{{ progressInfo.approvalComments || '-' }}
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, onMounted, getCurrentInstance, computed } from 'vue';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import { parseTime } from '@/utils/ruoyi';
|
||||
import { listAllCollectionBatch } from '@/api/xg-xx/collectionBatch';
|
||||
import {
|
||||
listStudentInfo,
|
||||
getStudentsByBatch,
|
||||
addStudentInfo,
|
||||
updateStudentInfo,
|
||||
delStudentInfo,
|
||||
batchAssociateStudentsToBatch,
|
||||
submitStudentInfo,
|
||||
withdrawStudentInfo,
|
||||
getStudentBatchProgress,
|
||||
getStudentBatchStatistics,
|
||||
batchSubmitStudentInfo,
|
||||
batchWithdrawStudentInfo
|
||||
} from '@/api/xg-xx/studentInfo';
|
||||
import BatchManagementComponent from './components/BatchManagementComponent.vue';
|
||||
|
||||
const { proxy } = getCurrentInstance() as any;
|
||||
|
||||
// 响应式数据
|
||||
const activeTab = ref('studentList');
|
||||
const loading = ref(false);
|
||||
const ids = ref<number[]>([]);
|
||||
const single = ref(true);
|
||||
const multiple = ref(true);
|
||||
const total = ref(0);
|
||||
const studentList = ref<any[]>([]);
|
||||
const batchOptions = ref<any[]>([]);
|
||||
const selectedBatchId = ref<number | null>(null);
|
||||
const currentBatch = ref<any>();
|
||||
|
||||
// 批次统计信息
|
||||
const batchStatistics = reactive({
|
||||
totalCount: 0,
|
||||
submittedCount: 0,
|
||||
approvedCount: 0,
|
||||
rejectedCount: 0,
|
||||
completionRate: 0
|
||||
});
|
||||
|
||||
// 查询参数
|
||||
const queryParams = reactive({
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
batchId: undefined as number | undefined,
|
||||
name: undefined,
|
||||
studentNo: undefined,
|
||||
className: undefined,
|
||||
submissionStatus: undefined,
|
||||
collegeName: undefined
|
||||
});
|
||||
|
||||
// 关联对话框
|
||||
const associateDialog = reactive({
|
||||
visible: false
|
||||
});
|
||||
|
||||
const associateForm = reactive({
|
||||
batchId: undefined as number | undefined,
|
||||
studentIds: [] as number[]
|
||||
});
|
||||
|
||||
const associateRules = {
|
||||
batchId: [{ required: true, message: '请选择批次', trigger: 'change' }],
|
||||
studentIds: [{ required: true, message: '请选择学生', trigger: 'change' }]
|
||||
};
|
||||
|
||||
// 进度对话框
|
||||
const progressDialog = reactive({
|
||||
visible: false
|
||||
});
|
||||
|
||||
const progressInfo = ref<any>();
|
||||
|
||||
// 计算已选学生
|
||||
const selectedStudents = computed(() => {
|
||||
return studentList.value.filter(student => ids.value.includes(student.id));
|
||||
});
|
||||
|
||||
// 获取列表数据
|
||||
const getList = async () => {
|
||||
loading.value = true;
|
||||
try {
|
||||
let response;
|
||||
if (selectedBatchId.value) {
|
||||
response = await getStudentsByBatch(selectedBatchId.value, queryParams);
|
||||
} else {
|
||||
response = await listStudentInfo(queryParams);
|
||||
}
|
||||
studentList.value = response.rows;
|
||||
total.value = response.total;
|
||||
} catch (error) {
|
||||
console.error('获取列表失败:', error);
|
||||
ElMessage.error('获取学生列表失败');
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 获取批次选项
|
||||
const getBatchOptions = async () => {
|
||||
try {
|
||||
const response = await listAllCollectionBatch({});
|
||||
batchOptions.value = response.data;
|
||||
} catch (error) {
|
||||
console.error('获取批次选项失败:', error);
|
||||
ElMessage.error('获取批次选项失败');
|
||||
}
|
||||
};
|
||||
|
||||
// 批次改变处理
|
||||
const handleBatchChange = (batchId: number) => {
|
||||
selectedBatchId.value = batchId;
|
||||
queryParams.batchId = batchId;
|
||||
currentBatch.value = batchOptions.value.find(batch => batch.id === batchId);
|
||||
|
||||
// 获取批次统计信息
|
||||
if (batchId) {
|
||||
getBatchStatistics();
|
||||
}
|
||||
|
||||
// 刷新列表
|
||||
getList();
|
||||
};
|
||||
|
||||
// 获取批次统计信息
|
||||
const getBatchStatistics = async () => {
|
||||
if (!selectedBatchId.value) return;
|
||||
|
||||
try {
|
||||
const response = await getStudentBatchStatistics(selectedBatchId.value);
|
||||
Object.assign(batchStatistics, response.data);
|
||||
} catch (error) {
|
||||
console.error('获取批次统计失败:', error);
|
||||
ElMessage.error('获取批次统计失败');
|
||||
}
|
||||
};
|
||||
|
||||
// 刷新批次信息
|
||||
const refreshBatchInfo = () => {
|
||||
if (selectedBatchId.value) {
|
||||
getBatchStatistics();
|
||||
}
|
||||
getList();
|
||||
};
|
||||
|
||||
// 搜索
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNum = 1;
|
||||
getList();
|
||||
};
|
||||
|
||||
// 重置搜索
|
||||
const resetQuery = () => {
|
||||
proxy.resetForm('queryFormRef');
|
||||
handleQuery();
|
||||
};
|
||||
|
||||
// 多选框选中数据
|
||||
const handleSelectionChange = (selection: any[]) => {
|
||||
ids.value = selection.map(item => item.id);
|
||||
single.value = selection.length !== 1;
|
||||
multiple.value = !selection.length;
|
||||
};
|
||||
|
||||
// 排序变化
|
||||
const handleSortChange = (column: any) => {
|
||||
getList();
|
||||
};
|
||||
|
||||
// 分页大小变化
|
||||
const handleSizeChange = (val: number) => {
|
||||
queryParams.pageSize = val;
|
||||
getList();
|
||||
};
|
||||
|
||||
// 当前页变化
|
||||
const handleCurrentChange = (val: number) => {
|
||||
queryParams.pageNum = val;
|
||||
getList();
|
||||
};
|
||||
|
||||
// 新增学生
|
||||
const handleAdd = () => {
|
||||
proxy.$router.push('/xg-xx/studentInfo/add');
|
||||
};
|
||||
|
||||
// 批量关联
|
||||
const handleBatchAssociate = () => {
|
||||
if (ids.value.length === 0) {
|
||||
ElMessage.warning('请选择要关联的学生');
|
||||
return;
|
||||
}
|
||||
|
||||
associateDialog.visible = true;
|
||||
associateForm.batchId = selectedBatchId.value;
|
||||
associateForm.studentIds = ids.value;
|
||||
};
|
||||
|
||||
// 移除学生
|
||||
const removeStudent = (studentId: number) => {
|
||||
const index = associateForm.studentIds.indexOf(studentId);
|
||||
if (index > -1) {
|
||||
associateForm.studentIds.splice(index, 1);
|
||||
}
|
||||
};
|
||||
|
||||
// 提交关联
|
||||
const submitAssociate = async () => {
|
||||
if (!associateForm.batchId || associateForm.studentIds.length === 0) {
|
||||
ElMessage.warning('请选择批次和学生');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await batchAssociateStudentsToBatch(associateForm.studentIds, associateForm.batchId);
|
||||
|
||||
ElMessage.success('关联成功');
|
||||
associateDialog.visible = false;
|
||||
|
||||
// 刷新统计信息和列表
|
||||
if (selectedBatchId.value) {
|
||||
getBatchStatistics();
|
||||
}
|
||||
getList();
|
||||
} catch (error) {
|
||||
console.error('关联失败:', error);
|
||||
ElMessage.error('关联失败');
|
||||
}
|
||||
};
|
||||
|
||||
// 批量提交
|
||||
const handleBatchSubmit = async () => {
|
||||
if (ids.value.length === 0) {
|
||||
ElMessage.warning('请选择要提交的学生');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!selectedBatchId.value) {
|
||||
ElMessage.warning('请先选择批次');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await ElMessageBox.confirm('确定要批量提交选中的学生信息吗?', '系统提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
});
|
||||
|
||||
await batchSubmitStudentInfo(ids.value, selectedBatchId.value);
|
||||
|
||||
ElMessage.success('批量提交成功');
|
||||
|
||||
// 刷新统计信息和列表
|
||||
getBatchStatistics();
|
||||
getList();
|
||||
} catch (error) {
|
||||
if (error !== 'cancel') {
|
||||
console.error('批量提交失败:', error);
|
||||
ElMessage.error('批量提交失败');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 批量撤回
|
||||
const handleBatchWithdraw = async () => {
|
||||
if (ids.value.length === 0) {
|
||||
ElMessage.warning('请选择要撤回的学生');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!selectedBatchId.value) {
|
||||
ElMessage.warning('请先选择批次');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await ElMessageBox.confirm('确定要批量撤回选中的学生信息吗?', '系统提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
});
|
||||
|
||||
await batchWithdrawStudentInfo(ids.value, selectedBatchId.value);
|
||||
|
||||
ElMessage.success('批量撤回成功');
|
||||
|
||||
// 刷新统计信息和列表
|
||||
getBatchStatistics();
|
||||
getList();
|
||||
} catch (error) {
|
||||
if (error !== 'cancel') {
|
||||
console.error('批量撤回失败:', error);
|
||||
ElMessage.error('批量撤回失败');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 删除学生
|
||||
const handleDelete = async (row?: any) => {
|
||||
const studentIds = row ? [row.id] : ids.value;
|
||||
if (studentIds.length === 0) {
|
||||
ElMessage.warning('请选择要删除的学生');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await ElMessageBox.confirm('确定要删除选中的学生信息吗?', '系统提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
});
|
||||
|
||||
await delStudentInfo(studentIds);
|
||||
|
||||
ElMessage.success('删除成功');
|
||||
|
||||
// 刷新统计信息和列表
|
||||
if (selectedBatchId.value) {
|
||||
getBatchStatistics();
|
||||
}
|
||||
getList();
|
||||
} catch (error) {
|
||||
if (error !== 'cancel') {
|
||||
console.error('删除失败:', error);
|
||||
ElMessage.error('删除失败');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 查看详情
|
||||
const handleView = (row: any) => {
|
||||
proxy.$router.push(`/xg-xx/studentInfo/detail/${row.id}`);
|
||||
};
|
||||
|
||||
// 编辑
|
||||
const handleEdit = (row: any) => {
|
||||
proxy.$router.push(`/xg-xx/studentInfo/edit/${row.id}`);
|
||||
};
|
||||
|
||||
// 提交
|
||||
const handleSubmit = async (row: any) => {
|
||||
if (!selectedBatchId.value) {
|
||||
ElMessage.warning('请先选择批次');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await ElMessageBox.confirm('确定要提交该学生信息吗?', '系统提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
});
|
||||
|
||||
await submitStudentInfo(row.id, selectedBatchId.value);
|
||||
|
||||
ElMessage.success('提交成功');
|
||||
|
||||
// 刷新统计信息和列表
|
||||
getBatchStatistics();
|
||||
getList();
|
||||
} catch (error) {
|
||||
if (error !== 'cancel') {
|
||||
console.error('提交失败:', error);
|
||||
ElMessage.error('提交失败');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 撤回
|
||||
const handleWithdraw = async (row: any) => {
|
||||
if (!selectedBatchId.value) {
|
||||
ElMessage.warning('请先选择批次');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await ElMessageBox.confirm('确定要撤回该学生信息吗?', '系统提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
});
|
||||
|
||||
await withdrawStudentInfo(row.id, selectedBatchId.value);
|
||||
|
||||
ElMessage.success('撤回成功');
|
||||
|
||||
// 刷新统计信息和列表
|
||||
getBatchStatistics();
|
||||
getList();
|
||||
} catch (error) {
|
||||
if (error !== 'cancel') {
|
||||
console.error('撤回失败:', error);
|
||||
ElMessage.error('撤回失败');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 查看进度
|
||||
const handleProgress = async (row: any) => {
|
||||
if (!selectedBatchId.value) {
|
||||
ElMessage.warning('请先选择批次');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await getStudentBatchProgress(row.id, selectedBatchId.value);
|
||||
progressInfo.value = response.data;
|
||||
progressDialog.visible = true;
|
||||
} catch (error) {
|
||||
console.error('获取进度失败:', error);
|
||||
ElMessage.error('获取进度失败');
|
||||
}
|
||||
};
|
||||
|
||||
// 导入
|
||||
const handleImport = () => {
|
||||
proxy.$router.push('/xg-xx/studentInfo/import');
|
||||
};
|
||||
|
||||
// 导出
|
||||
const handleExport = () => {
|
||||
proxy?.download(
|
||||
'xg/studentInfo/export',
|
||||
queryParams,
|
||||
`学生信息_${parseTime(new Date(), '{y}{m}{d}{h}{i}{s}')}.xlsx`
|
||||
);
|
||||
};
|
||||
|
||||
// 刷新
|
||||
const handleRefresh = () => {
|
||||
getList();
|
||||
};
|
||||
|
||||
// 获取批次状态文本
|
||||
const getBatchStatusText = (status: string) => {
|
||||
const statusMap: Record<string, string> = {
|
||||
'DRAFT': '草稿',
|
||||
'ACTIVE': '进行中',
|
||||
'COMPLETED': '已完成',
|
||||
'CANCELLED': '已取消'
|
||||
};
|
||||
return statusMap[status] || '未知';
|
||||
};
|
||||
|
||||
// 获取提交状态标签类型
|
||||
const getSubmissionStatusTagType = (status: string): 'success' | 'warning' | 'info' | 'primary' | 'danger' => {
|
||||
const statusMap: Record<string, 'success' | 'warning' | 'info' | 'primary' | 'danger'> = {
|
||||
'DRAFT': 'info',
|
||||
'SUBMITTED': 'warning',
|
||||
'APPROVED': 'success',
|
||||
'REJECTED': 'danger'
|
||||
};
|
||||
return statusMap[status] || 'info';
|
||||
};
|
||||
|
||||
// 获取提交状态文本
|
||||
const getSubmissionStatusText = (status: string) => {
|
||||
const statusMap: Record<string, string> = {
|
||||
'DRAFT': '草稿',
|
||||
'SUBMITTED': '已提交',
|
||||
'APPROVED': '已通过',
|
||||
'REJECTED': '已拒绝'
|
||||
};
|
||||
return statusMap[status] || '未知';
|
||||
};
|
||||
|
||||
// 获取审核状态标签类型
|
||||
const getApprovalStatusTagType = (status: string): 'success' | 'warning' | 'info' | 'primary' | 'danger' => {
|
||||
const statusMap: Record<string, 'success' | 'warning' | 'info' | 'primary' | 'danger'> = {
|
||||
'PENDING': 'warning',
|
||||
'APPROVED': 'success',
|
||||
'REJECTED': 'danger'
|
||||
};
|
||||
return statusMap[status] || 'info';
|
||||
};
|
||||
|
||||
// 获取审核状态文本
|
||||
const getApprovalStatusText = (status: string) => {
|
||||
const statusMap: Record<string, string> = {
|
||||
'PENDING': '待审核',
|
||||
'APPROVED': '已通过',
|
||||
'REJECTED': '已拒绝'
|
||||
};
|
||||
return statusMap[status] || '未知';
|
||||
};
|
||||
|
||||
// 批次创建成功回调
|
||||
const handleBatchCreated = () => {
|
||||
ElMessage.success('批次创建成功');
|
||||
getBatchOptions(); // 刷新批次选项
|
||||
if (activeTab.value === 'batchManagement') {
|
||||
getList(); // 刷新学生列表
|
||||
}
|
||||
};
|
||||
|
||||
// 批次更新成功回调
|
||||
const handleBatchUpdated = () => {
|
||||
ElMessage.success('批次更新成功');
|
||||
getBatchOptions(); // 刷新批次选项
|
||||
if (activeTab.value === 'batchManagement') {
|
||||
getList(); // 刷新学生列表
|
||||
}
|
||||
};
|
||||
|
||||
// 批次删除成功回调
|
||||
const handleBatchDeleted = () => {
|
||||
ElMessage.success('批次删除成功');
|
||||
getBatchOptions(); // 刷新批次选项
|
||||
if (activeTab.value === 'batchManagement') {
|
||||
getList(); // 刷新学生列表
|
||||
}
|
||||
};
|
||||
|
||||
// 初始化
|
||||
onMounted(() => {
|
||||
getBatchOptions();
|
||||
getList();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.page-container {
|
||||
background-color: #f5f5f5;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
text-align: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
font-size: 24px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.page-description {
|
||||
font-size: 16px;
|
||||
color: #606266;
|
||||
}
|
||||
|
||||
.main-tabs {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.tab-content {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.header-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.batch-info {
|
||||
padding: 16px 0;
|
||||
}
|
||||
|
||||
.toolbar-wrapper {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.toolbar-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.toolbar-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.table-wrapper {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.pagination-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.progress-info {
|
||||
padding: 8px 0;
|
||||
}
|
||||
|
||||
.progress-content {
|
||||
padding: 16px 0;
|
||||
}
|
||||
|
||||
.selected-students {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.mt-4 {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.mb-4 {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,630 +0,0 @@
|
||||
<template>
|
||||
<div class="p-2 page-container">
|
||||
<!-- 搜索区域 -->
|
||||
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
|
||||
<div v-show="showSearch" class="mb-[10px]">
|
||||
<el-card shadow="hover">
|
||||
<el-form ref="queryFormRef" :model="queryParams" label-width="100px">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="6">
|
||||
<el-form-item label="批次选择" prop="batchId">
|
||||
<el-select v-model="queryParams.batchId" placeholder="请选择批次" clearable style="width: 100%">
|
||||
<el-option v-for="batch in batchOptions" :key="batch.id" :label="batch.batchName" :value="batch.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="学生姓名" prop="studentName">
|
||||
<el-input v-model="queryParams.studentName" placeholder="请输入学生姓名" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="学号" prop="studentNo">
|
||||
<el-input v-model="queryParams.studentNo" placeholder="请输入学号" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="提交状态" prop="submissionStatus">
|
||||
<el-select v-model="queryParams.submissionStatus" placeholder="请选择提交状态" clearable style="width: 100%">
|
||||
<el-option label="草稿" value="DRAFT" />
|
||||
<el-option label="已提交" value="SUBMITTED" />
|
||||
<el-option label="已通过" value="APPROVED" />
|
||||
<el-option label="已拒绝" value="REJECTED" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="6">
|
||||
<el-form-item label="审核状态" prop="approvalStatus">
|
||||
<el-select v-model="queryParams.approvalStatus" placeholder="请选择审核状态" clearable style="width: 100%">
|
||||
<el-option label="待审核" value="PENDING" />
|
||||
<el-option label="已通过" value="APPROVED" />
|
||||
<el-option label="已拒绝" value="REJECTED" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="班级" prop="className">
|
||||
<el-input v-model="queryParams.className" placeholder="请输入班级名称" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="学院" prop="collegeName">
|
||||
<el-input v-model="queryParams.collegeName" placeholder="请输入学院名称" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</el-card>
|
||||
</div>
|
||||
</transition>
|
||||
|
||||
<!-- 主要内容区域 -->
|
||||
<el-card shadow="hover">
|
||||
<!-- 操作按钮区域 -->
|
||||
<div class="toolbar-wrapper">
|
||||
<div class="toolbar-left">
|
||||
<el-button-group>
|
||||
<el-button
|
||||
:type="currentTab === 'pending' ? 'primary' : 'default'"
|
||||
@click="switchTab('pending')"
|
||||
icon="Clock"
|
||||
>
|
||||
待审核 <el-badge :value="pendingCount" class="ml-1" />
|
||||
</el-button>
|
||||
<el-button
|
||||
:type="currentTab === 'approved' ? 'primary' : 'default'"
|
||||
@click="switchTab('approved')"
|
||||
icon="Check"
|
||||
>
|
||||
已审核 <el-badge :value="approvedCount" class="ml-1" />
|
||||
</el-button>
|
||||
</el-button-group>
|
||||
</div>
|
||||
|
||||
<div class="toolbar-right">
|
||||
<el-button
|
||||
type="success"
|
||||
icon="Select"
|
||||
:disabled="!multiple || ids.length === 0"
|
||||
@click="handleBatchApprove(true)"
|
||||
v-hasPermi="['xg:studentInfo:approve']"
|
||||
>
|
||||
批量通过
|
||||
</el-button>
|
||||
<el-button
|
||||
type="danger"
|
||||
icon="Close"
|
||||
:disabled="!multiple || ids.length === 0"
|
||||
@click="handleBatchApprove(false)"
|
||||
v-hasPermi="['xg:studentInfo:approve']"
|
||||
>
|
||||
批量拒绝
|
||||
</el-button>
|
||||
<el-button
|
||||
type="warning"
|
||||
icon="Download"
|
||||
@click="handleExport"
|
||||
v-hasPermi="['xg:studentInfo:export']"
|
||||
>
|
||||
导出
|
||||
</el-button>
|
||||
<el-button
|
||||
type="info"
|
||||
icon="Refresh"
|
||||
@click="handleRefresh"
|
||||
>
|
||||
刷新
|
||||
</el-button>
|
||||
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 表格区域 -->
|
||||
<div class="table-wrapper">
|
||||
<el-table
|
||||
v-loading="loading"
|
||||
:data="approvalList"
|
||||
@selection-change="handleSelectionChange"
|
||||
@sort-change="handleSortChange"
|
||||
row-key="id"
|
||||
border
|
||||
stripe
|
||||
size="small"
|
||||
>
|
||||
<el-table-column type="selection" width="50" align="center" />
|
||||
<el-table-column label="序号" type="index" width="60" align="center" />
|
||||
<el-table-column label="学生姓名" prop="studentName" width="100" align="center" show-overflow-tooltip />
|
||||
<el-table-column label="学号" prop="studentNo" width="120" align="center" show-overflow-tooltip />
|
||||
<el-table-column label="班级" prop="className" width="120" align="center" show-overflow-tooltip />
|
||||
<el-table-column label="学院" prop="collegeName" width="120" align="center" show-overflow-tooltip />
|
||||
<el-table-column label="批次名称" prop="batchName" width="150" align="center" show-overflow-tooltip />
|
||||
<el-table-column label="提交状态" prop="submissionStatus" width="100" align="center">
|
||||
<template #default="scope">
|
||||
<el-tag :type="getSubmissionStatusTagType(scope.row.submissionStatus)" size="small">
|
||||
{{ getSubmissionStatusText(scope.row.submissionStatus) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="审核状态" prop="approvalStatus" width="100" align="center">
|
||||
<template #default="scope">
|
||||
<el-tag :type="getApprovalStatusTagType(scope.row.approvalStatus)" size="small">
|
||||
{{ getApprovalStatusText(scope.row.approvalStatus) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="提交时间" prop="submissionTime" width="160" align="center" sortable="custom">
|
||||
<template #default="scope">
|
||||
<span>{{ proxy.parseTime(scope.row.submissionTime, '{y}-{m}-{d} {h}:{i}') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="审核时间" prop="approvalTime" width="160" align="center" sortable="custom">
|
||||
<template #default="scope">
|
||||
<span>{{ proxy.parseTime(scope.row.approvalTime, '{y}-{m}-{d} {h}:{i}') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="审核人" prop="approvalByName" width="100" align="center" show-overflow-tooltip />
|
||||
<el-table-column label="审核意见" prop="approvalComments" width="200" align="center" show-overflow-tooltip />
|
||||
<el-table-column label="操作" align="center" width="200" fixed="right" class-name="small-padding fixed-width">
|
||||
<template #default="scope">
|
||||
<div class="action-buttons">
|
||||
<el-tooltip content="查看详情" placement="top">
|
||||
<el-button link type="info" icon="View" size="small" @click="handleView(scope.row)" />
|
||||
</el-tooltip>
|
||||
<el-tooltip content="审核历史" placement="top">
|
||||
<el-button link type="warning" icon="Document" size="small" @click="handleHistory(scope.row)" />
|
||||
</el-tooltip>
|
||||
<template v-if="scope.row.approvalStatus === 'PENDING'">
|
||||
<el-tooltip content="通过" placement="top">
|
||||
<el-button link type="success" icon="Check" size="small" @click="handleApprove(scope.row, true)" v-hasPermi="['xg:studentInfo:approve']" />
|
||||
</el-tooltip>
|
||||
<el-tooltip content="拒绝" placement="top">
|
||||
<el-button link type="danger" icon="Close" size="small" @click="handleApprove(scope.row, false)" v-hasPermi="['xg:studentInfo:approve']" />
|
||||
</el-tooltip>
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-tooltip content="撤销审核" placement="top">
|
||||
<el-button link type="warning" icon="RefreshLeft" size="small" @click="handleRevoke(scope.row)" v-hasPermi="['xg:studentInfo:approve']" />
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<div class="pagination-container">
|
||||
<pagination v-show="total > 0" :total="total" :page="queryParams.pageNum" :limit="queryParams.pageSize" @pagination="getList" />
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<!-- 审核对话框 -->
|
||||
<el-dialog :title="approvalDialog.title" v-model="approvalDialog.visible" width="600px" append-to-body>
|
||||
<el-form ref="approvalFormRef" :model="approvalForm" :rules="approvalRules" label-width="100px">
|
||||
<el-form-item label="审核结果" prop="approved">
|
||||
<el-radio-group v-model="approvalForm.approved">
|
||||
<el-radio :label="true">通过</el-radio>
|
||||
<el-radio :label="false">拒绝</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="审核意见" prop="comments">
|
||||
<el-input
|
||||
v-model="approvalForm.comments"
|
||||
type="textarea"
|
||||
:rows="4"
|
||||
placeholder="请输入审核意见"
|
||||
maxlength="500"
|
||||
show-word-limit
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="approvalDialog.visible = false">取消</el-button>
|
||||
<el-button type="primary" @click="submitApproval">确定</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 审核历史对话框 -->
|
||||
<el-dialog title="审核历史" v-model="historyDialog.visible" width="800px" append-to-body>
|
||||
<el-timeline>
|
||||
<el-timeline-item
|
||||
v-for="item in historyList"
|
||||
:key="item.id"
|
||||
:timestamp="proxy.parseTime(item.createTime, '{y}-{m}-{d} {h}:{i}:{s}')"
|
||||
placement="top"
|
||||
>
|
||||
<el-card>
|
||||
<div class="history-item">
|
||||
<div class="history-header">
|
||||
<span class="history-action">{{ getHistoryActionText(item.approvalStatus) }}</span>
|
||||
<span class="history-user">{{ item.approvalByName }}</span>
|
||||
</div>
|
||||
<div class="history-content" v-if="item.approvalComments">
|
||||
{{ item.approvalComments }}
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-timeline-item>
|
||||
</el-timeline>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, onMounted, getCurrentInstance } from 'vue';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import {
|
||||
queryPendingApprovalList,
|
||||
queryApprovedList,
|
||||
approveStudentInfo,
|
||||
batchApproveStudentInfo,
|
||||
revokeApproval,
|
||||
queryApprovalHistory,
|
||||
getApprovalStatistics,
|
||||
exportApprovalResults,
|
||||
type XgStudentInfoApprovalQuery,
|
||||
type XgStudentInfoApprovalVO
|
||||
} from '@/api/xg-xx/studentInfoApproval';
|
||||
import { listAllCollectionBatch } from '@/api/xg-xx/collectionBatch';
|
||||
|
||||
const { proxy } = getCurrentInstance() as any;
|
||||
|
||||
// 响应式数据
|
||||
const loading = ref(false);
|
||||
const showSearch = ref(true);
|
||||
const ids = ref<number[]>([]);
|
||||
const single = ref(true);
|
||||
const multiple = ref(true);
|
||||
const total = ref(0);
|
||||
const approvalList = ref<XgStudentInfoApprovalVO[]>([]);
|
||||
const batchOptions = ref<any[]>([]);
|
||||
const currentTab = ref('pending');
|
||||
const pendingCount = ref(0);
|
||||
const approvedCount = ref(0);
|
||||
|
||||
// 查询参数
|
||||
const queryParams = reactive<XgStudentInfoApprovalQuery>({
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
batchId: undefined,
|
||||
studentName: undefined,
|
||||
studentNo: undefined,
|
||||
submissionStatus: undefined,
|
||||
approvalStatus: undefined,
|
||||
className: undefined,
|
||||
collegeName: undefined
|
||||
});
|
||||
|
||||
// 审核对话框
|
||||
const approvalDialog = reactive({
|
||||
visible: false,
|
||||
title: '',
|
||||
type: 'single' as 'single' | 'batch'
|
||||
});
|
||||
|
||||
const approvalForm = reactive({
|
||||
approved: true,
|
||||
comments: '',
|
||||
studentIds: [] as number[],
|
||||
batchId: undefined as number | undefined
|
||||
});
|
||||
|
||||
const approvalRules = {
|
||||
approved: [{ required: true, message: '请选择审核结果', trigger: 'change' }]
|
||||
};
|
||||
|
||||
// 审核历史对话框
|
||||
const historyDialog = reactive({
|
||||
visible: false
|
||||
});
|
||||
|
||||
const historyList = ref<XgStudentInfoApprovalVO[]>([]);
|
||||
|
||||
// 获取列表数据
|
||||
const getList = async () => {
|
||||
loading.value = true;
|
||||
try {
|
||||
let response;
|
||||
if (currentTab.value === 'pending') {
|
||||
response = await queryPendingApprovalList(queryParams);
|
||||
} else {
|
||||
response = await queryApprovedList(queryParams);
|
||||
}
|
||||
|
||||
approvalList.value = response.rows;
|
||||
total.value = response.total;
|
||||
|
||||
// 获取统计信息
|
||||
if (queryParams.batchId) {
|
||||
const statsResponse = await getApprovalStatistics(queryParams.batchId);
|
||||
pendingCount.value = statsResponse.data.pendingCount || 0;
|
||||
approvedCount.value = (statsResponse.data.approvedCount || 0) + (statsResponse.data.rejectedCount || 0);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取列表失败:', error);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 获取批次选项
|
||||
const getBatchOptions = async () => {
|
||||
try {
|
||||
const response = await listAllCollectionBatch({});
|
||||
batchOptions.value = response.data;
|
||||
} catch (error) {
|
||||
console.error('获取批次选项失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 切换标签页
|
||||
const switchTab = (tab: string) => {
|
||||
currentTab.value = tab;
|
||||
queryParams.pageNum = 1;
|
||||
getList();
|
||||
};
|
||||
|
||||
// 搜索
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNum = 1;
|
||||
getList();
|
||||
};
|
||||
|
||||
// 重置搜索
|
||||
const resetQuery = () => {
|
||||
proxy.resetForm('queryFormRef');
|
||||
handleQuery();
|
||||
};
|
||||
|
||||
// 多选框选中数据
|
||||
const handleSelectionChange = (selection: XgStudentInfoApprovalVO[]) => {
|
||||
ids.value = selection.map(item => item.studentId!);
|
||||
single.value = selection.length !== 1;
|
||||
multiple.value = !selection.length;
|
||||
};
|
||||
|
||||
// 排序变化
|
||||
const handleSortChange = (column: any) => {
|
||||
// 处理排序逻辑
|
||||
getList();
|
||||
};
|
||||
|
||||
// 查看详情
|
||||
const handleView = (row: XgStudentInfoApprovalVO) => {
|
||||
// 跳转到学生信息详情页面
|
||||
proxy.$router.push(`/xg-xx/studentInfo/detail/${row.studentId}`);
|
||||
};
|
||||
|
||||
// 审核操作
|
||||
const handleApprove = (row: XgStudentInfoApprovalVO, approved: boolean) => {
|
||||
approvalDialog.visible = true;
|
||||
approvalDialog.title = approved ? '审核通过' : '审核拒绝';
|
||||
approvalDialog.type = 'single';
|
||||
approvalForm.approved = approved;
|
||||
approvalForm.comments = '';
|
||||
approvalForm.studentIds = [row.studentId!];
|
||||
approvalForm.batchId = row.batchId;
|
||||
};
|
||||
|
||||
// 批量审核
|
||||
const handleBatchApprove = (approved: boolean) => {
|
||||
if (ids.value.length === 0) {
|
||||
ElMessage.warning('请选择要审核的学生');
|
||||
return;
|
||||
}
|
||||
|
||||
approvalDialog.visible = true;
|
||||
approvalDialog.title = approved ? '批量审核通过' : '批量审核拒绝';
|
||||
approvalDialog.type = 'batch';
|
||||
approvalForm.approved = approved;
|
||||
approvalForm.comments = '';
|
||||
approvalForm.studentIds = ids.value;
|
||||
approvalForm.batchId = queryParams.batchId;
|
||||
};
|
||||
|
||||
// 提交审核
|
||||
const submitApproval = async () => {
|
||||
try {
|
||||
if (approvalDialog.type === 'single') {
|
||||
await approveStudentInfo(
|
||||
approvalForm.studentIds[0],
|
||||
approvalForm.batchId!,
|
||||
approvalForm.approved,
|
||||
approvalForm.comments
|
||||
);
|
||||
} else {
|
||||
await batchApproveStudentInfo(
|
||||
approvalForm.studentIds,
|
||||
approvalForm.batchId!,
|
||||
approvalForm.approved,
|
||||
approvalForm.comments
|
||||
);
|
||||
}
|
||||
|
||||
ElMessage.success('审核成功');
|
||||
approvalDialog.visible = false;
|
||||
getList();
|
||||
} catch (error) {
|
||||
console.error('审核失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 撤销审核
|
||||
const handleRevoke = async (row: XgStudentInfoApprovalVO) => {
|
||||
try {
|
||||
await ElMessageBox.confirm('确定要撤销该学生的审核吗?', '系统提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
});
|
||||
|
||||
await revokeApproval(row.studentId!, row.batchId!);
|
||||
ElMessage.success('撤销成功');
|
||||
getList();
|
||||
} catch (error) {
|
||||
console.error('撤销失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 查看审核历史
|
||||
const handleHistory = async (row: XgStudentInfoApprovalVO) => {
|
||||
try {
|
||||
const response = await queryApprovalHistory(row.studentId!, row.batchId!);
|
||||
historyList.value = response.data;
|
||||
historyDialog.visible = true;
|
||||
} catch (error) {
|
||||
console.error('获取审核历史失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 导出
|
||||
const handleExport = () => {
|
||||
proxy?.download(
|
||||
'xg/studentInfoApproval/export',
|
||||
queryParams,
|
||||
`学生信息审核结果_${proxy.parseTime(new Date(), '{y}{m}{d}{h}{i}{s}')}.xlsx`
|
||||
);
|
||||
};
|
||||
|
||||
// 刷新
|
||||
const handleRefresh = () => {
|
||||
getList();
|
||||
};
|
||||
|
||||
// 获取提交状态标签类型
|
||||
const getSubmissionStatusTagType = (status: string) => {
|
||||
const statusMap: Record<string, string> = {
|
||||
'DRAFT': 'info',
|
||||
'SUBMITTED': 'warning',
|
||||
'APPROVED': 'success',
|
||||
'REJECTED': 'danger'
|
||||
};
|
||||
return statusMap[status] || 'info';
|
||||
};
|
||||
|
||||
// 获取提交状态文本
|
||||
const getSubmissionStatusText = (status: string) => {
|
||||
const statusMap: Record<string, string> = {
|
||||
'DRAFT': '草稿',
|
||||
'SUBMITTED': '已提交',
|
||||
'APPROVED': '已通过',
|
||||
'REJECTED': '已拒绝'
|
||||
};
|
||||
return statusMap[status] || '未知';
|
||||
};
|
||||
|
||||
// 获取审核状态标签类型
|
||||
const getApprovalStatusTagType = (status: string) => {
|
||||
const statusMap: Record<string, string> = {
|
||||
'PENDING': 'warning',
|
||||
'APPROVED': 'success',
|
||||
'REJECTED': 'danger'
|
||||
};
|
||||
return statusMap[status] || 'info';
|
||||
};
|
||||
|
||||
// 获取审核状态文本
|
||||
const getApprovalStatusText = (status: string) => {
|
||||
const statusMap: Record<string, string> = {
|
||||
'PENDING': '待审核',
|
||||
'APPROVED': '已通过',
|
||||
'REJECTED': '已拒绝'
|
||||
};
|
||||
return statusMap[status] || '未知';
|
||||
};
|
||||
|
||||
// 获取历史操作文本
|
||||
const getHistoryActionText = (status: string) => {
|
||||
const statusMap: Record<string, string> = {
|
||||
'PENDING': '提交审核',
|
||||
'APPROVED': '审核通过',
|
||||
'REJECTED': '审核拒绝'
|
||||
};
|
||||
return statusMap[status] || '未知操作';
|
||||
};
|
||||
|
||||
// 初始化
|
||||
onMounted(() => {
|
||||
getBatchOptions();
|
||||
getList();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.page-container {
|
||||
background-color: #f5f5f5;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.toolbar-wrapper {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.toolbar-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.toolbar-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.table-wrapper {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.pagination-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.history-item {
|
||||
padding: 8px 0;
|
||||
}
|
||||
|
||||
.history-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.history-action {
|
||||
font-weight: bold;
|
||||
color: #409eff;
|
||||
}
|
||||
|
||||
.history-user {
|
||||
color: #666;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.history-content {
|
||||
color: #333;
|
||||
line-height: 1.5;
|
||||
background-color: #f9f9f9;
|
||||
padding: 8px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
</style>
|
||||
Reference in new issue
Block a user