基层教学组织评估系统2_登录、申报表信息填报、申报表详情页、审核状态的修改功能初步完成及遇到的问题解决

登录、申报表信息填报、申报表详情页、审核状态的修改功能初步完成

一、需求

  • 登录:用户的登录、信息拉取、退出。
  • 申报表填报:基层负责人负责申报表信息的填写,初期未加入权限控制的话可以直接登录用户填报
  • 申报表详情页:可以看到填报的申报表列表,点击可进入申报表详情页
  • 审核状态的修改:在申报表详情页尾可以进行审核状态的改变

二、分析实现

2.1登录

登录大致逻辑见往期博客:基层教学组织评估系统1_SpringBoot、Shiro、前后端权限和登录初探

初期可以使用username + password登录、后期使用userNumber进一步完善

  1. 对应文档和实体

在这里插入图片描述

  1. 对应UserController
package henu.soft.xiaosi.controller;

import henu.soft.xiaosi.pojo.user.User;
import henu.soft.xiaosi.service.UserService;
import henu.soft.xiaosi.utils.JwtUtil;
import henu.soft.xiaosi.vo.ResultResponse;
import henu.soft.xiaosi.vo.VoUser;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authz.annotation.RequiresAuthentication;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@RestController
@RequestMapping("/user")
public class UserController {
    
    

    @Autowired
    UserService userService;

    /***
     * 1. 用户登录
     * @param voUser
     * @param
     * @param httpServletResponse
     * @return
     */

    @PostMapping("/login")
    public ResultResponse login(@RequestBody VoUser voUser, HttpServletResponse httpServletResponse){
    
    

        String currentUsername = voUser.getUsername();
        String currentUserPassword = voUser.getPassword();
        System.out.println("debug=>"+ currentUsername + currentUserPassword);

        // 用户不存在
        User findUser = userService.findUserByUsername(currentUsername);
        System.out.println(findUser);
        if(findUser == null){
    
    
            return ResultResponse.fail(401,"用户不存在!",null);
        }
        // 密码不正确
        if(!findUser.getPassword().equals(currentUserPassword)){
    
    
            return ResultResponse.fail(401,"用户密码错误!",null);
        }
        // 生成token,返回前端
        String token = JwtUtil.createToken(currentUsername);
        httpServletResponse.setHeader("Access-Control-Expose-Headers","token");
        httpServletResponse.setHeader("token",token);

        voUser.setToken(token);
        return ResultResponse.success(200,"正在登录...!",voUser);


    }

    /***
     * 2. 获取用户信息
     * @param httpServletRequest
     * @return
     */

    @GetMapping("/info")
    public ResultResponse userInfo(HttpServletRequest httpServletRequest){
    
    
        String token = httpServletRequest.getHeader("token");
        if(token == null) {
    
    
            return ResultResponse.success(200,"请重新登录!",null);
        }
        String username = JwtUtil.getUserNumber(token);

        User currentUser = userService.findUserByUsername(username);
        return ResultResponse.success(200,"获取用户信息成功!",currentUser);


    }

    /**
     * 退出功能
     * @return
     */

    @RequiresAuthentication
    @PostMapping("/logout")
    public  ResultResponse logout(){
    
    
        SecurityUtils.getSubject().logout();
        return ResultResponse.success(200,"退出成功!",null);
    }

}

  1. 对应UserService
package henu.soft.xiaosi.service;

import henu.soft.xiaosi.pojo.user.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Service;

@Service
public class UserService {
    
    

    @Autowired
    MongoTemplate mongoTemplate;



    /**
     * 1. 根据学工号查询用户
     */
    public User findUserByUserNumber(String currentUserNumber) {
    
    
        Query query=new Query(Criteria.where("usernumber").is(currentUserNumber));
        User currentUser = mongoTemplate.findOne(query, User.class);
        return currentUser;

    }

    /**
     * 2. 根据用户名查询用户
     */
    public User findUserByUsername(String currentUsername){
    
    
        Query query=new Query(Criteria.where("username").is(currentUsername));
        User currentUser = mongoTemplate.findOne(query, User.class);
        return currentUser;


    }




}

2.1实现过程中遇到的问题及解决
  1. 前端封装axios的request配置的response拦截器和后端Response的成功状态码不一致导致登录页面不跳转

    解决:将request.js中的请求成功的状态码改成200和后端返回成功状态吗一致

import axios from 'axios'
import {
    
     MessageBox, Message } from 'element-ui'
import store from '@/store'
import {
    
     getToken } from '@/utils/auth'

// create an axios instance
const service = axios.create({
    
    
  baseURL: process.env.VUE_APP_BASE_API, // url = base url + request url
  // withCredentials: true, // send cookies when cross-domain requests
  timeout: 5000 // request timeout
})

