|
@@ -1,560 +1,212 @@
|
|
|
-import { Component, ElementRef, ViewChild, AfterViewInit, inject } from '@angular/core';
|
|
|
+import { Component, OnInit, ViewChild, ElementRef } from '@angular/core';
|
|
|
import { CommonModule } from '@angular/common';
|
|
|
import { FormsModule } from '@angular/forms';
|
|
|
+import { TrainingReportsComponent } from './report-viewer.component';
|
|
|
import { TrainingService } from './training.service';
|
|
|
-import { CloudObject, CloudQuery } from '../../../../lib/ncloud';
|
|
|
+import { Report, Scene } from './report.interface';
|
|
|
|
|
|
@Component({
|
|
|
- selector: 'app-page-crm-training',
|
|
|
+ selector: 'page-crm-training',
|
|
|
standalone: true,
|
|
|
- imports: [CommonModule, FormsModule],
|
|
|
+ imports: [CommonModule, FormsModule, TrainingReportsComponent],
|
|
|
templateUrl: './page-crm-training.html',
|
|
|
styleUrls: ['./page-crm-training.scss']
|
|
|
})
|
|
|
-export class PageCrmTraining implements AfterViewInit {
|
|
|
- @ViewChild('conversationContainer') conversationContainer!: ElementRef;
|
|
|
- private trainingService = inject(TrainingService);
|
|
|
-
|
|
|
- // 状态控制
|
|
|
- isEditing = false;
|
|
|
- isRecording = false;
|
|
|
- showAddCustomerModal = false;
|
|
|
- showFullReportModal = false;
|
|
|
- showHistoryReportModal = false;
|
|
|
-
|
|
|
- // 表单数据
|
|
|
- userInput = '';
|
|
|
- newCustomerTypeName = '';
|
|
|
- newCustomerDifficulty = 1;
|
|
|
-
|
|
|
- // 评分数据
|
|
|
- currentScore = 0;
|
|
|
- scoreChange = 0;
|
|
|
+export class PageCrmTraining implements OnInit {
|
|
|
+ @ViewChild('chatMessagesContainer') chatMessagesContainer!: ElementRef;
|
|
|
+
|
|
|
+ // 场景数据
|
|
|
+ sceneLevels: { value: string; label: string }[] = [];
|
|
|
+ customerTypes: { value: string; label: string }[] = [];
|
|
|
+ allScenes: Scene[] = [];
|
|
|
+ filteredScenes: Scene[] = [];
|
|
|
+ selectedLevel: string = '';
|
|
|
+ selectedCustomerType: string = '';
|
|
|
+ selectedScene: Scene | null = null;
|
|
|
|
|
|
// 对话数据
|
|
|
- messages: Message[] = [];
|
|
|
- currentScenario: any = null;
|
|
|
- currentRecordId: string | null = null;
|
|
|
-
|
|
|
- // 客户类型数据
|
|
|
- customerTypes: CustomerType[] = [];
|
|
|
- difficultyTabs: DifficultyTab[] = [
|
|
|
- { label: '⭐ 初级', value: 1, active: true },
|
|
|
- { label: '⭐⭐ 中级', value: 2, active: false },
|
|
|
- { label: '⭐⭐⭐ 高级', value: 3, active: false },
|
|
|
- { label: '专家挑战', value: 4, active: false }
|
|
|
- ];
|
|
|
-
|
|
|
- // 技能数据
|
|
|
- skills: Skill[] = [
|
|
|
- { name: '反应力', value: 0, key: 'reactivity' },
|
|
|
- { name: '话术', value: 0, key: 'technology' },
|
|
|
- { name: '说服力', value: 0, key: 'persuasiveness' },
|
|
|
- { name: '专业度', value: 0, key: 'professionalism' }
|
|
|
- ];
|
|
|
-
|
|
|
- // 当前报告数据
|
|
|
- currentReport: Report = {
|
|
|
- date: new Date(),
|
|
|
- customerType: '',
|
|
|
- difficulty: '',
|
|
|
- summary: '',
|
|
|
- tags: [],
|
|
|
- evaluation: {
|
|
|
- intro: '',
|
|
|
- points: []
|
|
|
- },
|
|
|
- summaryMethods: {
|
|
|
- intro: '',
|
|
|
- methods: [],
|
|
|
- tags: []
|
|
|
- },
|
|
|
- score: 0
|
|
|
- };
|
|
|
-
|
|
|
- // 历史报告数据
|
|
|
- historyReports: Report[] = [];
|
|
|
+ chatMessages: Array<{
|
|
|
+ type: 'user' | 'bot';
|
|
|
+ content: string;
|
|
|
+ timestamp: Date;
|
|
|
+ }> = [];
|
|
|
+ chatInput: string = '';
|
|
|
+ currentRound: number = 1;
|
|
|
+ maxRounds: number = 5;
|
|
|
+ conversationScore: number | null = null;
|
|
|
|
|
|
- // 解决模板中 Math 报错
|
|
|
- Math = Math;
|
|
|
+ // 报告数据
|
|
|
+ generatedReports: Report[] = [];
|
|
|
+ selectedReport: Report | null = null;
|
|
|
|
|
|
- async ngAfterViewInit() {
|
|
|
- await this.loadTrainingScenarios();
|
|
|
- await this.loadTrainingHistory();
|
|
|
- this.scrollToBottom();
|
|
|
- }
|
|
|
+ constructor(private trainingService: TrainingService) {}
|
|
|
|
|
|
- /* 核心功能方法 */
|
|
|
- scrollToBottom(): void {
|
|
|
- try {
|
|
|
- setTimeout(() => {
|
|
|
- this.conversationContainer.nativeElement.scrollTop =
|
|
|
- this.conversationContainer.nativeElement.scrollHeight;
|
|
|
- }, 100);
|
|
|
- } catch(err) { }
|
|
|
+ ngOnInit(): void {
|
|
|
+ this.loadInitialData();
|
|
|
}
|
|
|
|
|
|
- // 智能返回方法
|
|
|
- goBack(): void {
|
|
|
- if (this.showFullReportModal) {
|
|
|
- this.closeFullReportModal();
|
|
|
- } else if (this.showHistoryReportModal) {
|
|
|
- this.closeHistoryReportModal();
|
|
|
- } else if (this.showAddCustomerModal) {
|
|
|
- this.closeAddCustomerModal();
|
|
|
- } else {
|
|
|
- console.log('返回上一页');
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- canGoBack(): boolean {
|
|
|
- return this.showFullReportModal ||
|
|
|
- this.showHistoryReportModal ||
|
|
|
- this.showAddCustomerModal;
|
|
|
- }
|
|
|
+ private loadInitialData(): void {
|
|
|
+ this.trainingService.getSceneLevels().subscribe(levels => {
|
|
|
+ this.sceneLevels = levels;
|
|
|
+ });
|
|
|
|
|
|
- /* 数据加载方法 */
|
|
|
- async loadTrainingScenarios() {
|
|
|
- try {
|
|
|
- const activeTab = this.difficultyTabs.find(t => t.active);
|
|
|
- const scenarios = await this.trainingService.getTrainingScenarios({
|
|
|
- difficulty: activeTab?.value
|
|
|
- });
|
|
|
-
|
|
|
- this.customerTypes = scenarios.map((s: any) => ({
|
|
|
- id: s.id,
|
|
|
- name: s.get('name'),
|
|
|
- active: false,
|
|
|
- icon: this.getRandomIcon(),
|
|
|
- difficulty: s.get('difficulty'),
|
|
|
- customerRole: s.get('customerRole')
|
|
|
- }));
|
|
|
-
|
|
|
- if (this.customerTypes.length > 0) {
|
|
|
- this.customerTypes[0].active = true;
|
|
|
- }
|
|
|
- } catch (error) {
|
|
|
- console.error('加载训练场景失败:', error);
|
|
|
- }
|
|
|
- }
|
|
|
+ this.trainingService.getCustomerTypes().subscribe(types => {
|
|
|
+ this.customerTypes = types;
|
|
|
+ });
|
|
|
|
|
|
- async loadTrainingHistory() {
|
|
|
- try {
|
|
|
- const records = await this.trainingService.getUserTrainingHistory(5);
|
|
|
- this.historyReports = await Promise.all(records.map(async (r: any) => {
|
|
|
- const scenario = await new CloudQuery("TrainingScenario").get(r.get('scenarioId').objectId);
|
|
|
-
|
|
|
- return {
|
|
|
- id: r.id,
|
|
|
- date: new Date(r.get('createdAt')),
|
|
|
- customerType: r.get('customerType'),
|
|
|
- difficulty: this.getDifficultyLabel(scenario?.get('difficulty')),
|
|
|
- summary: r.get('analysisResult') || '暂无分析结果',
|
|
|
- tags: [],
|
|
|
- evaluation: {
|
|
|
- intro: '',
|
|
|
- points: []
|
|
|
- },
|
|
|
- summaryMethods: {
|
|
|
- intro: '',
|
|
|
- methods: [],
|
|
|
- tags: []
|
|
|
- },
|
|
|
- score: 0
|
|
|
- };
|
|
|
- }));
|
|
|
- } catch (error) {
|
|
|
- console.error('加载训练历史失败:', error);
|
|
|
- }
|
|
|
+ this.trainingService.getAllScenes().subscribe(scenes => {
|
|
|
+ this.allScenes = scenes;
|
|
|
+ this.filteredScenes = [...scenes];
|
|
|
+ });
|
|
|
}
|
|
|
|
|
|
- /* 训练场景相关方法 */
|
|
|
- async selectTab(tab: DifficultyTab) {
|
|
|
- this.difficultyTabs.forEach(t => t.active = false);
|
|
|
- tab.active = true;
|
|
|
- await this.loadTrainingScenarios();
|
|
|
+ filterScenes(): void {
|
|
|
+ this.filteredScenes = this.allScenes.filter(scene => {
|
|
|
+ const levelMatch = !this.selectedLevel || scene.level === this.selectedLevel;
|
|
|
+ const typeMatch = !this.selectedCustomerType || scene.customerType === this.selectedCustomerType;
|
|
|
+ return levelMatch && typeMatch;
|
|
|
+ });
|
|
|
}
|
|
|
|
|
|
- selectCustomerType(type: CustomerType) {
|
|
|
- if (this.isEditing) return;
|
|
|
-
|
|
|
- this.customerTypes.forEach(t => t.active = false);
|
|
|
- type.active = true;
|
|
|
+ getLevelLabel(levelValue: string): string {
|
|
|
+ const level = this.sceneLevels.find(l => l.value === levelValue);
|
|
|
+ return level ? level.label : levelValue;
|
|
|
}
|
|
|
|
|
|
- toggleEditMode() {
|
|
|
- this.isEditing = !this.isEditing;
|
|
|
+ getCustomerLabel(typeValue: string): string {
|
|
|
+ const type = this.customerTypes.find(t => t.value === typeValue);
|
|
|
+ return type ? type.label : typeValue;
|
|
|
}
|
|
|
|
|
|
- async deleteCustomerType(type: CustomerType) {
|
|
|
- if (this.customerTypes.length <= 1) {
|
|
|
- alert('至少需要保留一个客户类型');
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- try {
|
|
|
- await this.trainingService.deleteCustomerType(type.id);
|
|
|
- const index = this.customerTypes.indexOf(type);
|
|
|
- if (index !== -1) {
|
|
|
- this.customerTypes.splice(index, 1);
|
|
|
- if (type.active && this.customerTypes.length > 0) {
|
|
|
- this.customerTypes[0].active = true;
|
|
|
- }
|
|
|
- }
|
|
|
- } catch (error) {
|
|
|
- console.error('删除客户类型失败:', error);
|
|
|
- alert('删除失败,请稍后重试');
|
|
|
- }
|
|
|
+ selectScene(scene: Scene): void {
|
|
|
+ this.selectedScene = scene;
|
|
|
+ this.currentRound = 1;
|
|
|
+ this.conversationScore = null;
|
|
|
+ this.chatMessages = [{
|
|
|
+ type: 'bot',
|
|
|
+ content: scene.initialMessage,
|
|
|
+ timestamp: new Date()
|
|
|
+ }];
|
|
|
+ this.scrollToBottom();
|
|
|
}
|
|
|
|
|
|
- /* 客户类型管理 */
|
|
|
- openAddCustomerModal() {
|
|
|
- this.showAddCustomerModal = true;
|
|
|
- }
|
|
|
+ sendMessage(): void {
|
|
|
+ if (!this.chatInput.trim() || !this.selectedScene) return;
|
|
|
|
|
|
- closeAddCustomerModal() {
|
|
|
- this.showAddCustomerModal = false;
|
|
|
- this.newCustomerTypeName = '';
|
|
|
- this.newCustomerDifficulty = 1;
|
|
|
- }
|
|
|
+ // 添加用户消息
|
|
|
+ const userMessage = {
|
|
|
+ type: 'user' as const,
|
|
|
+ content: this.chatInput,
|
|
|
+ timestamp: new Date()
|
|
|
+ };
|
|
|
+ this.chatMessages.push(userMessage);
|
|
|
+ this.chatInput = '';
|
|
|
+ this.scrollToBottom();
|
|
|
|
|
|
- async saveCustomerType() {
|
|
|
- if (!this.newCustomerTypeName.trim()) return;
|
|
|
-
|
|
|
- try {
|
|
|
- const newType = await this.trainingService.addCustomerType(
|
|
|
- this.newCustomerTypeName,
|
|
|
- this.newCustomerDifficulty,
|
|
|
- '自定义角色'
|
|
|
+ // 模拟AI回复
|
|
|
+ setTimeout(() => {
|
|
|
+ const botResponse = this.trainingService.generateResponse(
|
|
|
+ this.selectedScene!.id,
|
|
|
+ userMessage.content
|
|
|
);
|
|
|
|
|
|
- this.customerTypes.push({
|
|
|
- id: newType.id|| '',
|
|
|
- name: this.newCustomerTypeName,
|
|
|
- active: false,
|
|
|
- icon: this.getRandomIcon(),
|
|
|
- difficulty: this.newCustomerDifficulty,
|
|
|
- customerRole: '自定义角色'
|
|
|
+ this.chatMessages.push({
|
|
|
+ type: 'bot' as const,
|
|
|
+ content: botResponse,
|
|
|
+ timestamp: new Date()
|
|
|
});
|
|
|
-
|
|
|
- this.closeAddCustomerModal();
|
|
|
- } catch (error) {
|
|
|
- console.error('添加客户类型失败:', error);
|
|
|
- alert('添加失败,请稍后重试');
|
|
|
- }
|
|
|
- }
|
|
|
|
|
|
- getRandomIcon(): string {
|
|
|
- const icons = [
|
|
|
- 'fa-user', 'fa-user-tie', 'fa-users',
|
|
|
- 'fa-user-graduate', 'fa-user-md', 'fa-user-ninja'
|
|
|
- ];
|
|
|
- return icons[Math.floor(Math.random() * icons.length)];
|
|
|
- }
|
|
|
+ // 更新轮次和评分
|
|
|
+ this.currentRound = Math.ceil(this.chatMessages.length / 2);
|
|
|
+ if (this.currentRound <= this.maxRounds) {
|
|
|
+ this.updateScore();
|
|
|
+ }
|
|
|
|
|
|
- /* 训练相关方法 */
|
|
|
- async startTraining() {
|
|
|
- const activeType = this.customerTypes.find(type => type.active);
|
|
|
- if (!activeType) return;
|
|
|
-
|
|
|
- try {
|
|
|
- const record = await this.trainingService.createTrainingRecord(activeType.id, activeType.name);
|
|
|
- this.currentRecordId = record.id|| null;
|
|
|
-
|
|
|
- this.currentScenario = {
|
|
|
- id: activeType.id,
|
|
|
- name: activeType.name,
|
|
|
- difficulty: activeType.difficulty,
|
|
|
- customerRole: activeType.customerRole
|
|
|
- };
|
|
|
-
|
|
|
- this.messages = [{
|
|
|
- text: `您好,我是${activeType.customerRole},${this.generateCustomerOpening(activeType.name)}`,
|
|
|
- isCustomer: true
|
|
|
- }];
|
|
|
-
|
|
|
- this.currentScore = 70;
|
|
|
- this.scoreChange = 0;
|
|
|
- this.skills.forEach(s => s.value = 70);
|
|
|
-
|
|
|
this.scrollToBottom();
|
|
|
- } catch (error) {
|
|
|
- console.error('开始训练失败:', error);
|
|
|
- }
|
|
|
+ }, 800);
|
|
|
}
|
|
|
|
|
|
- generateCustomerOpening(type: string): string {
|
|
|
- const openings: Record<string, string[]> = {
|
|
|
- '商务客户': [
|
|
|
- "我想咨询一下贵公司的企业服务方案。",
|
|
|
- "我们需要预订一批商务客房,能提供优惠吗?",
|
|
|
- "关于贵公司的VIP服务,我有几个问题想了解。"
|
|
|
- ],
|
|
|
- '家庭客户': [
|
|
|
- "我们全家计划下周出游,能推荐适合的套餐吗?",
|
|
|
- "带孩子入住有什么需要注意的事项吗?",
|
|
|
- "请问有家庭优惠活动吗?"
|
|
|
- ],
|
|
|
- 'VIP客户': [
|
|
|
- "作为VIP会员,我应该享受哪些专属权益?",
|
|
|
- "这次入住希望能升级房型,可以安排吗?",
|
|
|
- "我的积分可以兑换什么服务?"
|
|
|
- ]
|
|
|
- };
|
|
|
-
|
|
|
- const defaultOpenings = [
|
|
|
- "我想咨询一些服务细节。",
|
|
|
- "能介绍一下你们的主要产品吗?",
|
|
|
- "我对你们的服务很感兴趣。"
|
|
|
- ];
|
|
|
+ private updateScore(): void {
|
|
|
+ const baseScore = 80;
|
|
|
+ const roundBonus = (this.currentRound - 1) * 5;
|
|
|
+ const randomVariation = Math.floor(Math.random() * 10);
|
|
|
|
|
|
- return openings[type]
|
|
|
- ? openings[type][Math.floor(Math.random() * openings[type].length)]
|
|
|
- : defaultOpenings[Math.floor(Math.random() * defaultOpenings.length)];
|
|
|
- }
|
|
|
+ this.conversationScore = Math.min(baseScore + roundBonus + randomVariation, 100);
|
|
|
|
|
|
- /* 对话功能 */
|
|
|
- toggleRecording() {
|
|
|
- this.isRecording = !this.isRecording;
|
|
|
- if (this.isRecording) {
|
|
|
- this.startVoiceRecording();
|
|
|
- } else {
|
|
|
- this.stopVoiceRecording();
|
|
|
+ // 如果完成所有轮次,生成报告
|
|
|
+ if (this.currentRound === this.maxRounds) {
|
|
|
+ this.generateReport();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- startVoiceRecording() {
|
|
|
- console.log('开始录音...');
|
|
|
- }
|
|
|
-
|
|
|
- stopVoiceRecording() {
|
|
|
- console.log('停止录音...');
|
|
|
- }
|
|
|
+ private generateReport(): void {
|
|
|
+ if (!this.selectedScene) return;
|
|
|
+
|
|
|
+ const newReport: Report = {
|
|
|
+ id: `report-${Date.now()}`,
|
|
|
+ name: this.selectedScene.name,
|
|
|
+ sceneId: this.selectedScene.id,
|
|
|
+ date: new Date().toISOString(),
|
|
|
+ difficulty: this.selectedScene.level,
|
|
|
+ customerRole: this.selectedScene.customerType,
|
|
|
+ score: this.conversationScore || 0,
|
|
|
+ evaluations: {
|
|
|
+ responsiveness: Math.min(this.conversationScore! + Math.floor(Math.random() * 10) - 5, 100),
|
|
|
+ 话术: Math.min((this.conversationScore || 80) + Math.floor(Math.random() * 8) - 4, 100),
|
|
|
+ persuasion: Math.min((this.conversationScore || 75) + Math.floor(Math.random() * 12) - 6, 100),
|
|
|
+ professionalism: Math.min((this.conversationScore || 85) + Math.floor(Math.random() * 6) - 3, 100)
|
|
|
+ },
|
|
|
+ summary: this.generateSummaryText()
|
|
|
+ };
|
|
|
|
|
|
- async sendMessage() {
|
|
|
- if (!this.userInput.trim() || !this.currentRecordId) return;
|
|
|
-
|
|
|
- this.messages.push({
|
|
|
- text: this.userInput,
|
|
|
- isCustomer: false
|
|
|
- });
|
|
|
- this.userInput = '';
|
|
|
- this.scrollToBottom();
|
|
|
-
|
|
|
- setTimeout(async () => {
|
|
|
- const aiResponse = this.generateAIResponse();
|
|
|
- this.messages.push({
|
|
|
- text: aiResponse,
|
|
|
- isCustomer: true
|
|
|
- });
|
|
|
-
|
|
|
- this.updateScore();
|
|
|
- this.scrollToBottom();
|
|
|
-
|
|
|
- if (this.messages.length >= 8) {
|
|
|
- await this.saveAnalysisResult();
|
|
|
- }
|
|
|
- }, 1000 + Math.random() * 1000);
|
|
|
+ this.generatedReports.unshift(newReport);
|
|
|
}
|
|
|
|
|
|
- generateAIResponse(): string {
|
|
|
- const responses = [
|
|
|
- "听起来不错,我会考虑这个方案。能提供一份详细报价吗?",
|
|
|
- "这个方案我需要再考虑一下,能否提供其他选择?",
|
|
|
- "感谢您的建议,我会和团队讨论后给您回复。",
|
|
|
- "这个价格可以接受,请问包含哪些服务?",
|
|
|
- "作为VIP会员,我希望能有更多优惠,可以吗?"
|
|
|
+ private generateSummaryText(): string {
|
|
|
+ const strengths = [
|
|
|
+ '展现了出色的沟通技巧',
|
|
|
+ '对产品知识掌握扎实',
|
|
|
+ '能够有效处理客户异议',
|
|
|
+ '表现出了高度的专业性',
|
|
|
+ '对话流畅自然',
|
|
|
+ '反应迅速准确'
|
|
|
];
|
|
|
- return responses[Math.floor(Math.random() * responses.length)];
|
|
|
- }
|
|
|
-
|
|
|
- /* 评分系统 */
|
|
|
- updateScore() {
|
|
|
- const change = Math.floor(Math.random() * 5) - 2;
|
|
|
- this.currentScore = Math.min(100, Math.max(0, this.currentScore + change));
|
|
|
- this.scoreChange = Math.floor(Math.random() * 10) - 3;
|
|
|
|
|
|
- this.skills.forEach(skill => {
|
|
|
- skill.value = Math.min(100, Math.max(0, skill.value + (Math.floor(Math.random() * 5) - 1)))
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
- getProgressOffset(): number {
|
|
|
- return (100 - this.currentScore) / 100 * 502;
|
|
|
- }
|
|
|
-
|
|
|
- getSkillColor(value: number): string {
|
|
|
- if (value >= 80) return '#34a853';
|
|
|
- if (value >= 60) return '#f9ab00';
|
|
|
- return '#ea4335';
|
|
|
- }
|
|
|
-
|
|
|
- /* 报告系统 */
|
|
|
- async saveAnalysisResult() {
|
|
|
- if (!this.currentRecordId) return;
|
|
|
-
|
|
|
- const analysis = `本次训练评分: ${this.currentScore}\n\n技能评估:\n` +
|
|
|
- this.skills.map(s => `${s.name}: ${s.value}`).join('\n') +
|
|
|
- `\n\n总结: ${this.generateSummary()}`;
|
|
|
-
|
|
|
- try {
|
|
|
- await this.trainingService.updateTrainingRecordAnalysis(
|
|
|
- this.currentRecordId,
|
|
|
- analysis
|
|
|
- );
|
|
|
-
|
|
|
- this.currentReport = {
|
|
|
- date: new Date(),
|
|
|
- customerType: this.getSelectedCustomerType(),
|
|
|
- difficulty: this.getSelectedDifficulty(),
|
|
|
- summary: analysis,
|
|
|
- tags: ['训练完成', '技能评估'],
|
|
|
- evaluation: {
|
|
|
- intro: '训练结果分析:',
|
|
|
- points: this.skills.map(s => `${s.name}得分: ${s.value}`)
|
|
|
- },
|
|
|
- summaryMethods: {
|
|
|
- intro: '建议改进方向:',
|
|
|
- methods: this.generateImprovementMethods(),
|
|
|
- tags: ['改进建议']
|
|
|
- },
|
|
|
- score: this.currentScore
|
|
|
- };
|
|
|
-
|
|
|
- await this.loadTrainingHistory();
|
|
|
- } catch (error) {
|
|
|
- console.error('保存分析结果失败:', error);
|
|
|
- }
|
|
|
- }
|
|
|
+ const improvements = [
|
|
|
+ '可以进一步提高反应速度',
|
|
|
+ '建议丰富案例说明',
|
|
|
+ '需要加强价格谈判技巧',
|
|
|
+ '可优化话术结构',
|
|
|
+ '应更关注客户需求',
|
|
|
+ '可增加数据支持论点'
|
|
|
+ ];
|
|
|
|
|
|
- generateImprovementMethods(): string[] {
|
|
|
- const methods = [];
|
|
|
- if (this.skills[0].value < 80) {
|
|
|
- methods.push('加强反应速度训练,提高即时应对能力');
|
|
|
- }
|
|
|
- if (this.skills[1].value < 80) {
|
|
|
- methods.push('学习更多话术技巧,丰富表达方式');
|
|
|
- }
|
|
|
- if (this.skills[2].value < 80) {
|
|
|
- methods.push('提高说服力,学习心理学技巧');
|
|
|
- }
|
|
|
- if (this.skills[3].value < 80) {
|
|
|
- methods.push('加强专业知识学习,提高专业度');
|
|
|
- }
|
|
|
- return methods.length > 0 ? methods : ['各方面表现良好,继续保持!'];
|
|
|
+ return `在${this.selectedScene?.name}场景训练中,${
|
|
|
+ strengths[Math.floor(Math.random() * strengths.length)]
|
|
|
+ },${improvements[Math.floor(Math.random() * improvements.length)]}。`;
|
|
|
}
|
|
|
|
|
|
- generateSummary(): string {
|
|
|
- const avgScore = this.skills.reduce((sum, skill) => sum + skill.value, 0) / this.skills.length;
|
|
|
- if (avgScore >= 80) {
|
|
|
- return '表现优秀!继续保持当前训练强度,可以挑战更高难度场景。';
|
|
|
- } else if (avgScore >= 60) {
|
|
|
- return '表现良好,但仍有提升空间。建议针对薄弱环节加强训练。';
|
|
|
- } else {
|
|
|
- return '表现有待提高。建议从基础场景开始,逐步提升各项能力。';
|
|
|
+ resetConversation(): void {
|
|
|
+ if (this.selectedScene) {
|
|
|
+ this.selectScene(this.selectedScene);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- openFullReportModal() {
|
|
|
- this.showFullReportModal = true;
|
|
|
- }
|
|
|
-
|
|
|
- closeFullReportModal() {
|
|
|
- this.showFullReportModal = false;
|
|
|
+ viewReport(report: Report): void {
|
|
|
+ this.selectedReport = report;
|
|
|
+ console.log('查看完整报告:', report);
|
|
|
}
|
|
|
|
|
|
- openHistoryReportModal() {
|
|
|
- this.showHistoryReportModal = true;
|
|
|
+ closeReport(): void {
|
|
|
+ this.selectedReport = null;
|
|
|
}
|
|
|
|
|
|
- closeHistoryReportModal() {
|
|
|
- this.showHistoryReportModal = false;
|
|
|
- }
|
|
|
-
|
|
|
- async viewHistoryReport(report: Report) {
|
|
|
- this.currentReport = report;
|
|
|
- this.currentScore = report.score;
|
|
|
- this.closeHistoryReportModal();
|
|
|
+ private scrollToBottom(): void {
|
|
|
setTimeout(() => {
|
|
|
- this.openFullReportModal();
|
|
|
- }, 300);
|
|
|
- }
|
|
|
-
|
|
|
- /* 辅助方法 */
|
|
|
- getCustomerIcon(): string {
|
|
|
- const activeType = this.customerTypes.find(type => type.active);
|
|
|
- return activeType ? activeType.icon : 'fa-user';
|
|
|
- }
|
|
|
-
|
|
|
- getSelectedCustomerType(): string {
|
|
|
- const activeType = this.customerTypes.find(type => type.active);
|
|
|
- return activeType ? activeType.name : '';
|
|
|
- }
|
|
|
-
|
|
|
- getSelectedDifficulty(): string {
|
|
|
- const activeTab = this.difficultyTabs.find(tab => tab.active);
|
|
|
- return activeTab ? activeTab.label : '';
|
|
|
- }
|
|
|
-
|
|
|
- getDifficultyLabel(difficulty?: number): string {
|
|
|
- if (difficulty === undefined) return '';
|
|
|
- const labels = ['⭐ 初级', '⭐⭐ 中级', '⭐⭐⭐ 高级', '专家挑战'];
|
|
|
- return labels[difficulty - 1] || '';
|
|
|
- }
|
|
|
-
|
|
|
- getScoreTagClass(): string {
|
|
|
- if (this.currentScore >= 80) return 'high-score';
|
|
|
- if (this.currentScore >= 60) return 'medium-score';
|
|
|
- return 'low-score';
|
|
|
+ if (this.chatMessagesContainer) {
|
|
|
+ this.chatMessagesContainer.nativeElement.scrollTop =
|
|
|
+ this.chatMessagesContainer.nativeElement.scrollHeight;
|
|
|
+ }
|
|
|
+ }, 100);
|
|
|
}
|
|
|
-}
|
|
|
-
|
|
|
-/* 类型定义 */
|
|
|
-interface Message {
|
|
|
- text: string;
|
|
|
- isCustomer: boolean;
|
|
|
-}
|
|
|
-
|
|
|
-interface CustomerType {
|
|
|
- id: string;
|
|
|
- name: string;
|
|
|
- active: boolean;
|
|
|
- icon: string;
|
|
|
- difficulty: number;
|
|
|
- customerRole: string;
|
|
|
-}
|
|
|
-
|
|
|
-interface DifficultyTab {
|
|
|
- label: string;
|
|
|
- value: number;
|
|
|
- active: boolean;
|
|
|
-}
|
|
|
-
|
|
|
-interface Skill {
|
|
|
- name: string;
|
|
|
- value: number;
|
|
|
- key: string;
|
|
|
-}
|
|
|
-
|
|
|
-interface Evaluation {
|
|
|
- intro: string;
|
|
|
- points: string[];
|
|
|
-}
|
|
|
-
|
|
|
-interface SummaryMethods {
|
|
|
- intro: string;
|
|
|
- methods: string[];
|
|
|
- tags: string[];
|
|
|
-}
|
|
|
-
|
|
|
-interface Report {
|
|
|
- id?: string;
|
|
|
- date: Date;
|
|
|
- customerType: string;
|
|
|
- difficulty: string;
|
|
|
- summary: string;
|
|
|
- tags: string[];
|
|
|
- evaluation: Evaluation;
|
|
|
- summaryMethods: SummaryMethods;
|
|
|
- score: number;
|
|
|
}
|