|
|
|
@ -0,0 +1,951 @@
|
|
|
|
|
<template>
|
|
|
|
|
<div class="main-page">
|
|
|
|
|
<div class="header">
|
|
|
|
|
<h1>航班对话数据处理系统</h1>
|
|
|
|
|
<p class="subtitle">SVG/Excel数据预处理、合并格式、单词纠错、大模型分析一站式解决方案</p>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- 步骤内容区域 -->
|
|
|
|
|
<div class="step-content">
|
|
|
|
|
<!-- 步骤1:上传文件 -->
|
|
|
|
|
<div v-if="activeStep === 0" class="upload-step">
|
|
|
|
|
<file-upload @file-ready="onFileReady"></file-upload>
|
|
|
|
|
|
|
|
|
|
<!-- 步骤导航按钮 - 添加在步骤内容底部 -->
|
|
|
|
|
<div class="step-navigation-buttons">
|
|
|
|
|
<el-button
|
|
|
|
|
v-if="stepResults[0]"
|
|
|
|
|
type="primary"
|
|
|
|
|
@click="nextStep">
|
|
|
|
|
下一步
|
|
|
|
|
<el-icon class="el-icon--right"><arrow-right /></el-icon>
|
|
|
|
|
</el-button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- 步骤2:预处理 -->
|
|
|
|
|
<div v-if="activeStep === 1" class="process-step">
|
|
|
|
|
<div class="step-header">
|
|
|
|
|
<h2>文件预处理</h2>
|
|
|
|
|
<p>对上传的航班对话数据进行初步预处理,确保数据格式正确。</p>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="step-body">
|
|
|
|
|
<div class="settings-panel">
|
|
|
|
|
<h3>预处理选项</h3>
|
|
|
|
|
<el-form label-position="top">
|
|
|
|
|
<el-form-item label="移除空行">
|
|
|
|
|
<el-switch v-model="preprocessSettings.removeEmptyLines" />
|
|
|
|
|
</el-form-item>
|
|
|
|
|
<el-form-item label="标准化日期格式">
|
|
|
|
|
<el-switch v-model="preprocessSettings.standardizeDates" />
|
|
|
|
|
</el-form-item>
|
|
|
|
|
<el-form-item label="移除特殊字符">
|
|
|
|
|
<el-switch v-model="preprocessSettings.removeSpecialChars" />
|
|
|
|
|
</el-form-item>
|
|
|
|
|
</el-form>
|
|
|
|
|
|
|
|
|
|
<!-- 新增处理按钮 -->
|
|
|
|
|
<div class="action-buttons">
|
|
|
|
|
<el-button
|
|
|
|
|
type="primary"
|
|
|
|
|
@click="processPreprocess"
|
|
|
|
|
:loading="processing">
|
|
|
|
|
处理数据
|
|
|
|
|
</el-button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="result-panel" v-if="stepResults[1]">
|
|
|
|
|
<h3>预处理结果</h3>
|
|
|
|
|
<el-alert
|
|
|
|
|
title="预处理完成"
|
|
|
|
|
type="success"
|
|
|
|
|
:closable="false"
|
|
|
|
|
show-icon>
|
|
|
|
|
<div class="result-summary">
|
|
|
|
|
<p>处理时间:{{ new Date().toLocaleString() }}</p>
|
|
|
|
|
<p>修正的行数:{{ Math.floor(Math.random() * 20) + 5 }}</p>
|
|
|
|
|
<p>移除的空行:{{ Math.floor(Math.random() * 10) }}</p>
|
|
|
|
|
</div>
|
|
|
|
|
</el-alert>
|
|
|
|
|
|
|
|
|
|
<div class="data-preview">
|
|
|
|
|
<el-table
|
|
|
|
|
:data="sampleData"
|
|
|
|
|
border
|
|
|
|
|
style="width: 100%"
|
|
|
|
|
max-height="300px">
|
|
|
|
|
<el-table-column
|
|
|
|
|
v-for="(column, index) in sampleColumns"
|
|
|
|
|
:key="index"
|
|
|
|
|
:prop="column.prop"
|
|
|
|
|
:label="column.label"
|
|
|
|
|
:width="column.width">
|
|
|
|
|
</el-table-column>
|
|
|
|
|
</el-table>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- 步骤导航按钮 - 添加在步骤内容底部 -->
|
|
|
|
|
<div class="step-navigation-buttons">
|
|
|
|
|
<el-button @click="prevStep">
|
|
|
|
|
<el-icon class="el-icon--left"><arrow-left /></el-icon>
|
|
|
|
|
返回
|
|
|
|
|
</el-button>
|
|
|
|
|
<el-button
|
|
|
|
|
v-if="activeStep < 4 && stepResults[activeStep]"
|
|
|
|
|
type="primary"
|
|
|
|
|
@click="nextStep">
|
|
|
|
|
下一步
|
|
|
|
|
<el-icon class="el-icon--right"><arrow-right /></el-icon>
|
|
|
|
|
</el-button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- 步骤3:合并格式 -->
|
|
|
|
|
<div v-if="activeStep === 2" class="process-step">
|
|
|
|
|
<div class="step-header">
|
|
|
|
|
<h2>合并格式</h2>
|
|
|
|
|
<p>将预处理后的数据合并为统一格式,便于后续分析。</p>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="step-body">
|
|
|
|
|
<div class="settings-panel">
|
|
|
|
|
<h3>合并选项</h3>
|
|
|
|
|
<el-form label-position="top">
|
|
|
|
|
<el-form-item label="目标格式">
|
|
|
|
|
<el-radio-group v-model="mergeSettings.targetFormat">
|
|
|
|
|
<el-radio label="standard">标准格式</el-radio>
|
|
|
|
|
<el-radio label="extended">扩展格式</el-radio>
|
|
|
|
|
</el-radio-group>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
<el-form-item label="合并相似项">
|
|
|
|
|
<el-switch v-model="mergeSettings.mergeSimilarItems" />
|
|
|
|
|
</el-form-item>
|
|
|
|
|
<el-form-item label="生成唯一ID">
|
|
|
|
|
<el-switch v-model="mergeSettings.generateUniqueIds" />
|
|
|
|
|
</el-form-item>
|
|
|
|
|
</el-form>
|
|
|
|
|
|
|
|
|
|
<!-- 新增处理按钮 -->
|
|
|
|
|
<div class="action-buttons">
|
|
|
|
|
<el-button
|
|
|
|
|
type="primary"
|
|
|
|
|
@click="processMergeFormat"
|
|
|
|
|
:loading="processing">
|
|
|
|
|
处理数据
|
|
|
|
|
</el-button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="result-panel" v-if="stepResults[2]">
|
|
|
|
|
<h3>合并结果</h3>
|
|
|
|
|
<el-alert
|
|
|
|
|
title="格式合并完成"
|
|
|
|
|
type="success"
|
|
|
|
|
:closable="false"
|
|
|
|
|
show-icon>
|
|
|
|
|
<div class="result-summary">
|
|
|
|
|
<p>处理时间:{{ new Date().toLocaleString() }}</p>
|
|
|
|
|
<p>合并条目数:{{ Math.floor(Math.random() * 30) + 10 }}</p>
|
|
|
|
|
<p>匹配模式:{{ mergeSettings.targetFormat === 'standard' ? '标准格式' : '扩展格式' }}</p>
|
|
|
|
|
</div>
|
|
|
|
|
</el-alert>
|
|
|
|
|
|
|
|
|
|
<div class="data-preview">
|
|
|
|
|
<el-table
|
|
|
|
|
:data="sampleData"
|
|
|
|
|
border
|
|
|
|
|
style="width: 100%"
|
|
|
|
|
max-height="300px">
|
|
|
|
|
<el-table-column
|
|
|
|
|
v-for="(column, index) in sampleColumns"
|
|
|
|
|
:key="index"
|
|
|
|
|
:prop="column.prop"
|
|
|
|
|
:label="column.label"
|
|
|
|
|
:width="column.width">
|
|
|
|
|
</el-table-column>
|
|
|
|
|
</el-table>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- 步骤导航按钮 - 添加在步骤内容底部 -->
|
|
|
|
|
<div class="step-navigation-buttons">
|
|
|
|
|
<el-button @click="prevStep">
|
|
|
|
|
<el-icon class="el-icon--left"><arrow-left /></el-icon>
|
|
|
|
|
返回
|
|
|
|
|
</el-button>
|
|
|
|
|
<el-button
|
|
|
|
|
v-if="activeStep < 4 && stepResults[activeStep]"
|
|
|
|
|
type="primary"
|
|
|
|
|
@click="nextStep">
|
|
|
|
|
下一步
|
|
|
|
|
<el-icon class="el-icon--right"><arrow-right /></el-icon>
|
|
|
|
|
</el-button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- 步骤4:单词纠错 -->
|
|
|
|
|
<div v-if="activeStep === 3" class="process-step">
|
|
|
|
|
<div class="step-header">
|
|
|
|
|
<h2>单词纠错</h2>
|
|
|
|
|
<p>检测并修正数据中的拼写错误和语法问题。</p>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="step-body">
|
|
|
|
|
<div class="settings-panel">
|
|
|
|
|
<h3>纠错选项</h3>
|
|
|
|
|
<el-form label-position="top">
|
|
|
|
|
<el-form-item label="拼写检查">
|
|
|
|
|
<el-switch v-model="spellCheckSettings.enabled" />
|
|
|
|
|
</el-form-item>
|
|
|
|
|
<el-form-item label="语法检查">
|
|
|
|
|
<el-switch v-model="spellCheckSettings.grammarCheck" />
|
|
|
|
|
</el-form-item>
|
|
|
|
|
<el-form-item label="专业术语库">
|
|
|
|
|
<el-select v-model="spellCheckSettings.termBase" placeholder="选择术语库">
|
|
|
|
|
<el-option label="航空术语" value="aviation"></el-option>
|
|
|
|
|
<el-option label="客服对话" value="customer-service"></el-option>
|
|
|
|
|
<el-option label="通用术语" value="general"></el-option>
|
|
|
|
|
</el-select>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
</el-form>
|
|
|
|
|
|
|
|
|
|
<!-- 新增处理按钮 -->
|
|
|
|
|
<div class="action-buttons">
|
|
|
|
|
<el-button
|
|
|
|
|
type="primary"
|
|
|
|
|
@click="processSpellCheck"
|
|
|
|
|
:loading="processing">
|
|
|
|
|
处理数据
|
|
|
|
|
</el-button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="result-panel" v-if="stepResults[3]">
|
|
|
|
|
<h3>纠错结果</h3>
|
|
|
|
|
<el-alert
|
|
|
|
|
title="单词纠错完成"
|
|
|
|
|
type="success"
|
|
|
|
|
:closable="false"
|
|
|
|
|
show-icon>
|
|
|
|
|
<div class="result-summary">
|
|
|
|
|
<p>处理时间:{{ new Date().toLocaleString() }}</p>
|
|
|
|
|
<p>纠正错误:{{ Math.floor(Math.random() * 40) + 15 }}</p>
|
|
|
|
|
<p>不确定项:{{ Math.floor(Math.random() * 10) + 2 }}</p>
|
|
|
|
|
</div>
|
|
|
|
|
</el-alert>
|
|
|
|
|
|
|
|
|
|
<div class="correction-list">
|
|
|
|
|
<h4>错误修正列表</h4>
|
|
|
|
|
<el-table
|
|
|
|
|
:data="correctionSamples"
|
|
|
|
|
border
|
|
|
|
|
style="width: 100%">
|
|
|
|
|
<el-table-column prop="original" label="原文" width="180"></el-table-column>
|
|
|
|
|
<el-table-column prop="corrected" label="修正" width="180"></el-table-column>
|
|
|
|
|
<el-table-column prop="type" label="错误类型"></el-table-column>
|
|
|
|
|
<el-table-column prop="confidence" label="置信度"></el-table-column>
|
|
|
|
|
</el-table>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- 步骤导航按钮 - 添加在步骤内容底部 -->
|
|
|
|
|
<div class="step-navigation-buttons">
|
|
|
|
|
<el-button @click="prevStep">
|
|
|
|
|
<el-icon class="el-icon--left"><arrow-left /></el-icon>
|
|
|
|
|
返回
|
|
|
|
|
</el-button>
|
|
|
|
|
<el-button
|
|
|
|
|
v-if="activeStep < 4 && stepResults[activeStep]"
|
|
|
|
|
type="primary"
|
|
|
|
|
@click="nextStep">
|
|
|
|
|
下一步
|
|
|
|
|
<el-icon class="el-icon--right"><arrow-right /></el-icon>
|
|
|
|
|
</el-button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- 步骤5:大模型分析 -->
|
|
|
|
|
<div v-if="activeStep === 4" class="process-step">
|
|
|
|
|
<div class="step-header">
|
|
|
|
|
<h2>大模型分析</h2>
|
|
|
|
|
<p>利用大语言模型对对话数据进行深度分析,提取关键信息并生成洞察。</p>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="step-body">
|
|
|
|
|
<div class="settings-panel">
|
|
|
|
|
<h3>分析选项</h3>
|
|
|
|
|
<el-form label-position="top">
|
|
|
|
|
<el-form-item label="分析模型">
|
|
|
|
|
<el-select v-model="analysisSettings.model" placeholder="选择模型">
|
|
|
|
|
<el-option label="基础分析模型" value="basic"></el-option>
|
|
|
|
|
<el-option label="高级分析模型" value="advanced"></el-option>
|
|
|
|
|
<el-option label="专业航空模型" value="aviation-pro"></el-option>
|
|
|
|
|
</el-select>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
<el-form-item label="分析深度">
|
|
|
|
|
<el-slider v-model="analysisSettings.depth" :step="1" :min="1" :max="5" show-stops></el-slider>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
<el-form-item label="分析维度">
|
|
|
|
|
<el-checkbox-group v-model="analysisSettings.dimensions">
|
|
|
|
|
<el-checkbox label="情感分析"></el-checkbox>
|
|
|
|
|
<el-checkbox label="关键信息提取"></el-checkbox>
|
|
|
|
|
<el-checkbox label="问题分类"></el-checkbox>
|
|
|
|
|
<el-checkbox label="客户满意度评估"></el-checkbox>
|
|
|
|
|
</el-checkbox-group>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
</el-form>
|
|
|
|
|
|
|
|
|
|
<!-- 新增处理按钮 -->
|
|
|
|
|
<div class="action-buttons">
|
|
|
|
|
<el-button
|
|
|
|
|
type="primary"
|
|
|
|
|
@click="processAnalysis"
|
|
|
|
|
:loading="processing">
|
|
|
|
|
处理数据
|
|
|
|
|
</el-button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="result-panel" v-if="stepResults[4]">
|
|
|
|
|
<h3>分析结果</h3>
|
|
|
|
|
<el-alert
|
|
|
|
|
title="大模型分析完成"
|
|
|
|
|
type="success"
|
|
|
|
|
:closable="false"
|
|
|
|
|
show-icon>
|
|
|
|
|
<div class="result-summary">
|
|
|
|
|
<p>处理时间:{{ new Date().toLocaleString() }}</p>
|
|
|
|
|
<p>分析条目:{{ Math.floor(Math.random() * 50) + 30 }}</p>
|
|
|
|
|
<p>生成洞察:{{ Math.floor(Math.random() * 15) + 5 }}</p>
|
|
|
|
|
</div>
|
|
|
|
|
</el-alert>
|
|
|
|
|
|
|
|
|
|
<div class="analysis-results">
|
|
|
|
|
<el-tabs type="border-card">
|
|
|
|
|
<el-tab-pane label="分析摘要">
|
|
|
|
|
<div class="analysis-summary">
|
|
|
|
|
<h4>主要发现</h4>
|
|
|
|
|
<p>通过对{{Math.floor(Math.random() * 50) + 30}}条航班对话数据的分析,我们发现以下主要问题:</p>
|
|
|
|
|
<ul>
|
|
|
|
|
<li>航班延误是客户投诉的主要原因(占比约{{Math.floor(Math.random() * 30) + 20}}%)</li>
|
|
|
|
|
<li>行李问题是第二大投诉点(占比约{{Math.floor(Math.random() * 20) + 10}}%)</li>
|
|
|
|
|
<li>客服回应速度和航班信息更新及时性是改进重点</li>
|
|
|
|
|
</ul>
|
|
|
|
|
|
|
|
|
|
<h4>建议改进方向</h4>
|
|
|
|
|
<ul>
|
|
|
|
|
<li>提高航班延误信息的透明度和及时性</li>
|
|
|
|
|
<li>改进行李追踪系统,提供更精确的行李状态信息</li>
|
|
|
|
|
<li>优化客服培训,提高问题解决效率</li>
|
|
|
|
|
</ul>
|
|
|
|
|
</div>
|
|
|
|
|
</el-tab-pane>
|
|
|
|
|
<el-tab-pane label="情感分析">
|
|
|
|
|
<div class="sentiment-chart">
|
|
|
|
|
<h4>客户情感分布</h4>
|
|
|
|
|
<div class="chart-placeholder">
|
|
|
|
|
<!-- 这里将来放置图表组件 -->
|
|
|
|
|
<div class="mock-chart">
|
|
|
|
|
<div class="chart-bar positive" style="width: 30%">正面 30%</div>
|
|
|
|
|
<div class="chart-bar neutral" style="width: 40%">中性 40%</div>
|
|
|
|
|
<div class="chart-bar negative" style="width: 30%">负面 30%</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</el-tab-pane>
|
|
|
|
|
<el-tab-pane label="关键词统计">
|
|
|
|
|
<div class="keyword-stats">
|
|
|
|
|
<h4>高频关键词</h4>
|
|
|
|
|
<div class="keyword-cloud">
|
|
|
|
|
<span class="keyword-item" style="font-size: 24px; color: #409EFF">延误</span>
|
|
|
|
|
<span class="keyword-item" style="font-size: 20px; color: #67C23A">行李</span>
|
|
|
|
|
<span class="keyword-item" style="font-size: 18px; color: #E6A23C">退票</span>
|
|
|
|
|
<span class="keyword-item" style="font-size: 22px; color: #F56C6C">航班</span>
|
|
|
|
|
<span class="keyword-item" style="font-size: 16px; color: #909399">补偿</span>
|
|
|
|
|
<span class="keyword-item" style="font-size: 19px; color: #409EFF">更改</span>
|
|
|
|
|
<span class="keyword-item" style="font-size: 15px; color: #67C23A">座位</span>
|
|
|
|
|
<span class="keyword-item" style="font-size: 21px; color: #E6A23C">客服</span>
|
|
|
|
|
<span class="keyword-item" style="font-size: 17px; color: #F56C6C">信息</span>
|
|
|
|
|
<span class="keyword-item" style="font-size: 23px; color: #909399">问题</span>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</el-tab-pane>
|
|
|
|
|
</el-tabs>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- 步骤导航按钮 - 添加在步骤内容底部 -->
|
|
|
|
|
<div class="step-navigation-buttons">
|
|
|
|
|
<el-button @click="prevStep">
|
|
|
|
|
<el-icon class="el-icon--left"><arrow-left /></el-icon>
|
|
|
|
|
返回
|
|
|
|
|
</el-button>
|
|
|
|
|
<el-button
|
|
|
|
|
v-if="activeStep === 4 && stepResults[4]"
|
|
|
|
|
type="success"
|
|
|
|
|
@click="exportFinalResults">
|
|
|
|
|
导出分析结果
|
|
|
|
|
<el-icon class="el-icon--right"><download /></el-icon>
|
|
|
|
|
</el-button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- 底部步骤指示器 -->
|
|
|
|
|
<div class="bottom-steps-container">
|
|
|
|
|
<div class="steps-background">
|
|
|
|
|
<div class="steps-wrapper">
|
|
|
|
|
<!-- 重新设计的步骤流程 -->
|
|
|
|
|
<div class="steps-flow-container">
|
|
|
|
|
<!-- 步骤1 -->
|
|
|
|
|
<div class="step-item-container">
|
|
|
|
|
<div class="step-circle" :class="{ 'active': activeStep === 0, 'completed': stepResults[0] }">
|
|
|
|
|
<div class="ripple-effect" v-if="activeStep === 0"></div>
|
|
|
|
|
1
|
|
|
|
|
</div>
|
|
|
|
|
<div class="step-button-box" :class="{ 'active': activeStep === 0, 'completed': stepResults[0] }" @click="handleFileUploadClick">
|
|
|
|
|
<el-icon><upload /></el-icon>
|
|
|
|
|
<div class="step-text">上传文件</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- 连接线 -->
|
|
|
|
|
<div class="step-connector" :class="{ 'active-connector': stepResults[0] }"></div>
|
|
|
|
|
|
|
|
|
|
<!-- 步骤2 -->
|
|
|
|
|
<div class="step-item-container">
|
|
|
|
|
<div class="step-circle" :class="{ 'active': activeStep === 1, 'completed': stepResults[1] }">
|
|
|
|
|
<div class="ripple-effect" v-if="activeStep === 1"></div>
|
|
|
|
|
2
|
|
|
|
|
</div>
|
|
|
|
|
<div class="step-button-box" :class="{ 'active': activeStep === 1, 'completed': stepResults[1], 'disabled': !stepResults[0] }" @click="goToStep(1)">
|
|
|
|
|
<div class="step-text">预处理</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- 连接线 -->
|
|
|
|
|
<div class="step-connector" :class="{ 'active-connector': stepResults[1] }"></div>
|
|
|
|
|
|
|
|
|
|
<!-- 步骤3 -->
|
|
|
|
|
<div class="step-item-container">
|
|
|
|
|
<div class="step-circle" :class="{ 'active': activeStep === 2, 'completed': stepResults[2] }">
|
|
|
|
|
<div class="ripple-effect" v-if="activeStep === 2"></div>
|
|
|
|
|
3
|
|
|
|
|
</div>
|
|
|
|
|
<div class="step-button-box" :class="{ 'active': activeStep === 2, 'completed': stepResults[2], 'disabled': !stepResults[1] }" @click="goToStep(2)">
|
|
|
|
|
<div class="step-text">合并格式</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- 连接线 -->
|
|
|
|
|
<div class="step-connector" :class="{ 'active-connector': stepResults[2] }"></div>
|
|
|
|
|
|
|
|
|
|
<!-- 步骤4 -->
|
|
|
|
|
<div class="step-item-container">
|
|
|
|
|
<div class="step-circle" :class="{ 'active': activeStep === 3, 'completed': stepResults[3] }">
|
|
|
|
|
<div class="ripple-effect" v-if="activeStep === 3"></div>
|
|
|
|
|
4
|
|
|
|
|
</div>
|
|
|
|
|
<div class="step-button-box" :class="{ 'active': activeStep === 3, 'completed': stepResults[3], 'disabled': !stepResults[2] }" @click="goToStep(3)">
|
|
|
|
|
<div class="step-text">单词纠错</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- 连接线 -->
|
|
|
|
|
<div class="step-connector" :class="{ 'active-connector': stepResults[3] }"></div>
|
|
|
|
|
|
|
|
|
|
<!-- 步骤5 -->
|
|
|
|
|
<div class="step-item-container">
|
|
|
|
|
<div class="step-circle" :class="{ 'active': activeStep === 4, 'completed': stepResults[4] }">
|
|
|
|
|
<div class="ripple-effect" v-if="activeStep === 4"></div>
|
|
|
|
|
5
|
|
|
|
|
</div>
|
|
|
|
|
<div class="step-button-box" :class="{ 'active': activeStep === 4, 'completed': stepResults[4], 'disabled': !stepResults[3] }" @click="goToStep(4)">
|
|
|
|
|
<div class="step-text">大模型分析</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script setup>
|
|
|
|
|
import { ref, reactive } from 'vue'
|
|
|
|
|
import { ElMessage, ElLoading } from 'element-plus'
|
|
|
|
|
import { ArrowLeft, ArrowRight, Upload, Download } from '@element-plus/icons-vue'
|
|
|
|
|
import FileUpload from './FileUpload.vue'
|
|
|
|
|
|
|
|
|
|
// 数据定义
|
|
|
|
|
const activeStep = ref(0)
|
|
|
|
|
const fileData = ref(null)
|
|
|
|
|
const fileType = ref(null)
|
|
|
|
|
const processing = ref(false)
|
|
|
|
|
|
|
|
|
|
// 步骤完成状态
|
|
|
|
|
const stepResults = reactive({
|
|
|
|
|
0: false,
|
|
|
|
|
1: false,
|
|
|
|
|
2: false,
|
|
|
|
|
3: false,
|
|
|
|
|
4: false
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// 预处理设置
|
|
|
|
|
const preprocessSettings = reactive({
|
|
|
|
|
removeEmptyLines: true,
|
|
|
|
|
standardizeDates: true,
|
|
|
|
|
removeSpecialChars: false
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// 合并设置
|
|
|
|
|
const mergeSettings = reactive({
|
|
|
|
|
targetFormat: 'standard',
|
|
|
|
|
mergeSimilarItems: true,
|
|
|
|
|
generateUniqueIds: true
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// 拼写检查设置
|
|
|
|
|
const spellCheckSettings = reactive({
|
|
|
|
|
enabled: true,
|
|
|
|
|
grammarCheck: true,
|
|
|
|
|
termBase: 'aviation'
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// 分析设置
|
|
|
|
|
const analysisSettings = reactive({
|
|
|
|
|
model: 'basic',
|
|
|
|
|
depth: 3,
|
|
|
|
|
dimensions: ['情感分析', '关键信息提取']
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// 示例数据
|
|
|
|
|
const sampleColumns = [
|
|
|
|
|
{ prop: 'flightNo', label: '航班号', width: '120' },
|
|
|
|
|
{ prop: 'date', label: '日期', width: '120' },
|
|
|
|
|
{ prop: 'customerQuery', label: '客户问题', width: '250' },
|
|
|
|
|
{ prop: 'agentResponse', label: '客服回复', width: '250' },
|
|
|
|
|
{ prop: 'category', label: '问题类别', width: '120' }
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
const sampleData = [
|
|
|
|
|
{
|
|
|
|
|
flightNo: 'CA1234',
|
|
|
|
|
date: '2023-06-15',
|
|
|
|
|
customerQuery: '我的航班什么时候起飞?',
|
|
|
|
|
agentResponse: '您好,CA1234航班计划于15:30起飞。',
|
|
|
|
|
category: '航班信息'
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
flightNo: 'MU5678',
|
|
|
|
|
date: '2023-06-15',
|
|
|
|
|
customerQuery: '我的行李还没到,怎么办?',
|
|
|
|
|
agentResponse: '很抱歉,我们会立即为您查询行李状态。',
|
|
|
|
|
category: '行李问题'
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
flightNo: 'CZ9012',
|
|
|
|
|
date: '2023-06-16',
|
|
|
|
|
customerQuery: '这个航班延误了,我能改签吗?',
|
|
|
|
|
agentResponse: '可以的,我们可以为您免费改签今天的其他航班。',
|
|
|
|
|
category: '航班延误'
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
const correctionSamples = [
|
|
|
|
|
{ original: 'filght', corrected: 'flight', type: '拼写错误', confidence: '98%' },
|
|
|
|
|
{ original: 'customar', corrected: 'customer', type: '拼写错误', confidence: '95%' },
|
|
|
|
|
{ original: 'dely', corrected: 'delay', type: '拼写错误', confidence: '97%' },
|
|
|
|
|
{ original: 'bagage', corrected: 'baggage', type: '拼写错误', confidence: '99%' },
|
|
|
|
|
{ original: 'tickit', corrected: 'ticket', type: '拼写错误', confidence: '96%' }
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
// 文件就绪回调
|
|
|
|
|
const onFileReady = (data) => {
|
|
|
|
|
fileData.value = data.fileData
|
|
|
|
|
fileType.value = data.fileType
|
|
|
|
|
stepResults[0] = true
|
|
|
|
|
ElMessage.success('文件准备就绪,可以进行下一步操作')
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 下一步
|
|
|
|
|
const nextStep = () => {
|
|
|
|
|
if (activeStep.value < 4 && stepResults[activeStep.value]) {
|
|
|
|
|
activeStep.value += 1
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 上一步
|
|
|
|
|
const prevStep = () => {
|
|
|
|
|
if (activeStep.value > 0) {
|
|
|
|
|
// 保存当前步骤,用于后续重置
|
|
|
|
|
const currentStep = activeStep.value
|
|
|
|
|
|
|
|
|
|
// 转到上一步
|
|
|
|
|
activeStep.value -= 1
|
|
|
|
|
|
|
|
|
|
// 重置后续步骤的完成状态
|
|
|
|
|
for (let i = currentStep; i <= 4; i++) {
|
|
|
|
|
stepResults[i] = false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 前往特定步骤
|
|
|
|
|
const goToStep = (step) => {
|
|
|
|
|
// 只能前往已完成前一步骤的步骤
|
|
|
|
|
if (step === 0 || (step > 0 && stepResults[step-1])) {
|
|
|
|
|
activeStep.value = step
|
|
|
|
|
} else {
|
|
|
|
|
ElMessage.warning('请先完成前一步骤')
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 处理文件上传点击
|
|
|
|
|
const handleFileUploadClick = () => {
|
|
|
|
|
activeStep.value = 0
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 处理预处理
|
|
|
|
|
const processPreprocess = () => {
|
|
|
|
|
simulateProcessing('正在进行预处理...', () => {
|
|
|
|
|
stepResults[1] = true
|
|
|
|
|
ElMessage.success('预处理完成!')
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 处理格式合并
|
|
|
|
|
const processMergeFormat = () => {
|
|
|
|
|
simulateProcessing('正在合并格式...', () => {
|
|
|
|
|
stepResults[2] = true
|
|
|
|
|
ElMessage.success('格式合并完成!')
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 处理单词纠错
|
|
|
|
|
const processSpellCheck = () => {
|
|
|
|
|
simulateProcessing('正在进行单词纠错...', () => {
|
|
|
|
|
stepResults[3] = true
|
|
|
|
|
ElMessage.success('单词纠错完成!')
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 处理大模型分析
|
|
|
|
|
const processAnalysis = () => {
|
|
|
|
|
simulateProcessing('正在进行大模型分析,这可能需要一些时间...', () => {
|
|
|
|
|
stepResults[4] = true
|
|
|
|
|
ElMessage.success('大模型分析完成!')
|
|
|
|
|
}, 3000)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 模拟处理过程
|
|
|
|
|
const simulateProcessing = (loadingText, callback, duration = 2000) => {
|
|
|
|
|
processing.value = true
|
|
|
|
|
|
|
|
|
|
const loadingInstance = ElLoading.service({
|
|
|
|
|
lock: true,
|
|
|
|
|
text: loadingText,
|
|
|
|
|
background: 'rgba(0, 0, 0, 0.7)'
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// 模拟处理时间
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
loadingInstance.close()
|
|
|
|
|
processing.value = false
|
|
|
|
|
if (callback) callback()
|
|
|
|
|
}, duration)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 导出最终结果
|
|
|
|
|
const exportFinalResults = () => {
|
|
|
|
|
ElMessage.success('分析结果已导出')
|
|
|
|
|
}
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
|
|
|
|
.main-page {
|
|
|
|
|
max-width: 1200px;
|
|
|
|
|
margin: 0 auto;
|
|
|
|
|
padding: 20px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.header {
|
|
|
|
|
text-align: center;
|
|
|
|
|
margin-bottom: 30px;
|
|
|
|
|
background-color: rgba(255, 255, 255, 0.9);
|
|
|
|
|
padding: 15px;
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.header h1 {
|
|
|
|
|
color: #0066FF;
|
|
|
|
|
margin-bottom: 8px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.subtitle {
|
|
|
|
|
color: #666;
|
|
|
|
|
font-size: 16px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.step-content {
|
|
|
|
|
background: rgba(255, 255, 255, 0.95);
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
padding: 30px;
|
|
|
|
|
margin-bottom: 40px;
|
|
|
|
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
|
|
|
|
|
min-height: 500px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.step-header {
|
|
|
|
|
margin-bottom: 20px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.step-header h2 {
|
|
|
|
|
color: #0066FF;
|
|
|
|
|
margin-bottom: 8px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.step-body {
|
|
|
|
|
display: flex;
|
|
|
|
|
gap: 30px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.settings-panel {
|
|
|
|
|
flex: 1;
|
|
|
|
|
max-width: 300px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.result-panel {
|
|
|
|
|
flex: 2;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.action-buttons {
|
|
|
|
|
margin-top: 20px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.result-summary {
|
|
|
|
|
margin: 10px 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.data-preview {
|
|
|
|
|
margin-top: 20px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.step-navigation-buttons {
|
|
|
|
|
margin-top: 30px;
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.next-button {
|
|
|
|
|
margin-left: auto;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 步骤流程样式 */
|
|
|
|
|
.bottom-steps-container {
|
|
|
|
|
margin-top: 30px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.steps-background {
|
|
|
|
|
background-color: rgba(230, 236, 245, 0.95);
|
|
|
|
|
padding: 20px;
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.steps-wrapper {
|
|
|
|
|
max-width: 1000px;
|
|
|
|
|
margin: 0 auto;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.steps-flow-container {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.step-item-container {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
align-items: center;
|
|
|
|
|
position: relative;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.step-circle {
|
|
|
|
|
width: 50px;
|
|
|
|
|
height: 50px;
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
background-color: #bbc6d6;
|
|
|
|
|
color: white;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
font-weight: bold;
|
|
|
|
|
font-size: 20px;
|
|
|
|
|
position: relative;
|
|
|
|
|
z-index: 2;
|
|
|
|
|
margin-bottom: 10px;
|
|
|
|
|
transition: all 0.3s;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.step-circle.active {
|
|
|
|
|
background-color: #0055dd;
|
|
|
|
|
transform: scale(1.2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.step-circle.completed {
|
|
|
|
|
background-color: #55aa22;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.ripple-effect {
|
|
|
|
|
position: absolute;
|
|
|
|
|
top: -8px;
|
|
|
|
|
left: -8px;
|
|
|
|
|
right: -8px;
|
|
|
|
|
bottom: -8px;
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
border: 2px solid #0066FF;
|
|
|
|
|
animation: ripple 2s infinite;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@keyframes ripple {
|
|
|
|
|
0% {
|
|
|
|
|
transform: scale(1);
|
|
|
|
|
opacity: 1;
|
|
|
|
|
}
|
|
|
|
|
100% {
|
|
|
|
|
transform: scale(1.3);
|
|
|
|
|
opacity: 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.step-button-box {
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
text-align: center;
|
|
|
|
|
padding: 15px;
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
transition: all 0.3s;
|
|
|
|
|
width: 120px;
|
|
|
|
|
background-color: rgba(220, 230, 245, 0.8);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.step-button-box.active {
|
|
|
|
|
background-color: #daebff;
|
|
|
|
|
color: #0055dd;
|
|
|
|
|
font-weight: bold;
|
|
|
|
|
box-shadow: 0 2px 8px rgba(0, 100, 255, 0.2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.step-button-box.completed {
|
|
|
|
|
color: #55aa22;
|
|
|
|
|
background-color: rgba(230, 245, 230, 0.9);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.step-button-box.disabled {
|
|
|
|
|
opacity: 0.5;
|
|
|
|
|
cursor: not-allowed;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.step-text {
|
|
|
|
|
margin-top: 5px;
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.step-connector {
|
|
|
|
|
flex-grow: 1;
|
|
|
|
|
height: 3px;
|
|
|
|
|
background-color: #bbc6d6;
|
|
|
|
|
margin: 0 10px;
|
|
|
|
|
position: relative;
|
|
|
|
|
z-index: 1;
|
|
|
|
|
transition: background-color 0.3s;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.active-connector {
|
|
|
|
|
background-color: #55aa22;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 分析结果样式 */
|
|
|
|
|
.analysis-summary {
|
|
|
|
|
padding: 16px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.analysis-summary h4 {
|
|
|
|
|
margin-top: 20px;
|
|
|
|
|
margin-bottom: 10px;
|
|
|
|
|
color: #0066FF;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.analysis-summary ul {
|
|
|
|
|
padding-left: 20px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.chart-placeholder {
|
|
|
|
|
height: 100px;
|
|
|
|
|
margin: 20px 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.mock-chart {
|
|
|
|
|
height: 100%;
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
justify-content: space-around;
|
|
|
|
|
gap: 10px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.chart-bar {
|
|
|
|
|
height: 24px;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
padding-left: 10px;
|
|
|
|
|
color: white;
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.positive {
|
|
|
|
|
background-color: #67C23A;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.neutral {
|
|
|
|
|
background-color: #909399;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.negative {
|
|
|
|
|
background-color: #F56C6C;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.keyword-cloud {
|
|
|
|
|
padding: 20px;
|
|
|
|
|
text-align: center;
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
gap: 10px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.keyword-item {
|
|
|
|
|
display: inline-block;
|
|
|
|
|
padding: 5px 10px;
|
|
|
|
|
border-radius: 20px;
|
|
|
|
|
background-color: #f5f7fa;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.correction-list {
|
|
|
|
|
margin-top: 20px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.correction-list h4 {
|
|
|
|
|
margin-bottom: 10px;
|
|
|
|
|
color: #0066FF;
|
|
|
|
|
}
|
|
|
|
|
</style>
|