Commit 1cb39071 by 焦子成

j

parent 3344c4a5
File added
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
// Excel处理Worker
// 使用本地XLSX库
importScripts('./xlsx.full.min.js');
self.onmessage = function(e) {
try {
const { data, type, neededColumns } = e.data;
if (type === 'parseExcel') {
const result = parseExcelData(data, neededColumns);
self.postMessage({
type: 'success',
data: result
});
}
} catch (error) {
console.error('Worker error:', error);
self.postMessage({
type: 'error',
error: error.message || '未知错误'
});
}
};
function parseExcelData(arrayBuffer, neededColumns = null) {
try {
if (!arrayBuffer) {
throw new Error('没有接收到数据');
}
const data = new Uint8Array(arrayBuffer);
const workbook = XLSX.read(data, {
type: 'array',
cellDates: true,
cellNF: false,
cellStyles: false,
cellHTML: false,
cellFormula: false,
cellText: false
});
if (!workbook.SheetNames || workbook.SheetNames.length === 0) {
throw new Error('Excel文件中没有找到工作表');
}
const sheetName = workbook.SheetNames[0];
const worksheet = workbook.Sheets[sheetName];
if (!worksheet) {
throw new Error('无法读取工作表数据');
}
// 限制最大行数,防止内存溢出
const maxRows = 100000; // 增加到10万行
const range = XLSX.utils.decode_range(worksheet['!ref'] || 'A1');
if (range.e.r > maxRows) {
range.e.r = maxRows - 1;
worksheet['!ref'] = XLSX.utils.encode_range(range);
}
const jsonData = XLSX.utils.sheet_to_json(worksheet, {
header: 1,
defval: '',
blankrows: false
});
if (!jsonData || jsonData.length === 0) {
throw new Error('Excel文件中没有数据');
}
// 转换为对象数组
const headers = jsonData[0];
if (!headers || headers.length === 0) {
throw new Error('Excel文件中没有列标题');
}
const rows = jsonData.slice(1).map(row => {
const obj = {};
headers.forEach((header, index) => {
obj[header] = row[index] || '';
});
return obj;
});
// 如果指定了需要的列,则只保留这些列
if (neededColumns && neededColumns.length > 0) {
return rows.map(row => {
const optimizedRow = {};
neededColumns.forEach(col => {
optimizedRow[col] = row[col] || '';
});
return optimizedRow;
});
}
return rows;
} catch (error) {
console.error('Excel解析错误:', error);
throw new Error('Excel解析失败: ' + error.message);
}
}
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>文件下载器 - 类似迅雷的下载工具</title>
<script type="module" crossorigin src="/assets/index-4ea3f23e.js"></script>
<link rel="stylesheet" href="/assets/index-1cc07315.css">
</head>
<body>
<div id="app"></div>
</body>
</html>
...@@ -349,13 +349,22 @@ ...@@ -349,13 +349,22 @@
</template> </template>
</el-table-column> --> </el-table-column> -->
<el-table-column label="大小" width="120" align="center" prop="fileLength"> <!-- <el-table-column label="大小" width="120" align="center" prop="fileLength">
<template #default="{ row }"> <template #default="{ row }">
<span v-if="row.totalBytes > 0" class="size-info"> <span v-if="row.totalBytes > 0" class="size-info">
{{ formatBytes(row.downloadedBytes) }} / {{ formatBytes(row.totalBytes) }} {{ formatBytes(row.downloadedBytes) }} / {{ formatBytes(row.totalBytes) }}
</span> </span>
<span v-else class="size-info">-</span> <span v-else class="size-info">-</span>
</template> </template>
</el-table-column> -->
<el-table-column label="大小" width="120" align="center">
<template #default="{ row }">
<span v-if="row.fileSize" class="size-info">
<!-- {{ formatBytes(row.downloadedBytes) }} / {{ formatBytes(row.totalBytes) }} -->
{{ row.fileSize }}
</span>
<span v-else class="size-info">-</span>
</template>
</el-table-column> </el-table-column>
<el-table-column label="操作" width="200" align="center" fixed="right"> <el-table-column label="操作" width="200" align="center" fixed="right">
...@@ -543,11 +552,18 @@ const selectExcelFile = async (fileId) => { ...@@ -543,11 +552,18 @@ const selectExcelFile = async (fileId) => {
excelColumns.value = res.data.fileColumnNameAll || [] excelColumns.value = res.data.fileColumnNameAll || []
columnMapping.value.fileNameColumns = res.data.fileColumnName || [] columnMapping.value.fileNameColumns = res.data.fileColumnName || []
columnMapping.value.url = res.data.urlColumnName columnMapping.value.url = res.data.urlColumnName
rawExcelData.value = res.data.details || [] var list = []
if (res.data.details.length > 5) { for (const element of res.data.details) {
previewData.value = res.data.details.slice(0, 5) list.push({
fileName: element.fileName,
url: element.fileUrl
})
}
rawExcelData.value = list || []
if (list.length > 5) {
previewData.value = list.slice(0, 5)
} else { } else {
previewData.value = res.data.details || [] previewData.value = list || []
} }
customSubFolder.value = res.data.filePrefix || '' customSubFolder.value = res.data.filePrefix || ''
} else { } else {
...@@ -689,7 +705,7 @@ const optimizeDataAfterColumnMapping = () => { ...@@ -689,7 +705,7 @@ const optimizeDataAfterColumnMapping = () => {
// 重新生成预览数据 // 重新生成预览数据
generatePreviewData() generatePreviewData()
console.log(`数据优化完成:从 ${Object.keys(originalData[0] || {}).length} 个字段优化到 ${Object.keys(optimizedData[0] || {}).length} 个字段`) // console.log(`数据优化完成:从 ${Object.keys(originalData[0] || {}).length} 个字段优化到 ${Object.keys(optimizedData[0] || {}).length} 个字段`)
} }
// 生成预览数据 // 生成预览数据
...@@ -941,8 +957,8 @@ function deleteRow(row) { ...@@ -941,8 +957,8 @@ function deleteRow(row) {
// 解析Excel文件 // 解析Excel文件
const parseExcelFile = async () => { const parseExcelFile = async () => {
if (rawExcelData.value.length > 3000) { if (rawExcelData.value.length > 10000) {
ElMessage.error('文件数据量太大,请拆分处理,每次不超过3000条') ElMessage.error('文件数据量太大,请拆分处理,每次不超过10000条')
return return
} }
if (!canParse.value) { if (!canParse.value) {
...@@ -1153,7 +1169,7 @@ function uploadDetailsFile() { ...@@ -1153,7 +1169,7 @@ function uploadDetailsFile() {
uplist = detailList uplist = detailList
detailList = [] detailList = []
} }
console.log('list-->', detailList.length) // console.log('list-->', detailList.length)
http.post(config.fileUploadDetail, { http.post(config.fileUploadDetail, {
batchId: upDetailId, batchId: upDetailId,
details: uplist details: uplist
...@@ -1163,6 +1179,7 @@ function uploadDetailsFile() { ...@@ -1163,6 +1179,7 @@ function uploadDetailsFile() {
uploadDetailsFile() uploadDetailsFile()
} else { } else {
ElMessage.success('上传任务完成') ElMessage.success('上传任务完成')
loadUpTableData()
loadDetailTableData() loadDetailTableData()
} }
} }
......
...@@ -48,20 +48,20 @@ ...@@ -48,20 +48,20 @@
<div class="card-header"> <div class="card-header">
<span>历史记录</span> <span>历史记录</span>
<div class="header-actions"> <div class="header-actions">
<el-select v-model="statusFilter" placeholder="状态筛选" size="small" style="width: 120px;"> <el-select v-model="statusFilter" placeholder="状态筛选" size="small" style="width: 120px;" @change="statusChange">
<el-option label="全部" value="" /> <el-option label="全部" value="all" />
<el-option label="已完成" :value="3" /> <el-option label="已完成" value="completed" />
<el-option label="失败" :value="4" /> <el-option label="失败" value="error" />
<el-option label="暂停" :value="2" /> <el-option label="暂停" value="paused" />
</el-select> </el-select>
<el-select v-model="excelFileFilter" placeholder="Excel文件筛选" size="small" <el-select v-model="excelFileFilter" placeholder="Excel文件筛选" size="small"
style="width: 150px; margin-left: 10px;"> style="width: 150px; margin-left: 10px;" @change="excelFileFilterChange">
<el-option label="全部Excel文件" value="" /> <el-option label="全部Excel文件" value="" />
<el-option v-for="excelFile in excelFileFilterList" :key="excelFile" :label="excelFile" <el-option v-for="excelFile in excelFileFilterList" :key="excelFile" :label="excelFile"
:value="excelFile" /> :value="excelFile" />
</el-select> </el-select>
<el-input v-model="searchKeyword" placeholder="搜索文件名或Excel文件" size="small" <el-input v-model="searchKeyword" placeholder="搜索文件名或Excel文件" size="small"
style="width: 200px; margin-left: 10px;" clearable> style="width: 200px; margin-left: 10px;" clearable @change="searchKeywordChange">
<template #prefix> <template #prefix>
<el-icon> <el-icon>
<Search /> <Search />
...@@ -114,7 +114,7 @@ ...@@ -114,7 +114,7 @@
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="progress" label="进度" width="120"> <!-- <el-table-column prop="progress" label="进度" width="120">
<template #default="scope"> <template #default="scope">
<div v-if="scope.row.status === 'completed'"> <div v-if="scope.row.status === 'completed'">
<el-progress :percentage="100" status="success" /> <el-progress :percentage="100" status="success" />
...@@ -126,17 +126,19 @@ ...@@ -126,17 +126,19 @@
<el-progress :percentage="scope.row.progress || 0" /> <el-progress :percentage="scope.row.progress || 0" />
</div> </div>
</template> </template>
</el-table-column> </el-table-column> -->
<el-table-column prop="fileSize" label="文件大小" width="120"> <el-table-column prop="fileSize" label="文件大小" width="120">
<template #default="scope"> <template #default="scope">
{{ formatFileSize(scope.row.fileSize) }} <!-- {{ formatFileSize(scope.row.fileSize) }} -->
{{ scope.row.fileSize }}
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="downloadedBytes" label="已下载" width="120"> <el-table-column prop="downloadedBytes" label="已下载" width="120">
<template #default="scope"> <template #default="scope">
{{ formatFileSize(scope.row.downloadedBytes) }} <!-- {{ formatFileSize(scope.row.downloadedBytes) }} -->
{{ scope.row.fileSize }}
</template> </template>
</el-table-column> </el-table-column>
...@@ -201,13 +203,14 @@ import { Clock, Search } from '@element-plus/icons-vue' ...@@ -201,13 +203,14 @@ import { Clock, Search } from '@element-plus/icons-vue'
import { useAuthStore } from '../stores/auth' import { useAuthStore } from '../stores/auth'
import http from '@/utils/request.js'; import http from '@/utils/request.js';
import config from '@/api/api.js'; import config from '@/api/api.js';
import { ca } from 'element-plus/es/locales.mjs'
const router = useRouter() const router = useRouter()
const authStore = useAuthStore() const authStore = useAuthStore()
// 状态 // 状态
const historyData = ref([]) const historyData = ref([])
const statusFilter = ref('') const statusFilter = ref('completed')
const excelFileFilter = ref('') const excelFileFilter = ref('')
const searchKeyword = ref('') const searchKeyword = ref('')
const currentPage = ref(1) const currentPage = ref(1)
...@@ -241,13 +244,19 @@ function laodFileAll(){ ...@@ -241,13 +244,19 @@ function laodFileAll(){
} }
}) })
} }
const statusFilterMap = {
'all': '',
'completed': 3,
'error': 4,
'paused': 2
}
// 加载历史数据 // 加载历史数据
const loadHistory = () => { const loadHistory = () => {
// historyData.value = authStore.getDownloadHistory() // historyData.value = authStore.getDownloadHistory()
http.post(config.uploadDetailList, { http.post(config.uploadDetailList, {
current: currentPage.value, current: currentPage.value,
pageSize: pageSize.value, pageSize: pageSize.value,
fileStatus: statusFilter.value, fileStatus: statusFilterMap[statusFilter.value],
uploadName: excelFileFilter.value, uploadName: excelFileFilter.value,
fileName: searchKeyword.value fileName: searchKeyword.value
}).then(res => { }).then(res => {
...@@ -306,6 +315,18 @@ function loadUploadDetailTotalNum(){ ...@@ -306,6 +315,18 @@ function loadUploadDetailTotalNum(){
} }
}) })
} }
function statusChange(){
currentPage.value = 1
loadHistory()
}
function excelFileFilterChange(){
currentPage.value = 1
loadHistory()
}
function searchKeywordChange(){
currentPage.value = 1
loadHistory()
}
// 获取唯一的Excel文件列表 // 获取唯一的Excel文件列表
const uniqueExcelFiles = computed(() => { const uniqueExcelFiles = computed(() => {
...@@ -407,6 +428,7 @@ const getStatusText = (status) => { ...@@ -407,6 +428,7 @@ const getStatusText = (status) => {
case 'error': return '失败' case 'error': return '失败'
case 'paused': return '暂停' case 'paused': return '暂停'
case 'downloading': return '下载中' case 'downloading': return '下载中'
case 'pending': return '等待中'
default: return '未知' default: return '未知'
} }
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment