基于Springboot+vue+elementUI+MySQL的学生信息管理系统(一)前端部分

news2024/11/23 7:21:21

源码在本人博客资源当中,本文为项目代码的详细介绍解释,供于大家学习使用

Vue项目的入口文件:mian.js

//vue项目入口文件
//导入vue
import Vue from 'vue'
//导入根组件app
import App from './App'
//导入路由文件
import router from './router'
//导入ElementUI
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
//导入axios
import axios from "axios";
import VueAxios from "vue-axios";
import mavonEditor from 'mavon-editor'
import 'mavon-editor/dist/css/index.css'
// use
Vue.use(mavonEditor)
//在vue当中挂载elementUI和axios
Vue.use(ElementUI)
Vue.use(VueAxios, axios)
//productionTip设置为 false ,可以阻止 vue 在启动时生成生产提示
Vue.config.productionTip = false

// axios请求拦截器
axios.interceptors.request.use(function (config) {
  // 在发送请求之前做些什么,例如加入token
  if (sessionStorage.getItem("auth") !== null) {
    config.headers.auth = sessionStorage.getItem("auth");
  }
  return config;
}, function (error) {
  // 对请求错误做些什么
  return Promise.reject(error);
});

// axios响应拦截器
axios.interceptors.response.use(function (response) {
  // 在接收响应做些什么,例如跳转到登录页
  if (response.data.data === "没有登录") {
    response.data.success = true;
    router.push("/login");
  }
  return response;
}, function (error) {
  // 对响应错误做点什么
  return Promise.reject(error);
});

//创建vue对象
/* eslint-disable no-new */
new Vue({
  el: '#app',
  router,
  components: { App },
  template: '<App/>'
})

这里唯一的难点就是axios请求拦截器和响应拦截器,就是判断用户有没有登录,没有登录就让他去登录

路由文件index.js

//vue项目的路由文件
//导入vue
import Vue from 'vue'
//导入路由
import Router from 'vue-router'
//导入路由对应的组件
import Home from '@/pages/Home'
import Student from "../pages/Student";
import Teacher from "../pages/Teacher";
import Team from "../pages/Team";
import Course from "../pages/Course"
import Login from "../pages/Login";
import Test from "../pages/Test";
import Chengji from "../pages/Chengji.vue"

//在vue中挂载路由
Vue.use(Router)

export default new Router({
  routes: [
    {
      path: '/',
      name: 'Home',
      component: Home,
      redirect: '/student',
      children: [
        {
          path:'/student',
          name: 'Student',
          component: Student
        },
        {
          path: '/teacher',
          name: 'Teacher',
          component: Teacher
        },
        {
          path: '/team',
          name: 'Team',
          component: Team
        },
        {
          path: '/course',
          name: 'Course',
          component: Course
        },
        {
					path: '/test',
					name: 'Test',
					component: Test
				}
      ]
    }, {
      path: '/login',
      name: 'Login',
      component: Login,
    }
  ]
})

这里有个子路由的渲染

登录页面:
在这里插入图片描述
Login.vue:

<template>
  <div id="login">
    <div class="backgroundImg"></div>
    <div class="container">
      <div class="body">
        <div class="title">
          学生信息管理系统
        </div>
        <el-form :model="formData" status-icon :rules="rules" ref="ruleForm" label-width="50px" class="form">
       /* 这段代码使用了 Element UI 框架的 <el-form> 组件,用于生成表单。其中,:model="formData" 表示组件中的数据与 formData 变量进行了双向绑定, :rules="rules" 表示对表单进行验证的规则,这可以保证表单填写的合法性。status-icon 属性表示在输入框内增加状态图标,:label-width="50px" 表示表单项左侧的标题部分宽度为 50 像素,class="form" 表示使用指定的样式类名来设置表单的样式。

ref="ruleForm" 配合 this.$refs.ruleForm.validate() 方法可以触发表单验证。当需要在组件内使用整个表单数据时,我们可以通过 this.formData 进行访问。总之,通过使用 <el-form> ,我们可以方便地生成表单,并进行数据绑定、验证等操作。*/
		  <el-form-item label="账号" prop="username" class="form-item">
            <el-input v-model="formData.username" placeholder="输入账号"></el-input>
          </el-form-item>
          /*这段代码是使用 Element UI 框架的 <el-form-item> 组件来生成一个表单项。其中,label="账号" 表示表单项左侧的标题为“账号”。 prop="username" 表示该表单项的唯一标识为 username,在表单验证时可以使用该标识进行验证。 class="form-item" 表示使用指定的样式类名来设置该表单项的样式。

这个表单项的主要内容位于 <el-input> 中,使用了 v-model="formData.username" 将用户输入的值与 formData 对应的 username 属性进行双向绑定。 placeholder="输入账号" 表示在该输入框未填写前,会显示提示信息“输入账号”。

总之,通过使用 <el-form-item> ,我们可以方便地生成表单项,并设置其样式和验证规则;通过使用 <el-input> ,我们可以生成文本输入框,并将其与组件中的数据进行双向绑定。*/
					
          <el-form-item label="密码" prop="password" class="form-item">
            <el-input type="password" placeholder="输入密码" v-model="formData.password" autocomplete="off"></el-input>
          </el-form-item>
					
          <el-form-item class="login-bar">
            <el-button type="success" @click="submitForm('ruleForm')">登录</el-button>
            <el-button type="danger" @click="resetForm('ruleForm')">重置</el-button>
          </el-form-item>
					
        </el-form>
      </div>
    </div>
  </div>
