校园旧物商城系统

一、项目说明

校园旧物回收商城,使用Springboot+Vue2.x开发,使用了JWT、MybatisPlus、JWT、ElementUI

项目已经开源在https://github.com/astudent2020/Campus_waste_recycling

校园旧物商城系统_第1张图片

文章目录

  • 一、项目说明
    • 一、说明书
      • 1、用户主页:
      • 2、登录注册页面
      • 3、后台页面
      • 4、Token测试点
    • 二、页面展示
      • 1、首页
      • 2、旧物上传
      • 3、购物车
      • 4、个人中心
      • 5、订单管理
      • 6、登录
      • 7、注册
      • 8、后台首页
      • 9、个人中心
      • 10、用户管理
      • 11、类别管理
      • 12、商品管理
      • 13、订单管理
  • 二、主页说明
    • 一、说明
      • 1、修改之前的前端结构,使用utils完成统一接口
      • 2、更新front和back中的header、aside
        • 1、FrontTemplate.vue表示前端模板【Header、Aside】
        • 2、BackTemplateb.vue表示后端模板【Header、Aside】
    • 二、主页前端
      • 1、header
        • header头像
      • 2、Aside
      • 3、主页详情内容展示
        • 1、创建数据库
    • 三、主页的条件查询
    • 四、主页的详情展示
    • 五、购物车的处理
    • 六、购物车结算
    • 七、订单展示
    • 八、注册页面
    • 九、旧物上传
    • 十、权限设置
  • 三、后台说明
  • 四、bug记录日志
    • 1、使用utils设置config.js导致Token未能正常放入请求头
    • 2、路由守卫失效
  • 五、创新性实验
    • Vue中方法重写

一、说明书

1、用户主页:

主页:http://localhost:8080/ 用户只需要使用
http://localhost:8080/ 即可访问主页 主页的真实路径是
http://localhost:8080/index/body
校园旧物商城系统_第2张图片

2、登录注册页面

登录:http://localhost:8080/user/login
校园旧物商城系统_第3张图片
提供管理员登录和用户登录
用户名:daetz
密码:123456
登陆成功后会返回主页,并给出相关的提示,提示登录成功

校园旧物商城系统_第4张图片

注册:http://localhost:8080/user/register

校园旧物商城系统_第5张图片
注册需要用户输入个人信息,大部分都是使用小组件完成

3、后台页面

后台主页:http://localhost:8080/admin
用户只需要使用
http://localhost:8080/admin 即可访问管理员后台
管路员主页的真实路径是 http://localhost:8080/admin/pageone
校园旧物商城系统_第6张图片

4、Token测试点

用户可以使用 http://localhost:8080/admin/pagetwo
来检测数据前端后端数据是否互通,以及Token是否能正常被放入请求头中。

校园旧物商城系统_第7张图片

二、页面展示

1、首页

校园旧物商城系统_第8张图片

2、旧物上传

校园旧物商城系统_第9张图片

3、购物车

校园旧物商城系统_第10张图片

校园旧物商城系统_第11张图片

4、个人中心

校园旧物商城系统_第12张图片

信息修改

校园旧物商城系统_第13张图片

5、订单管理

校园旧物商城系统_第14张图片

6、登录

校园旧物商城系统_第15张图片

7、注册

校园旧物商城系统_第16张图片

8、后台首页

校园旧物商城系统_第17张图片

9、个人中心

校园旧物商城系统_第18张图片

10、用户管理

校园旧物商城系统_第19张图片

11、类别管理

校园旧物商城系统_第20张图片

12、商品管理

校园旧物商城系统_第21张图片

13、订单管理

校园旧物商城系统_第22张图片

二、主页说明

一、说明

1、修改之前的前端结构,使用utils完成统一接口

校园旧物商城系统_第23张图片

2、更新front和back中的header、aside

描述:

1、FrontTemplate.vue表示前端模板【Header、Aside】

  • Header

访问路径

/index

/shoppingcart

/back

/login

/register

image-20230607112855289

  • Aside

访问路径

/front/digital

/front/food

/font/book

校园旧物商城系统_第24张图片

2、BackTemplateb.vue表示后端模板【Header、Aside】

校园旧物商城系统_第25张图片

Aside数据包括

校园旧物商城系统_第26张图片

二、主页前端

1、header

设置访问路径

校园旧物商城系统_第27张图片

image-20230607112855289

校园旧物商城系统_第28张图片

header头像

校园旧物商城系统_第29张图片

                
                    
                        
{{username}}
后台管理 退出

2、Aside

访问路径

/front/digital

/front/food

/font/book

校园旧物商城系统_第30张图片

