首页 前端知识 vue element 实现复杂数据报表

vue element 实现复杂数据报表

2024-05-12 00:05:27 前端知识 前端哥 360 9 我要收藏

上效果图:

完整代码

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8" />
		<meta name="viewport" content="width=device-width, initial-scale=1">
		<title>Vue2-Element ui实现 复杂动态报表(同组合并,同组小计,二级表头,内容总合计)</title>
		<!-- CDN方式引入element-ui可能会失败,建议浏览器输入下方网址将源码复制出来放到本地 -->
		<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
		<!-- <link rel="stylesheet" href="./element.css"> -->
	</head>
	<body>
		<div id="app">
			<h2>某平台双十一电脑销量报表</h2>
			<div class="tableBox" style="width: 800px;">
				<el-table :data="tableData" border :summary-method="getSummaries" :span-method="tablespan" ref="tabRef"
					:header-cell-style="{background:'#f5f7fa',color:'#666'}" :cell-style="cellClass" show-summary>
					<el-table-column align="center" prop="brandName" label="品牌名称" width="130"></el-table-column>
					<el-table-column align="center" prop="saleNumber" label="销售数量" width="110"></el-table-column>
					<el-table-column align="center" prop="salePrice" label="销售额" width="110"></el-table-column>
					<el-table-column align="center" prop="profit" label="毛利" width="110"></el-table-column>
					<el-table-column align="center" label="产品类型">
						<el-table-column align="center" prop="Desktop" label="台式机" width="110"></el-table-column>
						<el-table-column align="center" prop="laptop" label="笔记本" width="110"></el-table-column>
					</el-table-column>
					<el-table-column align="center" prop="saleTime" label="统计日期"></el-table-column>
				</el-table>
			</div>
		</div>
	</body>
	<script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
	<!-- CDN方式引入element-ui可能会失败,建议浏览器输入下方网址将源码复制出来放到本地 -->
	<script src="https://unpkg.com/element-ui/lib/index.js"></script> 
	<!-- <script src="./element.js"></script> -->
	<script type="text/javascript">
		const app = new Vue({
			el: '#app', // 这里的的app 是盒子定义的id名
			data() {
				return {
					// 模拟接口返回的表单数据
					// 字段说明: 品牌(brandName),品牌代码(brandCode),销售数量(saleNumber),销售额(salePrice).....
					list: [
						{id:1,brandName:"联想",brandCode:"01",saleNumber:"8",salePrice:"9995",profit:"2100",Desktop:"5",laptop:"3",saleTime:"2023-11-10"},
						{id:2,brandName:"惠普",brandCode:"02",saleNumber:"16",salePrice:"13995",profit:"5150",Desktop:"7",laptop:"9",saleTime:"2023-11-11"},
						{id:3,brandName:"机械革命",brandCode:"03",saleNumber:"12",salePrice:"8259",profit:"1850",Desktop:"2",laptop:"10",saleTime:"2023-11-10"},
						{id:4,brandName:"联想",brandCode:"01",saleNumber:"23",salePrice:"21295",profit:"3620",Desktop:"8",laptop:"15",saleTime:"2023-11-12"},
						{id:5,brandName:"惠普",brandCode:"02",saleNumber:"31",salePrice:"59995",profit:"12100",Desktop:"2",laptop:"29",saleTime:"2023-11-13"},
						{id:6,brandName:"联想",brandCode:"01",saleNumber:"11",salePrice:"16585",profit:"3250",Desktop:"2",laptop:"9",saleTime:"2023-11-14"},
						{id:7,brandName:"联想",brandCode:"01",saleNumber:"7",salePrice:"16540",profit:"1350",Desktop:"2",laptop:"5",saleTime:"2023-11-12"},
						{id:8,brandName:"惠普",brandCode:"02",saleNumber:"8",salePrice:"9995",profit:"2100",Desktop:"5",laptop:"3",saleTime:"2023-11-11"},
						{id:9,brandName:"联想",brandCode:"01",saleNumber:"6",salePrice:"10500",profit:"1500",Desktop:"1",laptop:"5",saleTime:"2023-11-15"},
						{id:10,brandName:"机械革命",brandCode:"03",saleNumber:"8",salePrice:"10850",profit:"1825",Desktop:"5",laptop:"3",saleTime:"2023-11-11"}
					],
					tableData: []
				};
			},
			mounted() {
				this.GetTabListFun()
			},
			methods: {
				// 这些都是根据调试经验得出的计算,代码可能看起来有点繁杂,不必纠结,只要知道如何使用即可
				GetTabListFun() {
					this.tableData = this.countClassFun({
						Tabchildren: this.$refs.tabRef.$children, // 表格对象的表头列表
						DataList: this.list, // 数组对象类型: 接口数据
						Countfield: "brandCode", // 字符串类型: 用于归类统计的类别代号字段名,如:根据品牌代码(brandCode)进行归类
						countcolumnName: ["销售数量", "销售额", "毛利", "台式机", "笔记本"] // 数组字符串类型:需要统计的列名称(对应列的表头名称)
					})
				},
				// 合并行
				tablespan({
					row,
					column,
					rowIndex,
					columnIndex
				}) {
					return this.tablespanFun({
						DataList: this.tableData, // 表格数据
						RuleField: 'brandCode', // 用于归类统计的类别代号字段名
						columnNameList: ['品牌名称'], // 需要合并列的表头名称
						row,
						column,
						rowIndex
					})
				},
				getSummaries(param) { // 总合计
					return this.getSummariesFun({
						param,
						CountList: ["销售数量", "销售额", "毛利", "台式机", "笔记本"] // 需要总合计的对应列
					})
				},
				//设置单元格背景色
				cellClass(prop) {
					if (this.tableData.length) {
						if (prop.row[this.$refs.tabRef.$children[0].prop].includes("合计")) {
							return { // 直接在这里改你想要的颜色即可
								"background-color": "#fdf3ea",
								"color": "red"
							}
						}
					}
				},
				// 下面是封装好的合并与统计规则,可不必理会,直接复制进去按照上方的用法使用即可================
				flattenTree(arr) { // 递归解析多级表头,并将树形表头扁平化为一维对象数组
					let newData = []
					const expanded = (item) => {
						if (item.length) {
							item.forEach(e => {
								if (e.label && e.prop) {
									newData.push(e)
								}
								expanded(e.$children)
							})
						}
					}
					expanded(arr)
					return newData
				},
				countClassFun({
					Tabchildren,
					DataList,
					Countfield,
					countcolumnName
				}) {
					if (!(DataList instanceof Array)) return []
					try {
						let newList = JSON.parse(JSON.stringify(DataList)) // 深拷贝一份,避免影响源数据
						let arr = [] // 将数据按指定字段进行 分组(Countfield)
						let idArr = [] // 存放各组id
						let tableData = [] // 处理完成后的最终数据
						// 自动分配表头的key、value(表头名称、名称对应的列表字段名)
						let TabLabel = this.flattenTree(Tabchildren).map((item) => {
							return {
								label: item.label,
								key: item.prop
							}
						})
						newList.forEach((item) => { // 循环查出数据一共有多少组不同的归类id(Countfield)
							if (!idArr.includes(item[Countfield])) {
								idArr.push(item[Countfield])
							}
						})
						idArr.forEach((id, i) => { // 根据id将数据进行切割分组
							let list = []
							newList.forEach((item) => {
								if (item[Countfield] == id) {
									list.push(item)
								}
							})
							// 创建一条合计数据,字段与普通数据相同
							let rowData = JSON.parse(JSON.stringify(newList[0]))
							Object.keys(rowData).forEach((key, v) => {
								rowData[key] = ""
							});
							TabLabel.forEach((val) => {
								if (countcolumnName.includes(val.label)) { // 根据判断当前字段是否需要累加
									rowData[val.key] = list.reduce((prev, cur) => { // 累加器
										if (!isNaN(cur[val.key])) { // 判断传入的的字段名是否是数字
											return prev + Number(cur[val.key]);
										}
									}, 0);
								}
							})
							rowData[TabLabel[0].key] = "合计:"
							list.push(rowData) // 处理完毕后,往当前分组尾端插入一条计算好的数据
							arr[i] = list
						})
						arr.forEach((item) => { // 最后将将分组数据扁平化
							tableData.push(...item)
						})
						return tableData
					} catch (e) {
						return []
					}
				},
				// 报表合并规则
				tablespanFun({
					DataList,
					RuleField,
					columnNameList,
					row,
					column,
					rowIndex
				}) {
					// RuleField:数组类型,接收指定有何列的名称
					let newList = JSON.parse(JSON.stringify(DataList))
					if (columnNameList.includes(column.label)) {
						if (rowIndex > 0 && row[RuleField] == newList[rowIndex - 1][RuleField]) {
							return {
								rowspan: 0,
								colspan: 0
							};
						} else {
							let _row = 1;
							for (let i = rowIndex + 1; i < newList.length; i++) {
								if (row[RuleField] == newList[i][RuleField]) {
									_row++;
								} else {
									break;
								}
							}
							return {
								rowspan: _row,
								colspan: 1
							};
						}
					}
				},
				// 总合计
				getSummariesFun({
					param,
					CountList
				}) {
					// CountList:*数组类型,接受需要合并的列名称
					let {
						columns,
						data
					} = param;
					let sums = []
					columns.forEach((column, index) => {
						if (index === 0) {
							sums[index] = "总合计:"
							return;
						}
						if (CountList.includes(column.label)) {
							const values = data.map((item) => { // 如果是合计行,则不对这行数值累加
								if (!Object.values(item)[1].includes("合计")) {
									return Number(item[column.property])
								}
							});
							if (!values.every(value => isNaN(value))) {
								sums[index] = values.reduce((prev, curr) => {
									const value = Number(curr);
									if (!isNaN(value)) {
										return prev + curr;
									} else {
										return prev;
									}
								}, 0);
							}
						}
					})
					return sums;
				},
			}
		})
	</script>
	<style>
		* {
			padding: 0px;
			margin: 0px;
			box-sizing: border-box;
		}

		#app {
			padding: 30px;
		}

		h2 {
			text-align: center;
		}

		.tableBox {
			margin: 20px auto;
		}

		/* 组件自带的边框线不太明显,稍微改动一下 */
		.el-table .cell {
			padding: 0px 5px !important;
		}

		.el-date-editor .el-range-separator {
			min-width: 28px !important;
		}

		.el-table td.el-table__cell {
			border-bottom: 1px solid #d0d0d0 !important;
		}

		.el-table--border .el-table__cell {
			border-bottom: 1px solid #d0d0d0 !important;
		}

		.el-table--border .el-table__cell {
			border-right: 1px solid #d0d0d0 !important;
		}

		.el-table th.el-table__cell.is-leaf {
			border-bottom: 1px solid #d0d0d0 !important;
		}

		.el-table--border .el-table__cell {
			border-bottom: 1px solid #d0d0d0 !important;
		}

		.el-table--border:after,
		.el-table--group:after,
		.el-table:before {
			background-color: #d0d0d0 !important;
		}

		.el-table--border,
		.el-table--group {
			border-color: #d0d0d0 !important;
		}

		.el-table td,
		.el-table th.is-leaf {
			border-bottom: 1px solid #d0d0d0 !important;
		}

		.el-table--border th,
		.el-table--border th.gutter:last-of-type {
			border-bottom: 1px solid #d0d0d0 !important;
		}

		.el-table--border td,
		.el-table--border th {
			border-right: 1px solid #d0d0d0 !important;
		}

		.el-table__footer-wrapper td.el-table__cell {
			border-top: 1px solid #d0d0d0 !important;
		}
	</style>
</html>

转载请注明出处或者链接地址:https://www.qianduange.cn//article/8254.html
标签
评论
发布的文章

JSON三种数据解析方法

2024-05-22 09:05:13

使用nvm管理(切换)node版本

2024-05-22 09:05:48

大家推荐的文章
会员中心 联系我 留言建议 回顶部
复制成功!