Bootstrap

学生信息管理系统(简化版)

前端部分(vue2)

!!前端采用vue2框架,下面只写出必要的代码文件,想要使用需自行先创建vue项目

部分截图

下面是目录结构

下面是public文件夹里面的html文件

<!DOCTYPE html>
<html lang="">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
    <title>吃西瓜不吐籽</title>

  </head>
  <body>
    <noscript>
      <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
    </noscript>
    <div id="app"></div>
  </body>
</html>

下面是src文件夹

assets文件夹

图片可以随便添加,要求格式是

下面给出几张图片,上传的时候大小是对的,如果下载下来不对还需自己调整

pages文件夹

components中没有文件

MyAdmin.vue
<template>
  <el-container>
    <el-header> 管理员 </el-header>
    <el-container>
      <el-aside width="200px">
        <el-menu
          default-active="2"
          class="el-menu-vertical-demo"
          background-color="#545c64"
          text-color="#fff"
          active-text-color="#ffd04b"
        >
          <el-submenu index="1">
            <template slot="title">
              <i class="el-icon-location"></i>
              <span>导航一</span>
            </template>
            <el-menu-item-group>
              <template slot="title">分组一</template>
              <el-menu-item index="1-1">选项1</el-menu-item>
              <el-menu-item index="1-2">选项2</el-menu-item>
            </el-menu-item-group>
            <el-menu-item-group title="分组2">
              <el-menu-item index="1-3">选项3</el-menu-item>
            </el-menu-item-group>
            <el-submenu index="1-4">
              <template slot="title">选项4</template>
              <el-menu-item index="1-4-1">选项1</el-menu-item>
            </el-submenu>
          </el-submenu>
          <el-menu-item index="2" @click="goToClass">
            <i class="el-icon-menu"></i>
            <span slot="title">班级</span>
          </el-menu-item>
          <el-menu-item index="3" @click="goToScore">
            <i class="el-icon-document"></i>
            <span slot="title">成绩</span>
          </el-menu-item>
          <el-menu-item index="4" @click="goToStudent">
            <i class="el-icon-setting"></i>
            <span slot="title">学籍</span>
          </el-menu-item>
        </el-menu>
      </el-aside>
      <el-main>
        <router-view></router-view>
      </el-main>
    </el-container>
  </el-container>
</template>

<script>
export default {
  name: "MyAdmin",
  methods: {
    goToClass() {
      this.$router.push("/MyAdmin/MyClass");
    },
    goToScore() {
      this.$router.push("/MyAdmin/MyScore");
    },
    goToStudent() {
      this.$router.push("/MyAdmin/MyStudents");
    },
  },
};
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.el-header,
.el-footer {
  background-color: #b3c0d1;
  color: #333;
  text-align: center;
  line-height: 60px;
}

.el-aside {
  background-color: #d3dce6;
  color: #333;
  text-align: center;
  line-height: 200px;
  height: 75vh;
}

.el-main {
  background-color: #e9eef3;
  color: #333;
  text-align: center;
  height: 75vh;
}

body > .el-container {
  margin-bottom: 40px;
}

.el-container:nth-child(5) .el-aside,
.el-container:nth-child(6) .el-aside {
  line-height: 260px;
}

.el-container:nth-child(7) .el-aside {
  line-height: 320px;
}
</style>
MyClass.vue
<template>
  <el-main>
    <!-- 顶部Header -->
    <el-header>
      <!-- 使用 el-row 和 el-col 来布局按钮,并添加自定义样式 -->
      <el-row
        type="flex"
        justify="end"
        style="width: 100%; padding-right: 20px"
        class="row-style"
      >
        <span
          style="
            flex-grow: 1;
            margin-right: 20px;
            text-align: center;
            display: block;
          "
        >
          班级信息
        </span>
        <el-button type="primary" @click="showAddDialog = true">添加</el-button>
        <el-button type="danger" @click="showBatchDelete = !showBatchDelete"
          >批量删除</el-button
        >
        <el-button
          v-if="showBatchDelete"
          type="danger"
          @click="confirmBatchDelete"
          >删除</el-button
        >
      </el-row>
    </el-header>

    <!-- 添加班级对话框 -->
    <el-dialog title="添加班级" :visible.sync="showAddDialog" width="30%">
      <el-form ref="addClassForm" :model="addFormData" label-width="100px">
        <el-form-item label="班级班号">
          <el-input v-model="addFormData.class_id"></el-input>
        </el-form-item>
        <el-form-item label="班级排名">
          <el-input v-model="addFormData.cls_rank"></el-input>
        </el-form-item>
        <el-form-item label="班主任">
          <el-input v-model="addFormData.head_teacher"></el-input>
        </el-form-item>
        <el-form-item label="学生总数">
          <el-input-number
            v-model="addFormData.student_count"
            :controls-position="right"
            :min="0"
          ></el-input-number>
        </el-form-item>
      </el-form>
      <span slot="footer" class="dialog-footer">
        <el-button @click="showAddDialog = false">取消</el-button>
        <el-button type="primary" @click="addClass">确定</el-button>
      </span>
    </el-dialog>

    <!-- 数据表格 -->
    <el-table
      v-if="tableData.length > 0"
      :data="tableData"
      style="width: 100%"
      @selection-change="handleSelectionChange"
      row-key="class_id"
    >
      <el-table-column
        v-if="showBatchDelete"
        type="selection"
        width="55"
      ></el-table-column>
      <el-table-column prop="class_id" label="班级班号" width="140">
      </el-table-column>
      <el-table-column prop="cls_rank" label="班级排名" width="120">
      </el-table-column>
      <el-table-column prop="head_teacher" label="班主任"> </el-table-column>
      <el-table-column prop="student_count" label="学生总数"> </el-table-column>
      <el-table-column label="操作" width="120">
        <template slot-scope="scope">
          <el-button
            type="danger"
            size="mini"
            @click="confirmDelete(scope.row.class_id)"
          >
            删除
          </el-button>
        </template>
      </el-table-column>
    </el-table>

    <!-- 无数据提示 -->
    <el-alert
      v-if="message === 'No data available'"
      title="暂无数据"
      type="info"
    >
    </el-alert>

    <!-- 错误提示 -->
    <el-alert v-if="error" :title="error" type="error" show-icon> </el-alert>

    <!-- 删除确认对话框 -->
    <el-dialog
      title="删除确认"
      :visible.sync="deleteDialogVisible"
      width="30%"
      :before-close="handleClose"
    >
      <span>确定要删除选中的班级吗?</span>
      <span slot="footer" class="dialog-footer">
        <el-button @click="deleteDialogVisible = false">取消</el-button>
        <el-button type="danger" @click="deleteSelected">确定删除</el-button>
      </span>
    </el-dialog>
  </el-main>
</template>