</template>

<script>
  export default {
    name: "Login",
    data() {
      var validateUsername = (rule, value, callback) => {
        if (value === '') {
          callback(new Error('请输入账号'));
        } else {
          callback();
        }
      }
      /*这段代码定义了一个验证函数 validateUsername ,用于对表单项的“账号”进行验证。该函数接受三个参数: rule 表示验证规则对象,value 表示当前表单项输入的值,callback 表示回调函数。

在 validateUsername 函数中,首先判断用户输入的值是否为空。如果为空,则通过调用 callback(new Error('请输入账号')) 抛出一个错误信息。如果不为空,则通过调用 callback() 这个方法通知表单验证通过。

这个函数体现了 Element UI 中表单验证的基本思想:将验证规则和验证逻辑分开,并且将验证结果通过回调函数返回给组件。由于这个函数是针对“账号”表单项的验证,所以我们可以在 <el-form-item> 中的 rules 属性中指定该验证函数,从而对该表单项进行验证。

总之,通过将表单项的验证逻辑封装为一个函数,我们可以方便地在 Element UI 中对表单进行自定义的验证规则。*/
      var validatePassword = (rule, value, callback) => {
        if (value === '') {
          callback(new Error('请输入密码'));
        } else {
          callback();
        }
      };
      return {
        formData: {
          username: '',
          password: '',
        },
        rules: {
          username: [
            {validator: validateUsername, trigger: 'blur'}
          ],
          password: [
            {validator: validatePassword, trigger: 'blur'}
          ],
        }
        /*这段代码是用来设置 <el-form> 组件中表单项的验证规则。在这里, rules 属性值是一个对象,其中每个属性名(如 username 和 password)对应着一个表单项的 prop 值,也就是该表单项的唯一标识符。而其属性值则是一个数组,表示一个数组中可以包含多个验证规则,每个规则都是一个对象。

在这个对象中, validator 表示要使用的验证方法,这里使用了之前定义的 validateUsername 和 validatePassword 函数。 trigger 属性表示触发验证的事件,这里使用了 blur ,即当用户离开该输入框时进行验证。*/
      };
    },
    methods: {
      submitForm(formName) {
        this.$refs[formName].validate((valid) => {
          if (valid) {
            this.axios({
              method: 'post',
              url: '/api/login',
              data: {
                username: this.formData.username,
                password: this.formData.password,
              },
            }).then((data) => {
              let ret = data.data;
              if (ret.success) {
                this.$message({
                  type: 'success',
                  message: '登录成功',
                });
                sessionStorage.setItem("auth", ret.data);
                this.$router.push("/")
              } else {
                this.$message({
                  message: ret.data,
                  type: 'error',
                });
              }
            })
          } else {
            return false;
          }
        });
      },
      /*这段代码是一个表单提交函数,用于在用户点击“登录”按钮时提交表单数据,并向服务器发送请求验证用户身份。

首先,通过 this.$refs[formName].validate() 方法对表单进行验证。该方法接受一个回调函数作为参数,当表单验证通过时可执行回调函数中的代码,否则返回一个错误。这里使用了 ES6 中的箭头函数,使得代码更加简洁。

当表单验证通过之后,会向服务器发送一个 POST 请求,请求的地址为 /api/login 。其中,传递给服务器的数据包括用户输入的账号和密码,这些数据存储在组件中的 formData 对象中。在服务器处理完请求之后,返回的结果保存在 data 变量中,使用 ret 变量来接收服务器的响应数据。如果服务器返回的数据中 success 属性值为 true,说明用户已经成功登录,此时弹出一个提示框,提示用户登录成功,并将用户的身份信息写入 sessionStorage 中。最后,使用 this.$router.push("/") 方法将用户重定向到系统首页。

如果服务器返回的数据中 success 属性值为 false,说明用户登录失败,此时弹出一个带有错误提示信息的提示框,提醒用户重新输入账号和密码。*/
      resetForm(formName) {
        this.$refs[formName].resetFields();
      }
    }
  }
  /* resetFields() 方法可以将该表单中所有的值重置为初始值*/
</script>

主页效果图:
在这里插入图片描述

Home.vue

<template>
    <div id="home">
      <LeftNav id="leftNav"></LeftNav>
      <TopNav id="topNav"></TopNav>
      <div class="container">
        <transition name="el-fade-in-linear">
          <router-view v-show="isRouterAlive" v-if="isRouterAlive">		  </router-view>
        </transition>
      </div>
    </div> 
