基于Node.js+Vue的学生会网站的设计与实现

发布于:2022-11-28 ⋅ 阅读:(285) ⋅ 点赞:(0)

目 录
摘 要 I
Abstract II
1绪论 1
1.1 课题的研究背景及意义 1
1.2 国内外研究现状 1
1.2.1国内研究现状 1
1.2.2国外研究现状 2
1.3 研究内容及结构安排 2
1.3.1 研究内容 2
1.3.2 结构安排 3
2相关技术浅析 4
2.1 Node.js语言概述 4
2.2 Express框架概述 4
2.3 Vue.js介绍 4
2.4 Mysql介绍 5
3网站需求分析 6
3.1可行性分析 6
3.2需求描述 6
4网站总体设计 8
4.1 网站架构 8
4.2 网站的总体设计 8
4.3 角色权限设计 9
4.4 数据库设计 11
5网站详细设计 14
5.1 功能模块设计 14
5.1.1 用户注册功能的设计与实现 14
5.1.2 登录功能的设计与实现 16
5.1.3 我的信息模块设计 19
5.1.4 路由守卫的配置 20
5.1.5 学生会数据模块的设计 21
5.2业务主流程设计 28
6网站测试 37
6.1 网站功能测试 37
6.1.1 注册模块测试 37
6.1.2 用户权限模块测试 37
6.1.3 评论回复模块测试 38
6.1.4 用户管理模块测试 38
6.1.5 文章管理模块测试 38
6.2 网站性能测试 39
结 论 40
致 谢 42
参考文献 43
1.3 研究内容及结构安排
1.3.1 研究内容
本课题是基于Node语言的Express框架作为后台进行设计并开发的一个学生会管理网站,前端使用的是 Element ui + Vue.js 设计并搭建界面[3];
(1)前端UI:
作为一个学生会网站,前台界面起到了引导网站管理员进行操作的作用,前台设计的简洁、易上手十分重要。
(2)后台:
后台为网站的核心,提供了数据的操作以及逻辑的实现、业务的处理等功能,因此后台的设计也尤为重要。
(3)数据库设计:
数据库作位数据存储单元,进行存储数据,而数据是网站的基础,且数据库设计的好坏关乎着网站的运行,所以,数据库的设计在网站的开发中也是不可或缺的。
1.3.2 结构安排
在文章中,分为了六大部分对网站的主要内容进行论述说明。
(1)绪论部分:该章节将对网站的背景以及网站的研究现状进行整体性的分析论述,对该项目在国内外的环境进行分析说明。
(2)相关技术浅析:对该网站所使用的编程语言、数据库以及前后台框架进行简要的介绍说明。
(3)网站需求分析:该章节对项目的可行性以及用途进行了分析概括,总结了用户需求,只有当用户需求明确时,开发才会变得更有效率。
(4)网站的总体设计:对网站所使用的架构进行简要概述,对网站所具有的功能一一进行分析概括。
(5)网站的详细设计:结合主要代码讲述功能实现逻辑,对项目开发中各个技术点进行功能分析。
(6)网站测试:对网站的功能及性能做出总结性的概括。
(7)对自己在网站开发中遇到的困难来进行总结性讲述,对网站的优点以及存在的不足进行总结。
4.2 网站的总体设计
该网站是为大学生以及学生会管理人员提供的一个学生会网站,网站主要分前台用户操作网页和后台管理员管理两个大模块。
(1)提供用户的登录注册
(2)用户的实名认证(进行实名认证并通过才能发布二手信息等,通过公司认证才可以发布招聘信息)
(3)管理员的账号添加,及权限分配
(4)问答交流、二手信息、校内活动、招聘信息、新闻文章相关管理、网站信息的发布与浏览。
(5)校内活动的参加,以及活动公告的发布,参加人员信息列表
(6)网站系统通知功能,提供评论、参加及活动公告等相关操作的系统通知。
(7)网站的监管和审核,提供用户认证信息(学生认证/公司认证)的审核,网站用户发布所有信息的审核,
(8)评论回复内容的审核
(9)网站用户反馈,账号申诉,账号举报
(10)评论回复功能
(11)用户权限控制,管理员权限控制
(12)二手信息‘有意向’(购物车)
(13)手机端(自适应)网站信息浏览
(14)站内信息搜索功能,
网站结构如图4.2所示:
在这里插入图片描述

