Bläddra i källkod

update:page-crm-decision

0235664 13 timmar sedan
förälder
incheckning
4e06cbc8d3

+ 2 - 2
ai-assisant/src/index.html

@@ -8,8 +8,8 @@
   <link rel="icon" type="image/x-icon" href="favicon.ico">
 </head>
 <body>
-  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
-  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
+  <link rel="stylesheet" href="https://cdn.bootcdn.net/ajax/libs/font-awesome/6.7.2/css/all.min.css">
+  <!-- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> -->
 <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
 
   <app-page-crm-data></app-page-crm-data>

+ 210 - 0
ai-assisant/src/lib/cloud/comletion.ts

@@ -0,0 +1,210 @@
+export interface TestMessage {
+    role: string
+    content: string
+}
+
+export class TestCompletion {
+    token: string = "r:60abef69e7cd8181b146ceaba1fdbf02"
+    messageList: any = []
+    stream: boolean = true;
+    constructor(messageList: any) {
+        this.messageList = messageList || this.messageList
+    }
+    
+    async sendMessage(messageList?: null | Array<TestMessage>, onMessage?: (content: string) => void): Promise<any> {
+        this.messageList = messageList || this.messageList
+        let body = {
+            "messages": this.messageList,
+            "stream": this.stream,
+            "model": "fmode-4.5-128k",
+            "temperature": 0.5,
+            "presence_penalty": 0,
+            "frequency_penalty": 0,
+            "token": "Bearer " + this.token
+        }
+
+        let response = await fetch("https://server.fmode.cn/api/apig/aigc/gpt/v1/chat/completions", {
+            "headers": {
+            },
+            "body": JSON.stringify(body),
+            "method": "POST",
+            "mode": "cors",
+            "credentials": "omit"
+        });
+
+        // 单次响应处理
+        if (this.stream == false) {
+            let data = await response.json()
+            //console.log(data)
+            let lastContent = data?.choices?.[0]?.message?.content
+            return lastContent
+        }
+
+        // 流式处理
+        if (!response.body) {
+            throw new Error("No response body in stream mode");
+        }
+
+        const reader = response.body.getReader();
+        const decoder = new TextDecoder("utf-8");
+        let buffer = '';
+        let accumulatedContent = "";
+
+        try {
+            while (true) {
+                const { done, value } = await reader.read();
+                if (done) break;
+
+                buffer += decoder.decode(value, { stream: true });
+                
+                // 处理缓冲区中的所有完整行
+                let lastNewline = buffer.lastIndexOf('\n');
+                if (lastNewline === -1) continue; // 等待更多数据
+
+                const completeLines = buffer.substring(0, lastNewline + 1);
+                buffer = buffer.substring(lastNewline + 1);
+
+                const lines = completeLines.split('\n').filter(line => line.trim() !== '');
+
+                for (const line of lines) {
+                    if (!line.startsWith('data:')) continue;
+                    if (line.includes('[DONE]')) continue;
+
+                    try {
+                        const jsonStr = line.substring(5).trim();
+                        if (!jsonStr) continue;
+
+                        // 尝试解析JSON
+                        let data;
+                        try {
+                            data = JSON.parse(jsonStr);
+                        } catch (e) {
+                            // 如果解析失败,可能是数据不完整,暂时保留到缓冲区
+                            buffer = line + '\n' + buffer;
+                            continue;
+                        }
+
+                        const content = data?.choices?.[0]?.delta?.content || '';
+                        if (content) {
+                            accumulatedContent += content;
+                            if (onMessage) {
+                                onMessage(accumulatedContent);
+                            }
+                        }
+                    } catch (e) {
+                        console.error("Error processing stream data:", e);
+                    }
+                }
+            }
+
+            // 处理缓冲区中剩余的数据
+            if (buffer.trim()) {
+                try {
+                    const line = buffer.trim();
+                    if (line.startsWith('data:') && !line.includes('[DONE]')) {
+                        const jsonStr = line.substring(5).trim();
+                        if (jsonStr) {
+                            const data = JSON.parse(jsonStr);
+                            const content = data?.choices?.[0]?.delta?.content || '';
+                            if (content) {
+                                accumulatedContent += content;
+                                if (onMessage) {
+                                    onMessage(accumulatedContent);
+                                }
+                            }
+                        }
+                    }
+                } catch (e) {
+                    console.error("Error processing remaining buffer:", e);
+                }
+            }
+        } finally {
+            reader.releaseLock();
+        }
+
+        return accumulatedContent;
+    }
+}
+
+/**
+ * 使用AI生成符合指定结构的JSON数据
+ * @param prompt 任务要求的整体提示词
+ * @param jsonSchema 期望的JSON结构描述(用于提示词)
+ * @param example 可选的JSON示例(帮助AI理解格式)
+ * @param onMessage 实时生成内容回调
+ * @returns 解析后的JSON对象
+ */
+export async function completionJSON(
+    prompt: string,
+    jsonSchema: string,
+    example: object | null = null,
+    onMessage?: (content: string) => void
+): Promise<any> {
+    // 1. 构建提示词
+    const JsonResultParsePrompt = `请严格按照以下要求生成JSON数据:
+- 数据结构要求:${jsonSchema}
+- 只能返回一个完整的JSON对象 确保JSON格式正确
+- 注意返回的JSON格式每个KEY都有""包裹和Value之间都有:`;
+
+    // 2. 初始化消息列表
+    const messages: TestMessage[] = [
+        {
+            role: "user",
+            content: prompt + JsonResultParsePrompt
+        }
+    ];
+
+    // 3. 创建TestCompletion实例
+    const completion = new TestCompletion(messages);
+    
+    // 4. 存储累积内容和JSON对象
+    let fullContent = "";
+    let jsonObject: any = null;
+    
+    // 5. 发送请求并处理流式响应
+    const result = await completion.sendMessage(null, (content) => {
+        fullContent = content;
+        if (onMessage) {
+            onMessage(content);
+        }
+    });
+    
+    // 6. 最终JSON提取
+    try {
+        jsonObject = extractJSON(fullContent);
+        if (!jsonObject) {
+            throw new Error("无法从响应中提取有效的JSON");
+        }
+    } catch (e) {
+        console.error("JSON解析失败:", e);
+        console.log("原始内容:", fullContent);
+        throw new Error("生成的响应不符合JSON格式");
+    }
+    
+    return jsonObject;
+}
+
+export function extractJSON(str: string) {
+    // 首先尝试直接解析整个字符串
+    try {
+        const obj = JSON.parse(str);
+        return obj;
+    } catch (e) {
+        // 如果直接解析失败,尝试提取JSON部分
+    }
+
+    // 提取JSON的备用方法
+    let firstBrace = str.indexOf('{');
+    let lastBrace = str.lastIndexOf('}');
+    
+    if (firstBrace === -1 || lastBrace === -1 || lastBrace <= firstBrace) {
+        return null;
+    }
+    
+    try {
+        const jsonStr = str.substring(firstBrace, lastBrace + 1);
+        return JSON.parse(jsonStr);
+    } catch (e) {
+        return null;
+    }
+}

+ 39 - 28
ai-assisant/src/modules/crm/mobile/page-crm-data/page-crm-data.ts

@@ -674,37 +674,49 @@ getTypeIcon(type: string): string {
     }, 800); // 模拟网络延迟
   }
 
