【admin-mini】封装table列表 vue3手摸手

146 阅读2分钟

logo-mini.jpg

本专栏为了还在Vue2x的广大同胞提供一点升级3X的借鉴思路

适用人群

  • Vue前端开发者

阅读条件

  • 撸一遍Vue3文档

收获

  • 纯净的Vue3-admin框架

如果你是一名成熟的开发工程师,可以直接访问成品

gitee

github


【admin-mini】封装table列表

一个标准的列表页,至少需要包含,搜索条件、分页配置、列表数据、分页切换函数、分页长度切换函数等等

如果在加上各种新增、删除、编辑、绑定等等操作

维护难度可想而知

示例

<template>
    <div class="admin-view">
        <div class="admin-view-header">
            <admin-bread></admin-bread>
        </div>
        <div class="admin-view-search">
            <el-form ref="queryFrom" @submit.prevent="$table.search" :inline="true" :model="$table.query">
                <el-form-item label="帐号" prop="username" :rules="[$rules.required]">
                    <el-input v-model="$table.query.username"></el-input>
                </el-form-item>
                <el-form-item label="下拉">
                    <select-dict :dict="$dict.userType" show-all></select-dict>
                </el-form-item>
                <el-form-item label="时间范围">
                    <date-picker-range v-model:begin="$table.query.beginTime"
                        v-model:end="$table.query.endTime"></date-picker-range>
                </el-form-item>
                <el-form-item>
                    <el-button native-type="submit" type="primary" :loading="$table.loading">搜索</el-button>
                </el-form-item>
            </el-form>
        </div>
        <div class="admin-view-body">
            <div class="admin-table-btns">
                <el-button @click="openAdd" type="primary" icon="plus">新增</el-button>
            </div>
            <div class="admin-table-list">
                <el-table border :data="$table.data" :empty-text="$table.emptyText" v-loading="$table.loading"
                    table-layout="auto">
                    <el-table-column prop="username" label="帐号" />
                    <el-table-column prop="userType" label="类型">
                        <template #default="scope">
                            <el-tag :type="scope.row.userTypeObj.color">
                                {{ scope.row.userTypeObj.label }}
                            </el-tag>

                        </template>
                    </el-table-column>
                    <el-table-column prop="nickName" label="用户姓名" />
                    <el-table-column prop="userType" label="类型" :formatter="$formatter.dict($dict.userType)" />
                    <el-table-column prop="loginTime" label="登陆时间" :formatter="$formatter.date(true)" />
                    <el-table-column prop="createTime" label="登陆时间" :formatter="$formatter.date()" />
                    <el-table-column label="操作" width="400px">
                        <template #default="scope">
                            <el-space spacer="|">
                                <el-link :disabled="scope.row.loading" type="primary"
                                    @click="openEdit(scope.row)">编辑</el-link>
                                <el-link :disabled="scope.row.loading" type="primary"
                                    @click="openDel(scope.row)">删除</el-link>
                                <el-link :disabled="scope.row.loading" type="primary"
                                    @click="openPassword(scope.row)">修改密码</el-link>
                            </el-space>
                        </template>
                    </el-table-column>
                </el-table>
                <div class="admin-pager-container">
                    <el-pagination :layout="$table.pageLayout" background v-model:current-page="$table.query.page"
                        :page-size="$table.query.size" :total="$table.total" @size-change="$table.handleSizeChange"
                        @current-change="$table.handleCurrentChange" />
                </div>
            </div>
        </div>
    </div>
</template>
<script  setup>
import { userList } from '@/api'
import { reactive, onMounted } from 'vue'
import { ElMessage, } from 'element-plus'
const rules = reactive({});
const $table = reactive({
    pageLayout: 'total, sizes, prev, pager, next, jumper',
    emptyText: '暂无数据',
    data: [],
    total: 0,
    loading: false,

    query: {
        page: 1,
        size: 20
    },
    handleCurrentChange: handleCurrentChange,
    handleSizeChange: handleSizeChange,
    getTable: getTable,
    search: search,
    rules: rules,
    ref: null, //如果指定会自动执行validate校验表单
    parse: (data) => data.data, //格式化返回数据
    parseQuery: (query) => query //格式化请求数据
})