图4.2 基于Node的学生会网站总体结构图

<template>
  <div class="index">
    <!-- Start of Header -->
    <div class="header-wrapper">
      <header>
        <div class="container">
          <div class="logo-container">
            <!-- Website Logo -->
            <span style="font-size: 35px;color: white;">ooo</span>
            <span class="tag-line">学生会网站</span>
          </div>
          <!-- Start of Main Navigation -->
          <nav class="main-nav">
            <div class="menu-top-menu-container">
              <ul id="menu-top-menu" class="clearfix">
                <router-link to="/" tag="li" exact-active-class="current-menu-item">
                  <a>首页</a>
                </router-link>
                <router-link to="/help" tag="li" exact-active-class="current-menu-item">
                  <a>问答</a>
                </router-link>
                <router-link to="/activity" tag="li" exact-active-class="current-menu-item">
                  <a>活动</a>
                </router-link>
                <router-link to="/job" tag="li" exact-active-class="current-menu-item">
                  <a>招聘信息</a>
                </router-link>
                <router-link to="/oldstuff" tag="li" exact-active-class="current-menu-item">
                  <a>二手信息</a>
                </router-link>
                <router-link to="/news" tag="li" exact-active-class="current-menu-item">
                  <a>文章/新闻</a>
                </router-link>

                <li v-if="avatar==''">
                  <a @click="closein">登录/注册</a>
                </li>
                <el-dropdown v-else>
                  <a style="color: #c1cad1;">
                    <img
                      v-if="unread==0"
                      style="     height: 20px; "
                      :src="avatar"
                      class="avatar touxiang avatar-60 photo"
                      height="20"
                      width="20"
                    />
                    <el-badge v-else :value="unread" class="item">
                      <img
                        style="     height: 20px; "
                        :src="avatar"
                        class="avatar touxiang avatar-60 photo"
                        height="20"
                        width="20"
                      />
                    </el-badge>
                    {{nickname}}
                  </a>
                  <el-dropdown-menu slot="dropdown">
                    <el-dropdown-item>
                      <router-link to="/admin" tag="a" exact-active-class="current-menu-item">个人中心</router-link>
                    </el-dropdown-item>

                    <el-dropdown-item>
                      <router-link
                        to="/admin/notice"
                        tag="li"
                        exact-active-class="current-menu-item"
                      >
                        <a v-if="unread==0">消息中心</a>
                        <el-badge v-else :value="unread" class="item">
                          <a>消息中心</a>
                        </el-badge>
                      </router-link>
                    </el-dropdown-item>
                    <el-dropdown-item>
                      <a @click="logout">退出登录</a>
                    </el-dropdown-item>
                  </el-dropdown-menu>
                </el-dropdown>
                <!-- <router-link to="/admin/notice" tag="li" exact-active-class="current-menu-item">
                  <el-button v-if="unread==0" size="mini" type="info" icon="el-icon-bell" circle></el-button>
                  <el-badge v-else :value="unread" class="item">
                     <img
                      style="     height: 20px; "
                      :src="avatar"
                      class="avatar touxiang avatar-60 photo"
                      height="20"
                      width="20"
                    />
                  </el-badge>
                </router-link>-->
              </ul>
            </div>
            <select
              v-model="selected"
              @change="changeHref(parseInt(selected))"
              class="responsive-nav"
            >
              <option value="1">首页</option>
              <option value="2">问答</option>
              <option value="3">活动</option>
              <option value="4">招聘信息</option>
              <option value="5">二手信息</option>
              <option value="6">登录/注册</option>
            </select>
          </nav>
          <!-- End of Main Navigation -->
        </div>
      </header>
    </div>
    <!-- End of Header -->
    <!-- Start of Search Wrapper -->
    <div class="search-area-wrapper">
      <div class="search-area container">
        <h3 class="search-header">OOOOOOOOOOOO</h3>
        <!-- <button class="header-btn">发布信息</button> -->
        <p class="search-tag-line" style="margin-top:50px">
          Information sharing and communication platform of OOOOOOOOOOOO University
          , Makes information transfer easier
        </p>

        <form class="search-form clearfix" @submit.prevent="onSubmit">
          <input
            class="search-term required"
            type="text"
            v-model="search"
            placeholder="Type your search terms here"
          />
          <input class="search-btn" type="submit" @click="searchbtn" value="搜索" />
          <div id="search-error-container"></div>
        </form>
      </div>
    </div>
    <!-- End of Search Wrapper -->
    <router-view />
    <!-- start of foot -->
    <foot />
    <!-- end of foot -->
    <!-- 弹窗组件 -->

    <div class="login" v-if="isclose">
      <div id="mask"></div>
      <div id="loginBox">
        <h2>{{islogin?"网站登录":"新用户注册"}}</h2>
        <div class="user">
          账 号:
          <input type="text" v-model="username" name="username" class="text" />
        </div>
        <div class="pass">
          密 码:
          <input type="password" v-model="password" name="password" class="text" />
        </div>
        <div class="pass" v-if="!islogin">
          确 认:
          <input type="password" v-model="password1" name="password" class="text" />
        </div>
        <div class="button" v-if="islogin">
          <input type="button" @click="login" value="登录" class="submit" />
        </div>
        <div class="button" v-else>
          <input type="button" value="注册" @click="   registered " class="submit" />
        </div>
        <div class="other" @click="join">{{islogin?"注册新用户":"快去登录"}}</div>
        <a class="iconfont" @click="close">&#xe608;</a>
      </div>
    </div>
  </div>