// request interceptor
service.interceptors.request.use(
  config => {
    
    
    // do something before request is sent

    if (store.getters.token) {
    
    
      // let each request carry token
      // ['X-Token'] is a custom headers key
      // please modify it according to the actual situation
      //config.headers['X-Token'] = getToken()
      config.headers['token'] = getToken()
    }
    return config
  },
  error => {
    
    
    // do something with request error
    console.log(error) // for debug
    return Promise.reject(error)
  }
)

// response interceptor
service.interceptors.response.use(
  /**
   * If you want to get http information such as headers or status
   * Please return  response => response
  */

  /**
   * Determine the request status by custom code
   * Here is just an example
   * You can also judge the status by HTTP Status Code
   */
  response => {
    
    
    const res = response.data

    //if the custom code is not 20000, it is judged as an error.
    if (res.code !== 200) {
    
    
      Message({
    
    
        message: res.message || 'Error',
        type: 'error',
        duration: 5 * 1000
      })

      // 50008: Illegal token; 50012: Other clients logged in; 50014: Token expired;
      if (res.code === 50008 || res.code === 50012 || res.code === 50014) {
    
    
        // to re-login
        MessageBox.confirm('You have been logged out, you can cancel to stay on this page, or log in again', 'Confirm logout', {
    
    
          confirmButtonText: 'Re-Login',
          cancelButtonText: 'Cancel',
          type: 'warning'
        }).then(() => {
    
    
          store.dispatch('user/resetToken').then(() => {
    
    
            location.reload()
          })
        })
      }
      return Promise.reject(new Error(res.message || 'Error'))
    }else {
    
    
      return res
    }
    return response
  },
  error => {
    
    
    console.log('err' + error) // for debug
    Message({
    
    
      message: error.message,
      type: 'error',
      duration: 5 * 1000
    })
    return Promise.reject(error)
  }
)

export default service

2.2 申报表填报

因为申报表的信息量偏大,原始的MySQL也被换成了MongoDB

为了提升用户体验,节省表单提交的时间,将整个申报表表单拆成了大约13个子组件,对应着14个数据库collection

在这里插入图片描述

使用declaration_form来存储另外13个分collection的id将整个申报表信息关联起来

在这里插入图片描述

后端具体源码见:https://github.com/GitHubSi/base_education_system/tree/main/src/main/java/henu/soft/xiaosi

