Commit b107747a authored by luwei's avatar luwei

修改

parent 8160a34d
...@@ -54,6 +54,7 @@ def get_files( ...@@ -54,6 +54,7 @@ def get_files(
async def upload_file( async def upload_file(
file: UploadFile = File(...), file: UploadFile = File(...),
category_id: str = Form(default=''), category_id: str = Form(default=''),
remark: str = Form(default=''),
): ):
try: try:
content = await file.read() content = await file.read()
...@@ -64,6 +65,7 @@ async def upload_file( ...@@ -64,6 +65,7 @@ async def upload_file(
filename=file.filename or 'unknown.xlsx', filename=file.filename or 'unknown.xlsx',
content=content, content=content,
category_id=category_id, category_id=category_id,
remark=remark.strip() or None,
) )
return success_response(data=file_meta, message='文件上传成功') return success_response(data=file_meta, message='文件上传成功')
except ValueError as error: except ValueError as error:
...@@ -77,7 +79,7 @@ def download_template(): ...@@ -77,7 +79,7 @@ def download_template():
content=content, content=content,
media_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', media_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
headers={ headers={
'Content-Disposition': 'attachment; filename=data_template.xlsx', 'Content-Disposition': 'attachment; filename=上传文件模板.xlsx',
}, },
) )
......
...@@ -157,7 +157,7 @@ class DataManagementService: ...@@ -157,7 +157,7 @@ class DataManagementService:
for item in rows for item in rows
] ]
def upload_file(self, filename: str, content: bytes, category_id: str = 'all') -> dict[str, Any]: def upload_file(self, filename: str, content: bytes, category_id: str = 'all', remark: str | None = None) -> dict[str, Any]:
suffix = Path(filename).suffix.lower() suffix = Path(filename).suffix.lower()
if suffix not in {'.xlsx', '.xls', '.csv'}: if suffix not in {'.xlsx', '.xls', '.csv'}:
raise ValueError('仅支持数据文件(.xlsx/.xls/.csv)') raise ValueError('仅支持数据文件(.xlsx/.xls/.csv)')
...@@ -194,6 +194,7 @@ class DataManagementService: ...@@ -194,6 +194,7 @@ class DataManagementService:
file_path=db_file_path, file_path=db_file_path,
category_id=category_db_id, category_id=category_db_id,
data_count=total_count, data_count=total_count,
remark=remark,
) )
session.add(file_meta) session.add(file_meta)
session.commit() session.commit()
...@@ -205,6 +206,7 @@ class DataManagementService: ...@@ -205,6 +206,7 @@ class DataManagementService:
'stored_name': file_meta.stored_name, 'stored_name': file_meta.stored_name,
'file_path': file_meta.file_path, 'file_path': file_meta.file_path,
'category_id': file_meta.category_id, 'category_id': file_meta.category_id,
'remark': file_meta.remark or '',
'uploaded_at': file_meta.uploaded_at.strftime('%Y-%m-%d %H:%M:%S') if file_meta.uploaded_at else '', 'uploaded_at': file_meta.uploaded_at.strftime('%Y-%m-%d %H:%M:%S') if file_meta.uploaded_at else '',
'data_count': file_meta.data_count, 'data_count': file_meta.data_count,
} }
......
...@@ -151,6 +151,10 @@ body { ...@@ -151,6 +151,10 @@ body {
border-bottom: 1px solid var(--border-color); border-bottom: 1px solid var(--border-color);
} }
.el-dialog__body {
padding-top: 24px;
}
/* Tabs (navigation) */ /* Tabs (navigation) */
.el-tabs__item { .el-tabs__item {
font-size: 14px !important; font-size: 14px !important;
......
<script setup> <script setup>
import { DataAnalysis, Delete, Document, Edit, Folder, FolderOpened, Upload } from '@element-plus/icons-vue' import { DataAnalysis, Delete, Document, Edit, Folder, FolderOpened, Upload, Search } from '@element-plus/icons-vue'
import { computed, onBeforeUnmount, onMounted, reactive, ref } from 'vue' import { computed, onBeforeUnmount, onMounted, reactive, ref } from 'vue'
import { ElAutoResizer, ElMessage, ElMessageBox, ElTableV2 } from 'element-plus' import { ElAutoResizer, ElMessage, ElMessageBox, ElTableV2 } from 'element-plus'
import { import {
...@@ -57,6 +57,7 @@ const categoryForm = reactive({ ...@@ -57,6 +57,7 @@ const categoryForm = reactive({
const uploadDialogVisible = ref(false) const uploadDialogVisible = ref(false)
const uploadForm = reactive({ const uploadForm = reactive({
categoryId: '', categoryId: '',
remark: '',
}) })
const qualityDialogVisible = ref(false) const qualityDialogVisible = ref(false)
...@@ -304,7 +305,7 @@ const handleDragging = (event) => { ...@@ -304,7 +305,7 @@ const handleDragging = (event) => {
} }
const startDragging = (target, event) => { const startDragging = (target, event) => {
if (window.innerWidth <= 980 || !layoutRef.value) { if (!layoutRef.value) {
return return
} }
...@@ -427,6 +428,7 @@ const handleReset = async () => { ...@@ -427,6 +428,7 @@ const handleReset = async () => {
const openUploadDialog = () => { const openUploadDialog = () => {
uploadForm.categoryId = selectedCategory.value && selectedCategory.value !== 'all' ? String(selectedCategory.value) : '' uploadForm.categoryId = selectedCategory.value && selectedCategory.value !== 'all' ? String(selectedCategory.value) : ''
uploadForm.remark = ''
uploadDialogVisible.value = true uploadDialogVisible.value = true
} }
...@@ -454,6 +456,9 @@ const handleUpload = async (uploadRequest) => { ...@@ -454,6 +456,9 @@ const handleUpload = async (uploadRequest) => {
const formData = new FormData() const formData = new FormData()
formData.append('file', uploadRequest.file) formData.append('file', uploadRequest.file)
formData.append('category_id', String(uploadForm.categoryId)) formData.append('category_id', String(uploadForm.categoryId))
if (uploadForm.remark.trim()) {
formData.append('remark', uploadForm.remark.trim())
}
try { try {
await uploadDataFile(formData) await uploadDataFile(formData)
...@@ -472,7 +477,7 @@ const handleDownloadTemplate = async () => { ...@@ -472,7 +477,7 @@ const handleDownloadTemplate = async () => {
const url = window.URL.createObjectURL(blob) const url = window.URL.createObjectURL(blob)
const link = document.createElement('a') const link = document.createElement('a')
link.href = url link.href = url
link.download = 'data_template.xlsx' link.download = '上传文件模板.xlsx'
document.body.appendChild(link) document.body.appendChild(link)
link.click() link.click()
link.remove() link.remove()
...@@ -508,6 +513,32 @@ const handleDeleteFile = async (row) => { ...@@ -508,6 +513,32 @@ const handleDeleteFile = async (row) => {
} }
} }
const selectedFiles = ref([])
const handleFileSelectionChange = (rows) => {
selectedFiles.value = rows
}
const handleBatchDeleteFiles = async () => {
if (!selectedFiles.value.length) return
try {
await ElMessageBox.confirm(
`确定删除选中的 ${selectedFiles.value.length} 个文件吗?此操作不可恢复。`,
'批量删除',
{ type: 'warning' },
)
await Promise.all(selectedFiles.value.map((f) => deleteDataFile(f.id)))
ElMessage.success(`已删除 ${selectedFiles.value.length} 个文件`)
if (selectedFiles.value.some((f) => f.id === currentFile.value?.id)) {
currentFile.value = null
recordList.value = []
}
selectedFiles.value = []
await loadFileList()
} catch {
// user cancelled
}
}
onMounted(async () => { onMounted(async () => {
loadLayoutCache() loadLayoutCache()
await loadCategoryTree() await loadCategoryTree()
...@@ -572,22 +603,29 @@ onBeforeUnmount(() => { ...@@ -572,22 +603,29 @@ onBeforeUnmount(() => {
<el-form :inline="true" class="search-form"> <el-form :inline="true" class="search-form">
<el-form-item> <el-form-item>
<el-input v-model="searchForm.filename" placeholder="文件名" clearable /> <el-input v-model="searchForm.filename" placeholder="文件名" clearable :prefix-icon="Search" />
</el-form-item> </el-form-item>
<el-form-item> <el-form-item class="form-actions">
<el-button
type="danger"
plain
:disabled="!selectedFiles.length"
@click="handleBatchDeleteFiles"
>批量删除</el-button>
<el-button type="primary" @click="handleSearch">搜索</el-button> <el-button type="primary" @click="handleSearch">搜索</el-button>
<el-button @click="handleReset">重置</el-button> <el-button @click="handleReset">重置</el-button>
<el-button type="info" plain @click="handleDownloadTemplate">下载模板</el-button> <el-button @click="handleDownloadTemplate">下载模板</el-button>
<el-button type="primary" :icon="Upload" @click="openUploadDialog">上传文件</el-button> <el-button type="primary" @click="openUploadDialog">上传文件</el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
<el-table :data="fileList" border stripe v-loading="loadingFiles" height="calc(100vh - 250px)" row-class-name="file-row" @row-click="handleViewFile"> <el-table :data="fileList" border stripe v-loading="loadingFiles" height="calc(100vh - 250px)" row-class-name="file-row" @row-click="handleViewFile" @selection-change="handleFileSelectionChange">
<el-table-column type="selection" width="44" @click.stop />
<el-table-column prop="filename" label="文件名" min-width="160" /> <el-table-column prop="filename" label="文件名" min-width="160" />
<el-table-column prop="remark" label="备注" min-width="120" show-overflow-tooltip /> <el-table-column prop="remark" label="备注" min-width="120" class-name="cell-wrap" />
<el-table-column prop="uploaded_at" label="上传时间" min-width="170" /> <el-table-column prop="uploaded_at" label="上传时间" min-width="170" />
<el-table-column prop="data_count" label="数据量" width="90" /> <el-table-column prop="data_count" label="数据量" width="90" />
<el-table-column label="操作" width="220" fixed="right"> <el-table-column label="操作" width="110" fixed="right">
<template #default="{ row }"> <template #default="{ row }">
<el-button link type="primary" :icon="Edit" @click.stop="openFileEditDialog(row)" /> <el-button link type="primary" :icon="Edit" @click.stop="openFileEditDialog(row)" />
<el-button link type="success" :icon="DataAnalysis" @click.stop="handleQualityCheck(row)" title="质量判定" /> <el-button link type="success" :icon="DataAnalysis" @click.stop="handleQualityCheck(row)" title="质量判定" />
...@@ -649,6 +687,16 @@ onBeforeUnmount(() => { ...@@ -649,6 +687,16 @@ onBeforeUnmount(() => {
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="备注">
<el-input
v-model="uploadForm.remark"
type="textarea"
:rows="2"
placeholder="可选,填写文件说明或标注"
maxlength="500"
/>
</el-form-item>
<el-form-item label="上传文件" required> <el-form-item label="上传文件" required>
<el-upload <el-upload
class="upload-panel" class="upload-panel"
...@@ -972,33 +1020,24 @@ onBeforeUnmount(() => { ...@@ -972,33 +1020,24 @@ onBeforeUnmount(() => {
} }
.search-form { .search-form {
display: flex;
margin-bottom: 4px; margin-bottom: 4px;
flex-shrink: 0; flex-shrink: 0;
} }
.form-actions {
margin-left: auto;
}
:deep(.el-table .cell-wrap .cell) {
white-space: normal;
word-break: break-word;
line-height: 1.5;
}
.content-wrap { .content-wrap {
flex: 1; flex: 1;
min-height: 0; min-height: 0;
} }
@media (max-width: 980px) {
.data-layout {
display: block;
overflow-y: auto;
}
.pane-divider {
display: none;
}
.pane-card {
width: 100% !important;
min-height: 420px;
margin-bottom: 12px;
border-right: none;
border-bottom: 1px solid var(--border-color);
}
}
// quality dialog // quality dialog
.quality-filename { .quality-filename {
......
...@@ -139,6 +139,29 @@ const handleDelete = async (row) => { ...@@ -139,6 +139,29 @@ const handleDelete = async (row) => {
} }
} }
const selectedRecords = ref([])
const handleRecordSelectionChange = (rows) => {
selectedRecords.value = rows
}
const handleBatchDeleteRecords = async () => {
if (!selectedRecords.value.length) return
try {
await ElMessageBox.confirm(
`确定删除选中的 ${selectedRecords.value.length} 条评估记录吗?此操作不可恢复。`,
'批量删除',
{ type: 'warning' },
)
await Promise.all(selectedRecords.value.map((r) => deleteEvalRecord(r.id)))
ElMessage.success(`已删除 ${selectedRecords.value.length} 条评估记录`)
if (selectedRecords.value.some((r) => r.id === currentResult.value?.id)) currentResult.value = null
selectedRecords.value = []
await loadRecords()
} catch {
// user cancelled
}
}
const fmtMetric = (v) => (v != null ? Number(v).toFixed(5) : '-') const fmtMetric = (v) => (v != null ? Number(v).toFixed(5) : '-')
const fmtMape = (v) => (v != null ? Number(v).toFixed(3) + ' %' : '-') const fmtMape = (v) => (v != null ? Number(v).toFixed(3) + ' %' : '-')
const fmtR2 = (v) => (v != null ? Number(v).toFixed(6) : '-') const fmtR2 = (v) => (v != null ? Number(v).toFixed(6) : '-')
...@@ -253,9 +276,18 @@ onMounted(async () => { ...@@ -253,9 +276,18 @@ onMounted(async () => {
<template #header> <template #header>
<div class="card-header-row"> <div class="card-header-row">
<span class="card-title">评估记录</span> <span class="card-title">评估记录</span>
<el-button :icon="Refresh" size="small" plain :loading="loadingRecords" @click="loadRecords"> <div style="display:flex;gap:8px;align-items:center">
刷新 <el-button
</el-button> type="danger"
plain
size="small"
:disabled="!selectedRecords.length"
@click="handleBatchDeleteRecords"
>批量删除</el-button>
<el-button :icon="Refresh" size="small" plain :loading="loadingRecords" @click="loadRecords">
刷新
</el-button>
</div>
</div> </div>
</template> </template>
...@@ -265,7 +297,9 @@ onMounted(async () => { ...@@ -265,7 +297,9 @@ onMounted(async () => {
border border
stripe stripe
height="calc(100vh - 580px)" height="calc(100vh - 580px)"
@selection-change="handleRecordSelectionChange"
> >
<el-table-column type="selection" width="44" />
<el-table-column prop="model_name" label="模型名称" min-width="140" show-overflow-tooltip /> <el-table-column prop="model_name" label="模型名称" min-width="140" show-overflow-tooltip />
<el-table-column prop="package_name" label="评估数据包" min-width="130" show-overflow-tooltip /> <el-table-column prop="package_name" label="评估数据包" min-width="130" show-overflow-tooltip />
<el-table-column label="样本数" width="88" align="center"> <el-table-column label="样本数" width="88" align="center">
......
...@@ -69,6 +69,28 @@ const handleDelete = async (row) => { ...@@ -69,6 +69,28 @@ const handleDelete = async (row) => {
} }
} }
const selectedModels = ref([])
const handleModelSelectionChange = (rows) => {
selectedModels.value = rows
}
const handleBatchDeleteModels = async () => {
if (!selectedModels.value.length) return
try {
await ElMessageBox.confirm(
`确定删除选中的 ${selectedModels.value.length} 个模型吗?删除后无法恢复。`,
'批量删除',
{ type: 'warning' },
)
await Promise.all(selectedModels.value.map((m) => deleteSavedModel(m.id)))
ElMessage.success(`已删除 ${selectedModels.value.length} 个模型`)
selectedModels.value = []
await loadModels()
} catch {
// user cancelled
}
}
// ── display helpers ─────────────────────────────────────────────────────────── // ── display helpers ───────────────────────────────────────────────────────────
const formatParams = (params) => { const formatParams = (params) => {
if (!params) return '-' if (!params) return '-'
...@@ -98,14 +120,23 @@ onMounted(loadModels) ...@@ -98,14 +120,23 @@ onMounted(loadModels)
<template #header> <template #header>
<div class="card-header-row"> <div class="card-header-row">
<span class="card-title">已保存模型</span> <span class="card-title">已保存模型</span>
<el-button :icon="Refresh" size="small" plain :loading="loading" @click="loadModels"> <div style="display:flex;gap:8px;align-items:center">
刷新 <el-button
</el-button> type="danger"
plain
size="small"
:disabled="!selectedModels.length"
@click="handleBatchDeleteModels"
>批量删除</el-button>
<el-button :icon="Refresh" size="small" plain :loading="loading" @click="loadModels">
刷新
</el-button>
</div>
</div> </div>
</template> </template>
<el-table :data="models" v-loading="loading" border stripe height="calc(100vh - 160px)"> <el-table :data="models" v-loading="loading" border stripe height="calc(100vh - 160px)" @selection-change="handleModelSelectionChange">
<el-table-column type="index" width="55" label="#" align="center" /> <el-table-column type="selection" width="44" />
<el-table-column prop="model_name" label="模型名称" min-width="140" show-overflow-tooltip /> <el-table-column prop="model_name" label="模型名称" min-width="140" show-overflow-tooltip />
<el-table-column prop="description" label="说明" min-width="160" show-overflow-tooltip> <el-table-column prop="description" label="说明" min-width="160" show-overflow-tooltip>
......
...@@ -216,6 +216,36 @@ const handleDelete = async (task) => { ...@@ -216,6 +216,36 @@ const handleDelete = async (task) => {
} }
} }
const selectedTasks = ref([])
const handleTaskSelectionChange = (rows) => {
selectedTasks.value = rows
}
const handleBatchDeleteTasks = async () => {
if (!selectedTasks.value.length) return
const deletable = selectedTasks.value.filter(
(t) => t.status !== 'pending' && t.status !== 'running',
)
if (!deletable.length) {
ElMessage.warning('选中的任务均在运行中,请先取消后再删除')
return
}
const skipped = selectedTasks.value.length - deletable.length
const tip = skipped > 0
? `确定删除选中的 ${deletable.length} 个任务?(${skipped} 个运行中的任务将被跳过)`
: `确定删除选中的 ${deletable.length} 个任务吗?此操作不可恢复。`
try {
await ElMessageBox.confirm(tip, '批量删除', { type: 'warning' })
await Promise.all(deletable.map((t) => deleteTrainTask(t.id)))
ElMessage.success(`已删除 ${deletable.length} 个任务`)
if (deletable.some((t) => t.id === selectedTask.value?.id)) selectedTask.value = null
selectedTasks.value = []
await loadTasks()
} catch {
// user cancelled
}
}
// ── task detail (epoch logs) ────────────────────────────────────────────────── // ── task detail (epoch logs) ──────────────────────────────────────────────────
const selectedTask = ref(null) const selectedTask = ref(null)
const detailMode = ref('table') // 'table' | 'chart' const detailMode = ref('table') // 'table' | 'chart'
...@@ -408,7 +438,16 @@ onBeforeUnmount(() => { ...@@ -408,7 +438,16 @@ onBeforeUnmount(() => {
<template #header> <template #header>
<div class="card-header-row"> <div class="card-header-row">
<span class="card-title">训练记录</span> <span class="card-title">训练记录</span>
<el-button :icon="Refresh" size="small" plain :loading="loadingTasks" @click="loadTasks">刷新</el-button> <div style="display:flex;gap:8px;align-items:center">
<el-button
type="danger"
plain
size="small"
:disabled="!selectedTasks.length"
@click="handleBatchDeleteTasks"
>批量删除</el-button>
<el-button :icon="Refresh" size="small" plain :loading="loadingTasks" @click="loadTasks">刷新</el-button>
</div>
</div> </div>
</template> </template>
...@@ -421,7 +460,9 @@ onBeforeUnmount(() => { ...@@ -421,7 +460,9 @@ onBeforeUnmount(() => {
height="100%" height="100%"
style="cursor:pointer" style="cursor:pointer"
@row-click="handleRowClick" @row-click="handleRowClick"
@selection-change="handleTaskSelectionChange"
> >
<el-table-column type="selection" width="44" @click.stop />
<el-table-column prop="model_name" label="模型名称" min-width="130" show-overflow-tooltip /> <el-table-column prop="model_name" label="模型名称" min-width="130" show-overflow-tooltip />
<el-table-column label="训练集" min-width="120" show-overflow-tooltip> <el-table-column label="训练集" min-width="120" show-overflow-tooltip>
<template #default="{ row }">{{ row.package_name }}</template> <template #default="{ row }">{{ row.package_name }}</template>
...@@ -475,10 +516,13 @@ onBeforeUnmount(() => { ...@@ -475,10 +516,13 @@ onBeforeUnmount(() => {
<template #header> <template #header>
<div class="card-header-row"> <div class="card-header-row">
<span class="card-title">训练过程 — {{ selectedTask.model_name }}</span> <span class="card-title">训练过程 — {{ selectedTask.model_name }}</span>
<el-radio-group v-model="detailMode" size="small"> <div style="display:flex;gap:8px;align-items:center">
<el-radio-button value="table">表格</el-radio-button> <el-radio-group v-model="detailMode" size="small">
<el-radio-button value="chart">曲线</el-radio-button> <el-radio-button value="table">表格</el-radio-button>
</el-radio-group> <el-radio-button value="chart">曲线</el-radio-button>
</el-radio-group>
<el-button size="small" plain @click="selectedTask = null">关闭</el-button>
</div>
</div> </div>
</template> </template>
......
...@@ -26,6 +26,11 @@ const loading = ref(false) ...@@ -26,6 +26,11 @@ const loading = ref(false)
const packageList = ref([]) const packageList = ref([])
const searchName = ref('') const searchName = ref('')
const currentPackage = ref(null) const currentPackage = ref(null)
const selectedPackages = ref([])
const handleSelectionChange = (rows) => {
selectedPackages.value = rows
}
const loadPackages = async () => { const loadPackages = async () => {
loading.value = true loading.value = true
...@@ -122,6 +127,26 @@ const handleDelete = async (row) => { ...@@ -122,6 +127,26 @@ const handleDelete = async (row) => {
} }
} }
const handleBatchDelete = async () => {
if (!selectedPackages.value.length) return
try {
await ElMessageBox.confirm(
`确定删除选中的 ${selectedPackages.value.length} 个数据包吗?此操作不可恢复。`,
'批量删除',
{ type: 'warning' },
)
await Promise.all(selectedPackages.value.map((p) => deletePackage(p.id)))
ElMessage.success(`已删除 ${selectedPackages.value.length} 个数据包`)
if (selectedPackages.value.some((p) => p.id === currentPackage.value?.id)) {
currentPackage.value = null
}
selectedPackages.value = []
await loadPackages()
} catch {
// user cancelled
}
}
watch(() => props.categoryId, loadPackages) watch(() => props.categoryId, loadPackages)
watch(() => props.refreshKey, () => { watch(() => props.refreshKey, () => {
currentPackage.value = null currentPackage.value = null
...@@ -136,22 +161,32 @@ onMounted(loadPackages) ...@@ -136,22 +161,32 @@ onMounted(loadPackages)
<template #header> <template #header>
<div class="pane-header"> <div class="pane-header">
<span>数据包列表</span> <span>数据包列表</span>
<el-button type="primary" :icon="Plus" size="small" @click="emit('add')">新增数据包</el-button>
</div> </div>
</template> </template>
<div class="search-bar"> <el-form :inline="true" class="search-form">
<el-input <el-form-item>
v-model="searchName" <el-input
placeholder="数据包名称" v-model="searchName"
clearable placeholder="数据包名称"
:prefix-icon="Search" clearable
@keyup.enter="handleSearch" :prefix-icon="Search"
@clear="handleReset" @keyup.enter="handleSearch"
/> @clear="handleReset"
<el-button type="primary" @click="handleSearch">搜索</el-button> />
<el-button @click="handleReset">重置</el-button> </el-form-item>
</div> <el-form-item class="form-actions">
<el-button
type="danger"
plain
:disabled="!selectedPackages.length"
@click="handleBatchDelete"
>批量删除</el-button>
<el-button type="primary" @click="handleSearch">搜索</el-button>
<el-button @click="handleReset">重置</el-button>
<el-button type="primary" @click="emit('add')">新增数据包</el-button>
</el-form-item>
</el-form>
<el-table <el-table
:data="packageList" :data="packageList"
...@@ -163,7 +198,9 @@ onMounted(loadPackages) ...@@ -163,7 +198,9 @@ onMounted(loadPackages)
:row-class-name="({ row }) => (currentPackage?.id === row.id ? 'current-row' : '')" :row-class-name="({ row }) => (currentPackage?.id === row.id ? 'current-row' : '')"
style="cursor: pointer" style="cursor: pointer"
@row-click="handleView" @row-click="handleView"
@selection-change="handleSelectionChange"
> >
<el-table-column type="selection" width="44" @click.stop />
<el-table-column prop="name" label="数据包名称" min-width="100" show-overflow-tooltip /> <el-table-column prop="name" label="数据包名称" min-width="100" show-overflow-tooltip />
<el-table-column prop="created_at" label="创建时间" min-width="100" /> <el-table-column prop="created_at" label="创建时间" min-width="100" />
<el-table-column prop="data_count" label="数据量" width="80" align="center" /> <el-table-column prop="data_count" label="数据量" width="80" align="center" />
...@@ -243,14 +280,13 @@ onMounted(loadPackages) ...@@ -243,14 +280,13 @@ onMounted(loadPackages)
color: var(--text-primary); color: var(--text-primary);
} }
.search-bar { .search-form {
display: flex; display: flex;
gap: 8px; margin-bottom: 4px;
margin-bottom: 12px; flex-shrink: 0;
}
.el-input { .form-actions {
flex: 1; margin-left: auto;
}
} }
:deep(.el-table .current-row > td) { :deep(.el-table .current-row > td) {
......
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