|
|
|
@ -1,127 +1,134 @@
|
|
|
|
|
<template>
|
|
|
|
|
<div class="data-preprocessing">
|
|
|
|
|
<ProcessStep
|
|
|
|
|
<process-step
|
|
|
|
|
title="数据预处理"
|
|
|
|
|
:status="status"
|
|
|
|
|
:loading="loading"
|
|
|
|
|
:progress="progress"
|
|
|
|
|
operationButtonText="开始数据预处理"
|
|
|
|
|
processingText="正在对数据进行清洗与规范化..."
|
|
|
|
|
errorText="预处理失败,请重试"
|
|
|
|
|
:showPrevButton="true"
|
|
|
|
|
@start="startPreprocess"
|
|
|
|
|
@retry="startPreprocess"
|
|
|
|
|
@prev="$emit('prev')"
|
|
|
|
|
@next="$emit('next')"
|
|
|
|
|
>
|
|
|
|
|
<template #pre-operation>
|
|
|
|
|
<div class="file-info" v-if="fileData">
|
|
|
|
|
<h3>待处理文件信息:</h3>
|
|
|
|
|
<el-descriptions border>
|
|
|
|
|
<el-descriptions-item label="文件名">{{ fileData.fileName }}</el-descriptions-item>
|
|
|
|
|
<el-descriptions-item label="文件大小">{{ formatFileSize(fileData.fileSize) }}</el-descriptions-item>
|
|
|
|
|
<el-descriptions-item label="记录数量">{{ fileData.rowCount }}</el-descriptions-item>
|
|
|
|
|
<el-descriptions-item label="上传时间">{{ formatDateTime(fileData.uploadTime) }}</el-descriptions-item>
|
|
|
|
|
</el-descriptions>
|
|
|
|
|
</div>
|
|
|
|
|
<el-empty v-else description="请先完成文件上传"></el-empty>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<template #operation>
|
|
|
|
|
<div class="preprocessing-config">
|
|
|
|
|
<h3>预处理配置</h3>
|
|
|
|
|
<el-form :model="preprocessConfig" label-width="180px" :disabled="status === 'running'">
|
|
|
|
|
<el-form-item label="数据清洗级别">
|
|
|
|
|
<el-select v-model="preprocessConfig.cleanLevel" placeholder="请选择清洗级别">
|
|
|
|
|
<el-option label="基本清洗" value="basic"></el-option>
|
|
|
|
|
<el-option label="标准清洗" value="standard"></el-option>
|
|
|
|
|
<el-option label="深度清洗" value="deep"></el-option>
|
|
|
|
|
</el-select>
|
|
|
|
|
<div class="preprocessing-options">
|
|
|
|
|
<h4>预处理设置</h4>
|
|
|
|
|
|
|
|
|
|
<el-form label-width="150px" size="small">
|
|
|
|
|
<el-form-item label="删除空行">
|
|
|
|
|
<el-switch v-model="options.removeEmptyRows"></el-switch>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
|
|
|
|
<el-form-item label="去除重复项">
|
|
|
|
|
<el-switch v-model="preprocessConfig.removeDuplicates"></el-switch>
|
|
|
|
|
<el-form-item label="裁剪空白字符">
|
|
|
|
|
<el-switch v-model="options.trimWhitespace"></el-switch>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
|
|
|
|
<el-form-item label="标准化文本">
|
|
|
|
|
<el-switch v-model="preprocessConfig.normalizeText"></el-switch>
|
|
|
|
|
<el-form-item label="标准化日期格式">
|
|
|
|
|
<el-switch v-model="options.normalizeDates"></el-switch>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
|
|
|
|
<el-form-item label="移除缺失值">
|
|
|
|
|
<el-select v-model="preprocessConfig.missingValueStrategy" placeholder="选择处理策略">
|
|
|
|
|
<el-option label="保留" value="keep"></el-option>
|
|
|
|
|
<el-option label="移除行" value="remove"></el-option>
|
|
|
|
|
<el-option label="填充默认值" value="fillDefault"></el-option>
|
|
|
|
|
</el-select>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
|
|
|
|
<el-form-item label="字段选择">
|
|
|
|
|
<el-checkbox-group v-model="preprocessConfig.selectedFields">
|
|
|
|
|
<el-checkbox label="title">标题</el-checkbox>
|
|
|
|
|
<el-checkbox label="content">内容</el-checkbox>
|
|
|
|
|
<el-checkbox label="category">分类</el-checkbox>
|
|
|
|
|
<el-checkbox label="tags">标签</el-checkbox>
|
|
|
|
|
<el-checkbox label="timestamp">时间戳</el-checkbox>
|
|
|
|
|
</el-checkbox-group>
|
|
|
|
|
<el-form-item label="数据类型转换">
|
|
|
|
|
<el-switch v-model="options.convertDataTypes"></el-switch>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
</el-form>
|
|
|
|
|
|
|
|
|
|
<div class="operation-buttons">
|
|
|
|
|
<el-button
|
|
|
|
|
type="primary"
|
|
|
|
|
@click="startPreprocess"
|
|
|
|
|
:disabled="!fileData || status === 'running' || status === 'completed'"
|
|
|
|
|
:loading="status === 'running'"
|
|
|
|
|
>
|
|
|
|
|
{{ status === 'running' ? '处理中...' : '开始预处理' }}
|
|
|
|
|
</el-button>
|
|
|
|
|
|
|
|
|
|
<div class="file-info" v-if="fileData">
|
|
|
|
|
<h4>待处理文件信息</h4>
|
|
|
|
|
<el-descriptions :column="2" border size="small">
|
|
|
|
|
<el-descriptions-item label="文件名称">{{ fileData.fileName }}</el-descriptions-item>
|
|
|
|
|
<el-descriptions-item label="文件大小">{{ fileData.fileSize }}</el-descriptions-item>
|
|
|
|
|
<el-descriptions-item label="总行数">{{ fileData.rowCount }}</el-descriptions-item>
|
|
|
|
|
<el-descriptions-item label="总列数">{{ fileData.columnCount }}</el-descriptions-item>
|
|
|
|
|
</el-descriptions>
|
|
|
|
|
</div>
|
|
|
|
|
<el-empty v-else description="请先完成文件上传"></el-empty>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<template #result-summary v-if="status === 'completed'">
|
|
|
|
|
<div class="preprocessing-summary">
|
|
|
|
|
<h3>预处理完成</h3>
|
|
|
|
|
<el-descriptions border>
|
|
|
|
|
<el-descriptions-item label="处理前记录">{{ fileData.rowCount }}</el-descriptions-item>
|
|
|
|
|
<el-descriptions-item label="处理后记录">{{ processedData.rowCount }}</el-descriptions-item>
|
|
|
|
|
<el-descriptions-item label="移除重复项">{{ preprocessConfig.removeDuplicates ? '是' : '否' }}</el-descriptions-item>
|
|
|
|
|
<el-descriptions-item label="清洗级别">{{ preprocessLevelText }}</el-descriptions-item>
|
|
|
|
|
<el-descriptions-item label="处理用时">{{ processingTime }}秒</el-descriptions-item>
|
|
|
|
|
</el-descriptions>
|
|
|
|
|
|
|
|
|
|
<template #result-summary>
|
|
|
|
|
<div class="process-result-summary">
|
|
|
|
|
<i class="el-icon-success"></i>
|
|
|
|
|
<h4>数据预处理完成</h4>
|
|
|
|
|
<p>原始数据已经过清洗和规范化处理,可进行下一步操作</p>
|
|
|
|
|
|
|
|
|
|
<el-row :gutter="20" class="summary-stats">
|
|
|
|
|
<el-col :span="8">
|
|
|
|
|
<div class="stat-item">
|
|
|
|
|
<p class="stat-value">{{ preprocessedData.processedRows }}</p>
|
|
|
|
|
<p class="stat-label">已处理行数</p>
|
|
|
|
|
</div>
|
|
|
|
|
</el-col>
|
|
|
|
|
<el-col :span="8">
|
|
|
|
|
<div class="stat-item">
|
|
|
|
|
<p class="stat-value">{{ preprocessedData.removedRows }}</p>
|
|
|
|
|
<p class="stat-label">已删除行数</p>
|
|
|
|
|
</div>
|
|
|
|
|
</el-col>
|
|
|
|
|
<el-col :span="8">
|
|
|
|
|
<div class="stat-item">
|
|
|
|
|
<p class="stat-value">{{ preprocessedData.changedCells }}</p>
|
|
|
|
|
<p class="stat-label">更改单元格数</p>
|
|
|
|
|
</div>
|
|
|
|
|
</el-col>
|
|
|
|
|
</el-row>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<template #result-detail v-if="status === 'completed'">
|
|
|
|
|
<div class="preprocessing-detail">
|
|
|
|
|
<h3>数据预览</h3>
|
|
|
|
|
<el-table
|
|
|
|
|
:data="previewData"
|
|
|
|
|
border
|
|
|
|
|
style="width: 100%"
|
|
|
|
|
max-height="300px"
|
|
|
|
|
>
|
|
|
|
|
|
|
|
|
|
<template #result-detail>
|
|
|
|
|
<div class="result-details">
|
|
|
|
|
<h4>处理结果预览</h4>
|
|
|
|
|
<el-table :data="preprocessedData.preview" border stripe style="width: 100%">
|
|
|
|
|
<el-table-column
|
|
|
|
|
v-for="field in preprocessConfig.selectedFields"
|
|
|
|
|
:key="field"
|
|
|
|
|
:prop="field"
|
|
|
|
|
:label="getFieldLabel(field)"
|
|
|
|
|
min-width="120"
|
|
|
|
|
v-for="column in preprocessedData.columns"
|
|
|
|
|
:key="column.prop"
|
|
|
|
|
:prop="column.prop"
|
|
|
|
|
:label="column.label"
|
|
|
|
|
:width="column.width || ''"
|
|
|
|
|
></el-table-column>
|
|
|
|
|
</el-table>
|
|
|
|
|
|
|
|
|
|
<div class="changes-summary">
|
|
|
|
|
<h4>变更摘要</h4>
|
|
|
|
|
<el-collapse>
|
|
|
|
|
<el-collapse-item title="数据类型转换" name="1">
|
|
|
|
|
<el-tag v-for="(change, index) in preprocessedData.typeChanges" :key="index"
|
|
|
|
|
size="small" style="margin-right: 5px; margin-bottom: 5px;">
|
|
|
|
|
{{ change }}
|
|
|
|
|
</el-tag>
|
|
|
|
|
</el-collapse-item>
|
|
|
|
|
<el-collapse-item title="规范化处理" name="2">
|
|
|
|
|
<el-tag v-for="(change, index) in preprocessedData.normalizationChanges" :key="index"
|
|
|
|
|
type="success" size="small" style="margin-right: 5px; margin-bottom: 5px;">
|
|
|
|
|
{{ change }}
|
|
|
|
|
</el-tag>
|
|
|
|
|
</el-collapse-item>
|
|
|
|
|
</el-collapse>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
</ProcessStep>
|
|
|
|
|
</process-step>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script>
|
|
|
|
|
import ProcessStep from './ProcessStep.vue';
|
|
|
|
|
import { mapState, mapActions } from 'vuex'
|
|
|
|
|
import ProcessStep from './ProcessStep.vue'
|
|
|
|
|
|
|
|
|
|
export default {
|
|
|
|
|
name: 'DataPreprocessing',
|
|
|
|
|
|
|
|
|
|
components: {
|
|
|
|
|
ProcessStep
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
props: {
|
|
|
|
|
status: {
|
|
|
|
|
type: String,
|
|
|
|
|
default: 'waiting',
|
|
|
|
|
validator: value => ['waiting', 'running', 'completed', 'error'].includes(value)
|
|
|
|
|
default: 'waiting'
|
|
|
|
|
},
|
|
|
|
|
loading: {
|
|
|
|
|
type: Boolean,
|
|
|
|
@ -136,113 +143,166 @@ export default {
|
|
|
|
|
default: null
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
data() {
|
|
|
|
|
return {
|
|
|
|
|
preprocessConfig: {
|
|
|
|
|
cleanLevel: 'standard',
|
|
|
|
|
removeDuplicates: true,
|
|
|
|
|
normalizeText: true,
|
|
|
|
|
missingValueStrategy: 'remove',
|
|
|
|
|
selectedFields: ['title', 'content', 'category']
|
|
|
|
|
options: {
|
|
|
|
|
removeEmptyRows: true,
|
|
|
|
|
trimWhitespace: true,
|
|
|
|
|
normalizeDates: true,
|
|
|
|
|
convertDataTypes: true
|
|
|
|
|
},
|
|
|
|
|
processedData: null,
|
|
|
|
|
previewData: [],
|
|
|
|
|
processingStartTime: null,
|
|
|
|
|
processingEndTime: null
|
|
|
|
|
};
|
|
|
|
|
},
|
|
|
|
|
computed: {
|
|
|
|
|
preprocessLevelText() {
|
|
|
|
|
const levels = {
|
|
|
|
|
basic: '基本清洗',
|
|
|
|
|
standard: '标准清洗',
|
|
|
|
|
deep: '深度清洗'
|
|
|
|
|
};
|
|
|
|
|
return levels[this.preprocessConfig.cleanLevel] || '未选择';
|
|
|
|
|
},
|
|
|
|
|
processingTime() {
|
|
|
|
|
if (!this.processingStartTime || !this.processingEndTime) return 0;
|
|
|
|
|
return ((this.processingEndTime - this.processingStartTime) / 1000).toFixed(2);
|
|
|
|
|
preprocessedData: {
|
|
|
|
|
processedRows: 0,
|
|
|
|
|
removedRows: 0,
|
|
|
|
|
changedCells: 0,
|
|
|
|
|
columns: [],
|
|
|
|
|
preview: [],
|
|
|
|
|
typeChanges: [],
|
|
|
|
|
normalizationChanges: []
|
|
|
|
|
},
|
|
|
|
|
processTimer: null,
|
|
|
|
|
localStatus: 'waiting',
|
|
|
|
|
localLoading: false,
|
|
|
|
|
localProgress: 0
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
watch: {
|
|
|
|
|
status(newStatus) {
|
|
|
|
|
if (newStatus === 'completed' && this.fileData) {
|
|
|
|
|
this.generateProcessedData();
|
|
|
|
|
}
|
|
|
|
|
this.localStatus = newStatus
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
mounted() {
|
|
|
|
|
// 检查是否已经有预处理结果
|
|
|
|
|
if (this.status === 'completed') {
|
|
|
|
|
this.generateMockResults()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 将prop值同步到本地
|
|
|
|
|
this.localStatus = this.status
|
|
|
|
|
this.localLoading = this.loading
|
|
|
|
|
this.localProgress = this.progress
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
methods: {
|
|
|
|
|
...mapActions('process', ['processPreprocess']),
|
|
|
|
|
|
|
|
|
|
startPreprocess() {
|
|
|
|
|
this.processingStartTime = Date.now();
|
|
|
|
|
// 触发父组件处理逻辑
|
|
|
|
|
this.$emit('start-preprocess', { ...this.preprocessConfig });
|
|
|
|
|
},
|
|
|
|
|
generateProcessedData() {
|
|
|
|
|
this.processingEndTime = Date.now();
|
|
|
|
|
if (!this.fileData) {
|
|
|
|
|
this.$message.warning('请先上传文件')
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 通知父组件开始预处理
|
|
|
|
|
this.$emit('start-preprocess', this.options)
|
|
|
|
|
|
|
|
|
|
// 更新本地状态
|
|
|
|
|
this.localLoading = true
|
|
|
|
|
this.localStatus = 'running'
|
|
|
|
|
this.localProgress = 0
|
|
|
|
|
|
|
|
|
|
// 这里是模拟生成处理后的数据
|
|
|
|
|
// 在实际项目中,这些数据应该来自服务器
|
|
|
|
|
if (this.fileData) {
|
|
|
|
|
this.processedData = {
|
|
|
|
|
rowCount: this.fileData.rowCount - Math.floor(Math.random() * 50),
|
|
|
|
|
processedAt: new Date().toISOString()
|
|
|
|
|
};
|
|
|
|
|
// 模拟进度更新
|
|
|
|
|
this.processTimer = setInterval(() => {
|
|
|
|
|
this.localProgress += 5
|
|
|
|
|
if (this.localProgress >= 100) {
|
|
|
|
|
clearInterval(this.processTimer)
|
|
|
|
|
this.completePreprocess()
|
|
|
|
|
}
|
|
|
|
|
}, 150)
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
completePreprocess() {
|
|
|
|
|
// 模拟完成预处理
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
// 清理计时器
|
|
|
|
|
clearInterval(this.processTimer)
|
|
|
|
|
|
|
|
|
|
// 生成预览数据
|
|
|
|
|
this.previewData = this.generatePreviewData();
|
|
|
|
|
}
|
|
|
|
|
// 生成模拟结果
|
|
|
|
|
this.generateMockResults()
|
|
|
|
|
|
|
|
|
|
// 更新状态
|
|
|
|
|
this.localLoading = false
|
|
|
|
|
this.localStatus = 'completed'
|
|
|
|
|
this.localProgress = 100
|
|
|
|
|
|
|
|
|
|
// 更新Vuex中的状态
|
|
|
|
|
this.$store.commit('process/UPDATE_STEP_STATUS', { step: 'preprocess', status: 'completed' })
|
|
|
|
|
|
|
|
|
|
// 存储预处理结果
|
|
|
|
|
this.processPreprocess(this.preprocessedData)
|
|
|
|
|
|
|
|
|
|
// 显示成功消息
|
|
|
|
|
this.$message.success('数据预处理完成')
|
|
|
|
|
|
|
|
|
|
// 确保父组件知道预处理已完成
|
|
|
|
|
this.$nextTick(() => {
|
|
|
|
|
console.log('数据预处理完成,可以继续下一步')
|
|
|
|
|
// 触发父组件的预处理完成事件
|
|
|
|
|
this.$emit('preprocess-completed', this.preprocessedData)
|
|
|
|
|
})
|
|
|
|
|
}, 500)
|
|
|
|
|
},
|
|
|
|
|
generatePreviewData() {
|
|
|
|
|
// 生成模拟的预览数据
|
|
|
|
|
const sampleData = [];
|
|
|
|
|
const fieldValues = {
|
|
|
|
|
title: ['项目报告', '季度总结', '市场分析', '用户调研', '功能规划'],
|
|
|
|
|
content: ['这是一份详细的项目报告...', '本季度业绩表现良好...', '市场竞争激烈,需要...', '根据用户反馈,我们...', '未来三个月的功能开发计划...'],
|
|
|
|
|
category: ['报告', '总结', '分析', '调研', '规划'],
|
|
|
|
|
tags: ['项目,重要', '季度,业绩', '市场,竞争', '用户,反馈', '功能,开发'],
|
|
|
|
|
timestamp: ['2023-10-01', '2023-10-15', '2023-11-01', '2023-11-15', '2023-12-01']
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
generateMockResults() {
|
|
|
|
|
// 确保数据行数有效
|
|
|
|
|
const rowCount = this.fileData?.rowCount || 1000
|
|
|
|
|
|
|
|
|
|
// 生成列定义
|
|
|
|
|
const columns = [
|
|
|
|
|
{ prop: 'id', label: 'ID', width: '70' },
|
|
|
|
|
{ prop: 'name', label: '名称' },
|
|
|
|
|
{ prop: 'date', label: '日期' },
|
|
|
|
|
{ prop: 'category', label: '类别' },
|
|
|
|
|
{ prop: 'value', label: '数值' }
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
for (let i = 0; i < 5; i++) {
|
|
|
|
|
const row = {};
|
|
|
|
|
this.preprocessConfig.selectedFields.forEach(field => {
|
|
|
|
|
if (fieldValues[field]) {
|
|
|
|
|
row[field] = fieldValues[field][i % fieldValues[field].length];
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
sampleData.push(row);
|
|
|
|
|
// 生成预览数据
|
|
|
|
|
const preview = []
|
|
|
|
|
for (let i = 1; i <= 5; i++) {
|
|
|
|
|
preview.push({
|
|
|
|
|
id: i,
|
|
|
|
|
name: `示例数据 ${i}`,
|
|
|
|
|
date: `2025-04-${10 + i}`,
|
|
|
|
|
category: ['A类', 'B类', 'C类'][i % 3],
|
|
|
|
|
value: Math.floor(Math.random() * 1000)
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return sampleData;
|
|
|
|
|
},
|
|
|
|
|
formatFileSize(size) {
|
|
|
|
|
if (!size) return '0 B';
|
|
|
|
|
const units = ['B', 'KB', 'MB', 'GB'];
|
|
|
|
|
let i = 0;
|
|
|
|
|
while (size >= 1024 && i < units.length - 1) {
|
|
|
|
|
size /= 1024;
|
|
|
|
|
i++;
|
|
|
|
|
// 生成变更记录
|
|
|
|
|
const typeChanges = [
|
|
|
|
|
'字符串→日期 (32项)',
|
|
|
|
|
'文本→数值 (147项)',
|
|
|
|
|
'数值→百分比 (56项)'
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
const normalizationChanges = [
|
|
|
|
|
'日期格式标准化 (118项)',
|
|
|
|
|
'移除前后空格 (243项)',
|
|
|
|
|
'大小写规范化 (87项)',
|
|
|
|
|
'数值精度统一 (92项)'
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
// 更新结果
|
|
|
|
|
this.preprocessedData = {
|
|
|
|
|
processedRows: rowCount,
|
|
|
|
|
removedRows: Math.floor(rowCount * 0.05), // 5%的行被移除
|
|
|
|
|
changedCells: Math.floor(rowCount * 1.2), // 平均每行1.2个单元格被更改
|
|
|
|
|
columns,
|
|
|
|
|
preview,
|
|
|
|
|
typeChanges,
|
|
|
|
|
normalizationChanges
|
|
|
|
|
}
|
|
|
|
|
return `${size.toFixed(2)} ${units[i]}`;
|
|
|
|
|
},
|
|
|
|
|
formatDateTime(timestamp) {
|
|
|
|
|
if (!timestamp) return '';
|
|
|
|
|
const date = new Date(timestamp);
|
|
|
|
|
return date.toLocaleString();
|
|
|
|
|
},
|
|
|
|
|
getFieldLabel(field) {
|
|
|
|
|
const labels = {
|
|
|
|
|
title: '标题',
|
|
|
|
|
content: '内容',
|
|
|
|
|
category: '分类',
|
|
|
|
|
tags: '标签',
|
|
|
|
|
timestamp: '时间戳'
|
|
|
|
|
};
|
|
|
|
|
return labels[field] || field;
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
beforeDestroy() {
|
|
|
|
|
if (this.processTimer) {
|
|
|
|
|
clearInterval(this.processTimer)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
|
|
|
@ -250,34 +310,83 @@ export default {
|
|
|
|
|
width: 100%;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.preprocessing-options {
|
|
|
|
|
margin-bottom: 20px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.preprocessing-options h4 {
|
|
|
|
|
margin-bottom: 15px;
|
|
|
|
|
font-size: 16px;
|
|
|
|
|
color: #303133;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.file-info {
|
|
|
|
|
margin-top: 20px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.file-info h4 {
|
|
|
|
|
margin-bottom: 10px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.process-result-summary {
|
|
|
|
|
text-align: center;
|
|
|
|
|
margin-bottom: 20px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.preprocessing-config {
|
|
|
|
|
.process-result-summary i {
|
|
|
|
|
font-size: 48px;
|
|
|
|
|
color: #67c23a;
|
|
|
|
|
margin-bottom: 10px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.process-result-summary h4 {
|
|
|
|
|
font-size: 18px;
|
|
|
|
|
margin: 10px 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.process-result-summary p {
|
|
|
|
|
color: #606266;
|
|
|
|
|
margin-bottom: 20px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.preprocessing-config h3,
|
|
|
|
|
.file-info h3,
|
|
|
|
|
.preprocessing-summary h3,
|
|
|
|
|
.preprocessing-detail h3 {
|
|
|
|
|
margin-bottom: 15px;
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
color: #303133;
|
|
|
|
|
.summary-stats {
|
|
|
|
|
margin-top: 25px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.stat-item {
|
|
|
|
|
text-align: center;
|
|
|
|
|
padding: 15px;
|
|
|
|
|
background-color: #f5f7fa;
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.stat-value {
|
|
|
|
|
font-size: 24px;
|
|
|
|
|
font-weight: bold;
|
|
|
|
|
margin: 0;
|
|
|
|
|
color: #409eff;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.stat-label {
|
|
|
|
|
margin: 5px 0 0;
|
|
|
|
|
color: #606266;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.operation-buttons {
|
|
|
|
|
.result-details {
|
|
|
|
|
margin-top: 20px;
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.preprocessing-summary {
|
|
|
|
|
margin-bottom: 20px;
|
|
|
|
|
.result-details h4 {
|
|
|
|
|
margin-bottom: 15px;
|
|
|
|
|
font-size: 16px;
|
|
|
|
|
color: #303133;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.preprocessing-detail {
|
|
|
|
|
.changes-summary {
|
|
|
|
|
margin-top: 20px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.changes-summary h4 {
|
|
|
|
|
margin-bottom: 10px;
|
|
|
|
|
}
|
|
|
|
|
</style>
|