From c6d26fc343a06cb7b0e69ac845431f5b643baf83 Mon Sep 17 00:00:00 2001 From: 666ghj <670939375@qq.com> Date: Tue, 16 Dec 2025 10:36:36 +0800 Subject: [PATCH] Refactor Step4Report component to implement Q&A format for interview display - Updated the InterviewDisplay component to adopt a Q&A format, enhancing user interaction with distinct question and answer sections. - Introduced platform-specific answer toggling between Twitter and Reddit, allowing users to switch views seamlessly. - Improved answer formatting and expanded answer display logic to accommodate varying lengths and content structures. - Enhanced styling for a more cohesive and visually appealing layout, including adjustments to badges, buttons, and overall spacing. --- frontend/src/components/Step4Report.vue | 398 ++++++++++++++---------- 1 file changed, 236 insertions(+), 162 deletions(-) diff --git a/frontend/src/components/Step4Report.vue b/frontend/src/components/Step4Report.vue index 5945b39..41b3707 100644 --- a/frontend/src/components/Step4Report.vue +++ b/frontend/src/components/Step4Report.vue @@ -746,28 +746,102 @@ const PanoramaDisplay = { } } -// Interview Display Component - Conversation Style +// Interview Display Component - Conversation Style (Q&A Format) const InterviewDisplay = { props: ['result'], setup(props) { const activeIndex = ref(0) const expandedAnswers = ref(new Set()) - const activeTab = ref('twitter') // 'twitter' or 'reddit' + // 为每个问题-回答对维护独立的平台选择状态 + const platformTabs = reactive({}) // { 'agentIdx-qIdx': 'twitter' | 'reddit' } - const toggleAnswer = (idx) => { + // 获取某个问题的当前平台选择 + const getPlatformTab = (agentIdx, qIdx) => { + const key = `${agentIdx}-${qIdx}` + return platformTabs[key] || 'twitter' + } + + // 设置某个问题的平台选择 + const setPlatformTab = (agentIdx, qIdx, platform) => { + const key = `${agentIdx}-${qIdx}` + platformTabs[key] = platform + } + + const toggleAnswer = (key) => { const newSet = new Set(expandedAnswers.value) - if (newSet.has(idx)) { - newSet.delete(idx) + if (newSet.has(key)) { + newSet.delete(key) } else { - newSet.add(idx) + newSet.add(key) } expandedAnswers.value = newSet } const formatAnswer = (text, expanded) => { if (!text) return '' - if (expanded || text.length <= 600) return text - return text.substring(0, 600) + '...' + if (expanded || text.length <= 400) return text + return text.substring(0, 400) + '...' + } + + // 尝试按问题编号分割回答 + const splitAnswerByQuestions = (answerText, questionCount) => { + if (!answerText || questionCount <= 0) return [answerText] + + // 尝试按编号分割 (如 "1." "2." 等) + const parts = [] + let remaining = answerText + + for (let i = 1; i <= questionCount; i++) { + const nextNum = i + 1 + // 查找下一个编号的位置 + const nextPattern = new RegExp(`\\n\\s*${nextNum}\\.\\s+|\\n\\s*${nextNum}、|\\n\\s*(${nextNum})|\\n\\s*\\(${nextNum}\\)`) + const match = remaining.match(nextPattern) + + if (match) { + // 找到下一个编号,截取当前部分 + const splitIdx = match.index + let currentPart = remaining.substring(0, splitIdx).trim() + // 移除当前编号前缀 + currentPart = currentPart.replace(new RegExp(`^\\s*${i}\\.\\s*|^\\s*${i}、|^\\s*(${i})|^\\s*\\(${i}\\)`), '').trim() + parts.push(currentPart) + remaining = remaining.substring(splitIdx).trim() + } else if (i === questionCount) { + // 最后一个问题,取剩余所有内容 + let currentPart = remaining.replace(new RegExp(`^\\s*${i}\\.\\s*|^\\s*${i}、|^\\s*(${i})|^\\s*\\(${i}\\)`), '').trim() + parts.push(currentPart) + } + } + + // 如果分割失败,返回整体答案 + if (parts.length === 0 || parts.every(p => !p)) { + return [answerText] + } + + return parts + } + + // 获取某个问题对应的回答 + const getAnswerForQuestion = (interview, qIdx, platform) => { + const answer = platform === 'twitter' ? interview.twitterAnswer : (interview.redditAnswer || interview.twitterAnswer) + if (!answer) return '' + + const questionCount = interview.questions?.length || 1 + const answers = splitAnswerByQuestions(answer, questionCount) + + // 如果只有一个回答部分,或者索引超出,返回完整回答 + if (answers.length === 1 || qIdx >= answers.length) { + return qIdx === 0 ? answer : '' + } + + return answers[qIdx] || '' + } + + // 检查某个问题是否有双平台回答 + const hasMultiplePlatforms = (interview, qIdx) => { + if (!interview.twitterAnswer || !interview.redditAnswer) return false + const twitterAnswer = getAnswerForQuestion(interview, qIdx, 'twitter') + const redditAnswer = getAnswerForQuestion(interview, qIdx, 'reddit') + return twitterAnswer && redditAnswer && twitterAnswer !== redditAnswer } return () => h('div', { class: 'interview-display' }, [ @@ -814,72 +888,82 @@ const InterviewDisplay = { ]) ]), - // Conversation Thread - h('div', { class: 'conversation-thread' }, [ - // Question Block (Interviewer) - h('div', { class: 'message interviewer' }, [ - h('div', { class: 'message-header' }, [ - h('span', { class: 'message-sender' }, 'Interviewer'), - h('span', { class: 'message-badge' }, 'Q') - ]), - h('div', { class: 'message-content' }, [ - props.result.interviews[activeIndex.value]?.questions?.length > 0 - ? h('ol', { class: 'question-list' }, - props.result.interviews[activeIndex.value].questions.map((q, qi) => - h('li', { key: qi, class: 'question-item' }, q) - ) - ) - : h('p', {}, props.result.interviews[activeIndex.value]?.question || 'No question available') + // Q&A Conversation Thread - 一问一答样式 + h('div', { class: 'qa-thread' }, + (props.result.interviews[activeIndex.value]?.questions?.length > 0 + ? props.result.interviews[activeIndex.value].questions + : [props.result.interviews[activeIndex.value]?.question || 'No question available'] + ).map((question, qIdx) => { + const interview = props.result.interviews[activeIndex.value] + const currentPlatform = getPlatformTab(activeIndex.value, qIdx) + const answerText = getAnswerForQuestion(interview, qIdx, currentPlatform) + const hasDualPlatform = hasMultiplePlatforms(interview, qIdx) + const expandKey = `${activeIndex.value}-${qIdx}` + const isExpanded = expandedAnswers.value.has(expandKey) + + return h('div', { class: 'qa-pair', key: qIdx }, [ + // Question Block + h('div', { class: 'qa-question' }, [ + h('div', { class: 'qa-badge q-badge' }, `Q${qIdx + 1}`), + h('div', { class: 'qa-content' }, [ + h('div', { class: 'qa-sender' }, 'Interviewer'), + h('div', { class: 'qa-text' }, question) + ]) + ]), + + // Answer Block + answerText && h('div', { class: 'qa-answer' }, [ + h('div', { class: 'qa-badge a-badge' }, `A${qIdx + 1}`), + h('div', { class: 'qa-content' }, [ + h('div', { class: 'qa-answer-header' }, [ + h('div', { class: 'qa-sender' }, interview?.name || 'Agent'), + // 双平台切换按钮 + hasDualPlatform && h('div', { class: 'platform-switch' }, [ + h('button', { + class: ['platform-btn', { active: currentPlatform === 'twitter' }], + onClick: (e) => { e.stopPropagation(); setPlatformTab(activeIndex.value, qIdx, 'twitter') } + }, [ + h('svg', { class: 'platform-icon', viewBox: '0 0 24 24', width: 12, height: 12, fill: 'currentColor' }, [ + h('path', { d: 'M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z' }) + ]), + h('span', {}, 'X') + ]), + h('button', { + class: ['platform-btn', { active: currentPlatform === 'reddit' }], + onClick: (e) => { e.stopPropagation(); setPlatformTab(activeIndex.value, qIdx, 'reddit') } + }, [ + h('svg', { class: 'platform-icon', viewBox: '0 0 24 24', width: 12, height: 12, fill: 'currentColor' }, [ + h('path', { d: 'M12 0A12 12 0 0 0 0 12a12 12 0 0 0 12 12 12 12 0 0 0 12-12A12 12 0 0 0 12 0zm5.01 4.744c.688 0 1.25.561 1.25 1.249a1.25 1.25 0 0 1-2.498.056l-2.597-.547-.8 3.747c1.824.07 3.48.632 4.674 1.488.308-.309.73-.491 1.207-.491.968 0 1.754.786 1.754 1.754 0 .716-.435 1.333-1.01 1.614a3.111 3.111 0 0 1 .042.52c0 2.694-3.13 4.87-7.004 4.87-3.874 0-7.004-2.176-7.004-4.87 0-.183.015-.366.043-.534A1.748 1.748 0 0 1 4.028 12c0-.968.786-1.754 1.754-1.754.463 0 .898.196 1.207.49 1.207-.883 2.878-1.43 4.744-1.487l.885-4.182a.342.342 0 0 1 .14-.197.35.35 0 0 1 .238-.042l2.906.617a1.214 1.214 0 0 1 1.108-.701zM9.25 12C8.561 12 8 12.562 8 13.25c0 .687.561 1.248 1.25 1.248.687 0 1.248-.561 1.248-1.249 0-.688-.561-1.249-1.249-1.249zm5.5 0c-.687 0-1.248.561-1.248 1.25 0 .687.561 1.248 1.249 1.248.688 0 1.249-.561 1.249-1.249 0-.687-.562-1.249-1.25-1.249zm-5.466 3.99a.327.327 0 0 0-.231.094.33.33 0 0 0 0 .463c.842.842 2.484.913 2.961.913.477 0 2.105-.056 2.961-.913a.361.361 0 0 0 .029-.463.33.33 0 0 0-.464 0c-.547.533-1.684.73-2.512.73-.828 0-1.979-.196-2.512-.73a.326.326 0 0 0-.232-.095z' }) + ]), + h('span', {}, 'Reddit') + ]) + ]) + ]), + h('div', { + class: 'qa-text answer-text', + innerHTML: formatAnswer(answerText, isExpanded) + .replace(/\*\*(.+?)\*\*/g, '$1') + .replace(/\n/g, '
') + }), + // Expand/Collapse Button + answerText.length > 400 && h('button', { + class: 'expand-answer-btn', + onClick: () => toggleAnswer(expandKey) + }, isExpanded ? 'Show Less' : 'Show More') + ]) + ]) ]) - ]), - - // Answer Block (Agent) - with platform tabs - h('div', { class: 'message agent' }, [ - h('div', { class: 'message-header' }, [ - h('span', { class: 'message-sender' }, props.result.interviews[activeIndex.value]?.name || 'Agent'), - h('span', { class: 'message-badge answer' }, 'A') - ]), - // Platform Tabs - (props.result.interviews[activeIndex.value]?.twitterAnswer && props.result.interviews[activeIndex.value]?.redditAnswer) && h('div', { class: 'platform-tabs' }, [ - h('button', { - class: ['platform-tab', { active: activeTab.value === 'twitter' }], - onClick: () => { activeTab.value = 'twitter' } - }, 'Twitter'), - h('button', { - class: ['platform-tab', { active: activeTab.value === 'reddit' }], - onClick: () => { activeTab.value = 'reddit' } - }, 'Reddit') - ]), - // Answer Content - h('div', { class: 'message-content answer-content' }, [ - h('div', { - class: 'answer-text', - innerHTML: formatAnswer( - activeTab.value === 'twitter' - ? props.result.interviews[activeIndex.value]?.twitterAnswer - : (props.result.interviews[activeIndex.value]?.redditAnswer || props.result.interviews[activeIndex.value]?.twitterAnswer), - expandedAnswers.value.has(activeIndex.value) - ).replace(/\*\*(.+?)\*\*/g, '$1').replace(/\n/g, '
') - }), - // Expand/Collapse Button - ((props.result.interviews[activeIndex.value]?.twitterAnswer?.length > 600) || - (props.result.interviews[activeIndex.value]?.redditAnswer?.length > 600)) && - h('button', { - class: 'expand-answer-btn', - onClick: () => toggleAnswer(activeIndex.value) - }, expandedAnswers.value.has(activeIndex.value) ? 'Show Less' : 'Show More') - ]) - ]), - - // Key Quotes Section - props.result.interviews[activeIndex.value]?.quotes?.length > 0 && h('div', { class: 'quotes-section' }, [ - h('div', { class: 'quotes-header' }, 'Key Quotes'), - h('div', { class: 'quotes-list' }, - props.result.interviews[activeIndex.value].quotes.slice(0, 3).map((quote, qi) => - h('blockquote', { key: qi, class: 'quote-item' }, quote.length > 200 ? quote.substring(0, 200) + '...' : quote) - ) + }) + ), + + // Key Quotes Section + props.result.interviews[activeIndex.value]?.quotes?.length > 0 && h('div', { class: 'quotes-section' }, [ + h('div', { class: 'quotes-header' }, 'Key Quotes'), + h('div', { class: 'quotes-list' }, + props.result.interviews[activeIndex.value].quotes.slice(0, 3).map((quote, qi) => + h('blockquote', { key: qi, class: 'quote-item' }, quote.length > 200 ? quote.substring(0, 200) + '...' : quote) ) - ]) + ) ]) ]), @@ -2590,133 +2674,123 @@ watch(() => props.reportId, (newId) => { overflow: hidden; } -/* Conversation Thread */ -:deep(.interview-display .conversation-thread) { +/* Q&A Thread - 一问一答样式 */ +:deep(.interview-display .qa-thread) { display: flex; flex-direction: column; + gap: 16px; +} + +:deep(.interview-display .qa-pair) { + display: flex; + flex-direction: column; + gap: 8px; + padding: 14px; + background: #F9FAFB; + border: 1px solid #E5E7EB; + border-radius: 12px; +} + +:deep(.interview-display .qa-question), +:deep(.interview-display .qa-answer) { + display: flex; gap: 12px; } -:deep(.interview-display .message) { - border: 1px solid #E5E7EB; - border-radius: 10px; - overflow: hidden; -} - -:deep(.interview-display .message.interviewer) { - background: #F9FAFB; -} - -:deep(.interview-display .message.agent) { - background: #FFFFFF; - border-color: #4F46E5; - border-width: 1px 1px 1px 3px; -} - -:deep(.interview-display .message-header) { - display: flex; - align-items: center; - justify-content: space-between; - padding: 10px 14px; - border-bottom: 1px solid #E5E7EB; -} - -:deep(.interview-display .message.agent .message-header) { - border-bottom-color: #EEF2FF; -} - -:deep(.interview-display .message-sender) { - font-size: 12px; - font-weight: 600; - color: #374151; -} - -:deep(.interview-display .message-badge) { - width: 22px; - height: 22px; +:deep(.interview-display .qa-badge) { + width: 28px; + height: 28px; display: flex; align-items: center; justify-content: center; - background: #E5E7EB; - color: #6B7280; - font-size: 11px; + font-size: 10px; font-weight: 700; - border-radius: 50%; + border-radius: 8px; + flex-shrink: 0; } -:deep(.interview-display .message-badge.answer) { +:deep(.interview-display .q-badge) { + background: #E5E7EB; + color: #6B7280; +} + +:deep(.interview-display .a-badge) { background: #4F46E5; color: #FFFFFF; } -:deep(.interview-display .message-content) { - padding: 14px; +:deep(.interview-display .qa-content) { + flex: 1; + min-width: 0; } -/* Question List */ -:deep(.interview-display .question-list) { - margin: 0; - padding-left: 20px; - list-style: decimal; +:deep(.interview-display .qa-sender) { + font-size: 11px; + font-weight: 600; + color: #6B7280; + margin-bottom: 4px; + text-transform: uppercase; + letter-spacing: 0.03em; } -:deep(.interview-display .question-item) { - font-size: 12px; - color: #4B5563; +:deep(.interview-display .qa-text) { + font-size: 13px; + color: #374151; line-height: 1.6; +} + +:deep(.interview-display .qa-answer) { + background: #FFFFFF; + padding: 12px; + border-radius: 8px; + border: 1px solid #E5E7EB; + margin-top: 4px; +} + +:deep(.interview-display .qa-answer-header) { + display: flex; + justify-content: space-between; + align-items: center; margin-bottom: 8px; } -:deep(.interview-display .question-item:last-child) { - margin-bottom: 0; -} - -/* Platform Tabs */ -:deep(.interview-display .platform-tabs) { +/* Platform Switch - 双平台切换按钮 */ +:deep(.interview-display .platform-switch) { display: flex; gap: 4px; - padding: 8px 14px; - border-bottom: 1px solid #EEF2FF; + background: #F3F4F6; + padding: 2px; + border-radius: 6px; } -:deep(.interview-display .platform-tab) { - padding: 5px 12px; +:deep(.interview-display .platform-btn) { + display: flex; + align-items: center; + gap: 4px; + padding: 4px 10px; background: transparent; - border: 1px solid #E5E7EB; + border: none; border-radius: 4px; font-size: 10px; font-weight: 600; color: #6B7280; cursor: pointer; transition: all 0.15s ease; - text-transform: uppercase; - letter-spacing: 0.03em; } -:deep(.interview-display .platform-tab:hover) { - border-color: #D1D5DB; +:deep(.interview-display .platform-btn:hover) { color: #374151; + background: rgba(255,255,255,0.5); } -:deep(.interview-display .platform-tab.active) { - background: #111827; - border-color: #111827; - color: #FFFFFF; +:deep(.interview-display .platform-btn.active) { + background: #FFFFFF; + color: #111827; + box-shadow: 0 1px 2px rgba(0,0,0,0.08); } -/* Answer Content */ -:deep(.interview-display .answer-content) { - max-height: 400px; - overflow-y: auto; -} - -:deep(.interview-display .answer-content::-webkit-scrollbar) { - width: 4px; -} - -:deep(.interview-display .answer-content::-webkit-scrollbar-thumb) { - background: #D1D5DB; - border-radius: 2px; +:deep(.interview-display .platform-icon) { + flex-shrink: 0; } :deep(.interview-display .answer-text) { @@ -2731,13 +2805,13 @@ watch(() => props.reportId, (newId) => { } :deep(.interview-display .expand-answer-btn) { - display: block; - margin-top: 12px; - padding: 6px 12px; + display: inline-block; + margin-top: 10px; + padding: 5px 10px; background: #F3F4F6; border: none; border-radius: 4px; - font-size: 11px; + font-size: 10px; font-weight: 500; color: #6B7280; cursor: pointer;