L2文档三大板块重构+评分细则不公开化;系统对齐:一键三轮评审、受验者注册表映射、提交状态探测工具
- 文档重构为 考核目的(1.x)/提交要求(2.x,含成果物需求)/考核内容(3.x) 板块编号体系 - 删除公开评分细则(7维表/合格线/难度赋分/功能拆分),保留验收基准与提交要求 - 自选题取消事前登记,改由 AGENTS.md 记录核心内容;前端同步移除登记字段 - 新增 l2-participants 注册表服务与 teams-config L2 URL 自动映射 - 新增 startReviewRounds 一键N轮评审(自动续跑+快照聚合中位数),前端「重新评审×3」 - 新增 check-repos.mjs 提交状态探测(API四态判定:已提交/空仓/未创建/未授权) - 文档措辞与实现对齐(AI辅助评审)、修复引用/残片/格式问题
This commit is contained in:
@@ -205,7 +205,7 @@ function EntryManager({ projectId, track }: { projectId: string; track?: string
|
||||
const [importResult, setImportResult] = useState<any>(null);
|
||||
const [editEntry, setEditEntry] = useState<any>(null);
|
||||
const [showAdd, setShowAdd] = useState(false);
|
||||
const [addForm, setAddForm] = useState({ title: '', repo_url: '', participant: '', sub_type: '', branch: '', selected_topic: 'self', reg_no: '', reg_features: '' });
|
||||
const [addForm, setAddForm] = useState({ title: '', repo_url: '', participant: '', sub_type: '', branch: '', selected_topic: 'self' });
|
||||
const [l2Topics, setL2Topics] = useState<any[]>([]);
|
||||
const [offset, setOffset] = useState(0);
|
||||
const PAGE_LIMIT = 50;
|
||||
@@ -246,9 +246,9 @@ function EntryManager({ projectId, track }: { projectId: string; track?: string
|
||||
setSelected(next);
|
||||
};
|
||||
|
||||
const doAction = async (action: string, entryId: string) => {
|
||||
const doAction = async (action: string, entryId: string, body: any = {}) => {
|
||||
try {
|
||||
if (action === 'start') await api.request('POST', `/projects/${projectId}/entries/${entryId}/start`);
|
||||
if (action === 'start') await api.request('POST', `/projects/${projectId}/entries/${entryId}/start`, { rounds: 3, ...body });
|
||||
else if (action === 'cancel') await api.request('POST', `/projects/${projectId}/entries/${entryId}/cancel`);
|
||||
else if (action === 'retry') await api.request('POST', `/projects/${projectId}/entries/${entryId}/retry`);
|
||||
await load();
|
||||
@@ -282,9 +282,6 @@ function EntryManager({ projectId, track }: { projectId: string; track?: string
|
||||
};
|
||||
if (track === 'L2考核') {
|
||||
payload.selected_topic = editEntry.selected_topic || 'self';
|
||||
if (payload.selected_topic === 'self') {
|
||||
payload.self_registration = { no: editEntry.reg_no, features: editEntry.reg_features };
|
||||
}
|
||||
}
|
||||
await api.request('PUT', `/projects/${projectId}/entries/${editEntry.id}`, payload);
|
||||
setEditEntry(null);
|
||||
@@ -293,30 +290,23 @@ function EntryManager({ projectId, track }: { projectId: string; track?: string
|
||||
};
|
||||
|
||||
const openEdit = (e: any) => {
|
||||
let reg = { no: '', features: '' };
|
||||
try { const p = JSON.parse(e.self_registration || '{}'); reg = { no: p.no || '', features: Array.isArray(p.features) ? p.features.join('\n') : (p.features || '') }; } catch { }
|
||||
setEditEntry({
|
||||
id: e.id, title: e.title, repo_url: e.repo_url, participant: e.participant || '',
|
||||
sub_type: e.sub_type || '', branch: e.branch || '',
|
||||
service_url: e.service_url || '', build_status: e.build_status || '',
|
||||
selected_topic: e.selected_topic || 'self', reg_no: reg.no, reg_features: reg.features,
|
||||
selected_topic: e.selected_topic || 'self',
|
||||
});
|
||||
};
|
||||
|
||||
const doAdd = async () => {
|
||||
if (!addForm.title.trim() || !addForm.repo_url.trim()) return;
|
||||
const payload: any = { ...addForm };
|
||||
delete payload.reg_no;
|
||||
delete payload.reg_features;
|
||||
if (track === 'L2考核') {
|
||||
payload.selected_topic = addForm.selected_topic || 'self';
|
||||
if (payload.selected_topic === 'self') {
|
||||
payload.self_registration = { no: addForm.reg_no, features: addForm.reg_features };
|
||||
}
|
||||
}
|
||||
try {
|
||||
await api.request('POST', `/projects/${projectId}/entries`, payload);
|
||||
setAddForm({ title: '', repo_url: '', participant: '', sub_type: '', branch: '', selected_topic: 'self', reg_no: '', reg_features: '' });
|
||||
setAddForm({ title: '', repo_url: '', participant: '', sub_type: '', branch: '', selected_topic: 'self' });
|
||||
setShowAdd(false);
|
||||
await load();
|
||||
} catch (err: any) { alert(err.message || '添加失败'); }
|
||||
@@ -439,20 +429,12 @@ function EntryManager({ projectId, track }: { projectId: string; track?: string
|
||||
</select>
|
||||
)}
|
||||
{track === 'L2考核' && (
|
||||
<>
|
||||
<select value={addForm.selected_topic} onChange={e => setAddForm({ ...addForm, selected_topic: e.target.value })} className="select-field">
|
||||
<option value="self">自选题(需事前登记)</option>
|
||||
<option value="self">自选题</option>
|
||||
{l2Topics.map(t => (
|
||||
<option key={t.id} value={t.id}>命题 {t.id}|{t.title}({'★'.repeat(t.difficulty)} 满分{t.maxScoreCap})</option>
|
||||
))}
|
||||
</select>
|
||||
{addForm.selected_topic === 'self' && (
|
||||
<>
|
||||
<input value={addForm.reg_no} onChange={e => setAddForm({ ...addForm, reg_no: e.target.value })} placeholder="登记编号(组委会登记确认后获得)" className="input-field" />
|
||||
<textarea value={addForm.reg_features} onChange={e => setAddForm({ ...addForm, reg_features: e.target.value })} placeholder="登记功能清单(每行一条;低于该范围核心功能的缩减按未实现计)" rows={3} className="input-field" />
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
<div className="form-actions">
|
||||
<button onClick={doAdd} disabled={!addForm.title.trim() || !addForm.repo_url.trim()}>添加</button>
|
||||
@@ -528,7 +510,7 @@ function EntryManager({ projectId, track }: { projectId: string; track?: string
|
||||
{e.status === 'verifying' && <button className="btn-action warn" onClick={() => doAction('cancel', e.id)}>取消</button>}
|
||||
{['clone_fail', 'analysis_fail', 'failed'].includes(e.status) && <button className="btn-action" onClick={() => doAction('retry', e.id)}>重试</button>}
|
||||
{['review_done', 'admin_reviewed'].includes(e.status) && <button className="btn-action" onClick={() => openDetail(e.id)}>详情</button>}
|
||||
{['review_done', 'admin_reviewed'].includes(e.status) && <button className="btn-action" onClick={() => doAction('start', e.id)}>重新评审</button>}
|
||||
{['review_done', 'admin_reviewed'].includes(e.status) && <button className="btn-action" onClick={() => doAction('start', e.id)}>重新评审×3</button>}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
@@ -576,20 +558,12 @@ function EntryManager({ projectId, track }: { projectId: string; track?: string
|
||||
</select>
|
||||
)}
|
||||
{track === 'L2考核' && (
|
||||
<>
|
||||
<select value={editEntry.selected_topic || 'self'} onChange={e => setEditEntry({ ...editEntry, selected_topic: e.target.value })} className="select-field">
|
||||
<option value="self">自选题(需事前登记)</option>
|
||||
<option value="self">自选题</option>
|
||||
{l2Topics.map(t => (
|
||||
<option key={t.id} value={t.id}>命题 {t.id}|{t.title}({'★'.repeat(t.difficulty)} 满分{t.maxScoreCap})</option>
|
||||
))}
|
||||
</select>
|
||||
{editEntry.selected_topic === 'self' && (
|
||||
<>
|
||||
<input value={editEntry.reg_no || ''} onChange={e => setEditEntry({ ...editEntry, reg_no: e.target.value })} placeholder="登记编号" className="input-field" />
|
||||
<textarea value={editEntry.reg_features || ''} onChange={e => setEditEntry({ ...editEntry, reg_features: e.target.value })} placeholder="登记功能清单(每行一条)" rows={3} className="input-field" />
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
<div className="form-actions">
|
||||
<button onClick={doEditSave}>保存</button>
|
||||
|
||||
Reference in New Issue
Block a user