174
社区成员
发帖
与我相关
我的任务
分享1. Introduction ( Including intention and simple project contents intro and project getting approaches )
1.1. Intention
1.2. Simple project contents ( Including the Requiremnts Analysis )
1.3. How to get the Project
2. PSP Table
3. Codes Specification and Front-end and back-end Separation Demonstration
3.1. Front-end WebPage Connection Demostration ( Including Login Page and Function Page )
3.2. Back-end Datebase Building Demostration
3.3 Datebase Contents Presentation ( Interactive Dates and Blocks )
4. Some small Thoughts on the project
This project aims to develop a web-based book lending and return management system with a front-end and back-end separation architecture. It is designed to assist those who have a need for software front-end and back-end separation, have a basic understanding of the Vue and Spring Boot development frameworks, and want to create a functional web page management system.
| Name | Github Address |
|---|---|
| Front-end of the Project | ZongshengLi3011/Inc: Library Book Management System |
| Back-end of the Project | Inc/front-end at main · ZongshengLi3011/Inc |
| PSP Phase | Task Describe | Estimated Cost Time (minutes) | Actual Cost Time (minutes) |
|---|---|---|---|
| Planning | Requirements Analysis | 30 | 15 |
| Development Analysis | 30 | 10 | |
| Design | Interface Design | 10 | 30 |
| Demo Datebase Design | 15 | 20 | |
| Controller Design | 15 | 30 | |
| Coding | Back-end Coding | 60 | 120 |
| Front-end Coding | 60 | 30 | |
| Test | Debug | 180 | 120 |
| Requirements Achievement | 10 | 10 | |
| Secondary optimization of code | 10 | 10 | |
| Summary | Record processing details and troubles | 10 | 10 |
| Total | 370 | 405 | |
Code Function Overview
Codes mainly builds an encapsulation module for HTTP requests based on the axios library, which is used to handle the interaction with the backend API in a Vue project (inferred from the import paths in the code). It implements the following functions:
axios configuration:
withCredentials to true, which is used to carry cookies during cross - domain requests.Authorization header and the Content - Type header (for POST requests).access - token in local storage, it is added to the Authorization field of the request header, and the loading state of the application (presumably for showing a loading animation or similar functionality) is set to true.false. Processing is based on the code value in the response data:
code is 401, trigger a jump to the /login page via the event bus bus (possibly for the case of not being logged in).code is 402, display an error message for login expiration and trigger a jump to the /login page via the event bus bus.POST, POSTFORM, GET, PUT, DELETE, PATCH. These methods are encapsulated based on the corresponding request methods of axios, uniformly set the base address of the request (base), and return the response data (through .then(res => res.data)).Code Detail Analysis
axios configuration - related:
During cross - domain requests, this allows the browser to include cookies in the request, which is important for scenarios involving authentication that require cookies.
The commented - out axios.defaults.headers.common['Authorization'] = AUTH_TOKEN; and axios.defaults.headers.post['Content - Type'] = 'application/x - www - form - urlencoded;charset=UTF - 8'; parts, if AUTH_TOKEN is defined, were originally used to globally set the authentication information in the request header and the Content - Type of POST requests.
Request interceptor part:
In axios.interceptors.request.use:
Checks if an access - token exists in local storage. If it does, it is added to the request header for backend authentication.
Updates the loading state in the store, which may be associated with a loading indicator on the UI to inform the user that a request is in progress.
Response interceptor part:
In axios.interceptors.response.use:
After the request is completed, regardless of success or failure, the loading state is set to false to hide the loading indicator.
Processing based on the value of response.data.code:
For error codes 401 and 402, operations to jump to the login page are triggered respectively. In the case of 402, an error message is also displayed to the user.
HTTP method encapsulation part:
POST method:
It accepts url and params parameters, uses the post method of axios to send a request. The request address is the concatenation of base (base address) and url, and the request parameter is params. Finally, it returns the response data.
POSTFORM method:
Similar to POST, but an additional request header Content - Type is set to multipart/form - data, which is used for handling form data uploads and other situations that require a special Content - Type.
GET method:
Uses the get method of axios and adds params as query parameters to the url.
PUT, DELETE, PATCH methods: Similar to POST, they use the corresponding PUT, DELETE, PATCH methods of axios respectively to implement the corresponding HTTP request functions.
import axios from 'axios'
import store from "../vuex/store";
import { Message } from 'element-ui';
import {
bus
} from '../bus.js'
axios.defaults.withCredentials = true;
// axios.defaults.headers.common['Authorization'] = AUTH_TOKEN;
// axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=UTF-8';//配置请求头
//添加一个请求拦截器
axios.interceptors.request.use(
config => {
if (window.localStorage.getItem('access-token')) {
config.headers.Authorization = window.localStorage.getItem('access-token');
}
store.state.loading = true
return config
},
error => {
return Promise.reject(error)
}
);
// 添加一个响应拦截器
axios.interceptors.response.use(function (response) {
store.state.loading = false
if (response.data && response.data.code) {
const resCode = parseInt(response.data.code);
if (resCode === 401) {
//未登录
bus.$emit('goto', '/login')
}
if (resCode === '402') {
//登录过期
Message.error({
showClose: true,
message: '登录过期,请重新登录',
duration: 2000
});
bus.$emit('goto', '/login')
}
}
return response;
}, function (error) {
store.state.loading = false
// Do something with response error
return Promise.reject(error);
});
//基地址
let base = process.env.API_ROOT
//通用方法
export const POST = (url, params) => {
return axios.post(`${base}${url}`, params).then(res => res.data)
}
//表单方法
export const POSTFORM = (url, params) => {
const config ={headers:{"Content-Type":"multipart/form-data"}}
return axios.post(`${base}${url}`, params, config).then(res => res.data)
}
export const GET = (url, params) => {
return axios.get(`${base}${url}`, {
params: params
}).then(res => res.data)
}
export const PUT = (url, params) => {
return axios.put(`${base}${url}`, params).then(res => res.data)
}
export const DELETE = (url, params) => {
return axios.delete(`${base}${url}`, {
params: params
}).then(res => res.data)
}
export const PATCH = (url, params) => {
return axios.patch(`${base}${url}`, params).then(res => res.data)
}
import Vue from 'vue'
import Router from 'vue-router'
import Home from '@/view/Home'
import Dashboard from '@/view/Dashboard'
import Index from '@/view/welcome/index.vue'
import BookList from '@/view/role/list'
import UserList from '@/view/user/list'
import UserChangePwd from '@/view/user/changepwd'
import UserProfile from '@/view/user/profile'
import MenuList from '@/view/menu/list'
import Book from '@/view/book/list'
// 懒加载方式,当路由被访问的时候才加载对应组件
const Login = resolve => require(['@/view/Login'], resolve)
Vue.use(Router)
let router = new Router({
mode: 'history',
routes: [
{
path: '/index',
name: '首页',
component: Index
},
{
path: '/login',
name: '登录',
component: Login
},
{
path: '/',
name: 'home',
component: Home,
redirect: '/dashboard',
leaf: true, // 只有一个节点
menuShow: true,
iconCls: 'fa fa-home', // 图标样式class
children: [
{path: '/dashboard', component: Dashboard, name: '首页', menuShow: true}
]
},
{
path: '/',
component: Home,
name: '用户管理',
menuShow: true,
leaf: true, // 只有一个节点
iconCls: 'fa fa-user', // 图标样式class
children: [
{path: '/admin/user', component: UserList, name: '用户列表', menuShow: true}
]
},
{
path: '/',
component: Home,
name: '图书管理',
menuShow: true,
leaf: true, // 只有一个节点
iconCls: 'fa fa-user', // 图标样式class
children: [
{path: '/book', component: Book, name: '图书列表', menuShow: true}
]
},
{
path: '/',
component: Home,
name: '菜单管理',
menuShow: true,
leaf: true, // 只有一个节点
iconCls: 'fa fa-server', // 图标样式class
children: [
{path: '/admin/menu', component: MenuList, name: '菜单列表', menuShow: true}
]
},
{
path: '/',
component: Home,
name: '角色管理',
menuShow: true,
leaf: true,
iconCls: 'fa fa-group',
children: [
{path: '/admin/role', component: BookList, name: '角色管理', menuShow: true},
]
},
{
path: '/',
component: Home,
name: '文件管理',
menuShow: true,
leaf: true,
iconCls: 'fa fa-group',
children: [
{path: '/cms/file', component: FileList, name: '文件管理', menuShow: true},
]
},
{
path: '/',
component: Home,
name: '设置',
menuShow: true,
iconCls: 'iconfont icon-setting1',
children: [
{path: '/user/profile', component: UserProfile, name: '个人信息', menuShow: true},
{path: '/user/changepwd', component: UserChangePwd, name: '修改密码', menuShow: true}
]
},
]
})
router.beforeEach((to, from, next) => {
if (to.path.startsWith('/login')) {
window.localStorage.removeItem('access-token')
//window.localStorage.removeItem('access-user')
next()
} else if (to.path.startsWith('/index')) {
next()
} else {
//let user = JSON.parse(window.localStorage.getItem('access-token'))
let user = window.localStorage.getItem('access-token');
if (!user) {
next({path: '/login'})
} else {
next()
}
}
})
export default router
Post Page: https://www.bilibili.com/video/BV1yF1GYhE1G/?spm_id_from=333.999.0.0
Page Function: https://www.bilibili.com/video/BV1WF1GYhEXn/?spm_id_from=333.999.0.0
Some parts of Controllers
package com.inc.admin.controller.biz;
import com.inc.admin.domain.biz.Book;
import com.inc.admin.service.biz.BookService;
import com.inc.admin.utils.R;
import javax.annotation.Resource;
import javax.validation.constraints.NotNull;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/book")
public class BookController {
@Resource
private BookService bookService;
/**
* 分页查询 图书列表
*/
@PostMapping("/listByPage")
public R listByPage(@RequestBody Book req) {
return R.ok().put("page", bookService.listByPage(req));
}
/**
* 添加 图书信息
*/
@PostMapping("/insert")
public R insert(@RequestBody Book req) {
return R.operate(bookService.insert(req)>0);
}
/**
* 更新 图书信息
*/
@PostMapping("/update")
public R update(@RequestBody Book req) {
return R.operate(bookService.update(req)>0);
}
/**
* 删除 图书信息
*/
@PostMapping("/delete")
public R delete(@Validated @NotNull(message = "编号不能为空") @RequestParam("id") @RequestBody Integer id) {
return R.operate(bookService.delete(id)>0);
}
}
package com.inc.admin.controller.sys;
import com.inc.admin.domain.sys.UserDO;
import com.inc.admin.dto.sys.UserDTO;
import com.inc.admin.dto.sys.req.UpPwdReq;
import com.inc.admin.service.sys.RoleService;
import com.inc.admin.service.sys.UserService;
import com.inc.admin.utils.MD5Utils;
import com.inc.admin.context.FilterContextHandler;
import com.inc.admin.dto.sys.LoginUserDTO;
import com.inc.admin.utils.ObjectCopyUtils;
import com.inc.admin.utils.PageUtils;
import com.inc.admin.utils.Query;
import com.inc.admin.utils.R;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RequestMapping("/user")
@RestController
public class UserController extends BaseController {
@Autowired
UserService userService;
@Autowired
RoleService roleService;
/**
* 登录的当前用户,前台需要验证用户登录的页面可以调用此方法
* @return
*/
@GetMapping("/currentUser")
LoginUserDTO currentUser(){
LoginUserDTO loginUserDTO = new LoginUserDTO();
loginUserDTO.setUserId(FilterContextHandler.getUserID());
loginUserDTO.setUsername(FilterContextHandler.getUsername());
loginUserDTO.setName(FilterContextHandler.getName());
return loginUserDTO;
}
/**
* 根据用户id获取用户
* @param id
* @return
*/
@GetMapping("{id}")
R get(@PathVariable("id") Long id ){
UserDO userDO = userService.get(id);
return R.ok().put("data", ObjectCopyUtils.copyProperties(new UserDTO(), userDO));
}
/**
* 分页查询用户
* @param params
* @return
*/
@GetMapping()
R listByPage(@RequestParam Map<String, Object> params) {
Query query = new Query(params);
List<UserDO> list = userService.list(query);
List<UserDTO> userDTOS = ObjectCopyUtils.copyListProperty(UserDTO.class, list);
int total = userService.count(query);
PageUtils pageUtil = new PageUtils(userDTOS, total);
return R.ok().put("page",pageUtil);
}
/**
* 增加用户
* @param user
* @return
*/
@PostMapping()
R save(@RequestBody UserDO user) {
if (exits(user.getUsername())) {
return R.error("用户名已存在");
}
user.setPassword(MD5Utils.encrypt(user.getUsername(), user.getPassword()));
return R.operate(userService.save(user) > 0);
}
@PostMapping("register")
R register(@RequestBody UserDO user) {
if (exits(user.getUsername())) {
return R.error("用户名已被注册");
}
user.setroleIds(Arrays.asList(56L));
user.setPassword(MD5Utils.encrypt(user.getUsername(), user.getPassword()));
return R.operate(userService.save(user) > 0);
}
@PostMapping("resetPwd")
R resetPwd(@RequestBody UserDO user) {
return userService.resetPwd(user.getUserId());
}
@PostMapping("updatePwd")
R updatePwd(@RequestBody @Validated UpPwdReq req) {
return userService.updatePwd(req);
}
/**
* 修改用户
* @param user
* @return
*/
@PutMapping()
R update(@RequestBody UserDO user) {
return R.operate(userService.update(user) > 0);
}
@PostMapping("profile")
R profile(@RequestBody UserDO user) {
user.setUserId(Long.parseLong(FilterContextHandler.getUserID()));
return R.operate(userService.profile(user)>0);
}
/**
* 删除用户
* @param id
* @return
*/
@DeleteMapping()
R remove( Long id) {
return R.operate (userService.remove(id) > 0);
}
@PostMapping("/batchRemove")
@ResponseBody
R batchRemove(@RequestParam("ids[]") Long[] userIds) {
int r = userService.batchremove(userIds);
if (r > 0) {
return R.ok();
}
return R.error();
}
boolean exits(String userName) {
// 存在,不通过,false
Map<String, Object> params = new HashMap<>();
params.put("username", userName);
return userService.exits(params);
}
}
Backend Post Buildup: https://www.bilibili.com/video/BV1sF1GYhEm3/
Frontend: https://www.bilibili.com/video/BV1WF1GYhEXg/
Backend: https://www.bilibili.com/video/BV1sF1GYhEz7/
The development of front-end and back-end separation projects includes planning requirements, technology selection and architecture design. The front - end builds the environment, develops page components and state management, and conducts tests. The back - end builds the environment, designs models and databases, develops interfaces, implements business logic, and conducts tests. Then, front - end and back - end integration and joint debugging are carried out, and optimization and adjustment are made through configuring cross - domain access and joint debugging of interfaces. For a person who has no foundation in software engineering projects at all, learning this entire project is really like opening the door to a new world.