fix(阅读任务): 前后端字段对齐与列表时间展示;教师端时间筛选与 dateRange 修复
Made-with: Cursor
This commit is contained in:
parent
b551af1355
commit
aa44f6262b
@ -1170,11 +1170,22 @@ export interface SchoolTaskQueryParams {
|
||||
|
||||
// ========== 新 API:只读接口 ==========
|
||||
|
||||
/** 与后端 TaskResponse 对齐:JSON 中 taskType/endDate(兼容历史 type/dueDate) */
|
||||
function normalizeSchoolTask(raw: any): SchoolTask {
|
||||
if (!raw) return raw;
|
||||
return {
|
||||
...raw,
|
||||
taskType: (raw.taskType ?? raw.type) as SchoolTask['taskType'],
|
||||
endDate: (raw.endDate ?? raw.dueDate ?? '') as string,
|
||||
startDate: (raw.startDate ?? '') as string,
|
||||
};
|
||||
}
|
||||
|
||||
// 获取学校端任务列表(只读,多维度筛选)
|
||||
export const getReadingTaskList = (params?: SchoolTaskQueryParams) =>
|
||||
http.get<{ list: SchoolTask[]; total: number; pageNum: number; pageSize: number; pages: number }>('/v1/school/reading-tasks', { params })
|
||||
.then(res => ({
|
||||
items: res.list || [],
|
||||
items: (res.list || []).map(normalizeSchoolTask),
|
||||
total: res.total || 0,
|
||||
pageNum: res.pageNum || 1,
|
||||
pageSize: res.pageSize || 10,
|
||||
@ -1183,7 +1194,7 @@ export const getReadingTaskList = (params?: SchoolTaskQueryParams) =>
|
||||
|
||||
// 获取任务详情(只读)
|
||||
export const getReadingTaskDetail = (taskId: number) =>
|
||||
http.get<SchoolTask>(`/v1/school/reading-tasks/${taskId}`);
|
||||
http.get<SchoolTask>(`/v1/school/reading-tasks/${taskId}`).then(normalizeSchoolTask);
|
||||
|
||||
// 获取任务完成情况列表
|
||||
export const getReadingTaskCompletions = (taskId: number, params?: {
|
||||
|
||||
@ -761,18 +761,38 @@ export interface UpdateTaskCompletionDto {
|
||||
feedback?: string;
|
||||
}
|
||||
|
||||
/** 与后端 TaskResponse 对齐:taskType/endDate(兼容 type/dueDate) */
|
||||
function normalizeTeacherTask(raw: any): TeacherTask {
|
||||
if (!raw) return raw;
|
||||
return {
|
||||
...raw,
|
||||
taskType: (raw.taskType ?? raw.type) as TeacherTask['taskType'],
|
||||
endDate: (raw.endDate ?? raw.dueDate ?? '') as string,
|
||||
startDate: (raw.startDate ?? '') as string,
|
||||
};
|
||||
}
|
||||
|
||||
// 获取教师任务列表
|
||||
export const getTeacherTasks = (params?: { pageNum?: number; pageSize?: number; keyword?: string; type?: string; status?: string }) =>
|
||||
export const getTeacherTasks = (params?: {
|
||||
pageNum?: number;
|
||||
pageSize?: number;
|
||||
keyword?: string;
|
||||
type?: string;
|
||||
taskType?: string;
|
||||
status?: string;
|
||||
startDate?: string;
|
||||
endDate?: string;
|
||||
}) =>
|
||||
http.get<{ list: any[]; total: number; pageNum: number; pageSize: number }>('/v1/teacher/tasks', { params })
|
||||
.then(res => ({
|
||||
items: res.list || [],
|
||||
items: (res.list || []).map(normalizeTeacherTask),
|
||||
total: res.total || 0,
|
||||
page: res.pageNum || 1,
|
||||
pageSize: res.pageSize || 10,
|
||||
}));
|
||||
|
||||
export const getTeacherTask = (id: number) =>
|
||||
http.get(`/v1/teacher/tasks/${id}`) as any;
|
||||
http.get(`/v1/teacher/tasks/${id}`).then(normalizeTeacherTask) as Promise<TeacherTask>;
|
||||
|
||||
// 获取任务完成情况列表(新版,支持分页和状态筛选)
|
||||
export const getTeacherTaskCompletions = (taskId: number, params?: {
|
||||
|
||||
@ -132,7 +132,7 @@
|
||||
</span>
|
||||
<span>
|
||||
<CalendarOutlined />
|
||||
{{ formatDate(task.startDate) }} - {{ formatDate(task.endDate) }}
|
||||
{{ formatTaskRange(task) }}
|
||||
</span>
|
||||
<span>
|
||||
<TeamOutlined /> {{ task.targetCount || 0 }} 个目标
|
||||
@ -581,7 +581,24 @@ const feedbackResultTexts: Record<string, string> = {
|
||||
const getFeedbackResultColor = (result: string) => feedbackResultColors[result] || 'default';
|
||||
const getFeedbackResultText = (result: string) => feedbackResultTexts[result] || result;
|
||||
|
||||
const formatDate = (date?: string) => date ? dayjs(date).format('YYYY-MM-DD') : '-';
|
||||
const formatDate = (date?: string | number[] | null) => {
|
||||
if (date == null || date === '') return '-';
|
||||
if (Array.isArray(date) && date.length >= 3) {
|
||||
const [y, m, d] = date;
|
||||
return dayjs(`${y}-${String(m).padStart(2, '0')}-${String(d).padStart(2, '0')}`).format('YYYY-MM-DD');
|
||||
}
|
||||
return dayjs(date as string).format('YYYY-MM-DD');
|
||||
};
|
||||
|
||||
/** 列表展示:与后端 startDate/endDate 对齐,避免仅一端有值时出现「日期 --」 */
|
||||
const formatTaskRange = (task: SchoolTask) => {
|
||||
const s = formatDate(task.startDate as any);
|
||||
const e = formatDate((task as any).endDate ?? (task as any).dueDate);
|
||||
if (e === '-') return s;
|
||||
if (s === '-') return e;
|
||||
return `${s} 至 ${e}`;
|
||||
};
|
||||
|
||||
const formatDateTime = (date?: string) => date ? dayjs(date).format('YYYY-MM-DD HH:mm') : '-';
|
||||
|
||||
// 模拟任务统计数据(实际应该从后端获取)
|
||||
|
||||
@ -62,6 +62,13 @@
|
||||
</a-select>
|
||||
<a-input-search v-model:value="filters.keyword" placeholder="搜索任务标题" style="width: 200px;" @search="loadTasks"
|
||||
allow-clear />
|
||||
<a-range-picker
|
||||
v-model:value="dateRange as any"
|
||||
style="width: 260px"
|
||||
:placeholder="['开始日期', '结束日期']"
|
||||
allow-clear
|
||||
@change="onDateRangeChange"
|
||||
/>
|
||||
</a-space>
|
||||
</div>
|
||||
|
||||
@ -84,7 +91,7 @@
|
||||
</span>
|
||||
<span>
|
||||
<CalendarOutlined />
|
||||
{{ formatDate(task.startDate) }} - {{ formatDate(task.endDate) }}
|
||||
{{ formatTaskRange(task) }}
|
||||
</span>
|
||||
<span>
|
||||
<TeamOutlined /> {{ task.targetCount || 0 }} 个目标
|
||||
@ -439,6 +446,14 @@ const filters = reactive({
|
||||
keyword: undefined as string | undefined,
|
||||
});
|
||||
|
||||
/** 列表筛选:任务时间范围(与 createForm.dateRange 区分,勿混用) */
|
||||
const dateRange = ref<[Dayjs, Dayjs] | null>(null);
|
||||
|
||||
const onDateRangeChange = () => {
|
||||
currentPage.value = 1;
|
||||
loadTasks();
|
||||
};
|
||||
|
||||
// 统计数据
|
||||
const stats = computed(() => {
|
||||
const all = tasks.value;
|
||||
@ -521,7 +536,22 @@ const getTypeText = (type: string) => typeMap[type]?.text || type;
|
||||
const getTypeColor = (type: string) => typeMap[type]?.color || 'default';
|
||||
const getStatusText = (status: string) => statusMap[status]?.text || status;
|
||||
const getStatusColor = (status: string) => statusMap[status]?.color || 'default';
|
||||
const formatDate = (date?: string) => date ? dayjs(date).format('YYYY-MM-DD') : '-';
|
||||
const formatDate = (date?: string | number[] | null) => {
|
||||
if (date == null || date === '') return '-';
|
||||
if (Array.isArray(date) && date.length >= 3) {
|
||||
const [y, m, d] = date;
|
||||
return dayjs(`${y}-${String(m).padStart(2, '0')}-${String(d).padStart(2, '0')}`).format('YYYY-MM-DD');
|
||||
}
|
||||
return dayjs(date as string).format('YYYY-MM-DD');
|
||||
};
|
||||
|
||||
const formatTaskRange = (task: TeacherTask) => {
|
||||
const s = formatDate(task.startDate as any);
|
||||
const e = formatDate((task as any).endDate ?? (task as any).dueDate);
|
||||
if (e === '-') return s;
|
||||
if (s === '-') return e;
|
||||
return `${s} 至 ${e}`;
|
||||
};
|
||||
|
||||
const getCompletionRate = (task: TeacherTask) => {
|
||||
if (!task.completionCount || !task.targetCount) return 0;
|
||||
@ -546,11 +576,16 @@ const filterCourseOption = (input: string, option: any) => {
|
||||
const loadTasks = async () => {
|
||||
loading.value = true;
|
||||
try {
|
||||
const data = await getTeacherTasks({
|
||||
const params: Parameters<typeof getTeacherTasks>[0] = {
|
||||
pageNum: currentPage.value,
|
||||
pageSize: pageSize.value,
|
||||
...filters,
|
||||
});
|
||||
};
|
||||
if (dateRange.value?.[0] && dateRange.value?.[1]) {
|
||||
params.startDate = dateRange.value[0].format('YYYY-MM-DD');
|
||||
params.endDate = dateRange.value[1].format('YYYY-MM-DD');
|
||||
}
|
||||
const data = await getTeacherTasks(params);
|
||||
tasks.value = data.items || [];
|
||||
total.value = data.total || 0;
|
||||
} catch (error: any) {
|
||||
|
||||
@ -69,11 +69,13 @@ public class TeacherTaskController {
|
||||
@RequestParam(required = false) String keyword,
|
||||
@RequestParam(required = false) String type,
|
||||
@RequestParam(required = false) String taskType,
|
||||
@RequestParam(required = false) String status) {
|
||||
@RequestParam(required = false) String status,
|
||||
@RequestParam(required = false) String startDate,
|
||||
@RequestParam(required = false) String endDate) {
|
||||
Long tenantId = SecurityUtils.getCurrentTenantId();
|
||||
Long teacherId = SecurityUtils.getCurrentUserId();
|
||||
String typeFilter = (type != null && !type.isEmpty()) ? type : taskType;
|
||||
Page<Task> page = taskService.getTaskPage(tenantId, pageNum, pageSize, keyword, typeFilter, status, teacherId);
|
||||
Page<Task> page = taskService.getTaskPage(tenantId, pageNum, pageSize, keyword, typeFilter, status, teacherId, startDate, endDate);
|
||||
List<TaskResponse> voList = taskMapper.toVO(page.getRecords());
|
||||
for (int i = 0; i < voList.size(); i++) {
|
||||
taskService.enrichTaskResponseWithStats(voList.get(i), page.getRecords().get(i).getId());
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
package com.reading.platform.dto.response;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAlias;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
@ -29,6 +31,8 @@ public class TaskResponse {
|
||||
private String description;
|
||||
|
||||
@Schema(description = "任务类型")
|
||||
@JsonProperty("taskType")
|
||||
@JsonAlias({"type"})
|
||||
private String type;
|
||||
|
||||
@Schema(description = "关联绘本名称")
|
||||
@ -47,6 +51,8 @@ public class TaskResponse {
|
||||
private LocalDate startDate;
|
||||
|
||||
@Schema(description = "截止日期")
|
||||
@JsonProperty("endDate")
|
||||
@JsonAlias({"dueDate"})
|
||||
private LocalDate dueDate;
|
||||
|
||||
@Schema(description = "状态")
|
||||
|
||||
@ -35,6 +35,11 @@ public interface TaskService extends com.baomidou.mybatisplus.extension.service.
|
||||
*/
|
||||
Page<Task> getTaskPage(Long tenantId, Integer pageNum, Integer pageSize, String keyword, String type, String status, Long creatorId);
|
||||
|
||||
/**
|
||||
* 分页查询任务(支持按创建人、任务时间范围筛选;与学校端时间交集逻辑一致)
|
||||
*/
|
||||
Page<Task> getTaskPage(Long tenantId, Integer pageNum, Integer pageSize, String keyword, String type, String status, Long creatorId, String startDate, String endDate);
|
||||
|
||||
Page<Task> getTasksByStudentId(Long studentId, Integer pageNum, Integer pageSize, String status);
|
||||
|
||||
/**
|
||||
|
||||
@ -173,11 +173,16 @@ public class TaskServiceImpl extends ServiceImpl<TaskMapper, Task>
|
||||
|
||||
@Override
|
||||
public Page<Task> getTaskPage(Long tenantId, Integer pageNum, Integer pageSize, String keyword, String type, String status) {
|
||||
return getTaskPage(tenantId, pageNum, pageSize, keyword, type, status, null);
|
||||
return getTaskPage(tenantId, pageNum, pageSize, keyword, type, status, null, null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<Task> getTaskPage(Long tenantId, Integer pageNum, Integer pageSize, String keyword, String type, String status, Long creatorId) {
|
||||
return getTaskPage(tenantId, pageNum, pageSize, keyword, type, status, creatorId, null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<Task> getTaskPage(Long tenantId, Integer pageNum, Integer pageSize, String keyword, String type, String status, Long creatorId, String startDate, String endDate) {
|
||||
Page<Task> page = new Page<>(pageNum, pageSize);
|
||||
LambdaQueryWrapper<Task> wrapper = new LambdaQueryWrapper<>();
|
||||
|
||||
@ -195,6 +200,33 @@ public class TaskServiceImpl extends ServiceImpl<TaskMapper, Task>
|
||||
if (StringUtils.hasText(status)) {
|
||||
wrapper.eq(Task::getStatus, status);
|
||||
}
|
||||
|
||||
// 任务时间筛选:与学校端 getSchoolTaskList 一致(任务区间与查询范围有交集)
|
||||
if (StringUtils.hasText(startDate) && StringUtils.hasText(endDate)) {
|
||||
try {
|
||||
LocalDate rangeStart = LocalDate.parse(startDate);
|
||||
LocalDate rangeEnd = LocalDate.parse(endDate);
|
||||
wrapper.le(Task::getStartDate, rangeEnd);
|
||||
wrapper.and(w -> w.ge(Task::getDueDate, rangeStart).or().isNull(Task::getDueDate));
|
||||
} catch (Exception e) {
|
||||
log.warn("解析日期参数失败 startDate={}, endDate={}", startDate, endDate, e);
|
||||
}
|
||||
} else if (StringUtils.hasText(startDate)) {
|
||||
try {
|
||||
LocalDate rangeStart = LocalDate.parse(startDate);
|
||||
wrapper.ge(Task::getDueDate, rangeStart);
|
||||
} catch (Exception e) {
|
||||
log.warn("解析 startDate 参数失败: {}", startDate, e);
|
||||
}
|
||||
} else if (StringUtils.hasText(endDate)) {
|
||||
try {
|
||||
LocalDate rangeEnd = LocalDate.parse(endDate);
|
||||
wrapper.le(Task::getStartDate, rangeEnd);
|
||||
} catch (Exception e) {
|
||||
log.warn("解析 endDate 参数失败: {}", endDate, e);
|
||||
}
|
||||
}
|
||||
|
||||
wrapper.orderByDesc(Task::getCreatedAt);
|
||||
|
||||
return taskMapper.selectPage(page, wrapper);
|
||||
@ -758,23 +790,27 @@ public class TaskServiceImpl extends ServiceImpl<TaskMapper, Task>
|
||||
// 转换为响应对象
|
||||
// TODO: 使用 TaskMapper 转换
|
||||
List<TaskResponse> responses = taskPage.getRecords().stream()
|
||||
.map(task -> TaskResponse.builder()
|
||||
.id(task.getId())
|
||||
.tenantId(task.getTenantId())
|
||||
.title(task.getTitle())
|
||||
.description(task.getDescription())
|
||||
.type(task.getType())
|
||||
.relatedBookName(task.getRelatedBookName())
|
||||
.courseId(task.getCourseId())
|
||||
.creatorId(task.getCreatorId())
|
||||
.creatorRole(task.getCreatorRole())
|
||||
.startDate(task.getStartDate())
|
||||
.dueDate(task.getDueDate())
|
||||
.status(task.getStatus())
|
||||
.attachments(task.getAttachments())
|
||||
.createdAt(task.getCreatedAt())
|
||||
.updatedAt(task.getUpdatedAt())
|
||||
.build())
|
||||
.map(task -> {
|
||||
TaskResponse r = TaskResponse.builder()
|
||||
.id(task.getId())
|
||||
.tenantId(task.getTenantId())
|
||||
.title(task.getTitle())
|
||||
.description(task.getDescription())
|
||||
.type(task.getType())
|
||||
.relatedBookName(task.getRelatedBookName())
|
||||
.courseId(task.getCourseId())
|
||||
.creatorId(task.getCreatorId())
|
||||
.creatorRole(task.getCreatorRole())
|
||||
.startDate(task.getStartDate())
|
||||
.dueDate(task.getDueDate())
|
||||
.status(task.getStatus())
|
||||
.attachments(task.getAttachments())
|
||||
.createdAt(task.getCreatedAt())
|
||||
.updatedAt(task.getUpdatedAt())
|
||||
.build();
|
||||
enrichTaskResponseWithStats(r, task.getId());
|
||||
return r;
|
||||
})
|
||||
.toList();
|
||||
|
||||
return PageResult.of(responses, taskPage.getTotal(), taskPage.getCurrent(), taskPage.getSize());
|
||||
|
||||
Loading…
Reference in New Issue
Block a user