</template>

<script>
	import LeftNav from "../components/LeftNav";
    import TopNav from "../components/TopNav";
    export default {
      name: "Home",
      components: {TopNav, LeftNav},
      // 提供方法到子组件中
      provide() {
        return {
          reload: this.reload,
        }
      },
      data() {
        return {
          isRouterAlive: true
        }
      },
      methods: {
        // 刷新组件
        reload() {
          this.isRouterAlive = false;
          this.$nextTick(function () {
            // 当数据被修改后使用这个方法会回调获取更新后的dom再渲染出来
            this.isRouterAlive = true;
          });
        }
      },
    }
</script>

侧边栏:Left.vue

<template>
    <div id="leftNav">
      <div class="container">
        <button  v-for="item in navData"
                     :to="{name: item.toUrl}"
                     :class="curNav===item.value?'active':''"
                     @click="navClick(item.value, item.toUrl)">
          {{item.value}}
        </button>
        /*
这段代码是一个 Vue.js 组件中的模板,用于渲染多个 button 元素,每个 button 元素代表着导航栏中的一个导航链接。navData 可能是一个数组,包含了每个导航按钮的名称和跳转路径等信息。通过 v-for 指令,可以将 navData 数组中的每个元素都映射成一个 button 元素,并依次进行渲染。

其中,:to="{name: item.toUrl}" 是一个 Vue Router 的路由跳转,在这里指定了该按钮的目标路由地址。:class="curNav===item.value?'active':''" 用于动态绑定该按钮的 class 属性,当选中该按钮时,会动态添加 active 样式类,以高亮显示该按钮。同时,@click 事件监听器将触发 navClick 方法,并传递两个参数:该按钮对应的 value 值和该按钮对应的跳转路径 toUrl,以便在组件中处理点击该按钮后的相应逻辑。

需要注意的是,在渲染该页面时,应该保证 navData 数组中的每个元素都有一个唯一的 value 值,以便在组件中进行状态管理和路由跳转等操作。
	*/
      </div>
    </div>
</template>

<script>
    export default {
      name: "LeftNav",
      inject: ['reload'],
      methods: {
        // 点击导航栏后,先转化点击状态
        navClick: function (curNav, toUrl) {
          this.reload();
          this.curNav = curNav;
          this.$router.push(toUrl);
        },
        setCurNav: function (newNav) {
          this.curNav = newNav;
        }
      },
      data() {
        let navMap = {
          '/student': "学生管理",
          '/team': "班级管理",
          '/teacher': "教师管理",
          '/course': "课程管理",
          
        }
        return {
          navData: [
            {
              value: '学生管理',
              toUrl: '/student',
            },
            {
              value: '班级管理',
              toUrl: '/team'
            },
            {
              value: '教师管理',
              toUrl: '/teacher'
            },
            {
              value: '课程管理',
              toUrl: '/course'
            },
          ],
          curNav: navMap[this.$route.path],
        }
      },
      
    }
</script>

Student.vue

