fix(app): Phase 1.1 紧急修复 — SyncEngine 接入 + authorId + catch 异常处理
Some checks failed
Main Merge / backend (push) Has been cancelled
Main Merge / frontend (push) Has been cancelled

- feat(sync): SyncEngine 接入 EditorPage, 保存时 enqueue + 网络恢复自动 trySync
- fix(editor): authorId 从 AuthBloc 获取, 替代硬编码 'local'
- fix(bloc): class_bloc/calendar/profile/parent catch(_).全部改为 debugPrint
- feat(editor): 编辑器工具栏拆分 (brush_panel/tag_panel/text_format_bar/dot_grid_painter)
- feat(editor): EditorBloc 扩展 + EditorPage 增强
- feat(search): SearchBloc 扩展搜索功能
- feat(home): HomeBloc/HomePage 增强
- feat(auth): LoginPage 增强
- feat(templates): TemplateGalleryPage 重构
- fix(web): 管理端班级/日记页面修复
- fix(server): comment_service + theme_handler 修复
- docs: 添加全链路审计报告和验证截图
This commit is contained in:
iven
2026-06-02 21:21:43 +08:00
parent 7e928ae1e1
commit 49d4aa36a7
55 changed files with 2738 additions and 677 deletions

View File

@@ -2,9 +2,18 @@ import client from '../client';
import type { SchoolClass, CreateClassReq, ClassMember, PaginatedResponse } from './types';
export const classApi = {
/** 班级列表 — 后端返回纯数组,前端转换为 PaginatedResponse 格式 */
list: (params?: { page?: number; page_size?: number }) =>
client.get<{ success: boolean; data: PaginatedResponse<SchoolClass> }>('/diary/classes', { params })
.then((r) => r.data.data),
client.get<{ success: boolean; data: SchoolClass[] }>('/diary/classes', { params })
.then((r) => {
const raw = r.data.data;
// 后端返回纯数组,包装为分页格式
if (Array.isArray(raw)) {
return { data: raw, total: raw.length, page: params?.page ?? 1, page_size: params?.page_size ?? 20 } as PaginatedResponse<SchoolClass>;
}
// 兼容:如果后端已升级为分页格式
return raw as unknown as PaginatedResponse<SchoolClass>;
}),
myClasses: () =>
client.get<{ success: boolean; data: SchoolClass[] }>('/diary/classes/my')

View File

@@ -19,7 +19,7 @@ interface DrawerFormProps {
sections?: FormSection[];
children?: React.ReactNode;
columns?: 1 | 2;
form?: ReturnType<typeof Form.useForm>[0];
form?: ReturnType<typeof Form.useForm<Record<string, unknown>>>[0];
onValuesChange?: (changedValues: Record<string, unknown>, allValues: Record<string, unknown>) => void;
}
@@ -37,7 +37,7 @@ export function DrawerForm({
form: externalForm,
onValuesChange,
}: DrawerFormProps) {
const [internalForm] = Form.useForm();
const [internalForm] = Form.useForm<Record<string, unknown>>();
const form = externalForm ?? internalForm;
const isDark = useThemeMode();
@@ -45,7 +45,8 @@ export function DrawerForm({
if (open) {
form.resetFields();
if (initialValues) {
form.setFieldsValue(initialValues);
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Ant Design 6 setFieldsValue requires Store type
form.setFieldsValue(initialValues as any);
}
}
}, [open, initialValues, form]);

View File

@@ -1,6 +1,6 @@
import { useCallback, useState, useEffect, useMemo } from 'react';
import { Layout, Avatar, Space, Dropdown, Tooltip, Spin, theme, Menu, message } from 'antd';
import type { MenuItemType, SubMenuType } from 'antd/es/menu/hooks/useItems';
import type { MenuItemType, SubMenuType } from 'antd/es/menu/interface';
import {
MenuFoldOutlined,
MenuUnfoldOutlined,

View File

@@ -7,7 +7,6 @@ import {
Input,
Tag,
Drawer,
Modal,
Badge,
Typography,
message,
@@ -26,14 +25,12 @@ import { PageContainer } from '../../components/PageContainer';
import { DrawerForm } from '../../components/DrawerForm';
import { useCrudDrawer } from '../../hooks/useCrudDrawer';
import { usePaginatedData } from '../../hooks/usePaginatedData';
import { useApiRequest } from '../../hooks/useApiRequest';
import { useThemeMode } from '../../hooks/useThemeMode';
const { Text } = Typography;
export default function ClassList() {
const isDark = useThemeMode();
const { execute } = useApiRequest();
const {
data: classes,
@@ -41,13 +38,13 @@ export default function ClassList() {
page,
loading,
refresh,
} = usePaginatedData<SchoolClass>(async (p, pageSize) => {
} = usePaginatedData<SchoolClass>(async (p: number, pageSize: number) => {
const result = await classApi.list({ page: p, page_size: pageSize });
return { data: result.data, total: result.total };
}, 20);
// --- Create/Edit drawer ---
const classDrawer = useCrudDrawer<SchoolClass>({
const classDrawer = useCrudDrawer<{ version: number } & SchoolClass>({
getId: (r) => r.id,
onCreate: async (values) => {
await classApi.create({ name: values.name as string, school_name: values.school_name as string | undefined });

View File

@@ -31,7 +31,6 @@ import { commentApi } from '../../api/diary/comments';
import { classApi } from '../../api/diary/classes';
import type { JournalEntry, Comment, SchoolClass } from '../../api/diary/types';
import { PageContainer } from '../../components/PageContainer';
import { FilterBar } from '../../components/FilterBar';
import { useApiRequest } from '../../hooks/useApiRequest';
import { useThemeMode } from '../../hooks/useThemeMode';
@@ -443,7 +442,7 @@ export default function JournalList() {
</Descriptions.Item>
</Descriptions>
<Divider orientation="left" style={{ fontSize: 15, fontWeight: 500 }}>
<Divider style={{ fontSize: 15, fontWeight: 500, textAlign: 'left' }}>
({comments.length})
</Divider>