Просмотр исходного кода

客户管理增加AI聊天功能存档

peicj 1 день назад
Родитель
Сommit
22495a5e7e
3 измененных файлов с 1637 добавлено и 320 удалено
  1. 116 0
      src/api/aiChat/aiChatSession.js
  2. 368 250
      src/views/crm/customer/customerDetail.vue
  3. 1153 70
      src/views/crm/customerAiChat/index.vue

+ 116 - 0
src/api/aiChat/aiChatSession.js

@@ -0,0 +1,116 @@
+import request from '@/utils/request'
+
+// 获取会话列表(分页)
+export function getChatSessionList(query) {
+  return request({
+    url: '/crm/customer/chat/chatSession/list',
+    method: 'get',
+    params: query
+  })
+}
+
+// 创建新会话
+export function createChatSession(data) {
+  return request({
+    url: '/crm/customer/chat/chatSession',
+    method: 'post',
+    data: data
+  })
+}
+
+// 更新会话标题
+export function updateChatSessionTitle(data) {
+  return request({
+    url: '/crm/customer/chat/chatSession/title',
+    method: 'put',
+    data: data
+  })
+}
+
+// 更新会话关联客户
+export function updateChatSessionCustomer(sessionId, customerId) {
+  return request({
+    url: '/crm/customer/chat/chatSession/customer',
+    method: 'put',
+    data: { sessionId, customerId }
+  })
+}
+
+// 删除会话
+export function deleteChatSession(sessionId) {
+  return request({
+    url: '/crm/customer/chat/chatSession/' + sessionId,
+    method: 'delete'
+  })
+}
+// 置顶/取消置顶会话
+export function pinChatSession(sessionId, isPinned) {
+    return request({
+        url: '/crm/customer/chat/chatSession/pin',
+        method: 'put',
+        data: { sessionId, isPinned }
+    })
+}
+
+
+
+
+
+
+
+
+
+
+
+// 获取消息列表(分页,带时间游标)
+export function getChatMessageList(query) {
+  return request({
+    url: '/crm/customer/chat/chatMsg/list',
+    method: 'get',
+    params: query
+  })
+}
+
+// 批量保存消息
+export function batchSaveMessages(data) {
+  return request({
+    url: '/crm/customer/chat/chatMsg/batch',
+    method: 'post',
+    data: data
+  })
+}
+
+// 单条消息保存
+export function saveChatMessage(data) {
+  return request({
+    url: '/crm/customer/chat/chatMsg',
+    method: 'post',
+    data: data
+  })
+}
+
+// 更新消息
+export function updateChatMessage(data) {
+  return request({
+    url: '/crm/customer/chat/chatMsg',
+    method: 'put',
+    data: data
+  })
+}
+
+// 删除消息
+export function deleteChatMessage(msgId) {
+  return request({
+    url: '/crm/customer/chat/chatMsg/' + msgId,
+    method: 'delete'
+  })
+}
+
+// 获取会话详情
+export function getChatSessionDetail(sessionId) {
+  return request({
+    url: '/crm/customer/chat/chatMsg/' + sessionId,
+    method: 'get'
+  })
+}
+

+ 368 - 250
src/views/crm/customer/customerDetail.vue

@@ -45,22 +45,21 @@
                         </h3>
                     </div>
                     <div class="tags-container">
-                        <div v-if="allAiTags.length > 0" class="tags-grid">
+                        <div v-if="allAiTags.length > 0" class="tags-list">
                             <div
                                 v-for="(item, index) in visibleTags"
                                 :key="item.id"
-                                class="tag-chip"
+                                class="tag-item"
                                 :class="{ 'tag-highlight': index < 3 }"
                             >
-                                <i class="fas fa-tag tag-icon"></i>
-                                <span class="tag-name">{{ item.propertyName }}</span>
+                                <span class="tag-key">{{ item.propertyName }}</span>
                                 <span class="tag-separator">:</span>
                                 <span class="tag-value">{{ item.propertyValue }}</span>
                             </div>
                         </div>
                         <div v-else class="empty-tags">
                             <i class="fas fa-inbox"></i>
-                            <span>暂无AI标签</span>
+                            <span>暂无 AI 标签</span>
                         </div>
 
                         <!-- 加载更多按钮 -->
@@ -116,39 +115,39 @@
                     <div class="records-table-wrapper">
                         <table class="records-table">
                             <thead>
-                                <tr>
-                                    <th><i class="fas fa-user"></i> 客户名称</th>
-                                    <th><i class="fas fa-chart-line"></i> 流失等级</th>
-                                    <th><i class="fas fa-heart"></i> 客户意向度</th>
-                                    <th><i class="far fa-clock"></i> 创建时间</th>
-                                    <th><i class="fas fa-cog"></i> 操作</th>
-                                </tr>
+                            <tr>
+                                <th><i class="fas fa-user"></i> 客户名称</th>
+                                <th><i class="fas fa-chart-line"></i> 流失等级</th>
+                                <th><i class="fas fa-heart"></i> 客户意向度</th>
+                                <th><i class="far fa-clock"></i> 创建时间</th>
+                                <th><i class="fas fa-cog"></i> 操作</th>
+                            </tr>
                             </thead>
                             <tbody>