2.2实现过程中遇到的问题及解决
  1. 前后端数据格式不匹配报错

    报错1:err:JSON parse error: Cannot construct instance of ``ava.util.ArrayList (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value (''); nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of ``java.util.ArrayList (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('') at [Source: (PushbackInputStream); line: 1, column: 324] (through reference chain: henu.soft.xiaosi.pojo.declarationform.form1_principal.Principal["teachingCoursesInRecentTwoAcademicYears"])

    报错2:err:JSON parse error: Cannot deserialize instance of `henu.soft.xiaosi.pojo.declarationform.form2_teachers.Teachers` out of START_ARRAY token; nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of `henu.soft.xiaosi.pojo.declarationform.form2_teachers.Teachers` out of START_ARRAY token at [Source: (PushbackInputStream); line: 1, column: 1]

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

在这里插入图片描述

在这里插入图片描述

报错的原因是,前后端为不同人开发,后端实体包括数据库数据类型为 对象数组、对象

而前段传递到后端的数据是字符串,导致报错

解决办法是,修改前、后端的数据格式类型使对应起来

2.3申报表详情页

申报表详情页 需要使用另外一个collection来存储所有填写申报表及审核状态的信息

在这里插入图片描述

当点击某个申报表的时候,会携带该申报表的formID来发起请求,先请求declaration_form表,获得各个分表ID,

通过父、子组件传值的方式传递给各个分表对应的子组件,

然后子组件带着xxxID中发起异步请求,获得各个分表的信息,渲染到前端页面展示

<template>
  <!-- eslint-disable -->
  <div class="app-container">
    <el-card>
      <div slot="header" class="card-header">
        <span>申报表详情</span>
      </div>

      <principal header="负责人概况" :principalID="this.form.principalID" />
      <member header="成员概况" :membersID="this.form.membersID" />
      <rules
        header="规章制度"
        :ruleAndRegulationID="this.form.ruleAndRegulationID"
      />
      <teambuilding
        header="队伍建设"
        :teamBuildingID="this.form.teamBuildingID"
      />
      <organization
        header="教学组织"
        :teachingOrganizationID="this.form.teachingOrganizationID"
      />
      <coursematerial
        header="课程教材概述"
        :courseMaterialID="this.form.courseMaterialID"
      />
      <teachingresearch
        header="教学研究"
        :teachingResearchID="this.form.teachingResearchID"
      />
      <specialtyconstruction
        header="专业建设"
        :specialtyConstructionID="this.form.specialtyConstructionID"
      />
      <practicalteaching
        header="实践教学"
        :practicalTeachingID="this.form.practicalTeachingID"
      />
      <conditionguarantee
        header="条件保障"
        :conditionGuaranteeID="this.form.conditionGuaranteeID"
      />
      <talentcultivationability
        header="人才培养能力"
        :talentCultivationAbilityID="this.form.talentCultivationAbilityID"
      />
      <futureconstructionplan
        header="今后计划建设"
        :futureConstructionPlanID="this.form.futureConstructionPlanID"
      />
      <opinionfeedback
        header="教务处意见反馈"
        :opinionFeedbackID="this.form.opinionFeedbackID"
        v-on:getStatus="getStatus($event)"
      />
    </el-card>
  </div>
</template>

<script>
// import { getForm } from '../../api/form'
import principal from "./components/principal/index";
import member from "./components/member/index";
import rules from "./components/rules-troops/index";
import organization from "./components/organization/index";
import teambuilding from "./components/team-building/index";
import coursematerial from "./components/course-material/index";
import teachingresearch from "./components/teaching-research/index";
import specialtyconstruction from "./components/specialty-construction/index";
import practicalteaching from "./components/practical-teaching/index";
import conditionguarantee from "./components/condition-guarantee/index";
import talentcultivationability from "./components/talent-cultivation-ability/index";
import futureconstructionplan from "./components/future-construction-plan/index";
import opinionfeedback from "./components/opinion-feedback/index";

import {
     
      getForm } from "@/api/detail-page/index";
import {
     
      updateInfo } from "@/api/detail-page/index";

export default {
     
     
  components: {
     
     
    principal,
    member,
    rules,
    organization,
    teambuilding,
    teachingresearch,
    practicalteaching,
    conditionguarantee,
    talentcultivationability,
    futureconstructionplan,
    opinionfeedback,
    coursematerial,
    specialtyconstruction,
  },
  data() {
     
     
    return {
     
     
      form: {
     
     
        info: {
     
     
          organization: "",
          college: "",
          principal: "",
          professionalTitle: "",
          status: "等待审核",
          reviewer: "",
        },
        principalID: "",
        membersID: "",
        ruleAndRegulationID: "",
        teamBuildingID: "",
        teachingOrganizationID: "",
        courseMaterialID: "",
        teachingResearchID: "",
        specialtyConstructionID: "",
        practicalTeachingID: "",
        conditionGuaranteeID: "",
        talentCultivationAbilityID: "",
        futureConstructionPlanID: "",
        opinionFeedbackID: "",
      },
      formID: "",
    };
  },
  created() {
     
     
    this.getFormID();
    this.getInfo();
  },
  methods: {
     
     
    // 1. 获取当前申报表的formID
    getFormID() {
     
     
      this.formID = this.$route.query.formID;
      console.log(this.formID);
    },
    // 2. 数据各表ID渲染
    getInfo() {
     
     
      getForm(this.formID)
        .then((result) => {
     
     
          console.log(result.data);
          const data = result.data;
          for (const item in data) {
     
     
            if (typeof data[item] === "function") {
     
     
              continue;
            }
            this.form[item] = data[item];
          }
        })
        .catch((err) => {
     
     
          console.log(err);
        });
    },

    // 3. 更新教务处审核状态和信息,由子组件调用
    updateForm() {
     
     
      updateInfo(this.formID, this.form)
        .then(() => {
     
     })
        .catch((err) => {
     
     
          console.log(err);
        });
    },
    getStatus(val) {
     
     
      console.log(val);
      this.form.info.status = val;
    },
  },
};
</script>

<style lang="scss" scoped>
.el-card {
     
     
  margin-bottom: 10px;
}
.card-header span {
     
     
  padding: 3px 0;
  border-bottom: 3px solid #409eff;
}
.form-header {
     
     
  margin-bottom: 20px;
  margin-top: 10px;
  span {
     
     
    padding: 3px 0;
    border-bottom: 3px solid #409eff;
  }
}

.cell-content span {
     
     
  padding: 1px 10px;
  border-bottom: 1px solid #000000;
}
.section-content {
     
     
  margin-top: -20px;
  p {
     
     
    min-height: 110px;
    padding: 10px;
    border-radius: 5px;
    border: 1px solid rgba(0, 0, 0, 0.4);
  }
}
</style>

2.3实现过程中遇到的问题及解决
  1. vue组件拆分_父组件传递值给子组件undefined,需要watch监听:Vue解决报错4_父组件传递给子组件值,子组件使用插值表达式可以渲染,放在created函数中提示undefined

猜你喜欢

转载自blog.csdn.net/qq_24654501/article/details/114323443