Initial certificate system
This commit is contained in:
199
frontend/src/views/AdminProjects.vue
Normal file
199
frontend/src/views/AdminProjects.vue
Normal file
@@ -0,0 +1,199 @@
|
||||
<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-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="状态" 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 ProjectCourse } from "../api";
|
||||
|
||||
const allProjects = ref<ProjectCourse[]>([]);
|
||||
const keyword = ref("");
|
||||
const editingId = ref<number | null>(null);
|
||||
const form = reactive({
|
||||
code: "",
|
||||
name: "",
|
||||
default_course_name: "",
|
||||
default_stage_name: "",
|
||||
default_issuer_name: "本公司",
|
||||
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;
|
||||
}
|
||||
|
||||
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,
|
||||
status: row.status,
|
||||
});
|
||||
}
|
||||
|
||||
function resetForm() {
|
||||
editingId.value = null;
|
||||
Object.assign(form, {
|
||||
code: "",
|
||||
name: "",
|
||||
default_course_name: "",
|
||||
default_stage_name: "",
|
||||
default_issuer_name: "本公司",
|
||||
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,
|
||||
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(loadProjects);
|
||||
</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>
|
||||
Reference in New Issue
Block a user