springboot + Vue前后端项目(第七记)

1.写在前面

  • 上一篇博客实现了对用户前后端对接,本篇博客主要实现对页面的优化
  • 本篇博客需要对vue的路由和组件化有所了解,读者可以提前学习相关知识

2. 页面优化

对HomeView.vue(改名Manage.vue)页面进行改造,拆分成一个个组件,现有的项目结构如下,箭头部分是本篇博客改动部分。
在这里插入图片描述

2.1 Aside.vue

侧边栏代码抽取

<template>
  <el-menu :default-openeds="['1', '3']" style="min-height: 100%; overflow-x: hidden"
           background-color="rgb(48, 65, 86)"
           text-color="#fff"
           active-text-color="#ffd04b"
           :collapse-transition="false"
           :collapse="isCollapse"
           router
  >
    <div style="height: 60px; line-height: 60px; text-align: center">
      <img src="../assets/logo.png" alt="" style="width: 20px; position: relative; top: 5px; right: 5px">
      <b style="color: white" v-show="logoTextShow">后台管理系统</b>
    </div>
    <el-menu-item index="/">
      <template slot="title">
        <i class="el-icon-s-home"></i>
        <span slot="title">主页</span>
      </template>
    </el-menu-item>
    <el-submenu index="2">
      <template slot="title">
        <i class="el-icon-menu"></i>
        <span slot="title">系统管理</span>
      </template>
      <el-menu-item index="/user">
        <i class="el-icon-user"></i>
        <span slot="title">用户管理</span>
      </el-menu-item>
    </el-submenu>

  </el-menu>
</template>

<script>
export default {
  name: "Aside",
  props: {
    isCollapse: Boolean,
    logoTextShow: Boolean
  }

}
</script>

<style scoped>

</style>

2.2.1 如何在当前组件下引入其他组件

这是引入组件的基本步骤

// 第一步  引入组件
import Aside from "@/components/Aside";

// 第二步,在components加入
components: {
    Aside,
  },

// 第三步 使用组件
<el-aside :width="sideWidth + 'px'" style="box-shadow: 2px 0 6px rgb(0 21 41 / 0.35);">
  <Aside :is-collapse="isCollapse" :logo-text-show="logoTextShow"/>
</el-aside>

2.2.2 父组件向子组件传递数据(番外篇)

在Vue中,父组件可以通过props属性向子组件传递数据。在父组件中使用子组件时,可以在子组件的标签上使用v-bind指令来绑定数据,将数据传递给子组件。例如:

<template>
  <div>
    <child-component :message="parentMessage" :isCollase="isCollase"></child-component>
  </div>
</template>

<script>
import ChildComponent from './ChildComponent.vue'

export default {
  components: {
    ChildComponent
  },
  data() {
    return {
      parentMessage: 'Hello from parent component',
      isCollase: false
    }
  }
}
</script>

在子组件中,可以通过props属性来接收父组件传递过来的数据。例如:

<template>
  <div>
    <p>{{ message }}</p>
  </div>
</template>

<script>
export default {
  props: {
    message: String,
    isCollase: Boolean
  }
}
</script>

这样,父组件中的parentMessage会传递给子组件中的message,并在子组件中显示出来。了解完这个,页面组件化基本大功告成

2.2 Header.vue

页面头部抽取

<template>
  <div style="line-height: 60px; display: flex">
    <div style="flex: 1;">
      <span :class="collapseBtnClass" style="cursor: pointer; font-size: 18px" @click="collapse"></span>

      <el-breadcrumb separator=">" style="display: inline-block; margin-left: 10px">
        <el-breadcrumb-item :to="'/'">首页</el-breadcrumb-item>
        <el-breadcrumb-item v-for="(item, index) in breadCrumbs" :key="item.path">
          <router-link :to="item.path">{{ item.meta.title }}</router-link>
        </el-breadcrumb-item>
      </el-breadcrumb>
    </div>
    <el-dropdown style="width: 70px; cursor: pointer">
      <span>王小虎</span><i class="el-icon-arrow-down" style="margin-left: 5px"></i>
      <el-dropdown-menu slot="dropdown" style="width: 100px; text-align: center">
        <el-dropdown-item style="font-size: 14px; padding: 5px 0">个人信息</el-dropdown-item>
        <el-dropdown-item style="font-size: 14px; padding: 5px 0">退出</el-dropdown-item>
      </el-dropdown-menu>
    </el-dropdown>
  </div>
</template>

