spring boot + vue + element-ui全栈开发入门——前端列表页面开发
一、页面
1.布局
假设,我们要开发一个会员列表的页面。
首先,添加vue页面文件“src\pages\Member.vue”
参照文档http://element.eleme.io/#/zh-CN/component/table中的例子,实现一个静态的列表页面
代码如下:
<template> <section> <!--工具条--> <el-col :span="24" class="toolbar" style="padding-bottom: 0px;"> <el-form :inline="true" :model="filters"> <el-form-item> <el-input v-model="filters.query" placeholder="姓名/手机号等条件" /> </el-form-item> <el-form-item> <el-button type="primary" v-on:click="getRows" icon="el-icon-search">查询</el-button> </el-form-item> <el-form-item> <el-button type="primary" v-on:click="handleAdd" icon="el-icon-plus">添加</el-button> </el-form-item> </el-form> </el-col> <el-table :data="rows" style="width: 100%" stripe border> <el-table-column label="注册日期" width="180"> <template slot-scope="scope"> <i class="el-icon-time"></i> <span style="margin-left: 10px">{{ scope.row.date }}</span> </template> </el-table-column> <el-table-column label="姓名" width="180" :show-overflow-tooltip="true"> <template slot-scope="scope"> <el-popover trigger="hover" placement="top"> <p>姓名: {{ scope.row.name }}</p> <p>住址: {{ scope.row.address }}</p> <div slot="reference" class="name-wrapper"> <el-tag size="medium">{{ scope.row.name }}</el-tag> </div> </el-popover> </template> </el-table-column> <el-table-column prop="sex" label="性别" width="100" align="center" :show-overflow-tooltip="true"> <template slot-scope="scope"> {{scope.row.sex===1?'男':'女'}} </template> </el-table-column> <el-table-column label="操作"> <template slot-scope="scope"> <el-button size="mini" type="primary" @click="handleEdit(scope.$index, scope.row)">编辑</el-button> <el-button size="mini" type="danger" @click="handleDelete(scope.$index, scope.row)">删除</el-button> </template> </el-table-column> </el-table> </section> </template> <script> let data = () => { return { filters: {}, rows: [] } } let handleAdd = function() { } let handleEdit = function(index, row) { console.log(index, row); } let handleDelete = function(index, row) { console.log(index, row); } let getRows = function() { this.rows = [] this.rows.push({ date: '2018-05-02', name: '王小虎1', sex: 1, address: '上海市普陀区金沙江路 1518 弄' }) this.rows.push({ date: '2018-05-04', name: '王小虎2', sex: 2, address: '上海市普陀区金沙江路 1517 弄' }) this.rows.push({ date: '2018-05-01', name: '王小虎3', sex: 2, address: '上海市普陀区金沙江路 1519 弄' }) this.rows.push({ date: '2018-05-03', name: '王小虎5', sex: 1, address: '上海市普陀区金沙江路 1516 弄' }) } export default { data: data, methods: { //添加 handleAdd, //修改 handleEdit, //删除 handleDelete, //获取分页 getRows }, mounted: function() { this.getRows() } } </script> <style scoped> </style>
2.修改路由
src\router\index.js文件中,添加
routes.push({ path: '/member', name: '会员管理', component: Main, iconCls: 'fa fa-user-circle-o', children: [{ path: '/member/data', component: Member, name: '会员信息管理' }] })
完整代码如下:
import Vue from 'vue' import Router from 'vue-router' Vue.use(Router) import Main from '@/pages/Main' import Dashboard from '@/pages/Dashboard' import Member from '@/pages/Member' let routes = [{ path: '/', component: Main, hidden: true, children: [{ path: '/', component: Dashboard, name: '首页' }] }] routes.push({ path: '/member', name: '会员管理', component: Main, iconCls: 'fa fa-user-circle-o', children: [{ path: '/member/data', component: Member, name: '会员信息管理' }] }) const router = new Router({ routes: routes }) export default router
3.修改首页,使其出现“会员管理”的菜单
<el-menu :default-active="$route.path" :collapse="collapsed"> <template v-for="(item,index) in menus"> <el-submenu :index="index+''" v-if="!item.leaf"> <template slot="title"><i :class="item.iconCls"></i><span v-if="!collapsed">{{item.name}}</span></template> <el-menu-item v-for="child in item.children" :index="child.path" :key="child.path" @click="$router.push(child.path)">{{child.name}}</el-menu-item> </el-submenu> <el-menu-item v-if="item.leaf&&item.children.length>0" :index="item.children[0].path"><i :class="item.iconCls"></i>{{item.children[0].name}}</el-menu-item> </template> </el-menu>
完整代码如下:
<template> <section> <el-container class="container"> <!--左边--> <el-aside :width="collapsed? '65px' : '200px' "> <el-container> <el-header> <span class="menu-button" v-if="collapsed" @click.prevent="collapsed=!collapsed"> <i class="fa fa-align-justify"></i> </span> <span v-else class="system-name">{{systemName}}</span> </el-header> <el-main> <el-menu :default-active="$route.path" :collapse="collapsed"> <template v-for="(item,index) in menus"> <el-submenu :index="index+''" v-if="!item.leaf"> <template slot="title"><i :class="item.iconCls"></i><span v-if="!collapsed">{{item.name}}</span></template> <el-menu-item v-for="child in item.children" :index="child.path" :key="child.path" @click="$router.push(child.path)">{{child.name}}</el-menu-item> </el-submenu> <el-menu-item v-if="item.leaf&&item.children.length>0" :index="item.children[0].path"><i :class="item.iconCls"></i>{{item.children[0].name}}</el-menu-item> </template> </el-menu> </el-main> </el-container> </el-aside> <!--内容--> <el-container> <!--页眉--> <el-header class="header"> <el-row> <el-col :span="18" class="header-title"> <span v-if="collapsed" class="system-name">{{systemName}}</span> <span v-else class="menu-button" @click.prevent="collapsed=!collapsed"> <i class="fa fa-align-justify"></i> </span> </el-col> <el-col :span="6"><span class="el-dropdown-link userinfo-inner">你好:{{userName}}</span></el-col> </el-row> </el-header> <!--中间--> <el-main class="main"> <transition name="fade" mode="out-in"> <router-view></router-view> </transition> </el-main> </el-container> </el-container> </section> </template> <script> let data = () => { return { collapsed: false, systemName: '后台管理', userName: '系统管理员', menus: [] } } let initMenu = function() { for (let i in this.$router.options.routes) { let root = this.$router.options.routes[i] if (root.hidden) continue let children = [] for (let j in root.children) { let item = root.children[j] if (item.hidden) continue children.push(item) } if (children.length < 1) continue this.menus.push(root) root.children = children } } export default { data: data, methods: { initMenu }, mounted: function() { this.initMenu() } } </script> <style scoped="scoped" lang="scss"> $width: 100%; $height: 100%; $background-color: #0b0a3e; $header-color: #fff; $header-height: 60px; .container { position: absolute; top: 0; bottom: 0; width: 100%; .el-aside { .el-header { line-height: $header-height; background-color: $background-color; color: $header-color; text-align: center; } .el-container { height: $height; .el-main { padding: 0; } } } .main { width: $width; height: $height; } .menu-button { width: 14px; cursor: pointer; } .userinfo-inner { cursor: pointer; } .el-menu { height: $height; } .header { background-color: $background-color; color: $header-color; text-align: center; line-height: $header-height; padding: 0; .header-title { text-align: left; span { padding: 0 20px; } } } .system-name { font-size: large; font-weight: bold; } } </style>
点击左侧的“会员信息管理”的菜单,运行效果如下:
二、动态查询数据
1.mock.js
添加文件:src\mock\member.js
import Mock from 'mockjs' let adapters = [] adapters.push( (mockAdapter) => mockAdapter.onPost('/api/member/loadPage').reply(req => { let promise = new Promise((resolve, reject) => { let data = req.data ? JSON.parse(req.data) : { size: 20 } let result = { rows: [], total: 10000 } for (let i = 0; i < data.size; i++) { let item = Mock.mock({ id: Mock.Random.guid(), name: Mock.Random.cname(), sex: Mock.Random.integer(1, 2), 'age|18-30': 1, date: Mock.Random.date(), address: Mock.mock('@county(true)'), }) result.rows.push(item) } setTimeout(() => { resolve([200, result]) }, 2000) }) return promise }) ) export { adapters }
添加src\mock\index.js文件
import axios from 'axios' import MockAdapter from 'axios-mock-adapter' let mockAdapter = new MockAdapter(axios) //会员api import { adapters as member } from '@/mock/member.js' member.forEach(item => item(mockAdapter)) export default mockAdapter
2.修改main.js文件
在main.js中导入mock.js
//开发模式开启mock.js if (process.env.NODE_ENV === 'development') { require('./mock') }
导入axios
import axios from 'axios'
Vue.prototype.$axios = axios
完整main.js代码:
// The Vue build version to load with the `import` command // (runtime-only or standalone) has been set in webpack.base.conf with an alias. import Vue from 'vue' import App from './App' import router from './router' Vue.config.productionTip = false import 'font-awesome/css/font-awesome.min.css' import ElementUI from 'element-ui' import './assets/theme/element-#0b0a3e/index.css' Vue.use(ElementUI) //开发模式开启mock.js if (process.env.NODE_ENV === 'development') { require('./mock') } import axios from 'axios' Vue.prototype.$axios = axios /* eslint-disable no-new */ new Vue({ el: '#app', router, components: { App }, template: '<App/>' })
3.修改Member.vue文件
修改查询分页的方法:
let getRows = function() { if (this.pageLoading) return this.pageLoading = true let params = { page: this.page, size: this.size, query: this.filters.query } //调用post请求 this.$axios.post('/api/member/loadPage', params).then(res => { this.pageLoading = false if (!res.data || !res.data.rows) return //总数赋值 this.total = res.data.total this.page++; //页面元素赋值 this.rows = res.data.rows }).catch(e => this.pageLoading = false) }
完整的Member.vue代码如下:
<template> <section> <!--工具条--> <el-col :span="24" class="toolbar" style="padding-bottom: 0px;"> <el-form :inline="true" :model="filters"> <el-form-item> <el-input v-model="filters.query" placeholder="姓名/手机号等条件" /> </el-form-item> <el-form-item> <el-button type="primary" v-on:click="handleQuery" icon="el-icon-search">查询</el-button> </el-form-item> <el-form-item> <el-button type="primary" v-on:click="handleAdd" icon="el-icon-plus">添加</el-button> </el-form-item> </el-form> </el-col> <el-table :data="rows" style="width: 100%;overflow: auto;" :height="clientHeight" stripe border highlight-current-row v-loading="pageLoading"> <el-table-column label="注册日期" width="180"> <template slot-scope="scope"> <i class="el-icon-time"></i> <span style="margin-left: 10px">{{ scope.row.date }}</span> </template> </el-table-column> <el-table-column label="姓名" width="180" :show-overflow-tooltip="true"> <template slot-scope="scope"> <el-popover trigger="hover" placement="top"> <p>姓名: {{ scope.row.name }}</p> <p>住址: {{ scope.row.address }}</p> <div slot="reference" class="name-wrapper"> <el-tag size="medium">{{ scope.row.name }}</el-tag> </div> </el-popover> </template> </el-table-column> <el-table-column prop="sex" label="性别" width="100" align="center" :show-overflow-tooltip="true"> <template slot-scope="scope"> {{scope.row.sex===1?'男':'女'}} </template> </el-table-column> <el-table-column label="操作"> <template slot-scope="scope"> <el-button size="mini" type="primary" @click="handleEdit(scope.$index, scope.row)"><i class="el-icon-edit"></i>编辑</el-button> <el-button size="mini" type="danger" @click="handleDelete(scope.$index, scope.row)"><i class="el-icon-delete"></i>删除</el-button> </template> </el-table-column> </el-table> <!--底部--> <el-col :span="24" class="toolbar"> <el-pagination layout="prev, pager, next" @current-change="handleCurrentChange" :page-size="20" :total="total" style="float:right;"> </el-pagination> </el-col> </section> </template> <script> let data = () => { return { //页码 page: 1, //每页数量 size: 20, //总数 total: 0, //查询条件 filters: {}, //页面数据 rows: [], //页面载入状态 pageLoading: false, //列表高度 clientHeight: '100%', } } let handleAdd = function() { } let handleEdit = function(index, row) { } let handleDelete = function(index, row) { } let getRows = function() { if (this.pageLoading) return this.pageLoading = true let params = { page: this.page, size: this.size, query: this.filters.query } //调用post请求 this.$axios.post('/api/member/loadPage', params).then(res => { this.pageLoading = false if (!res.data || !res.data.rows) return //总数赋值 this.total = res.data.total this.page++; //页面元素赋值 this.rows = res.data.rows }).catch(e => this.pageLoading = false) } let handleQuery = function() { this.page = 1 this.getRows() } let handleCurrentChange = function(val) { this.page = val this.getRows() } let initHeight = function() { this.clientHeight = (document.documentElement.clientHeight - 258) + 'px' } export default { data: data, methods: { //查询 handleQuery, //添加 handleAdd, //修改 handleEdit, //删除 handleDelete, //页数改变 handleCurrentChange, //获取分页 getRows, //初始化高度 initHeight }, mounted: function() { window.addEventListener('resize', this.initHeight) this.initHeight() this.getRows() } } </script> <style scoped> </style>
完整的项目结构如下图所示:
运行效果如下:
其中,使用了axios来调用http post协议,url是'/api/member/loadPage',并post了body参数。
但我并没有写任何后端代码。奇怪的是,获取的数据从哪里来?答案是:mock.js,因为使用了mock.js+axios-mock-adapter来拦截并模拟http协议。
git代码地址:https://github.com/carter659/spring-boot-vue-element.git
如果你觉得我的博客对你有帮助,可以给我点儿打赏,左侧微信,右侧支付宝。
有可能就是你的一点打赏会让我的博客写的更好:)
作者:刘冬.NET
博客地址:http://www.cnblogs.com/GoodHelper/
欢迎转载,但须保留版权