二次封装el-table组件,以便使用。
包含两个部分:使用说明、创建。
前沿:
1. 包含:表格展示、分页、二级表头、数据字典转换展示、时间格式转换展示、数组转换成逗号分割字符串展示、表格内按钮、表格内switch按钮、多选、pdf展示名字并点击名字时预览,等众多功能。
2. 各个属性灵活运用,也可自己添加、更改相关属性配置。
3. 这里设置从父组件中传输过来的对象和数组都用JSON串接收,目的是为了方便监听数值变化。
4. 代码为手写,已检查,但不保证没有单词写错,如报错,请检查一下单词的拼写。
前期准备:
如后台直接提供数据字典相关字段的翻译文字,则此步跳过。
添加Vue过滤器,过滤器中添加【数据字典值转文字】方法(若不用过滤器也可,可添加成公共方法,用时调用即可):
const filters = {};
/**
* 将value值转换成数据字典中对应的label值
* @value {String/Array} 数据value值,必传
* @dictionaryList {Array} value值所在的整个数据字典数据,必传
* @keys {Object} {value: 'code', label: 'codeDsc'}: 数据字典对应的value和label值设定,如不设定,默认'value'和'label',非必传
**/
filters.filterDictionary = (value, dictionaryList, keys) => {
if (!value || (Array.isArray(value) && value.length === 0)) {
return '';
}
if (!dictionaryList) {
return value;
}
const keyValue = keys ? keys.value : 'value'
const keyLabel = keys ? keys.label : 'label'
const valueList = Array.isArray(value) ? value : value.split(',')
const labelList = []
valueList.forEach(itemValue => {
const [...fileterList] = dictionaryList.filter(item => item[keyValue] === itemValue)
if (filterList && filterList.length > 0) {
const [filterObject] = filterList
labelList.push(filterObject[keyLabel])
}
})
if (labelList.length > 0) return labelList.join(',')
return value
}
使用说明:
1. 使用:
<com-table
ref="tablelists"
:table-list="tableList"
:height="height"
:params="params"
:url="url"
:tableRef="tableRef"
:dataValues="dataValues"
:pagesShow="false"
:pageSize="pageSize"
:pageSizes="pageSizes"
:isSelection="isSelection"
:selections="selections"
:pageCount="pageCount"
:pageSmall="false"
:pageLayout="pageLayout"
:cantSelect="cantSelect"
:rowId="rowId"
@editTableButton="editTableButton"
@switchChange="switchChange"
@selectChange="selectChange"
@selectAll="selectAll"
@getTableList="getTableList"
@currentPageChange="currentPageChange"
></com-table>
2. 各个属性使用说明:
1. tableList: 表格展示的相关属性信息,必传。
示例:
tableList: JSON.stringify(
[
{
model: 'name', // 单元格展示的表头key值,必传(当type===column/button/index时,model可不赋值)
label: '姓名', // 单元格展示的表头文字(二级表头时为一级表头文字),必传
minWidth: 100, // 单元格列最小宽度设置,默认空,非必传
width: 100, // 单元格列具体宽度设置,默认空,非必传
align: 'center', //单元格对齐方式(除表头外,表头默认左对齐,不可更改),默认left,非必传
fixed: 'left', // 列是否固定在左侧或右侧,可选值:left/right/true,true固定在左边,不传则不固定,默认空,非必传
style: 'color: red', // 单元格的样式,默认空,非必传
type: 'dictionary', // 数据值类型,根据不同类型做不同操作,默认text,非必传
type可选值如下:
1>. text: 直接显示数据的model值,不做任何处理
2>. dictionary: model值为数据字典的code值,需匹配数据字典拿到相应的文字值展示
3>. date、datetime: model值为date类型数据,需转换成String展示
4>. switch: 展示switch按钮
5>. button: 展示button按钮
6>. pdf: 直接展示model值,点击值可预览pdf
7>. index: 该单元格展示表格行索引,从1开始
8>. column: 多级表头
9>. list: model值为数组,需转换成逗号分割字符串展示
buttonList: [ // 按钮列按钮信息,每个按钮一个对象;当type===button时必传
{
id: '01', // 按钮id,唯一标识,必传
name: '编辑', // 按钮名字,必传
disable: [ // 是否禁用条件设置;非必传;逻辑:
// 每个属性限制为一个对象,多个对象时,为【或】的关系
// 当【model】值与【value】的值中有一条相等时,或与【notValue】的值中有一条不相等时,则此按钮禁用。
// 注意:【value】和【notValue】不同时存在,否则,以【value】的设置为准。
{
model: 'status',
notValue: ['01', '02'],
value: ['03', '04'],
},{...}
],
},{...}
],
column: [ // 二级表头具体信息
{
id: '01', // 表头id,唯一标识,必传。
model: 'card', // 单元格展示的表头key值,必传
minWidth: 100, // 单元格最小宽度设置,默认空,非必传
width: 80, // 单元格宽度设置,默认空,非必传
sortable: false, // 是否要排序,默认false,非必传
}, {...}
],
sorttable: false, // 是否要排序,默认false,非必传
dicListName: 'isNotList', // 所需的数据字典列表名, type===dictionary时必传,默认空
dicKeys: { // type===dictionary时,数据字典值和要展示的文字属性key,非必传,默认将【code】对应的【name】展示出来
code: 'code',
name: 'name',
},
switch: { // type===switch时,switch开关相关属性设置,默认开为'1',关为'0',开的颜色为'blue',非必传
on: '1',
off: '0',
onColor: 'blue'
},
tag: { // 标签配置,标签样式由el-tag展示,非必传。如有此属性,则除type===button,type的优先级都【低于】tag,数据会进入tag的判定里;配置:当【model】中的值为【01】时,标签type为【success】,为【02】时,标签type为【danger】
'01': 'success',
'02': 'danger',
},
tooltip: { // 文字提示设置,用el-tooltip展示,非必传。如有此属性,则除type===button,type的优先级都【低于】tag,数据会进入tag的判定里
effect: 'dark', // 主题,默认'dark',非必传
text: '', // 提示文字具体内容,若用tooltip属性,则必传
position: '', // 提示文字位置,默认在底部(bottom),非必传
},
},{...}
]
)
2. height: '', // 表格高度,默认不限制,非必传
3. params: JSON.stringify({name: '', age: ''}), // 表格查询时,除了分页参数以外的其余查询参数
4. url: '/getTableList', // 获取表格数据接口链接,默认空,【dataValues】和【url】二者必传一个 ,否则表格没有数据,会报错;如两者同时存在,【dataValues】优先级高于url
5. dataValues: JSON.stringify([{...}, {...}]), // 本地表格数据,传入后按照此数据展示表格
6. tableRef: 'commonTable', // 表格Ref配置,默认commonTable,非必传
7. pagesShow: true, // 是否展示分页,默认true,非必传
8. pageSize: '10', // 分页中每页几条数据设置,默认每页10条,非必传
9. pageSizes: JSON.stringify([10, 40, 100, 200]), // 分页中每页显示个数选择器的选项设置,默认[10, 40, 100, 200],非必传
10. isSelection: false, // 是否有多选框,默认false,非必传
11. selections: JSON.stringify({key: 'card', value: '01'}), // 需要选择的数据配置,查找数据中【card】为【01】的数据置为选中状态;默认空,非必传
12. pageCount: 7, // 分页中页码按钮数量,当总页数超过该值时会折叠,默认7,非必传
13. pageSmall: false, // 分页中是否使用小型分页样式,默认false,非必传
14. pageLayout: 'total, sizes, prev, pager, next, jumper', // 分页中组件布局,默认'total,sizes,prev,pager,next,jumper',非必传
15. cantSelect: JSON.stringify([{...},{...}]), // 禁用数据配置,默认空,非必传
16. rowId: 'id', // 数据主键,用于查找唯一数据,默认id,非必传
17. @editTableButton: 点击表格中的按钮时触发回调,两个参数:scope(点击按钮所在行的信息), id(按钮id)
18. @switchChange: 点击表格中switch框触发回调,两个参数:row(点击按钮所在行的信息), model(点击按钮的model值)
19. @selectChange: 有选择框时,当选择项发生变化时触发回调,参数:selection(当前选择的所有数据数组合集)
20. @selectAll: 有选择框时,当全选时触发回调,参数:selection(选择的数据合集)
21. @rowClick: 当某一行被点击时触发回调,参数:row(当前点击行的行数据)
22. @getTableList: 表格数据加载成功后触发回调,用于通知父组件数据条数,参数:total(数据总条数)
23. @currentPageChange: 当前页数改变时触发回调,参数: currentPage(当前所在页)
创建:
创建封装文件 - comTable.vue:
<template>
<div>
<el-table
:tef="tableRef"
v-loading="loading"
:data="tableData"
:height="tableHeight ? tableHeight : null"
stripe
tooltip-effect="dark"
size="mini"
style="width: 100%"
border
@selection-change="selectionChange"
@row-click="rowClick"
@select-all="selectAll"
@select="select"
>
<!-- 选择框 -->
<el-table-column
v-if="isSelection"
type="selection"
width="55"
align="center"
:selectable="isSelectFunction"
>
</el-table-column>
<el-table-column
v-for="item in tableInfoLists"
:key="item.model"
:prop="item.model"
:label="item.label"
:min-width="item.minWidth || ''"
:width="item.width || ''"
show-overflow-tooltip
:align="item.align || 'left'"
header-align="left"
:fixed="item.fixed || false"
:sortable="item.sortable || false"
>
<!-- 二级表单 -->
<template v-if="item.type == 'column'">
<el-table-column
v-for="itemC in item.column"
:key="itemC.id"
:prop="itemC.model"
:label="itemC.label"
:min-width="itemC.minWidth || ''"
:width="itemC.width || ''"
:sortable="item.sortable || false"
>
</el-table-column>
</template>
<!-- 详细数据展示 -->
<template slot-scope="scope">
<!-- 按钮操作栏 -->
<span v-if="item.type == 'button'">
<el-button
v-for="term in item.buttonLists"
:key="term.id"
type="text"
size="small"
:disabled="isButtonDisabled(scope, term)"
@click="handleTableView(scope, term.id)"
>
{{ term.name }}
</el-button>
</span>
<!-- 标签展示 -->
<span v-else-if="item.tag" :style="item.style || ''">
<!-- 如果是数据字典,则翻译之后展示 -->
<el-tag
v-if="item.type == 'dictionary'"
:type="item.tag[scope.row[item.model]]"
>
{{scope.row[item.model] | filterDictionary(dictionaryAll[item.dicName], item.dicKeys || '')}}
</el-tag>
<!-- 否则,直接展示 -->
<el-tag v-else :type="item.tag[scope.row[item.model]]">{{ scope.row[item.model] }}</el-tag>
</span>
<!-- 文字提示 -->
<span v-else-if="item.tooltip" :style="item.style || ''">
<el-tooltip
class="item"
:effect="item.tooltip.effect || 'dark'"
:content="item.tooltip.text"
:placement="item.tooltip.position || 'bottom'"
>
<!-- 如果是数据字典,则翻译后展示 -->
<span v-if="item.type == 'dictionary'">
{{ scope.row[item.model] | filterDictionary(dictionaryAll[item.dicName], item.dicKeys || '') }}
</span>
<!-- 否则,直接展示 -->
<span v-else>{{ scope.row[item.model] }}</span>
</el-tooltip>
</span>
<!-- 数据字典 -->
<span v-else-if="item.type == 'dictionary'" :style="item.style || ''">
<span>{{ scope.row[item.model] | fileterDictionary(dictionaryAll[item.dicName], item.dicKeys || '') }}</span>
</span>
<!-- 日期格式 -->
<span v-else-if="item.type == 'date'" :style="item.style || ''">{{ scope.row[item.model] ? $moment(scope.row[item.model]).format('YYYY-MM-DD') : '' }}</span>
<!-- 日期时间格式 -->
<span v-else-if="item.type == 'datetime'" :style="item.style || ''">{{ scope.row[item.model] ? $moment(scope.row[item.model]).format('YYYY-MM-DD HH:mm:ss') : '' }}</span>
<!-- switch框 -->
<span v-else-if="item.type == 'switch'">
<el-switch
v-model="scope.row[item.model]"
:active-color="item.switch ? item.switch.onColor || '#0160C0' : '#0160C0'"
:active-value="item.switch ? item.switch.on || '1' : '1'"
:inactive-value="item.switch ? item.switch.off || '0' : '0'"
@change="switchChange(scope, item.model)"
></el-switch>
</span>
<!-- 索引 -->
<span v-else-if="item.type == 'index'" :style="item.style || ''">{{ scope.$index + 1 }}</span>
<!-- pdf -->
<span v-else-if="item.type == 'pdf'" :style="item.style || ''">
<el-button type="text" size="small" @click="handlePdfView(scope.row)">{{ scope.row[item.model] }}</el-button>
</span>
<!-- 数组 -->
<span v-else-if="item.type == 'list'" :style="item.style || ''">{{ scope.row[item.model] ? scope.row[item.model].join(',') : '' }}</span>
<span v-else :style="item.style || ''">{{ scope.row[item.model] }}</span>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<div v-if="isShowPages" ref="paginationBoxRef" class="pagination-box">
<el-pagination
:current-page.sync="currentPage"
:page-sizes="pageSizeList"
:page-size="perPageSize"
:total="total"
:pager-count="pageCount"
:small="pageSmall"
layout="pageLayout"
@size-change="pageSizeChange"
@current-change="currentPageChange"
@prev-click="prevClick"
@next-click="nextClick"
></el-pagination>
</div>
<!-- pdf预览,改组件在之前的文章中有讲,这里不再赘述 -->
<com-pdf-show ref="comPdfShowBoxRef"></com-pdf-show>
</div>
</template>
<script>
import { mapState } from 'vuex';
import comPdfShow from './comPdfShow'
export default {
name: 'ComtTable',
components: { comPdfShow },
props: {
tableList: String,,
pageSize: {
type: Number,
default: 10,
},
pageSizes: {
type: String,
default: JSON.stringify([10, 40, 100, 200]),
},
url: String,
params: String,
dataValues: String,
pagesShow: {
type: Boolean,
default: true,
},
height: String,
isSelection: {
type: Boolean,
default: false,
},
selections: String,
tableRef: {
type: String,
default: 'commonTable',
},
pageCount: {
type: Number,
default: 7,
},
pageSmall: {
type: Boolean,
default: false,
},
pageLayout: {
type: String,
default: 'total, size, prev, pager, next, jumper',
},
cantSelect: String,
rowId: {
type: String,
default: 'id',
},
},
data() {
return {
tableData: [],
tableInfoLists: [],
total: 0,
currentPage: 1,
loading: false,
pageSizeList: JSON.parse(this.pageSizes),
perPageSize: this.pageSize,
getParams: {},
isShowPages: true,
tableHeight: '',
}
},
cumputed: {
...mapState({
dictionaryAll: state => state.dictionary
}),
},
watch: {
currentPage() {
this.$emit('currentPageChange', this.currentPage)
this.getTableData()
},
perPageSize() {
this.getTableData()
},
dataValues() {
this.tableData = this.dataValues ? JSON.parse(this.dataValues) : []
// 如有选择项,则进行选中操作
this.$nextTick(() => {
if (this.selections) {
this.getSelection()
}
})
},
selections() {
if (this.selections) this.getSelection()
else this.$refs[this.tableRef].clearSelection()
},
cantSelect() {
if (this.cantSelect) {
const notSelectList = JSON.parse(this.cantSelect)
// 标记不能选择的数据:添加字段【cantSelect】,利用true/false属性标记是否不可选择
this.tableData.forEach(itemT => {
const findRow = notSelectList.filter(itemN => itemN[this.rowId] === itemT[this.rowId])
if (findRow && findRow.length > 0) {
const [findRowObj] = findRow
this.setRowSelect(findRowObj, false, this.rowId)
itemT.cantSelect = true
} else {
itemT.cantSelect = false
}
})
}
},
},
created() {
this.isShowPages = this.pagesShow
this.tableData = this.dataValues ? JSON.parse(this.dataValues) : []
this.tableInfoLists = this.tableList ? JSON.parse(this.tableList) : []
if (this.params) this.getParams = JSON.parse(this.params)
},
mounted() {
if (this.url && !this.dataValues) this.getTableData()
this.$nextTick(() => {
if (this.height) this.tableHeight = this.height
// 如果数据是本地数据且有选择项,则进行选中操作
if (this.dataValues && this.selections) this.getSelection()
})
},
methods: {
/**
* 根据url请求接口获取表格详情数据
* @serachParams {Object} 获取列表的附加参数数据
**/
getTableData(searchParams) {
this.loading = true
if (searchParams) this.getParams = searchParams
// 这里设定分页的参数分别为pageSize和pageNum
this.getParams.pageSize = this.perPageSize
this.getParams.pageNum = this.currentPage
const params = {
params: this.getParams
}
this.axios.get(this.url, params).then(res => {
this.loading = false
const tableAllData = res.data
this.tableData = tableAllData.list
this.total = tableAllData.total
this.$emit('getTableList', tableAllData.total)
this.$nextTick(() => {
if (this.selections) this.getSelection()
})
}).catch(() => {
this.loading = false
this.tableData = []
this.total = 0
})
},
/**
* 表格中的按钮是否需要禁用
* @scope {Object} 本条数据信息
* @trem {Object} 当前按钮信息
**/
isButtonDisabled(scope, trem) {
if (!trem.disable) return false
// 拿到disable,数组间对象是或的关系,只要有一个对象为true,就返回true
const disableList = trem.disable
let isDis = false
for (let index = 0; index < disableList.length; index += 1) {
const disableInfo = disableList[index]
let isDisableString = ''
// value为数组,元素间是或的关系,只要有一个为true,则为true
if (disableInfo.value) {
disableInfo.value.forEach(itemY => {
if (scope.row[disableInfo.model] === itemY)
isDisableString += 'true,'
else
isDisableString += 'false,'
})
} else if (disableInfo.notValue) {
disableInfo.notValue.forEach(itemN => {
if (scope.row[disableInfo.model] === itemN)
isDisableString += 'false,'
else
isDisableString += 'true'
})
}
if (isDisableString.match('true,')) {
isDis = true;
break;
}
}
return isDis;
},
/**
* 将选中的数据进行选中
**/
getSelection() {
const list = this.tableData;
const { key, value } = JSON.parse(this.selections)
const valueList = value ? value.split(',') : []
list.forEach(element => {
if (valueList.indexOf(element[key]) > -1)
this.$refs[this.tableRef].toggleRowSelection(element)
})
},
/**
* 该行数据是否可选择
* @row {Object} 该行的数据信息
**/
isSelectFunction(row) {
if (this.cantSelect) {
const notSelectList = JSON.parse(this.cantSelect)
const findRow = notSelectList.filter(itemN => itemN[this.rowId] === row[this.rowId])
if (findRow && findRow.length > 0) {
row.cantSelect = true;
return false;
}
return true;
}
return true;
},
/**
* pageSize改变时触发
* @pageSize {Number} 当前每页条数
**/
pageSizeChange(pageSize) {
this.perPageSize = pageSize
},
/**
* currentPage改变时触发
* @page {Number} 当前页
**/
currentPageChange(page) {
this.currentPage = page
},
/**
* 用户点击上一页按钮改变当前页后触发
* @page {Number} 当前页
**/
prevClick(page) {
this.currentPage = page
},
/**
* 用户点击下一页按钮改变当前页后触发
* @page {Number} 当前页
**/
nextClick(page) {
this.currentPage = page
},
/**
* 点击表格中按钮时触发
* @scope {Object} 点击行信息
* @id {String} 按钮id
**/
handleTableView(scope, id) {
this.$emit('editTableButton', scope, id)
},
/**
* 点击表格中switch框触发
* @row {Object} 点击行信息
* @model {Object} 点击按钮的model值
**/
switchChange(scope, model) {
this.$emit('switchChange', scope.row, model)
},
/**
* 当选择项发生变化时会触发该事件
* @selection {Array} 选择的表格数据数据
**/
selectionChange(selection) {
this.$emit('selectChange', selection)
},
/**
* 当全选时会触发该事件
* @selection {Array} 选择的表格数据数组
**/
selectAll(selection) {
this.$emit('selectAll', selection)
},
/**
* 当用户手动勾选数据行的 Checkbox 时触发
* @selection {Array} 选择的表格数据数组
* @row {Object} 点击的行数据
**/
select(selection, row) {
this.$emit('rowClick', row)
},
/**
* 当某一行被点击时会触发
* @row {Object} 点击的行数据
**/
rowClick(row) {
this.$emit('rowClick', row);
if (!row.cantSelect) this.$refs[this.tableRef].toggleRowSelection(row)
},
/**
* 设置某一行的选中状态
* @row {Object} 行数据
* @selected {Boolean} 是否选中 true/false
* @selectKey {String} 数据主键,用于筛选出选择的这一条数据,不传的话就默认为this.rowId的值
**/
setRowSelect(row, selected, selectKey) {
const rowId = selectKey ? row[selectKey] : row[this.rowId]
const findRowIndex = this.tableData.findIndex(item => {
const itemId = selectKey ? item[selectKey] : item[this.rowId]
return itemId === rowId
})
if (findRowIndex >= 0)
this.$refs[this.tableRef].toggleRowSelection(this.tableData[findRowIndex], selected)
},
/**
* 设置全部的选中状态
* 目前作为父组件直接调用函数
**/
setAllRowSelect() {
this.$refs[this.tableRef].clearSelection();
this.$refs[this.tableRef].toggleAllSelection();
},
/**
* 点击某行中的pdf单元格时触发(这里传的是pdf的id,具体解析在【pdf预览】那一篇文章中)
* @row {Object} 点击的行数据
**/
handlePdfView(row) {
if (row.pdfId)
this.$refs.comPdfShowBoxRef.previewPDF(row.pdfId)
},
},
}
</script>
完。