<script>
export default {
  name: "Header",
  props: {
    collapseBtnClass: String,
    collapse: Boolean,
  },
  watch: {
    $route() {
      this.getBreadcrumb();
    }
  },
  data(){
    return {
      breadCrumbs: []
    }
  },
  created() {
    this.getBreadcrumb()
  },
  methods: {
    getBreadcrumb(){
      this.breadCrumbs = this.$route.matched.filter(item => item.meta && item.meta.title);
    }
  }
}
</script>

<style scoped>

</style>

2.3 User.vue

展示内容抽取

<template>

  <div>
    <div style="margin: 10px 0">
      <el-input style="width: 200px" placeholder="请输入名称" suffix-icon="el-icon-search" v-model="username"></el-input>
      <el-input style="width: 200px" placeholder="请输入地址" suffix-icon="el-icon-position" class="ml-5" v-model="address"></el-input>
      <el-button class="ml-5" type="primary" @click="getList">搜索</el-button>
      <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
    </div>

    <div style="margin: 10px 0">
      <el-button type="primary" @click="handleAdd">新增 <i class="el-icon-circle-plus-outline"></i></el-button>
      <el-button type="warning" plain icon="el-icon-edit" size="mini" :disabled="single" @click="handleUpdate">修改</el-button>
      <el-button type="danger" :disabled="multiple" @click="handleDelete">删除 <i class="el-icon-remove-outline"></i></el-button>
      <el-button type="primary">导入 <i class="el-icon-bottom"></i></el-button>
      <el-button type="primary">导出 <i class="el-icon-top"></i></el-button>
    </div>

    <el-table v-loading="loading" :data="tableData" border stripe :header-cell-class-name="headerBg" @selection-change="handleSelectionChange">
      <el-table-column type="selection" width="55"></el-table-column>
      <el-table-column prop="id" label="序号" width="140"></el-table-column>
      <el-table-column prop="username" label="用户名" width="140"></el-table-column>
      <el-table-column prop="nickname" label="昵称" width="140"></el-table-column>
      <el-table-column prop="email" label="邮箱" width="200"></el-table-column>
      <el-table-column prop="address" label="地址" width="140"></el-table-column>
      <el-table-column prop="createTime" label="创建时间" width="140"></el-table-column>
      <el-table-column label="操作"  align="center">
        <template v-slot="scope">
          <el-button type="success" @click="handleUpdate(scope.row)">编辑 <i class="el-icon-edit"></i></el-button>
          <el-button type="danger" @click="handleDelete(scope.row)">删除 <i class="el-icon-remove-outline"></i></el-button>
        </template>
      </el-table-column>
    </el-table>
    <div style="padding: 10px 0">
      <el-pagination
          class="page"
          @size-change="handleSizeChange"
          @current-change="handleCurrentChange"
          :page-sizes="[5, 10]"
          :page-size="pageSize"
          layout="total, sizes, prev, pager, next, jumper"
          :total="total">
      </el-pagination>
    </div>

    <!--  用户信息       -->
    <el-dialog title="用户信息" :visible.sync="dialogFormVisible" width="30%" >
      <el-form label-width="80px" size="small">
        <el-form-item label="用户名">
          <el-input v-model="form.username" autocomplete="off"></el-input>
        </el-form-item>
        <el-form-item label="昵称">
          <el-input v-model="form.nickname" autocomplete="off"></el-input>
        </el-form-item>
        <el-form-item label="邮箱">
          <el-input v-model="form.email" autocomplete="off"></el-input>
        </el-form-item>
        <el-form-item label="电话">
          <el-input v-model="form.phone" autocomplete="off"></el-input>
        </el-form-item>
        <el-form-item label="地址">
          <el-input v-model="form.address" autocomplete="off"></el-input>
        </el-form-item>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button @click="dialogFormVisible = false">取 消</el-button>
        <el-button type="primary" @click="save">确 定</el-button>
      </div>
    </el-dialog>
  </div>

</template>