<template>
  <div id="student">
    <div class="titleBar"><span>学生管理</span></div>
    <div class="container">
      <div class="body">
        <div class="operationBar">
          <el-button type="primary" @click="dialogVisible=true">添加学生</el-button>
        <el-dialog
          top="10vh"
          title="学生信息"
          :visible.sync="dialogVisible"
          :destroy-on-close="true"
          @opened="dialogOpen"
          @close="dialogClose"
          width="40%">
          /*
这段代码是使用了Element UI框架的 <el-dialog> 组件进行弹窗显示。
其中,top="10vh" 表示弹窗距离顶部的距离为 10vh。
 title="学生信息" 表示弹窗的标题为 “学生信息”。 :visible.sync="dialogVisible" 表示控制弹窗是否可见的变量为 dialogVisible。
 当dialogVisible的值被改变时,弹窗的可见性也会同步变化。 :destroy-on-close="true" 表示当弹窗被关闭时,将该组件销毁而不是保留在DOM中。 @opened="dialogOpen" 表示组件在打开时触发回调函数 dialogOpen。 @close="dialogClose" 表示组件在关闭时触发回调函数 dialogClose。width="40%" 表示弹窗宽度为父元素宽度的 40%。
	*/
          <StudentDialog
            ref="dialog"
            :teams="this.teams"
            :teamId="this.currentTeamId"
            :initTeamLabel="this.initTeamLabel"
            :isCreate="this.isAddButton"
            :oldStudentData="{ ...this.oldStudentData }"
          >
          </StudentDialog>
        </el-dialog>
          <div class="selectBar">
            <el-select
              @change="changeLabel"
              @clear="()=>{
                let path = this.$router.history.current.path;
                this.$router.push(path);
              }"
              v-model="initTeamLabel" clearable placeholder="选择班级">
              <el-option
                v-for="item in teams"
                :key="item.id"
                :label="getLabel(item)"
                :value="item.id">
              </el-option>
            </el-select>
          </div>
          /*@change="changeLabel" 指定了当用户选择班级信息后触发的函数 changeLabel,该函数可以在 Vue 实例中定义,用于处理选择班级后的相应逻辑。

@clear="..." 则指定了清空选择框后触发的函数,这里的函数中使用了 this.$router.history.current.path 获取当前路由路径,并通过 $router.push 方法重新跳转到当前路由,以清空已选中的班级信息。

v-model="initTeamLabel" 是双向绑定指令,用于将选中的班级信息绑定到 Vue 实例的 initTeamLabel 变量中,以在组件外部进行访问和处理。

<el-option> 标签用于渲染下拉列表中的每个选项,通过 v-for 指令遍历 teams 数组中的每个元素 item,并将其渲染为一个选项,并为该选项绑定 id 属性作为唯一标识符,同时为该选项绑定 label 和 value 属性,分别代表选项的显示名称和实际值。*/
          <div class="findBar">
            <input v-model="findKey" type="text" placeholder="根据姓名查询" @keyup.enter="findLikeName"></input>
            <el-button type="primary" @click="findLikeName"><i class="el-icon-search"></i></el-button>
            <el-button type="primary" @click="()=>{this.findKey=null;findLikeName();}"><i
              class="el-icon-refresh-right"></i>重置
            </el-button>
          </div>
        </div>
        <!--表格-->
        <el-table
          v-loading.lock="loading"
          :data="studentsData.content"
          border
          style="width: 96%; margin-left: 1vw; margin-top: 5vh;">
          /*
其中 v-loading.lock="loading" 指定了在加载数据时显示一个遮罩层,并禁止用户进行操作。loading 是一个布尔类型变量,用于指示当前是否正在加载数据,可以在 Vue 实例中进行定义和处理。

:data="studentsData.content" 绑定了表格的数据源 studentsData.content,该数据源通常是一个数组类型,在 Vue 实例中定义,在表格中可以通过 v-for 指令循环渲染每一行数据。

border 设置表格的边框样式,可以根据需要进行调整。

style="width: 96%; margin-left: 1vw; margin-top: 5vh;" 设置表格的样式属性,包括宽度、左边距和上边距等。
*/
          <el-table-column
            label=" #"
            width="50">
            <template slot-scope="scope">
              <span style="margin-left: 10px">{{ scope.$index + 1 }}</span>
            </template>
          </el-table-column>
          <el-table-column
            label="学号"
            width="100">
            <!--通过 Scoped slot 可以获取到 row, column, $index 和 store(table 内部的状态管理)的数据-->
            <template slot-scope="scope">
              <i class="el-icon-info"></i>
              <span style="margin-left: 10px">{{ scope.row.studentNumber }}</span>
            </template>
          </el-table-column>
          <el-table-column
            label="班级"
            width="200">
            <template slot-scope="scope">
              <span style="margin-left: 10px">{{ scope.row.teamFullName }}</span>
            </template>
          </el-table-column>
          <el-table-column
            label="姓名"
            width="100">
            <template slot-scope="scope">
              <div slot="reference" class="name-wrapper">
                <el-tag size="medium" type="info">{{ scope.row.name }}</el-tag>
              </div>
            </template>
          </el-table-column>
          <el-table-column
            label="性别"
            show-overflow-tooltip
            width="50">
            <template slot-scope="scope">
              <span style="margin-left: 10px">{{getGender(scope.row.gender)}}</span>
            </template>
          </el-table-column>
          <el-table-column
            label="民族"
            width="90">
            <template slot-scope="scope">
              <span style="margin-left: 10px">{{scope.row.national}}</span>
            </template>
          </el-table-column>
          <el-table-column
            label="出生日期"
            width="120">
            <template slot-scope="scope">
              <span style="margin-left: 10px">{{ getDate(scope.row.birthDate) }}</span>
            </template>
          </el-table-column>
          <el-table-column
            label="籍贯"
            show-overflow-tooltip
            width="200">
            <template slot-scope="scope">
              <span style="margin-left: 10px">{{ scope.row.nativePlace }}</span>
            </template>
          </el-table-column>
          <el-table-column
            label="电话号码"
            width="130">
            <template slot-scope="scope">
              <el-tag
                type="info"
                effect="plain">
                {{ scope.row.phoneNumber }}
              </el-tag>
            </template>
          </el-table-column>
          <el-table-column label="操作">
            <template slot-scope="scope">
              <el-button
                size="mini"
                @click="handleEdit(scope.row)">编辑
              </el-button>
              <el-button
                size="mini"
                type="danger"
                @click="handleDelete(scope.row.id)">删除
              </el-button>
            </template>
          </el-table-column>
        </el-table>
        <div class="pageBlock">
          <el-pagination
            @current-change="handleCurrentChange"
            @size-change="handleSizeChange"
            :current-page="currentPageNumber"
            :page-sizes="[8,16,24,40]"
            :page-size="currentPageSize"
            layout="total,sizes, prev, pager, next"
            :total="this.studentsData.totalCount">
          </el-pagination>
        </div>
      </div>
      <Footing class="footing"></Footing>
    </div>
  </div>
