222 lines
6.8 KiB
Vue
222 lines
6.8 KiB
Vue
<template>
|
|
<section>
|
|
<header class="page-head">
|
|
<div>
|
|
<h2>项目管理</h2>
|
|
<p>维护项目代码和导入时使用的证书默认信息。</p>
|
|
</div>
|
|
<el-button type="primary" @click="saveProject">{{ editingId ? "保存修改" : "新增项目" }}</el-button>
|
|
</header>
|
|
|
|
<el-card class="panel" shadow="never">
|
|
<el-form class="form-grid" label-position="top">
|
|
<el-form-item label="项目代码">
|
|
<el-input v-model.trim="form.code" :disabled="!!editingId" maxlength="16" />
|
|
</el-form-item>
|
|
<el-form-item label="项目名称">
|
|
<el-input v-model.trim="form.name" maxlength="128" />
|
|
</el-form-item>
|
|
<el-form-item label="默认发证单位">
|
|
<el-input v-model.trim="form.default_issuer_name" maxlength="128" />
|
|
</el-form-item>
|
|
<el-form-item label="默认证书模板">
|
|
<el-select v-model="form.default_template_code">
|
|
<el-option v-for="item in templates" :key="item.code" :label="item.name" :value="item.code" />
|
|
</el-select>
|
|
</el-form-item>
|
|
<el-form-item label="默认课程名称">
|
|
<el-input v-model.trim="form.default_course_name" maxlength="128" />
|
|
</el-form-item>
|
|
<el-form-item label="默认阶段名称">
|
|
<el-input v-model.trim="form.default_stage_name" maxlength="128" />
|
|
</el-form-item>
|
|
<el-form-item label="状态">
|
|
<el-select v-model="form.status">
|
|
<el-option label="启用" value="active" />
|
|
<el-option label="停用" value="disabled" />
|
|
</el-select>
|
|
</el-form-item>
|
|
<el-form-item label=" ">
|
|
<el-button @click="resetForm">清空</el-button>
|
|
</el-form-item>
|
|
</el-form>
|
|
</el-card>
|
|
|
|
<el-card class="panel" shadow="never">
|
|
<div class="toolbar">
|
|
<el-input v-model.trim="keyword" placeholder="搜索项目代码、名称、课程、阶段或状态" clearable />
|
|
</div>
|
|
<el-table :data="projects" border>
|
|
<el-table-column prop="code" label="项目代码" width="120" />
|
|
<el-table-column prop="name" label="项目名称" width="150" />
|
|
<el-table-column prop="default_course_name" label="课程名称" min-width="140" />
|
|
<el-table-column prop="default_stage_name" label="阶段名称" min-width="120" />
|
|
<el-table-column prop="default_issuer_name" label="发证单位" min-width="140" />
|
|
<el-table-column label="默认模板" min-width="150">
|
|
<template #default="{ row }">{{ templateName(row.default_template_code) }}</template>
|
|
</el-table-column>
|
|
<el-table-column label="状态" width="100">
|
|
<template #default="{ row }">
|
|
<el-tag :type="row.status === 'active' ? 'success' : 'info'">
|
|
{{ statusText(row.status) }}
|
|
</el-tag>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column label="操作" width="180" fixed="right">
|
|
<template #default="{ row }">
|
|
<el-button size="small" @click="editProject(row)">修改</el-button>
|
|
<el-button size="small" @click="toggleStatus(row)">
|
|
{{ row.status === "active" ? "停用" : "启用" }}
|
|
</el-button>
|
|
</template>
|
|
</el-table-column>
|
|
</el-table>
|
|
</el-card>
|
|
</section>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { ElMessage } from "element-plus";
|
|
import { computed, onMounted, reactive, ref } from "vue";
|
|
|
|
import { http, type CertificateTemplate, type ProjectCourse } from "../api";
|
|
|
|
const allProjects = ref<ProjectCourse[]>([]);
|
|
const keyword = ref("");
|
|
const editingId = ref<number | null>(null);
|
|
const templates = ref<CertificateTemplate[]>([]);
|
|
const form = reactive({
|
|
code: "",
|
|
name: "",
|
|
default_course_name: "",
|
|
default_stage_name: "",
|
|
default_issuer_name: "本公司",
|
|
default_template_code: "classic",
|
|
status: "active",
|
|
});
|
|
|
|
const projects = computed(() => {
|
|
const value = keyword.value.toLowerCase();
|
|
if (!value) return allProjects.value;
|
|
return allProjects.value.filter((item) => {
|
|
const text = [
|
|
item.code,
|
|
item.name,
|
|
item.default_course_name || "",
|
|
item.default_stage_name || "",
|
|
item.default_issuer_name,
|
|
statusText(item.status),
|
|
].join(" ").toLowerCase();
|
|
return text.includes(value);
|
|
});
|
|
});
|
|
|
|
function statusText(status: string) {
|
|
return status === "active" ? "启用" : "停用";
|
|
}
|
|
|
|
async function loadProjects() {
|
|
const { data } = await http.get<ProjectCourse[]>("/admin/projects");
|
|
allProjects.value = data;
|
|
}
|
|
|
|
async function loadTemplates() {
|
|
const { data } = await http.get<CertificateTemplate[]>("/admin/certificate-templates");
|
|
templates.value = data;
|
|
}
|
|
|
|
function templateName(code: string) {
|
|
return templates.value.find((item) => item.code === code)?.name || code;
|
|
}
|
|
|
|
function editProject(row: ProjectCourse) {
|
|
editingId.value = row.id;
|
|
Object.assign(form, {
|
|
code: row.code,
|
|
name: row.name,
|
|
default_course_name: row.default_course_name || "",
|
|
default_stage_name: row.default_stage_name || "",
|
|
default_issuer_name: row.default_issuer_name,
|
|
default_template_code: row.default_template_code,
|
|
status: row.status,
|
|
});
|
|
}
|
|
|
|
function resetForm() {
|
|
editingId.value = null;
|
|
Object.assign(form, {
|
|
code: "",
|
|
name: "",
|
|
default_course_name: "",
|
|
default_stage_name: "",
|
|
default_issuer_name: "本公司",
|
|
default_template_code: "classic",
|
|
status: "active",
|
|
});
|
|
}
|
|
|
|
function projectPayload() {
|
|
return {
|
|
code: form.code,
|
|
name: form.name,
|
|
default_course_name: form.default_course_name || null,
|
|
default_stage_name: form.default_stage_name || null,
|
|
default_issuer_name: form.default_issuer_name,
|
|
default_template_code: form.default_template_code,
|
|
status: form.status,
|
|
};
|
|
}
|
|
|
|
async function saveProject() {
|
|
if (!form.code || !form.name || !form.default_issuer_name) {
|
|
ElMessage.warning("项目代码、项目名称和发证单位必填");
|
|
return;
|
|
}
|
|
if (editingId.value) {
|
|
await http.put(`/admin/projects/${editingId.value}`, projectPayload());
|
|
} else {
|
|
await http.post("/admin/projects", projectPayload());
|
|
}
|
|
ElMessage.success("项目已保存");
|
|
resetForm();
|
|
await loadProjects();
|
|
}
|
|
|
|
async function toggleStatus(row: ProjectCourse) {
|
|
await http.put(`/admin/projects/${row.id}`, { status: row.status === "active" ? "disabled" : "active" });
|
|
await loadProjects();
|
|
}
|
|
|
|
onMounted(() => Promise.all([loadProjects(), loadTemplates()]));
|
|
</script>
|
|
|
|
<style scoped>
|
|
.page-head,
|
|
.toolbar {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
margin-bottom: 16px;
|
|
}
|
|
|
|
.page-head p {
|
|
margin: 6px 0 0;
|
|
color: #64748b;
|
|
}
|
|
|
|
.panel {
|
|
margin-bottom: 16px;
|
|
border-radius: 8px;
|
|
}
|
|
|
|
.form-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(4, 1fr);
|
|
gap: 12px;
|
|
}
|
|
|
|
.toolbar .el-input {
|
|
max-width: 420px;
|
|
}
|
|
</style>
|