校园旧物商城系统_第31张图片

校园旧物商城系统_第32张图片

3、主页详情内容展示

前台给出的数据

校园旧物商城系统_第33张图片

1、创建数据库

  • id
  • name
  • price
  • imgUrl
  • description
  • sale
  • store

校园旧物商城系统_第34张图片

增加部分数据

image-20230608083450642

三、主页的条件查询

校园旧物商城系统_第35张图片

前端代码






后端代码

package com.example.cshand.controller;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.example.cshand.DTO.ResultDataDTO;
import com.example.cshand.entity.Goods;
import com.example.cshand.mapper.GoodsMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author daetz
 * @creat 2023/6/8
 **/
@RestController
@RequestMapping("/goods")
public class GoodsController {

		@Autowired
		private GoodsMapper goodsMapper;

		@RequestMapping("/findByCategory/{PageNum}/{size}/{categoryId}")
		public ResultDataDTO findGoods(
						@PathVariable("PageNum") Integer PageNum,
						@PathVariable("size") Integer size,
						@PathVariable("categoryId") Integer categoryId
		){
				Page<Goods> page = new Page<>(PageNum, size);

				// 创建查询条件对象
				QueryWrapper<Goods> queryWrapper = new QueryWrapper<>();
				queryWrapper.eq("category_id", categoryId); // 根据categoryId进行条件查询

				Page<Goods> result = goodsMapper.selectPage(page, queryWrapper);
				return ResultDataDTO.success(result);
		}
}

四、主页的详情展示

校园旧物商城系统_第36张图片

前端







加入后端

package com.example.cshand.controller;

import com.example.cshand.DTO.ResultDataDTO;
import com.example.cshand.entity.Cart;
import com.example.cshand.service.impl.CartServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Date;

/**
 * @author daetz
 * @creat 2023/6/8
 **/
@RestController
@RequestMapping("/cart")
public class CartController {


		@Autowired
		private CartServiceImpl cartService;

		/**
		 * 加入购物车
		 * @param cart
		 * @return
		 */
		@PostMapping("/saveOrUpdate")
		public ResultDataDTO SaveCart(@RequestBody Cart cart){
				boolean save = cartService.saveOrUpdate(cart);
				return ResultDataDTO.success(save);
		}
}

五、购物车的处理

校园旧物商城系统_第37张图片

校园旧物商城系统_第38张图片

校园旧物商城系统_第39张图片

      request.post("/cart/saveOrUpdate", cart)
          .then(resp => {
            if (resp.data.code === "200") {
              this.$message.success("加入购物车成功");
            } else {
              this.$message.error("加入购物车失败");
            }
          });
      },
  },

校园旧物商城系统_第40张图片

六、购物车结算

校园旧物商城系统_第41张图片

校园旧物商城系统_第42张图片

校园旧物商城系统_第43张图片

结算事件

校园旧物商城系统_第44张图片

		}

		@RequestMapping("/deleteById/{id}")
		public ResultDataDTO deleteById(@PathVariable Integer id){
				boolean result = cartService.removeById(id);
				return ResultDataDTO.success(result);
		}
}
    // 将订单数据发送到服务器
        request.post("/order/create", order)
            .then(resp => {
              if (resp.data.code === "200") {
                this.$message.success("订单创建成功");
                request.post("/cart/deleteById/"+order.goodsId)
                    .then(resp => {
                      if (resp.data.code === "200"){
                        this.$message.info("购物车清除成功");
                      }else{
                        this.$message.error("购物车清除失败");
                      }
                    });
              } else {
                this.$message.error("订单创建失败");
              }
            });
      }

七、订单展示

校园旧物商城系统_第45张图片

校园旧物商城系统_第46张图片

<template>
    <div>
        <el-table :data="orders" style="width: 100%">
            <el-table-column prop="id" label="订单ID"></el-table-column>
            <el-table-column prop="username" label="用户姓名"></el-table-column>
            <el-table-column prop="goodsName" label="商品名称"></el-table-column>
            <el-table-column prop="count" label="数量"></el-table-column>
            <el-table-column prop="price" label="单价"></el-table-column>
            <el-table-column prop="subtotal" label="小计"></el-table-column>
        </el-table>
        <el-pagination
                background
                layout="prev, pager, next"
                :page-size="10"
                :total="total"
                @current-change="page">
        </el-pagination>
    </div>
</template>

<script>
import request from "@/utils/request";