</template>

<script>
  import Footing from "../components/Footing";
  import StudentPopover from "../components/StudentDialog";
  import StudentDialog from "../components/StudentDialog";
  import { Loading } from "element-ui";

  export default {
    name: "Student",
    components: {StudentDialog, Footing},
    inject: ['reload'],
    provide() {
      return {
        reload: this.reload,
      }
    },
    methods: {
      // 对话框关闭时的回调函数
      dialogClose: function() {
        // 默认为添加
        this.isAddButton = true;
      },
      // 对话框打开时,判断是添加还是更新
      dialogOpen: function() {
        if (this.isAddButton) {
          return;
        }
        // 通过ref找到子组件对其直接控制
        let dialog = this.$refs.dialog;
        dialog.studentData = dialog.oldStudentData;
        dialog.initTeamLabel = dialog.oldStudentData.teamFullName;
      },
      handleEdit: function(row) {
        this.dialogVisible = true;
        this.isAddButton = false;
        this.oldStudentData = row;
      },
      // 改变班级option
      changeLabel: function (value) {
        this.setTeamId(value);
        this.getStudentPageData(0, this.currentPageSize);
      },
      // 设置url的query参数
      setTeamId: function (newTeamId) {
        let path = this.$router.history.current.path;
        // 这里刷新了一次页面
        this.$router.push({path, query: {teamId: newTeamId}});
        this.currentTeamId = newTeamId;
      },
      // 设置option的Label
      getLabel: function (team) {
        let year = this.getYear(team.schoolYear);
        return `${year}${team.professional}专业${team.classNumber}`
      },
      getYear: function (timestamp) {
        let date = new Date(timestamp);
        return date.getFullYear();
      },
      // 处理删除按钮
      handleDelete: function (id) {
        this.axios({
          method: 'delete',
          url: '/api/students/' + id,
        }).then((data) => {
          let ret = data.data;
          if (ret.success) {
            this.$message({
              type: 'success',
              message: '删除成功',
            });
            // 当前删除的是否是本页的最后一条数据
            if (this.studentsData.content.length === 1 && this.currentPageNumber !== 1) {
              this.currentPageNumber--;
              this.getStudentPageData(this.currentPageNumber - 1, this.currentPageSize);
            } else {
              this.getStudentPageData(this.currentPageNumber - 1, this.currentPageSize);
            }
          } else {
            this.$message({
              message: '删除失败',
              type: 'error',
            });
          }
        })
      },
      // 处理分页改变
      handleCurrentChange: function (val) {
        this.currentPageNumber = val;
        this.getStudentPageData(this.currentPageNumber - 1, this.currentPageSize);
      },
      handleSizeChange: function(val) {
        // 设置新的页面大小后
        this.currentPageSize = val;
        // 回到第一页
        this.handleCurrentChange(1);
      },
      // 根据名称查询
      findLikeName: function () {
        this.handleCurrentChange(1);
      },
      // 封装axios请求
      getStudentPageData: function (number, size) {
        this.axios({
          method: 'get',
          url: '/api/students',
          params: {
            pageNumber: number,
            pageSize: size,
            name: this.findKey,
            teamId: this.currentTeamId,
          }
        }).then((data) => {
          let ret = data.data;
          if (ret.success) {
            this.studentsData = ret.data;
          } else {
            this.$message({
              message: '请求数据失败',
              type: 'error',
            });
          }
        });
      },
      // 查询所有班级信息
      getTeamAllData: function () {
        this.axios({
          method: 'get',
          url: "/api/teams/all",
        }).then((data) => {
          let ret = data.data;
          if (ret.success) {
            this.teams = ret.data;
            // 如果初始化时url上有teamId
            if (this.currentTeamId != null) {
              let i;
              let len = ret.data.length;
              for (i = 0; i < len; i++) {
                if (ret.data[i].id == this.currentTeamId) {
                  this.initTeamLabel = this.getLabel(ret.data[i]);
                  break;
                }
              }
              if (i == len) {
                this.$message({
                  message: '不存在指定的班级',
                  type: 'error',
                });
              }
            }
          } else {
            this.$message({
              message: '请求数据失败',
              type: 'error',
            });
          }
        })
      },
      getGender: function (gender) {
        if (gender === null) {
          return '';
        }
        if (gender) {
          return '男';
        } else {
          return '女';
        }
      },
      getDate: function (timestamp) {
        let dateTime = new Date(timestamp);
        let year = dateTime.getFullYear();
        let month = (dateTime.getMonth() + 1).toString().padStart(2, '0');
        let day = dateTime.getDate().toString().padStart(2, '0');
        return `${year}-${month}-${day}`
      }
    },
    data() {
      // 刚进入页面时从url上获取teamId的初始值
      let teamId = this.$route.query.teamId === undefined ? null : this.$route.query.teamId;
      return {
        // 姓名查询关键词
        findKey: null,
        // 学生分页数据
        studentsData: {},
        // 当前页码
        currentPageNumber: 1,
        // 当前页面数据数量
        currentPageSize: 8,
        // 所有班级信息用于select
        teams: null,
        // 当前班级id,用于url上刷新
        currentTeamId: teamId,
        // 如果url上有班级id,select上显示的是该班级的label
        initTeamLabel: null,
        // 对话框是否显示
        dialogVisible: false,
        // 是否是添加按钮
        isAddButton: true,
        // 旧学生数据 编辑使用
        oldStudentData: null,
        // 是否显示加载
        loading: false,
      }
    },
    created() {
      this.loading = true;
      this.getStudentPageData(0, this.currentPageSize);
      this.getTeamAllData();
      this.loading = false;
    }
  }
