校园旧物回收商城,使用Springboot+Vue2.x开发,使用了JWT、MybatisPlus、JWT、ElementUI
项目已经开源在https://github.com/astudent2020/Campus_waste_recycling
主页:http://localhost:8080/ 用户只需要使用
http://localhost:8080/ 即可访问主页 主页的真实路径是
http://localhost:8080/index/body
登录:http://localhost:8080/user/login
提供管理员登录和用户登录
用户名:daetz
密码:123456
登陆成功后会返回主页,并给出相关的提示,提示登录成功
注册:http://localhost:8080/user/register
后台主页:http://localhost:8080/admin
用户只需要使用
http://localhost:8080/admin 即可访问管理员后台
管路员主页的真实路径是 http://localhost:8080/admin/pageone
用户可以使用 http://localhost:8080/admin/pagetwo
来检测数据前端后端数据是否互通,以及Token是否能正常被放入请求头中。
信息修改
描述:
访问路径
/index
/shoppingcart
/back
/login
/register
访问路径
/front/digital
/front/food
/font/book
Aside数据包括
设置访问路径
{{username}}
后台管理
退出
访问路径
/front/digital
/front/food
/font/book
前台给出的数据
增加部分数据
前端代码
{{item.name}}
¥{{item.price}}
查看详情
后端代码
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);
}
}
前端
商品详情
{{ goods.name }}
¥{{ goods.price }}
商品详情:{{ goods.description }}
销量:{{ goods.sale }}
库存:{{ goods.store }}
加入购物车
加入后端
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);
}
}
request.post("/cart/saveOrUpdate", cart)
.then(resp => {
if (resp.data.code === "200") {
this.$message.success("加入购物车成功");
} else {
this.$message.error("加入购物车失败");
}
});
},
},
结算事件
}
@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("订单创建失败");
}
});
}
<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>
分页查询
@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);
}
}
修复用户头像无法正常显示
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
},
}
增加数据库
路由守卫
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自动渲染
注意这里面不能使用 role===1来判断
如果你通过 JSON 解析或从服务器获取的数据中,role
的值是字符串类型的 '1'
,那么在比较时确实需要将条件判断中的值也使用字符串形式来进行比较,如 role === '1'
。
在进行 JSON 解析时,属性值的类型会被转换为相应的 JavaScript 数据类型。如果 role
在 JSON 数据中被定义为字符串类型的 '1'
,那么在解析后,role
将保持为字符串类型。这就解释了为什么在比较时需要使用字符串形式的 '1'
。
确保在解析 JSON 数据后,检查 role
的类型,并根据需要进行类型转换或使用适当的条件判断进行比较。
点击开关调整商品状态
忘记增加数据持久化
查询
重置
新增
批量删除
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>
},
信息卡片
检测发现是命名未统一,包含user
和username
仍然出错,忘记修改成request了
确保你在发送请求时使用了request
对象来发起请求,而不是直接使用axios
请确保你使用了request
对象发送请求,并且在发送请求时会自动应用拦截器中设置的请求头。如果你直接使用axios
对象发送请求,那么拦截器中设置的请求头将不会生效。
在代码中,导入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:''
}
}
}
如果在
标签中嵌套了另一个
标签,并且您想在内部的嵌套视图中使用created
函数进行分页查询,您可以按照以下步骤进行操作。
在Vue组件中,可以通过在嵌套的
组件中定义created
生命周期钩子来执行分页查询逻辑。
在上述示例中,我们在外部组件中定义了一个名为outerMethod
的方法。然后,我们通过将该方法作为属性传递给内部视图组件
,并使用:override-method="outerMethod"
绑定。
在内部视图组件中,我们通过props
接收外部传递的方法,并在created
生命周期钩子中调用this.overrideMethod()
来执行重写的方法。内部视图组件中的overrideMethod
方法将覆盖外部视图组件中的同名方法。
当外部视图组件的方法被重写时,内部视图组件可以选择是否调用原始方法,或者完全替代原始方法的逻辑。
请根据您的具体需求调整示例代码,并在内部视图组件中实现适当的重写逻辑。