|
|
|
@ -1,24 +1,370 @@
|
|
|
|
|
<template>
|
|
|
|
|
<div class="main-container">
|
|
|
|
|
<h1 class="app-title">SVG/Excel 数据处理应用</h1>
|
|
|
|
|
|
|
|
|
|
<!-- 上层区域:文件上传 -->
|
|
|
|
|
<el-card class="upload-section">
|
|
|
|
|
<div slot="header" class="section-header">
|
|
|
|
|
<span>文件上传与准备</span>
|
|
|
|
|
<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>
|
|
|
|
|
|
|
|
|
|
<!-- 步骤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-switch>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
<el-form-item label="标准化日期格式">
|
|
|
|
|
<el-switch v-model="preprocessSettings.standardizeDates"></el-switch>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
<el-form-item label="移除特殊字符">
|
|
|
|
|
<el-switch v-model="preprocessSettings.removeSpecialChars"></el-switch>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
</el-form>
|
|
|
|
|
<el-button type="primary" @click="processStep(1)">开始预处理</el-button>
|
|
|
|
|
</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>
|
|
|
|
|
|
|
|
|
|
<!-- 步骤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-switch>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
<el-form-item label="生成唯一ID">
|
|
|
|
|
<el-switch v-model="mergeSettings.generateUniqueIds"></el-switch>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
</el-form>
|
|
|
|
|
<el-button type="primary" @click="processStep(2)">开始合并</el-button>
|
|
|
|
|
</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>
|
|
|
|
|
|
|
|
|
|
<!-- 步骤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-switch>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
<el-form-item label="语法检查">
|
|
|
|
|
<el-switch v-model="spellCheckSettings.grammarCheck"></el-switch>
|
|
|
|
|
</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>
|
|
|
|
|
<el-button type="primary" @click="processStep(3)">开始纠错</el-button>
|
|
|
|
|
</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>
|
|
|
|
|
<file-upload @file-ready="onFileReady" />
|
|
|
|
|
</el-card>
|
|
|
|
|
|
|
|
|
|
<!-- 下层区域:多步骤处理流程(将在后续实现) -->
|
|
|
|
|
<el-card class="process-section" v-if="fileReady">
|
|
|
|
|
<div slot="header" class="section-header">
|
|
|
|
|
<span>数据处理流程</span>
|
|
|
|
|
|
|
|
|
|
<!-- 步骤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>
|
|
|
|
|
<el-button type="primary" @click="processStep(4)">开始分析</el-button>
|
|
|
|
|
</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>
|
|
|
|
|
<div class="placeholder-text">
|
|
|
|
|
<p>文件处理准备就绪,后续步骤将在下一阶段实现。</p>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- 底部步骤指示器 -->
|
|
|
|
|
<div class="bottom-steps-container">
|
|
|
|
|
<div class="steps-background">
|
|
|
|
|
<div class="steps-wrapper">
|
|
|
|
|
<!-- 新的步骤指示器样式 -->
|
|
|
|
|
<div class="steps-flow">
|
|
|
|
|
<!-- 步骤1 -->
|
|
|
|
|
<div class="step-circle" :class="{ 'active': activeStep >= 0, 'completed': stepResults[0] }">1</div>
|
|
|
|
|
<!-- 连接线 -->
|
|
|
|
|
<div class="step-connector"></div>
|
|
|
|
|
<!-- 步骤2 -->
|
|
|
|
|
<div class="step-circle" :class="{ 'active': activeStep >= 1, 'completed': stepResults[1] }">2</div>
|
|
|
|
|
<!-- 连接线 -->
|
|
|
|
|
<div class="step-connector"></div>
|
|
|
|
|
<!-- 步骤3 -->
|
|
|
|
|
<div class="step-circle" :class="{ 'active': activeStep >= 2, 'completed': stepResults[2] }">3</div>
|
|
|
|
|
<!-- 连接线 -->
|
|
|
|
|
<div class="step-connector"></div>
|
|
|
|
|
<!-- 步骤4 -->
|
|
|
|
|
<div class="step-circle" :class="{ 'active': activeStep >= 3, 'completed': stepResults[3] }">4</div>
|
|
|
|
|
<!-- 连接线 -->
|
|
|
|
|
<div class="step-connector"></div>
|
|
|
|
|
<!-- 步骤5 -->
|
|
|
|
|
<div class="step-circle" :class="{ 'active': activeStep >= 4, 'completed': stepResults[4] }">5</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- 按钮区域 -->
|
|
|
|
|
<div class="step-buttons-container">
|
|
|
|
|
<!-- 步骤1按钮 -->
|
|
|
|
|
<div class="step-button-wrapper">
|
|
|
|
|
<div class="step-button" :class="{ 'active': activeStep === 0 }" @click="goToStep(0)">
|
|
|
|
|
<div class="btn-icon"><i class="el-icon-upload2"></i></div>
|
|
|
|
|
<div class="btn-text">上传文件</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- 步骤2按钮 -->
|
|
|
|
|
<div class="step-button-wrapper">
|
|
|
|
|
<div class="step-button" :class="{ 'active': activeStep === 1, 'disabled': !stepResults[0] }" @click="goToStep(1)">
|
|
|
|
|
<div class="btn-text">预处理</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- 步骤3按钮 -->
|
|
|
|
|
<div class="step-button-wrapper">
|
|
|
|
|
<div class="step-button" :class="{ 'active': activeStep === 2, 'disabled': !stepResults[1] }" @click="goToStep(2)">
|
|
|
|
|
<div class="btn-text">合并格式</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- 步骤4按钮 -->
|
|
|
|
|
<div class="step-button-wrapper">
|
|
|
|
|
<div class="step-button" :class="{ 'active': activeStep === 3, 'disabled': !stepResults[2] }" @click="goToStep(3)">
|
|
|
|
|
<div class="btn-text">单词纠错</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- 步骤5按钮 -->
|
|
|
|
|
<div class="step-button-wrapper">
|
|
|
|
|
<div class="step-button" :class="{ 'active': activeStep === 4, 'disabled': !stepResults[3] }" @click="goToStep(4)">
|
|
|
|
|
<div class="btn-text">大模型分析</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</el-card>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- 只保留返回按钮和导出结果按钮 -->
|
|
|
|
|
<div class="step-navigation">
|
|
|
|
|
<el-button v-if="activeStep > 0" icon="el-icon-arrow-left" @click="prevStep">返回</el-button>
|
|
|
|
|
<el-button v-if="activeStep === 4 && stepResults[4]" type="success" icon="el-icon-download" @click="exportFinalResults">导出分析结果</el-button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
@ -32,53 +378,450 @@ export default {
|
|
|
|
|
},
|
|
|
|
|
data() {
|
|
|
|
|
return {
|
|
|
|
|
fileReady: false,
|
|
|
|
|
activeStep: 0,
|
|
|
|
|
fileData: null,
|
|
|
|
|
fileType: null
|
|
|
|
|
fileType: null,
|
|
|
|
|
stepResults: {
|
|
|
|
|
0: false,
|
|
|
|
|
1: false,
|
|
|
|
|
2: false,
|
|
|
|
|
3: false,
|
|
|
|
|
4: false
|
|
|
|
|
},
|
|
|
|
|
preprocessSettings: {
|
|
|
|
|
removeEmptyLines: true,
|
|
|
|
|
standardizeDates: true,
|
|
|
|
|
removeSpecialChars: false
|
|
|
|
|
},
|
|
|
|
|
mergeSettings: {
|
|
|
|
|
targetFormat: 'standard',
|
|
|
|
|
mergeSimilarItems: true,
|
|
|
|
|
generateUniqueIds: true
|
|
|
|
|
},
|
|
|
|
|
spellCheckSettings: {
|
|
|
|
|
enabled: true,
|
|
|
|
|
grammarCheck: true,
|
|
|
|
|
termBase: 'aviation'
|
|
|
|
|
},
|
|
|
|
|
analysisSettings: {
|
|
|
|
|
model: 'basic',
|
|
|
|
|
depth: 3,
|
|
|
|
|
dimensions: ['情感分析', '关键信息提取']
|
|
|
|
|
},
|
|
|
|
|
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' }
|
|
|
|
|
],
|
|
|
|
|
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: '航班延误'
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
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%' }
|
|
|
|
|
]
|
|
|
|
|
};
|
|
|
|
|
},
|
|
|
|
|
computed: {
|
|
|
|
|
canGoNext() {
|
|
|
|
|
return this.stepResults[this.activeStep];
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
methods: {
|
|
|
|
|
onFileReady(data) {
|
|
|
|
|
this.fileReady = true;
|
|
|
|
|
this.fileData = data.fileData;
|
|
|
|
|
this.fileType = data.fileType;
|
|
|
|
|
this.$message.success('文件已准备好,可以开始处理');
|
|
|
|
|
this.stepResults[0] = true;
|
|
|
|
|
this.$message.success('文件准备就绪,可以进行下一步操作');
|
|
|
|
|
},
|
|
|
|
|
nextStep() {
|
|
|
|
|
if (this.activeStep < 4 && this.stepResults[this.activeStep]) {
|
|
|
|
|
this.activeStep += 1;
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
prevStep() {
|
|
|
|
|
if (this.activeStep > 0) {
|
|
|
|
|
this.activeStep -= 1;
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
goToStep(step) {
|
|
|
|
|
// 只能前往已完成前一步骤的步骤
|
|
|
|
|
if (step === 0 || (step > 0 && this.stepResults[step-1])) {
|
|
|
|
|
this.activeStep = step;
|
|
|
|
|
|
|
|
|
|
// 如果是前往第一步但尚未上传文件,显示文件上传对话框
|
|
|
|
|
if (step === 0 && !this.stepResults[0]) {
|
|
|
|
|
this.openUploadDialog();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 如果前往的步骤尚未完成处理,可以自动触发处理
|
|
|
|
|
if (step > 0 && !this.stepResults[step]) {
|
|
|
|
|
this.processStep(step);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
this.$message.warning('请先完成前一步骤');
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
processStep(step) {
|
|
|
|
|
// 模拟处理过程
|
|
|
|
|
const loadingInstance = this.$loading({
|
|
|
|
|
lock: true,
|
|
|
|
|
text: this.getLoadingText(step),
|
|
|
|
|
spinner: 'el-icon-loading',
|
|
|
|
|
background: 'rgba(0, 0, 0, 0.7)'
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 模拟处理时间
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
loadingInstance.close();
|
|
|
|
|
this.stepResults[step] = true;
|
|
|
|
|
this.$message.success(this.getSuccessMessage(step));
|
|
|
|
|
}, 2000);
|
|
|
|
|
},
|
|
|
|
|
getLoadingText(step) {
|
|
|
|
|
const texts = {
|
|
|
|
|
1: '正在进行预处理...',
|
|
|
|
|
2: '正在合并格式...',
|
|
|
|
|
3: '正在进行单词纠错...',
|
|
|
|
|
4: '正在进行大模型分析,这可能需要一些时间...'
|
|
|
|
|
};
|
|
|
|
|
return texts[step] || '处理中...';
|
|
|
|
|
},
|
|
|
|
|
getSuccessMessage(step) {
|
|
|
|
|
const messages = {
|
|
|
|
|
1: '预处理完成!',
|
|
|
|
|
2: '格式合并完成!',
|
|
|
|
|
3: '单词纠错完成!',
|
|
|
|
|
4: '大模型分析完成!'
|
|
|
|
|
};
|
|
|
|
|
return messages[step] || '处理完成!';
|
|
|
|
|
},
|
|
|
|
|
exportFinalResults() {
|
|
|
|
|
this.$message.success('分析结果已导出为CSV文件');
|
|
|
|
|
},
|
|
|
|
|
openUploadDialog() {
|
|
|
|
|
// 这个方法用于在底部步骤中点击"选择文件"按钮时触发上传文件功能
|
|
|
|
|
// 在实际应用中,可能需要调用子组件的方法或显示一个文件选择对话框
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
|
|
|
|
.main-container {
|
|
|
|
|
max-width: 1200px;
|
|
|
|
|
margin: 0 auto;
|
|
|
|
|
padding: 20px;
|
|
|
|
|
.main-page {
|
|
|
|
|
min-height: 100vh;
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
background: linear-gradient(180deg, #e6f7ff 0%, #ffffff 100%);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.app-title {
|
|
|
|
|
.header {
|
|
|
|
|
text-align: center;
|
|
|
|
|
padding: 40px 0 20px;
|
|
|
|
|
background: linear-gradient(135deg, #1976d2 0%, #2196f3 100%);
|
|
|
|
|
color: white;
|
|
|
|
|
margin-bottom: 30px;
|
|
|
|
|
color: #409EFF;
|
|
|
|
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.header h1 {
|
|
|
|
|
font-size: 32px;
|
|
|
|
|
margin: 0 0 10px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.subtitle {
|
|
|
|
|
font-size: 16px;
|
|
|
|
|
opacity: 0.9;
|
|
|
|
|
margin: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.step-content {
|
|
|
|
|
flex: 1;
|
|
|
|
|
max-width: 1200px;
|
|
|
|
|
margin: 0 auto;
|
|
|
|
|
padding: 0 20px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.upload-section {
|
|
|
|
|
.process-step {
|
|
|
|
|
background-color: white;
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);
|
|
|
|
|
padding: 20px;
|
|
|
|
|
margin-bottom: 30px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.process-section {
|
|
|
|
|
transition: all 0.3s ease;
|
|
|
|
|
.step-header {
|
|
|
|
|
margin-bottom: 20px;
|
|
|
|
|
padding-bottom: 15px;
|
|
|
|
|
border-bottom: 1px solid #ebeef5;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.step-header h2 {
|
|
|
|
|
margin: 0 0 10px;
|
|
|
|
|
color: #303133;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.step-header p {
|
|
|
|
|
margin: 0;
|
|
|
|
|
color: #606266;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.section-header {
|
|
|
|
|
.step-body {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
gap: 20px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.settings-panel {
|
|
|
|
|
flex: 1;
|
|
|
|
|
min-width: 300px;
|
|
|
|
|
padding: 20px;
|
|
|
|
|
background-color: #f5f7fa;
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.settings-panel h3 {
|
|
|
|
|
margin-top: 0;
|
|
|
|
|
margin-bottom: 20px;
|
|
|
|
|
font-size: 18px;
|
|
|
|
|
font-weight: bold;
|
|
|
|
|
color: #303133;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.placeholder-text {
|
|
|
|
|
.result-panel {
|
|
|
|
|
flex: 2;
|
|
|
|
|
min-width: 400px;
|
|
|
|
|
padding: 20px;
|
|
|
|
|
background-color: #f5f7fa;
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.result-panel h3 {
|
|
|
|
|
margin-top: 0;
|
|
|
|
|
margin-bottom: 20px;
|
|
|
|
|
font-size: 18px;
|
|
|
|
|
color: #303133;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.result-summary {
|
|
|
|
|
margin: 10px 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.data-preview {
|
|
|
|
|
margin-top: 20px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.correction-list,
|
|
|
|
|
.analysis-results {
|
|
|
|
|
margin-top: 20px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 底部步骤指示器样式 */
|
|
|
|
|
.bottom-steps-container {
|
|
|
|
|
width: 100%;
|
|
|
|
|
margin-top: 30px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.steps-background {
|
|
|
|
|
background-color: #f0f9ff;
|
|
|
|
|
padding: 30px 0;
|
|
|
|
|
background-image: linear-gradient(to bottom, #87cefa 0%, #1e90ff 100%);
|
|
|
|
|
border-top: 1px solid #d1e9ff;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.steps-wrapper {
|
|
|
|
|
max-width: 1200px;
|
|
|
|
|
margin: 0 auto;
|
|
|
|
|
padding: 20px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 新的流程步骤样式 */
|
|
|
|
|
.steps-flow {
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
align-items: center;
|
|
|
|
|
margin-bottom: 30px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.step-circle {
|
|
|
|
|
width: 60px;
|
|
|
|
|
height: 60px;
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
background-color: rgba(255, 255, 255, 0.5);
|
|
|
|
|
color: white;
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
align-items: center;
|
|
|
|
|
font-size: 28px;
|
|
|
|
|
font-weight: bold;
|
|
|
|
|
position: relative;
|
|
|
|
|
border: 2px solid white;
|
|
|
|
|
transition: all 0.3s;
|
|
|
|
|
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.step-circle.active, .step-circle.completed {
|
|
|
|
|
background-color: #67c23a;
|
|
|
|
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.step-connector {
|
|
|
|
|
height: 2px;
|
|
|
|
|
background-color: rgba(255, 255, 255, 0.7);
|
|
|
|
|
flex-grow: 1;
|
|
|
|
|
max-width: 100px;
|
|
|
|
|
margin: 0 5px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 按钮样式 */
|
|
|
|
|
.step-buttons-container {
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
align-items: flex-start;
|
|
|
|
|
margin-top: 20px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.step-button-wrapper {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
align-items: center;
|
|
|
|
|
width: 180px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.step-button {
|
|
|
|
|
background-color: #f5f7fa;
|
|
|
|
|
border: 1px solid #dcdfe6;
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
padding: 12px 15px;
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
transition: all 0.3s;
|
|
|
|
|
width: 100%;
|
|
|
|
|
height: 50px;
|
|
|
|
|
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.step-button:hover {
|
|
|
|
|
background-color: #ecf5ff;
|
|
|
|
|
border-color: #c6e2ff;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.step-button.active {
|
|
|
|
|
background-color: #ecf5ff;
|
|
|
|
|
border-color: #409eff;
|
|
|
|
|
color: #409eff;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.step-button.disabled {
|
|
|
|
|
cursor: not-allowed;
|
|
|
|
|
opacity: 0.6;
|
|
|
|
|
background-color: #f5f7fa;
|
|
|
|
|
border-color: #dcdfe6;
|
|
|
|
|
color: #c0c4cc;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.btn-icon {
|
|
|
|
|
font-size: 20px;
|
|
|
|
|
margin-bottom: 5px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.btn-text {
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.step-hint {
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
color: rgba(255, 255, 255, 0.8);
|
|
|
|
|
margin-top: 5px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 调整导航按钮位置 */
|
|
|
|
|
.step-navigation {
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
max-width: 1200px;
|
|
|
|
|
margin: 20px auto;
|
|
|
|
|
padding: 0 20px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.chart-placeholder {
|
|
|
|
|
height: 200px;
|
|
|
|
|
border: 1px dashed #dcdfe6;
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
margin-top: 20px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.mock-chart {
|
|
|
|
|
width: 80%;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.chart-bar {
|
|
|
|
|
height: 30px;
|
|
|
|
|
line-height: 30px;
|
|
|
|
|
margin-bottom: 10px;
|
|
|
|
|
color: white;
|
|
|
|
|
text-align: center;
|
|
|
|
|
color: #909399;
|
|
|
|
|
font-style: italic;
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.chart-bar.positive {
|
|
|
|
|
background-color: #67C23A;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.chart-bar.neutral {
|
|
|
|
|
background-color: #E6A23C;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.chart-bar.negative {
|
|
|
|
|
background-color: #F56C6C;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.keyword-cloud {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
gap: 15px;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
align-items: center;
|
|
|
|
|
padding: 20px;
|
|
|
|
|
background-color: #f5f7fa;
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.keyword-item {
|
|
|
|
|
display: inline-block;
|
|
|
|
|
padding: 5px 10px;
|
|
|
|
|
border-radius: 15px;
|
|
|
|
|
background-color: rgba(0,0,0,0.03);
|
|
|
|
|
}
|
|
|
|
|
</style>
|