feat: 新增参考材料前端 UI

- 在左侧片段选择卡片下方添加参考材料卡片
- 支持动态添加/删除参考材料
- 每个材料包含可选标题和内容输入
- 标题为空时自动显示为「参考材料{n}」
- 材料内容实时预览到 Prompt
- 草稿自动保存/加载包含材料数据
- 保存会话时支持保存材料
This commit is contained in:
2026-06-26 17:26:34 +08:00
parent e49b744385
commit 27a2dd743d
+82 -1
View File
@@ -73,6 +73,17 @@
<div class="card-body" id="snippets-container"></div>
</div>
<!-- 参考材料 -->
<div class="card">
<div class="card-header">
📦 参考材料
<button class="btn btn-sm" onclick="addMaterial()">+ 添加</button>
</div>
<div class="card-body" id="materials-container">
<div class="text-sm text-muted" id="materials-empty">暂无参考材料</div>
</div>
</div>
<!-- CLAUDE.md -->
<div class="card">
<div class="collapse-header" onclick="toggleCollapse(this)">
@@ -173,6 +184,7 @@
let allSnippets = [];
let selectedTags = {}; // tagId -> optionId
let selectedSnippets = []; // [{snippetId, sortOrder}]
let materials = []; // [{title, content}]
let suggestions = [];
// 初始化
@@ -380,6 +392,16 @@
}
}
// Materials
const validMaterials = materials.filter(m => m.content.trim());
if (validMaterials.length > 0) {
const materialParts = validMaterials.map((m, i) => {
const title = m.title.trim() || `参考材料${i + 1}`;
return `--- ${title} ---\n${m.content}`;
});
parts.push(`## 参考材料\n\n${materialParts.join('\n\n')}`);
}
// Suggestions
const adoptedSuggestions = suggestions.filter(s => s.adopted);
if (adoptedSuggestions.length > 0) {
@@ -441,6 +463,51 @@
document.getElementById('tag-search')?.addEventListener('input', debounce(() => renderTags(), 300));
document.getElementById('snippet-search')?.addEventListener('input', debounce(() => renderSnippets(), 300));
// 参考材料管理
function addMaterial() {
materials.push({ title: '', content: '' });
renderMaterials();
updatePreview();
}
function removeMaterial(index) {
materials.splice(index, 1);
renderMaterials();
updatePreview();
}
function renderMaterials() {
const container = document.getElementById('materials-container');
const emptyHint = document.getElementById('materials-empty');
if (materials.length === 0) {
container.innerHTML = '<div class="text-sm text-muted" id="materials-empty">暂无参考材料</div>';
return;
}
container.innerHTML = materials.map((m, i) => `
<div class="mb-3 p-3" style="background:var(--bg-tertiary);border-radius:8px">
<div class="flex items-center justify-between mb-2">
<input class="input" placeholder="参考材料${i + 1}" value="${escapeHtml(m.title)}"
oninput="updateMaterialTitle(${i}, this.value)" style="flex:1;margin-right:8px">
<button class="btn btn-sm" onclick="removeMaterial(${i})" style="color:var(--error-color)">✕</button>
</div>
<textarea class="textarea" placeholder="输入参考材料内容..." rows="3"
oninput="updateMaterialContent(${i}, this.value)">${escapeHtml(m.content)}</textarea>
</div>
`).join('');
}
function updateMaterialTitle(index, title) {
materials[index].title = title;
updatePreview();
}
function updateMaterialContent(index, content) {
materials[index].content = content;
updatePreview();
}
// 自定义输入变化时更新预览
document.getElementById('custom-content')?.addEventListener('input', debounce(() => updatePreview(), 200));
document.getElementById('project-name')?.addEventListener('input', debounce(() => updatePreview(), 200));
@@ -591,6 +658,14 @@
sort_order: s.sortOrder
}));
const materialsData = materials
.filter(m => m.content.trim())
.map((m, i) => ({
title: m.title.trim(),
content: m.content,
sort_order: i
}));
try {
await API.post('/api/builder/sessions', {
title,
@@ -598,7 +673,8 @@
final_prompt: finalPrompt,
claude_session_id: claudeSessionId,
tag_option_ids: tagOptionIds,
snippet_ids: snippetIds
snippet_ids: snippetIds,
materials: materialsData
});
closeSaveModal();
Toast.success('会话已保存');
@@ -616,6 +692,7 @@
claudeMD: document.getElementById('claude-md').value,
selectedTags: { ...selectedTags },
selectedSnippets: [...selectedSnippets],
materials: materials.map(m => ({ title: m.title, content: m.content })),
});
}, 1000);
@@ -633,6 +710,8 @@
document.getElementById('claude-md').value = draft.claudeMD || '';
selectedTags = draft.selectedTags || {};
selectedSnippets = draft.selectedSnippets || [];
materials = draft.materials || [];
renderMaterials();
}
function resetBuilder() {
@@ -644,11 +723,13 @@
document.getElementById('session-prompts').classList.add('hidden');
selectedTags = {};
selectedSnippets = [];
materials = [];
suggestions = [];
document.getElementById('suggestions-card').style.display = 'none';
Draft.clear('builder');
renderTags();
renderSnippets();
renderMaterials();
updatePreview();
Toast.success('已重置');
}