- {msg.role === "user" ? "您" : "AI 助手"} • {msg.time}
+ ) : null}
+
+
+ {!activeSession || activeSession.messages.length === 0 ? (
+
+
+
+ ) : (
+ activeSession.messages.map((msg) => (
+
+
+ {msg.attachments?.length ? (
+
+ {msg.attachments.map((attachment) => (
+
+ ))}
+
+ ) : null}
+
+ {msg.content ? (
+
+ {msg.content}
+
+ ) : null}
+
+
+ {msg.time}
+ {msg.role === "assistant" ? (
+
+ ) : null}
+
-
- {msg.content}
+
+ ))
+ )}
+
+ {sending ? (
+
- ))
- )}
+ ) : null}
+
+
- {sending ? (
-
-
-
-
-
-
-
-
-
+
+
+ {draftAttachments.length ? (
+
+ {draftAttachments.map((attachment) => (
+
+
removeDraftAttachment(Number(attachment.fileId))}
+ />
+
+ ))}
+
+ ) : null}
+
+
+
+
+
- ) : null}
-
-
-
-
-
-
- AI 生成的内容可能不完全准确,请结合教学场景和业务要求审慎使用。
+
+ 支持图片、PDF、Word、Excel、TXT、Markdown、JSON、CSV 等文件分析。AI 结果请结合业务场景审慎使用。
+
diff --git a/ui/src/pages/ai/DocumentReview.tsx b/ui/src/pages/ai/DocumentReview.tsx
index 29c986f..6442f09 100644
--- a/ui/src/pages/ai/DocumentReview.tsx
+++ b/ui/src/pages/ai/DocumentReview.tsx
@@ -5,6 +5,8 @@ import { ConfirmDialog } from "../../components/system/ConfirmDialog";
import { EmptyState } from "../../components/system/EmptyState";
import { FeedbackBanner } from "../../components/system/FeedbackBanner";
import { LoadingState } from "../../components/system/LoadingState";
+import { RequiredLabel } from "../../components/system/RequiredLabel";
+import { SideDrawer } from "../../components/ui/SideDrawer";
import { Select } from "../../components/ui/Select";
import { formatDateTime } from "../../lib/system-utils";
import { aiService } from "../../services/ai";
@@ -28,6 +30,7 @@ const initialFormState: ReviewFormState = {
promptTemplateCode: "",
scoringRubric: "",
};
+const DOCUMENT_REVIEW_ACCEPT = ".pdf,.doc,.docx,.xlsx,.xls,.txt,.md,.json,.csv,.xml,.yml,.yaml,.log,.java";
export function DocumentReviewComponent() {
const [tasks, setTasks] = useState
([]);
@@ -36,6 +39,7 @@ export function DocumentReviewComponent() {
const [loading, setLoading] = useState(true);
const [refreshing, setRefreshing] = useState(false);
const [feedback, setFeedback] = useState<{ type: "success" | "error"; message: string } | null>(null);
+ const [modalFeedback, setModalFeedback] = useState<{ type: "success" | "error"; message: string } | null>(null);
const [keyword, setKeyword] = useState("");
const [statusFilter, setStatusFilter] = useState("");
const [activeTask, setActiveTask] = useState(null);
@@ -67,9 +71,9 @@ export function DocumentReviewComponent() {
setTemplates(templateList.filter((item) => item.status === 1));
setActiveTask((previous) => {
if (!previous) {
- return taskList[0] ?? null;
+ return null;
}
- return taskList.find((item) => String(item.id) === String(previous.id)) ?? taskList[0] ?? null;
+ return taskList.find((item) => String(item.id) === String(previous.id)) ?? null;
});
setFormState((previous) => ({
...previous,
@@ -113,11 +117,13 @@ export function DocumentReviewComponent() {
});
setStandardFile(null);
setStudentFile(null);
+ setModalFeedback(null);
setModalOpen(true);
};
const closeModal = () => {
setModalOpen(false);
+ setModalFeedback(null);
setFormState(initialFormState);
setStandardFile(null);
setStudentFile(null);
@@ -158,7 +164,20 @@ export function DocumentReviewComponent() {
event.preventDefault();
setSubmitting(true);
setFeedback(null);
+ setModalFeedback(null);
try {
+ if (!formState.reviewName.trim()) {
+ throw new Error("请填写任务名称。");
+ }
+ if (!formState.totalScore.trim() || Number(formState.totalScore) <= 0) {
+ throw new Error("总分必须为大于 0 的数字。");
+ }
+ if (!formState.modelConfigId) {
+ throw new Error("请选择模型配置。");
+ }
+ if (!formState.promptTemplateCode) {
+ throw new Error("请选择 Prompt 模板。");
+ }
if (!standardFile?.fileId || !studentFile?.fileId) {
throw new Error("请先上传标准答案卷和学生答卷。");
}
@@ -176,7 +195,7 @@ export function DocumentReviewComponent() {
await loadData(false);
setActiveTask(created);
} catch (error) {
- setFeedback({ type: "error", message: (error as AppRequestError).message || "创建文档批阅任务失败。" });
+ setModalFeedback({ type: "error", message: (error as AppRequestError).message || "创建文档批阅任务失败。" });
} finally {
setSubmitting(false);
}
@@ -215,7 +234,10 @@ export function DocumentReviewComponent() {
-
-
-
- {loading ? (
-
- ) : filteredTasks.length === 0 ? (
-
- ) : (
-
-
-
-
- | 任务 |
- 模型 |
- 建议分 / 最终分 |
- 状态 |
- 创建时间 |
- 操作 |
-
-
-
- {filteredTasks.map((item) => (
-
- |
- {item.reviewName}
- {item.teacherName || "--"} / 标准卷:{item.standardFileName}
- |
- {item.modelName || "--"} |
-
- {item.suggestedScore ?? "--"} / {item.finalScore ?? "--"}
- |
-
-
- {item.reviewStatusLabel || "--"}
-
- |
- {formatDateTime(item.createTime)} |
-
-
- setActiveTask(item)} className="hover:text-indigo-700 transition-colors flex items-center gap-1">
-
- 查看
-
- {item.reviewStatus === 1 || item.reviewStatus === 0 ? (
- <>
- setPendingAction({ type: "adopt", task: item })} className="hover:text-emerald-600 transition-colors text-slate-500">采纳
- setPendingAction({ type: "reject", task: item })} className="hover:text-red-600 transition-colors text-slate-500">驳回
- >
- ) : null}
+
+ {loading ? (
+
+ ) : filteredTasks.length === 0 ? (
+
+ ) : (
+
+
+
+
+ | 任务 |
+ 模型 |
+ 分数 |
+ 状态 |
+ 创建时间 |
+ 操作 |
+
+
+
+ {filteredTasks.map((item) => (
+
+
+
+ {item.reviewName}
+
+ {item.teacherName || "--"} / 标准卷:{item.standardFileName}
- |
-
- ))}
-
-
-
- )}
-
-
-
-
- 批阅详情
- 查看总分、扣分点、优点问题和教师最终采纳结果。
+
+ |
+ {item.modelName || "--"} |
+
+
+ {item.suggestedScore ?? "--"} / {item.finalScore ?? "--"}
+
+ 建议分 / 最终分
+ |
+
+
+ {item.reviewStatusLabel || "--"}
+
+ |
+ {formatDateTime(item.createTime)} |
+
+
+ setActiveTask(item)}
+ className="inline-flex items-center gap-1 text-indigo-600 transition-colors hover:text-indigo-700"
+ >
+
+ 查看批阅
+
+ {item.reviewStatus === 1 || item.reviewStatus === 0 ? (
+ <>
+ setPendingAction({ type: "adopt", task: item })}
+ className="text-slate-500 transition-colors hover:text-emerald-600"
+ >
+ 采纳
+
+ setPendingAction({ type: "reject", task: item })}
+ className="text-slate-500 transition-colors hover:text-red-600"
+ >
+ 驳回
+
+ >
+ ) : null}
+
+ |
+
+ ))}
+
+
+ )}
+
- {activeTask ? (
-
-
-
{activeTask.reviewName}
-
模型:{activeTask.modelName || "--"} / 模板:{activeTask.promptTemplateName || "--"}
-
-
-
-
-
总分
-
{activeTask.totalScore ?? "--"}
-
-
-
建议分
-
{activeTask.suggestedScore ?? "--"}
-
-
-
最终分
-
{activeTask.finalScore ?? "--"}
-
-
-
-
-
openFile(activeTask.standardFile?.previewUrl)} className="rounded-2xl border border-slate-100 bg-white p-4 text-left hover:border-indigo-200 transition-colors">
- 标准答案卷
- {activeTask.standardFile?.fileName || activeTask.standardFileName}
-
-
openFile(activeTask.studentFile?.previewUrl)} className="rounded-2xl border border-slate-100 bg-white p-4 text-left hover:border-indigo-200 transition-colors">
- 学生答卷
- {activeTask.studentFile?.fileName || activeTask.studentFileName}
-
+
setActiveTask(null)}
+ title="批阅详情"
+ description="右侧抽屉查看总分、扣分点、优点问题和教师最终采纳结果。"
+ widthClassName="w-full max-w-[640px]"
+ >
+ {activeTask ? (
+
+
+
{activeTask.reviewName}
+
+ 模型:{activeTask.modelName || "--"} / 模板:{activeTask.promptTemplateName || "--"}
+
+
-
评分标准
-
{activeTask.scoringRubric || "--"}
+
总分
+
{activeTask.totalScore ?? "--"}
-
-
扣分明细
- {activeTask.deductionDetails.length ? (
-
- {activeTask.deductionDetails.map((item, index) => (
-
-
{item.item || `扣分项 ${index + 1}`}
-
扣分:{item.score ?? "--"}
-
{item.reason || "--"}
-
- ))}
-
- ) : (
-
没有扣分项。
- )}
+
建议分
+
{activeTask.suggestedScore ?? "--"}
-
-
总体评语
-
{activeTask.summary || "--"}
+
最终分
+
{activeTask.finalScore ?? "--"}
+
-
-
改进建议
-
{activeTask.suggestion || "--"}
-
+
+
openFile(activeTask.standardFile?.previewUrl)}
+ className="rounded-2xl border border-slate-100 bg-white p-4 text-left transition-colors hover:border-indigo-200"
+ >
+ 标准答案卷
+ {activeTask.standardFile?.fileName || activeTask.standardFileName}
+
+
openFile(activeTask.studentFile?.previewUrl)}
+ className="rounded-2xl border border-slate-100 bg-white p-4 text-left transition-colors hover:border-indigo-200"
+ >
+ 学生答卷
+ {activeTask.studentFile?.fileName || activeTask.studentFileName}
+
- ) : (
-
请选择一条文档批阅任务
- )}
-
-
+
+
+
评分标准
+
{activeTask.scoringRubric || "--"}
+
+
+
+
扣分明细
+ {activeTask.deductionDetails.length ? (
+
+ {activeTask.deductionDetails.map((item, index) => (
+
+
{item.item || `扣分项 ${index + 1}`}
+
扣分:{item.score ?? "--"}
+
{item.reason || "--"}
+
+ ))}
+
+ ) : (
+
没有扣分项。
+ )}
+
+
+
+
总体评语
+
{activeTask.summary || "--"}
+
+
+
+
改进建议
+
{activeTask.suggestion || "--"}
+
+
+ ) : null}
+
{modalOpen ? (
@@ -389,22 +436,27 @@ export function DocumentReviewComponent() {
-
-