<script>
export default {
  name: "User",
  data(){
    return {
      tableData: [],
      pageSize: 5,
      total: 0,
      pageNum: 1,
      username: '',
      address: '',
      collapseBtnClass: 'el-icon-s-fold',
      isCollapse: false,
      sideWidth: 200,
      logoTextShow: true,
      headerBg: 'headerBg',
      dialogFormVisible: false,
      form: {},
      // 遮罩层
      loading: true,
      // 选中数组
      ids: [],
      // 非单个禁用
      single: true,
      // 非多个禁用
      multiple: true,
    }
  },
  created() {
    this.getList();
  },
  methods: {
    getList(){
      this.loading = true;
      this.request.get('/user/page',
          {
            params: {
              pageNum: this.pageNum,
              pageSize: this.pageSize,
              username: this.username,
              address: this.address
            }
          }
      ).then(res => {
        this.tableData = res.data.records;
        this.total = res.data.total;
        this.loading = false;
      })
    },
    handleSizeChange(val) {
      this.pageSize = val;
    },
    handleCurrentChange(val) {
      this.pageNum = val;
      this.getList();
    },
    // 新增
    handleAdd(){
      this.dialogFormVisible = true;
      this.form = {};
    },
    save(){
      this.request.post("/user",this.form).then(res => {
        if(res.code === "200" || res.code === 200){
          this.$message.success("操作成功")
        }else {
          this.$message.error("操作失败")
        }
        this.dialogFormVisible = false;
        this.getList();
      })
    },
    // 修改
    handleUpdate(row){
      this.form = row;
      this.dialogFormVisible = true;
    },
    // 删除
    handleDelete(row){
      let _this = this;
      const userIds = row.id || this.ids;
      this.$confirm('是否确认删除用户编号为"' + userIds + '"的数据项?', '删除用户', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        _this.request.delete("/user/"+userIds).then(res=>{
          if(res.code === "200" || res.code === 200){
            _this.$message.success("删除成功")
          }else {
            _this.$message.error("删除失败")
          }
          this.getList();
        })
      }).catch(() => {
      });
    },
    // 多选框选中数据
    handleSelectionChange(selection) {
      this.ids = selection.map(item => item.id);
      this.single = selection.length != 1;
      this.multiple = !selection.length;
    },
    // 重置按钮
    resetQuery(){
      this.username = undefined;
      this.address = undefined;
      this.getList();
    }

  }
}
</script>

<style scoped>

</style>

2.4 Manage.vue

完整的整体页面代码

<template>
  <el-container style="min-height: 100vh">

    <el-aside :width="sideWidth + 'px'" style="box-shadow: 2px 0 6px rgb(0 21 41 / 0.35);">
      <Aside :is-collapse="isCollapse" :logo-text-show="logoTextShow"/>
    </el-aside>

    <el-container>
      <el-header style="border-bottom: 1px solid #ccc;">
        <Header :collapse-btn-class="collapseBtnClass" :collapse="isCollapse"/>
      </el-header>

      <el-main>
        <!--  表示当前页面的子路由会在 <router-view/> 里面显示     -->
        <router-view/>
      </el-main>

    </el-container>
  </el-container>
</template>

<script>

import Aside from "@/components/Aside";
import Header from "@/components/Header";

export default {
  name: 'HomeView',
  components: {
    Aside,
    Header
  },
  data() {
    return {
      collapseBtnClass: 'el-icon-s-fold',
      isCollapse: false,
      sideWidth: 200,
      logoTextShow: true,
      headerBg: 'headerBg',
    }
  },
  methods: {
    collapse() {  // 点击收缩按钮触发
      this.isCollapse = !this.isCollapse
      if (this.isCollapse) {  // 收缩
        this.sideWidth = 64
        this.collapseBtnClass = 'el-icon-s-unfold'
        this.logoTextShow = false
      } else {   // 展开
        this.sideWidth = 200
        this.collapseBtnClass = 'el-icon-s-fold'
        this.logoTextShow = true
      }
    }
  }
}
</script>

<style>
.headerBg {
  background: #eee!important;
}
</style>

2.5 页面路由

指定路由所对应的页面

import Vue from 'vue'
import VueRouter from 'vue-router'
import Manage from '../views/Manage.vue'

Vue.use(VueRouter)
//定义一个路由对象数组
const routes = [
  {
    path: '/',
    component: Manage,
    // 重定向
    redirect: "/home",
    // 子组件
    children: [
      {
        // 注:子组件路径不要含/
        path: 'home',
        name: '首页',
        component: () => import('../views/Home.vue'),
        meta: {
          title: '首页'
        }
      },
      {
        path: 'user',
        name: '用户管理',
        component: () => import('../views/User.vue'),
        meta: {
          title: '用户管理'
        }
      }
    ]
  }
]
//使用路由对象数组创建路由实例,供main.js引用
const router = new VueRouter({
  mode: 'history',
  base: process.env.BASE_URL,
  routes
})

export default router

2.6 最终页面显示

在这里插入图片描述

3 难点

如何显示当前操作所在的目录
在这里插入图片描述

3.1 meta的使用

路由的index.js改动,添加meta属性

import Vue from 'vue'
import VueRouter from 'vue-router'
import Manage from '../views/Manage.vue'
import store from "@/store";