<script>
export default {
  name: "MyClass",
  data() {
    return {
      tableData: [],
      message: "",
      error: null,
      selectedRows: [],
      showBatchDelete: false, // 控制批量删除按钮的显示
      deleteDialogVisible: false, // 控制删除确认对话框的显示
      currentDeleteId: null, // 当前要删除的学号
      showAddDialog: false, // 控制添加对话框的显示
      addFormData: {
        class_id: "",
        cls_rank: "",
        head_teacher: "",
        student_count: 0,
      },
    };
  },
  created() {
    this.fetchClasses();
  },
  methods: {
    async fetchClasses() {
      try {
        // 注意:确保这里的URL与后端接口匹配
        const response = await fetch("http://127.0.0.1:8000/WJH/classes");
        if (!response.ok) {
          throw new Error("连接到服务器失败");
        }
        const data = await response.json();
        if (data.success) {
          this.tableData = data.data;
          this.message = data.message || "";
          if (this.tableData.length === 0 && !this.error) {
            this.message = "No data available"; // 设置无数据消息
          }
        } else {
          this.error = data.message || "未知错误";
        }
      } catch (error) {
        this.error = error.message || "请求数据时发生错误";
      }
    },
    confirmDelete(class_id) {
      this.currentDeleteId = class_id;
      this.deleteDialogVisible = true;
    },
    deleteSelected() {
      if (this.showBatchDelete) {
        this.sendDeleteRequest(this.selectedRows.map((row) => row.student_id));
      } else {
        this.sendDeleteRequest([this.currentDeleteId]);
      }
      this.deleteDialogVisible = false;
      this.selectedRows = [];
    },
    handleSelectionChange(val) {
      this.selectedRows = val;
    },
    confirmBatchDelete() {
      this.deleteDialogVisible = true;
    },
    // 发送删除请求到后端
    async sendDeleteRequest(classIds) {
      try {
        // 注意:确保这里的URL与后端接口匹配
        const response = await fetch("http://127.0.0.1:8000/WJH/classes", {
          method: "DELETE",
          headers: {
            "Content-Type": "application/json",
          },
          body: JSON.stringify({ class_ids: classIds }), // 将班级ID列表作为字典传递
        });
        if (!response.ok) {
          throw new Error("请求失败");
        }
        const data = await response.json();
        if (data.success) {
          this.$message({
            message: "删除成功",
            type: "success",
          });
          // 刷新表格数据
          this.fetchClasses();
        } else {
          this.$message.error(data.message || "删除操作失败");
        }
      } catch (error) {
        this.$message.error(error.message || "删除操作异常");
      }
    },
    handleClose(done) {
      this.$confirm("确认关闭?")
        .then(() => {
          done();
        })
        .catch(() => {});
    },

    addClass() {
      // 调用添加函数,将 addFormData 发送给后端
      this.addClassData(this.addFormData);
      this.showAddDialog = false; // 关闭对话框
    },
    // 添加学生数据的方法
    async addClassData(classData) {
      try {
        // 使用 fetch 发送 POST 请求到后端
        const response = await fetch("http://127.0.0.1:8000/WJH/classes", {
          // 确保这里的URL与后端接口匹配
          method: "POST",
          headers: {
            "Content-Type": "application/json",
          },
          body: JSON.stringify(classData), // 将班级数据转换为 JSON 字符串
        });

        if (!response.ok) {
          throw new Error("添加请求失败");
        }

        const data = await response.json();
        if (data.success) {
          this.$message({
            message: "添加成功",
            type: "success",
          });
          // 刷新表格数据
          this.fetchClasses();
        } else {
          // 检查是否有特定的错误信息
          if (data.error === "班级ID已存在") {
            this.$message.error("班级ID已存在");
          } else {
            this.$message.error(data.message || "添加操作失败");
          }
        }
      } catch (error) {
        this.$message.error(error.message || "添加操作异常");
      }
    },
  },
};
</script>

<style>
.el-header {
  background-color: #b3c0d1;
  color: #333;
  line-height: 60px;
}

.row-style {
  padding-top: 10px; /* 自定义上边距 */
  padding-bottom: 10px; /* 自定义下边距 */
}

.row-style {
  align-items: center; /* 垂直居中 */
  height: 100%; /* 确保占满整个header的高度 */
}
.el-aside {
  color: #333;
}
</style>
MyHome.vue
<template>
  <div class="container">
    <header>
      <!-- 顶部部分 -->
    </header>
    <div class="home-intro">
      <h1>吃西瓜不吐籽</h1>
      <h1>斯人若彩虹,遇上方知有</h1>
      <p v-if="isLoading">Loading...</p>
      <p v-else>{{ hitokoto }}</p>
    </div>

    <div class="image-row">
      <!-- 第一个部分,三张图片 -->
      <div class="image-overlay">
        <img
          src="../assets/picture/11.jpg"
          alt="Image 111"
          loading="lazy"
          width="600"
          height="444"
        />
        <div class="overlay-text">在我心里</div>
      </div>
      <div class="image-overlay">
        <img
          src="../assets/picture/2.jpg"
          alt="Image 2"
          loading="lazy"
          width="600"
          height="444"
        />
        <div class="overlay-text">你真的</div>
      </div>
      <div class="image-overlay">
        <img
          src="../assets/picture/8.jpg"
          alt="Image 3"
          loading="lazy"
          width="600"
          height="444"
        />
        <div class="overlay-text">很特别</div>
      </div>
    </div>
    <div class="image-row">
      <!-- 第二个部分,四张图片 -->
      <div class="image-overlay">
        <img
          src="../assets/picture/4.jpg"
          alt="Image 4"
          loading="lazy"
          width="600"
          height="444"
        />
        <div class="overlay-text">要是</div>
      </div>
      <div class="image-overlay">
        <img
          src="../assets/picture/5.jpg"
          alt="Image 5"
          loading="lazy"
          width="600"
          height="444"
        />
        <div class="overlay-text">能和你</div>
      </div>
      <div class="image-overlay">
        <img
          src="../assets/picture/6.jpg"
          alt="Image 6"
          loading="lazy"
          width="600"
          height="444"
        />
        <div class="overlay-text">在一起</div>
      </div>
      <div class="image-overlay">
        <img
          src="../assets/picture/7.jpg"
          alt="Image 7"
          loading="lazy"
          width="600"
          height="444"
        />
        <div class="overlay-text">就好了</div>
      </div>
    </div>
    <!-- 随便写点内容 -->
    <!-- 文字介绍盒子 -->
    <div class="intro-box">
      <p>《唐多令·芦叶满汀洲》</p>
      <p>宋代 刘过</p>
      <p>芦叶满汀洲,寒沙带浅流。</p>
      <p>二十年重过南楼。</p>
      <p>柳下系船犹未稳,能几日,又中秋。</p>
      <p>黄鹤断矶头,故人今在否?</p>
      <p>旧江山浑是新愁。</p>
      <p>欲买桂花同载酒,终不似,少年游。</p>
    </div>
    <!-- 登录按钮 -->
    <div class="login-button">
      <router-link to="/login">登录</router-link>
    </div>
  </div>
</template>

<script>
export default {
  name: "MyHome",
  data() {
    return {
      hitokoto: "",
      isLoading: true,
      loadingMessage: "加载中...",
    };
  },
  created() {
    this.fetchHitokoto();
  },
  methods: {
    fetchHitokoto() {
      // 请确保网络环境允许访问该 API,如果无法访问,请检查链接的合法性,并在网络稳定的情况下重试。
      fetch("https://v1.hitokoto.cn")
        .then((res) => res.json())
        .then((data) => {
          this.hitokoto = data.hitokoto;
          this.isLoading = false;
        })
        .catch((err) => {
          console.error(err);
          this.isLoading = false;
        });
    },
  },
};
</script>

