- 1 后端项目创建
- 2. 前端环境搭建
- 3. 用户登陆模块创建
- 4. 用户首页布局
- 5 用户模块实现
- 5.8 用户状态修改
- 5.9 用户搜索按钮实现
- 6. 商品分类功能实现
- 7 商品分类参数列表实现
- 8.商品模块实现
1 后端项目创建
1.1 结构如图所示
1.2 项目demo测试
2. 前端环境搭建
2.1 VUE项目运行测试
2.1.1 运行项目
2.1.2 启动APP展现项目
2.1.3 关闭编码格式校验
2.1.4 删除默认样式
2.1.5 删除默认路由
2.1.6 删除默认Hello组件
3. 用户登陆模块创建
3.1 登陆准备
3.1.1 创建文件
3.1.2 编辑路由文件
1).编辑路由文件
2).指定路由占位符
3).路由测试
3.1.3 设定默认路径跳转
当用户访问 根目录时,要求重定向到登陆组件
3.1.4 导入静态资源文件
将课前资料中的文件导入项目中
3.1.5 用户访问测试
3.2 用户数据验证
1).定义表单校验规则
2).定义校验规则JS
3).效果展现
3.3 表单数据重置
3.3.1 业务需求说明
当点击重置表单按钮时 应该清空数据
3.3.2 实现重置
3.4 登录预校验
3.4.1 elementUI API
当用户输入数据之后,需要对数据进行校验.当数据有效时 才能提交数据.
3.4.2 编辑登录按钮的JS
3.5 导入Axios组件
3.5.1 导入axios组件
说明:在main.js中添加如下代码
/* 导入axios包 */
import axios from 'axios'
/* 设定axios的请求根目录 */
axios.defaults.baseURL = 'http://localhost:8091/'
/* 向vue对象中添加全局对象 以后发送ajax请求使用$http对象 */
Vue.prototype.$http = axios
3.6 用户登录
3.6.1 发送Ajax请求
3.6.2 编辑UserController
3.6.3 编辑UserService
3.6.4 返回值类型
{"status":200,"msg":"服务器调用成功!","data":"1e893a97634847b3a8b499b173bea620"}
3.7 保存用户信息
3.7.1 业务说明
说明: 当用户登陆成功之后,将token保存到客户端中的sessionStorage中. 因为用户的其他操作都必须保证用户已经登录之后才能操作. 所以通过SessionStorage保存用户的token记录 表示已经登陆.
3.7.2 编辑页面JS
3.7.3 浏览器效果展现
3.8 跳转Home页面
3.8.1 编辑路由
3.8.2 页面效果展现
3.9 路由导航守卫
3.9.1 业务说明
当用户没有登录时,不可以访问其他页面 如果sessionStorage中没有token数据,则表示没有登录 用该拦截,跳转到登录页面.
3.9.2路由守卫语法
编辑router的index.js
//设置路由导航守卫
//关于参数说明:
//1. to 将要访问的路径
//2. from 从哪个路径跳转来的
//3. next 是一个函数 next() 表示放行 next("/login") 强制跳转
router.beforeEach((to,from,next) => {
//1.当用户访问登录页面,则直接放行 如果不需要执行后续操作 执行return
if(to.path === '/login') return next()
//2.当用户访问其他页面 需要校验是否有token
let token = window.sessionStorage.getItem('token')
//如果数据为null 则访问登录页面
if(!token) return next('/login')
//如果数据不为null 则放行
next()
})
3.10 用户退出操作
3.10.1 业务说明
当用户点击退出按钮时,应该删除sessionStorage中的token信息. 并且访问登录页面
3.10.2 用户退出操作
4. 用户首页布局
4.1 ElementUI 页面布局介绍
4.1.1 页面HTML
页面效果:
4.2 实现左侧菜单获取
4.2.1编辑Home.vue
4.2.2 编辑RightsController
4.2.3 编辑RightsService
package com.jt.service;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.jt.mapper.RightsMapper;
import com.jt.pojo.Rights;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.embedded.tomcat.TomcatWebServer;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* @author 刘昱江
* 时间 2021/5/12
*/
@Service
public class RightsServiceImpl implements RightsService{
@Autowired
private RightsMapper rightsMapper;
@Override
public List<Rights> getRightsList() {
//1.查询所有的一级菜单
QueryWrapper<Rights> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("parent_id", 0);
List<Rights> oneList = rightsMapper.selectList(queryWrapper);
//2.根据一级查询二级菜单
for (Rights oneRights : oneList){
//查询二级菜单
QueryWrapper<Rights> queryWrapper2 = new QueryWrapper<>();
queryWrapper2.eq("parent_id", oneRights.getId());
List<Rights> twoList = rightsMapper.selectList(queryWrapper2);
oneRights.setChildren(twoList);
}
return oneList;
}
}
4.2.4 页面效果展现
4.3 菜单栏介绍
4.3.1 导航菜单样式
4.3.2 菜单属性介绍
4.3.3 子菜单跳转
属性介绍:是否使用 vue-router 的模式,启用该模式会在激活导航时以 index 作为 path 进行路由跳转
定义子组件请求路径
4.3.4 欢迎页面展现
说明: 添加welcome页面
定义路由组件: 实现默认跳转
5 用户模块实现
5.1 实现用户页面跳转
5.1.1 创建user模块
5.1.2 定义用户路由
5.2 面包屑导航
5.2.1 官网Demo
5.2.2 定义面包屑
<!-- 1.定义面包屑导航 -->
<el-breadcrumb separator-class="el-icon-arrow-right">
<el-breadcrumb-item :to="{ path: '/home' }">首页</el-breadcrumb-item>
<el-breadcrumb-item>用户管理</el-breadcrumb-item>
<el-breadcrumb-item>用户列表</el-breadcrumb-item>
</el-breadcrumb>
5.3 定义卡片视图
5.3.1 官网API
5.3.2 定义卡片视图
<!-- 2.定义卡片视图 -->
<el-card class="box-card">
<h1>我是一个卡片视图</h1>
</el-card>
5.4 定义输入框
5.4.1 官网API
5.4.2 定义栅格
使用栅格是为了固定内容的大小 el-row 表示一行 :span=24 一行最多24个 代表宽度
5.4.2 编辑页面
<!-- 2.定义卡片视图 -->
<el-card class="box-card">
<!-- 3.定义栅格的一行 :gutter="20"定义行间距 :span="9" 占位符 -->
<el-row :gutter="20">
<el-col :span="9">
<!-- 3.定义搜索框 -->
<el-input placeholder="请输入内容">
<el-button slot="append" icon="el-icon-search"></el-button>
</el-input>
</el-col>
<el-col :span="4">
<!-- 定义添加按钮-->
<el-button type="primary">添加用户</el-button>
</el-col>
</el-row>
</el-card>
5.4.3 页面效果
5.5 实现用户列表查询
5.5.1 业务接口
5.5.2 定义PageResult对象
5.5.3 编辑UserController
5.5.4 编辑UserService
5.5.5 编辑MybatisPlus 配置类
官网API:
package com.jt.config;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author 刘昱江
* 时间 2021/5/13
*/
@Configuration
public class MybatisPlusConfig {
// 最新版
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MARIADB));
return interceptor;
}
}
5.6 实现用户列表显示VUE页面
5.6.1 获取用户数据
<script>
export default {
data(){
return {
queryInfo: {
query: '',
pageNum: 1,
pageSize: 5
},
//为了后续操作 定义数据在外边
userList: [],
total: 0
}
},
methods: {
async getUserList(){
const {data: result} = await this.$http.get('/user/list',{
params: this.queryInfo
})
if(result.status !== 200) return this.$message.error("用户列表查询失败")
this.userList = result.data.rows
this.total = result.data.total
}
},
//利用钩子函数实现数据查询
mounted(){
this.getUserList()
}
}
</script>
5.6.2 表格数据API
5.6.2 编辑页面
5.6.3 页面效果
5.6.4 状态信息
作用域插槽: slot-scope=“scope”
5.6.5 编辑按钮
5.6.6 效果展现
5.7 实现用户列表分页
5.7.1 官方API
5.7.2 复制分页JS
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="currentPage4"
:page-sizes="[100, 200, 300, 400]"
:page-size="100"
layout="total, sizes, prev, pager, next, jumper"
:total="400">
</el-pagination>
5.7.3 编辑分页操作
编辑页面JS
效果展现
5.8 用户状态修改
5.8.1 修改事件触发
在switch中 通过change事件可以实现数据的修改
5.8.2 编辑Vue页面
通过回调函数和作用域插槽 实现数据传递
5.8.3 编辑页面JS
5.8.4 编辑UserController
5.8.5 编辑UserService
5.9 用户搜索按钮实现
5.9.1 业务说明
当用户点击按钮之后,将数据绑定到查询对象中,重新获取列表数据即可.
5.9.2 编辑Vue页面
5.9.3 Input输入框清空操作
说明: 当用户输入成功之后,可以通过清空属性 已写数据 实现查询全部的功能
清空事件: 当用户调用clearable之后,调用clear事件
触发页面JS
5.10 添加用户功能实现
5.9.1 绘制新增表单对话框
1).绘制Dialog对话框
5.9.2 编辑页面HTML
<!-- 编辑用户新增对话框 visible.sync 控制对话框的显示-->
<el-dialog title="添加用户" :visible.sync="dialogVisible" width="50%">
<!-- 定义用户提交表单数据-->
<el-form :model="addUserModel" :rules="rules" ref="addUserRef" label-width="100px" class="demo-ruleForm">
<el-form-item label="用户名" prop="username">
<el-input v-model="addUserModel.username"></el-input>
</el-form-item>
<el-form-item label="密码" prop="password">
<el-input v-model="addUserModel.password" type="password"></el-input>
</el-form-item>
<el-form-item label="密码确认" prop="password2">
<el-input v-model="addUserModel.password2" type="password"></el-input>
</el-form-item>
<el-form-item label="电话" prop="phone">
<el-input v-model="addUserModel.phone"></el-input>
</el-form-item>
<el-form-item label="邮箱" prop="email">
<el-input v-model="addUserModel.email"></el-input>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false">取 消</el-button>
<el-button type="primary" @click="dialogVisible = false">确 定</el-button>
</span>
</el-dialog>
5.9.3 校验邮箱/手机正则/密码校验
//校验邮箱规则 rule校验规则 value校验的数据 callback回调函数
const checkEmail = (rule, value, callback) => {
//定义邮箱的正则表达式 JS中用/来表示正则表达式的开始和结束
const emailRege = /^[a-zA-Z0-9-_]+@[a-zA-Z0-9-_]+\.[a-zA-Z0-9-_]+$/
//正则表达式语法校验 test(xx) 校验成功 返回true 校验失败返回false
if (emailRege.test(value)) {
//表示邮箱合法 正确返回
return callback()
}
callback(new Error('请填写正确的邮箱地址'))
}
//校验手机号的邮箱规则
const checkPhone = (rule, value, callback) => {
//定义校验手机号的正则语法
const phoneRege = /^1[34578][0-9]{9}$/
if (phoneRege.test(value)) {
return callback()
}
callback(new Error('请填写正确的手机号'))
}
const checkPassword = (rule, value, callback) => {
if(this.addUserModel.password !== value) return callback(new Error('2次密码输入不一致'))
//否则校验成功
callback()
}
使用校验规则
5.11 重置表单数据
5.11.1 重置事件 close
5.11.2 编辑页面VUE
5.12 实现用户新增
5.12.1 编辑页面html
5.12.2 编辑UserController
5.12.3 编辑UserService
5.12.4 自动填充API说明
5.12.5 配置自动填充配置类
页面效果:
5.13 用户修改数据回显
5.13.1 绘制修改Dialog页面
<!-- 定义修改页面 -->
<el-dialog title="修改用户" :visible.sync="updateDialogVisible" width="50%">
<!-- 定义用户提交表单数据-->
<el-form :model="updateUserModel" :rules="rules" ref="updateUserRef" label-width="100px">
<el-form-item label="用户名" prop="username">
<el-input v-model="updateUserModel.username" disabled ></el-input>
</el-form-item>
<el-form-item label="电话" prop="phone">
<el-input v-model="updateUserModel.phone"></el-input>
</el-form-item>
<el-form-item label="邮箱" prop="email">
<el-input v-model="updateUserModel.email"></el-input>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="updateDialogVisible = false">取 消</el-button>
<el-button type="primary" @click="updateDialogVisible = false">确 定</el-button>
</span>
</el-dialog>
5.13.2 根据ID获取数据
async updateUserBtn(user){
this.updateDialogVisible = true
const {data: result} = await this.$http.get("/user/"+user.id)
if(result.status !== 200) return this.$message.error("用户查询失败")
this.updateUserModel = result.data
}
5.13.3 编辑UserController
5.13.4 编辑UserService
5.14 重置修改页面
5.14.1 官网API
5.14.2 编辑JS
5.15 用户修改提交
5.15.1 表单数据预校验
5.15.2 编辑UserController
5.15.3 编辑UserService
5.16 用户删除
5.16.1 消息提示框
5.16.2 编辑页面JS
5.16.3 编辑UserController
5.16.4 编辑UserService
5.16.5 页面效果展现
6. 商品分类功能实现
6.1 商品分类列表展现
6.1.1 编辑页面JS
6.1.2 编辑后台Controller
6.1.3 编辑后台Service
@Override
public List<ItemCat> findItemCatList(Integer type) {
//获取数据
Map<Integer,List<ItemCat>> map = getItemCatMap();
return findChildrenList(null,type,map);
}
public List<ItemCat> findChildrenList(Integer id,Integer type, Map<Integer,List<ItemCat>> map){
if(id==null) id = 0;
List<ItemCat> itemCatList = map.get(id);
if(type == 1){
return itemCatList;
}
if(itemCatList == null){
return null;
}
for (ItemCat itemCat: itemCatList){
if(itemCat.getLevel()<type){
List<ItemCat> childrenList = findChildrenList(itemCat.getId(),type,map);
itemCat.setChildren(childrenList);
}
}
return itemCatList;
}
public Map<Integer,List<ItemCat>> getItemCatMap(){
List<ItemCat> itemCatList = itemCatMapper.selectList(null);
Map<Integer,List<ItemCat>> map = new HashMap<>();
for(ItemCat itemCat : itemCatList){
Integer parentId = itemCat.getParentId();
if(map.containsKey(parentId)){
map.get(parentId).add(itemCat);
}else{
List<ItemCat> list = new ArrayList<>();
list.add(itemCat);
map.put(parentId, list);
}
}
return map;
}
方式2:
@Override
public List<ItemCat> findItemCatList(Integer type) {
Map<Integer,List<ItemCat>> map = getItemCatMap();
if(type == 1) return map.get(0);
if(type == 2) return findTwoItemCatList(map);
return findThreeItemCatList(map);
}
public List<ItemCat> findTwoItemCatList(Map<Integer,List<ItemCat>> map){
//1.获取一级商品分类
List<ItemCat> oneList = map.get(0);
//2.根据一级查询二级
for(ItemCat itemCat : oneList){
List<ItemCat> twoList = map.get(itemCat.getId());
itemCat.setChildren(twoList);
}
return oneList;
}
private List<ItemCat> findThreeItemCatList(Map<Integer,List<ItemCat>> map) {
List<ItemCat> twoList = findTwoItemCatList(map);
for (ItemCat itemCat : twoList){ //1级菜单
if(itemCat.getChildren() !=null){
for(ItemCat itemCat2 :itemCat.getChildren()){
List<ItemCat> threeList = map.get(itemCat2.getId());
itemCat2.setChildren(threeList);
}
}
}
return twoList;
}
public Map<Integer,List<ItemCat>> getItemCatMap(){
List<ItemCat> itemCatList = itemCatMapper.selectList(null);
Map<Integer,List<ItemCat>> map = new HashMap<>();
for(ItemCat itemCat : itemCatList){
Integer parentId = itemCat.getParentId();
if(map.containsKey(parentId)){
map.get(parentId).add(itemCat);
}else{
List<ItemCat> list = new ArrayList<>();
list.add(itemCat);
map.put(parentId, list);
}
}
return map;
}
6.1.4 页面效果展现
6.2 商品分类状态实现
6.2.1 编辑页面VUE
6.2.2 编辑ItemCatController
6.2.3 编辑ItemCatService
6.3 商品分类新增
6.3.1 编辑页面JS
6.3.2 编辑ItemCatController
6.3.3 编辑ItemCatService
6.4 商品分类修改
6.4.1 页面JS展现
6.4.2 编辑ItemCatController
6.4.3 编辑ItemCatService
6.5 商品分类删除
6.5.1 编辑页面JS
6.5.2 编辑ItemCatController
6.5.3 编辑ItemCatService
@Override
public void deleteItemCat(ItemCat itemCat) {
if(itemCat.getLevel() == 3){
itemCatMapper.deleteById(itemCat.getId());
return;
}
if(itemCat.getLevel() == 2){
itemCatMapper.delete(new QueryWrapper<>(itemCat).eq("parent_id", itemCat.getId()));
itemCatMapper.deleteById(itemCat.getId());
}
if(itemCat.getLevel() == 1){
QueryWrapper queryWrapper = new QueryWrapper();
queryWrapper.eq("parent_id", itemCat.getId());
List<ItemCat> list2 = itemCatMapper.selectList(queryWrapper);
for (ItemCat itemCat2 : list2){
QueryWrapper<ItemCat> queryWrapperTemp = new QueryWrapper<>();
queryWrapperTemp.eq("parent_id", itemCat2.getId());
itemCatMapper.delete(queryWrapperTemp);
itemCatMapper.deleteById(itemCat2.getId());
}
itemCatMapper.deleteById(itemCat.getId());
}
}
7 商品分类参数列表实现
7.1 商品分类参数列表
7.1.1 编辑ItemCatParam
7.1.2 编辑itemCatParamController
7.1.3 编辑itemCatParamService
7.1.4 页面效果展现
7.2 新增分类参数
7.2.1 编辑VUE页面
1).商品分类参数新增页面
2).新增商品分类参数
7.2.2 编辑ItemCatParamController
7.2.3 编辑ItemCatParamService
7.3 商品分类参数删除
7.3.1 编辑页面VUE
1).编辑页面JS
2).删除JS
7.3.2 编辑ItemCatParamController
7.3.2 编辑ItemCatParamService
7.4 商品分类参数修改
7.4.1 编辑页面JS
7.4.2 编辑Controller
7.4.3 编辑Service
8.商品模块实现
8.1 商品列表展现
8.1.1 编辑页面VUE
8.1.2 编辑ItemController
8.1.3 编辑ItemService
8.2 修改商品状态
8.2.1 编辑页面VUE
8.2.2 编辑ItemController
8.2.3 编辑ItemService
8.3 删除商品
8.3.1 编辑页面VUE
8.3.2 编辑ItemController
8.3.3 编辑ItemService
8.4 文件上传操作
8.4.1 文件上传页面
1).文件上传操作
8.4.2 编辑FileController
8.4.2 编辑properties配置文件
#配置windows服务器路径
image.localPathDir=D:/JT_IMAGE
#配置Linux服务器路径
#image.localPathDir=/usr/local/src/images
image.localUrlPath=http://image.jt.com
8.4.3 编辑FileService
@Service
@PropertySource("classpath:/properties/image.properties")
public class FileServiceImpl implements FileService{
//为属性动态赋值 注解@Value
@Value("${image.localPathDir}")
private String localPathDir; // = "D:/JT_IMAGE";
@Value("${image.localUrlPath}")
private String localUrlPath; // = "http://image.jt.com";
//1.参数 ~~~已知条件
//2.干什么 ~~~ 实现文件上传
//3.返回值: void ImageVO 有效返回
/**
* 文件上传案例实现
* 1.如何保证前端是上传的数据是有效的!!
* 1.1 校验文件的名称检查是否为图片
* 1.2 校验是否为恶意程序.
* @param file
* @return
*/
@Override
public ImageVO upload(MultipartFile file) throws IOException {
//1.1校验是否为图片类型 abc.jpg ABC.JPG 文件大小写
String fileName = file.getOriginalFilename();
//将所有的文件名称转化为小写
fileName = fileName.toLowerCase();
if(!fileName.matches("^.+\\.(jpg|png|gif)$")){
return null;
}
//1.2 校验图片类型是否为木马
try {
BufferedImage bufferedImage = ImageIO.read(file.getInputStream());
int width = bufferedImage.getWidth();
int height = bufferedImage.getHeight();
if(width == 0 || height == 0 ){
return null;
}
}catch (Exception e){
return null;
}
//2. 目录结构
//2.1 实现分目录存储... 可以以时间维度进行分隔 /yyyy/MM/dd/
String datePath =
new SimpleDateFormat("/yyyy/MM/dd/").format(new Date());
//2.2 进行目录的拼接 "D:/JT_IMAGE/2021/11/11";
String localDir = localPathDir + datePath;
//2.3 需要创建目录
File dirFile = new File(localDir);
if(!dirFile.exists()){
dirFile.mkdirs();
}
//3.文件名称重复 采用UUID防止文件重名 uuid.jpg
String uuid = UUID.randomUUID().toString()
.replace("-", "");
//fileName = abc.jpg
String fileType =
fileName.substring(fileName.lastIndexOf("."));
String realFileName = uuid + fileType;
//c:jt_image/2021/11/11/uuid.jpg
String filePathAll = localDir + realFileName;
//实现文件上传
File realFile = new File(filePathAll);
file.transferTo(realFile);
//封装ImageVO对象 //2021/11/11/uuid.jpg 图片路径
String virtualPath = datePath + realFileName;
//封装虚拟URl地址 http://image.jt.com/2021/11/11/uuid.jpg
String urlPath = localUrlPath + virtualPath;
return new ImageVO(virtualPath,urlPath,realFileName);
}
}
8.4.4 成功之后的回调
8.5 商品新增
8.5.1 编辑vue 页面
8.5.2 编辑ItemController
8.5.3 编辑ItemService
/**
* 实现三张表入库 商品表/商品详情表/商品参数表
* @param itemVO
*/
@Transactional
@Override
public void saveItem(ItemVO itemVO){
//1.入库商品表
Item item = itemVO.getItem();
item.setStatus(true); //默认启用状态
//要求入库之后返回主键
//MP如果设定了主键自增则会自动的实现数据回显
itemMapper.insert(item);
//2.入库商品详情
ItemDesc itemDesc = itemVO.getItemDesc();
itemDesc.setId(item.getId());
itemDescMapper.insert(itemDesc);
//3.入库商品参数
//1.一个商品应该有自己的单独的参数. 动态参数/静态属性 KYE-VLAUE [key:value,key2:value2]
ItemParam itemParam = itemVO.getItemParam();
ItemParamVO[] dynamic = itemParam.getDynamicArray();
ItemParamVO[] staticParam = itemParam.getStaticArray();
try {
//将页面传递的数据转化为JSON,之后数据库保存
String dynamicJSON = MAPPER.writeValueAsString(dynamic);
String staticJSON = MAPPER.writeValueAsString(staticParam);
//封装商品参数信息
itemParam.setDynamicArgs(dynamicJSON)
.setStaticArgs(staticJSON)
.setId(item.getId());
//实现入库操作
itemParamMapper.insert(itemParam);
} catch (JsonProcessingException e) {
e.printStackTrace();
//如果程序执行报错 则抛出异常
throw new RuntimeException(e);
}
}
本文由 liyunfei 创作,采用 知识共享署名4.0
国际许可协议进行许可
本站文章除注明转载/出处外,均为本站原创或翻译,转载前请务必署名
最后编辑时间为: Jul 26,2022