Vue.use(VueRouter)
//定义一个路由对象数组
const routes = [
  {
    path: '/',
    component: Manage,
    redirect: "/home",
    children: [
      {
        path: 'home',
        name: '首页',
        component: () => import('../views/Home.vue'),
        //添加meta属性
        meta: {
          title: '首页'
        }
      },
      {
        path: 'user',
        name: '用户管理',
        component: () => import('../views/User.vue'),
        //添加meta属性
        meta: {
          title: '用户管理'
        }
      }
    ]
  }
]
//使用路由对象数组创建路由实例,供main.js引用
const router = new VueRouter({
  mode: 'history',
  base: process.env.BASE_URL,
  routes
})
export default router

3.2 面包屑

主要步骤:
1. 第一步,获取相应的路由记录
2. 第二步,当当前路由发生变化时,更新路由记录
3. 注意for循环,添加:key=“value”,value唯一

<template>
  <div style="line-height: 60px; display: flex">
    <div style="flex: 1;">
      <span :class="collapseBtnClass" style="cursor: pointer; font-size: 18px" @click="collapse"></span>
      <el-breadcrumb separator=">" style="display: inline-block; margin-left: 10px">
        <el-breadcrumb-item :to="'/'">首页</el-breadcrumb-item>
        <el-breadcrumb-item v-for="(item, index) in breadCrumbs" :key="item.path">
          <router-link :to="item.path">{{ item.meta.title }}</router-link>
        </el-breadcrumb-item>
      </el-breadcrumb>
    </div>
    <el-dropdown style="width: 70px; cursor: pointer">
      <span>王小虎</span><i class="el-icon-arrow-down" style="margin-left: 5px"></i>
      <el-dropdown-menu slot="dropdown" style="width: 100px; text-align: center">
        <el-dropdown-item style="font-size: 14px; padding: 5px 0">个人信息</el-dropdown-item>
        <el-dropdown-item style="font-size: 14px; padding: 5px 0">退出</el-dropdown-item>
      </el-dropdown-menu>
    </el-dropdown>
  </div>
</template>

<script>
export default {
  name: "Header",
  props: {
    collapseBtnClass: String,
    collapse: Boolean,
  },
  // 当当前路由发生变化时,调用getBreadcrumb方法来更新面包屑导航的数据
  watch: {
    $route() {
      this.getBreadcrumb();
    }
  },
  data(){
    return {
      breadCrumbs: []
    }
  },
  created() {
    this.getBreadcrumb()
  },
  methods: {
    getBreadcrumb(){
      // 从当前路由的匹配记录中过滤出具有meta属性且包含title属性的路由记录
      this.breadCrumbs = this.$route.matched.filter(item => item.meta && item.meta.title);
    }
  }
}
</script>

<style scoped>

</style>

总结

  1. 学习vue组件化思想以及vue路由;
  2. 父组件如何向子组件传递数据,子组件如何接收数据;
  3. 本篇博客面包屑方法与原视频不同,不同方法实现同一个功能,可用来参考借鉴。

声明

项目源于此地址:程序员青戈

写在最后

如果此文对您有所帮助,请帅戈靓女们务必不要吝啬你们的Zan,感谢!!不懂的可以在评论区评论,有空会及时回复。
文章会一直更新

  • 39
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
SpringBootVue3商城项目是一种基于Java后端框架SpringBoot和前端框架Vue3开发的电商项目。 首先,SpringBoot是一个快速开发的Java后端框架,它提供了简化配置和快速集成的特性。它能够快速搭建一个稳定可靠的后端服务,并且可以与各种第三方库和组件进行集成,例如数据库、缓存、消息队列等。在商城项目中,SpringBoot可以作为后端服务提供接口供前端调用,处理用户登录、注册、商品列表、购物车等功能。 而Vue3是一款流行的前端框架,它提供了组件化开发、数据响应式、虚拟DOM等特性,使前端开发更加高效和可维护。在商城项目中,Vue3可以负责展示商品列表、购物车、用户订单等页面,并与后端接口进行交互,实现前后端数据的传输和展示。 在商城项目中,SpringBootVue3需要进行前后端的数据传输,可以使用RESTful API进行通信,通过HTTP协议传递JSON格式的数据。后端接收到前端的请求后,可以进行相应的业务逻辑处理,查询数据库、处理用户的购物请求等。前端则可以通过Ajax或者Axios等工具发起请求,并将后端返回的数据渲染到页面上。 此外,商城项目还可以使用一些其他的技术和工具,例如数据库可以选用MySQL、Redis等;前端可以使用ElementUI进行页面快速布局设计;图片上传可以使用七牛云等存储服务。通过使用这些技术和工具,可以快速搭建一个功能完善、用户友好的电商网站。 总之,SpringBootVue3商城项目是一种基于后端Java框架和前端JavaScript框架的电商网站开发方式,通过前后端的配合,实现商品展示、购物车管理、用户登录等功能。使用这种开发方式,可以大大提高项目的开发效率和用户体验。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值