export default {
  name:'OrderShow',
  data() {
    return {
      total:null,
      goods:null,
      orders: [] // 存储订单数据
    };
  },
  methods: {
    page(currentPage){ //alert("wait...") 动态分页
      const _this=this
      request.get('/order/findAll/'+currentPage+'/10').then(function (resp){
        _this.orders=resp.data.data.records
        _this.total=resp.data.data.total
      })
    },
  },
  created() { //alert 初始化操作
    const _this=this
    request.get('/order/findAll/1/10').then(function (resp){
      console.log(resp.data)
      _this.orders=resp.data.data.records
      _this.total=resp.data.data.total
      _this.size=resp.data.data.size
    })
  },
}
</script>





分页查询

校园旧物商城系统_第47张图片

		@RequestMapping("/findAll/{PageNum}/{size}")
		public ResultDataDTO findAll(@PathVariable("PageNum") Integer PageNum, @PathVariable("size") Integer size){
				Page page = new Page<>(PageNum, size);
				Page result = ordersMapper.selectPage(page, null);
				return ResultDataDTO.success(result);
		}
}

校园旧物商城系统_第48张图片

八、注册页面

校园旧物商城系统_第49张图片

修复用户头像无法正常显示

校园旧物商城系统_第50张图片

  methods: {
    handleSelect(key, keyPath) {
      console.log(key, keyPath);
    },
    logout() {
      // 调用 Vue Router 的 push 方法,将路径跳转到 '/login'
      router.push('/index/login')
      // 删除本地存储的用户信息
      localStorage.removeItem('user')
      // 显示退出成功的消息
      this.$message.success('退出成功')
    }
  },
  created() {
    const storedUser = localStorage.getItem('user');
    const user = JSON.parse(storedUser);
    this.username=user.username
    this.userImage=user.avatar
  },
}

九、旧物上传

image-20230608201743367

增加数据库

校园旧物商城系统_第51张图片

校园旧物商城系统_第52张图片

校园旧物商城系统_第53张图片

校园旧物商城系统_第54张图片

校园旧物商城系统_第55张图片

校园旧物商城系统_第56张图片

十、权限设置

路由守卫

import Vue from 'vue'
import VueRouter from 'vue-router'

import Index from "@/views/front/Index.vue";
import adminHome from "@/views/back/adminHome.vue";
import UserLogin from "@/views/front/UserLogin.vue";//用户登录页面
import Register from "@/views/front/UserRegister.vue";

import AddUserInformation from "@/views/back/AddUserInformation.vue";
import UserUpdate from "@/views/back/UserUpdate.vue";
import AdminLogin from "@/views/back/AdminLogin.vue";


//后台
import PageTwo from "@/views/back/PageTwo.vue";
import PageThree from "@/views/back/PageThree.vue";
import PageFour from "@/views/back/PageFour.vue";
import PageFive from "@/views/back/PageFive.vue";
import GoodsUpdate from "@/views/back/GoodsUpdate.vue";



/**
 * 主页
 */


//主页设计和详情展示
import ShoppingHome from "@/views/front/ShoppingHome.vue";
import ShoppingDetail from "@/views/front/ShoppingDetail.vue";
import UserInformation from "@/views/front/UserInformation.vue";
import ChangeInformation from "@/views/front/ChangeInformation.vue";

//后端信息
import AdminInformation from "@/views/back/AdminInformation.vue";
import AdminChangeInformation from "@/views/back/AdminChangeInformation.vue";
//aside

import digital1 from "@/views/Aside/digital1.vue"
import digital2 from "@/views/Aside/digital2.vue"
import digital3 from "@/views/Aside/digital3.vue"

import book1 from "@/views/Aside/book1.vue";
import book2 from "@/views/Aside/book2.vue";
import book3 from "@/views/Aside/book3.vue";

import food1 from  "@/views/Aside/food1.vue";
import food2 from  "@/views/Aside/food2.vue";
import food3 from  "@/views/Aside/food3.vue";

import CartShow from "@/views/front/CartShow.vue";
import OrderShow from "@/views/front/OrderShow.vue";
import GoodsUpload from "@/views/front/GoodsUpload.vue";

Vue.use(VueRouter)