</script>

studentDialog.vue

<template>
  <div id="studentDialog">
    <div class="container">
      <div class="properties">
        <div class="property">
          <span class="propertyTitle">学号:</span>
          <el-input class="propertyInput" v-model="studentData.studentNumber" placeholder="请输入学号"></el-input>
        </div>
        <div class="property">
          <span class="propertyTitle">班级:</span>
          <el-select class="propertyInput"
                     @change="labelChange"
                     v-model="this.initTeamLabel" clearable placeholder="选择班级">
            <el-option
              v-for="item in teams"
              :key="item.id"
              :label="getLabel(item)"
              :value="item.id">
            </el-option>
          </el-select>
        </div>
        <div class="property">
          <span class="propertyTitle">姓名:</span>
          <el-input class="propertyInput" v-model="studentData.name" placeholder="请输入姓名"></el-input>
        </div>
        <div class="property">
          <span class="propertyTitle">性别:</span>
          <div class="propertyInput">
            <el-radio-group v-model="studentData.gender">
              <el-radio :label="true"></el-radio>
              <el-radio :label="false"></el-radio>
            </el-radio-group>
          </div>
        </div>
        <div class="property">
          <span class="propertyTitle">民族:</span>
          <el-select class="propertyInput" v-model="studentData.national" clearable placeholder="选择民族">
            <el-option
              v-for="item in nationals"
              :key="item"
              :value="item">
            </el-option>
          </el-select>
        </div>
        <div class="property">
          <span class="propertyTitle">出生日期:</span>
          <el-date-picker
            class="propertyInput"
            value-format="timestamp"
            v-model="studentData.birthDate"
            type="date"
            placeholder="选择出生日期">
          </el-date-picker>
        </div>
        <div class="property">
          <span class="propertyTitle">籍贯:</span>
          <el-input class="propertyInput" v-model="studentData.nativePlace" placeholder="请输入籍贯"></el-input>
        </div>
        <div class="property">
          <span class="propertyTitle">电话号码:</span>
          <el-input class="propertyInput"
                    maxlength="11"
                    show-word-limit
                    suffix-icon="el-icon-phone"
                    v-model="studentData.phoneNumber"
                    placeholder="请输入电话号码"></el-input>
        </div>
        <div slot="footer" class="dialog-footer">
          <el-button @click="closeClick">取 消</el-button>
          <el-button type="primary" @click="solveClick">确 定</el-button>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
  export default {
    name: "StudentDialog",
    props: ['teams', 'isCreate', 'oldStudentData', 'teamId', 'initTeamLabel'],
    inject: ['reload'],
    methods: {
      updateStudent: function() {
        this.axios({
          method: 'put',
          url: '/api/students/' + this.studentData.id,
          data: this.studentData,
        }).then((data) => {
          let ret = data.data;
          if (ret.success) {
            this.$message({
              type: 'success',
              message: '更新成功',
            });
            // 刷新页面
            this.reload();
          } else {
            this.$message({
              message: '更新失败' + ret.data,
              type: 'error',
            });
          }
        })
      },
      createStudent: function() {
        this.axios({
          method: 'post',
          url: '/api/students',
          data: this.studentData,
        }).then((data) => {
          let ret = data.data;
          if (ret.success) {
            this.$message({
              type: 'success',
              message: '添加成功',
            });
            // 刷新页面
            this.reload();
          } else {
            this.$message({
              message: '添加失败' + ret.data,
              type: 'error',
            });
          }
        })
      },
      // 班级下拉框改变时
      labelChange: function(value) {
        this.initTeamLabel = value;
        this.studentData.teamId = value;
      },
      // 确定按钮
      solveClick: function() {
        if (this.isCreate) {
          this.createStudent();
        } else {
          this.updateStudent();
        }
      },
      // 取消处理
      closeClick: function() {
        // 关闭对话框
        this.$parent.$parent.dialogVisible = false;
      },
      // 设置option的Label
      getLabel: function (team) {
        let year = this.getYear(team.schoolYear);
        return `${year}${team.professional}专业${team.classNumber}`
      },
      getYear: function (timestamp) {
        let date = new Date(timestamp);
        return date.getFullYear();
      },
      setStudentData: function (data) {
        this.studentData = data;
      }
    },
    data() {
      return {
        studentData: {
          teamId: this.teamId,
        },
        nationals: [
          "汉族", "壮族", "满族", "回族", "苗族", "维吾尔族", "土家族", "彝族", "蒙古族", "藏族", "布依族", "侗族", "瑶族", "朝鲜族", "白族", "哈尼族",
          "哈萨克族", "黎族", "傣族", "畲族", "傈僳族", "仡佬族", "东乡族", "高山族", "拉祜族", "水族", "佤族", "纳西族", "羌族", "土族", "仫佬族", "锡伯族",
          "柯尔克孜族", "达斡尔族", "景颇族", "毛南族", "撒拉族", "布朗族", "塔吉克族", "阿昌族", "普米族", "鄂温克族", "怒族", "京族", "基诺族", "德昂族", "保安族",
          "俄罗斯族", "裕固族", "乌孜别克族", "门巴族", "鄂伦春族", "独龙族", "塔塔尔族", "赫哲族", "珞巴族"
        ],
      }
    },
  }
