feat: 前端增加提交功能

This commit is contained in:
2025-08-19 21:02:49 +08:00
parent 2aef180ddb
commit ca0322137b
6 changed files with 236 additions and 101 deletions

View File

@@ -8,19 +8,7 @@
{{ mode === "create" ? "新建实验" : "编辑实验" }}
</h2>
<button @click="close" 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>
<XIcon class="w-6 h-6" />
</button>
</div>
@@ -417,11 +405,13 @@ import {
BinaryIcon,
FileArchiveIcon,
FileJsonIcon,
XIcon,
} from "lucide-vue-next";
import {
ExamClient,
ExamDto,
ResourceClient,
ResourcePurpose,
type FileParameter,
} from "@/APIClient";
import { useAlertStore } from "@/components/Alert";
@@ -685,7 +675,12 @@ async function uploadExamResources(examId: string) {
data: uploadFiles.value.mdFile,
fileName: uploadFiles.value.mdFile.name,
};
await client.addResource("doc", "template", examId, mdFileParam);
await client.addResource(
"doc",
ResourcePurpose.Template,
examId,
mdFileParam,
);
console.log("MD文档上传成功");
}
@@ -695,7 +690,12 @@ async function uploadExamResources(examId: string) {
data: imageFile,
fileName: imageFile.name,
};
await client.addResource("image", "template", examId, imageFileParam);
await client.addResource(
"image",
ResourcePurpose.Template,
examId,
imageFileParam,
);
console.log("图片上传成功:", imageFile.name);
}
@@ -707,7 +707,7 @@ async function uploadExamResources(examId: string) {
};
await client.addResource(
"bitstream",
"template",
ResourcePurpose.Template,
examId,
bitstreamFileParam,
);
@@ -720,7 +720,12 @@ async function uploadExamResources(examId: string) {
data: canvasFile,
fileName: canvasFile.name,
};
await client.addResource("canvas", "template", examId, canvasFileParam);
await client.addResource(
"canvas",
ResourcePurpose.Template,
examId,
canvasFileParam,
);
console.log("画布模板上传成功:", canvasFile.name);
}
@@ -732,7 +737,7 @@ async function uploadExamResources(examId: string) {
};
await client.addResource(
"resource",
"template",
ResourcePurpose.Template,
examId,
resourceFileParam,
);
@@ -775,6 +780,7 @@ defineExpose({
show,
close,
editExam,
editExamInfo,
});
</script>

View File

@@ -13,19 +13,7 @@
@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>
<XIcon class="w-6 h-6" />
</button>
</div>
@@ -147,17 +135,18 @@
<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 class="badge badge-error">
{{
isUndefined(commitsList) || commitsList.length === 0
? "未提交"
: "已提交"
}}
</div>
</div>
<div class="flex justify-between items-center">
<span class="text-base-content/70">成绩</span>
<span class="text-base-content/50">未评分</span>
<div class="badge badge-ghost">未评分</div>
</div>
</div>
@@ -167,17 +156,22 @@
<div class="space-y-3">
<h4 class="font-medium text-base-content">提交历史</h4>
<div
v-if="isUndefined(commitsList)"
v-if="isUndefined(commitsList) || commitsList.length === 0"
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>
<li
class="step"
:class="{ 'step-primary': _idx === 1 }"
v-for="(commit, _idx) in commitsList.slice(0, 3)"
>
{{ commit.id }} ---
{{ commit.uploadTime.toDateString() }}
</li>
<li class="step" v-if="commitsList.length > 3">......</li>
</ul>
</div>
</div>
@@ -187,58 +181,27 @@
<!-- 操作按钮 -->
<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>
<Smile class="w-5 h-5" />
开始实验
</button>
<button @click="uploadModal?.show" class="btn btn-info w-full">
<Upload class="w-5 h-5" />
提交实验
</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>
<Download class="w-5 h-5" />
<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>
<GitGraph class="w-5 h-5" />
查看记录
</button>
</div>
@@ -247,6 +210,12 @@
</div>
</div>
<div class="modal-backdrop" @click="closeExamDetail"></div>
<UploadModal
ref="uploadModal"
class="fixed z-auto"
:auto-upload="true"
:callback="submitExam"
/>
</div>
</template>
<script setup lang="ts">
@@ -266,10 +235,16 @@ import { formatDate } from "@/utils/Common";
import { computed } from "vue";
import { watch } from "vue";
import { isNull, isUndefined } from "lodash";
import { Download, GitGraph, Smile, Upload, XIcon } from "lucide-vue-next";
import UploadModal from "@/components/UploadModal.vue";
import { templateRef } from "@vueuse/core";
import { toFileParameter } from "@/utils/Common";
const alertStore = useRequiredInjection(useAlertStore);
const router = useRouter();
const uploadModal = templateRef("uploadModal");
const show = defineModel<boolean>("show", {
default: false,
});
@@ -350,6 +325,24 @@ const startExam = () => {
}
};
const submitExam = (files: File[]) => {
try {
const client = AuthManager.createClient(ResourceClient);
for (const file of files) {
client.addResource(
"compression",
ResourcePurpose.Homework,
props.selectedExam.id,
toFileParameter(file),
);
}
} catch (err: any) {
alertStore.error(err.message || "上传资料失败");
console.error("上传资料失败:", err);
}
};
const closeExamDetail = () => {
show.value = false;
};

View File

@@ -191,6 +191,7 @@ import { templateRef } from "@vueuse/core";
import { isArray, isNull } from "lodash";
import { watch } from "vue";
import { watchEffect } from "vue";
import { nextTick } from "vue";
const router = useRouter();
const route = useRoute();
@@ -218,13 +219,18 @@ watch(
},
);
watchEffect(() => {
if (showEditModal.value) {
router.replace({ path: `/exam/edit` });
} else {
router.replace({ path: `/exam` });
}
});
watch(
() => showEditModal.value,
() => {
if (showEditModal.value) {
router.replace({
path: `/exam/edit/${examEditModalRef.value?.editExamInfo.id}`,
});
} else {
router.replace({ path: `/exam` });
}
},
);
async function refreshExams() {
loading.value = true;
@@ -263,7 +269,8 @@ async function handleCardClicked(event: MouseEvent, examId: string) {
}
async function handleEditExamClicked(event: MouseEvent, examId: string) {
examEditModalRef?.value?.editExam(examId);
await examEditModalRef?.value?.editExam(examId);
router.replace(`/exam/edit/${examId}`);
}
// 生命周期
@@ -278,17 +285,28 @@ onMounted(async () => {
await refreshExams();
});
async function loadBasicPage(page: string) {
if (page === "") return;
else if (page === "edit") showEditModal.value = true;
else if (page) await viewExam(page);
else router.push("/exam");
}
onMounted(async () => {
// 处理路由参数如果有examId则自动打开该实验的详情模态框
const examId = route.params.examId as string;
if (examId === "") return;
const page = route.params.page;
if (isArray(examId)) return;
if (examId === "edit") showEditModal.value = true;
if (examId) {
await viewExam(examId);
if (Array.isArray(page)) {
if (page.length == 1) await loadBasicPage(page[0]);
else if (page.length == 2) {
if (page[0] === "edit") {
await examEditModalRef.value?.editExam(page[1]);
} else {
router.push("/exam");
}
} else router.push("/exam");
} else {
await loadBasicPage(page);
}
});
</script>