feat: 支持 AskUserQuestion 多问题和多选功能

- 新增 QuestionItem 类型支持单个问题配置(question/options/multiSelect)
   - AskUserEvent 改为 questions 数组支持多问题
   - AnswerRequest 新增 answers 字段支持多问题答案提交
   - 前端渲染支持单选按钮(radio)和多选复选框(checkbox)
   - 答案格式:{\"0\": [\"选项1\"], \"1\": [\"选项A\", \"选项B\"]}
   - 保持向后兼容旧的单问题格式
This commit is contained in:
Roe-xin
2026-03-05 16:58:59 +08:00
parent f6b1f5c45a
commit fa55e32153
7 changed files with 132 additions and 74 deletions

View File

@ -129,7 +129,8 @@ export function showICHelperPanel(context: vscode.ExtensionContext) {
handleUserAnswer(
message.askId,
message.selected,
message.customInput
message.customInput,
message.answers
);
break;
// 新增:中止对话

View File

@ -1188,64 +1188,60 @@ export function getMessageAreaScript(): string {
} else if (segment.type === 'question') {
segmentDiv.className += ' segment-question';
// 兼容旧格式:如果有 segment.question转换为 questions 数组
const questions = segment.questions || (segment.question ? [{
question: segment.question,
options: segment.options || [],
multiSelect: false
}] : []);
// 检查是否已回答
const isAnswered = answeredQuestions.has(segment.askId);
const selectedAnswer = answeredQuestions.get(segment.askId);
const savedAnswers = answeredQuestions.get(segment.askId) || {};
if (isAnswered) {
segmentDiv.classList.add('answered');
}
// 检查是否有选项
const hasOptions = segment.options && segment.options.length > 0;
// 渲染多个问题
const questionsHtml = questions.map((q, qIndex) => {
const inputType = q.multiSelect ? 'checkbox' : 'radio';
const inputName = \`q\${qIndex}\`;
const selectedAnswers = savedAnswers[qIndex] || [];
const optionsHtml = hasOptions
? (segment.options || []).map(opt => {
const isSelected = isAnswered && opt === selectedAnswer;
return \`<button class="question-option\${isSelected ? ' selected' : ''}" data-option="\${opt}">\${opt}</button>\`;
}).join('')
: '';
const optionsHtml = q.options.map(opt => {
const isSelected = selectedAnswers.includes(opt);
return \`<label style="display:flex;align-items:center;gap:6px;cursor:pointer;padding:4px 0;">
<input type="\${inputType}" name="\${inputName}" value="\${opt}" \${isSelected ? 'checked' : ''} \${isAnswered ? 'disabled' : ''}>
<span>\${opt}</span>
</label>\`;
}).join('');
return \`
<div class="question-item" data-question-index="\${qIndex}" style="margin-bottom:12px;">
<div class="question-text" style="margin-bottom:8px;">\${formatText(q.question)}</div>
<div class="question-options">\${optionsHtml}</div>
</div>
\`;
}).join('');
segmentDiv.innerHTML = \`
<div class="question-text">\${formatText(segment.question || '')}</div>
\${hasOptions ? \`<div class="question-options" data-ask-id="\${segment.askId}">\${optionsHtml}</div>\` : ''}
<div class="custom-input-container" style="display: \${isAnswered ? 'none' : 'flex'};">
<input type="text" class="custom-input" placeholder="\${hasOptions ? '输入其他答案...' : '请输入您的答案...'}" />
<button class="custom-submit">提交</button>
</div>
\${questionsHtml}
<button class="custom-submit" style="display:\${isAnswered ? 'none' : 'block'};margin-top:8px;padding:8px 16px;background:var(--vscode-button-background);color:var(--vscode-button-foreground);border:none;border-radius:6px;cursor:pointer;">提交答案</button>
\`;
// 只在未回答时添加事件监听
if (!isAnswered) {
setTimeout(() => {
if (hasOptions) {
const optionButtons = segmentDiv.querySelectorAll('.question-option');
optionButtons.forEach(btn => {
btn.addEventListener('click', function() {
const option = this.getAttribute('data-option');
handleQuestionAnswerInSegment(segment.askId, option, segmentDiv);
});
});
}
const submitBtn = segmentDiv.querySelector('.custom-submit');
const customInput = segmentDiv.querySelector('.custom-input');
if (submitBtn && customInput) {
if (submitBtn) {
submitBtn.addEventListener('click', function() {
const customValue = customInput.value.trim();
if (customValue) {
handleQuestionAnswerInSegment(segment.askId, customValue, segmentDiv);
}
});
// 支持回车提交
customInput.addEventListener('keypress', function(e) {
if (e.key === 'Enter') {
const customValue = customInput.value.trim();
if (customValue) {
handleQuestionAnswerInSegment(segment.askId, customValue, segmentDiv);
}
}
const answers = {};
questions.forEach((q, qIndex) => {
const inputs = segmentDiv.querySelectorAll(\`input[name="q\${qIndex}"]:checked\`);
answers[qIndex] = Array.from(inputs).map(input => input.value);
});
handleMultiQuestionAnswer(segment.askId, answers, segmentDiv);
});
}
}, 0);
@ -1723,6 +1719,34 @@ export function getMessageAreaScript(): string {
});
}
// 处理多问题答案提交
function handleMultiQuestionAnswer(askId, answers, segmentDiv) {
console.log('[WebView] 多问题答案提交:', askId, answers);
// 保存答案到 Map 中
answeredQuestions.set(askId, answers);
// 标记问题已回答
segmentDiv.classList.add('answered');
// 禁用所有输入
const inputs = segmentDiv.querySelectorAll('input');
inputs.forEach(input => input.disabled = true);
// 隐藏提交按钮
const submitBtn = segmentDiv.querySelector('.custom-submit');
if (submitBtn) {
submitBtn.style.display = 'none';
}
// 发送答案到后端
vscode.postMessage({
command: 'submitAnswer',
askId: askId,
answers: answers
});
}
${getWaveformPreviewScript()}
${getCodeHighlightScript()}