基于Vue3若依开源框架的专业培养方案教学计划表的前端实现
应头条网友要求,把本人最近在开发的专业培养方案教学计划表功能做个demo给大家参考。实现效果如下图所示。
点击“添加课程”按钮,会弹出如下图所示的对话框,进行课程选择,选择一个课程后,课程会添加到计划表中。
用TablesGenerator设计复杂表格模板
在https://www.tablesgenerator.com/html_tables网站中,根据表格需要进行设计,做好单元格合并。
设计过程会自动生成htm和css代码,如下图所示。
将得到的htm和css代码复制到.vue文件
(1)得到的html复制到.vue文件<template></template>中,并且用v-for和v-if进行数据绑定,<template></template>代码如下:
<template> <div> <el-row> <el-divider content-position=”left”> 专业课程(31必修) <el-button type=”primary” size=”small” icon=”plus” @click=”openSelectCourse”>添加课程</el-button> </el-divider> <el-col :span=”22″> <el-form-item label-width=”10px”> <table class=”tg-theory”> <colgroup> <col style=”width: 25px” /> <col style=”width: 25px” /> <col style=”width: 70px” /> <col style=”width: 170px” /> <col style=”width: 42px” /> <col style=”width: 42px” /> <col style=”width: 42px” /> <col style=”width: 42px” /> <col style=”width: 38px” /> <col style=”width: 38px” /> <col style=”width: 38px” /> <col style=”width: 38px” /> <col style=”width: 38px” /> <col style=”width: 38px” /> <col style=”width: 35px” /> <col style=”width: 35px” /> <col style=”width: 25px” /> <col style=”width: 70px” /> <col style=”width: 70px” /> </colgroup> <thead> <tr> <th class=”tg-9wq8″ colspan=”2″ rowspan=”3″> 课程 <br />类别 </th> <th class=”tg-9wq8″ rowspan=”3″>课程代码</th> <th class=”tg-9wq8″ rowspan=”3″>课程名称</th> <th class=”tg-9wq8″ rowspan=”3″>学分</th> <th class=”tg-9wq8″ colspan=”3″>学时数</th> <th class=”tg-9wq8″ colspan=”8″>开课学期及周学时安排</th> <th class=”tg-9wq8″ rowspan=”3″> 考 <br />核 <br />方 <br />式 </th> <th class=”tg-9wq8″ rowspan=”3″> 开课 <br />单位 </th> <th class=”tg-9wq8″ rowspan=”3″>操作</th> </tr> <tr> <th class=”tg-9wq8″ rowspan=”2″>总计</th> <th class=”tg-9wq8″ rowspan=”2″>讲授</th> <th class=”tg-9wq8″ rowspan=”2″> 实践 <br />(验) </th> <th class=”tg-9wq8″ colspan=”2″>第一学年</th> <th class=”tg-9wq8″ colspan=”2″>第二学年</th> <th class=”tg-9wq8″ colspan=”2″>第三学年</th> <th class=”tg-9wq8″ colspan=”2″>第四学年</th> </tr> <tr> <th class=”tg-9wq8″>1</th> <th class=”tg-9wq8″>2</th> <th class=”tg-9wq8″>3</th> <th class=”tg-9wq8″>4</th> <th class=”tg-9wq8″>5</th> <th class=”tg-9wq8″>6</th> <th class=”tg-9wq8″>7</th> <th class=”tg-9wq8″>8</th> </tr> </thead> <tbody> <template v-for=”(item, index) in course31List” :key=”‘course31_’+item.courseId” > <tr v-if=”index == 0″> <td class=”tg-9wq8″ :rowspan=”course31List.length + 1″> 专 <br />业 <br />课 <br />程 </td> <td class=”tg-9wq8″ :rowspan=”course31List.length + 1″> 必 <br />修 </td> <td class=”tg-9wq8″>{{ item.courseCode }}</td> <td class=”tg-9wq8″> <span style=”color:red;”>{{ item.isCore == ‘1’ ? ” : ” }}</span> <span style=”color:blue;font-size: x-large;vertical-align:-9px;line-height:7px;” >{{ item.isZcrh == ‘1’ ? ‘*’ : ” }}</span> {{ item.courseNameZh }} <br /> <span style=”color: gray;”>{{ item.courseNameEn }}</span> </td> <td class=”tg-9wq8″>{{ item.credits }}</td> <td class=”tg-9wq8″>{{ item.classesTotal }}</td> <td class=”tg-9wq8″>{{ item.classesTheory }}</td> <td class=”tg-9wq8″> {{ item.classesPractice }} <br /> {{ item.courseId != 0 ? ‘(‘ : ” }}{{ item.classesExperiment }}{{ item.courseId != 0 ? ‘)’ : ” }} </td> <td class=”tg-9wq8″ >{{ item.doTerm == ‘1’ ? item.classesWeek : ” }}</td> <td class=”tg-9wq8″ >{{ item.doTerm == ‘2’ ? item.classesWeek : ” }}</td> <td class=”tg-9wq8″ >{{ item.doTerm == ‘3’ ? item.classesWeek : ” }}</td> <td class=”tg-9wq8″ >{{ item.doTerm == ‘4’ ? item.classesWeek : ” }}</td> <td class=”tg-9wq8″ >{{ item.doTerm == ‘5’ ? item.classesWeek : ” }}</td> <td class=”tg-9wq8″ >{{ item.doTerm == ‘6’ ? item.classesWeek : ” }}</td> <td class=”tg-9wq8″ >{{ item.doTerm == ‘7’ ? item.classesWeek : ” }}</td> <td class=”tg-9wq8″ >{{ item.doTerm == ‘8’ ? item.classesWeek : ” }}</td> <td class=”tg-9wq8″>{{ item.assessmentType }}</td> <td class=”tg-9wq8″>{{ item.collegeName2 }}</td> <td class=”tg-9wq8″> <el-popconfirm confirm-button-text=”确定” cancel-button-text=”取消” title=”确定要删除该课程吗?” @confirm=”removeCourse(index, ’31’)” v-hasPermi=”[‘teach:plan:edit’]” > <template #reference> <span> <el-tooltip content=”删除” placement=”top”> <el-button v-if=”item.courseId != 0″ icon=”CloseBold” type=”text” /> </el-tooltip> </span> </template> </el-popconfirm> <br /> <el-tooltip content=”设为核心课程 ” placement=”bottom”> <el-button v-if=”item.courseId != 0″ icon=”StarFilled” type=”text” @click=”toggleCoreCourse(index, ’31’)” v-hasPermi=”[‘teach:plan:edit’]” /> </el-tooltip> <el-tooltip content=”设为专创融合课程 *” placement=”bottom”> <el-button v-if=”item.courseId != 0″ icon=”Sunny” type=”text” @click=”toggleZcrhCourse(index, ’31’)” v-hasPermi=”[‘teach:plan:edit’]” /> </el-tooltip> </td> </tr> </template> <template v-for=”(item, index) in course31List” :key=”‘course31_’+item.courseId” > <tr v-if=”index > 0″> <td class=”tg-9wq8″>{{ item.courseCode }}</td> <td class=”tg-9wq8″> <span style=”color:red;”>{{ item.isCore == ‘1’ ? ” : ” }}</span> <span style=”color:blue;font-size: x-large;vertical-align:-9px;line-height:7px;” >{{ item.isZcrh == ‘1’ ? ‘*’ : ” }}</span> {{ item.courseNameZh }} <br /> <span style=”color: gray;”>{{ item.courseNameEn }}</span> </td> <td class=”tg-9wq8″>{{ item.credits }}</td> <td class=”tg-9wq8″>{{ item.classesTotal }}</td> <td class=”tg-9wq8″>{{ item.classesTheory }}</td> <td class=”tg-9wq8″> {{ item.classesPractice }} <br /> {{ ‘(‘ + item.classesExperiment + ‘)’ }} </td> <td class=”tg-9wq8″ >{{ item.doTerm == ‘1’ ? item.classesWeek : ” }}</td> <td class=”tg-9wq8″ >{{ item.doTerm == ‘2’ ? item.classesWeek : ” }}</td> <td class=”tg-9wq8″ >{{ item.doTerm == ‘3’ ? item.classesWeek : ” }}</td> <td class=”tg-9wq8″ >{{ item.doTerm == ‘4’ ? item.classesWeek : ” }}</td> <td class=”tg-9wq8″ >{{ item.doTerm == ‘5’ ? item.classesWeek : ” }}</td> <td class=”tg-9wq8″ >{{ item.doTerm == ‘6’ ? item.classesWeek : ” }}</td> <td class=”tg-9wq8″ >{{ item.doTerm == ‘7’ ? item.classesWeek : ” }}</td> <td class=”tg-9wq8″ >{{ item.doTerm == ‘8’ ? item.classesWeek : ” }}</td> <td class=”tg-9wq8″>{{ item.assessmentType }}</td> <td class=”tg-9wq8″>{{ item.collegeName2 }}</td> <td class=”tg-9wq8″> <el-tooltip content=”上移” placement=”top”> <el-button icon=”ArrowUpBold” type=”text” @click=”upCourse(index, ’31’)” v-hasPermi=”[‘teach:plan:edit’]” /> </el-tooltip> <el-popconfirm confirm-button-text=”确定” cancel-button-text=”取消” title=”确定要删除该课程吗?” @confirm=”removeCourse(index, ’31’)” v-hasPermi=”[‘teach:plan:edit’]” > <template #reference> <span> <el-tooltip content=”删除” placement=”top”> <el-button v-if=”item.courseId != 0″ icon=”CloseBold” type=”text” /> </el-tooltip> </span> </template> </el-popconfirm> <br /> <el-tooltip content=”设为核心课程 ” placement=”bottom”> <el-button icon=”StarFilled” type=”text” @click=”toggleCoreCourse(index, ’31’)” v-hasPermi=”[‘teach:plan:edit’]” /> </el-tooltip> <el-tooltip content=”设为专创融合课程 *” placement=”bottom”> <el-button icon=”Sunny” type=”text” @click=”toggleZcrhCourse(index, ’31’)” v-hasPermi=”[‘teach:plan:edit’]” /> </el-tooltip> </td> </tr> </template> <tr> <td class=”tg-9wq8-2″ colspan=”2″>小计</td> <td class=”tg-9wq8-2″>{{ course31Summary.credits }}</td> <td class=”tg-9wq8-2″>{{ course31Summary.classesTotal }}</td> <td class=”tg-9wq8-2″>{{ course31Summary.classesTheory }}</td> <td class=”tg-9wq8-2″> {{ course31Summary.classesPractice }} <br /> {{ ‘(‘ + course31Summary.classesExperiment + ‘)’ }} </td> <td class=”tg-9wq8-2″>{{ course31Summary.classesWeek1 }}</td> <td class=”tg-9wq8-2″>{{ course31Summary.classesWeek2 }}</td> <td class=”tg-9wq8-2″>{{ course31Summary.classesWeek3 }}</td> <td class=”tg-9wq8-2″>{{ course31Summary.classesWeek4 }}</td> <td class=”tg-9wq8-2″>{{ course31Summary.classesWeek5 }}</td> <td class=”tg-9wq8-2″>{{ course31Summary.classesWeek6 }}</td> <td class=”tg-9wq8-2″>{{ course31Summary.classesWeek7 }}</td> <td class=”tg-9wq8-2″>{{ course31Summary.classesWeek8 }}</td> <td class=”tg-9wq8-2″></td> <td class=”tg-9wq8-2″></td> <td class=”tg-9wq8-2″></td> </tr> </tbody> </table> </el-form-item> </el-col> </el-row> <!– 子组件:选择课程对话框 –> <select-course ref=”selectCourseRef” dlgTitle=”选择课程-专业课程(必修)” courseType=”31″ @ok=”handleSelectCourse” /> </div></template>(2)得到的css复制到.vue文件<style></style>中,<style></style>代码如下:
<style>.tg-theory { border-collapse: collapse; border-spacing: 0;}.tg-theory td { border-color: black; border-style: solid; border-width: 1px; font-family: Arial, sans-serif; font-size: 14px; overflow: hidden; padding: 0px 5px; word-break: normal;}.tg-theory th { height: 20px; line-height: 20px; border-color: black; border-style: solid; border-width: 1px; font-family: Arial, sans-serif; font-size: 14px; font-weight: normal; overflow: hidden; padding: 0px 5px; word-break: normal;}.tg-theory .tg-9wq8 { border-color: inherit; text-align: center; vertical-align: middle; line-height: 16px; font-size: 12px; padding: 2px;}.tg-theory .tg-9wq8-2 { border-color: inherit; text-align: center; vertical-align: middle; line-height: 16px; font-size: 12px; padding: 8px 2px;}.tg-theory .el-button { padding: 0px 5px; height: 20px; margin-left: 5px;}tbody tr:hover { background-color: #f2f6fc;}</style>
在.vue文件中编写js代码
(1)引入一些组件和api,定义一些响应式数据。
//是用setup语法糖<script setup name=”PlanEdit”>import { ElMessage } from ‘element-plus’; //导入消息提示组件import { getCourse } from ‘@/api/teach/course’; //导入课程apiimport { ref } from ‘vue’;import selectCourse from ‘./selectCourse.vue’; //导入自定义的选择课程组件/** 空课程对象 */const course31Null = ref({ courseId: 0, //课程id courseCode: ”, //课程代码 courseNameZh: ”, //课程名称(中文) courseNameEn: ”, //课程名称(英文) credits: ”, //学分 classesTotal: ”, //总课时 classesTheory: ”, //讲授课时 classesExperiment: ”, //实验课时 classesPractice: ”, //实践课时 isZcrh: ‘0’, //是否专创融合课程 isCore: ‘0’, //是否核心课程});/** 课程对象列表 */const course31List = ref([ { courseId: 0, courseCode: ”, courseNameZh: ”, courseNameEn: ”, credits: ”, classesTotal: ”, classesTheory: ”, classesExperiment: ”, classesPractice: ”, isZcrh: ‘0’, isCore: ‘0’, }]); /** 课程小计对象 */const course31Summary = ref({ credits: 0, classesTotal: 0, classesTheory: 0, classesExperiment: 0, classesPractice: 0, classesWeek1: 0, //第1学期周学时 classesWeek2: 0, //第2学期周学时 classesWeek3: 0, //第3学期周学时 classesWeek4: 0, //第4学期周学时 classesWeek5: 0, //第5学期周学时 classesWeek6: 0, //第6学期周学时 classesWeek7: 0, //第7学期周学时 classesWeek8: 0, //第8学期周学时});(2)选择课程对话框返回的处理函数handleSelectCourse,用子组件返回的课程id到后端查询课程详情,然后push到course31List列表中:
functiоn handleSelectCourse(courseId) { //子组件返回的 courseId,调用getCourse api到后端查询课程详情 getCourse(courseId).then(res => { if (course31List.value[0].courseId == 0) { course31List.value.shift(); // 如果是第一门课程,则删除空课程 } // 判断课程是否已存在 if (course31List.value.some(item => { if (item.courseCode == res.data.courseCode) { return true; } else { return false; } })) { ElMessage.error(‘该课程已添加,不能重复添加!’) } else { course31List.value.push(res.data); xiaoJi(’31’); //添加新课程后,进行小计 } });}(3)上移操作的处理函数upCourse:
functiоn upCourse(index, courseType) { switch (courseType) { case ’31’: course31List.value[index] = course31List.value.splice(index – 1, 1, course31List.value[index])[0]; break; }}(4)删除操作的处理函数removeCourse:
functiоn removeCourse(index, courseType) { console.log(‘index:’ + index + ‘,courseType:’ + courseType); switch (courseType) { case ’31’: course31List.value.splice(index, 1); xiaoJi(’31’); if (course31List.value.length == 0) { course31List.value.push(course31Null.value); } break; }}(5)设置为核心课程的处理函数toggleCoreCourse:
functiоn toggleCoreCourse(index, courseType) { switch (courseType) { case ’31’: course31List.value[index].isCore = course31List.value[index].isCore == ‘1’ ? ‘0’ : ‘1’; break; }}(6)设置为专创融合课程的处理函数toggleZcrhCourse:
functiоn toggleZcrhCourse(index, courseType) { switch (courseType) { case ’31’: course31List.value[index].isZcrh = course31List.value[index].isZcrh == ‘1’ ? ‘0’ : ‘1’; break; }}(7)小计的处理函数xiaoJi:
/**小计 */functiоn xiaoJi(courseType) { let summary = { credits: 0, classesTotal: 0, classesTheory: 0, classesExperiment: 0, classesPractice: 0, classesWeek1: 0, classesWeek2: 0, classesWeek3: 0, classesWeek4: 0, classesWeek5: 0, classesWeek6: 0, classesWeek7: 0, classesWeek8: 0, } switch (courseType) { case ’31’: course31List.value.forEach((item, index) => { summary.credits += parseFloat(item.credits); summary.classesTotal += parseFloat(item.classesTotal); summary.classesTheory += parseFloat(item.classesTheory); summary.classesExperiment += parseFloat(item.classesExperiment); summary.classesPractice += parseFloat(item.classesPractice); switch (item.doTerm) { case ‘1’: summary.classesWeek1 += parseFloat(item.classesWeek); break; case ‘2’: summary.classesWeek2 += parseFloat(item.classesWeek); break; case ‘3’: summary.classesWeek3 += parseFloat(item.classesWeek); break; case ‘4’: summary.classesWeek4 += parseFloat(item.classesWeek); break; case ‘5’: summary.classesWeek5 += parseFloat(item.classesWeek); break; case ‘6’: summary.classesWeek6 += parseFloat(item.classesWeek); break; case ‘7’: summary.classesWeek7 += parseFloat(item.classesWeek); break; case ‘8’: summary.classesWeek8 += parseFloat(item.classesWeek); break; } }); course31Summary.value = summary; break; }}</script>
子组件SelectCourse代码
其功能是弹开一个课程选择对话框。
<template> <!– 选择课程 –> <el-dialog :title=”props.dlgTitle” v-model=”visible” width=”800px” top=”5vh” APPend-to-body> <el-form :model=”queryParams” ref=”queryRef” :inline=”true”> <el-form-item label=”课程代码” prop=”courseCode”> <el-input v-model=”queryParams.courseCode” placeholder=”请输入课程代码” clearable @keyup.enter=”handleQuery” /> </el-form-item> <el-form-item label=”课程名称” prop=”courseNameZh”> <el-input v-model=”queryParams.courseNameZh” placeholder=”请输入课程名称” clearable @keyup.enter=”handleQuery” /> </el-form-item> <el-form-item> <el-button type=”primary” icon=”Search” @click=”handleQuery”>搜索</el-button> <el-button icon=”Refresh” @click=”resetQuery”>重置</el-button> </el-form-item> </el-form> <el-row> <el-table class=”single_select” @row-click=”clickRow” ref=”refTable” :data=”courseList” @selection-change=”handleSelectionChange” height=”260px” > <el-table-column type=”selection” width=”40″></el-table-column> <el-table-column label=”课程代码” prop=”courseCode” width=”90″ /> <el-table-column label=”课程名称” prop=”courseNameZh” :show-overflow-tooltip=”true” /> <el-table-column label=”学分” prop=”credits” width=”50″ /> <el-table-column label=”学时” prop=”classesTotal” width=”50″ /> <el-table-column label=”课程类别” align=”center” prop=”courseType”> <template #default=”scope”> <dict-tag :options=”sys_course_type” :value=”scope.row.courseType” /> </template> </el-table-column> <el-table-column label=”创建时间” align=”center” prop=”createTime” width=”160″> <template #default=”scope”> <span>{{ parseTime(scope.row.createTime) }}</span> </template> </el-table-column> </el-table> <pagination v-show=”total > 0″ :total=”total” v-model:page=”queryParams.pageNum” v-model:limit=”queryParams.pageSize” @pagination=”getList” /> </el-row> <template #footer> <div class=”dialog-footer”> <el-button type=”primary” @click=”handleSelectCourse”>确 定</el-button> <el-button @click=”visible = false”>取 消</el-button> </div> </template> </el-dialog></template><script setup name=”SelectCourse”>import { listCourse } from “@/api/teach/course”;import { ref } from “vue”;const props = defineProps({ dlgTitle: { type: String }, courseType: { type: [String] }});const { proxy } = getCurrentInstance();const { sys_course_type } = proxy.useDict(“sys_course_type”); //使用字典:课程类别const courseList = ref([]);const visible = ref(false);const total = ref(0);const courseId = ref(0);const queryParams = reactive({ pageNum: 1, pageSize: 10, courseType: props.courseType, courseCode: undefined, courseNameZh: undefined});// 显示弹框functiоn show() { queryParams.courseType = props.courseType; getList(); visible.value = true;}/**选择行 */functiоn clickRow(row) { proxy.$refs[“refTable”].toggleRowSelection(row);}// 多选框选中数据functiоn handleSelectionChange(selection) { if (selection.length > 1) { let del_row = selection.shift() proxy.$refs[“refTable”].toggleRowSelection(del_row, false) // 用于多选表格,切换某一行的选中状态,如果使用了第二个参数,则是设置这一行选中与否(selected 为 true 则选中) } else { courseId.value = selection.map(item => item.courseId)[0]; }}// 查询表数据functiоn getList() { listCourse(queryParams).then(res => { courseList.value = res.rows; total.value = res.total; });}/** 搜索按钮操作 */functiоn handleQuery() { queryParams.pageNum = 1; getList();}/** 重置按钮操作 */functiоn resetQuery() { proxy.resetForm(“queryRef”); handleQuery();}const emit = defineEmits([“ok”]); //定义一个向父组件发射的函数,命名为ok,返回父组件时会执行父组件中@ok绑定的方法。/** 选择课程操作 */functiоn handleSelectCourse() { if (courseId.value == “”) { proxy.$modal.msgError(“请选择一门课程”); return; } visible.value = false; emit(“ok”, courseId.value); //向父组件返回课程id}/** 向父组件暴露show方法,父组件可调用子组件的此方法 */defineExpose({ show,});</script><style>.single_select .el-table__header-wrapper .el-checkbox { display: none;}</style>
课程api文件(@/api/teach/course)代码
import request from ‘@/utils/request’// 查询课程列表export functiоn listCourse(query) { return request({ url: ‘/teach/course/list’, method: ‘get’, params: query })}// 查询课程详细export functiоn getCourse(courseId) { return request({ url: ‘/teach/course/’ + courseId, method: ‘get’ })}// 新增课程export functiоn addCourse(data) { return request({ url: ‘/teach/course’, method: ‘post’, data: data })}// 修改课程export functiоn updateCourse(data) { return request({ url: ‘/teach/course’, method: ‘put’, data: data })}// 删除课程export functiоn delCourse(courseId) { return request({ url: ‘/teach/course/’ + courseId, method: ‘delete’ })}
服务端代码
按照若依系统的开发模式开发课程模块和培养方案模块(略)
原创文章,作者:starterknow,如若转载,请注明出处:https://www.starterknow.com/126533.html