</script>

<style scoped>
  .container {
    display: flex;
    justify-content: center;
  }

  .properties {
    width: 100%;
    display: flex;
    flex-direction: column;
  }

  .property {
    width: 80%;
    display: flex;
    justify-content: space-between;
    padding-bottom: 1vh;
  }

  .propertyTitle {
    font-weight: 600;
    width: 40%;
    display: flex;
    justify-content: flex-end;
    align-items: center;
    padding-right: 1vw;
  }

  .propertyTitle:before {
    content: "*";
    color: rgba(241,8,8,0.88);
    padding-right: 2px;
  }

  .propertyInput {
    height: 40px;
    width: 60%;
    display: flex;
    align-items: center;
  }

  .dialog-footer {
    display: flex;
    justify-content: flex-end;
    padding-right: 20%;
    padding-top: 1vh;
  }
</style>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/875285.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Jsoup爬取简单信息

1. 豆瓣图书最受关注 1.1 创建SpringBoot项目或者Maven项目 1.2 引入jsoup <dependency><!-- jsoup HTML parser library https://jsoup.org/ --><groupId>org.jsoup</groupId><artifactId>jsoup</artifactId><version>1.15.3<…

全国区划代码数据筛选重组

你知道的越多&#xff0c;你不知道的越多 点赞再看&#xff0c;养成习惯 如果您有疑问或者见解&#xff0c;欢迎指教&#xff1a; 企鹅&#xff1a;869192208 文章目录 前言引入jar包实现思路代码实现验证 Guava工具类找出两个 Map 集合的差异数据筛选残联区划和全国区划差异组…

宇凡微电热毯方案开发,多档调节带定时

电热毯在1912年发明&#xff0c;到现在已有百年历史。现在的电热毯更有了许多智能化产品&#xff0c;这么多年来拯救了许多怕冷的小伙伴们&#xff0c;在寒冷的冬季靠它续命。宇凡微推出的电热毯方案&#xff0c;电热毯单片机使用54E&#xff0c;实现的功能有档位调节&#xff…

扬起的沙尘如何形成卷云

被气旋吹到空中的沙尘为冰云的形成提供了成核粒子。 卷云是由空气中的冰粒形成的。 卷云是由纯冰粒子组成的高云&#xff0c;主要在8-17 公里高空出现。 这些云通过散射入射的阳光和吸收地球发出的红外辐射&#xff0c;对地球的气候产生重要影响。 在一项最新的研究中&#xf…

保姆级SPSS图文安装教程

1.SPSS安装包下载 链接&#xff1a;百度网盘 请输入提取码 提取码&#xff1a;rb0n 2.SPSS安装 1.上面压缩包解压后双击解压文件中的setup.exe 2.点击下图绿色框中内容进行安装 3.下一步 4.接受协议&#xff0c;下一步 5.是&#xff0c;下一步 6.接受协议&#xff0c;下一步…

python菱形问题

Python类分为两种&#xff0c;一种叫经典类&#xff0c;一种叫新式类。都支持多继承&#xff0c;但继承顺序不同。 新式类&#xff1a;从object继承来的类。&#xff08;如:class A(object)&#xff09;&#xff0c;采用广度优先搜索的方式继承&#xff08;即先水平搜索&#…

图分类,图机器学习最新进展

图分类&#xff0c;图机器学习最新进展 1.Flat_Pooling TitleVenueTaskCodeDatasetDMLAP: Multi-level attention pooling for graph neural networks: Unifying graph representations with multiple localitiesNeural Networks 20221. Graph ClassificationNonesynthetic, …

Tomcat日志中文乱码

修改安装目录下的日志配置 D:\ProgramFiles\apache-tomcat-9.0.78\conf\logging.properties java.util.logging.ConsoleHandler.encoding GBK

感受RFID服装门店系统的魅力

嘿&#xff0c;亲爱的时尚追随者们&#xff01;今天小编要给你们带来一股时尚新风潮&#xff0c;让你们感受一下什么叫做“RFID服装门店系统”&#xff0c;这个超酷的东西&#xff01; 别着急&#xff0c;先别翻白眼&#xff0c;小编来解释一下RFID是什么玩意儿。它是射频识别…