<style scoped>
/* 全局容器样式 */
.container {
  margin: 0; /* 移除默认外边距 */
  padding: 0; /* 移除默认内边距 */
  font-family: Arial, sans-serif; /* 设置字体为Arial,无Arial时使用系统默认无衬线字体 */
  background-color: #2c2c2c; /* 设置背景颜色为深沉黑灰色 */
  color: #f5f5f5; /* 设置字体颜色为浅灰色 */
  width: 100vw; /* 设置宽度为视口宽度的100% */
  min-height: 100vh; /* 设置最小高度为视口高度的100% */
  padding: 20px; /* 设置内边距为20px */
}

/* 容器样式,确保最大宽度为100%并居中 */
.container {
  max-width: 100%;
  margin: 0 auto; /* 垂直方向0,水平方向自动,实现居中 */
  padding: 20px;
}

/* 头部样式 */
header {
  border-bottom: 1px solid #6b6b6b; /* 设置底部边框为1px的深灰色 */
  padding-top: 120px; /* 设置顶部内边距为120px */
  height: 100px; /* 设置header的高度为100px */
  margin-top: 300px; /* 添加20px的顶部外边距 */
}

/* 首页介绍区域样式 */
.home-intro {
  text-align: center; /* 文本居中 */
  padding: 20px 0; /* 设置顶部和底部内边距为20px */
}

/* 首页介绍区域标题样式 */
.home-intro h1 {
  color: #eae8f1; /* 设置标题颜色为高贵的紫色 */
}

/* 图片容器样式 */
.image-row {
  display: flex; /* 使用弹性盒子布局 */
  justify-content: center; /* 水平居中 */
  margin: 30px auto; /* 垂直方向30px外边距,水平方向自动外边距实现居中 */
  gap: 40px; /* 保持图片间隔为40px */
  width: 60%; /* 设置宽度为屏幕宽度的三分之二 */
}

/* 计算最大图片宽度 */
.max-image-width {
  max-width: calc(60vw / 3 - 70px); /* 基于图片盒子宽度的三分之一减去70px */
}

/* 图片样式 */
.image-row img {
  width: 100%; /* 图片宽度填满容器 */
  height: 56.25%; /* 图片高度设置为宽度的56.25%,以实现3:4的宽高比 */
  object-fit: cover; /* 保持图片宽高比,裁剪超出部分 */
  transition: filter 0.3s ease; /* 平滑过渡效果 */
}

/* 第二行图片样式,与第一行图片宽度一致 */
.image-row:nth-child(2) img {
  width: 100%; /* 图片宽度填满容器 */
  height: 56.25%; /* 图片高度设置为宽度的56.25% */
}

/* 图片覆盖层样式 */
.image-overlay {
  position: relative; /* 相对定位 */
  display: inline-block; /* 内联块级元素 */
}

/* 图片覆盖层中的图片样式 */
.image-overlay img {
  display: block; /* 块级元素 */
  width: 100%; /* 图片宽度填满容器 */
  height: auto; /* 高度自动 */

  transition: filter 0.3s ease; /* 平滑过渡效果 */
}

/* 鼠标悬停在图片覆盖层上的图片样式 */
.image-overlay:hover img {
  filter: blur(11px); /* 模糊效果 */
}

/* 图片覆盖层上的文字样式 */
.overlay-text {
  position: absolute; /* 绝对定位 */
  top: 50%; /* 顶部距离为父元素高度的50% */
  left: 50%; /* 左侧距离为父元素宽度的50% */
  transform: translate(-50%, -50%); /* 居中定位 */
  color: #ffffff; /* 字体颜色 */
  font-size: 24px; /* 字体大小 */
  opacity: 0; /* 初始透明度 */
  transition: opacity 0.3s ease; /* 透明度过渡效果 */
  text-align: center; /* 文本居中 */
}

/* 鼠标悬停在图片覆盖层上的文字样式 */
.image-overlay:hover .overlay-text {
  opacity: 1; /* 透明度为1,显示文字 */
}

@media (max-width: 768px) {
  .image-row img {
    width: calc(50% - 20px); /* 小屏幕下图片宽度 */
  }
}

/* 媒体查询,适用于较小屏幕(例如平板和部分大手机) */
@media (max-width: 768px) {
  .image-row {
    gap: 20px; /* 在较小屏幕上,间隔减少为20px */
    width: 100%; /* 图片盒子宽度占满屏幕宽度 */
  }
  .image-row img {
    width: calc(50% - 20px); /* 小屏幕下图片宽度 */
    height: auto; /* 高度自动调节 */
    object-fit: cover; /* 保持图片比例,裁剪超出部分 */
  }
}

/* 媒体查询,适用于较小屏幕(例如手机) */
@media (max-width: 480px) {
  .image-row {
    gap: 10px; /* 在非常小的屏幕上,间隔减少为10px */
    width: 100%; /* 图片盒子宽度占满屏幕宽度 */
  }
  .image-row img {
    width: 100%; /* 超小屏幕下图片占满宽度 */
    height: auto; /* 高度自动调节 */
    object-fit: cover; /* 保持图片比例,裁剪超出部分 */
  }
  header {
    border-bottom: 1px solid #6b6b6b; /* 设置底部边框为1px的深灰色 */
    padding-top: 120px; /* 设置顶部内边距为120px */
    height: 100px; /* 设置header的高度为100px */
    margin-top: 30px; /* 添加20px的顶部外边距 */
  }
}

/* 文字介绍盒子样式 */
.intro-box {
  width: 66.666%; /* 与图片容器相同的宽度 */
  margin: 30px auto; /* 垂直方向30px外边距,水平方向自动外边距实现居中 */
  padding: 20px;
  background-color: #333; /* 深色背景 */
  color: #f5f5f5; /* 字体颜色与页面一致 */
  text-align: center; /* 文本居中 */
}

.intro-box p {
  font-size: 16px;
  line-height: 1.6;
  color: #d8dade;
  margin-bottom: 8px;
}

.intro-box p:not(:last-child) {
  margin-bottom: 8px;
}

/* 登录按钮样式 */
.login-button {
  margin: 30px auto; /* 垂直方向30px外边距,水平方向自动外边距实现居中 */
  text-align: center; /* 文本居中 */
}

/* 路由链接样式 */
.login-button router-link {
  display: inline-block;
  padding: 10px 20px;
  background-color: #b388ff; /* 高贵紫作为按钮颜色 */
  color: #ffffff; /* 白色字体 */
  text-decoration: none; /* 移除下划线 */
  border-radius: 5px; /* 圆角边框 */
  transition: background-color 0.3s ease; /* 平滑背景颜色变化 */
}

/* 登录按钮悬停效果 */
.login-button router-link:hover {
  background-color: #9a67e6; /* 悬停时的背景颜色 */
}
</style>
MyIndex.vue

这是当时测试用的,可以不要

<template>
  <div class="container">
    <h1>登录成功</h1>
  </div>
</template>

<script>
export default {
  name: "MyIndex",
};
</script>

<style></style>
MyLogin.vue
<template>
  <div class="container">
    <div class="header">登录</div>
    <div class="form-wrapper">
      <input
        type="text"
        v-model="username"
        placeholder="Username"
        class="input-item"
        @input="checkInputLength"
      />
      <input
        type="password"
        v-model="password"
        placeholder="Password"
        class="input-item"
        @input="checkInputLength"
      />
      <button type="button" class="btn" @click="login">登录</button>
    </div>
    <div class="msg">
      <!-- 没有账户? <a href="#">注册</a> -->
      <router-link to="/enroll">注册</router-link>
    </div>
  </div>