const routes = [
  {
    path: '/',
    redirect:'/index/home',
  },
  {
    path: '/index',
    name: '起始页',
    component: Index,
    children:[
      {
        path:'home',
        name:'商品购物主页',
        component: ShoppingHome,
      },
      {
        meta: {
          requiresAuth: true // 设置需要登录才能访问
        },
        name: '更新个人信息',
        component: ChangeInformation,
        path: 'change'
      },
      {
        meta: {
          requiresAuth: true // 设置需要登录才能访问
        },
        name:'个人信息',
        path: 'information',
        component: UserInformation,
      },
      {
        name:'商品详情展示',
        component: ShoppingDetail,
        path:'detail',
      },
      {
        path: 'login',
        name:'用户登录页面',
        component: UserLogin
      },
      {
        path: 'register',
        name:'用户注册页面',
        component: Register
      },
      {
        meta: {
          requiresAuth: true // 设置需要登录才能访问
        },
        name:'购物车结算',
        component: CartShow,
        path:'cart',
      },
      {
        meta: {
          requiresAuth: true // 设置需要登录才能访问
        },        name:'旧物上传',
        component: GoodsUpload,
        path: 'upload'
      },
      {
        meta: {
          requiresAuth: true // 设置需要登录才能访问
        },
        name:'订单中心',
        component: OrderShow,
        path: 'order'
      },
      {
        path:'digital1',
        name:'电脑',
        component: digital1,
      },
      {
        path:'digital2',
        name:'手机',
        component: digital2,
      },
      {
        path:'digital3',
        name:'平板',
        component: digital3,
      },
      {
        path:'food1',
        name:'面包',
        component: food1,
      },
      {
        path:'food2',
        name:'牛奶',
        component: food2,
      },
      {
        path:'food3',
        name:'饮料',
        component: food3,
      },
      {
        path:'book1',
        name:'教材',
        component: book1,
      },
      {
        path:'book2',
        name:'人文',
        component: book2,
      },
      {
        path:'book3',
        name:'艺术',
        component: book3,
      },

    ]
  },

  {
    path: '/admin',
    name: '管理员主页',
    component: adminHome,
    meta: {
      requiresAuth: true // 设置需要登录才能访问
    },
    children:[
      {
        path: 'information',
        name: '后台用户信息展示',
        component: AdminInformation,
      },
      {
        path: 'changeinfo',
        name: '后台更新用户信息',
        component: AdminChangeInformation,
      },
      {
        path:'pagetwo',
        name:'用户',
        component: PageTwo,
      },
      {
        path:'pagethree',
        name:'商品1',
        component: PageThree,
      },
      {
        path:'pagefour',
        name:'商品2',
        component: PageFour,
      },
      {
        path:'pagefive',
        name:'商品3',
        component: PageFive,
      },
      {
        path: 'addUser',
        name:'用户信息添加',
        component: AddUserInformation,
        meta: {
          requiresAuth: true // 设置需要登录才能访问
        }
      },
      {
        path: 'update',
        name:'用户更新',
        component: UserUpdate,
        meta: {
          requiresAuth: true // 设置需要登录才能访问
        },
      },
      {
        name:'修改商品状态',
        path: 'change',
        component: GoodsUpdate,
      }
    ]
  },
  {
    path: '/adminLogin',
    name:'后台管理员登录',
    component: AdminLogin
  },

]

const router = new VueRouter({
  mode: 'history',
  base: process.env.BASE_URL,
  routes
})

// 路由守卫
router.beforeEach((to, from, next) => {
  // 判断当前路由是否需要登录才能访问
  if (to.matched.some(record => record.meta.requiresAuth)) {
    // 判断用户是否已经登录
    if (!localStorage.getItem('user')) {
      // 如果没有登录,给出提示
      Vue.prototype.$message.error('您还未登录,请先登录!')
      // 等待2秒后跳转
      setTimeout(() => {
        next('/index/home')
      }, 2000)
      return // 终止路由导航
    }
  }
  // 其他情况继续跳转
  next()
})


export default router

v-if自动渲染

校园旧物商城系统_第57张图片

校园旧物商城系统_第58张图片

注意这里面不能使用 role===1来判断

如果你通过 JSON 解析或从服务器获取的数据中,role 的值是字符串类型的 '1',那么在比较时确实需要将条件判断中的值也使用字符串形式来进行比较,如 role === '1'

在进行 JSON 解析时,属性值的类型会被转换为相应的 JavaScript 数据类型。如果 role 在 JSON 数据中被定义为字符串类型的 '1',那么在解析后,role 将保持为字符串类型。这就解释了为什么在比较时需要使用字符串形式的 '1'

确保在解析 JSON 数据后,检查 role 的类型,并根据需要进行类型转换或使用适当的条件判断进行比较。

三、后台说明

点击开关调整商品状态

忘记增加数据持久化

校园旧物商城系统_第59张图片

校园旧物商城系统_第60张图片