-                                <tr v-for="record in communicationRecords" :key="record.id" class="record-row">
-                                    <td class="record-cell">{{ customerData.customerName }}</td>
-                                    <td class="record-cell">
+                            <tr v-for="record in communicationRecords" :key="record.id" class="record-row">
+                                <td class="record-cell">{{ customerData.customerName }}</td>
+                                <td class="record-cell">
                                         <span class="risk-level-tag" :class="getRecordRiskLevelClass(record)">
                                             {{ getRecordRiskLevelLabel(record) }}
                                         </span>
-                                    </td>
-                                    <td class="record-cell">
+                                </td>
+                                <td class="record-cell">
                                         <span class="intention-degree">
                                             {{ getIntentionDegreeFromRecord(record) }}
                                         </span>
-                                    </td>
-                                    <td class="record-cell">{{ record.createTime }}</td>
-                                    <td class="record-cell">
-                                        <button @click="viewChat(record)" class="btn-view-chat">
-                                            <i class="fas fa-comments"></i> 聊天详情
-                                        </button>
-                                    </td>
-                                </tr>
-                                <tr v-if="!communicationRecords.length">
-                                    <td colspan="5" class="empty-tip">
-                                        <i class="fas fa-inbox"></i> 暂无沟通记录
-                                    </td>
-                                </tr>
+                                </td>
+                                <td class="record-cell">{{ record.createTime }}</td>
+                                <td class="record-cell">
+                                    <button @click="viewChat(record)" class="btn-view-chat">
+                                        <i class="fas fa-comments"></i> 聊天详情
+                                    </button>
+                                </td>
+                            </tr>
+                            <tr v-if="!communicationRecords.length">
+                                <td colspan="5" class="empty-tip">
+                                    <i class="fas fa-inbox"></i> 暂无沟通记录
+                                </td>
+                            </tr>
                             </tbody>
                         </table>
 
@@ -158,7 +157,7 @@
                                 @current-change="handleCommunicationRecordsPageChange"
                                 @size-change="handleCommunicationRecordsSizeChange"
                                 :current-page="communicationRecordsPageNum"
-                                :page-sizes="[3, 10, 20, 50]"
+                                :page-sizes="[4, 10, 20, 50]"
                                 :page-size="communicationRecordsPageSize"
                                 layout="total, sizes, prev, pager, next, jumper"
                                 :total="communicationRecordsTotal"
@@ -172,7 +171,9 @@
                         <div class="chat-dialog-header">
                             <div class="chat-title">
                                 <i class="fas fa-comments"></i>
-                                <span>{{ (currentChatRecord && currentChatRecord.customerName) || (customerData && customerData.customerName) }} - 历史聊天记录</span>
+                                <span>{{
+                                        (currentChatRecord && currentChatRecord.customerName) || (customerData && customerData.customerName)
+                                    }} - 历史聊天记录</span>
                             </div>
                             <button @click="closeChatDialog" class="btn-close">
                                 ×
@@ -190,7 +191,8 @@
                                     <!-- AI 消息:头像在左,名称在聊天内容上方靠左 -->
                                     <div v-if="msg.type === 'ai'" class="message-wrapper message-wrapper-left">
                                         <div class="message-avatar message-avatar-ai">
-                                            <img src="/static/images/ai-avatar.svg" alt="AI" @error="handleAvatarError($event, 'ai')" />
+                                            <img src="/static/images/ai-avatar.svg" alt="AI"
+                                                 @error="handleAvatarError($event, 'ai')"/>
                                         </div>
                                         <div class="message-content">
                                             <div class="message-name message-name-ai">AI</div>
@@ -208,13 +210,16 @@
                                             </div>
                                         </div>
                                         <div class="message-avatar message-avatar-customer">
-                                            <img src="/static/images/customer-avatar.svg" alt="客户" @error="handleAvatarError($event, 'customer')" />
+                                            <img src="/static/images/customer-avatar.svg" alt="客户"
+                                                 @error="handleAvatarError($event, 'customer')"/>
                                         </div>
                                     </div>
                                 </div>
 
                                 <!-- 空数据提示 -->
-                                <div v-if="!parseChatMessages(currentChatRecord && currentChatRecord.aiChatRecord).length" class="empty-chat-tip">
+                                <div
+                                    v-if="!parseChatMessages(currentChatRecord && currentChatRecord.aiChatRecord).length"
+                                    class="empty-chat-tip">
                                     <i class="fas fa-inbox"></i> 暂无聊天内容
                                 </div>
                             </div>
