1、目的
- 为啥 需要将这边拿出来?
- 做个记录
- 帮助其他人 更多的学习思路
ps: 基于 element ui(需在项目中引入) 注意 更多 展示 思路, 非直接 拷过去使用
, 非完美版本 , 还有很多优化点 ,仅供参考
2、data-table
- 主要 是
三部分 内容 一部分 搜索, 另一部分 表单展示 , 还有部分 翻页
- 主要 组件展示
- index.vue
<template> <div class="data-table"> <Search v-if="searchItems.length > 0" @search="handleSearch" ref="search-form" :formItems="searchItems" /> <mk-table ref="mk-table" :loading="loading" :columns="columns" @radio-change="radioChange" :data="data" @selection-change="selectionChange" @sort-change="onSortChange" :row-key="rowKey" :span-method="spanMethod" > </mk-table> <el-pagination transfer class="page-table layout-footer" placement="top" :total="total" :page-size="pageSize" :current="page" showElevator showTotal showSizer :page-size-opts="[10, 20, 30, 40, 50, 100]" @on-change="handlePageChange" @on-page-size-change="handlePageSizeChange" ></el-pagination> </div> </template> <script> import MkTable from '@/components/mk-table' import Search from './search.vue' export default { components: { Search, MkTable }, props: { columns: { type: Array, default: () => [], }, func: { type: [Function, Object], }, spanMethod: { type: Function, }, // 切换页数是否是偏移逻辑 isOffset: { type: Boolean, default: false, }, // 分页是否从0开始 isFromZero: { type: Boolean, default: false, }, props: { type: Object, default() { return { total: 'total', page: 'page', pageSize: 'pageSize', list: 'data', } }, }, // 额外参数 extraQuery: { type: Object, default: () => ({}), }, searchItems: { type: Array, default: () => [], }, rowKey: { type: [Function, String], }, }, data() { return { data: [], total: 0, page: 1, pageSize: 10, loading: false, saveQuery: {}, reqFunc: null, canDo: false, } }, watch: { func: { handler(v) { if (!v) return this.reqFunc = v // 有搜索项时 handleSearch会默认执行 if (this.searchItems.length === 0 || this.canDo) this.getList() }, immediate: true, }, }, computed: { hasReserveSelection() { return !!this.columns.find((v) => v && v.type === 'selection' && v['reserve-selection']) }, }, methods: { handleSearch(v) { this.$emit('searchChange', v) this.page = 1 this.saveQuery = { ...v } // 默认执行 this.getList() // 有搜索且初始化请求函数未赋值时 watch请求函数变化时继续请求列表 this.canDo = !this.reqFunc }, async getList(params = {}) { if (!this.reqFunc) return this.loading = true const { page = 'page', pageSize = 'pageSize', list = 'data', total = 'total' } = this.props // 设置页面和数量 this.page = params.page || this.page this.pageSize = params.pageSize || this.pageSize Reflect.deleteProperty(params, 'page') Reflect.deleteProperty(params, 'pageSize') const obj = { ...this.saveQuery, ...this.extraQuery, ...params } // 兼容后端偏移量逻辑 if (this.isOffset) { obj[page] = (this.page - 1) * this.pageSize } else { obj[page] = this.isFromZero ? this.page - 1 : this.page } obj[pageSize] = this.pageSize try { const { data } = await this.reqFunc(obj) if(!data[list]) data[list] = [] // 兼容列表不存在的情况 // 处理临界条件 if (data[list].length === 0 && this.page > 1) { this.page -= 1 this.getList() } this.$set(this, 'data', data[list]) this.total = Number(data[total]) // Number转换->兼容后台int64返回是字符串 } finally { this.loading = false // 重新请求时保留选中不清空 !this.hasReserveSelection && this.$emit('update:selected', []) } }, // 重置搜索后获取列表 searchList(v) { this.$refs['search-form'].initSearch(v) }, handlePageChange(page) { this.page = page this.getList() }, handlePageSizeChange(pageSize) { this.page = 1 this.pageSize = pageSize this.getList() }, clearSelection() { this.$refs['mk-table'].$refs['mk-table'].clearSelection() }, selectionChange(v) { this.$emit('update:selected', v) }, onSortChange(v) { this.$emit('on-sort-change', v) }, radioChange(v) { this.$emit('radio-change', v) }, }, } </script> 复制代码
- Search.vue
<Form class="search-form" ref="seachForm" :model="form" inline label-position="left"> <FormItem v-for="item in formItems" :key="item.prop" :prop="item.prop" :label="item.label" :label-width="item.labelWidth || 100"> <el-input v-if="item.itemType === 'input'" :type="item.type || 'text'" v-model="form[item.prop]" clearable v-bind="item" :placeholder="item.placeholder || '请输入'" /> <Select style="width: 150px" v-if="item.itemType === 'select'" :placeholder="item.placeholder || '请选择'" v-model="form[item.prop]" v-bind="item" :clearable="item.clearable == undefined ? true : item.clearable" > <Option v-for="option in item.options" :key="option.value" :value="option.value"> {{ option.label }}</Option> </Select> <DatePicker v-if="item.itemType === 'date-picker'" v-model="form[item.prop]" v-bind="item" :placeholder="item.placeholder || '请选择'" /> <el-cascader v-if="item.itemType === 'cascader'" style="width: 250px" v-bind="item" :clearable="item.clearable == undefined ? true : item.clearable" :filterable="item.filterable == undefined ? true : item.filterable" :collapse-tags="item['collapse-tags'] == undefined ? true : item['collapse-tags']" size="small" :placeholder="item.placeholder || '可搜索选择'" v-model="form[item.prop]" :props="item.props || { multiple: true, emitPath: false }" :options="item.options" ></el-cascader> </FormItem> <FormItem> <Button type="primary" @click="search" icon="search">搜索</Button> <Button type="ghost" @click="reset">重置</Button> </FormItem> </Form> </template> <script> export default { props: { formItems: { type: Array, default: () => [], }, }, data() { return { form: {}, outputEnum: {}, } }, created() { this.initSearch() }, methods: { initSearch(init = {}) { this.form = this.formItems.reduce((pre, cur) => { if (cur.output) { this.outputEnum[cur.prop] = { output: cur.output, outputMul: cur.outputMul || cur.type === 'daterange', } } pre[cur.prop] = Reflect.has(init, cur.prop) ? init[cur.prop] : cur.default === undefined ? '' : cur.default return pre }, {}) this.search() }, search() { const form = Object.entries(this.form).reduce((pre, [key, value]) => { if (typeof value === 'string') { value = value.trim() // 排除掉空数据筛选 if (!value) return pre } // 不需要格式化输出 if (!this.outputEnum[key]) { pre[key] = value return pre } // 格式化输出 if (this.outputEnum[key].outputMul) { pre = { ...pre, ...this.outputEnum[key].output(value) } } else { pre[key] = this.outputEnum[key].output(value) } return pre }, {}) this.$emit('search', form) }, async reset() { await this.$refs['seachForm'].resetFields() this.search() }, }, } </script> <style lang="less" scoped> .search-form { margin: 20px 0 0 16px; display: flex; justify-content: center; flex-wrap: wrap; } </style> 复制代码
- Mk.vue
<template> <el-table ref="mk -table" v-loading="loading" v-bind="$attrs" :data="dataValue" :row-key="rowKey || '_expandedKey'" :expand-row-keys="expandRowKeys || ttEpdRowKeys" v-on="$listeners" :row-class-name="setClassName" :header-cell-style="{ 'background-color': '#f8f8f9', color: '#606571' }" > <template v-for="(item, index) in columns"> <!-- 兼容computed columns --> <template v-if="item"> <el-table-column v-if="item.type === 'radio'" :key="`${item.key || item.type || 'key'}-${index}`" :prop="item.key" :label="item.title" v-bind="item" > <template slot-scope="scope"> <pf-checkbox @on-change="radioChange(scope)" v-model="scope.row._radioChecked"></pf-checkbox> </template> </el-table-column> <el-table-column v-if="item.render" :prop="item.key" :label="item.title" :key="`${item.key || item.type || 'key'}-${index}`" v-bind="item"> <template slot-scope="scope"> <Render :render="item.render" :scope="scope" /> </template> </el-table-column> <el-table-column v-if="!item.render && item.type !== 'radio'" :key="`${item.key || item.type || 'key'}-${index}`" :prop="item.key" :label="item.title" v-bind="item" ></el-table-column> </template> </template> </el-table> </template> <script> import Render from './render' export default { name: 'TtTable', components: { Render, }, props: { columns: { type: Array, default: () => [], }, data: { type: Array, default: () => [], }, loading: { type: Boolean, default: false, }, rowKey: { type: [Function, String], }, expandRowKeys: { type: Array, }, rowClassName: { type: [Function, String], }, }, data() { return { dataValue: [], ttEpdRowKeys: [], } }, watch: { data: { handler(v) { this.ttEpdRowKeys = [] this.dataValue = v.map((v, i) => { const item = { ...v, _expandedKey: `${i}-${Math.random()}`, _radioChecked: false, } item._expanded && this.ttEpdRowKeys.push(item._expandedKey) return item }) }, immediate: true, }, }, methods: { setClassName({ row, rowIndex }) { const className = !this.rowClassName ? '' : typeof this.rowClassName === 'string' ? this.rowClassName : this.rowClassName({ row, rowIndex }) return row._disableExpand ? `${className} mk-table-disable-expanded` : className }, radioChange({ $index, row }) { this.dataValue.forEach((item, i) => { // 排他,每次选择时把其他选项都清除 if (i !== $index) { item._radioChecked = false } }) this.$emit('radio-change', row._radioChecked ? row : {}) }, }, } </script> <style lang="less"> .mk-table-disable-expanded .el-table__expand-column .cell { display: none; } </style> 复制代码
- render.js
export default { name: 'Render', functional: true, props: { scope: Object, render: Function, }, render: (h, ctx) => { const params = { ...ctx.props.scope, } return ctx.props.render(h, params) }, } 复制代码
如何使用 ?
- 引入
<data-table ref="mk-table" :props="{ list: 'list', page: 'page', pageSize: 'size', total: 'total' }" :func="getList" :columns="columns" :searchItems="searchForm" > </data-table> 复制代码
- func 请求数据
- columns 表格表头
- searchForm 搜索 内容
实现效果
网络异常,图片无法展示
|