Initial certificate system
This commit is contained in:
166
frontend/src/views/AdminImports.vue
Normal file
166
frontend/src/views/AdminImports.vue
Normal file
@@ -0,0 +1,166 @@
|
||||
<template>
|
||||
<section>
|
||||
<header class="page-head">
|
||||
<div>
|
||||
<h2>Excel 导入</h2>
|
||||
<p>上传后先校验,确认导入后才会正式写入学员和证书数据。</p>
|
||||
</div>
|
||||
<el-button @click="downloadTemplate">下载模板</el-button>
|
||||
</header>
|
||||
|
||||
<el-upload
|
||||
class="upload"
|
||||
drag
|
||||
:auto-upload="false"
|
||||
:show-file-list="true"
|
||||
:limit="1"
|
||||
accept=".xlsx"
|
||||
@change="pickFile"
|
||||
@remove="selectedFile = null"
|
||||
>
|
||||
<el-icon class="upload-icon"><UploadFilled /></el-icon>
|
||||
<div>把 Excel 文件拖到这里,或点击选择文件</div>
|
||||
</el-upload>
|
||||
<el-button type="primary" :disabled="!selectedFile" :loading="uploading" @click="uploadFile">上传并校验</el-button>
|
||||
|
||||
<el-card class="panel" shadow="never">
|
||||
<el-table :data="batches" border>
|
||||
<el-table-column prop="id" label="批次ID" width="90" />
|
||||
<el-table-column prop="filename" label="文件名" min-width="220" />
|
||||
<el-table-column label="状态" width="120">
|
||||
<template #default="{ row }">{{ statusText(row.status) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="total_rows" label="总行数" width="100" />
|
||||
<el-table-column prop="valid_rows" label="可导入" width="100" />
|
||||
<el-table-column prop="failed_rows" label="失败" width="100" />
|
||||
<el-table-column prop="created_at" label="上传时间" width="190" />
|
||||
<el-table-column label="操作" width="380">
|
||||
<template #default="{ row }">
|
||||
<el-button size="small" @click="downloadSource(row)">下载原文件</el-button>
|
||||
<el-button
|
||||
v-if="row.status === 'validated'"
|
||||
size="small"
|
||||
type="primary"
|
||||
:loading="confirmingId === row.id"
|
||||
@click="confirmBatch(row)"
|
||||
>
|
||||
确认导入
|
||||
</el-button>
|
||||
<el-button v-if="row.error_report_path" size="small" @click="downloadErrorReport(row)">错误报告</el-button>
|
||||
<el-button size="small" type="danger" @click="deleteBatch(row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { UploadFilled } from "@element-plus/icons-vue";
|
||||
import { ElMessage, ElMessageBox, type UploadFile } from "element-plus";
|
||||
import { onMounted, ref } from "vue";
|
||||
|
||||
import { http, type ImportBatch } from "../api";
|
||||
import { downloadFile } from "../download";
|
||||
|
||||
const batches = ref<ImportBatch[]>([]);
|
||||
const selectedFile = ref<File | null>(null);
|
||||
const uploading = ref(false);
|
||||
const confirmingId = ref<number | null>(null);
|
||||
|
||||
function statusText(status: string) {
|
||||
const labels: Record<string, string> = {
|
||||
uploaded: "已上传",
|
||||
validated: "已校验",
|
||||
imported: "已导入",
|
||||
failed: "校验失败",
|
||||
};
|
||||
return labels[status] || status;
|
||||
}
|
||||
|
||||
function pickFile(file: UploadFile) {
|
||||
selectedFile.value = file.raw || null;
|
||||
}
|
||||
|
||||
function downloadTemplate() {
|
||||
downloadFile("/admin/import-batches/template", "certificate-import-template.xlsx");
|
||||
}
|
||||
|
||||
async function loadBatches() {
|
||||
const { data } = await http.get<ImportBatch[]>("/admin/import-batches");
|
||||
batches.value = data;
|
||||
}
|
||||
|
||||
async function uploadFile() {
|
||||
if (!selectedFile.value) return;
|
||||
uploading.value = true;
|
||||
try {
|
||||
const body = new FormData();
|
||||
body.append("file", selectedFile.value);
|
||||
await http.post("/admin/import-batches", body);
|
||||
ElMessage.success("文件已上传并完成校验,请检查结果后点击确认导入");
|
||||
selectedFile.value = null;
|
||||
await loadBatches();
|
||||
} finally {
|
||||
uploading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function confirmBatch(row: ImportBatch) {
|
||||
await ElMessageBox.confirm(`确认把批次 ${row.id} 中 ${row.valid_rows} 行校验通过的数据写入系统?`, "确认导入", { type: "warning" });
|
||||
confirmingId.value = row.id;
|
||||
try {
|
||||
const { data } = await http.post<ImportBatch>(`/admin/import-batches/${row.id}/confirm`);
|
||||
row.status = data.status;
|
||||
ElMessage.success(`导入完成,当前状态:${statusText(data.status)}`);
|
||||
await loadBatches();
|
||||
} finally {
|
||||
confirmingId.value = null;
|
||||
}
|
||||
}
|
||||
|
||||
function downloadSource(row: ImportBatch) {
|
||||
downloadFile(`/admin/import-batches/${row.id}/file`, row.filename);
|
||||
}
|
||||
|
||||
function downloadErrorReport(row: ImportBatch) {
|
||||
downloadFile(`/admin/import-batches/${row.id}/error-report`, `import-errors-${row.id}.xlsx`);
|
||||
}
|
||||
|
||||
async function deleteBatch(row: ImportBatch) {
|
||||
await ElMessageBox.confirm(`确认删除导入批次 ${row.id}?这只删除上传文件和导入记录,不会删除已经生成的学员和证书。`, "删除导入记录", { type: "warning" });
|
||||
await http.delete(`/admin/import-batches/${row.id}`);
|
||||
ElMessage.success("导入记录已删除");
|
||||
await loadBatches();
|
||||
}
|
||||
|
||||
onMounted(loadBatches);
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.page-head {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.page-head p {
|
||||
margin: 6px 0 0;
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
.upload {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.upload-icon {
|
||||
font-size: 36px;
|
||||
color: #208a87;
|
||||
}
|
||||
|
||||
.panel {
|
||||
margin-top: 20px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user