From 9555bb66fd417d3913aa3c7f46b962961a052bbc Mon Sep 17 00:00:00 2001 From: lbw_9527443 <780139497@qq.com> Date: Mon, 13 Oct 2025 11:45:27 +0800 Subject: [PATCH] =?UTF-8?q?feat(FeedbackForm):=20=E5=9C=A8=E5=8F=8D?= =?UTF-8?q?=E9=A6=88=E6=8F=90=E4=BA=A4=E4=B8=AD=E6=B7=BB=E5=8A=A0=E9=A1=B9?= =?UTF-8?q?=E7=9B=AE=E5=AD=97=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit refactor(CustomerDetail): 重命名并启用总通话分析功能 重构客户详情组件,将"客户诉求分析"改为"总通话分析"并启用相关功能,优化分析逻辑和UI显示 style(sale): 移除冗余标题并调整布局样式 删除客户详情区域的冗余标题,调整主布局的宽度和边距 perf(CustomerDetail): 优化分析请求和错误处理 移除调试日志,优化API请求参数和错误处理逻辑 --- my-vue-app/src/components/FeedbackForm.vue | 2 +- .../person/components/CustomerDetail.vue | 439 +++++++++--------- my-vue-app/src/views/person/sale.vue | 10 +- 3 files changed, 219 insertions(+), 232 deletions(-) diff --git a/my-vue-app/src/components/FeedbackForm.vue b/my-vue-app/src/components/FeedbackForm.vue index 88f56c9..0750ecb 100644 --- a/my-vue-app/src/components/FeedbackForm.vue +++ b/my-vue-app/src/components/FeedbackForm.vue @@ -124,7 +124,7 @@ const handleSubmit = async () => { const token = localStorage.getItem('token') || ''; // 发送 POST 请求到后端接口 - const response = await axios.post('https://mldash.nycjy.cn/api/v1/submit_feedback', {type: formData.type, content: formData.content}, { + const response = await axios.post('https://mldash.nycjy.cn/api/v1/submit_feedback', {project:'mldash',type: formData.type, content: formData.content}, { headers: { 'Authorization': `Bearer ${token}` } diff --git a/my-vue-app/src/views/person/components/CustomerDetail.vue b/my-vue-app/src/views/person/components/CustomerDetail.vue index 0091f03..1c2f6e6 100644 --- a/my-vue-app/src/views/person/components/CustomerDetail.vue +++ b/my-vue-app/src/views/person/components/CustomerDetail.vue @@ -5,27 +5,27 @@
请选择一个客户查看详情
@@ -117,7 +117,6 @@ const props = defineProps({ } }); -console.log(999999999,props.selectedContact); // 分析结果状态 const basicAnalysisResult = ref(''); // 基础信息分析结果 const sopAnalysisResult = ref(''); // SOP通话分析结果 @@ -130,7 +129,7 @@ const isDemandAnalysisLoading = ref(false); // 诉求分析加载状态 // Dify API配置 const DIFY_API_KEY_01 = 'app-h4uBo5kOGoiYhjuBF1AHZi8b'; //基础信息分析 -const DIFY_API_KEY = 'app-ZIJSFWbcdZLufkwCp9RrvpUR'; +const DIFY_API_KEY = 'app-ZIJSFWbcdZLufkwCp9RrvpUR'; // 总通话分析 // 初始化ChatService const chatService_01 = new SimpleChatService(DIFY_API_KEY_01); const chatService = new SimpleChatService(DIFY_API_KEY); @@ -189,24 +188,22 @@ watch(() => props.selectedContact, (newContact) => { // 基础信息分析 const startBasicAnalysis = async () => { if (!props.selectedContact) return; - + isBasicAnalysisLoading.value = true; basicAnalysisResult.value = ''; - + // 构建表单信息 const formData = props.formInfo || {}; let formInfoText = '暂无表单信息'; - + if (Object.keys(formData).length > 0) { - console.log(888888,formData); const allInfo = []; // 处理第一种格式:基础信息和additional_info if (formData.name || formData.mobile || formData.additional_info) { const basicInfo = []; const additionalInfo = []; - - // 处理基础信息字段 + const basicFields = { name: '姓名', mobile: '手机号', @@ -217,14 +214,13 @@ const startBasicAnalysis = async () => { child_education: '孩子教育阶段', child_relation: '与孩子关系' }; - + Object.entries(basicFields).forEach(([key, label]) => { if (formData[key] && formData[key] !== '暂无' && formData[key] !== '') { basicInfo.push(`${label}: ${formData[key]}`); } }); - - // 处理 additional_info 数组 + if (formData.additional_info && Array.isArray(formData.additional_info)) { formData.additional_info.forEach(item => { if (item.topic && item.answer) { @@ -232,55 +228,46 @@ const startBasicAnalysis = async () => { } }); } - - // 添加基础信息 + if (basicInfo.length > 0) { allInfo.push('=== 基础信息 ==='); allInfo.push(...basicInfo); } - - // 添加问卷信息 + if (additionalInfo.length > 0) { allInfo.push('\n=== 问卷信息 ==='); allInfo.push(...additionalInfo); } } - + // 处理第二种格式:customerExpandFieldMap if (formData.customerExpandFieldMap) { const expandInfo = []; const map = formData.customerExpandFieldMap; - - // 处理所有expand字段 + Object.entries(map).forEach(([key, value]) => { - // 跳过原型链上的属性 if (!map.hasOwnProperty(key)) return; - - // 如果是对象类型(包含key和typeCode的字段) + if (value && typeof value === 'object' && value.key) { const question = value.key; let answer = ''; - - // 根据不同的类型处理答案 + if (value.typeCode === 'SINGLE_SELECT' || value.typeCode === 'MULTIPLE_SELECT') { if (value.expandValueList && value.expandValueList.length > 0) { answer = value.expandValueList.map(item => item.itemName).join('、'); } } else if (value.typeCode === 'TEXT' || value.typeCode === 'TEXTAREA' || value.typeCode === 'NUMBER') { - // 直接从formData中获取对应的expand值 answer = formData[key] || ''; } - - // 只添加有答案的问题 + if (answer && answer !== '暂无' && answer !== '') { expandInfo.push(`${question}\n答案: ${answer}`); } } }); - - // 添加扩展信息 + if (expandInfo.length > 0) { - if (allInfo.length > 0) allInfo.push('\n'); // 如果已有其他信息,添加分隔行 + if (allInfo.length > 0) allInfo.push('\n'); allInfo.push('=== 问卷详细信息 ==='); allInfo.push(...expandInfo); } @@ -288,19 +275,19 @@ const startBasicAnalysis = async () => { formInfoText = allInfo.length > 0 ? allInfo.join('\n') : '暂无表单信息'; } - + // 构建聊天记录信息 const chatData = props.chatRecords || []; - const chatInfoText = chatData.messages.length > 0 ? - `聊天记录数量: ${chatData.messages.length}条\n最近聊天内容: ${JSON.stringify(chatData.messages.slice(-3), null, 2)}` : + const chatInfoText = chatData.messages && chatData.messages.length > 0 ? + `聊天记录数量: ${chatData.messages.length}条\n最近聊天内容: ${JSON.stringify(chatData.messages.slice(-3), null, 2)}` : '暂无聊天记录'; - + // 构建通话记录信息 const callData = props.callRecords || []; - const callInfoText = callData.length > 0 ? - `通话记录数量: ${callData.length}次\n通话记录详情: ${JSON.stringify(callData, null, 2)}` : + const callInfoText = callData.length > 0 ? + `通话记录数量: ${callData.length}次\n通话记录详情: ${JSON.stringify(callData, null, 2)}` : '暂无通话记录'; - + const query = `请对客户进行基础信息分析: 客户姓名:${props.selectedContact.name} 联系电话:${props.selectedContact.phone || '未提供'} @@ -315,9 +302,8 @@ ${chatInfoText} === 通话记录 === ${callData.length > 0 && callData[0].record_context ? callData[0].record_context : callInfoText} -请基于以上客户的表单信息、聊天记录和通话记录,分析客户的基本情况、背景信息和初步画像。`; +请基于以上客户的表单信息、聊天记录和通话记录,分析客户的基本情况、背景信息和初步画像。`; -console.log(888888,formInfoText); try { await chatService_01.sendMessage( query, @@ -338,38 +324,19 @@ console.log(888888,formInfoText); // SOP通话分析 const startSopAnalysis = async () => { -console.log(888888888777777,props.selectedContact.wechat_id) if (!props.selectedContact) return; - + isSopAnalysisLoading.value = true; sopAnalysisResult.value = ''; - // 构建通话记录信息 - const callData = props.callRecords || []; - const callInfoText = callData.length > 0 ? - `通话记录数量: ${callData.length}次\n通话记录详情: ${JSON.stringify(callData, null, 2)}` : - '暂无通话记录'; - const query = `=== 通话记录 === -${callData.length > 0 && callData[0].record_context ? callData[0].record_context : callInfoText}`; - - + try { - // await chatService.sendMessage( - // query, - // (update) => { - // sopAnalysisResult.value = update.content; - // }, - // () => { - // isSopAnalysisLoading.value = false; - // console.log('SOP通话分析完成'); - // } - // ); - const res= await axios.get('https://analysis.api.nycjy.cn/api/v1/call', - { - params:{ - wechat_id:props.selectedContact.wechat_id + const res = await axios.get('https://analysis.api.nycjy.cn/api/v1/call', { + params: { + wechat_id: props.selectedContact.wechat_id } - }) + }); sopAnalysisResult.value = res.data.report_content; + isSopAnalysisLoading.value = false; } catch (error) { console.error('SOP通话分析失败:', error); sopAnalysisResult.value = `分析失败: ${error.message}`; @@ -377,31 +344,32 @@ ${callData.length > 0 && callData[0].record_context ? callData[0].record_context } }; -// 客户诉求分析 +// 总通话分析 const startDemandAnalysis = async () => { + console.log("所有通话记录:", props.callRecords); if (!props.selectedContact) return; - + isDemandAnalysisLoading.value = true; demandAnalysisResult.value = ''; - - const query = `请对客户 ${props.selectedContact.name} 进行深度诉求分析: -请从以下维度分析客户的真实需求和诉求: -1. 显性需求分析(客户明确表达的需求) -2. 隐性需求挖掘(潜在的、未明确表达的需求) -3. 痛点识别(客户面临的主要问题和挑战) -4. 决策因素分析(影响客户决策的关键因素) -5. 价值期望(客户期望获得的价值和收益) -6. 风险顾虑(客户可能的担忧和顾虑) -7. 个性化建议(针对性的解决方案建议) + // 1. 检查并拼接所有通话记录,为每次通话添加清晰的标签和区分 + let allCallTexts = '暂无通话记录'; + if (props.callRecords && props.callRecords.length > 0) { + allCallTexts = props.callRecords + .map((call, index) => { + // 为每段通话记录构建一个结构化的“信息头”,包含序号、标签和时间 + return `--- 通话记录 ${index + 1} ---\n标签: ${call.record_tag || '无'}\n时间: ${call.record_create_time || '未知'}\n\n内容:\n${call.record_context}`; + }) + .join('\n\n'); // 使用两个换行符清晰地分隔不同的通话记录 + } + + // 2. 构建一个详细的、结构化的分析指令 + const query = ` +请基于以下该客户的所有通话记录,进行全面深入的分析: +=== 全部通话记录 === +${allCallTexts} + `; -客户信息: -姓名:${props.selectedContact.name} -公司:${props.selectedContact.company || '未提供'} -职位:${props.selectedContact.position || '未提供'} -销售阶段:${props.selectedContact.salesStage || '未知'} -健康度:${props.selectedContact.health || '未知'}%`; - try { await chatService.sendMessage( query, @@ -410,11 +378,11 @@ const startDemandAnalysis = async () => { }, () => { isDemandAnalysisLoading.value = false; - console.log('客户诉求分析完成'); + console.log('总通话分析完成'); } ); } catch (error) { - console.error('客户诉求分析失败:', error); + console.error('总通话分析失败:', error); demandAnalysisResult.value = `分析失败: ${error.message}`; isDemandAnalysisLoading.value = false; } @@ -447,22 +415,22 @@ $purple: #a855f7; height: 100%; box-sizing: border-box; padding: 16px; - + // PC端保持一致布局 @media (min-width: 1024px) { // padding: 24px; } - + // 平板端适配 @media (max-width: 1023px) and (min-width: 769px) { padding: 20px; } - + // 移动端适配 @media (max-width: 768px) { padding: 12px; } - + // 小屏移动端适配 @media (max-width: 480px) { padding: 8px; @@ -487,19 +455,19 @@ $purple: #a855f7; display: flex; justify-content: space-between; align-items: center; - + // PC端保持一致布局 @media (min-width: 1024px) { padding: 20px; border-radius: 12px; } - + // 平板端适配 @media (max-width: 1023px) and (min-width: 769px) { padding: 18px; border-radius: 10px; } - + // 移动端适配 @media (max-width: 768px) { flex-direction: column; @@ -507,56 +475,56 @@ $purple: #a855f7; gap: 16px; padding: 16px; } - + // 小屏移动端适配 @media (max-width: 480px) { padding: 12px; gap: 12px; } - + h3 { margin: 0; color: $slate-800; font-size: 20px; font-weight: 600; - + // PC端保持一致布局 @media (min-width: 1024px) { font-size: 24px; } - + // 平板端适配 @media (max-width: 1023px) and (min-width: 769px) { font-size: 22px; } - + // 移动端适配 @media (max-width: 768px) { font-size: 18px; } - + // 小屏移动端适配 @media (max-width: 480px) { font-size: 16px; } } - + .action-buttons { display: flex; gap: 12px; - + // 移动端适配 @media (max-width: 768px) { width: 100%; flex-direction: column; gap: 8px; } - + // 小屏移动端适配 @media (max-width: 480px) { gap: 6px; } - + .analysis-button { padding: 10px 16px; background: $blue; @@ -568,21 +536,21 @@ $purple: #a855f7; font-weight: 500; transition: all 0.2s ease; white-space: nowrap; - + // PC端保持一致布局 @media (min-width: 1024px) { padding: 12px 20px; font-size: 15px; border-radius: 8px; } - + // 平板端适配 @media (max-width: 1023px) and (min-width: 769px) { padding: 11px 18px; font-size: 14px; border-radius: 7px; } - + // 移动端适配 @media (max-width: 768px) { width: 100%; @@ -590,35 +558,35 @@ $purple: #a855f7; font-size: 14px; text-align: center; } - + // 小屏移动端适配 @media (max-width: 480px) { padding: 10px 12px; font-size: 13px; } - + &:hover:not(:disabled) { background: #2563eb; transform: translateY(-1px); } - + &:disabled { background: $slate-400; cursor: not-allowed; transform: none; } - + &.sop-button { background: $green; - + &:hover:not(:disabled) { background: #16a34a; } } - + &.demand-button { background: $purple; - + &:hover:not(:disabled) { background: #9333ea; } @@ -642,24 +610,24 @@ $purple: #a855f7; grid-template-columns: 1fr 1fr; gap: 16px; height: 45%; - + // PC端保持一致布局 @media (min-width: 1024px) { gap: 20px; } - + // 平板端适配 @media (max-width: 1023px) and (min-width: 769px) { gap: 18px; } - + // 移动端适配 @media (max-width: 768px) { grid-template-columns: 1fr; height: auto; gap: 16px; } - + // 小屏移动端适配 @media (max-width: 480px) { gap: 12px; @@ -680,132 +648,132 @@ $purple: #a855f7; display: flex; flex-direction: column; overflow: hidden; - + // PC端保持一致布局 @media (min-width: 1024px) { border-radius: 12px; } - + // 平板端适配 @media (max-width: 1023px) and (min-width: 769px) { border-radius: 10px; } - + // 移动端适配 @media (max-width: 768px) { min-height: 300px; } - + // 小屏移动端适配 @media (max-width: 480px) { min-height: 250px; } - + .section-header { padding: 12px 16px; background: $slate-50; border-bottom: 1px solid $slate-200; - + // PC端保持一致布局 @media (min-width: 1024px) { padding: 16px 20px; } - + // 平板端适配 @media (max-width: 1023px) and (min-width: 769px) { padding: 14px 18px; } - + // 移动端适配 @media (max-width: 768px) { padding: 12px 16px; } - + // 小屏移动端适配 @media (max-width: 480px) { padding: 10px 12px; } - + h4 { margin: 0; font-size: 16px; font-weight: 600; color: $slate-700; - + // PC端保持一致布局 @media (min-width: 1024px) { font-size: 18px; } - + // 平板端适配 @media (max-width: 1023px) and (min-width: 769px) { font-size: 17px; } - + // 移动端适配 @media (max-width: 768px) { font-size: 15px; } - + // 小屏移动端适配 @media (max-width: 480px) { font-size: 14px; } } } - + .section-content { flex: 1; padding: 16px; overflow-y: auto; - + // PC端保持一致布局 @media (min-width: 1024px) { padding: 20px; } - + // 平板端适配 @media (max-width: 1023px) and (min-width: 769px) { padding: 18px; } - + // 移动端适配 @media (max-width: 768px) { padding: 16px; } - + // 小屏移动端适配 @media (max-width: 480px) { padding: 12px; } - + .text-content { height: 100%; - + .analysis-text { color: $slate-700; font-size: 14px; line-height: 1.6; word-wrap: break-word; - + // PC端保持一致布局 @media (min-width: 1024px) { font-size: 15px; line-height: 1.7; } - + // 平板端适配 @media (max-width: 1023px) and (min-width: 769px) { font-size: 14px; line-height: 1.65; } - + // 移动端适配 @media (max-width: 768px) { font-size: 13px; line-height: 1.6; } - + // 小屏移动端适配 @media (max-width: 480px) { font-size: 12px; @@ -813,7 +781,7 @@ $purple: #a855f7; } } } - + .placeholder-text { display: flex; align-items: center; @@ -822,32 +790,32 @@ $purple: #a855f7; background: $slate-50; border-radius: 6px; border: 2px dashed $slate-200; - + p { margin: 0; color: $slate-500; font-size: 14px; text-align: center; padding: 16px; - + // PC端保持一致布局 @media (min-width: 1024px) { font-size: 15px; padding: 20px; } - + // 平板端适配 @media (max-width: 1023px) and (min-width: 769px) { font-size: 14px; padding: 18px; } - + // 移动端适配 @media (max-width: 768px) { font-size: 13px; padding: 16px; } - + // 小屏移动端适配 @media (max-width: 480px) { font-size: 12px; @@ -856,144 +824,170 @@ $purple: #a855f7; } } } - + // 不同分析区域的主题色 &.basic-analysis { .section-header { background: linear-gradient(135deg, #eff6ff 0%, #dbeafe 100%); - + h4 { color: $blue; } } } - + &.sop-analysis { .section-header { background: linear-gradient(135deg, #f0fdf4 0%, #dcfce7 100%); - + h4 { color: $green; } } } - + &.demand-analysis { .section-header { background: linear-gradient(135deg, #faf5ff 0%, #f3e8ff 100%); - + h4 { color: $purple; } } } - } +} // Markdown样式 .analysis-text { + // Markdown样式 - h1, h2, h3, h4, h5, h6 { + :deep(h1), + :deep(h2), + :deep(h3), + :deep(h4), + :deep(h5), + :deep(h6) { margin: 1rem 0 0.5rem 0; font-weight: 600; color: $slate-800; - + &:first-child { margin-top: 0; } } - - h1 { font-size: 1.25rem; } - h2 { font-size: 1.125rem; } - h3 { font-size: 1rem; } - h4 { font-size: 0.875rem; } - h5 { font-size: 0.75rem; } - h6 { font-size: 0.75rem; } - - p { + + :deep(h1) { + font-size: 1.25rem; + } + + :deep(h2) { + font-size: 1.125rem; + } + + :deep(h3) { + font-size: 1rem; + } + + :deep(h4) { + font-size: 0.875rem; + } + + :deep(h5) { + font-size: 0.75rem; + } + + :deep(h6) { + font-size: 0.75rem; + } + + :deep(p) { margin: 0.5rem 0; - + &:first-child { margin-top: 0; } - + &:last-child { margin-bottom: 0; } } - - ul, ol { + + :deep(ul), + :deep(ol) { margin: 0.5rem 0; padding-left: 1.5rem; - + li { margin: 0.25rem 0; } } - - blockquote { + + :deep(blockquote) { margin: 1rem 0; padding: 0.5rem 1rem; border-left: 4px solid $blue; background: rgba(59, 130, 246, 0.05); - font-style: italic; + color: $slate-600; } - - code { + + :deep(code) { background: $slate-100; padding: 0.125rem 0.25rem; border-radius: 0.25rem; font-family: 'Courier New', monospace; font-size: 0.8rem; + color: $indigo; } - - pre { + + :deep(pre) { background: $slate-100; padding: 1rem; border-radius: 0.5rem; overflow-x: auto; margin: 1rem 0; - + code { background: none; padding: 0; } } - - strong { + + :deep(strong) { font-weight: 600; color: $slate-800; } - - em { + + :deep(em) { font-style: italic; } - - a { + + :deep(a) { color: $blue; text-decoration: none; - + &:hover { text-decoration: underline; } } - - hr { + + :deep(hr) { margin: 1.5rem 0; border: none; border-top: 1px solid $slate-200; } - - table { + + :deep(table) { width: 100%; border-collapse: collapse; margin: 1rem 0; - - th, td { + + th, + td { padding: 0.5rem; border: 1px solid $slate-200; text-align: left; } - + th { background: $slate-50; font-weight: 600; @@ -1011,55 +1005,55 @@ $purple: #a855f7; border-radius: 8px; border: 2px dashed $slate-200; color: $slate-500; - + // PC端保持一致布局 @media (min-width: 1024px) { border-radius: 12px; min-height: 500px; } - + // 平板端适配 @media (max-width: 1023px) and (min-width: 769px) { border-radius: 10px; min-height: 450px; } - + // 移动端适配 @media (max-width: 768px) { height: 400px; border-radius: 8px; } - + // 小屏移动端适配 @media (max-width: 480px) { height: 300px; border-radius: 6px; } - + p { margin: 0; font-size: 1rem; text-align: center; padding: 1rem; - + // PC端保持一致布局 @media (min-width: 1024px) { font-size: 1.125rem; padding: 1.5rem; } - + // 平板端适配 @media (max-width: 1023px) and (min-width: 769px) { font-size: 1.0625rem; padding: 1.25rem; } - + // 移动端适配 @media (max-width: 768px) { font-size: 0.875rem; padding: 1rem; } - + // 小屏移动端适配 @media (max-width: 480px) { font-size: 0.75rem; @@ -1075,9 +1069,9 @@ h2.section-title { } h4 { - font-weight: 600; - color: $slate-700; - margin-bottom: 0.5rem; + font-weight: 600; + color: $slate-700; + margin-bottom: 0.5rem; } // Context Panel @@ -1085,8 +1079,6 @@ h4 { background-color: $white; border-radius: 0.5rem; box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06); - // padding: 1rem; - // margin-top: 12px; } // 分析区域布局优化 @@ -1095,17 +1087,17 @@ h4 { @media (min-width: 1024px) { gap: 20px; } - + // 平板端适配 @media (max-width: 1023px) and (min-width: 769px) { gap: 18px; } - + // 移动端适配 @media (max-width: 768px) { gap: 16px; } - + // 小屏移动端适配 @media (max-width: 480px) { gap: 12px; @@ -1119,11 +1111,10 @@ h4 { height: auto; min-height: 300px; } - + // 小屏移动端适配 @media (max-width: 480px) { min-height: 250px; } } - \ No newline at end of file diff --git a/my-vue-app/src/views/person/sale.vue b/my-vue-app/src/views/person/sale.vue index b92e1af..5b6e1a2 100644 --- a/my-vue-app/src/views/person/sale.vue +++ b/my-vue-app/src/views/person/sale.vue @@ -98,9 +98,6 @@