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>
Member.vue

 

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
src\router\index.js

 

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>
src\pages\Main.vue

 

点击左侧的“会员信息管理”的菜单,运行效果如下:

 

二、动态查询数据


 

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/>'
})
src\main.js

 

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>
src\pages\Member.vue

 

完整的项目结构如下图所示:

 

 

 

运行效果如下:

 

 

 

其中,使用了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

 

如果你觉得我的博客对你有帮助,可以给我点儿打赏,左侧微信,右侧支付宝。

有可能就是你的一点打赏会让我的博客写的更好:)

posted @ 2018-02-09 17:53  冬子哥  阅读(10122)  评论(1编辑  收藏  举报