add: 添加实验列表界面,实验增删完全依赖数据库实现

This commit is contained in:
alivender
2025-08-01 12:57:30 +08:00
parent d27b5d7737
commit c564844673
19 changed files with 3511 additions and 1688 deletions

View File

@@ -1,21 +1,64 @@
<template>
<div class="flex flex-col bg-base-100 justify-center items-center">
<div class="flex flex-col bg-base-100 justify-center items-center gap-4">
<!-- Title -->
<h1 class="font-bold text-2xl">上传比特流文件</h1>
<h1 class="font-bold text-2xl">比特流文件</h1>
<!-- 示例比特流下载区域 (仅在有examId时显示) -->
<div v-if="examId && availableBitstreams.length > 0" class="w-full">
<fieldset class="fieldset w-full">
<legend class="fieldset-legend text-sm">示例比特流文件</legend>
<div class="space-y-2">
<div v-for="bitstream in availableBitstreams" :key="bitstream.id" class="flex items-center justify-between p-2 bg-base-200 rounded">
<span class="text-sm">{{ bitstream.name }}</span>
<div class="flex gap-2">
<button
@click="downloadExampleBitstream(bitstream)"
class="btn btn-sm btn-secondary"
:disabled="isDownloading || isProgramming"
>
<div v-if="isDownloading">
<span class="loading loading-spinner loading-xs"></span>
下载中...
</div>
<div v-else>
下载示例
</div>
</button>
<button
@click="programExampleBitstream(bitstream)"
class="btn btn-sm btn-primary"
:disabled="isDownloading || isProgramming || !uploadEvent"
>
<div v-if="isProgramming">
<span class="loading loading-spinner loading-xs"></span>
烧录中...
</div>
<div v-else>
直接烧录
</div>
</button>
</div>
</div>
</div>
</fieldset>
</div>
<!-- 分割线 -->
<div v-if="examId && availableBitstreams.length > 0" class="divider"></div>
<!-- Input File -->
<fieldset class="fieldset w-full">
<legend class="fieldset-legend text-sm">选择或拖拽上传文件</legend>
<legend class="fieldset-legend text-sm">上传自定义比特流文件</legend>
<input type="file" ref="fileInput" class="file-input w-full" @change="handleFileChange" />
<label class="fieldset-label">文件最大容量: {{ maxMemory }}MB</label>
</fieldset>
<!-- Upload Button -->
<div class="card-actions w-full">
<button @click="handleClick" class="btn btn-primary grow" :disabled="isUploading">
<button @click="handleClick" class="btn btn-primary grow" :disabled="isUploading || isProgramming">
<div v-if="isUploading">
<span class="loading loading-spinner"></span>
下载...
上传...
</div>
<div v-else>
{{ buttonText }}
@@ -27,6 +70,7 @@
<script lang="ts" setup>
import { computed, ref, useTemplateRef, onMounted } from "vue";
import { AuthManager } from "@/utils/AuthManager"; // Adjust the path based on your project structure
import { useDialogStore } from "@/stores/dialog";
import { isNull, isUndefined } from "lodash";
@@ -34,10 +78,12 @@ interface Props {
uploadEvent?: (file: File) => Promise<boolean>;
downloadEvent?: () => Promise<boolean>;
maxMemory?: number;
examId?: string; // 新增examId属性
}
const props = withDefaults(defineProps<Props>(), {
maxMemory: 4,
examId: '',
});
const emits = defineEmits<{
@@ -47,6 +93,10 @@ const emits = defineEmits<{
const dialog = useDialogStore();
const isUploading = ref(false);
const isDownloading = ref(false);
const isProgramming = ref(false);
const availableBitstreams = ref<{id: number, name: string}[]>([]);
const buttonText = computed(() => {
return isUndefined(props.downloadEvent) ? "上传" : "上传并下载";
});
@@ -56,14 +106,113 @@ const bitstream = defineModel("bitstreamFile", {
type: File,
default: undefined,
});
onMounted(() => {
// 初始化时加载示例比特流
onMounted(async () => {
if (!isUndefined(bitstream.value) && !isNull(fileInput.value)) {
let fileList = new DataTransfer();
fileList.items.add(bitstream.value);
fileInput.value.files = fileList.files;
}
await loadAvailableBitstreams();
});
// 加载可用的比特流文件列表
async function loadAvailableBitstreams() {
console.log('加载可用比特流文件examId:', props.examId);
if (!props.examId) {
availableBitstreams.value = [];
return;
}
try {
const examClient = AuthManager.createAuthenticatedExamClient();
// 使用新的API获取比特流资源列表
const resources = await examClient.getExamResourceList(props.examId, 'bitstream');
availableBitstreams.value = resources.map(r => ({ id: r.id, name: r.name })) || [];
} catch (error) {
console.error('加载比特流列表失败:', error);
availableBitstreams.value = [];
}
}
// 下载示例比特流
async function downloadExampleBitstream(bitstream: {id: number, name: string}) {
if (isDownloading.value) return;
isDownloading.value = true;
try {
const examClient = AuthManager.createAuthenticatedExamClient();
// 使用动态API获取资源文件
const response = await examClient.getExamResourceById(bitstream.id);
if (response && response.data) {
// 创建下载链接
const url = URL.createObjectURL(response.data);
const link = document.createElement('a');
link.href = url;
link.download = response.fileName || bitstream.name;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(url);
dialog.info("示例比特流下载成功");
} else {
dialog.error("下载失败:响应数据为空");
}
} catch (error) {
console.error('下载示例比特流失败:', error);
dialog.error("下载示例比特流失败");
} finally {
isDownloading.value = false;
}
}
// 直接烧录示例比特流
async function programExampleBitstream(bitstream: {id: number, name: string}) {
if (isProgramming.value || !props.uploadEvent) return;
isProgramming.value = true;
try {
const examClient = AuthManager.createAuthenticatedExamClient();
// 使用动态API获取比特流文件数据
const response = await examClient.getExamResourceById(bitstream.id);
if (!response || !response.data) {
throw new Error('获取比特流文件失败');
}
const file = new File([response.data], response.fileName || bitstream.name, { type: response.data.type });
// 调用上传事件
const uploadSuccess = await props.uploadEvent(file);
if (uploadSuccess) {
// 如果有下载事件(烧录),则执行
if (props.downloadEvent) {
const downloadSuccess = await props.downloadEvent();
if (downloadSuccess) {
dialog.info("示例比特流烧录成功");
} else {
dialog.error("烧录失败");
}
} else {
dialog.info("示例比特流上传成功");
}
} else {
dialog.error("上传失败");
}
} catch (error) {
console.error('烧录示例比特流失败:', error);
dialog.error("烧录示例比特流失败");
} finally {
isProgramming.value = false;
}
}
function handleFileChange(event: Event): void {
const target = event.target as HTMLInputElement;
const file = target.files?.[0]; // 获取选中的第一个文件