新增课程起止日期管理
This commit is contained in:
@@ -19,6 +19,8 @@ export interface PublicCertificate {
|
||||
project_code: string;
|
||||
course_name: string | null;
|
||||
stage_name: string | null;
|
||||
course_start_date: string | null;
|
||||
course_end_date: string | null;
|
||||
issue_date: string;
|
||||
issuer_name: string;
|
||||
status: string;
|
||||
@@ -70,6 +72,8 @@ export interface AdminCertificate {
|
||||
class_name: string | null;
|
||||
course_name: string | null;
|
||||
stage_name: string | null;
|
||||
course_start_date: string | null;
|
||||
course_end_date: string | null;
|
||||
issue_date: string;
|
||||
issuer_name: string;
|
||||
status: string;
|
||||
|
||||
@@ -21,6 +21,13 @@
|
||||
<el-form-item label="发证日期">
|
||||
<el-date-picker v-model="form.issue_date" type="date" value-format="YYYY-MM-DD" />
|
||||
</el-form-item>
|
||||
<el-form-item label="课程开始日期">
|
||||
<el-date-picker v-model="form.course_start_date" type="date" value-format="YYYY-MM-DD" placeholder="选择开始日期" />
|
||||
</el-form-item>
|
||||
<el-form-item label="课程结束日期">
|
||||
<el-date-picker v-model="form.course_end_date" type="date" value-format="YYYY-MM-DD" placeholder="选择结束日期" />
|
||||
<div class="form-tip">证书正文将显示这段课程起止时间,结束日期不能早于开始日期。</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="课程名称">
|
||||
<el-input v-model.trim="form.course_name" />
|
||||
</el-form-item>
|
||||
@@ -64,6 +71,9 @@
|
||||
<el-table-column prop="learner_name" label="学员姓名" width="100" show-overflow-tooltip />
|
||||
<el-table-column prop="project_code" label="项目" width="72" />
|
||||
<el-table-column prop="certificate_name" label="证书名称" min-width="140" show-overflow-tooltip />
|
||||
<el-table-column label="课程时间" width="190">
|
||||
<template #default="{ row }">{{ coursePeriodText(row) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="issue_date" label="发证日期" width="108" />
|
||||
<el-table-column label="PDF" width="92">
|
||||
<template #default="{ row }">
|
||||
@@ -96,6 +106,7 @@
|
||||
<el-descriptions-item label="项目/课程/阶段">
|
||||
{{ preview.project_code }} {{ preview.course_name || "" }} {{ preview.stage_name || "" }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="课程时间">{{ coursePeriodText(preview) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="发证日期">{{ preview.issue_date }}</el-descriptions-item>
|
||||
<el-descriptions-item label="发证单位">{{ preview.issuer_name }}</el-descriptions-item>
|
||||
<el-descriptions-item label="公开链接">
|
||||
@@ -160,6 +171,8 @@ const form = reactive({
|
||||
project_code: "",
|
||||
course_name: "",
|
||||
stage_name: "",
|
||||
course_start_date: "",
|
||||
course_end_date: "",
|
||||
issue_date: "",
|
||||
issuer_name: "",
|
||||
remark: "",
|
||||
@@ -175,6 +188,12 @@ function pdfStatusText(status: string) {
|
||||
return { generated: "已生成", cleaned: "已清理", not_generated: "未生成" }[status] || status;
|
||||
}
|
||||
|
||||
function coursePeriodText(certificate: Pick<AdminCertificate, "course_start_date" | "course_end_date" | "issue_date">) {
|
||||
const start = certificate.course_start_date || certificate.issue_date;
|
||||
const end = certificate.course_end_date || certificate.issue_date;
|
||||
return `${start} 至 ${end}`;
|
||||
}
|
||||
|
||||
const pregenerationDone = computed(() => ["completed", "completed_with_errors"].includes(pregenerationJob.value?.status || ""));
|
||||
|
||||
async function loadProjects() {
|
||||
@@ -204,8 +223,12 @@ function handleSelectionChange(rows: AdminCertificate[]) {
|
||||
}
|
||||
|
||||
async function createCertificate() {
|
||||
if (!form.learner_id || !form.project_code || !form.issue_date || !form.issuer_name) {
|
||||
ElMessage.warning("请填写学员ID、项目、发证日期和发证单位");
|
||||
if (!form.learner_id || !form.project_code || !form.course_start_date || !form.course_end_date || !form.issue_date || !form.issuer_name) {
|
||||
ElMessage.warning("请填写学员ID、项目、课程起止日期、发证日期和发证单位");
|
||||
return;
|
||||
}
|
||||
if (form.course_end_date < form.course_start_date) {
|
||||
ElMessage.warning("课程结束日期不能早于课程开始日期");
|
||||
return;
|
||||
}
|
||||
await http.post("/admin/certificates", {
|
||||
@@ -213,6 +236,8 @@ async function createCertificate() {
|
||||
project_code: form.project_code,
|
||||
course_name: form.course_name || null,
|
||||
stage_name: form.stage_name || null,
|
||||
course_start_date: form.course_start_date,
|
||||
course_end_date: form.course_end_date,
|
||||
issue_date: form.issue_date,
|
||||
issuer_name: form.issuer_name,
|
||||
remark: form.remark || null,
|
||||
|
||||
@@ -8,6 +8,13 @@
|
||||
<el-button @click="downloadTemplate">下载模板</el-button>
|
||||
</header>
|
||||
|
||||
<el-alert
|
||||
class="import-tip"
|
||||
type="info"
|
||||
:closable="false"
|
||||
title="课程开始日期、课程结束日期和发证日期均为必填项,请统一使用 YYYY-MM-DD 格式,例如 2026-06-01。"
|
||||
/>
|
||||
|
||||
<el-upload
|
||||
class="upload"
|
||||
drag
|
||||
@@ -224,9 +231,14 @@ onUnmounted(stopPregenerationPolling);
|
||||
}
|
||||
|
||||
.upload {
|
||||
margin-top: 16px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.import-tip {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.upload-icon {
|
||||
font-size: 36px;
|
||||
color: #208a87;
|
||||
|
||||
@@ -17,6 +17,10 @@
|
||||
<dt>证书编号</dt>
|
||||
<dd>{{ certificate.certificate_no }}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>课程时间</dt>
|
||||
<dd>{{ coursePeriodText }}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>发证日期</dt>
|
||||
<dd>{{ certificate.issue_date }}</dd>
|
||||
@@ -36,7 +40,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ElLoading, ElMessage } from "element-plus";
|
||||
import { onMounted, ref } from "vue";
|
||||
import { computed, onMounted, ref } from "vue";
|
||||
import { useRoute } from "vue-router";
|
||||
|
||||
import { http, type PublicCertificate } from "../api";
|
||||
@@ -48,6 +52,12 @@ const certificate = ref<PublicCertificate | null>(null);
|
||||
const message = ref("");
|
||||
const messageType = ref<"success" | "warning" | "error">("success");
|
||||
const downloading = ref(false);
|
||||
const coursePeriodText = computed(() => {
|
||||
if (!certificate.value) return "";
|
||||
const start = certificate.value.course_start_date || certificate.value.issue_date;
|
||||
const end = certificate.value.course_end_date || certificate.value.issue_date;
|
||||
return `${start} 至 ${end}`;
|
||||
});
|
||||
|
||||
function statusText(status: string) {
|
||||
return status === "valid" ? "有效" : "已作废";
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
<el-descriptions-item label="证书编号">{{ certificate.certificate_no }}</el-descriptions-item>
|
||||
<el-descriptions-item label="学员姓名">{{ certificate.learner_name }}</el-descriptions-item>
|
||||
<el-descriptions-item label="证书名称">{{ certificate.certificate_name }}</el-descriptions-item>
|
||||
<el-descriptions-item label="课程时间">{{ coursePeriodText }}</el-descriptions-item>
|
||||
<el-descriptions-item label="发证日期">{{ certificate.issue_date }}</el-descriptions-item>
|
||||
<el-descriptions-item label="发证单位">{{ certificate.issuer_name }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</section>
|
||||
@@ -16,7 +18,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref } from "vue";
|
||||
import { computed, onMounted, ref } from "vue";
|
||||
import { useRoute } from "vue-router";
|
||||
|
||||
import { http, type PublicCertificate } from "../api";
|
||||
@@ -26,6 +28,12 @@ const token = String(route.params.token);
|
||||
const certificate = ref<PublicCertificate | null>(null);
|
||||
const message = ref("正在核验证书信息");
|
||||
const messageType = ref<"success" | "warning" | "error" | "info">("info");
|
||||
const coursePeriodText = computed(() => {
|
||||
if (!certificate.value) return "";
|
||||
const start = certificate.value.course_start_date || certificate.value.issue_date;
|
||||
const end = certificate.value.course_end_date || certificate.value.issue_date;
|
||||
return `${start} 至 ${end}`;
|
||||
});
|
||||
|
||||
async function loadCertificate() {
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user