@@ -256,19 +261,25 @@
                     <div class="intention-section">
                         <div class="intention-header">
                             <span class="intention-label">客户意向度</span>
-                            <span v-if="getIntentionDegree() === -1" class="no-data-tip">暂无分析数据</span>
-                        </div>
-                        <div class="progress-bar-wrapper" v-if="getIntentionDegree() !== -1">
-                            <div class="progress-bar-modern">
-                                <div class="progress-fill-modern"
-                                     :style="{
-                                         width: getIntentionDegree() ,
-                                         background: getProgressGradient(getIntentionDegree())
-                                     }"
-                                     :class="getIntentionClass(getIntentionDegree())">
-                                    <span class="progress-text">{{ getIntentionDegree() }}</span>
+                            <el-tooltip placement="top" effect="light">
+                                <i class="el-icon-info intention-info-icon"></i>
+                                <div slot="content" class="intention-tooltip">
+                                    <div><strong>A 级</strong> - 最高意向度</div>
+                                    <div><strong>B 级</strong> - 高意向度</div>
+                                    <div><strong>C 级</strong> - 中等意向度</div>
+                                    <div><strong>D 级</strong> - 较低意向度</div>
+                                    <div><strong>E 级</strong> - 低意向度</div>
+                                    <div><strong>F 级</strong> - 最低意向度</div>
                                 </div>
-                            </div>
+                            </el-tooltip>
+                        </div>
+                        <div class="intention-watermark"
+                             v-if="getIntentionDegree()"
+                             :class="getIntentionColorClass(getIntentionDegree())">
+                            {{ getIntentionDegree() }}
+                        </div>
+                        <div class="no-intention-tip" v-else>
+                            暂无评级
                         </div>
                     </div>
                 </div>