-  // 生成AI响应
-  private generateAIResponse(question: string): string {
-    const model = this.currentAIModel.title;
-    const knowledge = this.knowledgeBase[model] || {};
-    
-    // 问题分类和回答
-    if (question.includes('功能') || question.includes('做什么') || question.includes('用途')) {
-      return `关于<strong>${model}</strong>的功能:<br><br>${knowledge.description || 
-        '该模型主要用于销售场景的数据分析和决策支持,具体功能包括预测分析、趋势识别和客户行为分析等。'}`;
-    }
-    else if (question.includes('数据') || question.includes('训练') || question.includes('样本')) {
-      return `关于<strong>${model}</strong>的训练数据:<br><br>${knowledge.dataUsed || 
-        '模型使用了公司多年的销售数据、客户互动记录和市场调研数据,经过严格的数据清洗和特征工程处理。'}`;
-    }
-    else if (question.includes('准确') || question.includes('性能') || question.includes('指标')) {
-      return `关于<strong>${model}</strong>的性能指标:<br><br>${knowledge.accuracy || 
-        '模型在测试集上表现良好,准确率和召回率均超过80%,具体指标会根据不同业务场景有所变化。'}`;
-    }
-    else if (question.includes('使用') || question.includes('怎么用') || question.includes('集成')) {
-      return `关于<strong>${model}</strong>的使用方法:<br><br>${knowledge.usage || 
-        '可以通过我们的API接口集成到现有系统中,也支持导出分析报告。详细技术文档请联系技术团队获取。'}`;
+// 修改后的代码
+private generateAIResponse(question: string): string {
+  const model = this.currentAIModel.title;
+  const knowledge = this.knowledgeBase[model] || {};
+
+  // 定义关键词映射
+  const keywordMap: { [key: string]: string } = {
+    '功能': 'description',
+    '做什么': 'description',
+    '用途': 'description',
+    '训练数据': 'dataUsed',
+    '数据': 'dataUsed',
+    '样本': 'dataUsed',
+    '准确率': 'accuracy',
+    '性能指标': 'accuracy',
+    '准确': 'accuracy',
+    '性能': 'accuracy',
+    '使用方法': 'usage',
+    '怎么用': 'usage',
+    '集成': 'usage'
+  };
+
+  // 查找匹配的关键词
+  let answerKey = '';
+  for (const keyword in keywordMap) {
+    if (question.includes(keyword)) {
+      answerKey = keywordMap[keyword];
+      break;
     }
-    else {
-      return `感谢您的提问。关于"${question}",${model}可以提供专业分析:<br><br>
-      1. 该问题涉及${model}的${question.includes('客户') ? '客户分析' : '预测'}功能<br>
-      2. 我们建议${question.includes('销售') ? '结合历史销售数据' : '参考模型输出'}进行决策<br>
-      3. 如需更详细分析,可以提供具体数据样本<br><br>
-      您是否需要更具体的信息?`;
+  }
+
+  // 如果找到匹配的关键词,返回相应的答案
+  if (answerKey) {
+    const answer = knowledge[answerKey];
+    if (answer) {
+      return `关于<strong>${model}</strong>的${Object.keys(keywordMap).find(key => keywordMap[key] === answerKey)}:<br><br>${answer}`;
     }
   }
 
+  // 如果没有匹配的关键词,返回默认回答
+  return `很抱歉,我不太理解您的问题。您可以换一种表达方式,或者问我一些关于模型功能、训练数据、准确率或使用方法的问题。`;
+}
+
   // 选择建议问题
   selectSuggestion(suggestion: string) {
     this.userMessage = suggestion;
@@ -750,4 +762,3 @@ getTypeIcon(type: string): string {
 }
 
 
-  //

+ 204 - 139
ai-assisant/src/modules/crm/mobile/page-crm-decision/page-crm-decision.html

@@ -1,162 +1,227 @@
 <div class="container">
-    <!-- 顶部导航 -->
-    <header class="app-header">
-      <div class="header-title">
-        <i class="fas fa-robot"></i>
-        <h1>话术决策助手</h1>
+  <!-- 顶部导航 -->
+  <header class="app-header">
+    <div class="header-title">
+      <i class="fas fa-robot"></i>
+      <h1>九州宴会 - 销售话术决策助手</h1>
+    </div>
+  </header>
+  
+  <!-- 场景选择 -->
+  <section class="scene-selector">
+    <h2 class="input-title">
+      <i class="fas fa-dharmachakra"></i>
+      选择分析场景
+    </h2>
+    <div class="scene-options">
+      <div class="scene-option" [class.active]="selectedScene === 'first-contact'" 
+          (click)="selectScene('first-contact')">
+        <i class="fas fa-handshake"></i>
+        <h3>首次接触</h3>
+        <p>分析初次沟通策略</p>
       </div>
-    </header>
-    
-    <!-- 场景选择 -->
-    <section class="scene-selector">
-      <h2 class="input-title">
-        <i class="fas fa-dharmachakra"></i>
-        选择对话场景
-      </h2>
-      <div class="scene-options">
-        <div class="scene-option" [class.active]="selectedScene === 'first-contact'" 
-            (click)="selectScene('first-contact')">
-          <i class="fas fa-handshake"></i>
-          <h3>首次接触</h3>
-          <p>初次沟通建立信任</p>
-        </div>
-        <div class="scene-option" [class.active]="selectedScene === 'deep-talk'" 
-            (click)="selectScene('deep-talk')">
-          <i class="fas fa-comments"></i>
-          <h3>深度交流</h3>
-          <p>深入挖掘客户需求</p>
-        </div>
+      <div class="scene-option" [class.active]="selectedScene === 'deep-talk'" 
+          (click)="selectScene('deep-talk')">
+        <i class="fas fa-comments"></i>
+        <h3>深度交流</h3>
+        <p>优化后续跟进策略</p>
       </div>
-    </section>
-    
-    <!-- 客户对话分析 -->
-    <section class="input-area">
-      <h2 class="chat-title">
+    </div>
+  </section>
+  
+  <!-- 文件上传区域 -->
+  <section class="upload-area">
+    <div class="upload-container">
+      <label for="file-upload" class="upload-label">
+        <i class="fas fa-cloud-upload-alt"></i>
+        <span>上传聊天记录文件</span>
+      </label>
+      <input type="file" id="file-upload" #fileInput (change)="onFileSelected($event)" accept=".json" class="hidden">
+      <p class="upload-hint">支持JSON格式的聊天记录文件</p>
+    </div>
+  </section>
+  
+  <!-- 客户对话分析 -->
+  <section class="input-area">
+    <h2 class="chat-title">
+      <i class="fas fa-comments"></i>
+      客户对话分析
+      <span class="current-role-indicator" [ngClass]="{'customer-indicator': currentRole === 'customer', 'assistant-indicator': currentRole === 'assistant'}">
+        当前角色: {{currentRole === 'customer' ? '客户' : '销售顾问'}}
+      </span>
+    </h2>
+    <div class="chat-area" #chatContainer>
+      <div *ngIf="!messages.length" class="empty-chat">
         <i class="fas fa-comments"></i>
-        客户对话分析
-      </h2>
-      <div class="chat-area" #chatContainer>
-        <div *ngFor="let msg of messages" class="chat-message" 
-            [class.customer-message]="msg.role === 'customer'"
-            [class.assistant-message]="msg.role === 'assistant'">
-          <div class="message-header" [class.customer-header]="msg.role === 'customer'"
-               [class.assistant-header]="msg.role === 'assistant'">
-            <div class="icon">
-              <i *ngIf="msg.role === 'customer'" class="fas fa-user"></i>
-              <i *ngIf="msg.role === 'assistant'" class="fas fa-headset"></i>
-            </div>
-            <div>{{msg.role === 'customer' ? '客户' : '销售顾问'}}</div>
+        <p>请上传聊天记录或开始新的对话</p>
+      </div>
+      <div *ngFor="let msg of messages" class="chat-message" 
+          [class.customer-message]="msg.role === 'customer'"
+          [class.assistant-message]="msg.role === 'assistant'">
+        <div class="message-header" [class.customer-header]="msg.role === 'customer'"
+             [class.assistant-header]="msg.role === 'assistant'">
+          <div class="icon">
+            <i *ngIf="msg.role === 'customer'" class="fas fa-user"></i>
+            <i *ngIf="msg.role === 'assistant'" class="fas fa-headset"></i>
           </div>
-          <div class="message-content">{{msg.content}}</div>
-          <div class="message-time">{{msg.time}}</div>
+          <div>{{msg.role === 'customer' ? '客户' : '销售顾问'}}</div>
         </div>
+        <div class="message-content">{{msg.content}}</div>
+        <div class="message-time">{{msg.time}}</div>
       </div>
-      
-      <!-- 客户标签编辑区 -->
-      <div class="tags-area">
-        <div class="tags-header">
-          <div class="tags-title">客户标签</div>
-          <div class="add-tag" (click)="addTag()">
-            <i class="fas fa-plus"></i>
-            <span>添加标签</span>
-          </div>
+    </div>
+    
+    <!-- 客户标签编辑区 -->
+    <div class="tags-area">
+      <div class="tags-header">
+        <div class="tags-title">客户标签</div>
+        <div class="add-tag" (click)="addTag()">
+          <i class="fas fa-plus"></i>
+          <span>添加标签</span>
         </div>
-        <div class="input-tags">
-          <div *ngFor="let tag of tags" class="tag" [style.background]="tag.background" 
-              [style.color]="tag.color">
-            <i [class]="tag.icon"></i>
-            <span>{{tag.text}}</span>
-            <span class="delete" (click)="removeTag($event, tag)">
-              <i class="fas fa-times"></i>
-            </span>
-          </div>
+      </div>
+      <div class="input-tags">
+        <div *ngFor="let tag of tags" class="tag" [style.background]="tag.background" 
+            [style.color]="tag.color">
+          <i [class]="tag.icon"></i>
+          <span>{{tag.text}}</span>
+          <span class="delete" (click)="removeTag($event, tag)">
+            <i class="fas fa-times"></i>
+          </span>
         </div>
       </div>
-    </section>
+    </div>
+  </section>
+  
+  <!-- 输入工作区 -->
+  <section class="input-area">
+    <h2 class="input-title">
+      <i class="fas fa-comment-dots"></i>
+      添加对话内容
+    </h2>
+    
+    <div class="role-selector">
+      <div class="role-btn" [class.active]="currentRole === 'customer'" 
+          (click)="setRole('customer')">客户消息</div>
+      <div class="role-btn" [class.active]="currentRole === 'assistant'" 
+          (click)="setRole('assistant')">销售回复</div>
+    </div>
+    
+    <div class="input-field">
+      <textarea class="text-input" placeholder="输入对话内容..." [(ngModel)]="newMessage" 
+                (keydown)="onTextareaKeydown($event)" [disabled]="isProcessing"></textarea>
+      <div *ngIf="isProcessing" class="processing-indicator">
+        <i class="fas fa-circle-notch fa-spin"></i>
+        <span>AI分析中...</span>
+      </div>
+    </div>
     
-    <!-- 输入工作区 -->
-    <section class="input-area">
-      <h2 class="input-title">
-        <i class="fas fa-comment-dots"></i>
-        输入对话内容
-      </h2>
-      
-      <div class="role-selector">
-        <div class="role-btn" [class.active]="currentRole === 'customer'" 
-            (click)="setRole('customer')">客户消息</div>
-        <div class="role-btn" [class.active]="currentRole === 'assistant'" 
-            (click)="setRole('assistant')">销售回复</div>
+    <div class="action-buttons">
+      <div class="action-btn save-btn" (click)="sendMessage()" [class.disabled]="isProcessing" [style.pointer-events]="isProcessing ? 'none' : 'auto'">
+        <i class="fa fa-paper-plane"></i>
+        <span>发送消息</span>
       </div>
-      
-      <div class="input-field">
-        <textarea class="text-input" placeholder="输入对话内容..." [(ngModel)]="newMessage" (keydown)="handleKeydown($event)"> <!-- 修改这里 -->
-        </textarea>
-            
+      <div class="action-btn history-btn" (click)="viewHistory()">
+        <i class="fa fa-history"></i>
+        <span>历史记录</span>
       </div>
-      
-      <div class="action-buttons">
-        <div class="action-btn save-btn" (click)="sendMessage()">
-          <i class="fas fa-paper-plane"></i>
-          <span>发送消息</span>
-        </div>
+      <div class="action-btn" (click)="generateAIResponse()" [class.disabled]="isProcessing" [style.pointer-events]="isProcessing ? 'none' : 'auto'">
+        <i class="fa fa-robot"></i>
+        <span>AI回答</span>
       </div>
-    </section>
-    
-    <!-- 策略卡片组 -->
-    <section class="strategy-area">
-      <h2 class="strategy-title">
-        <i class="fas fa-cards"></i>
-        {{sceneTitle}} - 推荐应对策略
-      </h2>
-      <div class="cards-container">
-        <div *ngFor="let strategy of strategies; let i = index" 
-            class="strategy-card"
-            [class.card-aggressive]="strategy.type === 'aggressive'"
-            [class.card-neutral]="strategy.type === 'neutral'"
-            [class.card-conservative]="strategy.type === 'conservative'"
-            [style.transform]="getCardTransform(i)"
-            [style.zIndex]="getCardZIndex(i)"
-            (click)="activateCard(i)">
-          <div class="card-header">
-            <div class="card-title">{{strategy.title}}</div>
-            <div class="card-icon" [style.background]="strategy.iconBg" 
-                [style.color]="strategy.iconColor">
-              <i [class]="strategy.icon"></i>
-            </div>
+    </div>    
+  </section>
+  
+  <!-- 策略卡片组 -->
+  <section class="strategy-area">
+    <h2 class="strategy-title">
+      <i class="fas fa-cards"></i>
+      {{sceneTitle}} - 推荐应对策略
+    </h2>
+    <div class="cards-container">
+      {{aicontent}}
+      <div *ngFor="let strategy of strategies; let i = index" 
+          class="strategy-card"
+          [class.card-aggressive]="strategy.type === 'aggressive'"
+          [class.card-neutral]="strategy.type === 'neutral'"
+          [class.card-conservative]="strategy.type === 'conservative'"
+          [style.transform]="getCardTransform(i)"
+          [style.zIndex]="getCardZIndex(i)"
+          (click)="activateCard(i)">
+        <div class="card-header">
+          <div class="card-title">{{strategy.title}}</div>
+          <div class="card-icon" [style.background]="strategy.iconBg" 
+              [style.color]="strategy.iconColor">
+            <i [class]="strategy.icon"></i>
           </div>
-          <div class="card-content">
-            <p>{{strategy.description}}</p>
-            <div class="card-highlight">
-              {{strategy.highlight}}
-            </div>
-            <p><strong>适用场景:</strong>{{strategy.applicable}}</p>
-            <p><strong>优势:</strong>{{strategy.advantage}}</p>
-          </div>
-          <div class="card-footer">
-            <div class="card-hint">成功率: {{strategy.successRate}}</div>
-            <div class="card-hint"><i class="fas fa-clock"></i> 推荐指数: {{strategy.rating}}</div>
+        </div>
+        <div class="card-content">
+          <p>{{strategy.description}}</p>
+          <div class="card-highlight">
+            {{strategy.highlight}}
           </div>
+          <p><strong>适用场景:</strong>{{strategy.applicable}}</p>
+          <p><strong>优势:</strong>{{strategy.advantage}}</p>
+        </div>
+        <div class="card-footer">
+          <div class="card-hint">成功率: {{strategy.successRate}}</div>
+          <div class="card-hint"><i class="fas fa-clock"></i> 推荐指数: {{strategy.rating}}</div>
         </div>
       </div>
-      
-      <!-- 卡片导航点 -->
-      <div class="card-navigation">
-        <div *ngFor="let strategy of strategies; let i = index" 
-            class="card-nav-btn" 
-            [class.active]="activeCardIndex === i"
-            (click)="activateCard(i)"></div>
+    </div>
+    
+    <!-- 卡片导航点 -->
+    <div class="card-navigation">
+      <div *ngFor="let strategy of strategies; let i = index" 
+          class="card-nav-btn" 
+          [class.active]="activeCardIndex === i"
+          (click)="activateCard(i)"></div>
+    </div>
+  </section>
+  
+  <!-- 操作按钮 -->
+  <div class="action-buttons fixed-buttons">
+    <div class="action-btn save-btn" (click)="saveRecord()">
+      <i class="fas fa-save"></i>
+      <span>保存记录</span>
+    </div>
+    <div class="action-btn history-btn" (click)="viewHistory()">
+      <i class="fas fa-history"></i>
+      <span>查看历史</span>
+    </div>
+  </div>
+  
+  <!-- 历史记录模态框 -->
+  <div *ngIf="showHistoryModal" class="history-modal">
+    <div class="modal-content">
+      <div class="modal-header">
+        <h3>历史记录</h3>
+        <button class="close-btn" (click)="showHistoryModal = false">
+          <i class="fas fa-times"></i>
+        </button>
       </div>
-      
-      <!-- 操作按钮 -->
-      <div class="action-buttons">
-        <div class="action-btn save-btn" (click)="saveRecord()">
-          <i class="fas fa-save"></i>
-          <span>保存记录</span>
+      <div class="modal-body">
+        <div *ngIf="!chatRecords.length" class="empty-history">
+          <i class="fas fa-folder-open"></i>
+          <p>暂无保存的记录</p>
         </div>
-        <div class="action-btn history-btn" (click)="viewHistory()">
-          <i class="fas fa-history"></i>
-          <span>查看历史</span>
+        <div *ngFor="let record of chatRecords" class="history-item" (click)="selectRecord(record)">
+          <div class="history-header">
+            <div class="history-title">
+              <span>{{record.scene === 'first-contact' ? '首次接触' : '深度交流'}}</span>
+              <span class="history-timestamp">{{formatDate(record.timestamp)}}</span>
+            </div>
+            <button class="delete-btn" (click)="deleteRecord(record); $event.stopPropagation()">
+              <i class="fas fa-trash"></i>
+            </button>
+          </div>
+          <div class="history-preview">
+            <p class="history-summary">
+              {{record.messages.length}}条消息 | 策略: {{record.strategies[activeCardIndex].title}}
+            </p>
+          </div>
         </div>
       </div>
-    </section>
+    </div>
   </div>
+</div>  

+ 880 - 524
ai-assisant/src/modules/crm/mobile/page-crm-decision/page-crm-decision.scss

@@ -1,551 +1,907 @@
 :host {
-    display: block;
-    background-color: #f0f5ff;
-    color: var(--dark);
-    line-height: 1.6;
-    overflow-x: hidden;
-    background: linear-gradient(135deg, #e6f0ff 0%, #f5f9ff 100%);
-    min-height: 100vh;
-    padding: 20px 0;
-  }
-  
-  
-  /* 变量定义 */
-  :root {
-    --primary: #64a0ff;
-    --primary-light: rgba(100, 160, 255, 0.1);
-    --dark: #0a192f;
-    --dark-light: #112240;
-    --gray: #8892b0;
-    --light-gray: #f8f9fa;
-    --white: #ffffff;
-    --blue: #64a0ff;
-    --blue-light: rgba(100, 160, 255, 0.15);
-    --yellow: #ffd43b;
-    --orange: #ff922b;
-    --purple: #9c36b5;
-    --shadow: 0 4px 20px rgba(0, 0, 0, 0.05);
-    --shadow-primary: 0 4px 20px rgba(100, 160, 255, 0.2);
-  }
-  
-  /* 基础样式 */
-  * {
-    margin: 0;
-    padding: 0;
-    box-sizing: border-box;
-    font-family: 'Segoe UI', 'SF Pro Display', -apple-system, BlinkMacSystemFont, sans-serif;
-  }
-  
-  /* 容器样式 */
+  display: block;
+  background-color: #f0f5ff;
+  color: var(--dark);
+  line-height: 1.6;
+  overflow-x: hidden;
+  background: linear-gradient(135deg, #e6f0ff 0%, #f5f9ff 100%);
+  min-height: 100vh;
+  padding: 20px 0;
+}
+
+
+/* 变量定义 */
+:root {
+  --primary: #64a0ff;
+  --primary-light: rgba(100, 160, 255, 0.1);
+  --dark: #0a192f;
+  --dark-light: #112240;
+  --gray: #8892b0;
+  --light-gray: #f8f9fa;
+  --white: #ffffff;
+  --blue: #64a0ff;
+  --blue-light: rgba(100, 160, 255, 0.15);
+  --yellow: #ffd43b;
+  --orange: #ff922b;
+  --purple: #9c36b5;
+  --shadow: 0 4px 20px rgba(0, 0, 0, 0.05);
+  --shadow-primary: 0 4px 20px rgba(100, 160, 255, 0.2);
+}
+
+/* 基础样式 */
+* {
+  margin: 0;
+  padding: 0;
+  box-sizing: border-box;
+  font-family: 'Segoe UI', 'SF Pro Display', -apple-system, BlinkMacSystemFont, sans-serif;
+}
+
+/* 容器样式 */
+.container {
+  max-width: 480px;
+  margin: 0 auto;
+  padding: 0 16px 100px;
+  position: relative;
+}
+
+/* 顶部导航 */
+.app-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  padding: 15px 0;
+  margin-bottom: 15px;
+  border-bottom: 1px solid rgba(10, 25, 47, 0.05);
+}
+
+.header-title {
+  font-size: 22px;
+  font-weight: 700;
+  color: var(--dark);
+  display: flex;
+  align-items: center;
+  gap: 10px;
+}
+
+.header-title i {
+  color: var(--primary);
+  background: var(--primary-light);
+  width: 36px;
+  height: 36px;
+  border-radius: 10px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+
+/* 场景选择 */
+.scene-selector {
+  background: var(--white);
+  border-radius: 20px;
+  padding: 20px;
+  margin-bottom: 20px;
+  box-shadow: var(--shadow);
+  position: relative;
+  overflow: hidden;
+  border: 1px solid rgba(100, 160, 255, 0.2);
+}
+
+.scene-options {
+  display: flex;
+  gap: 15px;
+  margin-top: 10px;
+}
+
+.scene-option {
+  flex: 1;
+  padding: 15px;
+  border-radius: 15px;
+  background: var(--light-gray);
+  text-align: center;
+  cursor: pointer;
+  transition: all 0.3s ease;
+  border: 1px solid rgba(100, 160, 255, 0.1);
+}
+
+.scene-option.active {
+  background: var(--primary-light);
+  border-color: var(--primary);
+  box-shadow: var(--shadow-primary);
+}
+
+.scene-option i {
+  font-size: 28px;
+  margin-bottom: 10px;
+  color: var(--primary);
+}
+
+.scene-option h3 {
+  font-size: 16px;
+  margin-bottom: 5px;
+}
+
+.scene-option p {
+  font-size: 13px;
+  color: var(--gray);
+}
+
+/* 文件上传区域 */
+.upload-area {
+  margin-bottom: 20px;
+}
+
+.upload-container {
+  background: var(--white);
+  border-radius: 20px;
+  padding: 30px 20px;
+  text-align: center;
+  box-shadow: var(--shadow);
+  border: 1px solid rgba(100, 160, 255, 0.2);
+  cursor: pointer;
+  transition: all 0.3s ease;
+}
+
+.upload-container:hover {
+  transform: translateY(-2px);
+  box-shadow: var(--shadow-primary);
+}
+
+.upload-label {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  gap: 10px;
+  cursor: pointer;
+}
+
+.upload-label i {
+  font-size: 36px;
+  color: var(--primary);
+  background: var(--primary-light);
+  width: 60px;
+  height: 60px;
+  border-radius: 15px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+
+.upload-label span {
+  font-size: 16px;
+  font-weight: 600;
+  color: var(--dark);
+}
+
+.upload-hint {
+  font-size: 14px;
+  color: var(--gray);
+  margin-top: 10px;
+}
+
+.hidden {
+  display: none;
+}
+
+/* 对话聊天区 */
+.chat-area {
+  background: var(--white);
+  border-radius: 20px;
+  padding: 20px;
+  margin-bottom: 20px;
+  box-shadow: var(--shadow);
+  height: 350px;
+  overflow-y: auto;
+  display: flex;
+  flex-direction: column;
+  gap: 15px;
+  border: 1px solid rgba(100, 160, 255, 0.2);
+}
+
+.chat-title, .input-title, .strategy-title {
+  font-size: 18px;
+  font-weight: 600;
+  margin-bottom: 15px;
+  color: var(--dark);
+  display: flex;
+  align-items: center;
+  gap: 10px;
+  padding-bottom: 10px;
+  border-bottom: 1px solid rgba(100, 160, 255, 0.1);
+}
+
+.current-role-indicator {
+  margin-left: auto;
+  font-size: 14px;
+  padding: 5px 10px;
+  border-radius: 8px;
+  font-weight: 500;
+}
+
+.customer-indicator {
+  background: var(--blue-light);
+  color: var(--blue);
+}
+
+.assistant-indicator {
+  background: var(--primary-light);
+  color: var(--primary);
+}
+
+.chat-title i, .input-title i, .strategy-title i {
+  color: var(--primary);
+}
+
+.chat-message {
+  max-width: 85%;
+  padding: 14px 18px;
+  border-radius: 18px;
+  position: relative;
+  animation: fadeIn 0.3s ease;
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
+}
+
+@keyframes fadeIn {
+  from { opacity: 0; transform: translateY(10px); }
+  to { opacity: 1; transform: translateY(0); }
+}
+
+.customer-message {
+  background: linear-gradient(135deg, #e6f0ff, #ebf2ff);
+  align-self: flex-start;
+  border-bottom-left-radius: 5px;
+  border-left: 3px solid var(--blue);
+}
+
+.assistant-message {
+  background: linear-gradient(135deg, #e6f7ff, #ebf9ff);
+  border: 1px solid rgba(100, 160, 255, 0.3);
+  align-self: flex-end;
+  border-bottom-right-radius: 5px;
+  border-right: 3px solid var(--primary);
+}
+
+.message-header {
+  display: flex;
+  align-items: center;
+  gap: 8px;
+  margin-bottom: 8px;
+  font-weight: 600;
+}
+
+.customer-header .icon {
+  background: var(--blue-light);
+  color: var(--blue);
+}
+
+.assistant-header .icon {
+  background: var(--primary-light);
+  color: var(--primary);
+}
+
+.message-header .icon {
+  width: 28px;
+  height: 28px;
+  border-radius: 8px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  font-size: 14px;
+}
+
+.message-content {
+  line-height: 1.5;
+}
+
+.message-time {
+  font-size: 11px;
+  color: var(--gray);
+  margin-top: 8px;
+  text-align: right;
+}
+
+.empty-chat {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  height: 100%;
+  color: var(--gray);
+}
+
+.empty-chat i {
+  font-size: 48px;
+  margin-bottom: 15px;
+  opacity: 0.5;
+}
+
+/* 客户标签编辑区 */
+.tags-area {
+  background: var(--light-gray);
+  border-radius: 15px;
+  padding: 15px;
+  margin-top: 10px;
+  border: 1px solid rgba(100, 160, 255, 0.1);
+}
+
+.tags-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 10px;
+}
+
+.tags-title {
+  font-size: 14px;
+  font-weight: 600;
+  color: var(--dark);
+}
+
+.input-tags {
+  display: flex;
+  gap: 8px;
+  flex-wrap: wrap;
+}
+
+.tag {
+  background: var(--primary-light);
+  color: var(--primary);
+  padding: 6px 12px 6px 8px;
+  border-radius: 20px;
+  font-size: 13px;
+  display: flex;
+  align-items: center;
+  gap: 5px;
+  cursor: pointer;
+  transition: all 0.2s ease;
+  border: 1px solid rgba(100, 160, 255, 0.2);
+}
+
+.tag:hover {
+  background: rgba(100, 160, 255, 0.3);
+}
+
+.tag .delete {
+  font-size: 12px;
+  margin-left: 5px;
+  opacity: 0.7;
+}
+
+.tag .delete:hover {
+  opacity: 1;
+}
+
+.add-tag {
+  background: rgba(136, 146, 176, 0.1);
+  color: var(--gray);
+  padding: 6px 12px;
+  border-radius: 20px;
+  font-size: 13px;
+  display: flex;
+  align-items: center;
+  gap: 5px;
+  cursor: pointer;
+  transition: all 0.2s ease;
+}
+
+.add-tag:hover {
+  background: rgba(136, 146, 176, 0.2);
+}
+
+/* 输入工作区 */
+.input-area {
+  background: var(--white);
+  border-radius: 20px;
+  padding: 20px;
+  margin-bottom: 20px;
+  box-shadow: var(--shadow);
+  border: 1px solid rgba(100, 160, 255, 0.2);
+}
+
+.role-selector {
+  display: flex;
+  background: var(--light-gray);
+  border-radius: 12px;
+  padding: 4px;
+  margin-bottom: 15px;
+}
+
+.role-btn {
+  flex: 1;
+  padding: 10px;
+  text-align: center;
+  border-radius: 10px;
+  cursor: pointer;
+  transition: all 0.3s ease;
+  font-weight: 500;
+}
+
+.role-btn.active {
+  background: var(--white);
+  box-shadow: var(--shadow);
+  color: var(--primary);
+}
+
+.input-field {
+  position: relative;
+}
+
+.text-input {
+  width: 100%;
+  height: 120px;
+  padding: 15px;
+  border-radius: 15px;
+  border: 1px solid rgba(136, 146, 176, 0.2);
+  background: var(--light-gray);
+  resize: none;
+  font-size: 16px;
+  color: var(--dark);
+  transition: all 0.3s ease;
+}
+
+.text-input:focus {
+  outline: none;
+  border-color: var(--primary);
+  box-shadow: 0 0 0 2px rgba(100, 160, 255, 0.2);
+}
+
+.processing-indicator {
+  position: absolute;
+  bottom: 15px;
+  right: 15px;
+  display: flex;
+  align-items: center;
+  gap: 5px;
+  color: var(--primary);
+  background: rgba(255, 255, 255, 0.8);
+  padding: 5px 10px;
+  border-radius: 8px;
+  backdrop-filter: blur(5px);
+  font-size: 14px;
+}
+
+.processing-indicator i {
+  animation: spin 1s linear infinite;
+}
+
+@keyframes spin {
+  from { transform: rotate(0deg); }
+  to { transform: rotate(360deg); }
+}
+
+/* 策略卡片组 */
+.strategy-area {
+  margin-bottom: 25px;
+}
+
+.cards-container {
+  position: relative;
+  height: 320px;
+  perspective: 1000px;
+}
+
+.strategy-card {
+  position: absolute;
+  width: 100%;
+  height: 280px;
+  border-radius: 20px;
+  padding: 25px;
+  box-shadow: var(--shadow);
+  display: flex;
+  flex-direction: column;
+  transition: transform 0.6s cubic-bezier(0.34, 1.56, 0.64, 1), 
+              opacity 0.4s ease, 
+              box-shadow 0.3s ease;
+  backface-visibility: hidden;
+  cursor: pointer;
+  border: 1px solid rgba(100, 160, 255, 0.2);
+}
+
+.card-aggressive {
+  background: linear-gradient(135deg, #eef6ff, #f0f8ff);
+  border-top: 4px solid var(--blue);
+  z-index: 30;
+}
+
+.card-neutral {
+  background: linear-gradient(135deg, #fff9e6, #fffbf0);
+  border-top: 4px solid var(--yellow);
+  transform: translateY(20px) scale(0.95);
+  z-index: 20;
+}
+
+.card-conservative {
+  background: linear-gradient(135deg, #f9f0ff, #fdf5ff);
+  border-top: 4px solid var(--purple);
+  transform: translateY(40px) scale(0.9);
+  z-index: 10;
+}
+
+.card-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 20px;
+}
+
+.card-title {
+  font-size: 18px;
+  font-weight: 700;
+}
+
+.card-icon {
+  width: 40px;
+  height: 40px;
+  border-radius: 12px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  font-size: 18px;
+}
+
+.card-content {
+  flex: 1;
+  overflow-y: auto;
+  padding-right: 10px;
+}
+
+.card-content p {
+  margin-bottom: 15px;
+  line-height: 1.7;
+  color: #333;
+}
+
+.card-highlight {
+  background: rgba(10, 25, 47, 0.05);
+  padding: 12px 15px;
+  border-radius: 12px;
+  border-left: 3px solid var(--primary);
+  margin: 15px 0;
+  font-weight: 500;
+  line-height: 1.6;
+}
+
+.card-footer {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-top: 15px;
+  padding-top: 15px;
+  border-top: 1px solid rgba(136, 146, 176, 0.1);
+}
+
+.card-hint {
+  font-size: 14px;
+  color: var(--gray);
+}
+
+/* 卡片导航点 */
+.card-navigation {
+  display: flex;
+  justify-content: center;
+  gap: 10px;
+  margin-top: 15px;
+}
+
+.card-nav-btn {
+  width: 12px;
+  height: 12px;
+  border-radius: 50%;
+  background: var(--light-gray);
+  cursor: pointer;
+  transition: all 0.2s ease;
+}
+
+.card-nav-btn.active {
+  background: var(--primary);
+  transform: scale(1.2);
+}
+
+/* 操作按钮 */
+.action-buttons {
+  display: flex;
+  gap: 15px;
+}
+
+.action-btn {
+  flex: 1;
+  height: 50px;
+  border-radius: 15px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  gap: 10px;
+  font-weight: 600;
+  cursor: pointer;
+  transition: all 0.3s ease;
+}
+
+.save-btn {
+  background: var(--primary);
+  color: var(--dark);
+  border: none;
+  box-shadow: 0 4px 15px rgba(100, 160, 255, 0.3);
+}
+
+.save-btn:hover {
+  transform: translateY(-2px);
+  box-shadow: 0 6px 20px rgba(100, 160, 255, 0.4);
+}
+
+.history-btn {
+  background: var(--white);
+  color: var(--dark);
+  border: 2px solid var(--primary);
+  box-shadow: 0 4px 15px rgba(100, 160, 255, 0.1);
+}
+
+.history-btn:hover {
+  transform: translateY(-2px);
+  box-shadow: 0 6px 20px rgba(100, 160, 255, 0.2);
+}
+
+.fixed-buttons {
+  position: fixed;
+  bottom: 20px;
+  left: 50%;
+  transform: translateX(-50%);
+  width: calc(100% - 40px);
+  max-width: 480px;
+  padding: 0 16px;
+  z-index: 50;
+}
+
+/* 角色指示器 */
+.current-role-indicator {
+  margin-left: auto;
+  font-size: 14px;
+  padding: 5px 10px;
+  border-radius: 8px;
+  font-weight: 500;
+  transition: all 0.3s ease;
+}
+
+.customer-indicator {
+  background: var(--blue-light);
+  color: var(--blue);
+}
+
+.assistant-indicator {
+  background: var(--primary-light);
+  color: var(--primary);
+}
+
+/* 策略卡片动画增强 */
+.strategy-card:hover {
+  box-shadow: 0 10px 30px rgba(100, 160, 255, 0.15);
+}
+
+.strategy-card:active {
+  transform: translateY(5px) scale(0.98);
+}
+
+/* 标签悬停效果 */
+.tag {
+  position: relative;
+  overflow: hidden;
+}
+
+.tag::before {
+  content: '';
+  position: absolute;
+  top: 0;
+  left: -100%;
+  width: 100%;
+  height: 100%;
+  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.3), transparent);
+  transition: all 0.6s ease;
+}
+
+.tag:hover::before {
+  left: 100%;
+}
+
+/* 输入区域增强 */
+.text-input {
+  position: relative;
+}
+
+.text-input:focus {
+  border-color: var(--primary);
+  box-shadow: 0 0 0 3px rgba(100, 160, 255, 0.3);
+}
+
+/* 历史记录模态框 */
+.history-modal {
+  position: fixed;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  background: rgba(0, 0, 0, 0.5);
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  z-index: 100;
+  backdrop-filter: blur(5px);
+}
+
+.modal-content {
+  background: var(--white);
+  border-radius: 20px;
+  width: 90%;
+  max-width: 450px;
+  max-height: 80vh;
+  overflow-y: auto;
+  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
+  animation: fadeInModal 0.3s ease;
+}
+
+@keyframes fadeInModal {
+  from { opacity: 0; transform: scale(0.95); }
+  to { opacity: 1; transform: scale(1); }
+}
+
+.modal-header {
+  padding: 20px;
+  border-bottom: 1px solid rgba(10, 25, 47, 0.05);
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  position: sticky;
+  top: 0;
+  background: var(--white);
+  z-index: 10;
+}
+
+.modal-header h3 {
+  font-size: 18px;
+  font-weight: 600;
+}
+
+.close-btn {
+  width: 30px;
+  height: 30px;
+  border-radius: 50%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  cursor: pointer;
+  transition: all 0.2s ease;
+}
+
+.close-btn:hover {
+  background: var(--light-gray);
+}
+
+.modal-body {
+  padding: 20px;
+}
+
+.history-item {
+  background: var(--light-gray);
+  border-radius: 15px;
+  padding: 15px;
+  margin-bottom: 15px;
+  cursor: pointer;
+  transition: all 0.3s ease;
+  border: 1px solid rgba(100, 160, 255, 0.1);
+}
+
+.history-item:hover {
+  background: var(--primary-light);
+  transform: translateY(-2px);
+}
+
+.history-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 10px;
+}
+
+.history-title {
+  display: flex;
+  flex-direction: column;
+}
+
+.history-title span:first-child {
+  font-weight: 600;
+}
+
+.history-timestamp {
+  font-size: 12px;
+  color: var(--gray);
+}
+
+.delete-btn {
+  color: var(--gray);
+  cursor: pointer;
+  transition: all 0.2s ease;
+}
+
+.delete-btn:hover {
+  color: #ff4d4f;
+}
+
+.history-summary {
+  font-size: 14px;
+  color: var(--gray);
+}
+
+.empty-history {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  padding: 50px 0;
+  color: var(--gray);
+}
+
+.empty-history i {
+  font-size: 48px;
+  margin-bottom: 15px;
+  opacity: 0.5;
+}
+
+/* 响应式调整 */
+@media (max-width: 380px) {
   .container {
-    max-width: 480px;
-    margin: 0 auto;
-    padding: 0 16px 30px;
-    position: relative;
-  }
-  
-  /* 顶部导航 */
-  .app-header {
-    display: flex;
-    justify-content: space-between;
-    align-items: center;
-    padding: 15px 0;
-    margin-bottom: 15px;
-    border-bottom: 1px solid rgba(10, 25, 47, 0.05);
-  }
-  
-  .header-title {
-    font-size: 22px;
-    font-weight: 700;
-    color: var(--dark);
-    display: flex;
-    align-items: center;
-    gap: 10px;
-  }
-  
-  .header-title i {
-    color: var(--primary);
-    background: var(--primary-light);
-    width: 36px;
-    height: 36px;
-    border-radius: 10px;
-    display: flex;
-    align-items: center;
-    justify-content: center;
-  }
-  
-  /* 场景选择 */
-  .scene-selector {
-    background: var(--white);
-    border-radius: 20px;
-    padding: 20px;
-    margin-bottom: 20px;
-    box-shadow: var(--shadow);
-    position: relative;
-    overflow: hidden;
-    border: 1px solid rgba(100, 160, 255, 0.2);
+    padding: 0 12px 100px;
   }
   
   .scene-options {
-    display: flex;
-    gap: 15px;
-    margin-top: 10px;
-  }
-  
-  .scene-option {
-    flex: 1;
-    padding: 15px;
-    border-radius: 15px;
-    background: var(--light-gray);
-    text-align: center;
-    cursor: pointer;
-    transition: all 0.3s ease;
-    border: 1px solid rgba(100, 160, 255, 0.1);
-  }
-  
-  .scene-option.active {
-    background: var(--primary-light);
-    border-color: var(--primary);
-    box-shadow: var(--shadow-primary);
-  }
-  
-  .scene-option i {
-    font-size: 28px;
-    margin-bottom: 10px;
-    color: var(--primary);
-  }
-  
-  .scene-option h3 {
-    font-size: 16px;
-    margin-bottom: 5px;
-  }
-  
-  .scene-option p {
-    font-size: 13px;
-    color: var(--gray);
-  }
-  
-  /* 对话聊天区 */
-  .chat-area {
-    background: var(--white);
-    border-radius: 20px;
-    padding: 20px;
-    margin-bottom: 20px;
-    box-shadow: var(--shadow);
-    height: 350px;
-    overflow-y: auto;
-    display: flex;
     flex-direction: column;
-    gap: 15px;
-    border: 1px solid rgba(100, 160, 255, 0.2);
-  }
-  
-  .chat-title, .input-title, .strategy-title {
-    font-size: 18px;
-    font-weight: 600;
-    margin-bottom: 15px;
-    color: var(--dark);
-    display: flex;
-    align-items: center;
-    gap: 10px;
-    padding-bottom: 10px;
-    border-bottom: 1px solid rgba(100, 160, 255, 0.1);
-  }
-  
-  .chat-title i, .input-title i, .strategy-title i {
-    color: var(--primary);
-  }
-  
-  .chat-message {
-    max-width: 85%;
-    padding: 14px 18px;
-    border-radius: 18px;
-    position: relative;
-    animation: fadeIn 0.3s ease;
-    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
-  }
-  
-  @keyframes fadeIn {
-    from { opacity: 0; transform: translateY(10px); }
-    to { opacity: 1; transform: translateY(0); }
-  }
-  
-  .customer-message {
-    background: linear-gradient(135deg, #e6f0ff, #ebf2ff);
-    align-self: flex-start;
-    border-bottom-left-radius: 5px;
-    border-left: 3px solid var(--blue);
-  }
-  
-  .assistant-message {
-    background: linear-gradient(135deg, #e6f7ff, #ebf9ff);
-    border: 1px solid rgba(100, 160, 255, 0.3);
-    align-self: flex-end;
-    border-bottom-right-radius: 5px;
-    border-right: 3px solid var(--primary);
-  }
-  
-  .message-header {
-    display: flex;
-    align-items: center;
-    gap: 8px;
-    margin-bottom: 8px;
-    font-weight: 600;
-  }
-  
-  .customer-header .icon {
-    background: var(--blue-light);
-    color: var(--blue);
-  }
-  
-  .assistant-header .icon {
-    background: var(--primary-light);
-    color: var(--primary);
-  }
-  
-  .message-header .icon {
-    width: 28px;
-    height: 28px;
-    border-radius: 8px;
-    display: flex;
-    align-items: center;
-    justify-content: center;
-    font-size: 14px;
-  }
-  
-  .message-content {
-    line-height: 1.5;
-  }
-  
-  .message-time {
-    font-size: 11px;
-    color: var(--gray);
-    margin-top: 8px;
-    text-align: right;
-  }
-  
-  /* 客户标签编辑区 */
-  .tags-area {
-    background: var(--light-gray);
-    border-radius: 15px;
-    padding: 15px;
-    margin-top: 10px;
-    border: 1px solid rgba(100, 160, 255, 0.1);
-  }
-  
-  .tags-header {
-    display: flex;
-    justify-content: space-between;
-    align-items: center;
-    margin-bottom: 10px;
-  }
-  
-  .tags-title {
-    font-size: 14px;
-    font-weight: 600;
-    color: var(--dark);
-  }
-  
-  .input-tags {
-    display: flex;
-    gap: 8px;
-    flex-wrap: wrap;
-  }
-  
-  .tag {
-    background: var(--primary-light);
-    color: var(--primary);
-    padding: 6px 12px 6px 8px;
-    border-radius: 20px;
-    font-size: 13px;
-    display: flex;
-    align-items: center;
-    gap: 5px;
-    cursor: pointer;
-    transition: all 0.2s ease;
-    border: 1px solid rgba(100, 160, 255, 0.2);
-  }
-  
-  .tag:hover {
-    background: rgba(100, 160, 255, 0.3);
-  }
-  
-  .tag .delete {
-    font-size: 12px;
-    margin-left: 5px;
-    opacity: 0.7;
-  }
-  
-  .tag .delete:hover {
-    opacity: 1;
-  }
-  
-  .add-tag {
-    background: rgba(136, 146, 176, 0.1);
-    color: var(--gray);
-    padding: 6px 12px;
-    border-radius: 20px;
-    font-size: 13px;
-    display: flex;
-    align-items: center;
-    gap: 5px;
-    cursor: pointer;
-    transition: all 0.2s ease;
-  }
-  
-  .add-tag:hover {
-    background: rgba(136, 146, 176, 0.2);
-  }
-  
-  /* 输入工作区 */
-  .input-area {
-    background: var(--white);
-    border-radius: 20px;
-    padding: 20px;
-    margin-bottom: 20px;
-    box-shadow: var(--shadow);
-    border: 1px solid rgba(100, 160, 255, 0.2);
-  }
-  
-  .role-selector {
-    display: flex;
-    background: var(--light-gray);
-    border-radius: 12px;
-    padding: 4px;
-    margin-bottom: 15px;
-  }
-  
-  .role-btn {
-    flex: 1;
-    padding: 10px;
-    text-align: center;
-    border-radius: 10px;
-    cursor: pointer;
-    transition: all 0.3s ease;
-    font-weight: 500;
-  }
-  
-  .role-btn.active {
-    background: var(--white);
-    box-shadow: var(--shadow);
-    color: var(--primary);
-  }
-  
-  .input-field {
-    position: relative;
-  }
-  
-  .text-input {
-    width: 100%;
-    height: 120px;
-    padding: 15px;
-    border-radius: 15px;
-    border: 1px solid rgba(136, 146, 176, 0.2);
-    background: var(--light-gray);
-    resize: none;
-    font-size: 16px;
-    color: var(--dark);
-    transition: all 0.3s ease;
-  }
-  
-  .text-input:focus {
-    outline: none;
-    border-color: var(--primary);
-    box-shadow: 0 0 0 2px rgba(100, 160, 255, 0.2);
-  }
-  
-  /* 策略卡片组 */
-  .strategy-area {
-    margin-bottom: 25px;
   }
   
   .cards-container {
-    position: relative;
-    height: 320px;
-    perspective: 1000px;
+    height: 300px;
   }
   
   .strategy-card {
-    position: absolute;
-    width: 100%;
-    height: 280px;
-    border-radius: 20px;
-    padding: 25px;
-    box-shadow: var(--shadow);
-    display: flex;
-    flex-direction: column;
-    transition: transform 0.6s cubic-bezier(0.34, 1.56, 0.64, 1), 
-                opacity 0.4s ease, 
-                box-shadow 0.3s ease;
-    backface-visibility: hidden;
-    cursor: pointer;
-    border: 1px solid rgba(100, 160, 255, 0.2);
-  }
-  
-  .card-aggressive {
-    background: linear-gradient(135deg, #eef6ff, #f0f8ff);
-    border-top: 4px solid var(--blue);
-    z-index: 30;
-  }
-  
-  .card-neutral {
-    background: linear-gradient(135deg, #fff9e6, #fffbf0);
-    border-top: 4px solid var(--yellow);
-    transform: translateY(20px) scale(0.95);
-    z-index: 20;
-  }
-  
-  .card-conservative {
-    background: linear-gradient(135deg, #f9f0ff, #fdf5ff);
-    border-top: 4px solid var(--purple);
-    transform: translateY(40px) scale(0.9);
-    z-index: 10;
-  }
-  
-  .card-header {
-    display: flex;
-    justify-content: space-between;
-    align-items: center;
-    margin-bottom: 20px;
-  }
-  
-  .card-title {
-    font-size: 18px;
-    font-weight: 700;
-  }
-  
-  .card-icon {
-    width: 40px;
-    height: 40px;
-    border-radius: 12px;
-    display: flex;
-    align-items: center;
-    justify-content: center;
-    font-size: 18px;
-  }
-  
-  .card-content {
-    flex: 1;
-    overflow-y: auto;
-    padding-right: 10px;
-  }
-  
-  .card-content p {
-    margin-bottom: 15px;
-    line-height: 1.7;
-    color: #333;
-  }
-  
-  .card-highlight {
-    background: rgba(10, 25, 47, 0.05);
-    padding: 12px 15px;
-    border-radius: 12px;
-    border-left: 3px solid var(--primary);
-    margin: 15px 0;
-    font-weight: 500;
-    line-height: 1.6;
+    height: 260px;
+    padding: 20px;
   }
   
-  .card-footer {
-    display: flex;
-    justify-content: space-between;
-    align-items: center;
-    margin-top: 15px;
-    padding-top: 15px;
-    border-top: 1px solid rgba(136, 146, 176, 0.1);
+  .chat-area {
+    height: 320px;
   }
   
-  .card-hint {
-    font-size: 14px;
-    color: var(--gray);
+  .current-role-indicator {
+    display: none;
   }
   
-  /* 卡片导航点 */
-  .card-navigation {
-    display: flex;
-    justify-content: center;
-    gap: 10px;
-    margin-top: 15px;
+  .chat-title, .input-title, .strategy-title {
+    font-size: 16px;
   }
   
-  .card-nav-btn {
-    width: 12px;
-    height: 12px;
-    border-radius: 50%;
-    background: var(--light-gray);
-    cursor: pointer;
-    transition: all 0.2s ease;
+  .modal-content {
+    width: 95%;
   }
-  
-  .card-nav-btn.active {
+}    
+/* 添加禁用状态样式 */
+.save-btn.disabled {
+  opacity: 0.6;
+  cursor: not-allowed;
+  box-shadow: none;
+}
+/* 保持AI回答按钮与发送消息按钮风格一致 */
+.action-buttons .action-btn {
+  &:nth-child(3) {
     background: var(--primary);
-    transform: scale(1.2);
-  }
-  
-  /* 操作按钮 */
-  .action-buttons {
-    display: flex;
-    gap: 15px;
-    margin-top: 25px;
-  }
-  
-  .action-btn {
-    flex: 1;
-    height: 50px;
-    border-radius: 15px;
-    display: flex;
-    align-items: center;
-    justify-content: center;
-    gap: 10px;
-    font-weight: 600;
-    cursor: pointer;
-    transition: all 0.3s ease;
-  }
-  
-  .save-btn {
-    background: var(--primary);
-    color: var(--dark);
-    border: none;
-    box-shadow: 0 4px 15px rgba(100, 160, 255, 0.3);
-  }
-  
-  .save-btn:hover {
-    transform: translateY(-2px);
-    box-shadow: 0 6px 20px rgba(100, 160, 255, 0.4);
-  }
-  
-  .history-btn {
-    background: var(--white);
     color: var(--dark);
-    border: 2px solid var(--primary);
-    box-shadow: 0 4px 15px rgba(100, 160, 255, 0.1);
-  }
-  
-  .history-btn:hover {
-    transform: translateY(-2px);
-    box-shadow: 0 6px 20px rgba(100, 160, 255, 0.2);
-  }
-  
-  /* 响应式调整 */
-  @media (max-width: 380px) {
-    .container {
-      padding: 0 12px 25px;
-    }
-    
-    .scene-options {
-      flex-direction: column;
-    }
-    
-    .cards-container {
-      height: 300px;
-    }
+    transition: all 0.3s ease;
+    border: 1px solid rgba(100, 160, 255, 0.2);
     
-    .strategy-card {
-      height: 260px;
-      padding: 20px;
+    &:hover {
+      transform: translateY(-2px);
+      box-shadow: 0 6px 20px rgba(100, 160, 255, 0.4);
     }
     
-    .chat-area {
-      height: 320px;
+    &.disabled {
+      opacity: 0.6;
+      cursor: not-allowed;
+      box-shadow: none;
+      transform: none;
     }
-  }
+  }
+}    

+ 568 - 66
ai-assisant/src/modules/crm/mobile/page-crm-decision/page-crm-decision.ts

@@ -7,10 +7,35 @@ import {
 } from '@angular/core';
 import { FormsModule } from '@angular/forms';
 import { CommonModule } from '@angular/common';
+import { TestCompletion, TestMessage,extractJSON} from '../../../../lib/cloud/comletion';
 
 type SceneType = 'first-contact' | 'deep-talk';
 type MessageRole = 'customer' | 'assistant';
 
+interface StrategyAnalysis {
+  aggressive: {
+    title: string;
+    highlight: string;
+    applicable: string;
+    advantage: string;
+    successRate: string;
+  };
+  neutral: {
+    title: string;
+    highlight: string;
+    applicable: string;
+    advantage: string;
+    successRate: string;
+  };
+  conservative: {
+    title: string;
+    highlight: string;
+    applicable: string;
+    advantage: string;
+    successRate: string;
+  };
+}
+
 interface Message {
   role: MessageRole;
   content: string;
@@ -36,6 +61,16 @@ interface Strategy {
   advantage: string;
   successRate: string;
   rating: string;
+  isDefault: boolean; // 新增:标记是否为默认策略
+}
+
+interface ChatRecord {
+  id: string;
+  scene: SceneType;
+  messages: Message[];
+  tags: Tag[];
+  strategies: Strategy[];
+  timestamp: number;
 }
 
 @Component({
@@ -47,19 +82,34 @@ interface Strategy {
 })
 export class PageCrmDecision implements OnInit, AfterViewChecked {
   @ViewChild('chatContainer') chatContainer!: ElementRef;
-  
-  
+  @ViewChild('fileInput') fileInput!: ElementRef;
+
+  private aiAssistant!: TestCompletion;
+  private scenePrompts = {
+    'first-contact': `你是一名专业的酒店销售顾问,正在与一位潜在客户进行首次接触。
+客户可能对酒店服务、房型、价格等方面有疑问。你的任务是:
+1. 建立良好的沟通氛围
+2. 了解客户基本需求
+3. 介绍酒店核心优势
+4. 避免过于强硬的推销语气
+5. 为后续沟通留下空间`,
+    'deep-talk': `你是一名经验丰富的酒店销售顾问,正在与一位对酒店有一定兴趣的客户进行深度交流。
+客户可能已经了解了基本信息,正在比较选择或寻求更详细的信息。你的任务是:
+1. 深入挖掘客户具体需求和关注点
+2. 提供针对性的解决方案和建议
+3. 有效处理客户提出的异议和问题
+4. 展示酒店差异化竞争优势
+5. 推动客户向决策阶段发展`
+  };
+
+  openHistory() {
+    this.showHistoryModal = true;
+  }
+
   selectedScene: SceneType = 'first-contact';
   sceneTitle = '首次接触';
   
-  messages: Message[] = [
-    { role: 'customer', content: '您好,我对贵酒店的豪华套房很感兴趣,但觉得价格偏高,能否提供一些优惠?', time: '10:25 AM' },
-    { role: 'assistant', content: '感谢您对我们豪华套房的关注!我们的套房拥有全景海景和私人管家服务,目前预订可享8折优惠,您觉得如何?', time: '10:26 AM' },
-    { role: 'customer', content: '8折还是有点高,我看到其他酒店类似房型有更优惠的价格。', time: '10:27 AM' },
-    { role: 'assistant', content: '我理解您的考虑。我们的套房包含双人早餐和免费SPA体验,这些增值服务能让您的住宿体验更加完美。', time: '10:28 AM' },
-    { role: 'customer', content: '这个听起来不错,我考虑一下。', time: '10:29 AM' },
-    { role: 'assistant', content: '好的,如果您今天预订,我们还可以额外赠送您一次下午茶体验。', time: '10:30 AM' }
-  ];
+  messages: Message[] = [];
   
   tags: Tag[] = [
     { text: 'VIP客户', icon: 'fas fa-user', background: 'rgba(100, 160, 255, 0.15)', color: '#64a0ff' },
@@ -77,12 +127,13 @@ export class PageCrmDecision implements OnInit, AfterViewChecked {
       icon: 'fas fa-bolt',
       iconBg: 'var(--blue-light)',
       iconColor: 'var(--blue)',
-      description: '直接强调酒店独特价值,限量优惠制造紧迫感:',
+      description: '直接强调酒店独特价值,制造紧迫感:',
       highlight: '',
       applicable: '客户表现出明确兴趣,决策周期短',
       advantage: '快速成交,提升单笔订单价值',
       successRate: '68%',
-      rating: '⭐⭐⭐⭐'
+      rating: '⭐⭐⭐⭐',
+      isDefault: true
     },
     {
       type: 'neutral',
@@ -95,7 +146,8 @@ export class PageCrmDecision implements OnInit, AfterViewChecked {
       applicable: '客户在多个选项间犹豫',
       advantage: '平衡双方利益,建立长期关系',
       successRate: '82%',
-      rating: '⭐⭐⭐⭐⭐'
+      rating: '⭐⭐⭐⭐⭐',
+      isDefault: true
     },
     {
       type: 'conservative',
@@ -108,11 +160,15 @@ export class PageCrmDecision implements OnInit, AfterViewChecked {
       applicable: '客户犹豫不决,决策周期长',
       advantage: '降低客户风险感知,提高转化率',
       successRate: '75%',
-      rating: '⭐⭐⭐⭐'
+      rating: '⭐⭐⭐⭐',
+      isDefault: true
     }
   ];
   
   activeCardIndex = 0;
+  isProcessing = false;
+  showHistoryModal = false;
+  chatRecords: ChatRecord[] = [];
   
   private strategiesContent: Record<SceneType, { aggressive: string; neutral: string; conservative: string }> = {
     "first-contact": {
@@ -128,7 +184,8 @@ export class PageCrmDecision implements OnInit, AfterViewChecked {
   };
   
   ngOnInit() {
-    this.updateStrategies();
+    this.loadRecords();
+    this.selectScene('first-contact');
   }
   
   ngAfterViewChecked() {
@@ -137,18 +194,14 @@ export class PageCrmDecision implements OnInit, AfterViewChecked {
   
   scrollToBottom(): void {
     try {
-      this.chatContainer.nativeElement.scrollTop = this.chatContainer.nativeElement.scrollHeight;
+      if (this.chatContainer) {
+        this.chatContainer.nativeElement.scrollTop = this.chatContainer.nativeElement.scrollHeight;
+      }
     } catch (err) {
       console.error(err);
     }
   }
   
-  handleKeydown(event: KeyboardEvent): void {
-    if (event.key === 'Enter' && !event.shiftKey) {
-      event.preventDefault();
-      this.sendMessage();
-    }
-  }
   onTextareaKeydown(event: KeyboardEvent): void {
     if (event.key === 'Enter' && !event.shiftKey) {
       event.preventDefault();
@@ -156,17 +209,83 @@ export class PageCrmDecision implements OnInit, AfterViewChecked {
     }
   }
   
-  updateStrategies(): void {
+  // 只在初始化时设置默认策略
+  initializeDefaultStrategies(): void {
     const sceneStrategies = this.strategiesContent[this.selectedScene];
-    this.strategies[0].highlight = sceneStrategies.aggressive;
-    this.strategies[1].highlight = sceneStrategies.neutral;
-    this.strategies[2].highlight = sceneStrategies.conservative;
+    
+    this.strategies[0] = {
+      ...this.strategies[0],
+      highlight: sceneStrategies.aggressive,
+      isDefault: true
+    };
+    
+    this.strategies[1] = {
+      ...this.strategies[1],
+      highlight: sceneStrategies.neutral,
+      isDefault: true
+    };
+    
+    this.strategies[2] = {
+      ...this.strategies[2],
+      highlight: sceneStrategies.conservative,
+      isDefault: true
+    };
   }
   
   selectScene(scene: SceneType): void {
     this.selectedScene = scene;
     this.sceneTitle = scene === 'first-contact' ? '首次接触' : '深度交流';
-    this.updateStrategies();
+    
+    // 初始化AI助手
+    this.messages = [];
+    const systemPrompt = this.scenePrompts[scene];
+    this.aiAssistant = new TestCompletion([
+      { role: "system", content: systemPrompt },
+      { role: "assistant", content: scene === 'first-contact' ? 
+        "您好!我是九州宴会的销售顾问,很高兴认识您。请问您今天想了解哪方面的信息呢?" : 
+        "感谢您继续深入交流。基于我们之前的沟通,我整理了几个可能适合您的方案,您想先了解哪个方面?"
+      }
+    ]);
+    
+    // 添加初始问候语
+    this.addMessage('assistant', scene === 'first-contact' ? 
+      "您好!我是九州宴会的销售顾问,很高兴认识您。请问您今天想了解哪方面的信息呢?" : 
+      "感谢您继续深入交流。基于我们之前的沟通,我整理了几个可能适合您的方案,您想先了解哪个方面?"
+    );
+    
+    // 重置并初始化策略
+    this.resetStrategies();
+    this.initializeDefaultStrategies();
+  }
+  
+  // 重置策略内容(切换场景时调用)
+  private resetStrategies() {
+    this.strategies = [
+      {
+        ...this.strategies[0],
+        highlight: "",
+        applicable: "客户表现出明确兴趣,决策周期短",
+        advantage: "快速成交,提升单笔订单价值",
+        successRate: "68%",
+        isDefault: true
+      },
+      {
+        ...this.strategies[1],
+        highlight: "",
+        applicable: "客户在多个选项间犹豫",
+        advantage: "平衡双方利益,建立长期关系",
+        successRate: "82%",
+        isDefault: true
+      },
+      {
+        ...this.strategies[2],
+        highlight: "",
+        applicable: "客户犹豫不决,决策周期长",
+        advantage: "降低客户风险感知,提高转化率",
+        successRate: "75%",
+        isDefault: true
+      }
+    ];
   }
   
   setRole(role: MessageRole): void {
@@ -188,47 +307,154 @@ export class PageCrmDecision implements OnInit, AfterViewChecked {
     this.tags = this.tags.filter(t => t !== tag);
   }
   
-  sendMessage(): void {
-    if (this.newMessage.trim()) {
-      const now = new Date();
-      const timeString = `${now.getHours()}:${now.getMinutes().toString().padStart(2, '0')}`;
+  async sendMessage() {
+    if (!this.newMessage.trim() || !this.selectedScene) return;
+    
+    this.isProcessing = true;
+    
+    // 添加用户消息
+    const now = new Date();
+    this.addMessage(this.currentRole, this.newMessage.trim());
+    this.newMessage = '';
+    
+    // 如果是客户消息,更新策略分析
+    if (this.currentRole === 'customer') {
+      try {
+        // 显示加载状态
+        this.strategies.forEach(strategy => {
+          strategy.highlight = "分析中...";
+          strategy.applicable = "生成最佳策略...";
+          strategy.successRate = "--";
+          strategy.isDefault = false;
+        });
+        
+        // 更新策略分析(基于最新客户消息)
+        await this.updateStrategiesBasedOnMessage(this.messages[this.messages.length-1].content);
+      } catch (error) {
+        console.error('更新策略失败:', error);
+        // 失败时使用关键词匹配生成动态内容
+        const fallbackStrategies = this.generateDynamicStrategies(this.messages[this.messages.length-1].content);
+        this.updateStrategiesWithFallback(fallbackStrategies);
+      } finally {
+        this.isProcessing = false;
+      }
+    } else {
+      this.isProcessing = false;
+    }
+  }
+  
+  private addMessage(role: MessageRole, content: string): void {
+    const now = new Date();
+    this.messages.push({
+      role,
+      content,
+      time: `${now.getHours()}:${now.getMinutes().toString().padStart(2, '0')}`
+    });
+    this.scrollToBottom();
+  }
+  
+  // 根据客户消息动态更新策略卡片内容
+  private async updateStrategiesBasedOnMessage(customerMessage: string) {
+    try {
+      // 调用AI分析客户消息,生成策略建议
+      const analysis = await this.analyzeCustomerMessage(customerMessage);
       
-      this.messages.push({
-        role: this.currentRole,
-        content: this.newMessage.trim(),
-        time: timeString
-      });
+      // 更新策略卡片内容
+      this.strategies[0] = {
+        ...this.strategies[0],
+        highlight: analysis.aggressive.highlight,
+        applicable: analysis.aggressive.applicable,
+        advantage: analysis.aggressive.advantage,
+        successRate: analysis.aggressive.successRate,
+        isDefault: false
+      };
       
-      this.newMessage = '';
+      this.strategies[1] = {
+        ...this.strategies[1],
+        highlight: analysis.neutral.highlight,
+        applicable: analysis.neutral.applicable,
+        advantage: analysis.neutral.advantage,
+        successRate: analysis.neutral.successRate,
+        isDefault: false
+      };
       
-      if (this.currentRole === 'assistant') {
-        setTimeout(() => {
-          this.simulateCustomerReply(now);
-        }, 1500);
-      }
+      this.strategies[2] = {
+        ...this.strategies[2],
+        highlight: analysis.conservative.highlight,
+        applicable: analysis.conservative.applicable,
+        advantage: analysis.conservative.advantage,
+        successRate: analysis.conservative.successRate,
+        isDefault: false
+      };
+      
+      // 根据消息内容选择最合适的策略
+      this.selectBestStrategy(customerMessage);
+    } catch (error) {
+      console.error('更新策略失败:', error);
+      throw error; // 让调用者处理错误
     }
   }
   
-  simulateCustomerReply(prevTime: Date): void {
-    const replies = [
-      "这个优惠包含早餐吗?",
-      "我需要和家人商量一下",
-      "能否提供免费接机服务?",
-      "价格还是有点高,能再优惠些吗?",
-      "你们的取消政策是怎样的?",
-      "如果我今天预订,还有额外优惠吗?",
-      "套餐包含哪些服务?",
-      "有更经济的房型推荐吗?"
-    ];
+  // 使用备用策略更新(AI调用失败时)
+  private updateStrategiesWithFallback(strategies: ReturnType<typeof this.generateDynamicStrategies>) {
+    this.strategies[0] = {
+      ...this.strategies[0],
+      highlight: strategies.aggressive.highlight,
+      applicable: "客户表现出明确兴趣,决策周期短",
+      successRate: "68%",
+      isDefault: false
+    };
     
-    const randomReply = replies[Math.floor(Math.random() * replies.length)];
-    const time = new Date(prevTime.getTime() + 60000); // 加1分钟
+    this.strategies[1] = {
+      ...this.strategies[1],
+      highlight: strategies.neutral.highlight,
+      applicable: "客户在多个选项间犹豫",
+      successRate: "82%",
+      isDefault: false
+    };
     
-    this.messages.push({
-      role: 'customer',
-      content: randomReply,
-      time: `${time.getHours()}:${time.getMinutes().toString().padStart(2, '0')}`
-    });
+    this.strategies[2] = {
+      ...this.strategies[2],
+      highlight: strategies.conservative.highlight,
+      applicable: "客户犹豫不决,决策周期长",
+      successRate: "75%",
+      isDefault: false
+    };
+    
+    // 仍然选择最佳策略
+    this.selectBestStrategy(this.messages[this.messages.length-1].content);
+  }
+  
+  // 生成动态策略内容(关键词匹配)
+  private generateDynamicStrategies(customerMessage: string) {
+    // 关键词匹配,确保内容随客户消息变化
+    let focus = "酒店服务";
+    if (customerMessage.toLowerCase().includes("价格")) focus = "价格优势";
+    if (customerMessage.toLowerCase().includes("位置")) focus = "地理位置";
+    if (customerMessage.toLowerCase().includes("设施")) focus = "设施特色";
+    if (customerMessage.toLowerCase().includes("日期")) focus = "日期灵活性";
+    if (customerMessage.toLowerCase().includes("容量")) focus = "场地容量";
+    
+    return {
+      aggressive: {
+        highlight: `立即强调${focus},提供专属限时优惠`,
+        applicable: "客户表现出明确兴趣",
+        advantage: "快速促成交易",
+        successRate: "68%"
+      },
+      neutral: {
+        highlight: `提供${focus}的3种选择方案,满足不同需求`,
+        applicable: "客户需要比较选择",
+        advantage: "提高选择满意度",
+        successRate: "82%"
+      },
+      conservative: {
+        highlight: `详细说明${focus}的优势,提供案例参考`,
+        applicable: "客户需要更多决策信息",
+        advantage: "降低决策阻力",
+        successRate: "75%"
+      }
+    };
   }
   
   activateCard(index: number): void {
@@ -255,13 +481,289 @@ export class PageCrmDecision implements OnInit, AfterViewChecked {
     }
   }
   
-  saveRecord(): void {
-    console.log('记录已保存');
-    // 这里可以添加实际的保存逻辑
+  // 文件上传处理
+  onFileSelected(event: Event) {
+    const input = event.target as HTMLInputElement;
+    if (!input.files || input.files.length === 0) return;
+    
+    const file = input.files[0];
+    if (file.type !== 'application/json' && !file.name.endsWith('.json')) {
+      alert('请上传JSON格式的文件');
+      return;
+    }
+    
+    const reader = new FileReader();
+    reader.onload = (e: ProgressEvent<FileReader>) => {
+      try {
+        const content = e.target?.result as string;
+        const data = JSON.parse(content);
+        
+        if (Array.isArray(data) && data.every(item => item.role && item.content && item.time)) {
+          this.messages = data as Message[];
+          this.updateStrategiesBasedOnMessages();
+          this.fileInput.nativeElement.value = ''; // 清空文件选择
+        } else {
+          alert('文件格式不正确,请上传有效的聊天记录JSON文件');
+        }
+      } catch (error) {
+        console.error('文件解析错误:', error);
+        alert('解析文件时出错');
+      }
+    };
+    reader.readAsText(file);
+  }
+  
+  // 根据完整对话更新策略
+  private updateStrategiesBasedOnMessages() {
+    // 从对话中提取最后一条客户消息
+    const lastCustomerMessage = this.messages
+      .filter(msg => msg.role === 'customer')
+      .pop();
+      
+    if (lastCustomerMessage) {
+      this.updateStrategiesBasedOnMessage(lastCustomerMessage.content);
+    }
+  }
+  
+  // 保存记录
+  saveRecord() {
+    if (!this.messages.length) {
+      alert('没有可保存的对话记录');
+      return;
+    }
+    
+    const record: ChatRecord = {
+      id: Date.now().toString(),
+      scene: this.selectedScene,
+      messages: [...this.messages],
+      tags: [...this.tags],
+      strategies: [...this.strategies],
+      timestamp: Date.now()
+    };
+    
+    this.chatRecords.unshift(record); // 添加到开头
+    this.saveRecords();
+    alert('记录已保存');
+  }
+  
+  // 加载记录
+  loadRecords() {
+    const records = localStorage.getItem('chatRecords');
+    if (records) {
+      this.chatRecords = JSON.parse(records);
+    }
+  }
+  
+  // 保存记录到本地存储
+  saveRecords() {
+    localStorage.setItem('chatRecords', JSON.stringify(this.chatRecords));
+  }
+  
+  // 查看历史
+  viewHistory() {
+    this.showHistoryModal = true;
+  }
+  
+  // 选择历史记录
+  selectRecord(record: ChatRecord) {
+    this.selectedScene = record.scene;
+    this.sceneTitle = record.scene === 'first-contact' ? '首次接触' : '深度交流';
+    this.messages = [...record.messages];
+    this.tags = [...record.tags];
+    this.strategies = [...record.strategies];
+    
+    // 初始化AI助手
+    const systemPrompt = this.scenePrompts[record.scene];
+    this.aiAssistant = new TestCompletion([
+      { role: "system", content: systemPrompt },
+      ...record.messages.map(msg => ({ 
+        role: msg.role === 'customer' ? 'user' : msg.role, 
+        content: msg.content 
+      }))
+    ]);
+    
+    this.showHistoryModal = false;
+  }
+  
+  // 删除历史记录
+  deleteRecord(record: ChatRecord) {
+    if (confirm('确定要删除这条记录吗?')) {
+      this.chatRecords = this.chatRecords.filter(r => r.id !== record.id);
+      this.saveRecords();
+    }
   }
   
-  viewHistory(): void {
-    console.log('查看历史记录');
-    // 这里可以添加历史查看逻辑
+  // 格式化日期
+  formatDate(timestamp: number) {
+    const date = new Date(timestamp);
+    return `${date.getFullYear()}-${(date.getMonth()+1).toString().padStart(2, '0')}-${date.getDate().toString().padStart(2, '0')} ${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}`;
+  }
+  
+  // 导出对话记录为JSON文件
+  exportChat() {
+    if (!this.messages.length) {
+      alert('没有可导出的对话记录');
+      return;
+    }
+    
+    const jsonContent = JSON.stringify(this.messages, null, 2);
+    const blob = new Blob([jsonContent], { type: 'application/json' });
+    const url = URL.createObjectURL(blob);
+    
+    const a = document.createElement('a');
+    a.href = url;
+    a.download = `chat-record-${new Date().toISOString().slice(0,10)}.json`;
+    document.body.appendChild(a);
+    a.click();
+    document.body.removeChild(a);
+    URL.revokeObjectURL(url);
+  }
+  
+  // 生成AI回复
+  async generateAIResponse() {
+    if (!this.messages.length || this.messages[this.messages.length - 1].role === 'assistant') {
+      alert('请先发送客户消息');
+      return;
+    }
+    
+    this.isProcessing = true;
+    
+    try {
+      const aiReply = await this.aiAssistant.sendMessage(
+        [...this.messages.map(msg => ({ 
+          role: msg.role === 'customer' ? 'user' : msg.role, 
+          content: msg.content 
+        }))],
+        (partialContent) => {
+          const lastMsg = this.messages[this.messages.length - 1];
+          if (lastMsg.role === 'assistant') {
+            lastMsg.content = partialContent;
+          } else {
+            this.addMessage('assistant', partialContent);
+          }
+        }
+      );
+      
+      if (typeof aiReply === 'string') {
+        this.addMessage('assistant', aiReply);
+        // 基于最新客户消息更新策略
+        const lastCustomerMsg = this.messages
+          .filter(msg => msg.role === 'customer')
+          .pop();
+          
+        if (lastCustomerMsg) {
+          this.updateStrategiesBasedOnMessage(lastCustomerMsg.content);
+        }
+      }
+    } catch (error) {
+      console.error('AI交互错误:', error);
+      this.addMessage('assistant', '抱歉,处理您的请求时出现了问题。');
+    } finally {
+      this.isProcessing = false;
+    }
+  }
+  
+  // 调用AI分析客户消息并生成策略建议
+  private async analyzeCustomerMessage(customerMessage: string): Promise<StrategyAnalysis> {
+    // 构建提示词,引导AI生成三种策略
+    const prompt = `
+    客户消息: "${customerMessage}"
+    
+    请针对此客户消息,为酒店销售顾问生成三种应对策略:
+    1. 主动型策略: 直接、果断的销售方法
+    2. 平衡型策略: 中等强度的销售方法
+    3. 保守型策略: 温和、信息丰富的销售方法
+    
+    请以JSON格式返回,包含以下字段:
+    {
+      "aggressive": {
+        "title": "主动型策略标题",
+        "highlight": "核心建议",
+        "applicable": "适用场景",
+        "advantage": "主要优势",
+        "successRate": "成功率百分比"
+      },
+      "neutral": {
+        "title": "平衡型策略标题",
+        "highlight": "核心建议",
+        "applicable": "适用场景",
+        "advantage": "主要优势",
+        "successRate": "成功率百分比"
+      },
+      "conservative": {
+        "title": "保守型策略标题",
+        "highlight": "核心建议",
+        "applicable": "适用场景",
+        "advantage": "主要优势",
+        "successRate": "成功率百分比"
+      }
+    }
+    `;
+    
+    // 使用TestCompletion调用AI
+    const strategyAnalyzer = new TestCompletion([
+      { role: "system", content: "你是一位专业的酒店销售策略顾问,擅长根据客户消息生成针对性的销售策略。" },
+      { role: "user", content: prompt }
+    ]);
+    
+    // 获取AI响应
+    const response = await strategyAnalyzer.sendMessage([
+      { role: "user", content: prompt }
+    ],(content)=>{
+      this.aicontent = content
+    });
+    
+    // 解析JSON响应
+    try {
+      let json = extractJSON(response)
+      return json;
+    } catch (parseError) {
+      console.error('解析策略分析失败:', parseError);
+      // 返回默认策略
+      return this.getDefaultStrategies();
+    }
+  }
+  aicontent:string = ""
+  // 根据客户消息选择最合适的策略
+  private selectBestStrategy(customerMessage: string) {
+    const lowerCaseMsg = customerMessage.toLowerCase();
+    
+    if (lowerCaseMsg.includes('价格') || lowerCaseMsg.includes('优惠')) {
+      this.activeCardIndex = 1; // 平衡型策略通常最适合价格讨论
+    } else if (lowerCaseMsg.includes('兴趣') || lowerCaseMsg.includes('考虑')) {
+      this.activeCardIndex = 0; // 主动型策略适合有兴趣的客户
+    } else if (lowerCaseMsg.includes('犹豫') || lowerCaseMsg.includes('比较')) {
+      this.activeCardIndex = 2; // 保守型策略适合犹豫的客户
+    } else {
+      // 默认选择平衡型策略
+      this.activeCardIndex = 1;
+    }
+  }
+  
+  // 返回默认策略(当AI分析失败时使用)
+  private getDefaultStrategies(): StrategyAnalysis {
+    return {
+      aggressive: {
+        title: "主动型策略",
+        highlight: "直接介绍酒店特色和限时优惠",
+        applicable: "客户表现出明确兴趣,决策周期短",
+        advantage: "快速成交,提升单笔订单价值",
+        successRate: "68%"
+      },
+      neutral: {
+        title: "平衡型策略",
+        highlight: "提供套餐选择,引导客户了解不同房型",
+        applicable: "客户在多个选项间犹豫",
+        advantage: "平衡双方利益,建立长期关系",
+        successRate: "82%"
+      },
+      conservative: {
+        title: "保守型策略",
+        highlight: "邀请客户参观虚拟酒店,提供详细资料",
+        applicable: "客户犹豫不决,决策周期长",
+        advantage: "降低客户风险感知,提高转化率",
+        successRate: "75%"
+      }
+    };
   }
 }