</template>

<script>
import { mapState, mapActions } from "vuex";
import foot from "@/components/foot.vue";
import moment from "moment";

export default {
  name: "index",
  components: {
    foot
  },
  data() {
    return {
      selected: 1,
      password: "",
      password1: "",
      username: "",
      hover: false,
      search: ""
    };
  },
  computed: {
    ...mapState({
      isclose: state => state.user.isclose,
      islogin: state => state.user.islogin,
      avatar: state => state.user.userinfo.avatar,
      nickname: state => state.user.userinfo.nickname,
      unread: state => state.user.unread
    })
  },
  methods: {
    ...mapActions("user", [
      "setUserInfo",
      "changeislog",
      "setToken",
      "join",
      "close",
      "setunread",
      "deleteuserinfo"
    ]),
    //个人hover弹窗
    overShow() {
      this.hover = !this.hover;
    },
    changeHref(sortnum) {
      switch (sortnum) {
        case 1:
          this.$router.push({ path: "/" });
          break;
        case 2:
          this.$router.push({ path: "/help" });
          break;
        case 3:
          this.$router.push({ path: "/activity" });
          break;
        case 4:
          this.$router.push({ path: "/job" });
          break;
        case 5:
          this.$router.push({ path: "/oldstuff" });
          break;
        case 6:
          this.close();
          break;
      }
    },
    //退出登录
    logout() {
      this.deleteuserinfo();
      this.$message.success("退出成功");
    },
    joinin() {
      this.join();
    },
    closein() {
      this.close();
    },
    onSubmit() {
      return false;
    },
    searchbtn() {
      if (this.search == "") {
        this.$message.error("关键字不能为空");
        return;
      }
      this.$router.push({ path: "/search", query: { search: this.search } });
    },
    registered() {
      const userReg = /^[1-9a-zA-Z]{1}[0-9a-zA-Z]{5,9}$/; //6-10位字母数字
      const pwdReg = /^[a-zA-Z]\w{5,17}$/; //6-18位字母数字下划线 字母开头
      if (!userReg.test(this.username)) {
        this.$message.error("账号为6-10位字母数字字母");
        return;
      }
      if (!pwdReg.test(this.password)) {
        this.$message.error("密码为6-18位字母数字或下划线 字母开头");
        return;
      }
      if (this.password !== this.password1) {
        this.$message.error("两次密码不相等");
        return;
      }
      let obj = {
        password: this.password,
        username: this.username
      };
      this.$axios({
        url: "/webadmin/registered",
        method: "POST",
        //  headers:{
        //    'Content-Type':'application/x-www-form-urlencoded',
        //    'Authorization': 'Bearer ' + window.localStorage.getItem('luffy_jwt_token')
        //    },
        data: this.qs.stringify(obj)
      })
        .then(res => {
          let data = res.data;
          if (data.state.type !== "SUCCESS") {
            if (data.state.type == "ERROR_PARAMS_EXIST") {
              this.$message.error("用户名重复");
            } else {
              this.$message.error("注册失败");
            }
            return;
          }
          this.$message.success("注册成功请登录");
          this.join();
        })
        .catch(e => {
          this.$message.error(e);
        });
    },
    //登录
    login() {
      if (this.password == "" || this.password == "") {
        this.$message.error("账号或者密码为空");
        return;
      }
      // this.pwdhash = crypto
      //   .createHash("sha1")
      //   .update(this.password)
      //   .digest("hex");
      // let this_ = this;
      let obj = {
        password: this.password,
        username: this.username,
        type: ""
      };
      this.$axios({
        url: "/webadmin/login",
        method: "POST",
        data: this.qs.stringify(obj)
      })
        .then(res => {
          let data = res.data.data;
          if (res.data.state.type === "SUCCESS") {
            this.$message.success("登录成功");
            // this.$store.user.dispatch("setUserInfo", data)
            this.setUserInfo(data.userinfo);
            this.setToken(data.token);
            this.changeislog();
            console.log(data);
            this.close();
          }
          if (res.data.state === "s") {
            let time = moment(res.data.data).format("YYYY-MM-DD HH:mm");
            this.$message.error(`您的账号由于不良行为被限制登录至${time}`);
          }
          if (res.data.state.type === "ERROE")
            this.$message.error("用户名或密码错误");
        })
        .catch(e => {
          this.$message(e);
        });
    },
    async getnocitenmu() {     
          const res = await this.$axios.post(
            "/web/getnotice",
            this.qs.stringify({ num: 1 })
          );
          console.log(res.data);
          this.setunread(res.data.data.count);
    }
  },
  created() {
     localStorage.luffy_jwt_token&&this.getnocitenmu();
  }
};
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.header-btn {
  background-color: #2c696d;
  font-size: 14px;
  line-height: 19px;
  font-weight: 600;
  padding: 14px 30px 15px;
  color: #fff;
  display: table;
  margin: 0 auto;
}
#mask {
  position: fixed;
  z-index: 999;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: #000;
  filter: alpha(Opacity=30);
  opacity: 0.2;
  margin: 0;
  /* display: none;  */
}
#loginBox {
  position: fixed;
  left: 50%; /* 定位父级的50% */
  top: 50%;
  transform: translate(-50%, -50%); /*自己的50% */
  z-index: 1000;
  width: 380px;
  height: 330px;
  border: 1px solid #ccc;
  background-color: #fff;
  /* display: none;  */
}
#loginBox h2 {
  height: 40px;
  text-align: center;
  line-height: 40px;
  font-size: 14px;
  letter-spacing: 1px;
  color: #666;
  /* background: url(../images/login_header.png) repeat-x; */
  margin: 0;
  padding: 0;
  border-bottom: 1px solid #ccc;
  margin: 0 0 20px 0;
}
#loginBox h2 img {
  display: block;
  float: right;
  position: relative;
  top: 10px;
  right: 10px;
  cursor: pointer;
}
#loginBox .user,
#loginBox .pass {
  font-size: 14px;
  color: #666;
  padding: 5px 0;
  text-align: center;
}
#loginBox input.text {
  width: 200px;
  height: 25px;
  font-size: 14px;
  border: 1px solid #ccc;
  background-color: #fff;
}
#loginBox .button {
  text-align: center;
  padding: 10px 0;
}
#loginBox input.submit {
  width: 107px;
  height: 30px;
  background-color: rgb(179, 146, 233);
  border: none;
  cursor: pointer;
}
#loginBox .other {
  text-align: right;
  padding: 15px 10px;
  font-size: 14px;
  color: #666;

  cursor: pointer;
}
.iconfont {
  font-size: 20px;
  color: #000;
  position: absolute;
  right: 10px;
  top: 10px;
  cursor: pointer;
}
</style>


在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

本文含有隐藏内容,请 开通VIP 后查看

网站公告

今日签到

点亮在社区的每一天
去签到