Commit a2c01127 by 焦子成

1

parent 85482980
...@@ -353,12 +353,12 @@ ...@@ -353,12 +353,12 @@
</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>
<el-table-column label="操作" width="200" align="center" fixed="right"> <el-table-column label="操作" width="200" align="center" fixed="right">
...@@ -440,14 +440,14 @@ const activeCollapse = ref(['upload']) // 默认展开配置区域 ...@@ -440,14 +440,14 @@ const activeCollapse = ref(['upload']) // 默认展开配置区域
const currentPage = ref(1) const currentPage = ref(1)
const pageSize = ref(10) const pageSize = ref(10)
// 已上传文件列表 // 已上传文件列表
const upQuery = ref({ let upQuery = ref({
current: 1, current: 1,
pageSize: 5 pageSize: 5
}) })
let upTableData = reactive([]) let upTableData = reactive([])
let upTotal = ref(0) let upTotal = ref(0)
// 下载任务管理列表 // 下载任务管理列表
const deQuery = ref({ let deQuery = ref({
current: 1, current: 1,
pageSize: 10, pageSize: 10,
fileStatus:'' fileStatus:''
...@@ -455,13 +455,13 @@ const deQuery = ref({ ...@@ -455,13 +455,13 @@ const deQuery = ref({
let deTableData = reactive([]) let deTableData = reactive([])
let deTotal = ref(0) let deTotal = ref(0)
const columnMapping = ref({ let columnMapping = ref({
fileNameColumns: [], fileNameColumns: [],
url: '' url: ''
}) })
// 状态筛选 // 状态筛选
const statusFilter = ref('') let statusFilter = ref('')
// 计算是否可以解析 // 计算是否可以解析
const canParse = computed(() => { const canParse = computed(() => {
...@@ -824,20 +824,21 @@ onMounted(() => { ...@@ -824,20 +824,21 @@ onMounted(() => {
detectBrowserDownloadPath() detectBrowserDownloadPath()
loadUpTableData() loadUpTableData()
loadDetailTableData() loadDetailTableData()
loadUploadDetailTotalNum()
}) })
function loadDetailTableData(){ function loadDetailTableData(){
http.post(config.uploadDetailList,deQuery.value).then(res=>{ http.post(config.uploadDetailList,deQuery.value).then(res=>{
if (res.code === 200){ if (res.code === 200){
deTableData = res.data.rows deTableData = res.data.rows
deTotal.value = res.data.total deTotal.value = res.data.total
loadUploadDetailTotalNum()
}else { }else {
ElMessage.error(res.message) ElMessage.error(res.message)
} }
}) })
} }
const totalNumObj = reactive({ let totalNumObj = ref({
countNum:0, countNum:0,
fileLength: '',
status0: 0, status0: 0,
status1: 0, status1: 0,
status2: 0, status2: 0,
...@@ -847,7 +848,10 @@ const totalNumObj = reactive({ ...@@ -847,7 +848,10 @@ const totalNumObj = reactive({
function loadUploadDetailTotalNum(){ function loadUploadDetailTotalNum(){
http.post(config.uploadDetailTotalNum).then(res=>{ http.post(config.uploadDetailTotalNum).then(res=>{
if (res.code === 200){ if (res.code === 200){
totalNumObj.value.countNum = res.data.countNum
totalNumObj.value.status3 = res.data.status3
totalNumObj.value.status4 = res.data.status4
totalNumObj.value.fileLength = res.data.fileLength
}else { }else {
ElMessage.error(res.message) ElMessage.error(res.message)
} }
...@@ -902,7 +906,7 @@ function deleteRow(row) { ...@@ -902,7 +906,7 @@ function deleteRow(row) {
// 解析Excel文件 // 解析Excel文件
const parseExcelFile = async () => { const parseExcelFile = async () => {
if (rawExcelData.value.length > 8000){ if (rawExcelData.value.length > 3000){
ElMessage.error('文件数据量太大,请拆分处理,每次不超过3000条') ElMessage.error('文件数据量太大,请拆分处理,每次不超过3000条')
return return
} }
......
...@@ -3,7 +3,9 @@ ...@@ -3,7 +3,9 @@
<!-- 头部 --> <!-- 头部 -->
<header class="header"> <header class="header">
<div class="logo"> <div class="logo">
<el-icon><Clock /></el-icon> <el-icon>
<Clock />
</el-icon>
下载历史 下载历史
</div> </div>
<div class="header-actions"> <div class="header-actions">
...@@ -22,19 +24,19 @@ ...@@ -22,19 +24,19 @@
<el-card class="stats-card"> <el-card class="stats-card">
<div class="stats-grid"> <div class="stats-grid">
<div class="stat-item"> <div class="stat-item">
<div class="stat-number">{{ totalRecords }}</div> <div class="stat-number">{{ totalNumObj.countNum }}</div>
<div class="stat-label">总记录数</div> <div class="stat-label">总记录数</div>
</div> </div>
<div class="stat-item"> <div class="stat-item">
<div class="stat-number">{{ completedCount }}</div> <div class="stat-number">{{ totalNumObj.status3 }}</div>
<div class="stat-label">已完成</div> <div class="stat-label">已完成</div>
</div> </div>
<div class="stat-item"> <div class="stat-item">
<div class="stat-number">{{ failedCount }}</div> <div class="stat-number">{{ totalNumObj.status4 }}</div>
<div class="stat-label">失败</div> <div class="stat-label">失败</div>
</div> </div>
<div class="stat-item"> <div class="stat-item">
<div class="stat-number">{{ totalSize }}</div> <div class="stat-number">{{ totalNumObj.fileLength }}</div>
<div class="stat-label">总大小</div> <div class="stat-label">总大小</div>
</div> </div>
</div> </div>
...@@ -42,76 +44,68 @@ ...@@ -42,76 +44,68 @@
<!-- 历史记录列表 --> <!-- 历史记录列表 -->
<el-card class="history-card"> <el-card class="history-card">
<template #header> <template #header>
<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="deQuery.fileStatus" placeholder="状态筛选" size="small" style="width: 120px;">
<el-option label="全部" value="" /> <el-option label="全部" value="" />
<el-option label="已完成" value="completed" /> <el-option label="已完成" :value="3" />
<el-option label="失败" value="error" /> <el-option label="失败" :value="4" />
<el-option label="暂停" value="paused" /> <el-option label="暂停" :value="2" />
</el-select> </el-select>
<el-select v-model="excelFileFilter" placeholder="Excel文件筛选" size="small" style="width: 150px; margin-left: 10px;"> <el-select v-model="excelFileFilter" placeholder="Excel文件筛选" size="small"
<el-option label="全部Excel文件" value="" /> style="width: 150px; margin-left: 10px;">
<el-option <el-option label="全部Excel文件" value="" />
v-for="excelFile in uniqueExcelFiles" <el-option v-for="excelFile in uniqueExcelFiles" :key="excelFile" :label="excelFile"
:key="excelFile" :value="excelFile" />
:label="excelFile" </el-select>
:value="excelFile" <el-input v-model="deQuery.fileName" placeholder="搜索文件名或Excel文件" size="small"
/> style="width: 200px; margin-left: 10px;" clearable>
</el-select> <template #prefix>
<el-input <el-icon>
v-model="searchKeyword" <Search />
placeholder="搜索文件名或Excel文件" </el-icon>
size="small" </template>
style="width: 200px; margin-left: 10px;" </el-input>
clearable </div>
> </div>
<template #prefix> </template>
<el-icon><Search /></el-icon>
</template>
</el-input>
</div>
</div>
</template>
<!-- 历史记录表格 --> <!-- 历史记录表格 -->
<el-table <el-table :data="deTableData" style="width: 100%" :default-sort="{ prop: 'timestamp', order: 'descending' }"
:data="filteredHistory" stripe>
style="width: 100%" <el-table-column prop="timestamp" label="时间" width="180" sortable>
:default-sort="{ prop: 'timestamp', order: 'descending' }" <template #default="scope">
stripe {{ formatDate(scope.row.timestamp) }}
> </template>
<el-table-column prop="timestamp" label="时间" width="180" sortable> </el-table-column>
<template #default="scope">
{{ formatDate(scope.row.timestamp) }} <el-table-column prop="excelFileName" label="Excel文件" width="180">
</template> <template #default="scope">
</el-table-column> <div class="excel-file-info">
<el-icon>
<el-table-column prop="excelFileName" label="Excel文件" width="180"> <Document />
<template #default="scope"> </el-icon>
<div class="excel-file-info"> <span class="excel-file-name">{{ scope.row.excelFileName || '-' }}</span>
<el-icon><Document /></el-icon> </div>
<span class="excel-file-name">{{ scope.row.excelFileName || '-' }}</span> </template>
</div> </el-table-column>
</template>
</el-table-column> <el-table-column prop="fileName" label="文件名" min-width="200">
<template #default="scope">
<el-table-column prop="fileName" label="文件名" min-width="200"> <div class="file-info">
<template #default="scope"> <span class="file-name">
<div class="file-info"> <span v-if="scope.row.fileNamePrefix" class="file-prefix">
<span class="file-name"> {{ scope.row.fileNamePrefix }}_
<span v-if="scope.row.fileNamePrefix" class="file-prefix"> </span>
{{ scope.row.fileNamePrefix }}_ {{ scope.row.fileName }}
</span> </span>
{{ scope.row.fileName }} <span class="file-url">{{ scope.row.url }}</span>
</span> </div>
<span class="file-url">{{ scope.row.url }}</span> </template>
</div> </el-table-column>
</template>
</el-table-column>
<el-table-column prop="status" label="状态" width="100"> <el-table-column prop="status" label="状态" width="100">
<template #default="scope"> <template #default="scope">
<el-tag :type="getStatusType(scope.row.status)"> <el-tag :type="getStatusType(scope.row.status)">
...@@ -119,7 +113,7 @@ ...@@ -119,7 +113,7 @@
</el-tag> </el-tag>
</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'">
...@@ -133,34 +127,26 @@ ...@@ -133,34 +127,26 @@
</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) }}
</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) }}
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="操作" width="120" fixed="right"> <el-table-column label="操作" width="120" fixed="right">
<template #default="scope"> <template #default="scope">
<el-button <el-button v-if="scope.row.status === 'error'" @click="retryDownload(scope.row)" type="primary"
v-if="scope.row.status === 'error'" size="small">
@click="retryDownload(scope.row)"
type="primary"
size="small"
>
重试 重试
</el-button> </el-button>
<el-button <el-button @click="viewDetails(scope.row)" type="info" size="small">
@click="viewDetails(scope.row)"
type="info"
size="small"
>
详情 详情
</el-button> </el-button>
</template> </template>
...@@ -169,15 +155,9 @@ ...@@ -169,15 +155,9 @@
<!-- 分页 --> <!-- 分页 -->
<div class="pagination-container"> <div class="pagination-container">
<el-pagination <el-pagination v-model:current-page="deQuery.current" v-model:page-size="deQuery.pageSize" :page-sizes="[10, 20, 50, 100]"
v-model:current-page="currentPage" :total="deTotal" layout="total, sizes, prev, pager, next, jumper"
v-model:page-size="pageSize" @size-change="handleSizeChange" @current-change="handleCurrentChange" />
:page-sizes="[10, 20, 50, 100]"
:total="filteredHistory.length"
layout="total, sizes, prev, pager, next, jumper"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div> </div>
</el-card> </el-card>
</div> </div>
...@@ -185,15 +165,15 @@ ...@@ -185,15 +165,15 @@
<!-- 详情对话框 --> <!-- 详情对话框 -->
<el-dialog v-model="detailsVisible" title="下载详情" width="600px"> <el-dialog v-model="detailsVisible" title="下载详情" width="600px">
<div v-if="selectedRecord" class="details-content"> <div v-if="selectedRecord" class="details-content">
<el-descriptions :column="1" border> <el-descriptions :column="1" border>
<el-descriptions-item label="Excel文件">{{ selectedRecord.excelFileName || '-' }}</el-descriptions-item> <el-descriptions-item label="Excel文件">{{ selectedRecord.excelFileName || '-' }}</el-descriptions-item>
<el-descriptions-item label="文件名"> <el-descriptions-item label="文件名">
<span v-if="selectedRecord.fileNamePrefix" class="file-prefix"> <span v-if="selectedRecord.fileNamePrefix" class="file-prefix">
{{ selectedRecord.fileNamePrefix }}_ {{ selectedRecord.fileNamePrefix }}_
</span> </span>
{{ selectedRecord.fileName }} {{ selectedRecord.fileName }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="下载地址">{{ selectedRecord.url }}</el-descriptions-item> <el-descriptions-item label="下载地址">{{ selectedRecord.url }}</el-descriptions-item>
<el-descriptions-item label="状态"> <el-descriptions-item label="状态">
<el-tag :type="getStatusType(selectedRecord.status)"> <el-tag :type="getStatusType(selectedRecord.status)">
{{ getStatusText(selectedRecord.status) }} {{ getStatusText(selectedRecord.status) }}
...@@ -214,11 +194,13 @@ ...@@ -214,11 +194,13 @@
</template> </template>
<script setup> <script setup>
import { ref, computed, onMounted } from 'vue' import { ref, computed, onMounted, reactive } from 'vue'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import { ElMessage, ElMessageBox } from 'element-plus' import { ElMessage, ElMessageBox } from 'element-plus'
import { Clock, Search } from '@element-plus/icons-vue' 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 config from '@/api/api.js';
const router = useRouter() const router = useRouter()
const authStore = useAuthStore() const authStore = useAuthStore()
...@@ -233,56 +215,93 @@ const pageSize = ref(20) ...@@ -233,56 +215,93 @@ const pageSize = ref(20)
const detailsVisible = ref(false) const detailsVisible = ref(false)
const selectedRecord = ref(null) const selectedRecord = ref(null)
const deQuery = ref({
current: 1,
pageSize: 10,
fileStatus: 3,
fileName: ''
})
let deTableData = reactive([])
let deTotal = ref(0)
// 加载历史数据 // 加载历史数据
const loadHistory = () => { const loadHistory = () => {
historyData.value = authStore.getDownloadHistory() // historyData.value = authStore.getDownloadHistory()
http.post(config.uploadDetailList, deQuery.value).then(res => {
if (res.code === 200) {
deTableData = res.data.rows
deTotal.value = res.data.total
} else {
ElMessage.error(res.message)
}
})
}
let totalNumObj = ref({
"countNum": 0,
"fileLength": '0 KB',
"status0": 0,
"status1": 0,
"status2": 0,
"status3": 0,
"status4": 0
})
function loadUploadDetailTotalNum(){
http.post(config.uploadDetailTotalNum).then(res=>{
if (res.code === 200){
totalNumObj.value.countNum = res.data.countNum
totalNumObj.value.status3 = res.data.status3
totalNumObj.value.status4 = res.data.status4
totalNumObj.value.fileLength = res.data.fileLength
}else {
ElMessage.error(res.message)
}
})
} }
// 获取唯一的Excel文件列表 // 获取唯一的Excel文件列表
const uniqueExcelFiles = computed(() => { const uniqueExcelFiles = computed(() => {
const allDownloads = historyData.value.flatMap(record => const allDownloads = historyData.value.flatMap(record =>
record.downloads.map(download => ({ record.downloads.map(download => ({
...download, ...download,
timestamp: record.timestamp timestamp: record.timestamp
})) }))
) )
const excelFiles = allDownloads const excelFiles = allDownloads
.map(item => item.excelFileName) .map(item => item.excelFileName)
.filter(file => file && file.trim()) .filter(file => file && file.trim())
return [...new Set(excelFiles)] return [...new Set(excelFiles)]
}) })
// 筛选历史记录 // 筛选历史记录
const filteredHistory = computed(() => { const filteredHistory = computed(() => {
let filtered = historyData.value.flatMap(record => let filtered = historyData.value.flatMap(record =>
record.downloads.map(download => ({ record.downloads.map(download => ({
...download, ...download,
timestamp: record.timestamp timestamp: record.timestamp
})) }))
) )
// 状态筛选 // 状态筛选
if (statusFilter.value) { if (statusFilter.value) {
filtered = filtered.filter(item => item.status === statusFilter.value) filtered = filtered.filter(item => item.status === statusFilter.value)
} }
// Excel文件筛选 // Excel文件筛选
if (excelFileFilter.value) { if (excelFileFilter.value) {
filtered = filtered.filter(item => item.excelFileName === excelFileFilter.value) filtered = filtered.filter(item => item.excelFileName === excelFileFilter.value)
} }
// 关键词搜索 // 关键词搜索
if (searchKeyword.value) { if (searchKeyword.value) {
const keyword = searchKeyword.value.toLowerCase() const keyword = searchKeyword.value.toLowerCase()
filtered = filtered.filter(item => filtered = filtered.filter(item =>
item.fileName.toLowerCase().includes(keyword) || item.fileName.toLowerCase().includes(keyword) ||
item.url.toLowerCase().includes(keyword) || item.url.toLowerCase().includes(keyword) ||
(item.excelFileName && item.excelFileName.toLowerCase().includes(keyword)) (item.excelFileName && item.excelFileName.toLowerCase().includes(keyword))
) )
} }
return filtered return filtered
}) })
...@@ -313,11 +332,11 @@ const formatDate = (dateString) => { ...@@ -313,11 +332,11 @@ const formatDate = (dateString) => {
// 格式化文件大小 // 格式化文件大小
const formatFileSize = (bytes) => { const formatFileSize = (bytes) => {
if (!bytes || bytes === 0) return '0 B' if (!bytes || bytes === 0) return '0 B'
const k = 1024 const k = 1024
const sizes = ['B', 'KB', 'MB', 'GB', 'TB'] const sizes = ['B', 'KB', 'MB', 'GB', 'TB']
const i = Math.floor(Math.log(bytes) / Math.log(k)) const i = Math.floor(Math.log(bytes) / Math.log(k))
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i] return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]
} }
...@@ -360,7 +379,7 @@ const retryDownload = async (record) => { ...@@ -360,7 +379,7 @@ const retryDownload = async (record) => {
type: 'info' type: 'info'
} }
) )
// 这里可以调用下载store的方法重新下载 // 这里可以调用下载store的方法重新下载
ElMessage.success('已添加到下载队列') ElMessage.success('已添加到下载队列')
} catch { } catch {
...@@ -380,7 +399,7 @@ const clearAllHistory = async () => { ...@@ -380,7 +399,7 @@ const clearAllHistory = async () => {
type: 'warning' type: 'warning'
} }
) )
authStore.clearDownloadHistory() authStore.clearDownloadHistory()
loadHistory() loadHistory()
ElMessage.success('历史记录已清空') ElMessage.success('历史记录已清空')
...@@ -391,12 +410,17 @@ const clearAllHistory = async () => { ...@@ -391,12 +410,17 @@ const clearAllHistory = async () => {
// 分页处理 // 分页处理
const handleSizeChange = (size) => { const handleSizeChange = (size) => {
pageSize.value = size // pageSize.value = size
currentPage.value = 1 // currentPage.value = 1
deQuery.value.pageSize = size
deQuery.value.current = 1
loadHistory()
} }
const handleCurrentChange = (page) => { const handleCurrentChange = (page) => {
currentPage.value = page // currentPage.value = page
deQuery.value.current = page
loadHistory()
} }
// 返回下载器 // 返回下载器
...@@ -407,6 +431,7 @@ const goBack = () => { ...@@ -407,6 +431,7 @@ const goBack = () => {
// 组件挂载时初始化 // 组件挂载时初始化
onMounted(() => { onMounted(() => {
loadHistory() loadHistory()
loadUploadDetailTotalNum()
}) })
</script> </script>
...@@ -525,17 +550,17 @@ onMounted(() => { ...@@ -525,17 +550,17 @@ onMounted(() => {
.main-content { .main-content {
padding: 0 10px; padding: 0 10px;
} }
.stats-grid { .stats-grid {
grid-template-columns: repeat(2, 1fr); grid-template-columns: repeat(2, 1fr);
} }
.card-header { .card-header {
flex-direction: column; flex-direction: column;
align-items: flex-start; align-items: flex-start;
gap: 10px; gap: 10px;
} }
.header-actions { .header-actions {
flex-direction: column; flex-direction: column;
} }
......
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