Android使用Gradle kotlin dsl 优雅配置构建项目

目录 概述1.Gradle Kotlin-DSL配置1.1 在根目录下建立一个buildSrc目录&#xff0c;1.2.新建build.gradle.kts文件并添加Kotlin dsl相关配置 2.Gradle Kotlin DSL 的编写2.1 定义项目的版本号信息2.2.定义Dependencies管理项目中需要使用的库依赖2.3 定义APK的打包脚本构建APK的…

R-Meta分析与【文献计量分析、贝叶斯、机器学习等】多技术融合

Meta分析是针对某一科研问题&#xff0c;根据明确的搜索策略、选择筛选文献标准、采用严格的评价方法&#xff0c;对来源不同的研究成果进行收集、合并及定量统计分析的方法&#xff0c;最早出现于“循证医学”&#xff0c;现已广泛应用于农林生态&#xff0c;资源环境等方面。…

600份国家自然基金申报书--模板

600份国家自然基金申报书--模板 0、引言1、 目录2、网盘链接 ⚠申明&#xff1a; 未经许可&#xff0c;禁止以任何形式转载&#xff0c;若要引用&#xff0c;请标注链接地址。 全文共计3077字&#xff0c;阅读大概需要3分钟 &#x1f308;更多学习内容&#xff0c; 欢迎&#x…

增速骤降2703亿

号外&#xff1a;公众号「刘教链Pro」今日发表《如果牛市停止加仓》。公众号「刘教链内参」今日发表《内参&#xff1a;灰度负溢价继续收窄&#xff0c;微策略跑赢一切》。欢迎点击阅读。 * * * * * * 日前&#xff0c;中国人民银行网站公布了7月份的金融统计数据报告&#xf…

原生信息流广告特点,如何帮APP开发者增加变现收益?

简单来说&#xff1a;原生广告&#xff0c;就是把广告片和账号&#xff0c;一起用消耗推流的买量模式&#xff0c;一同投放出去。 用户看到的广告/内容&#xff0c;与原生视频没有差别——用户可以点头像关注、也可以查看账号历史信息。原生广告本质&#xff0c;是显得真实、原…

头条移动端项目Day03 —— 自媒体素材管理、自媒体文章管理、自媒体文章发布

❤ 作者主页&#xff1a;欢迎来到我的技术博客&#x1f60e; ❀ 个人介绍&#xff1a;大家好&#xff0c;本人热衷于Java后端开发&#xff0c;欢迎来交流学习哦&#xff01;(&#xffe3;▽&#xffe3;)~* &#x1f34a; 如果文章对您有帮助&#xff0c;记得关注、点赞、收藏、…

JDK 17 营销初体验 —— 亚毫秒停顿 ZGC 落地实践 | 京东云技术团队

前言 自 2014 年发布以来&#xff0c; JDK 8 一直都是相当热门的 JDK 版本。其原因就是对底层数据结构、JVM 性能以及开发体验做了重大升级&#xff0c;得到了开发人员的认可。但距离 JDK 8 发布已经过去了 9 年&#xff0c;那么这 9 年的时间&#xff0c;JDK 做了哪些升级&am…

Java 8:Stream API 流式操作

&#x1f497;wei_shuo的个人主页 &#x1f4ab;wei_shuo的学习社区 &#x1f310;Hello World &#xff01; Java 8&#xff1a;Stream API Java 8 中的 Stream API 是一组用于对集合数据进行处理的新特性&#xff1b;提供一种以声明式风格对集合进行操作的方式&#xff0c;简…

pdf加水印怎么加?掌握这几种加水印方法就够了

pdf加水印怎么加&#xff1f;水印可以帮助保护你的文档免受未经授权的复制或滥用。通过在PDF文件上添加水印&#xff0c;可以在每页或指定位置显示作者、公司名称、版权信息或其他标识&#xff0c;以确保他人无法随意盗用你的作品。下面就给大家介绍几种添加水印的方法。 【迅捷…

Openlayers 实战 - 地图视野(View)- 图层 -(layer)- 资源(source)显示等级设置。

Openlayers 实战 - 地图视野&#xff08;View&#xff09;- 图层 -&#xff08;layer&#xff09;- 资源&#xff08;source&#xff09;显示等级设置。 问题原因核心代码完整代码&#xff1a;在线示例 在以往的项目维护中&#xff0c;出现一个问题&#xff0c;使用最新高清底图…

JS图表库LightningChart JS全新发布v4.2——新增多种雷达图表类型

LightningChartJS是Web上性能最高的图表库具有出色的执行性能 - 使用高数据速率同时监控数十个数据源。 GPU加速和WebGL渲染确保您的设备的图形处理器得到有效利用&#xff0c;从而实现高刷新率和流畅的动画。用于贸易&#xff0c;工程&#xff0c;航空航天&#xff0c;医药和其…