You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

349 lines
11 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<template>
<div>
<xy-dialog
ref="dialog"
:width="80"
:is-show.sync="isShow"
:type="'form'"
:title="type === 'add' ? '新增计划' : '编辑计划'"
:form="form"
:rules="rules"
@submit="submit"
>
<template v-slot:year>
<div class="xy-table-item">
<div class="xy-table-item-label" style="font-weight: bold">
<span style="color: red; font-weight: bold; padding-right: 4px;">*</span>计划年份
</div>
<div class="xy-table-item-content">
<el-date-picker
v-model="form.year"
type="year"
value-format="yyyy"
format="yyyy"
placeholder="请选择计划年份"
style="width: 100%;"
/>
</div>
</div>
</template>
<template v-slot:plan_system_id>
<div class="xy-table-item">
<div class="xy-table-item-label" style="font-weight: bold">
<span style="color: red; font-weight: bold; padding-right: 4px;">*</span>计划体系:
</div>
<div class="xy-table-item-content">
<el-select v-model="form.plan_system_id" filterable clearable placeholder="请选择计划体系" style="width: 100%;">
<el-option
v-for="item in planSystemList"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</el-select>
</div>
</div>
</template>
<template v-slot:course_type_id>
<div class="xy-table-item">
<div class="xy-table-item-label" style="font-weight: bold">
<span style="color: red; font-weight: bold; padding-right: 4px;">*</span>课程体系:
</div>
<div class="xy-table-item-content">
<el-select v-model="form.course_type_id" filterable clearable placeholder="请选择课程体系" style="width: 100%;">
<el-option
v-for="item in courseTypesList"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</el-select>
</div>
</div>
</template>
<template v-slot:course_name>
<div class="xy-table-item">
<div class="xy-table-item-label" style="font-weight: bold">
<span style="color: red; font-weight: bold; padding-right: 4px;">*</span>课程名称:
</div>
<div class="xy-table-item-content">
<el-input v-model="form.course_name" placeholder="请输入课程名称" clearable style="width: 100%;" />
</div>
</div>
</template>
<template v-slot:details>
<div class="xy-table-item">
<div class="xy-table-item-label detail-label" style="font-weight: bold">
<span style="color: red; font-weight: bold; padding-right: 4px;">*</span>模块/期数:
</div>
<div class="xy-table-item-content">
<div class="detail-wrap">
<div class="detail-toolbar">
<el-button type="primary" size="small" @click="addDetail">新增一条</el-button>
</div>
<el-table :data="form.details" border size="small" style="width: 100%;">
<el-table-column label="名称" min-width="180">
<template slot-scope="scope">
<el-input v-model="scope.row.name" placeholder="请输入模块/期数名称,非必填" size="small" />
</template>
</el-table-column>
<el-table-column label="月份" width="120">
<template slot-scope="scope">
<el-select v-model="scope.row.month" placeholder="月份" size="small" style="width: 100%;">
<el-option
v-for="item in monthOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</template>
</el-table-column>
<el-table-column label="地点" min-width="180">
<template slot-scope="scope">
<el-select v-model="scope.row.location_id" filterable placeholder="请选择地点" size="small" style="width: 100%;">
<el-option
v-for="item in locationList"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</el-select>
</template>
</el-table-column>
<el-table-column label="负责人" min-width="160">
<template slot-scope="scope">
<el-input v-model="scope.row.owner_name" placeholder="请输入负责人" size="small" />
</template>
</el-table-column>
<el-table-column label="操作" width="100" align="center">
<template slot-scope="scope">
<el-button type="danger" size="mini" @click="removeDetail(scope.$index)">删除</el-button>
</template>
</el-table-column>
</el-table>
<div class="detail-tip">至少保留一条模块/期数;名称可不填,月份、地点、负责人必填。</div>
<div v-if="type === 'editor'" class="danger-actions">
<el-button type="danger" size="small" @click="handleDeletePlan">删除计划</el-button>
</div>
</div>
</div>
</div>
</template>
</xy-dialog>
</div>
</template>
<script>
import { showMockPlan as show, saveMockPlan as save, destroyMockPlan as destroy } from "../mockService.js";
const getCurrentYear = () => String(new Date().getFullYear());
const createDetail = () => ({
id: "",
name: "",
month: "",
location_id: "",
owner_name: "",
});
const getDefaultForm = () => ({
year: getCurrentYear(),
plan_system_id: "",
course_type_id: "",
course_name: "",
details: [createDetail()],
});
export default {
data() {
return {
isShow: false,
type: "add",
id: "",
courseTypesList: [],
planSystemList: [],
locationList: [],
form: getDefaultForm(),
rules: {
year: [{
required: true,
message: "请选择计划年份",
}],
plan_system_id: [{
required: true,
message: "请选择计划体系",
}],
course_type_id: [{
required: true,
message: "请选择课程体系",
}],
course_name: [{
required: true,
message: "请输入课程名称",
}],
},
monthOptions: Array.from({ length: 12 }, (_, index) => ({
value: index + 1,
label: `${index + 1}月`,
})),
};
},
methods: {
addDetail() {
this.form.details.push(createDetail());
},
removeDetail(index) {
if (this.form.details.length === 1) {
this.$message.warning("模块/期数至少保留一条");
return;
}
this.form.details.splice(index, 1);
},
normalizeDetail(item) {
return {
id: item.id || "",
name: item.name || item.module_name || "",
month: item.month === 0 ? 0 : Number(item.month || ""),
location_id: item.location_id || item.location?.id || "",
owner_name: item.owner_name || item.owner || item.principal || "",
};
},
getDetailListFromResponse(res) {
const list = res.details || res.detail_list || res.modules || res.items || [];
if (!Array.isArray(list) || !list.length) {
return [createDetail()];
}
return list.map((item) => this.normalizeDetail(item));
},
validateDetails() {
if (!Array.isArray(this.form.details) || this.form.details.length === 0) {
this.$message.warning("请至少新增一条模块/期数");
return false;
}
for (let index = 0; index < this.form.details.length; index += 1) {
const item = this.form.details[index];
if (!item.month) {
this.$message.warning(`第 ${index + 1} 条模块/期数未选择月份`);
return false;
}
if (!item.location_id) {
this.$message.warning(`第 ${index + 1} 条模块/期数未选择地点`);
return false;
}
if (!item.owner_name) {
this.$message.warning(`第 ${index + 1} 条模块/期数未填写负责人`);
return false;
}
}
return true;
},
submit() {
if (!this.validateDetails()) {
return;
}
const payload = {
id: this.type === "editor" ? this.id : "",
year: this.form.year,
plan_system_id: this.form.plan_system_id,
course_type_id: this.form.course_type_id,
course_name: this.form.course_name,
details: this.form.details.map((item) => ({
id: item.id || "",
name: item.name || "",
month: Number(item.month),
location_id: item.location_id,
owner_name: item.owner_name,
})),
};
save(payload).then(() => {
this.$message({
type: "success",
message: this.type === "add" ? "新增成功" : "编辑成功",
});
this.isShow = false;
this.$emit("refresh");
});
},
getDetail() {
show({ id: this.id }).then((res) => {
this.form = {
year: String(res.year || getCurrentYear()),
plan_system_id: res.plan_system_id || res.plan_system?.id || res.plan_system_detail?.id || "",
course_type_id: res.course_type_id || res.course_type?.id || res.type_detail?.id || "",
course_name: res.course_name || res.name || "",
details: this.getDetailListFromResponse(res),
};
});
},
handleDeletePlan() {
this.$confirm(
"删除该计划将删除所有的模块/期数,是否确认删除?",
"提示",
{
type: "warning",
confirmButtonText: "确定",
cancelButtonText: "取消",
}
).then(() => {
destroy({ id: this.id }).then(() => {
this.$message.success("删除成功");
this.isShow = false;
this.$emit("refresh");
});
}).catch(() => {});
},
resetForm() {
this.id = "";
this.form = getDefaultForm();
this.$refs.dialog.reset();
},
},
watch: {
isShow(newVal) {
if (newVal) {
if (this.type === "editor" && this.id) {
this.getDetail();
}
} else if (this.$refs.dialog) {
this.resetForm();
}
},
},
};
</script>
<style scoped lang="scss">
::v-deep .year,
::v-deep .plan_system_id,
::v-deep .course_type_id,
::v-deep .course_name,
::v-deep .details {
flex-basis: 100%;
}
.detail-label {
align-self: flex-start;
padding-top: 8px;
}
.detail-wrap {
width: 100%;
}
.detail-toolbar {
margin-bottom: 10px;
}
.detail-tip {
margin-top: 8px;
font-size: 12px;
color: #909399;
}
.danger-actions {
margin-top: 16px;
text-align: right;
}
</style>