feat: HSAP platform v2 — modular navigation, quality review, audit log, world model simulation

Major changes:
- New frontend (platform/web/): Vite + React 18 + TypeScript + Tailwind
- 4-module navigation: 数据送标 / 模型管理 / 车队管理 / 系统管理
- Data catalog with charts (DMS/ADAS/Lane 3-tab view)
- Quality review workflow (标注质检): Good/Fine/Bad scoring with auto-advance
- Audit enhancements: batch operations, rejection categories, Feishu notifications
- Operation audit log (操作日志)
- World model simulation studio (仿真工坊)
- Dataset version management with snapshots and diff
- ADAS 7-class dataset integration (138K images organized + compressed)
- User management with Feishu integration and pagination
- CRUD/search/filter on all pages, card layout redesign
- PIL-optimized image overlay rendering
- Auto-snapshot on build, in_review workflow stage
- Removed embedded algorithm code (now in workspace)
This commit is contained in:
2026-06-03 11:40:21 +08:00
parent 7c43b44c57
commit e72bc061c5
5487 changed files with 979207 additions and 6197 deletions

View File

@@ -0,0 +1,95 @@
import React, { useState } from "react";
import { Link } from "react-router-dom";
import { hsapApi } from "@/app/hsap-api";
import { Button } from "@/components/ui/Button";
const ACTION_LABELS: Record<string, string> = {
train_dms: "DMS 训练", train_lane: "Lane 训练",
eval_dms: "DMS 评估", eval_lane: "Lane 评估",
promote_dms: "DMS 晋级", promote_lane: "Lane 晋级",
pipeline_dms: "DMS 全流程",
};
export const TrainingSubmitPage: React.FC = () => {
const [action, setAction] = useState("train_dms");
const [project, setProject] = useState("dms");
const [task, setTask] = useState("");
const [pack, setPack] = useState("");
const [note, setNote] = useState("");
const [submitting, setSubmitting] = useState(false);
const [result, setResult] = useState<Record<string, unknown> | null>(null);
const [error, setError] = useState<string | null>(null);
const [errors, setErrors] = useState<Record<string, string>>({});
const validate = (): boolean => {
const e: Record<string, string> = {};
if (!task.trim()) e.task = "请输入任务名称";
if (["train_dms", "train_lane", "eval_dms", "eval_lane"].includes(action) && !pack.trim()) e.pack = "请输入数据包名称";
setErrors(e);
return Object.keys(e).length === 0;
};
const handleSubmit = async () => {
if (!validate()) return;
setSubmitting(true);
setError(null);
setResult(null);
try {
const res = await hsapApi.createTrainingRecord(action, { project, task: task.trim(), pack: pack.trim() }, note.trim() || undefined) as Record<string, unknown>;
setResult(res);
} catch (e) {
setError(String(e));
}
setSubmitting(false);
};
return (
<div className="page-container">
<div className="page-header">
<h1></h1>
<p>/</p>
</div>
<div className="card max-w-lg">
<div className="form-group">
<label className="form-label"> *</label>
<select className="form-input" value={action} onChange={(e) => { setAction(e.target.value); setErrors({}); }}>
{Object.entries(ACTION_LABELS).map(([k, v]) => <option key={k} value={k}>{k} {v}</option>)}
</select>
</div>
<div className="form-group">
<label className="form-label"> *</label>
<select className="form-input" value={project} onChange={(e) => setProject(e.target.value)}>
<option value="dms">DMS</option>
<option value="lane">Lane</option>
</select>
</div>
<div className="form-group">
<label className="form-label"> *</label>
<input className={`form-input ${errors.task ? "border-red-500" : ""}`} value={task} onChange={(e) => { setTask(e.target.value); setErrors((p) => ({ ...p, task: "" })); }} placeholder="如 ddaw, dam, lane_v1" />
{errors.task && <p className="text-red-500 text-xs mt-1">{errors.task}</p>}
</div>
<div className="form-group">
<label className="form-label"></label>
<input className={`form-input ${errors.pack ? "border-red-500" : ""}`} value={pack} onChange={(e) => { setPack(e.target.value); setErrors((p) => ({ ...p, pack: "" })); }} placeholder="如 dms_v1" />
{errors.pack && <p className="text-red-500 text-xs mt-1">{errors.pack}</p>}
</div>
<div className="form-group">
<label className="form-label"></label>
<textarea className="form-input" value={note} onChange={(e) => setNote(e.target.value)} placeholder="可选备注信息" rows={3} />
</div>
{error && <div className="bg-red-50 border border-red-200 rounded p-3 mb-3 text-sm text-red-700">{error}</div>}
{result && (
<div className="bg-green-50 border border-green-200 rounded p-3 mb-3">
<p className="text-green-700 text-sm font-medium"></p>
<p className="text-xs text-gray-500 mt-1">Job ID: {result.id as string}</p>
<Link to="/models/training/records" className="text-blue-600 text-xs hover:underline mt-1 inline-block"> </Link>
</div>
)}
<Button variant="primary" onClick={handleSubmit} loading={submitting}></Button>
</div>
</div>
);
};