</template>

<script>
export default {
  name: "MyLogin",
  data() {
    return {
      username: "",
      password: "",
    };
  },
  methods: {
    async login() {
      if (this.username.length > 16 || this.password.length > 16) {
        alert("用户名和密码均不得超过16个字符");
        return;
      }
      if (!this.username || !this.password) {
        alert("用户名和密码不能为空");
        return;
      }
      try {
        const response = await fetch("http://127.0.0.1:8000/WJH/login", {
          method: "POST",
          headers: {
            "Content-Type": "application/json",
          },
          body: JSON.stringify({
            username: this.username,
            password: this.password,
          }),
        });
        if (!response.ok) {
          throw new Error("连接失败");
        }
        const result = await response.json();
        if (!result.success) {
          alert("不存在此用户或密码错误");
        } else {
          //   处理登录成功的逻辑,例如跳转到另一个页面
          if (result.user_type === "admin") {
            this.$router.push("/MyAdmin"); // 管理员页面
          } else {
            this.$router.push("/MyOrdinary"); // 普通用户页面
          }
        }
      } catch (error) {
        alert(`连接失败: ${error.message}`);
      }
    },
    checkInputLength(event) {
      const maxLength = 16;
      if (event.target.value.length > maxLength) {
        alert(`输入内容不得超过${maxLength}个字符`);
        event.target.value = event.target.value.substring(0, maxLength);
      }
    },
  },
};
</script>

