This repository has been archived on 2025-10-29. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
FPGA_WebLab/src/views/Exam/ExamInfoModal.vue

353 lines
12 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div v-if="show" class="modal modal-open overflow-hidden">
<div
class="modal-box w-full max-w-6xl h-[90vh] max-h-[90vh] p-0 overflow-hidden"
>
<div
class="flex justify-between items-center p-6 border-b border-base-300"
>
<h2 class="text-2xl font-bold text-base-content">
{{ selectedExam.id }} - {{ selectedExam.name }}
</h2>
<button
@click="closeExamDetail"
class="btn btn-sm btn-circle btn-ghost"
>
<svg
class="w-6 h-6"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M6 18L18 6M6 6l12 12"
/>
</svg>
</button>
</div>
<div class="flex h-[calc(90vh-5rem)]">
<!-- 左侧实验信息和描述 -->
<div class="flex-1 p-6 overflow-y-auto border-r border-base-300">
<div class="space-y-6">
<!-- 实验信息 -->
<div class="card bg-base-200">
<div class="card-body">
<h3 class="card-title text-lg mb-4">实验信息</h3>
<div class="space-y-3">
<div class="flex">
<span class="font-medium text-base-content w-24"
>实验ID</span
>
<span class="text-base-content/70">{{
selectedExam.id
}}</span>
</div>
<div class="flex">
<span class="font-medium text-base-content w-24"
>实验名称</span
>
<span class="text-base-content/70">{{
selectedExam.name
}}</span>
</div>
<div class="flex">
<span class="font-medium text-base-content w-24"
>难度等级</span
>
<div class="flex items-center gap-2">
<div class="rating rating-sm">
<span
v-for="i in 5"
:key="i"
class="mask mask-star-2"
:class="
i <= selectedExam.difficulty
? 'bg-orange-400'
: 'bg-base-300'
"
></span>
</div>
<span class="text-sm text-base-content/50"
>({{ selectedExam.difficulty }}/5)</span
>
</div>
</div>
<div
v-if="selectedExam.tags && selectedExam.tags.length > 0"
class="flex"
>
<span class="font-medium text-base-content w-24"
>标签</span
>
<div class="flex flex-wrap gap-1">
<span
v-for="tag in selectedExam.tags"
:key="tag"
class="badge badge-outline badge-sm"
>{{ tag }}</span
>
</div>
</div>
<div class="flex">
<span class="font-medium text-base-content w-24"
>创建时间</span
>
<span class="text-base-content/70">{{
formatDate(selectedExam.createdTime)
}}</span>
</div>
<div class="flex">
<span class="font-medium text-base-content w-24"
>更新时间</span
>
<span class="text-base-content/70">{{
formatDate(selectedExam.updatedTime)
}}</span>
</div>
<div class="flex">
<span class="font-medium text-base-content w-24"
>可见性</span
>
<span class="text-base-content/70">{{
selectedExam.isVisibleToUsers
? "对学生可见"
: "仅管理员可见"
}}</span>
</div>
</div>
</div>
</div>
<!-- 实验描述 -->
<div class="card bg-base-200">
<div class="card-body">
<h3 class="card-title text-lg mb-4">实验描述</h3>
<div class="prose prose-sm max-w-none">
<p class="text-base-content/70">
{{ selectedExam.description }}
</p>
</div>
</div>
</div>
</div>
</div>
<!-- 右侧完成情况和控制 -->
<div class="w-80 p-6 bg-base-200 overflow-y-auto">
<div class="space-y-6">
<!-- 完成情况 -->
<div class="card bg-base-100">
<div class="card-body">
<h3 class="card-title text-lg mb-4">完成情况</h3>
<div class="space-y-4">
<div class="flex justify-between items-center">
<span class="text-base-content/70">当前状态</span>
<div class="badge badge-error">未完成</div>
</div>
<div class="flex justify-between items-center">
<span class="text-base-content/70">批阅状态</span>
<div class="badge badge-ghost">待提交</div>
</div>
<div class="flex justify-between items-center">
<span class="text-base-content/70">成绩</span>
<span class="text-base-content/50">未评分</span>
</div>
</div>
<div class="divider"></div>
<!-- 提交历史 -->
<div class="space-y-3">
<h4 class="font-medium text-base-content">提交历史</h4>
<div
v-if="isUndefined(commitsList)"
class="text-sm text-base-content/50 text-center py-4"
>
暂无提交记录
</div>
<div v-else class="overflow-y-auto">
<ul class="steps steps-vertical">
<li class="step step-primary">Register</li>
<li class="step step-primary">Choose plan</li>
<li class="step">Purchase</li>
<li class="step">Receive Product</li>
</ul>
</div>
</div>
</div>
</div>
<!-- 操作按钮 -->
<div class="space-y-3">
<button @click="startExam" class="btn btn-primary w-full">
<svg
class="w-5 h-5 mr-2"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M14.828 14.828a4 4 0 01-5.656 0M9 10h1m4 0h1m-6 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
/>
</svg>
开始实验
</button>
<button
@click="downloadResources"
class="btn btn-outline w-full"
:disabled="downloadingResources"
>
<svg
class="w-5 h-5 mr-2"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"
/>
</svg>
<span v-if="downloadingResources">下载中...</span>
<span v-else>下载资源包</span>
</button>
<button class="btn btn-outline w-full">
<svg
class="w-5 h-5 mr-2"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"
/>
</svg>
查看记录
</button>
</div>
</div>
</div>
</div>
</div>
<div class="modal-backdrop" @click="closeExamDetail"></div>
</div>
</template>
<script setup lang="ts">
import type { Commit, ExamInfo } from "@/APIClient";
import { useAlertStore } from "@/components/Alert";
import { AuthManager } from "@/utils/AuthManager";
import { useRequiredInjection } from "@/utils/Common";
import { defineModel, ref } from "vue";
import { useRouter } from "vue-router";
import { formatDate } from "@/utils/Common";
import { computed } from "vue";
import { watch } from "vue";
import { isNull, isUndefined } from "lodash";
const alertStore = useRequiredInjection(useAlertStore);
const router = useRouter();
const show = defineModel<boolean>("show", {
default: false,
});
const props = defineProps<{
selectedExam: ExamInfo;
}>();
const commitsList = ref<Commit[]>();
async function updateCommits() {
const client = AuthManager.createAuthenticatedExamClient();
const list = await client.getCommitsByExamId(props.selectedExam.id);
commitsList.value = list;
}
watch(() => props.selectedExam, updateCommits);
// Download resources
const downloadingResources = ref(false);
const downloadResources = async () => {
if (!props.selectedExam || downloadingResources.value) return;
downloadingResources.value = true;
try {
const resourceClient = AuthManager.createAuthenticatedResourceClient();
// 获取资源包列表(模板资源)
const resourceList = await resourceClient.getResourceList(
props.selectedExam.id,
"resource",
"template",
);
if (resourceList && resourceList.length > 0) {
// 使用新的ResourceClient API获取第一个资源包
const resourceId = resourceList[0].id;
const fileResponse = await resourceClient.getResourceById(resourceId);
// 创建Blob URL
const blobUrl = URL.createObjectURL(fileResponse.data);
// 创建下载链接
const link = document.createElement("a");
link.href = blobUrl;
link.download =
fileResponse.fileName ||
resourceList[0].name ||
`${props.selectedExam.name}_资源包`;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
// 清理Blob URL
URL.revokeObjectURL(blobUrl);
alertStore.success("资料下载成功");
console.log("资料下载成功:", props.selectedExam.id);
} else {
alertStore.error("该实验暂无资料包");
}
} catch (err: any) {
alertStore.error(err.message || "下载资料失败");
console.error("下载资料失败:", err);
} finally {
downloadingResources.value = false;
}
};
// 开始实验
const startExam = () => {
if (props.selectedExam) {
// 跳转到项目页面传递实验ID
console.log("开始实验:", props.selectedExam.id);
router.push({
name: "project",
query: { examId: props.selectedExam.id },
});
}
};
const closeExamDetail = () => {
show.value = false;
};
</script>
<style lang="postcss" scoped></style>