Element-ui之table封装

二次封装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>

完。

  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值