<style>
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}
html,
body {
  height: 100%;
  font-family: Arial, sans-serif;
}
body {
  background-color: #f7f7f7;
  display: flex;
  justify-content: center;
  align-items: center;
}
.container {
  background-color: white;
  width: 100%;
  max-width: 400px;
  padding: 40px;
  border-radius: 8px;
  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
.header {
  font-size: 24px;
  font-weight: bold;
  text-align: center;
  margin-bottom: 20px;
}
.input-item {
  width: 100%;
  margin-bottom: 15px;
  padding: 12px;
  border: 1px solid #ddd;
  border-radius: 4px;
  font-size: 16px;
}
.input-item::placeholder {
  color: #aaa;
}
.btn {
  display: block;
  width: 100%;
  padding: 15px;
  margin-top: 20px;
  background-color: #5c67f2;
  color: white;
  border: none;
  border-radius: 4px;
  font-size: 18px;
  cursor: pointer;
  transition: background-color 0.3s ease;
}
.btn:hover {
  background-color: #4a54e1;
}
.msg {
  text-align: center;
  margin-top: 15px;
  font-size: 14px;
}
a {
  color: #5c67f2;
  text-decoration: none;
}
</style>
MyOrdinary.vue
<template>
  <el-container>
    <el-header> 普通用户 </el-header>
    <el-container>
      <el-aside width="200px">
        <el-menu
          default-active="2"
          class="el-menu-vertical-demo"
          background-color="#545c64"
          text-color="#fff"
          active-text-color="#ffd04b"
        >
          <el-menu-item index="2" @click="goToClass">
            <i class="el-icon-menu"></i>
            <span slot="title">班级</span>
          </el-menu-item>
          <el-menu-item index="3" @click="goToScore">
            <i class="el-icon-document"></i>
            <span slot="title">成绩</span>
          </el-menu-item>
          <el-menu-item index="4" @click="goToStudent">
            <i class="el-icon-setting"></i>
            <span slot="title">学籍</span>
          </el-menu-item>
        </el-menu>
      </el-aside>
      <el-main>
        <router-view></router-view>
      </el-main>
    </el-container>
  </el-container>
</template>

<script>
export default {
  name: "MyOrdinary",
  methods: {
    goToClass() {
      this.$router.push("/MyOrdinary/OrdinaryClass");
    },
    goToScore() {
      this.$router.push("/MyOrdinary/OrdinaryScore");
    },
    goToStudent() {
      this.$router.push("/MyOrdinary/OrdinaryStudent");
    },
  },
};
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.el-header,
.el-footer {
  background-color: #48959ba9;
  color: #161515;
  text-align: center;
  line-height: 60px;
}

.el-aside {
  background-color: #48959ba9;
  color: #333333;
  text-align: center;
  line-height: 200px;
  height: 75vh;
}

.el-main {
  background-color: #e9eef3;
  color: #201c1c;
  text-align: center;
  height: 75vh;
}

body > .el-container {
  margin-bottom: 40px;
}

.el-container:nth-child(5) .el-aside,
.el-container:nth-child(6) .el-aside {
  line-height: 260px;
}

.el-container:nth-child(7) .el-aside {
  line-height: 320px;
}
</style>
MyScore.vue
<template>
  <el-main>
    <!-- 顶部Header -->
    <el-header>
      <!-- 使用 el-row 和 el-col 来布局按钮,并添加自定义样式 -->
      <el-row
        type="flex"
        justify="end"
        style="width: 100%; padding-right: 20px"
        class="row-style"
      >
        <span
          style="
            flex-grow: 1;
            margin-right: 20px;
            text-align: center;
            display: block;
          "
        >
          学生成绩信息
        </span>
        <el-button type="primary" @click="showAddDialog = true">添加</el-button>
        <el-button type="danger" @click="showBatchDelete = !showBatchDelete"
          >批量删除</el-button
        >
        <el-button
          v-if="showBatchDelete"
          type="danger"
          @click="confirmBatchDelete"
          >删除</el-button
        >
      </el-row>
    </el-header>

    <!-- 添加学生对话框 -->
    <el-dialog title="添加学生" :visible.sync="showAddDialog" width="30%">
      <el-form ref="addForm" :model="addFormData" label-width="100px">
        <el-form-item label="学号">
          <el-input v-model="addFormData.student_id"></el-input>
        </el-form-item>
        <el-form-item label="姓名">
          <el-input v-model="addFormData.stu_name"></el-input>
        </el-form-item>
        <el-form-item label="总成绩">
          <el-input v-model="addFormData.total_score"></el-input>
        </el-form-item>
        <el-form-item label="分数排名">
          <el-input v-model="addFormData.score_rank"></el-input>
        </el-form-item>
        <el-form-item label="班级">
          <el-input v-model="addFormData.class_"></el-input>
        </el-form-item>
      </el-form>
      <span slot="footer" class="dialog-footer">
        <el-button @click="showAddDialog = false">取消</el-button>
        <el-button type="primary" @click="addStudent">确定</el-button>
      </span>
    </el-dialog>

    <!-- 数据表格 -->
    <el-table
      v-if="tableData.length > 0"
      :data="tableData"
      style="width: 100%"
      @selection-change="handleSelectionChange"
      row-key="student_id"
    >
      <el-table-column
        v-if="showBatchDelete"
        type="selection"
        width="55"
      ></el-table-column>
      <el-table-column prop="student_id" label="学号" width="140">
      </el-table-column>
      <el-table-column prop="stu_name" label="姓名" width="120">
      </el-table-column>
      <el-table-column prop="total_score" label="总成绩"> </el-table-column>
      <el-table-column prop="score_rank" label="分数排名"> </el-table-column>
      <el-table-column prop="class_" label="班级"> </el-table-column>
      <el-table-column label="操作" width="120">
        <template slot-scope="scope">
          <el-button
            type="danger"
            size="mini"
            @click="confirmDelete(scope.row.student_id)"
          >
            删除
          </el-button>
        </template>
      </el-table-column>
    </el-table>

    <!-- 无数据提示 -->
    <el-alert
      v-if="message === 'No data available'"
      title="暂无数据"
      type="info"
    >
    </el-alert>

    <!-- 错误提示 -->
    <el-alert v-if="error" :title="error" type="error" show-icon> </el-alert>

    <!-- 删除确认对话框 -->
    <el-dialog
      title="删除确认"
      :visible.sync="deleteDialogVisible"
      width="30%"
      :before-close="handleClose"
    >
      <span>确定要删除选中的学号吗?</span>
      <span slot="footer" class="dialog-footer">
        <el-button @click="deleteDialogVisible = false">取消</el-button>
        <el-button type="danger" @click="deleteSelected">确定删除</el-button>
      </span>
    </el-dialog>
  </el-main>
</template>

<script>
export default {
  name: "MyScore",
  data() {
    return {
      tableData: [],
      message: "",
      error: null,
      selectedRows: [],
      showBatchDelete: false, // 控制批量删除按钮的显示
      deleteDialogVisible: false, // 控制删除确认对话框的显示
      currentDeleteId: null, // 当前要删除的学号
      showAddDialog: false, // 控制添加对话框的显示
      addFormData: {
        // 用户输入的数据
        student_id: "",
        stu_name: "",
        total_score: "",
        score_rank: "",
        class_: "",
      },
    };
  },
  created() {
    this.fetchStudentScores();
  },
  methods: {
    async fetchStudentScores() {
      try {
        const response = await fetch("http://127.0.0.1:8000/WJH/studentscores");
        if (!response.ok) {
          throw new Error("连接到服务器失败");
        }
        const data = await response.json();
        if (data.success) {
          this.tableData = data.data;
          this.message = data.message || "";
          if (this.tableData.length === 0 && !this.error) {
            this.message = "No data available"; // 设置无数据消息
          }
        } else {
          this.error = data.message || "未知错误";
        }
      } catch (error) {
        this.error = error.message;
      }
    },
    confirmDelete(studentId) {
      this.currentDeleteId = studentId;
      this.deleteDialogVisible = true;
    },
    deleteSelected() {
      if (this.showBatchDelete) {
        this.sendDeleteRequest(this.selectedRows.map((row) => row.student_id));
      } else {
        this.sendDeleteRequest([this.currentDeleteId]);
      }
      this.deleteDialogVisible = false;
      this.selectedRows = [];
    },
    handleSelectionChange(val) {
      this.selectedRows = val;
    },
    confirmBatchDelete() {
      this.deleteDialogVisible = true;
    },
    // 发送删除请求到后端
    async sendDeleteRequest(studentIds) {
      try {
        const response = await fetch(
          "http://127.0.0.1:8000/WJH/studentscores",
          {
            method: "DELETE",
            headers: {
              "Content-Type": "application/json",
            },
            body: JSON.stringify({ student_ids: studentIds }), // 将学号列表作为字典传递
          }
        );
        if (!response.ok) {
          throw new Error("请求失败");
        }
        const data = await response.json();
        if (data.success) {
          this.$message({
            message: "删除成功",
            type: "success",
          });
          // 刷新表格数据
          this.fetchStudentScores();
        } else {
          this.$message.error(data.message || "删除操作失败");
        }
      } catch (error) {
        this.$message.error(error.message || "删除操作异常");
      }
    },
    handleClose(done) {
      this.$confirm("确认关闭?")
        .then(() => {
          done();
        })
        .catch(() => {});
    },

    addStudent() {
      // 调用添加函数,将 addFormData 发送给后端
      this.addStudentData(this.addFormData);
      this.showAddDialog = false; // 关闭对话框
    },
    // 添加学生数据的方法
    async addStudentData(studentData) {
      try {
        // 使用 fetch 发送 POST 请求到后端
        const response = await fetch(
          "http://127.0.0.1:8000/WJH/studentscores",
          {
            method: "POST",
            headers: {
              "Content-Type": "application/json",
            },
            body: JSON.stringify(studentData), // 将学生数据转换为 JSON 字符串
          }
        );

        if (!response.ok) {
          throw new Error("添加请求失败");
        }

        const data = await response.json();
        if (data.success) {
          this.$message({
            message: "添加成功",
            type: "success",
          });
          // 刷新表格数据
          this.fetchStudentScores();
        } else {
          // 检查是否有特定的错误信息
          if (data.error === "学号已存在") {
            this.$message.error("学号已存在");
          } else {
            this.$message.error(data.message || "添加操作失败");
          }
        }
      } catch (error) {
        this.$message.error(error.message || "添加操作异常");
      }
    },
  },
};
</script>

<style>
.el-header {
  background-color: #b3c0d1;
  color: #333;
  line-height: 60px;
}

.row-style {
  padding-top: 10px; /* 自定义上边距 */
  padding-bottom: 10px; /* 自定义下边距 */
}

.row-style {
  align-items: center; /* 垂直居中 */
  height: 100%; /* 确保占满整个header的高度 */
}
.el-aside {
  color: #333;
}
</style>
MyStudents.vue
<template>
  <el-main>
    <!-- 顶部Header -->
    <el-header>
      <!-- 使用 el-row 和 el-col 来布局按钮,并添加自定义样式 -->
      <el-row
        type="flex"
        justify="end"
        style="width: 100%; padding-right: 20px"
        class="row-style"
      >
        <span
          style="
            flex-grow: 1;
            margin-right: 20px;
            text-align: center;
            display: block;
          "
        >
          学生学籍信息
        </span>
        <el-button type="primary" @click="showAddDialog = true">添加</el-button>
        <el-button type="danger" @click="showBatchDelete = !showBatchDelete"
          >批量删除</el-button
        >
        <el-button
          v-if="showBatchDelete"
          type="danger"
          @click="confirmBatchDelete"
          >删除</el-button
        >
      </el-row>
    </el-header>

    <!-- 添加学生对话框 -->
    <el-dialog title="添加学生" :visible.sync="showAddDialog" width="30%">
      <el-form ref="addForm" :model="addFormData" label-width="100px">
        <el-form-item label="学号">
          <el-input v-model="addFormData.student_id"></el-input>
        </el-form-item>
        <el-form-item label="姓名">
          <el-input v-model="addFormData.stu_name"></el-input>
        </el-form-item>
        <el-form-item label="性别">
          <el-input v-model="addFormData.gender"></el-input>
        </el-form-item>
        <el-form-item label="班级">
          <el-input v-model="addFormData.class_"></el-input>
        </el-form-item>
        <el-form-item label="出生日期">
          <el-input v-model="addFormData.birth_date"></el-input>
        </el-form-item>
        <el-form-item label="学校">
          <el-input v-model="addFormData.school"></el-input>
        </el-form-item>
      </el-form>
      <span slot="footer" class="dialog-footer">
        <el-button @click="showAddDialog = false">取消</el-button>
        <el-button type="primary" @click="addStudent">确定</el-button>
      </span>
    </el-dialog>

    <!-- 数据表格 -->
    <el-table
      v-if="tableData.length > 0"
      :data="tableData"
      style="width: 100%"
      @selection-change="handleSelectionChange"
      row-key="student_id"
    >
      <el-table-column
        v-if="showBatchDelete"
        type="selection"
        width="55"
      ></el-table-column>
      <el-table-column prop="student_id" label="学号" width="140">
      </el-table-column>
      <el-table-column prop="stu_name" label="姓名" width="120">
      </el-table-column>
      <el-table-column prop="gender" label="性别"> </el-table-column>
      <el-table-column prop="class_" label="班级"> </el-table-column>
      <el-table-column prop="birth_date" label="出生日期"> </el-table-column>
      <el-table-column prop="school" label="学校"> </el-table-column>
      <el-table-column label="操作" width="120">
        <template slot-scope="scope">
          <el-button
            type="danger"
            size="mini"
            @click="confirmDelete(scope.row.student_id)"
          >
            删除
          </el-button>
        </template>
      </el-table-column>
    </el-table>

    <!-- 无数据提示 -->
    <el-alert
      v-if="message === 'No data available'"
      title="暂无数据"
      type="info"
    >
    </el-alert>

    <!-- 错误提示 -->
    <el-alert v-if="error" :title="error" type="error" show-icon> </el-alert>

    <!-- 删除确认对话框 -->
    <el-dialog
      title="删除确认"
      :visible.sync="deleteDialogVisible"
      width="30%"
      :before-close="handleClose"
    >
      <span>确定要删除选中的学号吗?</span>
      <span slot="footer" class="dialog-footer">
        <el-button @click="deleteDialogVisible = false">取消</el-button>
        <el-button type="danger" @click="deleteSelected">确定删除</el-button>
      </span>
    </el-dialog>
  </el-main>
</template>

<script>
export default {
  name: "MyStudents",
  data() {
    return {
      tableData: [],
      message: "",
      error: null,
      selectedRows: [],
      showBatchDelete: false, // 控制批量删除按钮的显示
      deleteDialogVisible: false, // 控制删除确认对话框的显示
      currentDeleteId: null, // 当前要删除的学号
      showAddDialog: false, // 控制添加对话框的显示
      addFormData: {
        student_id: "",
        stu_name: "",
        gender: "", // 新增性别字段
        class_: "", // 班级
        birth_date: "", // 新增出生日期字段
        school: "", // 新增学校字段
      },
    };
  },
  created() {
    this.fetchStudentScores();
  },
  methods: {
    async fetchStudentScores() {
      try {
        // 注意:确保这里的URL与后端接口匹配
        const response = await fetch("http://127.0.0.1:8000/WJH/enrollments");
        if (!response.ok) {
          throw new Error("连接到服务器失败");
        }
        const data = await response.json();
        if (data.success) {
          this.tableData = data.data;
          this.message = data.message || "";
          if (this.tableData.length === 0 && !this.error) {
            this.message = "No data available"; // 设置无数据消息
          }
        } else {
          this.error = data.message || "未知错误";
        }
      } catch (error) {
        this.error = error.message || "请求数据时发生错误";
      }
    },
    confirmDelete(studentId) {
      this.currentDeleteId = studentId;
      this.deleteDialogVisible = true;
    },
    deleteSelected() {
      if (this.showBatchDelete) {
        this.sendDeleteRequest(this.selectedRows.map((row) => row.student_id));
      } else {
        this.sendDeleteRequest([this.currentDeleteId]);
      }
      this.deleteDialogVisible = false;
      this.selectedRows = [];
    },
    handleSelectionChange(val) {
      this.selectedRows = val;
    },
    confirmBatchDelete() {
      this.deleteDialogVisible = true;
    },
    // 发送删除请求到后端
    async sendDeleteRequest(studentIds) {
      try {
        const response = await fetch("http://127.0.0.1:8000/WJH/enrollments", {
          // 确保这里的URL与后端接口匹配
          method: "DELETE",
          headers: {
            "Content-Type": "application/json",
          },
          body: JSON.stringify({ student_ids: studentIds }), // 将学号列表作为字典传递
        });
        if (!response.ok) {
          throw new Error("请求失败");
        }
        const data = await response.json();
        if (data.success) {
          this.$message({
            message: "删除成功",
            type: "success",
          });
          // 刷新表格数据
          this.fetchStudentScores();
        } else {
          this.$message.error(data.message || "删除操作失败");
        }
      } catch (error) {
        this.$message.error(error.message || "删除操作异常");
      }
    },
    handleClose(done) {
      this.$confirm("确认关闭?")
        .then(() => {
          done();
        })
        .catch(() => {});
    },

    addStudent() {
      // 调用添加函数,将 addFormData 发送给后端
      this.addStudentData(this.addFormData);
      this.showAddDialog = false; // 关闭对话框
    },
    // 添加学生数据的方法
    async addStudentData(studentData) {
      try {
        // 使用 fetch 发送 POST 请求到后端
        const response = await fetch("http://127.0.0.1:8000/WJH/enrollments", {
          // 确保这里的URL与后端接口匹配
          method: "POST",
          headers: {
            "Content-Type": "application/json",
          },
          body: JSON.stringify(studentData), // 将学生数据转换为 JSON 字符串
        });

        if (!response.ok) {
          throw new Error("添加请求失败");
        }

        const data = await response.json();
        if (data.success) {
          this.$message({
            message: "添加成功",
            type: "success",
          });
          // 刷新表格数据
          this.fetchStudentScores();
        } else {
          // 检查是否有特定的错误信息
          if (data.error === "学号已存在") {
            this.$message.error("学号已存在");
          } else {
            this.$message.error(data.message || "添加操作失败");
          }
        }
      } catch (error) {
        this.$message.error(error.message || "添加操作异常");
      }
    },
  },
};
</script>

<style>
.el-header {
  background-color: #b3c0d1;
  color: #333;
  line-height: 60px;
}

.row-style {
  padding-top: 10px; /* 自定义上边距 */
  padding-bottom: 10px; /* 自定义下边距 */
}

.row-style {
  align-items: center; /* 垂直居中 */
  height: 100%; /* 确保占满整个header的高度 */
}
.el-aside {
  color: #333;
}
</style>
OrdinaryClass.vue
<template>
  <el-main>
    <!-- 顶部Header -->
    <el-header>
      <!-- 使用 el-row 和 el-col 来布局按钮,并添加自定义样式 -->
      <span
        style="
          flex-grow: 1;
          margin-right: 20px;
          text-align: center;
          display: block;
        "
      >
        班级信息
      </span>
    </el-header>
    <!-- 数据表格 -->
    <el-table
      v-if="tableData.length > 0"
      :data="tableData"
      style="width: 100%"
      @selection-change="handleSelectionChange"
      row-key="class_id"
    >
      <el-table-column
        v-if="showBatchDelete"
        type="selection"
        width="55"
      ></el-table-column>
      <el-table-column prop="class_id" label="班级班号" width="140">
      </el-table-column>
      <el-table-column prop="cls_rank" label="班级排名" width="120">
      </el-table-column>
      <el-table-column prop="head_teacher" label="班主任"> </el-table-column>
      <el-table-column prop="student_count" label="学生总数"> </el-table-column>
    </el-table>

    <!-- 无数据提示 -->
    <el-alert
      v-if="message === 'No data available'"
      title="暂无数据"
      type="info"
    >
    </el-alert>

    <!-- 错误提示 -->
    <el-alert v-if="error" :title="error" type="error" show-icon> </el-alert>

    <!-- 删除确认对话框 -->
  </el-main>
</template>

<script>
export default {
  name: "OrdinaryClass",
  data() {
    return {
      tableData: [],
      message: "",
      error: null,
      addFormData: {
        class_id: "",
        cls_rank: "",
        head_teacher: "",
        student_count: 0,
      },
    };
  },
  created() {
    this.fetchClasses();
  },
  methods: {
    async fetchClasses() {
      try {
        // 注意:确保这里的URL与后端接口匹配
        const response = await fetch("http://127.0.0.1:8000/WJH/classes");
        if (!response.ok) {
          throw new Error("连接到服务器失败");
        }
        const data = await response.json();
        if (data.success) {
          this.tableData = data.data;
          this.message = data.message || "";
          if (this.tableData.length === 0 && !this.error) {
            this.message = "No data available"; // 设置无数据消息
          }
        } else {
          this.error = data.message || "未知错误";
        }
      } catch (error) {
        this.error = error.message || "请求数据时发生错误";
      }
    },
    handleClose(done) {
      this.$confirm("确认关闭?")
        .then(() => {
          done();
        })
        .catch(() => {});
    },
  },
};
</script>

<style>
.el-header {
  background-color: #b3c0d1;
  color: #333;
  line-height: 60px;
}

.row-style {
  padding-top: 10px; /* 自定义上边距 */
  padding-bottom: 10px; /* 自定义下边距 */
}

.row-style {
  align-items: center; /* 垂直居中 */
  height: 100%; /* 确保占满整个header的高度 */
}
.el-aside {
  color: #333;
}
</style>
OrdinaryScore.vue
<template>
  <el-main>
    <!-- 顶部Header -->
    <el-header>
      <!-- 使用 el-row 和 el-col 来布局按钮,并添加自定义样式 -->
      <span
        style="
          flex-grow: 1;
          margin-right: 20px;
          text-align: center;
          display: block;
        "
      >
        学生成绩信息
      </span>
    </el-header>

    <!-- 数据表格 -->
    <el-table
      v-if="tableData.length > 0"
      :data="tableData"
      style="width: 100%"
      @selection-change="handleSelectionChange"
      row-key="student_id"
    >
      <el-table-column
        v-if="showBatchDelete"
        type="selection"
        width="55"
      ></el-table-column>
      <el-table-column prop="student_id" label="学号" width="140">
      </el-table-column>
      <el-table-column prop="stu_name" label="姓名" width="120">
      </el-table-column>
      <el-table-column prop="total_score" label="总成绩"> </el-table-column>
      <el-table-column prop="score_rank" label="分数排名"> </el-table-column>
      <el-table-column prop="class_" label="班级"> </el-table-column>
    </el-table>

    <!-- 无数据提示 -->
    <el-alert
      v-if="message === 'No data available'"
      title="暂无数据"
      type="info"
    >
    </el-alert>

    <!-- 错误提示 -->
    <el-alert v-if="error" :title="error" type="error" show-icon> </el-alert>
  </el-main>
</template>

<script>
export default {
  name: "OrdinaryScore",
  data() {
    return {
      tableData: [],
      message: "",
      error: null,
      addFormData: {
        // 用户输入的数据
        student_id: "",
        stu_name: "",
        total_score: "",
        score_rank: "",
        class_: "",
      },
    };
  },
  created() {
    this.fetchStudentScores();
  },
  methods: {
    async fetchStudentScores() {
      try {
        const response = await fetch("http://127.0.0.1:8000/WJH/studentscores");
        if (!response.ok) {
          throw new Error("连接到服务器失败");
        }
        const data = await response.json();
        if (data.success) {
          this.tableData = data.data;
          this.message = data.message || "";
          if (this.tableData.length === 0 && !this.error) {
            this.message = "No data available"; // 设置无数据消息
          }
        } else {
          this.error = data.message || "未知错误";
        }
      } catch (error) {
        this.error = error.message;
      }
    },
  },
};
</script>

<style>
.el-header {
  background-color: #b3c0d1;
  color: #333;
  line-height: 60px;
}

.row-style {
  padding-top: 10px; /* 自定义上边距 */
  padding-bottom: 10px; /* 自定义下边距 */
}

.row-style {
  align-items: center; /* 垂直居中 */
  height: 100%; /* 确保占满整个header的高度 */
}
.el-aside {
  color: #333;
}
</style>
OrdinaryStudent.vue
<template>
  <el-main>
    <!-- 顶部Header -->
    <el-header>
      <span
        style="
          flex-grow: 1;
          margin-right: 20px;
          text-align: center;
          display: block;
        "
      >
        学生学籍信息
      </span>
    </el-header>
    <!-- 数据表格 -->
    <el-table
      v-if="tableData.length > 0"
      :data="tableData"
      style="width: 100%"
      @selection-change="handleSelectionChange"
      row-key="student_id"
    >
      <el-table-column
        v-if="showBatchDelete"
        type="selection"
        width="55"
      ></el-table-column>
      <el-table-column prop="student_id" label="学号" width="140">
      </el-table-column>
      <el-table-column prop="stu_name" label="姓名" width="120">
      </el-table-column>
      <el-table-column prop="gender" label="性别"> </el-table-column>
      <el-table-column prop="class_" label="班级"> </el-table-column>
      <el-table-column prop="birth_date" label="出生日期"> </el-table-column>
      <el-table-column prop="school" label="学校"> </el-table-column>
    </el-table>

    <!-- 无数据提示 -->
    <el-alert
      v-if="message === 'No data available'"
      title="暂无数据"
      type="info"
    >
    </el-alert>

    <!-- 错误提示 -->
    <el-alert v-if="error" :title="error" type="error" show-icon> </el-alert>
  </el-main>
</template>

<script>
export default {
  name: "OrdinaryStudent",
  data() {
    return {
      tableData: [],
      message: "",
      error: null,
      addFormData: {
        student_id: "",
        stu_name: "",
        gender: "", // 新增性别字段
        class_: "", // 班级
        birth_date: "", // 新增出生日期字段
        school: "", // 新增学校字段
      },
    };
  },
  created() {
    this.fetchStudentScores();
  },
  methods: {
    async fetchStudentScores() {
      try {
        // 注意:确保这里的URL与后端接口匹配
        const response = await fetch("http://127.0.0.1:8000/WJH/enrollments");
        if (!response.ok) {
          throw new Error("连接到服务器失败");
        }
        const data = await response.json();
        if (data.success) {
          this.tableData = data.data;
          this.message = data.message || "";
          if (this.tableData.length === 0 && !this.error) {
            this.message = "No data available"; // 设置无数据消息
          }
        } else {
          this.error = data.message || "未知错误";
        }
      } catch (error) {
        this.error = error.message || "请求数据时发生错误";
      }
    },
  },
};
</script>

<style>
.el-header {
  background-color: #b3c0d1;
  color: #333;
  line-height: 60px;
}

.row-style {
  padding-top: 10px; /* 自定义上边距 */
  padding-bottom: 10px; /* 自定义下边距 */
}

.row-style {
  align-items: center; /* 垂直居中 */
  height: 100%; /* 确保占满整个header的高度 */
}
.el-aside {
  color: #333;
}
</style>
UserEnroll.vue
<template>
  <div class="container">
    <div class="header">注册</div>
    <div class="form-wrapper">
      <input
        type="text"
        v-model="username"
        placeholder="Username"
        class="input-item"
        @input="checkInputLength"
      />
      <input
        type="password"
        v-model="password"
        placeholder="Password"
        class="input-item"
        @input="checkInputLength"
      />
      <button type="button" class="btn" @click="register">马上注册</button>
    </div>
  </div>
</template>

<script>
export default {
  name: "UserEnroll",
  data() {
    return {
      username: "",
      password: "",
    };
  },
  methods: {
    async register() {
      if (this.username.length > 16 || this.password.length > 16) {
        alert("用户名和密码均不得超过16个字符");
        return;
      }
      if (!this.username || !this.password) {
        alert("用户名和密码不能为空");
        return;
      }
      try {
        const response = await fetch("http://127.0.0.1:8000/WJH/register", {
          method: "POST",
          headers: {
            "Content-Type": "application/json",
          },
          body: JSON.stringify({
            username: this.username,
            password: this.password,
          }),
        });
        if (!response.ok) {
          throw new Error("连接失败");
        }
        const result = await response.json();
        if (result.success) {
          alert("注册成功");
          // 可以在这里添加跳转到登录页面的逻辑
          this.$router.push("/login");
        } else {
          alert(result.message || "注册失败");
        }
      } catch (error) {
        alert(`注册失败: ${error.message}`);
      }
    },
    checkInputLength(event) {
      const maxLength = 16;
      if (event.target.value.length > maxLength) {
        alert(`输入内容不得超过${maxLength}个字符`);
        event.target.value = event.target.value.substring(0, maxLength);
      }
    },
  },
};
</script>

<style>
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}
html,
body {
  height: 100%;
  font-family: Arial, sans-serif;
}
body {
  background-color: #f7f7f7;
  display: flex;
  justify-content: center;
  align-items: center;
}
.container {
  background-color: white;
  width: 100%;
  max-width: 400px;
  padding: 40px;
  border-radius: 8px;
  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
.header {
  font-size: 24px;
  font-weight: bold;
  text-align: center;
  margin-bottom: 20px;
}
.input-item {
  width: 100%;
  margin-bottom: 15px;
  padding: 12px;
  border: 1px solid #ddd;
  border-radius: 4px;
  font-size: 16px;
}
.input-item::placeholder {
  color: #aaa;
}
.btn {
  display: block;
  width: 100%;
  padding: 15px;
  margin-top: 20px;
  background-color: #5c67f2;
  color: white;
  border: none;
  border-radius: 4px;
  font-size: 18px;
  cursor: pointer;
  transition: background-color 0.3s ease;
}
.btn:hover {
  background-color: #4a54e1;
}
.msg {
  text-align: center;
  margin-top: 15px;
  font-size: 14px;
}
a {
  color: #5c67f2;
  text-decoration: none;
}
</style>

router文件夹

// 该文件专门用于创建整个应用的路由器
import VueRouter from 'vue-router'
//引入组件
import MyLogin from '@/pages/MyLogin.vue'
import UserEnroll from '@/pages/UserEnroll.vue'
import MyHome from '@/pages/MyHome.vue'
import MyIndex from '@/pages/MyIndex.vue'
import MyAdmin from '@/pages/MyAdmin.vue'
import MyOrdinary from '@/pages/MyOrdinary.vue'
import MyClass from '@/pages/MyClass.vue'
import MyStudents from '@/pages/MyStudents.vue'
import MyScore from '@/pages/MyScore.vue'
import OrdinaryClass from '@/pages/OrdinaryClass.vue'
import OrdinaryScore from '@/pages/OrdinaryScore.vue'
import OrdinaryStudent from '@/pages/OrdinaryStudent.vue'

//创建并暴露一个路由器
export default new VueRouter({
	routes:[
		{
            name:'Enroll',
			path:'/enroll',
			component:UserEnroll,
		},
        {
            name:'Login',
			path:'/login',
			component:MyLogin,
		},
        {
            name:'MyHome',
            path:'/MyHome',
            component:MyHome
        },
        {
            name:'MyIndex',
            path:'/MyIndex',
            component:MyIndex
        },
        {
            name:'MyAdmin',
            path:'/MyAdmin',
            component:MyAdmin,
            children: [
                {
                    path: 'MyClass',
                    name: 'MyClass',
                    component: MyClass
                },
                {
                    path: 'MyScore',
                    name: 'MyScore',
                    component: MyScore
                },
                {
                    path: 'MyStudents',
                    name: 'MyStudents',
                    component: MyStudents
                }
            ]

        },
        {
            name:'MyOrdinary',
            path:'/MyOrdinary',
            component:MyOrdinary,
            children: [
                {
                    path: 'OrdinaryClass',
                    name: 'OrdinaryClass',
                    component: OrdinaryClass
                },
                {
                    path: 'OrdinaryScore',
                    name: 'OrdinaryScore',
                    component: OrdinaryScore
                },
                {
                    path: 'OrdinaryStudent',
                    name: 'OrdinaryStudent',
                    component: OrdinaryStudent
                }
            ]
        },
          // 添加默认路由
        { path: '/',
          redirect: '/MyHome' } // 重定向根路径到登录页面
	]
})

App.vue

<template>

  <router-view></router-view>

</template>

<script>


export default {
  name: 'App',
  // components: {
  // }
}
</script>

<style>

</style>

main.js

//引入Vue
import Vue from 'vue'
//引入App
import App from './App.vue'
//引入VueRouter
import VueRouter from 'vue-router'
//引入路由器
import router from './router'



import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
Vue.use(ElementUI);

// // 按需引入
// import { MenuItemGroup, Row, Col, Button, Input, Dialog, Form,FormItem, Table, TableColumn, Alert, Container, Header, Aside, Main, Menu, Submenu, MenuItem, MessageBox } from 'element-ui';

// // 按需引入
// Vue.component(Button.name, Button);
// Vue.component(Input.name, Input);
// Vue.component(Dialog.name, Dialog);
// Vue.component(Form.name, Form);
// Vue.component(FormItem.name, FormItem);
// Vue.component(Table.name, Table);
// Vue.component(TableColumn.name, TableColumn);
// Vue.component(Alert.name, Alert);
// Vue.component(Container.name, Container);
// Vue.component(Header.name, Header);
// Vue.component(Aside.name, Aside);
// Vue.component(Main.name, Main);
// Vue.component(Menu.name, Menu);
// Vue.component(Submenu.name, Submenu);
// Vue.component(MenuItem.name, MenuItem);
// Vue.component(MenuItemGroup.name, MenuItemGroup);

// Vue.component(Row.name, Row);
// Vue.component(Col.name, Col);
// Vue.component(MessageBox.name, MessageBox);


//关闭Vue的生产提示
Vue.config.productionTip = false
//应用插件
Vue.use(VueRouter)

//创建vm
new Vue({
	el:'#app',
	render: h => h(App),
	router:router
})

本项目需安装nodejs,下面是我使用的版本,不用这个版本也行,项目使用了element-ui需要使用npm下载

下面是后端链接:学生信息管理系统(简化版)后端接口-CSDN博客

数据库链接:学生信息管理系统(简化版)数据库部分-CSDN博客

也可在主页找到

;