实现功能
    2.gif

    前段请求
    image.png
    后端接口:http://localhost:8000/api/v1/articles?page=2&limit=10

    1. {
    2. "code": 200,
    3. "data": {
    4. "lists": [
    5. {
    6. "id": 21,
    7. "created_on": "2019-08-19T21:00:39+08:00",
    8. "modified_on": "2019-08-19T21:00:39+08:00",
    9. "state": 0,
    10. "tag_id": 1,
    11. "title": "test1",
    12. "desc": "test-desc",
    13. "Content": "test-content",
    14. "cover_image_url": "",
    15. "created_by": "test-created",
    16. "tag": {
    17. "id": 0,
    18. "created_on": "0001-01-01T00:00:00Z",
    19. "modified_on": "0001-01-01T00:00:00Z",
    20. "name": "",
    21. "created_by": "",
    22. "modified_by": "",
    23. "state": 0
    24. }
    25. },
    26. ...//省略
    27. {
    28. "id": 41555,
    29. "created_on": "2019-08-19T21:00:39+08:00",
    30. "modified_on": "2019-08-19T21:00:39+08:00",
    31. "state": 0,
    32. "tag_id": 1,
    33. "title": "test1",
    34. "desc": "test-desc",
    35. "Content": "test-content",
    36. "cover_image_url": "",
    37. "created_by": "test-created",
    38. "tag": {
    39. "id": 0,
    40. "created_on": "0001-01-01T00:00:00Z",
    41. "modified_on": "0001-01-01T00:00:00Z",
    42. "name": "",
    43. "created_by": "",
    44. "modified_by": "",
    45. "state": 0
    46. }
    47. }
    48. ],
    49. "total": 22
    50. },
    51. "msg": "ok"
    52. }

    数据库
    image.png
    DDL

    CREATE TABLE `blog_article` (
      `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
      `tag_id` int(10) unsigned DEFAULT '0' COMMENT '标签ID',
      `title` varchar(100) DEFAULT '' COMMENT '文章标题',
      `desc` varchar(255) DEFAULT '' COMMENT '简述',
      `content` text COMMENT '内容',
      `cover_image_url` varchar(255) DEFAULT '' COMMENT '封面图片地址',
      `created_on` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
      `created_by` varchar(100) DEFAULT '' COMMENT '创建人',
      `modified_on` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',
      `modified_by` varchar(255) DEFAULT '' COMMENT '修改人',
      `deleted_on` int(10) unsigned DEFAULT '0' COMMENT '删除时间',
      `state` tinyint(3) unsigned DEFAULT '1' COMMENT '状态 0为草稿、1为已发布、2为删除',
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=4155556 DEFAULT CHARSET=utf8 COMMENT='文章管理';
    

    1.src\router\index.js添加路由

      {
        path: '/article',
        component: Layout,
        children: [
          {
            path: 'index',
            name: 'Article',
            component: () => import('@/views/article/index'),
            meta: { title: '文章管理', icon: 'form' }
          }
        ]
      },
    

    2.新建src\views\article\index.vue

    <template>
      <div class="app-container">
        <el-table v-loading="listLoading" :data="list" border fit highlight-current-row style="width: 100%">
          <el-table-column align="center" label="ID" width="80">
            <template slot-scope="scope">
              <span>{{ scope.row.id }}</span>
            </template>
          </el-table-column>
    
          <el-table-column width="180px" align="center" label="Date">
            <template slot-scope="scope">
              <span>{{ scope.row.created_on | parseTime('{y}-{m}-{d} {h}:{i}') }}</span>
            </template>
          </el-table-column>
    
          <el-table-column width="120px" align="center" label="作者">
            <template slot-scope="scope">
              <span>{{ scope.row.created_by }}</span>
            </template>
          </el-table-column>
    
          <el-table-column width="100px" label="Importance">
            <template slot-scope="scope">
              <!-- <svg-icon v-for="n in +scope.row.tag_id" :key="n" icon-class="star" class="meta-item__icon" /> -->
              <i v-for="n in +scope.row.tag_id" :key="n" class="el-icon-star-on" />
            </template>
          </el-table-column>
    
          <el-table-column class-name="state-col" label="Status" width="110">
            <template slot-scope="{row}">
              <el-tag :type="row.state | statusFilter">
                {{ row.state == 1?'发布':'草稿' }}
              </el-tag>
            </template>
          </el-table-column>
    
          <el-table-column min-width="300px" label="Title">
            <template slot-scope="{row}">
              <router-link :to="'/form/edit/'+row.id" class="link-type">
                <span>{{ row.title }}</span>
              </router-link>
            </template>
          </el-table-column>
    
          <el-table-column align="center" label="Actions" width="120">
            <template slot-scope="scope">
              <router-link :to="'/form/edit/'+scope.row.id">
                <el-button type="primary" size="small" icon="el-icon-edit">
                  编辑
                </el-button>
              </router-link>
            </template>
          </el-table-column>
        </el-table>
    
        <pagination v-show="total>0" :total="total" :page.sync="listQuery.page" :limit.sync="listQuery.limit" @pagination="getList" />
      </div>
    </template>
    <script>
    import { parseTime } from '@/utils'
    import { fetchList } from '@/api/article'
    import Pagination from '@/components/Pagination'
    export default {
      name: 'ArticleList',
      components: { Pagination },
      filters: {
        statusFilter(state) {
          const statusMap = {
            1: 'success',
            0: 'info',
            2: 'danger'
          }
          return statusMap[state]
        },
        parseTime: parseTime
      },
      data() {
        return {
          list: null,
          total: 0,
          listLoading: true,
          listQuery: {
            page: 1,
            limit: 10
          }
        }
      },
      created() {
        this.getList()
      },
      methods: {
        getList() {
          this.listLoading = true
          fetchList(this.listQuery).then(response => {
            console.log(response.data)
            this.list = response.data.lists
            this.total = response.data.total
            this.listLoading = false
          })
        }
      }
    }
    </script>
    
    <style>
    
    </style>
    

    3.新建公共依赖文件src\utils\index.js
    实际是https://github.com/PanJiaChen/vue-element-admin下的src\utils\index.js

    /**
     * Created by PanJiaChen on 16/11/18.
     */
    
    /**
     * Parse the time to string
     * @param {(Object|string|number)} time
     * @param {string} cFormat
     * @returns {string}
     */
    export function parseTime(time, cFormat) {
      if (arguments.length === 0) {
        return null
      }
      const format = cFormat || '{y}-{m}-{d} {h}:{i}:{s}'
      let date
      if (typeof time === 'object') {
        date = time
      } else {
        if ((typeof time === 'string') && (/^[0-9]+$/.test(time))) {
          time = parseInt(time)
        }
        if ((typeof time === 'number') && (time.toString().length === 10)) {
          time = time * 1000
        }
        date = new Date(time)
      }
      const formatObj = {
        y: date.getFullYear(),
        m: date.getMonth() + 1,
        d: date.getDate(),
        h: date.getHours(),
        i: date.getMinutes(),
        s: date.getSeconds(),
        a: date.getDay()
      }
      const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => {
        let value = formatObj[key]
        // Note: getDay() returns 0 on Sunday
        if (key === 'a') { return ['日', '一', '二', '三', '四', '五', '六'][value ] }
        if (result.length > 0 && value < 10) {
          value = '0' + value
        }
        return value || 0
      })
      return time_str
    }
    
    /**
     * @param {number} time
     * @param {string} option
     * @returns {string}
     */
    export function formatTime(time, option) {
      if (('' + time).length === 10) {
        time = parseInt(time) * 1000
      } else {
        time = +time
      }
      const d = new Date(time)
      const now = Date.now()
    
      const diff = (now - d) / 1000
    
      if (diff < 30) {
        return '刚刚'
      } else if (diff < 3600) {
        // less 1 hour
        return Math.ceil(diff / 60) + '分钟前'
      } else if (diff < 3600 * 24) {
        return Math.ceil(diff / 3600) + '小时前'
      } else if (diff < 3600 * 24 * 2) {
        return '1天前'
      }
      if (option) {
        return parseTime(time, option)
      } else {
        return (
          d.getMonth() +
          1 +
          '月' +
          d.getDate() +
          '日' +
          d.getHours() +
          '时' +
          d.getMinutes() +
          '分'
        )
      }
    }
    
    /**
     * @param {string} url
     * @returns {Object}
     */
    export function param2Obj(url) {
      const search = url.split('?')[1]
      if (!search) {
        return {}
      }
      return JSON.parse(
        '{"' +
          decodeURIComponent(search)
            .replace(/"/g, '\\"')
            .replace(/&/g, '","')
            .replace(/=/g, '":"')
            .replace(/\+/g, ' ') +
          '"}'
      )
    }
    

    4.新建src\components\Pagination\index.vue分页组件
    实际是https://github.com/PanJiaChen/vue-element-admin下的src\components\Pagination\index.vue

    <template>
      <div :class="{'hidden':hidden}" class="pagination-container">
        <el-pagination
          :background="background"
          :current-page.sync="currentPage"
          :page-size.sync="pageSize"
          :layout="layout"
          :page-sizes="pageSizes"
          :total="total"
          v-bind="$attrs"
          @size-change="handleSizeChange"
          @current-change="handleCurrentChange"
        />
      </div>
    </template>
    
    <script>
    import { scrollTo } from '@/utils/scroll-to'
    
    export default {
      name: 'Pagination',
      props: {
        total: {
          required: true,
          type: Number
        },
        page: {
          type: Number,
          default: 1
        },
        limit: {
          type: Number,
          default: 10
        },
        pageSizes: {
          type: Array,
          default() {
            return [10, 20, 30, 50]
          }
        },
        layout: {
          type: String,
          default: 'total, sizes, prev, pager, next, jumper'
        },
        background: {
          type: Boolean,
          default: true
        },
        autoScroll: {
          type: Boolean,
          default: true
        },
        hidden: {
          type: Boolean,
          default: false
        }
      },
      computed: {
        currentPage: {
          get() {
            return this.page
          },
          set(val) {
            this.$emit('update:page', val)
          }
        },
        pageSize: {
          get() {
            return this.limit
          },
          set(val) {
            this.$emit('update:limit', val)
          }
        }
      },
      methods: {
        handleSizeChange(val) {
          this.$emit('pagination', { page: this.currentPage, limit: val })
          if (this.autoScroll) {
            scrollTo(0, 800)
          }
        },
        handleCurrentChange(val) {
          this.$emit('pagination', { page: val, limit: this.pageSize })
          if (this.autoScroll) {
            scrollTo(0, 800)
          }
        }
      }
    }
    </script>
    
    <style scoped>
    .pagination-container {
      background: #fff;
      padding: 32px 16px;
    }
    .pagination-container.hidden {
      display: none;
    }
    </style>
    

    5.分页组件依赖src\utils\scroll-to.js

    Math.easeInOutQuad = function(t, b, c, d) {
      t /= d / 2
      if (t < 1) {
        return c / 2 * t * t + b
      }
      t--
      return -c / 2 * (t * (t - 2) - 1) + b
    }
    
    // requestAnimationFrame for Smart Animating http://goo.gl/sx5sts
    var requestAnimFrame = (function() {
      return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function(callback) { window.setTimeout(callback, 1000 / 60) }
    })()
    
    /**
     * Because it's so fucking difficult to detect the scrolling element, just move them all
     * @param {number} amount
     */
    function move(amount) {
      document.documentElement.scrollTop = amount
      document.body.parentNode.scrollTop = amount
      document.body.scrollTop = amount
    }
    
    function position() {
      return document.documentElement.scrollTop || document.body.parentNode.scrollTop || document.body.scrollTop
    }
    
    /**
     * @param {number} to
     * @param {number} duration
     * @param {Function} callback
     */
    export function scrollTo(to, duration, callback) {
      const start = position()
      const change = to - start
      const increment = 20
      let currentTime = 0
      duration = (typeof (duration) === 'undefined') ? 500 : duration
      var animateScroll = function() {
        // increment the time
        currentTime += increment
        // find the value with the quadratic in-out easing function
        var val = Math.easeInOutQuad(currentTime, start, change, duration)
        // move the document.body
        move(val)
        // do the animation unless its over
        if (currentTime < duration) {
          requestAnimFrame(animateScroll)
        } else {
          if (callback && typeof (callback) === 'function') {
            // the animation is done so lets callback
            callback()
          }
        }
      }
      animateScroll()
    }
    

    6.新建src\api\article.js文件调用axios

    import request from '@/utils/request'
    
    export function fetchList(params) {
      return request({
        url: '/api/v1/articles',
        method: 'get',
        params
      })
    }