校园旧物商城系统_第61张图片

        
查询 重置 新增 批量删除
        search(){
          const _this = this;
          request.get('/admin/findByName/'+_this.input2).then(function(resp) {
            console.log(resp)
            _this.tableData=resp.data
            _this.total=resp.data.length
          });
        },
        addUser(){
          this.$router.push('/addUser')
        },
        resetForm(){
            this.input1='';
            this.input2='';
        },
        deleteUser(row){
          const _this=this
          request.delete('/admin/delete/'+row.id).then(function (resp){
            //console.log(resp)
            alert("删除成功")
            window.location.reload();
          })
        },
        edit(row) {
          this.$router.push({
            path:'/update',
            query:{
              id:row.id
            }
          })
           //row.id<点击的id>
        },

校园旧物商城系统_第62张图片

信息卡片

校园旧物商城系统_第63张图片

校园旧物商城系统_第64张图片

校园旧物商城系统_第65张图片

校园旧物商城系统_第66张图片

校园旧物商城系统_第67张图片

校园旧物商城系统_第68张图片

校园旧物商城系统_第69张图片

校园旧物商城系统_第70张图片

校园旧物商城系统_第71张图片

校园旧物商城系统_第72张图片

四、bug记录日志

1、使用utils设置config.js导致Token未能正常放入请求头

校园旧物商城系统_第73张图片

检测发现是命名未统一,包含userusername

校园旧物商城系统_第74张图片

仍然出错,忘记修改成request了

确保你在发送请求时使用了request对象来发起请求,而不是直接使用axios

请确保你使用了request对象发送请求,并且在发送请求时会自动应用拦截器中设置的请求头。如果你直接使用axios对象发送请求,那么拦截器中设置的请求头将不会生效。

校园旧物商城系统_第75张图片

2、路由守卫失效

  • 忘记导入Vue

校园旧物商城系统_第76张图片

在代码中,导入Vue是为了使用Vue.prototype.$message.error,该语句用于在路由守卫中显示错误消息。

Vue.prototype.$message.error是使用Vue的原型属性$message来显示错误消息的方法。通过导入Vue,你可以在路由守卫中访问Vue实例,并使用$message.error方法来显示错误消息。

请注意,确保你的项目中已经安装并正确导入了Vue库。如果你的项目中没有使用Vue,或者没有安装Vue相关依赖,那么你可能不需要导入Vue。但在代码中使用了Vue.prototype.$message.error的情况下,需要确保Vue正确导入和配置。

import request from '@/utils/request'; // 导入自定义的request对象

export default {
    name: "PageTwo",
    methods:{
      search() {
          const _this = this;
          request.get('/admin/findByName/' + _this.input2).then(function(resp) {
            console.log(resp);
            _this.tableData = resp.data;
            _this.total = resp.data.length;
          });
        },
        addUser(){
          this.$router.push('/addUser')
        },
        resetForm(){
            this.input1='';
            this.input2='';
        },
        deleteUser(row){
          const _this=this
          request.post('/admin/delete/'+row.id).then(function (resp){
            //console.log(resp)
            alert("删除成功")
            window.location.reload();
          })
        },
        edit(row) {
          this.$router.push({
            path:'/update',
            query:{
              id:row.id
            }
          })
           //row.id<点击的id>
        },
        page(currentPage){ //alert("wait...") 动态分页
            const _this=this
            request.get('/findUser/'+currentPage+'/3').then(function (resp){
                _this.tableData=resp.data.records
                _this.total=resp.data.total
            })
        }
    },
  created() {
      const _this = this;
      request.get('/findUser/1/3').then(function(resp) {
        _this.tableData = resp.data.records;
        _this.total = resp.data.total;
        _this.size = resp.data.size;
        console.log(resp.data);
      });
    },
    data() {
        return {
            input1: '',
            input2: '',
            total:null,
            tableData:null,
            avatar:''
        }
    }
}

五、创新性实验

Vue中方法重写

如果在标签中嵌套了另一个标签,并且您想在内部的嵌套视图中使用created函数进行分页查询,您可以按照以下步骤进行操作。

在Vue组件中,可以通过在嵌套的组件中定义created生命周期钩子来执行分页查询逻辑。




在上述示例中,我们在外部组件中定义了一个名为outerMethod的方法。然后,我们通过将该方法作为属性传递给内部视图组件,并使用:override-method="outerMethod"绑定。

在内部视图组件中,我们通过props接收外部传递的方法,并在created生命周期钩子中调用this.overrideMethod()来执行重写的方法。内部视图组件中的overrideMethod方法将覆盖外部视图组件中的同名方法。

当外部视图组件的方法被重写时,内部视图组件可以选择是否调用原始方法,或者完全替代原始方法的逻辑。

请根据您的具体需求调整示例代码,并在内部视图组件中实现适当的重写逻辑。

你可能感兴趣的:(java,前端,服务器)