onMounted(function () {
    search()
})

function handleSizeChange(size) {
    $table.query.size = size
    search()
}
function handleCurrentChange(p) {
    $table.query.page = p
    getTable()
}
function getTable() {
    if ($table.ref) {
        $table.ref.validate((valid) => {
            if (valid) {
                _getTable()
            }
        })
    } else {
        _getTable()
    }
}
function _getTable() {

    $table.loading = true
    $table.emptyText = '加载中...'
    let query = $table.parseQuery(JSON.parse(JSON.stringify($table.query)))
    userList(query)
        .then((res) => {
            if (res.data.code == 0) {
                let rows = res.data.data
                $table.data = rows
                $table.total = parseInt(res.data.total) || 0
            } else {
                throw new Error('接口错误')
            }
        })
        .catch((err) => {
            console.error(err)
            ElMessage.error(err || '接口错误')
        })
        .finally(() => {
            $table.loading = false
            $table.emptyText = '暂无数据'
        })
}
function search() {
    $table.query.page = 1
    getTable()
}
</script>
<style></style>
  

精简!

把通用逻辑封装成一个VUE3插件,即可完成蜕变

示例

<template>
  ...html部分同上
</template>
<script  setup>
import { userList } from '@/api'
import useAdminTable from '@/plugins/use-admin-table'
import dict from "@/utils/dict"
const $table = useAdminTable({api:userList})
</script>
<style></style>

封装后,仅需一行代码即可实现简单的列表页

当然,为了保证灵活性,还提需要提供一些可配置项

  • $table.parse 用于格式化响应数据
  • $table.parseQuery 用于格式化请求数据
  • $table.api 修改列表api函数
  • $table下所有属性均可diy

实现代码

import { reactive, onMounted } from 'vue'

import { ElMessage } from 'element-plus'
export default function useAdminTable(options) {
  const rules = reactive({})
  const $table = reactive({
    pageLayout: 'total, sizes, prev, pager, next, jumper',
    emptyText: '暂无数据',
    data: [],
    total: 0,
    loading: false,
    api: undefined,
    query: {
      page: 1,
      size: 20
    },
    handleCurrentChange: handleCurrentChange,
    handleSizeChange: handleSizeChange,
    getTable: getTable,
    search: search,
    rules: rules,
    ref: null, //如果指定会自动执行validate校验表单
    parse: (data) => data.data, //格式化返回数据
    parseQuery: (query) => query, //格式化请求数据
    autoSearch: true //是否在mounted周期中自动调用search
  })
  Object.assign($table, options)

  onMounted(function () {
    if (!$table.autoSearch) {
      return false
    }
    if ($table.api) {
      search()
    }
  })

  function handleSizeChange(size) {
    $table.query.size = size
    search()
  }
  function handleCurrentChange(p) {
    $table.query.page = p
    getTable()
  }
  function getTable() {
    if ($table.ref) {
      $table.ref.validate((valid) => {
        if (valid) {
          _getTable()
        }
      })
    } else {
      _getTable()
    }
  }
  function _getTable() {
    if (!$table.api) {
      return false
    }
    $table.loading = true
    $table.emptyText = '加载中...'
    let query = $table.parseQuery(JSON.parse(JSON.stringify($table.query)))
    $table
      .api(query)
      .then((res) => {
        if (res.data.code == 0) {
          let rows = res.data.data
          rows = $table.parse(res.data)
          $table.data = rows
          $table.total = parseInt(res.data.total) || 0
        } else {
          throw new Error('接口错误')
        }
      })
      .catch((err) => {
        console.error(err)
        ElMessage.error(err || '接口错误')
      })
      .finally(() => {
        $table.loading = false
        $table.emptyText = '暂无数据'
      })
  }
  function search() {
    $table.query.page = 1
    getTable()
  }

  return $table
}

useAdminTable.install = function (app) {
  void app
}