@@ -289,13 +300,13 @@ export default {
             customerData: null, // 从列表页传递过来的完整客户数据
             aiTags: [],// 需要显示的 AI 标签
             allAiTags: [], // 存储所有 AI 标签
-            tagsPageSize: 3,//默认展开标签的数量
+            tagsPageSize: 5,//默认展开标签的数量
             isExpanded: false, // 是否已展开显示全部标签
             // 聊天记录分页相关
             communicationRecords: [],
             communicationRecordsTotal: 0,
             communicationRecordsPageNum: 1,
-            communicationRecordsPageSize: 3,
+            communicationRecordsPageSize: 4,
             // 聊天弹窗相关
             chatDialogVisible: false, // 聊天弹窗是否显示
             currentChatRecord: null, // 当前查看的聊天记录
@@ -380,7 +391,7 @@ export default {
     methods: {
         loadCustomerTags() {
             listByCustomerId(this.customerUserId).then((response) => {
-                if(response.code === 200){
+                if (response.code === 200) {
                     this.allAiTags = response.data || [];
                     // 强制 Vue 更新视图
                     this.$forceUpdate();
@@ -398,7 +409,7 @@ export default {
                 customerId: this.customerUserId
             };
             listAnalyze(params).then((response) => {
-                if(response.code === 200){
+                if (response.code === 200) {
                     this.communicationRecords = response.rows || [];
                     this.communicationRecordsTotal = response.total || 0;
                 } else {
@@ -443,7 +454,7 @@ export default {
                         }));
                     }
                     // 如果是对象,转换为数组
-                    return [{ content: parsed.content, type: parsed.type || 'ai' }];
+                    return [{content: parsed.content, type: parsed.type || 'ai'}];
                 } catch (e) {
                     // 解析失败,返回空数组
                     console.error('解析聊天记录失败:', e);
@@ -470,25 +481,7 @@ export default {
                 img.parentElement.innerHTML = '<i class="fas fa-user" style="font-size: 24px; color: white;"></i>';
             }
         },
-        // 获取意向度样式类
-        getIntentionClass(percent) {
-            if (percent >= 80) return 'excellent';
-            if (percent >= 60) return 'good';
-            if (percent >= 40) return 'normal';
-            return 'poor';
-        },
-        // 获取进度条渐变色
-        getProgressGradient(percent) {
-            if (percent >= 80) {
-                return 'linear-gradient(90deg, #10b981 0%, #059669 100%)';
-            } else if (percent >= 60) {
-                return 'linear-gradient(90deg, #3b82f6 0%, #2563eb 100%)';
-            } else if (percent >= 40) {
-                return 'linear-gradient(90deg, #f59e0b 0%, #d97706 100%)';
-            } else {
-                return 'linear-gradient(90deg, #ef4444 0%, #dc2626 100%)';
-            }
-        },
+
         // 分页改变事件
         handleCommunicationRecordsPageChange(pageNum) {
             this.communicationRecordsPageNum = pageNum;
@@ -553,10 +546,24 @@ export default {
         // 获取客户意向度
         getIntentionDegree() {
             if (!this.communicationRecords || this.communicationRecords.length === 0) {
-                return -1;
+                return '';
             }
             const latestRecord = this.communicationRecords[0];
-            return latestRecord.intentionDegree
+            return latestRecord.intentionDegree || '';
+        },
+        // 根据意向度等级获取颜色样式类
+        getIntentionColorClass(grade) {
+            if (!grade) return '';
+            const gradeUpper = grade.toUpperCase();
+            const colorMap = {
+                'A': 'intention-grade-a',
+                'B': 'intention-grade-b',
+                'C': 'intention-grade-c',
+                'D': 'intention-grade-d',
+                'E': 'intention-grade-e',
+                'F': 'intention-grade-f'
+            };
+            return colorMap[gradeUpper] || '';
         },
         // 获取单条记录的风险等级数值
         getRecordAttritionLevel(record) {
@@ -615,13 +622,31 @@ export default {
     box-sizing: border-box;
 }
 
+.left-column {
+    display: flex;
+    flex-direction: column;
+    gap: 12px;
+}
+
+.middle-column {
+    display: flex;
+    flex-direction: column;
+    gap: 12px;
+}
+
+.right-column {
+    display: flex;
+    flex-direction: column;
+    gap: 12px;
+}
+
 .customer-container {
     max-width: 1600px;
     margin: 0 auto;
     font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
     background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
-    padding: 24px;
-    min-height: 100vh;
+    padding: 8px;
+    min-height: calc(100vh - 50px);
 }
 
 @keyframes pulse {
@@ -636,8 +661,9 @@ export default {
 .main-grid-three-columns {
     display: grid;
     grid-template-columns: 380px 1fr 340px;
-    gap: 28px;
+    gap: 12px;
     animation: fadeIn 0.6s ease-in-out;
+    align-items: stretch;
 }
 
 @keyframes fadeIn {
@@ -666,10 +692,10 @@ export default {
 
 .card {
     background: white;
-    border-radius: 20px;
-    padding: 24px;
-    margin-bottom: 28px;
-    box-shadow: 0 8px 32px rgba(0, 0, 0, 0.08);
+    border-radius: 12px;
+    padding: 12px;
+    margin-bottom: 12px;
+    box-shadow: 0 4px 16px rgba(0, 0, 0, 0.06);
     transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
     border: 1px solid rgba(226, 232, 240, 0.5);
     position: relative;
@@ -715,13 +741,17 @@ export default {
     right: -50%;
     width: 200%;
     height: 200%;
-    background: radial-gradient(circle, rgba(255,255,255,0.1) 0%, transparent 70%);
+    background: radial-gradient(circle, rgba(255, 255, 255, 0.1) 0%, transparent 70%);
     animation: shimmer 3s infinite;
 }
 
 @keyframes shimmer {
-    0%, 100% { transform: translate(0, 0); }
-    50% { transform: translate(-30%, -30%); }
+    0%, 100% {
+        transform: translate(0, 0);
+    }
+    50% {
+        transform: translate(-30%, -30%);
+    }
 }
 
 .card-highlight .card-header h3 {
@@ -730,9 +760,9 @@ export default {
 
 .card-highlight .summary-text {
     color: white;
-    font-size: 15px;
-    line-height: 1.7;
-    max-height: 150px;
+    font-size: 17px;
+    line-height: 1.6;
+    max-height: 120px;
     overflow-y: auto;
     overflow-x: hidden;
     padding-right: 4px;
@@ -759,10 +789,12 @@ export default {
 .card-highlight .summary-meta span {
     color: rgba(255, 255, 255, 0.9);
 }
+
 /* 表格卡片 */
 .card-table {
     background: white;
 }
+
 /* 风险卡片 */
 .risk-card {
     border-left: 4px solid #10b981;
@@ -820,7 +852,7 @@ export default {
     gap: 6px;
     padding: 6px 14px;
     border-radius: 8px;
-    font-size: 13px;
+    font-size: 16px;
     font-weight: 700;
     color: white;
     box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
@@ -854,12 +886,12 @@ export default {
 
 /* 风险分析内容 */
 .risk-analysis {
-    margin-top: 16px;
-    padding: 16px;
+    margin-top: 10px;
+    padding: 10px;
     background: rgba(255, 255, 255, 0.7);
-    border-radius: 12px;
+    border-radius: 10px;
     backdrop-filter: blur(10px);
-    max-height: 200px;
+    max-height: 180px;
     overflow-y: auto;
     overflow-x: hidden;
     padding-right: 4px;
@@ -884,28 +916,28 @@ export default {
 }
 
 .risk-text {
-    font-size: 14px;
-    line-height: 1.8;
+    font-size: 16px;
+    line-height: 1.7;
     color: #475569;
     margin: 0;
 }
 
 .risk-tip {
-    margin-top: 12px;
-    padding: 12px;
+    margin-top: 8px;
+    padding: 8px;
     background: linear-gradient(135deg, rgba(255, 255, 255, 0.9) 0%, rgba(248, 250, 252, 0.9) 100%);
     border-left: 3px solid #f59e0b;
-    border-radius: 8px;
-    font-size: 13px;
+    border-radius: 6px;
+    font-size: 15px;
     color: #92400e;
     display: flex;
     align-items: flex-start;
-    gap: 8px;
+    gap: 6px;
 }
 
 .risk-tip i {
-    font-size: 14px;
-    margin-top: 2px;
+    font-size: 12px;
+    margin-top: 1px;
     color: #f59e0b;
 }
 
@@ -921,17 +953,17 @@ export default {
     justify-content: space-between;
     align-items: center;
     border-bottom: 1px solid #eef2ff;
-    padding-bottom: 12px;
-    margin-bottom: 16px;
+    padding-bottom: 6px;
+    margin-bottom: 10px;
     flex-wrap: wrap;
 }
 
 .card-header h3 {
-    font-size: 17px;
+    font-size: 19px;
     font-weight: 700;
     display: flex;
     align-items: center;
-    gap: 10px;
+    gap: 8px;
     color: #0f172a;
     letter-spacing: -0.02em;
 }
@@ -968,7 +1000,7 @@ export default {
 .records-table {
     width: 100%;
     border-collapse: collapse;
-    font-size: 14px;
+    font-size: 15px;
 }
 
 .records-table thead {
@@ -980,7 +1012,7 @@ export default {
     padding: 12px 16px;
     text-align: center !important;
     font-weight: 600;
-    font-size: 13px;
+    font-size: 14px;
     border-bottom: 2px solid #e2e8f0;
     color: #64748b;
 }
@@ -1007,7 +1039,7 @@ export default {
 }
 
 .record-cell {
-    font-size: 14px;
+    font-size: 15px;
     color: #334155;
     text-align: center !important;
 }
@@ -1017,7 +1049,7 @@ export default {
     display: inline-block;
     padding: 4px 12px;
     border-radius: 6px;
-    font-size: 12px;
+    font-size: 13px;
     font-weight: 500;
     border: 1px solid;
 }
@@ -1027,7 +1059,7 @@ export default {
     display: inline-block;
     padding: 4px 12px;
     border-radius: 6px;
-    font-size: 13px;
+    font-size: 14px;
     font-weight: 600;
     color: #6366f1;
     background: rgba(99, 102, 241, 0.08);
@@ -1040,7 +1072,7 @@ export default {
     border: 1px solid #667eea;
     padding: 6px 12px;
     border-radius: 6px;
-    font-size: 13px;
+    font-size: 14px;
     cursor: pointer;
     display: inline-flex;
     align-items: center;
@@ -1054,7 +1086,7 @@ export default {
 
 .empty-tip {
     color: #94a3b8;
-    font-size: 13px;
+    font-size: 16px;
     text-align: center;
     padding: 40px 20px;
     background: transparent;
@@ -1063,11 +1095,11 @@ export default {
 
 /* 沟通摘要样式 */
 .summary-text.compact {
-    max-height: 120px;
+    max-height: 100px;
     overflow-y: auto;
     overflow-x: hidden;
     line-height: 1.6;
-    font-size: 14px;
+    font-size: 16px;
     color: #475569;
     padding-right: 4px;
 }
@@ -1093,7 +1125,7 @@ export default {
 /* AI 标签美化样式 */
 .tags-container {
     padding: 0;
-    max-height: 300px;
+    min-height: calc(16px * 5 + 6px * 4 + 12px * 2);
     overflow-y: auto;
     overflow-x: hidden;
     display: flex;
@@ -1118,50 +1150,49 @@ export default {
     background: linear-gradient(180deg, #94a3b8 0%, #64748b 100%);
 }
 
-.tags-grid {
-    display: grid;
-    grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
+/* 标签列表 - 每行一个 */
+.tags-list {
+    display: flex;
+    flex-direction: column;
     gap: 6px;
-    margin-bottom: 12px;
+    margin-bottom: 6px;
 }
 
-.tag-chip {
-    display: grid;
-    grid-template-columns: 35% 65%;
+.tag-item {
+    display: flex;
     align-items: center;
-    gap: 8px;
-    padding: 6px 12px;
+    gap: 6px;
+    padding: 6px 10px;
     background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);
     border: 1px solid #e2e8f0;
-    border-radius: 8px;
-    font-size: 13px;
+    border-radius: 6px;
+    font-size: 15px;
     transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
     cursor: default;
     position: relative;
     overflow: hidden;
-    word-break: break-word;
 }
 
-.tag-chip::before {
+.tag-item::before {
     content: '';
     position: absolute;
     top: 0;
     left: 0;
-    width: 3px;
+    width: 4px;
     height: 100%;
     background: linear-gradient(180deg, #94a3b8 0%, #64748b 100%);
     transition: width 0.3s ease;
 }
 
-.tag-chip:hover {
+.tag-item:hover {
     background: linear-gradient(135deg, #f1f5f9 0%, #e2e8f0 100%);
     border-color: #cbd5e1;
-    transform: translateX(4px);
-    box-shadow: 0 2px 6px rgba(0, 0, 0, 0.06);
+    transform: translateX(6px);
+    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
 }
 
-.tag-chip:hover::before {
-    width: 4px;
+.tag-item:hover::before {
+    width: 5px;
     background: linear-gradient(180deg, #3b82f6 0%, #2563eb 100%);
 }
 
@@ -1174,28 +1205,19 @@ export default {
     background: linear-gradient(180deg, #3b82f6 0%, #2563eb 100%);
 }
 
-.tag-icon {
-    color: #64748b;
-    font-size: 12px;
-    transition: all 0.3s ease;
-}
-
-.tag-chip:hover .tag-icon {
-    color: #3b82f6;
-    transform: scale(1.05);
-}
-
-.tag-name {
+.tag-key {
     font-weight: 600;
     color: #475569;
-    white-space: normal;
-    word-break: break-word;
-    line-height: 1.3;
+    white-space: nowrap;
+    flex-shrink: 0;
+    font-size: 15px;
 }
 
 .tag-separator {
     color: #94a3b8;
     font-weight: 300;
+    flex-shrink: 0;
+    font-size: 15px;
 }
 
 .tag-value {
@@ -1204,6 +1226,7 @@ export default {
     word-break: break-word;
     flex: 1;
     min-width: 0;
+    font-size: 15px;
 }
 
 .empty-tags {
@@ -1213,7 +1236,7 @@ export default {
     justify-content: center;
     padding: 40px 20px;
     color: #94a3b8;
-    font-size: 14px;
+    font-size: 17px;
     background: linear-gradient(135deg, rgba(248, 250, 252, 0.5) 0%, rgba(241, 245, 249, 0.5) 100%);
     border-radius: 12px;
     border: 2px dashed #e2e8f0;
@@ -1244,7 +1267,7 @@ export default {
     border: 1px solid #667eea;
     padding: 8px 16px;
     border-radius: 6px;
-    font-size: 13px;
+    font-size: 16px;
     cursor: pointer;
     display: inline-flex;
     align-items: center;
@@ -1411,6 +1434,7 @@ export default {
     color: #667eea;
     font-weight: 500;
 }
+
 .message-avatar {
     width: 32px;
     height: 32px;
@@ -1519,6 +1543,7 @@ export default {
     border-left: 6px solid #d9fdd3;
     border-right: none;
 }
+
 .empty-chat-tip {
     display: flex;
     flex-direction: column;
@@ -1539,8 +1564,8 @@ export default {
 .profile-grid {
     display: flex;
     flex-direction: column;
-    gap: 6px;
-    max-height: 400px;
+    gap: 4px;
+    max-height: 350px;
     overflow-y: auto;
     overflow-x: hidden;
     padding-right: 4px;
@@ -1569,9 +1594,9 @@ export default {
     display: grid;
     grid-template-columns: 35% 65%;
     align-items: flex-start;
-    gap: 8px;
-    padding: 6px 12px;
-    border-radius: 8px;
+    gap: 6px;
+    padding: 4px 10px;
+    border-radius: 6px;
     transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
     background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);
     border: 1px solid #e2e8f0;
@@ -1603,13 +1628,13 @@ export default {
 }
 
 .profile-item .label {
-    font-size: 13px;
+    font-size: 15px;
     color: #64748b;
     font-weight: 600;
     white-space: normal;
     display: flex;
     align-items: center;
-    gap: 6px;
+    gap: 4px;
     line-height: 1.4;
     min-width: 0;
 }
@@ -1623,7 +1648,7 @@ export default {
 }
 
 .profile-item .value {
-    font-size: 14px;
+    font-size: 16px;
     color: #0f172a;
     font-weight: 500;
     word-break: break-word;
@@ -1633,7 +1658,7 @@ export default {
 
 .profile-item .value.highlight {
     color: #0369a1;
-    font-size: 14px;
+    font-size: 16px;
     font-weight: 600;
 }
 
@@ -1654,29 +1679,29 @@ export default {
 /* 客户关注点 & 意向度样式 */
 .focus-points {
     padding: 0;
-    margin-bottom: 20px;
+    margin-bottom: 10px;
 }
 
 .focus-title {
-    font-size: 14px;
+    font-size: 17px;
     color: #64748b;
     font-weight: 600;
-    margin-bottom: 12px;
+    margin-bottom: 8px;
     display: flex;
     align-items: center;
-    gap: 8px;
+    gap: 6px;
 }
 
 .focus-title i {
     color: #3b82f6;
-    font-size: 16px;
+    font-size: 14px;
 }
 
 .focus-list {
     list-style: none;
     padding: 0;
     margin: 0;
-    max-height: 180px;
+    max-height: 200px;
     overflow-y: auto;
     overflow-x: hidden;
     padding-right: 4px;
@@ -1703,14 +1728,14 @@ export default {
 .focus-item {
     display: flex;
     align-items: flex-start;
-    gap: 8px;
-    padding: 8px 12px;
+    gap: 6px;
+    padding: 6px 10px;
     margin-bottom: 6px;
     background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);
-    border-radius: 8px;
+    border-radius: 6px;
     border: 1px solid #e2e8f0;
     transition: all 0.3s ease;
-    font-size: 14px;
+    font-size: 15px;
     color: #334155;
     line-height: 1.5;
 }
@@ -1724,131 +1749,224 @@ export default {
 
 .focus-item i {
     color: #3b82f6;
-    font-size: 12px;
-    margin-top: 2px;
+    font-size: 10px;
+    margin-top: 1px;
     flex-shrink: 0;
 }
 
-/* 意向度样式 */
+/* 意向度样式 - 水印风格 */
 .intention-section {
-    margin-top: 20px;
-    padding-top: 16px;
+    margin-top: 10px;
+    padding-top: 8px;
     border-top: 1px solid #e2e8f0;
 }
 
 .intention-header {
+    margin-bottom: 6px;
     display: flex;
-    justify-content: space-between;
     align-items: center;
-    margin-bottom: 12px;
+    gap: 6px;
 }
 
-.no-data-tip {
-    font-size: 13px;
+.intention-info-icon {
     color: #94a3b8;
-    font-style: italic;
-    padding: 4px 12px;
-    background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);
-    border-radius: 6px;
-    border: 1px dashed #cbd5e1;
+    font-size: 16px;
+    cursor: pointer;
+    transition: all 0.3s ease;
+}
+
+.intention-info-icon:hover {
+    color: #3b82f6;
+}
+
+/* 意向度提示框样式 */
+.intention-tooltip {
+    line-height: 1.8;
+    font-size: 13px;
+}
+
+.intention-tooltip div {
+    padding: 2px 0;
+}
+
+.intention-tooltip strong {
+    color: #1e293b;
+    font-weight: 600;
 }
 
 .intention-label {
-    font-size: 14px;
+    font-size: 16px;
     font-weight: 600;
     color: #64748b;
     display: flex;
     align-items: center;
-    gap: 6px;
+    gap: 4px;
 }
 
 .intention-label::before {
     content: '';
-    width: 4px;
-    height: 16px;
+    width: 3px;
+    height: 14px;
     background: linear-gradient(180deg, #3b82f6 0%, #2563eb 100%);
     border-radius: 2px;
 }
 
-.progress-bar-wrapper {
+/* 水印风格意向度显示 - 按等级着色 */
+.intention-watermark {
+    font-size: 59px;
+    font-weight: 800;
+    text-align: center;
+    padding: 18px 12px;
+    border-radius: 10px;
+    border: 2px solid;
     position: relative;
+    overflow: hidden;
+    letter-spacing: 6px;
+    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
+    transition: all 0.3s ease;
 }
 
-.progress-bar-modern {
-    width: 100%;
-    height: 24px;
-    background: linear-gradient(135deg, #f1f5f9 0%, #e2e8f0 100%);
-    border-radius: 12px;
-    overflow: hidden;
-    box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.06);
-    border: 1px solid #e2e8f0;
-    position: relative;
+/* A 级 - 金色/绿色,最高级别 */
+.intention-grade-a {
+    background: linear-gradient(135deg, #fef3c7 0%, #fde68a 50%, #fef3c7 100%);
+    border-color: #f59e0b;
+    color: #92400e;
+    text-shadow: 0 2px 4px rgba(146, 64, 14, 0.2);
 }
 
-.progress-bar-modern::before {
-    content: '';
+.intention-grade-a::after {
+    content: 'A';
     position: absolute;
-    top: 0;
-    left: 0;
-    right: 0;
-    bottom: 0;
-    background: repeating-linear-gradient(
-        45deg,
-        transparent,
-        transparent 10px,
-        rgba(255, 255, 255, 0.03) 10px,
-        rgba(255, 255, 255, 0.03) 20px
-    );
-    z-index: 1;
+    top: 50%;
+    left: 50%;
+    transform: translate(-50%, -50%) rotate(-25deg);
+    font-size: 90px;
+    font-weight: 900;
+    color: rgba(245, 158, 11, 0.08);
+    z-index: 0;
     pointer-events: none;
 }
 
-.progress-fill-modern {
-    height: 100%;
-    border-radius: 12px;
-    display: flex;
-    align-items: center;
-    justify-content: flex-end;
-    padding-right: 12px;
-    transition: width 1s cubic-bezier(0.4, 0, 0.2, 1), background 0.3s ease;
-    position: relative;
-    overflow: hidden;
-    min-width: 60px;
+/* B 级 - 蓝色 */
+.intention-grade-b {
+    background: linear-gradient(135deg, #dbeafe 0%, #bfdbfe 50%, #dbeafe 100%);
+    border-color: #3b82f6;
+    color: #1e40af;
+    text-shadow: 0 2px 4px rgba(30, 64, 175, 0.2);
 }
 
-.progress-fill-modern::before {
-    content: '';
+.intention-grade-b::after {
+    content: 'B';
     position: absolute;
-    top: 0;
-    left: 0;
-    right: 0;
-    bottom: 0;
-    background: linear-gradient(
-        90deg,
-        rgba(255, 255, 255, 0.2) 0%,
-        rgba(255, 255, 255, 0) 50%,
-        rgba(255, 255, 255, 0.2) 100%
-    );
-    animation: shimmer 2s infinite;
-    z-index: 1;
+    top: 50%;
+    left: 50%;
+    transform: translate(-50%, -50%) rotate(-25deg);
+    font-size: 90px;
+    font-weight: 900;
+    color: rgba(59, 130, 246, 0.08);
+    z-index: 0;
+    pointer-events: none;
 }
 
-@keyframes shimmer {
-    0% {
-        transform: translateX(-100%);
-    }
-    100% {
-        transform: translateX(100%);
-    }
+/* C 级 - 紫色 */
+.intention-grade-c {
+    background: linear-gradient(135deg, #e9d5ff 0%, #d8b4fe 50%, #e9d5ff 100%);
+    border-color: #a855f7;
+    color: #6b21a8;
+    text-shadow: 0 2px 4px rgba(107, 33, 168, 0.2);
 }
 
-.progress-text {
-    font-size: 13px;
-    font-weight: 700;
-    color: white;
-    text-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
-    z-index: 2;
-    font-variant-numeric: tabular-nums;
+.intention-grade-c::after {
+    content: 'C';
+    position: absolute;
+    top: 50%;
+    left: 50%;
+    transform: translate(-50%, -50%) rotate(-25deg);
+    font-size: 90px;
+    font-weight: 900;
+    color: rgba(168, 85, 247, 0.08);
+    z-index: 0;
+    pointer-events: none;
+}
+
+/* D 级 - 橙色 */
+.intention-grade-d {
+    background: linear-gradient(135deg, #fed7aa 0%, #fdba74 50%, #fed7aa 100%);
+    border-color: #f97316;
+    color: #9a3412;
+    text-shadow: 0 2px 4px rgba(154, 52, 18, 0.2);
+}
+
+.intention-grade-d::after {
+    content: 'D';
+    position: absolute;
+    top: 50%;
+    left: 50%;
+    transform: translate(-50%, -50%) rotate(-25deg);
+    font-size: 90px;
+    font-weight: 900;
+    color: rgba(249, 115, 22, 0.08);
+    z-index: 0;
+    pointer-events: none;
+}
+
+/* E 级 - 粉红色 */
+.intention-grade-e {
+    background: linear-gradient(135deg, #fbcfe8 0%, #f9a8d4 50%, #fbcfe8 100%);
+    border-color: #ec4899;
+    color: #9d174d;
+    text-shadow: 0 2px 4px rgba(157, 23, 77, 0.2);
+}
+
+.intention-grade-e::after {
+    content: 'E';
+    position: absolute;
+    top: 50%;
+    left: 50%;
+    transform: translate(-50%, -50%) rotate(-25deg);
+    font-size: 90px;
+    font-weight: 900;
+    color: rgba(236, 72, 153, 0.08);
+    z-index: 0;
+    pointer-events: none;
+}
+
+/* F 级 - 红色,最低级别 */
+.intention-grade-f {
+    background: linear-gradient(135deg, #fecaca 0%, #fca5a5 50%, #fecaca 100%);
+    border-color: #ef4444;
+    color: #991b1b;
+    text-shadow: 0 2px 4px rgba(153, 27, 27, 0.2);
+}
+
+.intention-grade-f::after {
+    content: 'F';
+    position: absolute;
+    top: 50%;
+    left: 50%;
+    transform: translate(-50%, -50%) rotate(-25deg);
+    font-size: 90px;
+    font-weight: 900;
+    color: rgba(239, 68, 68, 0.08);
+    z-index: 0;
+    pointer-events: none;
+}
+
+.intention-watermark span {
+    position: relative;
+    z-index: 1;
+}
+
+/* 暂无评级提示 */
+.no-intention-tip {
+    text-align: center;
+    padding: 18px 12px;
+    font-size: 16px;
+    color: #94a3b8;
+    font-style: italic;
+    background: linear-gradient(135deg, rgba(248, 250, 252, 0.5) 0%, rgba(241, 245, 249, 0.5) 100%);
+    border-radius: 10px;
+    border: 2px dashed #e2e8f0;
 }
 
 .card-header h3 i {

Разница между файлами не показана из-за своего большого размера
+ 1153 - 70
src/views/crm/customerAiChat/index.vue


Некоторые файлы не были показаны из-за большого количества измененных файлов