|
| 1 | +/** |
| 2 | + * ModelCardFromNoteDialog — 笔记段 → 模型卡草稿(v0.20.3 / REQ-302)。 |
| 3 | + * |
| 4 | + * @ai-context: 接线唯一生成链(commands_knowledge_cards::create_model_card_inner, |
| 5 | + * 防双轨):组内 kind=model 卡,front=归一化概念名,定义行=可选 |
| 6 | + * 笔记摘录草稿(应用案例留空——复习面/卡编辑完善);笔记未归组 |
| 7 | + * → 后端引导先归组(组=唯一容器)。 |
| 8 | + */ |
| 9 | +import { useState } from "react"; |
| 10 | +import { invoke } from "@tauri-apps/api/core"; |
| 11 | + |
| 12 | +interface Props { |
| 13 | + noteId: number; |
| 14 | + noteTitle: string; |
| 15 | + onClose: () => void; |
| 16 | + onCreated?: () => void; |
| 17 | +} |
| 18 | + |
| 19 | +const overlayStyle: React.CSSProperties = { |
| 20 | + position: "fixed", |
| 21 | + inset: 0, |
| 22 | + background: "rgba(0,0,0,.45)", |
| 23 | + zIndex: 1000, |
| 24 | + display: "flex", |
| 25 | + alignItems: "center", |
| 26 | + justifyContent: "center", |
| 27 | +}; |
| 28 | +const cardStyle: React.CSSProperties = { |
| 29 | + background: "#fff", |
| 30 | + borderRadius: 12, |
| 31 | + width: 460, |
| 32 | + padding: 16, |
| 33 | + fontSize: 13, |
| 34 | +}; |
| 35 | +const btn: React.CSSProperties = { padding: "5px 12px", cursor: "pointer", fontSize: 12, borderRadius: 6, border: "1px solid #e5e7eb", background: "#fff", color: "#374151" }; |
| 36 | + |
| 37 | +export default function ModelCardFromNoteDialog({ noteId, noteTitle, onClose, onCreated }: Props) { |
| 38 | + const [name, setName] = useState(""); |
| 39 | + const [excerpt, setExcerpt] = useState(""); |
| 40 | + const [err, setErr] = useState(""); |
| 41 | + const [msg, setMsg] = useState(""); |
| 42 | + const [busy, setBusy] = useState(false); |
| 43 | + |
| 44 | + const create = async () => { |
| 45 | + setBusy(true); |
| 46 | + setErr(""); |
| 47 | + try { |
| 48 | + await invoke("model_card_from_note", { noteId, name, excerpt: excerpt.trim() || null }); |
| 49 | + setMsg("✓ 已建模型卡草稿(组内 model 卡;复习面/卡编辑可继续完善定义与应用案例)"); |
| 50 | + onCreated?.(); |
| 51 | + setTimeout(onClose, 900); |
| 52 | + } catch (e) { |
| 53 | + setErr(String(e)); |
| 54 | + } finally { |
| 55 | + setBusy(false); |
| 56 | + } |
| 57 | + }; |
| 58 | + |
| 59 | + return ( |
| 60 | + <div style={overlayStyle} onClick={onClose}> |
| 61 | + <div style={cardStyle} onClick={(e) => e.stopPropagation()}> |
| 62 | + <div style={{ display: "flex", alignItems: "center", marginBottom: 10 }}> |
| 63 | + <h3 style={{ margin: 0, fontSize: 15 }}>🧠 提炼模型卡草稿</h3> |
| 64 | + <span style={{ fontSize: 11, color: "#6b7280", marginLeft: 8 }}>来源:{noteTitle.slice(0, 24)}</span> |
| 65 | + <button style={{ ...btn, marginLeft: "auto" }} onClick={onClose}>取消</button> |
| 66 | + </div> |
| 67 | + {msg && <div style={{ fontSize: 12, color: "#047857", marginBottom: 8 }}>{msg}</div>} |
| 68 | + {err && <div style={{ fontSize: 12, color: "#dc2626", marginBottom: 8 }}>{err}</div>} |
| 69 | + <div style={{ display: "flex", flexDirection: "column", gap: 8 }}> |
| 70 | + <div> |
| 71 | + <div style={{ fontSize: 12, color: "#374151", marginBottom: 2 }}>概念名(front)</div> |
| 72 | + <input value={name} onChange={(e) => setName(e.target.value)} autoFocus placeholder="如:安全边际" style={{ width: "100%", boxSizing: "border-box", fontSize: 13, border: "1px solid #e5e7eb", borderRadius: 4, padding: "4px 6px" }} /> |
| 73 | + </div> |
| 74 | + <div> |
| 75 | + <div style={{ fontSize: 12, color: "#374151", marginBottom: 2 }}> |
| 76 | + 定义草稿(可空——应用案例留空,后续完善) |
| 77 | + </div> |
| 78 | + <textarea value={excerpt} onChange={(e) => setExcerpt(e.target.value)} rows={4} placeholder="粘贴笔记中的模型表述(≤200 字)…" style={{ width: "100%", boxSizing: "border-box", fontSize: 12, border: "1px solid #e5e7eb", borderRadius: 4, padding: "4px 6px", resize: "vertical" }} /> |
| 79 | + </div> |
| 80 | + <div style={{ display: "flex", justifyContent: "flex-end", gap: 6 }}> |
| 81 | + <button style={btn} disabled={busy} onClick={() => void create()}>创建草稿</button> |
| 82 | + </div> |
| 83 | + </div> |
| 84 | + </div> |
| 85 | + </div> |
| 86 | + ); |
| 87 | +} |
0 commit comments