feat: 初始化Vue3项目并添加核心功能模块

新增项目基础结构,包括Vue3、Pinia、Element Plus等核心依赖
添加路由配置和用户认证状态管理
实现销售数据看板、客户画像、团队管理等核心功能模块
集成图表库和API请求工具,完成基础样式配置
This commit is contained in:
2025-08-12 14:34:44 +08:00
commit f93236ab36
71 changed files with 32821 additions and 0 deletions

View File

@@ -0,0 +1,992 @@
<template>
<div class="customer-detail-container">
<div v-if="selectedContact" class="customer-detail-content">
<!-- 头部信息 -->
<div class="customer-header">
<h3>{{ selectedContact.name }}</h3>
<div class="action-buttons">
<button
@click="startBasicAnalysis"
class="analysis-button"
:disabled="isBasicAnalysisLoading"
>
{{ isBasicAnalysisLoading ? '基础分析中...' : '基础信息分析' }}
</button>
<button
@click="startSopAnalysis"
class="analysis-button sop-button"
:disabled="isSopAnalysisLoading"
>
{{ isSopAnalysisLoading ? 'SOP分析中...' : 'SOP通话分析' }}
</button>
<button
@click="startDemandAnalysis"
class="analysis-button demand-button"
:disabled="isDemandAnalysisLoading"
>
{{ isDemandAnalysisLoading ? '诉求分析中...' : '客户诉求分析' }}
</button>
</div>
</div>
<!-- 分析区域 -->
<div class="analysis-areas">
<!-- 上方两个区域 -->
<div class="top-row">
<!-- 基础信息分析 -->
<div class="analysis-section basic-analysis">
<div class="section-header">
<h4>基础信息分析</h4>
</div>
<div class="section-content">
<div class="text-content" v-if="basicAnalysisResult">
<div class="analysis-text" v-html="formattedBasicAnalysis"></div>
</div>
<div class="placeholder-text" v-else>
<p>点击"基础信息分析"按钮开始分析客户基础信息</p>
</div>
</div>
</div>
<!-- SOP通话分析 -->
<div class="analysis-section sop-analysis">
<div class="section-header">
<h4>SOP通话分析</h4>
</div>
<div class="section-content">
<div class="text-content" v-if="sopAnalysisResult">
<div class="analysis-text" v-html="formattedSopAnalysis"></div>
</div>
<div class="placeholder-text" v-else>
<p>点击"SOP通话分析"按钮开始分析通话记录</p>
</div>
</div>
</div>
</div>
<!-- 下方整行区域 -->
<div class="bottom-row">
<!-- 客户诉求分析 -->
<div class="analysis-section demand-analysis">
<div class="section-header">
<h4>客户诉求分析</h4>
</div>
<div class="section-content">
<div class="text-content" v-if="demandAnalysisResult">
<div class="analysis-text" v-html="formattedDemandAnalysis"></div>
</div>
<div class="placeholder-text" v-else>
<p>点击"客户诉求分析"按钮开始深度分析客户需求和诉求</p>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 未选择客户时的提示 -->
<div v-else class="no-selection">
<p>请选择一个客户查看详情</p>
</div>
</div>
</template>
<script setup>
import { ref, watch, computed } from 'vue';
import { SimpleChatService } from '@/utils/ChatService.js';
import MarkdownIt from 'markdown-it';
// 定义props
const props = defineProps({
selectedContact: {
type: Object,
default: null
}
});
// 分析结果状态
const basicAnalysisResult = ref(''); // 基础信息分析结果
const sopAnalysisResult = ref(''); // SOP通话分析结果
const demandAnalysisResult = ref(''); // 客户诉求分析结果
// 加载状态
const isBasicAnalysisLoading = ref(false); // 基础分析加载状态
const isSopAnalysisLoading = ref(false); // SOP分析加载状态
const isDemandAnalysisLoading = ref(false); // 诉求分析加载状态
// Dify API配置
const DIFY_API_KEY_01 = 'app-wbR1P1j6kvdBK8Q1qXzdswzP';
const DIFY_API_KEY = 'app-37VXHRieOnq17BSury9ONavG';
// 初始化ChatService
const chatService_01 = new SimpleChatService(DIFY_API_KEY_01);
const chatService = new SimpleChatService(DIFY_API_KEY);
// 初始化markdown-it
const md = new MarkdownIt({
html: true,
linkify: true,
typographer: true
});
// 计算属性:格式化基础分析结果
const formattedBasicAnalysis = computed(() => {
if (!basicAnalysisResult.value) return '';
return md.render(basicAnalysisResult.value);
});
// 计算属性格式化SOP分析结果
const formattedSopAnalysis = computed(() => {
if (!sopAnalysisResult.value) return '';
return md.render(sopAnalysisResult.value);
});
// 计算属性:格式化诉求分析结果
const formattedDemandAnalysis = computed(() => {
if (!demandAnalysisResult.value) return '';
return md.render(demandAnalysisResult.value);
});
// 监听selectedContact变化重置所有分析结果
watch(() => props.selectedContact, (newContact) => {
if (newContact) {
// 重置所有分析状态
basicAnalysisResult.value = '';
sopAnalysisResult.value = '';
demandAnalysisResult.value = '';
isBasicAnalysisLoading.value = false;
isSopAnalysisLoading.value = false;
isDemandAnalysisLoading.value = false;
} else {
// 清空所有结果
basicAnalysisResult.value = '';
sopAnalysisResult.value = '';
demandAnalysisResult.value = '';
isBasicAnalysisLoading.value = false;
isSopAnalysisLoading.value = false;
isDemandAnalysisLoading.value = false;
}
}, { immediate: true });
// 基础信息分析
const startBasicAnalysis = async () => {
if (!props.selectedContact) return;
isBasicAnalysisLoading.value = true;
basicAnalysisResult.value = '';
const query = `请对客户进行基础信息分析:
客户姓名:${props.selectedContact.name}
联系电话:${props.selectedContact.phone || '未提供'}
邮箱:${props.selectedContact.email || '未提供'}
公司:${props.selectedContact.company || '未提供'}
职位:${props.selectedContact.position || '未提供'}
销售阶段:${props.selectedContact.salesStage || '未知'}
健康度:${props.selectedContact.health || '未知'}%
请分析客户的基本情况、背景信息和初步画像。`;
try {
await chatService_01.sendMessage(
query,
(update) => {
basicAnalysisResult.value = update.content;
},
() => {
isBasicAnalysisLoading.value = false;
console.log('基础信息分析完成');
}
);
} catch (error) {
console.error('基础信息分析失败:', error);
basicAnalysisResult.value = `分析失败: ${error.message}`;
isBasicAnalysisLoading.value = false;
}
};
// SOP通话分析
const startSopAnalysis = async () => {
if (!props.selectedContact) return;
isSopAnalysisLoading.value = true;
sopAnalysisResult.value = '';
const query = `请对客户 ${props.selectedContact.name} 进行SOP通话分析
基于标准销售流程(SOP),分析以下方面:
1. 通话质量评估
2. 销售流程执行情况
3. 客户响应度分析
4. 沟通效果评价
5. 改进建议
客户当前状态:${props.selectedContact.salesStage || '未知'}
健康度:${props.selectedContact.health || '未知'}%`;
try {
await chatService.sendMessage(
query,
(update) => {
sopAnalysisResult.value = update.content;
},
() => {
isSopAnalysisLoading.value = false;
console.log('SOP通话分析完成');
}
);
} catch (error) {
console.error('SOP通话分析失败:', error);
sopAnalysisResult.value = `分析失败: ${error.message}`;
isSopAnalysisLoading.value = false;
}
};
// 客户诉求分析
const startDemandAnalysis = async () => {
if (!props.selectedContact) return;
isDemandAnalysisLoading.value = true;
demandAnalysisResult.value = '';
const query = `请对客户 ${props.selectedContact.name} 进行深度诉求分析:
请从以下维度分析客户的真实需求和诉求:
1. 显性需求分析(客户明确表达的需求)
2. 隐性需求挖掘(潜在的、未明确表达的需求)
3. 痛点识别(客户面临的主要问题和挑战)
4. 决策因素分析(影响客户决策的关键因素)
5. 价值期望(客户期望获得的价值和收益)
6. 风险顾虑(客户可能的担忧和顾虑)
7. 个性化建议(针对性的解决方案建议)
客户信息:
姓名:${props.selectedContact.name}
公司:${props.selectedContact.company || '未提供'}
职位:${props.selectedContact.position || '未提供'}
销售阶段:${props.selectedContact.salesStage || '未知'}
健康度:${props.selectedContact.health || '未知'}%`;
try {
await chatService.sendMessage(
query,
(update) => {
demandAnalysisResult.value = update.content;
},
() => {
isDemandAnalysisLoading.value = false;
console.log('客户诉求分析完成');
}
);
} catch (error) {
console.error('客户诉求分析失败:', error);
demandAnalysisResult.value = `分析失败: ${error.message}`;
isDemandAnalysisLoading.value = false;
}
};
</script>
<style lang="scss" scoped>
// Color Palette
$slate-50: #f8fafc;
$slate-100: #f1f5f9;
$slate-200: #e2e8f0;
$slate-300: #cbd5e1;
$slate-400: #94a3b8;
$slate-500: #64748b;
$slate-600: #475569;
$slate-700: #334155;
$slate-800: #1e293b;
$white: #ffffff;
$blue: #3b82f6;
$green: #22c55e;
$amber: #f59e0b;
$red: #ef4444;
$indigo: #4f46e5;
$purple: #a855f7;
// 容器样式
.customer-detail-container {
width: 100%;
height: 100%;
box-sizing: border-box;
padding: 16px;
// PC端保持一致布局
@media (min-width: 1024px) {
// padding: 24px;
}
// 平板端适配
@media (max-width: 1023px) and (min-width: 769px) {
padding: 20px;
}
// 移动端适配
@media (max-width: 768px) {
padding: 12px;
}
// 小屏移动端适配
@media (max-width: 480px) {
padding: 8px;
}
}
// 客户详情内容
.customer-detail-content {
height: 100%;
display: flex;
flex-direction: column;
gap: 16px;
}
// 客户头部信息
.customer-header {
background: $white;
border-radius: 8px;
padding: 16px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
border: 1px solid $slate-200;
display: flex;
justify-content: space-between;
align-items: center;
// PC端保持一致布局
@media (min-width: 1024px) {
padding: 20px;
border-radius: 12px;
}
// 平板端适配
@media (max-width: 1023px) and (min-width: 769px) {
padding: 18px;
border-radius: 10px;
}
// 移动端适配
@media (max-width: 768px) {
flex-direction: column;
align-items: flex-start;
gap: 16px;
padding: 16px;
}
// 小屏移动端适配
@media (max-width: 480px) {
padding: 12px;
gap: 12px;
}
h3 {
margin: 0;
color: $slate-800;
font-size: 20px;
font-weight: 600;
// PC端保持一致布局
@media (min-width: 1024px) {
font-size: 24px;
}
// 平板端适配
@media (max-width: 1023px) and (min-width: 769px) {
font-size: 22px;
}
// 移动端适配
@media (max-width: 768px) {
font-size: 18px;
}
// 小屏移动端适配
@media (max-width: 480px) {
font-size: 16px;
}
}
.action-buttons {
display: flex;
gap: 12px;
// 移动端适配
@media (max-width: 768px) {
width: 100%;
flex-direction: column;
gap: 8px;
}
// 小屏移动端适配
@media (max-width: 480px) {
gap: 6px;
}
.analysis-button {
padding: 10px 16px;
background: $blue;
color: white;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 14px;
font-weight: 500;
transition: all 0.2s ease;
white-space: nowrap;
// PC端保持一致布局
@media (min-width: 1024px) {
padding: 12px 20px;
font-size: 15px;
border-radius: 8px;
}
// 平板端适配
@media (max-width: 1023px) and (min-width: 769px) {
padding: 11px 18px;
font-size: 14px;
border-radius: 7px;
}
// 移动端适配
@media (max-width: 768px) {
width: 100%;
padding: 12px 16px;
font-size: 14px;
text-align: center;
}
// 小屏移动端适配
@media (max-width: 480px) {
padding: 10px 12px;
font-size: 13px;
}
&:hover:not(:disabled) {
background: #2563eb;
transform: translateY(-1px);
}
&:disabled {
background: $slate-400;
cursor: not-allowed;
transform: none;
}
&.sop-button {
background: $green;
&:hover:not(:disabled) {
background: #16a34a;
}
}
&.demand-button {
background: $purple;
&:hover:not(:disabled) {
background: #9333ea;
}
}
}
}
}
// 分析区域
.analysis-areas {
flex: 1;
display: flex;
flex-direction: column;
gap: 16px;
min-height: 0;
}
// 上方行
.top-row {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 16px;
height: 45%;
// PC端保持一致布局
@media (min-width: 1024px) {
gap: 20px;
}
// 平板端适配
@media (max-width: 1023px) and (min-width: 769px) {
gap: 18px;
}
// 移动端适配
@media (max-width: 768px) {
grid-template-columns: 1fr;
height: auto;
gap: 16px;
}
// 小屏移动端适配
@media (max-width: 480px) {
gap: 12px;
}
}
// 下方行
.bottom-row {
height: 55%;
}
// 分析区域样式
.analysis-section {
background: $white;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
border: 1px solid $slate-200;
display: flex;
flex-direction: column;
overflow: hidden;
// PC端保持一致布局
@media (min-width: 1024px) {
border-radius: 12px;
}
// 平板端适配
@media (max-width: 1023px) and (min-width: 769px) {
border-radius: 10px;
}
// 移动端适配
@media (max-width: 768px) {
min-height: 300px;
}
// 小屏移动端适配
@media (max-width: 480px) {
min-height: 250px;
}
.section-header {
padding: 12px 16px;
background: $slate-50;
border-bottom: 1px solid $slate-200;
// PC端保持一致布局
@media (min-width: 1024px) {
padding: 16px 20px;
}
// 平板端适配
@media (max-width: 1023px) and (min-width: 769px) {
padding: 14px 18px;
}
// 移动端适配
@media (max-width: 768px) {
padding: 12px 16px;
}
// 小屏移动端适配
@media (max-width: 480px) {
padding: 10px 12px;
}
h4 {
margin: 0;
font-size: 16px;
font-weight: 600;
color: $slate-700;
// PC端保持一致布局
@media (min-width: 1024px) {
font-size: 18px;
}
// 平板端适配
@media (max-width: 1023px) and (min-width: 769px) {
font-size: 17px;
}
// 移动端适配
@media (max-width: 768px) {
font-size: 15px;
}
// 小屏移动端适配
@media (max-width: 480px) {
font-size: 14px;
}
}
}
.section-content {
flex: 1;
padding: 16px;
overflow-y: auto;
// PC端保持一致布局
@media (min-width: 1024px) {
padding: 20px;
}
// 平板端适配
@media (max-width: 1023px) and (min-width: 769px) {
padding: 18px;
}
// 移动端适配
@media (max-width: 768px) {
padding: 16px;
}
// 小屏移动端适配
@media (max-width: 480px) {
padding: 12px;
}
.text-content {
height: 100%;
.analysis-text {
color: $slate-700;
font-size: 14px;
line-height: 1.6;
word-wrap: break-word;
// PC端保持一致布局
@media (min-width: 1024px) {
font-size: 15px;
line-height: 1.7;
}
// 平板端适配
@media (max-width: 1023px) and (min-width: 769px) {
font-size: 14px;
line-height: 1.65;
}
// 移动端适配
@media (max-width: 768px) {
font-size: 13px;
line-height: 1.6;
}
// 小屏移动端适配
@media (max-width: 480px) {
font-size: 12px;
line-height: 1.5;
}
}
}
.placeholder-text {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
background: $slate-50;
border-radius: 6px;
border: 2px dashed $slate-200;
p {
margin: 0;
color: $slate-500;
font-size: 14px;
text-align: center;
padding: 16px;
// PC端保持一致布局
@media (min-width: 1024px) {
font-size: 15px;
padding: 20px;
}
// 平板端适配
@media (max-width: 1023px) and (min-width: 769px) {
font-size: 14px;
padding: 18px;
}
// 移动端适配
@media (max-width: 768px) {
font-size: 13px;
padding: 16px;
}
// 小屏移动端适配
@media (max-width: 480px) {
font-size: 12px;
padding: 12px;
}
}
}
}
// 不同分析区域的主题色
&.basic-analysis {
.section-header {
background: linear-gradient(135deg, #eff6ff 0%, #dbeafe 100%);
h4 {
color: $blue;
}
}
}
&.sop-analysis {
.section-header {
background: linear-gradient(135deg, #f0fdf4 0%, #dcfce7 100%);
h4 {
color: $green;
}
}
}
&.demand-analysis {
.section-header {
background: linear-gradient(135deg, #faf5ff 0%, #f3e8ff 100%);
h4 {
color: $purple;
}
}
}
}
// Markdown样式
.analysis-text {
// Markdown样式
h1, h2, h3, h4, h5, h6 {
margin: 1rem 0 0.5rem 0;
font-weight: 600;
color: $slate-800;
&:first-child {
margin-top: 0;
}
}
h1 { font-size: 1.25rem; }
h2 { font-size: 1.125rem; }
h3 { font-size: 1rem; }
h4 { font-size: 0.875rem; }
h5 { font-size: 0.75rem; }
h6 { font-size: 0.75rem; }
p {
margin: 0.5rem 0;
&:first-child {
margin-top: 0;
}
&:last-child {
margin-bottom: 0;
}
}
ul, ol {
margin: 0.5rem 0;
padding-left: 1.5rem;
li {
margin: 0.25rem 0;
}
}
blockquote {
margin: 1rem 0;
padding: 0.5rem 1rem;
border-left: 4px solid $blue;
background: rgba(59, 130, 246, 0.05);
font-style: italic;
}
code {
background: $slate-100;
padding: 0.125rem 0.25rem;
border-radius: 0.25rem;
font-family: 'Courier New', monospace;
font-size: 0.8rem;
}
pre {
background: $slate-100;
padding: 1rem;
border-radius: 0.5rem;
overflow-x: auto;
margin: 1rem 0;
code {
background: none;
padding: 0;
}
}
strong {
font-weight: 600;
color: $slate-800;
}
em {
font-style: italic;
}
a {
color: $blue;
text-decoration: none;
&:hover {
text-decoration: underline;
}
}
hr {
margin: 1.5rem 0;
border: none;
border-top: 1px solid $slate-200;
}
table {
width: 100%;
border-collapse: collapse;
margin: 1rem 0;
th, td {
padding: 0.5rem;
border: 1px solid $slate-200;
text-align: left;
}
th {
background: $slate-50;
font-weight: 600;
}
}
}
// 未选择状态
.no-selection {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
background: $slate-50;
border-radius: 8px;
border: 2px dashed $slate-200;
color: $slate-500;
// PC端保持一致布局
@media (min-width: 1024px) {
border-radius: 12px;
min-height: 500px;
}
// 平板端适配
@media (max-width: 1023px) and (min-width: 769px) {
border-radius: 10px;
min-height: 450px;
}
// 移动端适配
@media (max-width: 768px) {
height: 400px;
border-radius: 8px;
}
// 小屏移动端适配
@media (max-width: 480px) {
height: 300px;
border-radius: 6px;
}
p {
margin: 0;
font-size: 1rem;
text-align: center;
padding: 1rem;
// PC端保持一致布局
@media (min-width: 1024px) {
font-size: 1.125rem;
padding: 1.5rem;
}
// 平板端适配
@media (max-width: 1023px) and (min-width: 769px) {
font-size: 1.0625rem;
padding: 1.25rem;
}
// 移动端适配
@media (max-width: 768px) {
font-size: 0.875rem;
padding: 1rem;
}
// 小屏移动端适配
@media (max-width: 480px) {
font-size: 0.75rem;
padding: 0.75rem;
}
}
}
h2.section-title {
font-size: 1.25rem;
font-weight: bold;
color: $slate-700;
}
h4 {
font-weight: 600;
color: $slate-700;
margin-bottom: 0.5rem;
}
// Context Panel
.section-card {
background-color: $white;
border-radius: 0.5rem;
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
// padding: 1rem;
// margin-top: 12px;
}
// 分析区域布局优化
.analysis-areas {
// PC端保持一致布局
@media (min-width: 1024px) {
gap: 20px;
}
// 平板端适配
@media (max-width: 1023px) and (min-width: 769px) {
gap: 18px;
}
// 移动端适配
@media (max-width: 768px) {
gap: 16px;
}
// 小屏移动端适配
@media (max-width: 480px) {
gap: 12px;
}
}
// 下方行适配
.bottom-row {
// 移动端适配
@media (max-width: 768px) {
height: auto;
min-height: 300px;
}
// 小屏移动端适配
@media (max-width: 480px) {
min-height: 250px;
}
}
</style>