- 请求本地 JSON 文件获取数据 。
- 问题:cors ,跨域问题 。
- 解决:使用 jsonp 方式,请求相关数据 。
- 注意:在 json 文件中,json 数据使用 callback( json数据 ) 包裹,才能保持浏览器控制台打印出数据无报错。我这里回调函数是 getData( json数据 )。
- 代码:
<script type="text/javascript">
/*
getData 函数: JSONP 的函调函数,解决跨域问题。请求本地 JSON 文件
参数: data---》拿到的 JSON 数据
*/
function getData(data) {
// initData---》存放当前操作完的全局树形数据
var initData = []
initData = JSON.parse(JSON.stringify(formatData(data.menuTree,'')))
initTable(initData)
}
</script>
<script type="text/javascript" src="./data.json?callback=getData"></script>
- 一维数组数据转换为树形数据 (递归)。
- 思路:使用递归的方式将一维数组转换成树形数据,根据 menuParentid 和 menuId 的对比,对数据进行分类,添加到合适的位置上。
- 代码:
/*
formatData 函数:数据转换,将一维数组数据转换为树形数据
参数:data---》原始一维数组数据
返回值:resultArr---》格式化好的树形数据
*/
function formatData(data, pid) {
var resultArr = [], temp;
for (var i = 0; i < data.length; i++) {
if (data[i].menuParentid == pid) {
var obj = JSON.parse(JSON.stringify(data[i]));
temp = formatData(data, data[i].menuId);
if (temp.length > 0) {
obj.children = temp;
}
resultArr.push(obj);
}
}
return resultArr;
}
- JS 中初始化表格 。
- 思路:在 initTR_DG 函数中递归的根据数据,动态新增 tr 到 table 中。根据 当前数据项的 menuType 是否为菜单,决定是否有前缀的展开和收起按钮。
- 代码:
/*
initTable 函数: 初始化表格
参数: data---》转换的树形数据
*/
function initTable(data) {
globalData = JSON.parse(JSON.stringify(data))
var tableEL = document.createElement('table')
var containerEL = document.getElementById('container')
containerEL.appendChild(tableEL)
tableEL.appendChild(createTH())
initTR_DG(data,tableEL,'')
}
function initTR_DG(data,tableEL,index){
if(data && data.length != 0){
for(var i=0; i<data.length; i++){
if(data[i].menuType == '菜单') {
var trEL = createTR(data[i], 1, index+'.'+(i+1))
} else {
var trEL = createTR(data[i], 0, index+'.'+(i+1))
}
tableEL.appendChild(trEL)
if(data[i].children && data[i].children.length != 0){
initTR_DG(data[i].children,tableEL,index+'.'+(i+1))
}
}
}
}
/*
createTH 函数: 创建表头
返回值: trEL---》表头元素
*/
function createTH() {
var trEL = document.createElement('tr')
var thEL0 = document.createElement('th')
thEL0.innerHTML = '序号'
trEL.appendChild(thEL0)
var thEL1 = document.createElement('th')
thEL1.innerHTML = '功能名称'
trEL.appendChild(thEL1)
var thEL2 = document.createElement('th')
thEL2.innerHTML = '是否启用'
trEL.appendChild(thEL2)
var thEL3 = document.createElement('th')
thEL3.innerHTML = '功能类型'
trEL.appendChild(thEL3)
var thEL4 = document.createElement('th')
thEL4.innerHTML = '创建时间'
trEL.appendChild(thEL4)
var thEL5 = document.createElement('th')
thEL5.innerHTML = '功能备注'
trEL.appendChild(thEL5)
var thEL6 = document.createElement('th')
thEL6.innerHTML = '相关操作'
trEL.appendChild(thEL6)
return trEL
}
/*
createTR 函数: 创建每一列
参数1: currentData---》当前列的数据
参数2: type---》类型,决定有没有展开折叠按钮,1 有,其他无
参数3 index---》计算的当前列的标号
参数4: tier---》第几层
返回值: trEL---》创建的当前 tr
*/
function createTR(currentData, type, index) {
var trEL = document.createElement('tr')
if (type == 1) {
var tdEL0 = document.createElement('td')
tdEL0.innerHTML = index
tdEL0.style.fontWeight = 'bold'
tdEL0.style.fontSize = '8px'
trEL.appendChild(tdEL0)
var spanEL = document.createElement('span')
spanEL.classList.add('span')
spanEL.title = '展开'
spanEL.onclick = clickZK
var spanEL1 = document.createElement('span')
spanEL1.classList.add('span1')
spanEL1.title = '收起'
spanEL1.onclick = clickSQ
var tdEL1 = document.createElement('td')
tdEL1.innerHTML = currentData.menuText
tdEL0.style.textAlign = 'left'
tdEL0.appendChild(spanEL)
tdEL0.appendChild(spanEL1)
trEL.appendChild(tdEL1)
var tdEL2 = document.createElement('td')
tdEL2.innerHTML = currentData.menuIsmodify
trEL.appendChild(tdEL2)
var tdEL3 = document.createElement('td')
tdEL3.innerHTML = currentData.menuType
currentData.menuType == '菜单' ? tdEL3.style.color = '#409EFF' : tdEL3.style.color = 'green'
trEL.appendChild(tdEL3)
var tdEL4 = document.createElement('td')
tdEL4.innerHTML = datefomate(currentData.menuCreatetime)
trEL.appendChild(tdEL4)
var tdEL5 = document.createElement('td')
tdEL5.innerHTML = currentData.menuNote
trEL.appendChild(tdEL5)
var tdEL6 = document.createElement('td')
var editBtn = document.createElement('button')
editBtn.innerText = '编辑'
editBtn.classList.add('button1')
editBtn.onclick = editTableData
tdEL6.appendChild(editBtn)
var delBtn = document.createElement('button')
delBtn.innerText = '删除'
delBtn.classList.add('button2')
delBtn.onclick = delTableData
tdEL6.appendChild(delBtn)
var addBtn = document.createElement('button')
addBtn.innerText = '新增'
addBtn.onclick = addTableData
tdEL6.appendChild(addBtn)
addBtn.classList.add('button0')
trEL.appendChild(tdEL6)
} else {
var tdEL0 = document.createElement('td')
var strongEL = document.createElement('strong')
strongEL.innerHTML = index
tdEL0.appendChild(strongEL)
tdEL0.style.fontSize = '8px'
trEL.appendChild(tdEL0)
var tdEL1 = document.createElement('td')
tdEL1.innerHTML = currentData.menuText
trEL.appendChild(tdEL1)
var tdEL2 = document.createElement('td')
tdEL2.innerHTML = currentData.menuIsmodify
trEL.appendChild(tdEL2)
var tdEL3 = document.createElement('td')
tdEL3.innerHTML = currentData.menuType
currentData.menuType == '菜单' ? tdEL3.style.color = '#409EFF' : tdEL3.style.color = 'green'
trEL.appendChild(tdEL3)
var tdEL4 = document.createElement('td')
tdEL4.innerHTML = datefomate(currentData.menuCreatetime)
trEL.appendChild(tdEL4)
var tdEL5 = document.createElement('td')
tdEL5.innerHTML = currentData.menuNote
trEL.appendChild(tdEL5)
var tdEL6 = document.createElement('td')
var editBtn = document.createElement('button')
editBtn.innerText = '编辑'
editBtn.classList.add('button1')
editBtn.onclick = editTableData
tdEL6.appendChild(editBtn)
var delBtn = document.createElement('button')
delBtn.innerText = '删除'
delBtn.classList.add('button2')
delBtn.onclick = delTableData
tdEL6.appendChild(delBtn)
trEL.appendChild(tdEL6)
}
return trEL
}
- 列收起和列展开。
- 思路:根据 clickSQ 的点击事件,确定点击的数据项,在根据 clickSQ_DG 函数,递归的找出当前数据项,让它的 children = [ ],重新初始化表格。根据 clickZK 的点击事件,清空原始 table 表格结构,使用缓存的 initData 数据去重新初始化表格。
- 代码:
// clickSQ 函数: 列收起
function clickSQ() {
initData = JSON.parse(JSON.stringify(globalData))
var currentText = this.parentNode.parentNode.children[1].innerText
clickSQ_DG(globalData,currentText)
document.getElementById('container').innerHTML = ''
initTable(globalData)
SQstatus = false
}
function clickSQ_DG(globalData,currentText){
for (var i = 0; i < globalData.length; i++) {
if (globalData[i].menuText == currentText) {
globalData[i].children = []
SQstatus = true
break
}
if(SQstatus == false){
if(globalData[i].children && globalData[i].children.length != 0){
clickSQ_DG(globalData[i].children,currentText)
}
}
}
}
// clickZK 函数: 列展开
function clickZK() {
document.getElementById('container').innerHTML = ''
initTable(initData)
}
- 删除操作 。
- 思路:根据 delTableData 确定当前操作的数据列,使用 delTableData_DG 函数递归来找到当前的数据,进行删除 ,重新初始化表格 。
- 表格:
// delTableData函数: 删除操作
function delTableData() {
var currentText = this.parentNode.parentNode.children[1].innerText
delTableData_DG(globalData,currentText)
document.getElementById('container').innerHTML = ''
initTable(globalData)
SQstatus = false
}
function delTableData_DG(globalData,currentText){
for (var i = 0; i < globalData.length; i++) {
if (globalData[i].menuText == currentText) {
globalData.splice(i, 1)
SQstatus = true
break
}
if(SQstatus == false){
if(globalData[i].children && globalData[i].children.length != 0){
delTableData_DG(globalData[i].children,currentText)
}
}
}
}
- 编辑操作 。
- 思路:根据 editTableData 确定当前操作的数据列,使用 editTableData_DG 函数递归来找到当前的数据,使用 setEditData 函数进行数据回显,使用 getCurrentEdit 函数进行数据缓存,当点击编辑保存事件时进行当前缓存数据的更改,最后重新初始化表格 。
- 代码:
// editTableData 函数: 编辑操作
function editTableData() {
document.getElementById('edit').style.display = 'block'
var currentText = this.parentNode.parentNode.children[1].innerText
editTableData_DG(globalData,currentText)
SQstatus = false
}
function editTableData_DG(globalData,currentText){
for (var i = 0; i < globalData.length; i++) {
if (globalData[i].menuText == currentText) {
setEditData(globalData[i])
getCurrentEdit(globalData[i])
SQstatus = true
return
}
if(SQstatus == false){
if(globalData[i].children && globalData[i].children.length != 0){
editTableData_DG(globalData[i].children,currentText,currentGlobalData)
}
}
}
}
function getCurrentEdit(data){
return currentGlobalData = data
}
/*
etEditData 函数: 编辑数据回显
参数: data---》编辑回显的当前数据
*/
function setEditData(data) {
document.getElementById('menuText').value = data.menuText
document.getElementById('menuIsmodify').value = data.menuIsmodify == '启用' ? 1 : 0
document.getElementById('menuType').value = data.menuType == '菜单' ? 1 : 0
document.getElementById('menuCreatetime').value = datefomate(data.menuCreatetime)
document.getElementById('menuNote').value = data.menuNote
}
// 编辑关闭
document.getElementById('editClose').onclick = function() {
document.getElementById('edit').style.display = 'none'
}
// 编辑保存
document.getElementById('editSave').onclick = function() {
currentGlobalData.menuText = document.getElementById('menuText').value
currentGlobalData.menuIsmodify = document.getElementById('menuIsmodify').value == 1 ? '启用' : '禁用'
currentGlobalData.menuType = document.getElementById('menuType').value == 1 ? '菜单' : '链接'
currentGlobalData.menuCreatetime = document.getElementById('menuCreatetime').value
currentGlobalData.menuNote = document.getElementById('menuNote').value
document.getElementById('container').innerHTML = ''
initTable(globalData)
document.getElementById('edit').style.display = 'none'
}
// 编辑取消
document.getElementById('editCancle').onclick = function() {
document.getElementById('edit').style.display = 'none'
}
- 新增操作 。
- 思路:根据 addTableData 确定当前操作的数据列,使用 addTableData_DG 函数递归来找到当前的数据,使用 getCurrentAdd 函数进行数据缓存,当点击新增保存事件时进将数据添加到缓存的当前数据项的 children 中,最后重新初始化表格 。
- 代码:
// addTableData 函数:新增操作
function addTableData() {
document.getElementById('add').style.display = 'block'
document.getElementById('menuCreatetime1').value = datefomate(Date.now())
var currentText = this.parentNode.parentNode.children[1].innerText
addTableData_DG(globalData,currentText)
SQstatus = false
}
function addTableData_DG(globalData,currentText){
for (var i = 0; i < globalData.length; i++) {
if (globalData[i].menuText == currentText) {
getCurrentAdd(globalData[i])
SQstatus = true
return
}
if(SQstatus == false){
if(globalData[i].children && globalData[i].children.length != 0){
addTableData_DG(globalData[i].children,currentText)
}
}
}
}
function getCurrentAdd(data){
return currentGlobalData1 = data
}
// 新增关闭
document.getElementById('addClose').onclick = function() {
document.getElementById('menuText1').value = ''
document.getElementById('menuNote1').value = ''
document.getElementById('add').style.display = 'none'
}
//新增保存
document.getElementById('addSave').onclick = function() {
var obj = {}
obj.children = []
obj.menuText = document.getElementById('menuText1').value
obj.menuIsmodify = document.getElementById('menuIsmodify1').value == 1 ? '启用' : '禁用'
obj.menuType = document.getElementById('menuType1').value == 1 ? '菜单' : '链接'
obj.menuCreatetime = document.getElementById('menuCreatetime1').value
obj.menuNote = document.getElementById('menuNote1').value
if(!currentGlobalData1.children){
currentGlobalData1.children = []
currentGlobalData1.children[0] = obj
}else{
currentGlobalData1.children.push(obj)
}
document.getElementById('container').innerHTML = ''
initTable(globalData)
document.getElementById('menuText1').value = ''
document.getElementById('menuNote1').value = ''
document.getElementById('add').style.display = 'none'
}
// 新增取消
document.getElementById('addCancle').onclick = function() {
document.getElementById('menuText1').value = ''
document.getElementById('menuNote1').value = ''
document.getElementById('add').style.display = 'none'
}
- 格式化日期 。
- 思路:使用 new Date(value) 方法返回值提供的一系列方法进行详细日期时间子项的提取,然后在根据合适的格式进行返回。
- 代码:
// datefomate函数: 格式化日期
function datefomate(value) {
if (value == null || value == undefined) {
return "";
}
var date = new Date(value);
Y = date.getFullYear(),
m = date.getMonth() + 1,
d = date.getDate(),
H = date.getHours(),
i = date.getMinutes(),
s = date.getSeconds();
return Y + '-' + m + '-' + d + ' ' + H + ':' + i + ':' + s;
};
使用 VUE 和 Element-UI 实现树形表格 ?
【 如需查看完整示例请点击:tree_table_vue_element: 使用 vue 和 element-ui 实现,树形表格案例,支持多级数据渲染,新增,数辑,删除等操作。 】
- 请求本地 JSON 文件获取数据 。
- 思路:使用 axios.get( ) 的方法,请求本地 JSON 文件,获取数据 。
- 注意:cli2 需要把 json 放在 static 目录下,cli3 中静态资源文件目录由 static 变为 public 不能把 .json 文件直接放到 public 下,必须放到 public/js 下(自己创建 js 目录),否则会报 404 错误 。
- 代码:
axios.get('/js/data.json').then((res) => {
this.defaultData = res.data.menuTree
this.tableData = this.formatData(this.defaultData)
})
- 一维数组数据转换为树形数据(三级)。
- 思路:数据格式转换,根据自己需求场景的不同来决定。我这里只涉及三级数据,选择使用 for 循环嵌套去实现(第一层判断 menuParentid 是否为空,第二三层根据比对 menuParentid 和 menuId 的值来确定存放的合适 children 位置)。更多级推荐使用递归方式实现,具体使用参考上面 。
- 代码:
formatData(data) {
let resultArr = []
for (let i = 0; i < data.length; i++) {
if (data[i].menuParentid == '') {
resultArr.push(data[i])
}
}
for (let i = 0; i < resultArr.length; i++) {
let newArr = []
for (let j = 0; j < data.length; j++) {
if (data[j].menuParentid == resultArr[i].menuId) {
newArr.push(data[j])
}
}
resultArr[i].children = newArr
}
for (let i = 0; i < resultArr.length; i++) {
for (let k = 0; k < resultArr[i].children.length; k++) {
let newArr1 = []
for (let j = 0; j < data.length; j++) {
if (data[j].menuParentid == resultArr[i].children[k].menuId) {
newArr1.push(data[j])
}
}
resultArr[i].children[k].children = newArr1
}
}
return resultArr
}
- 数据渲染,使用 Element-UI 中提供的表格组件 。
- 思路:这里使用 Element-UI 中的表格组件去实现,也可以使用 layui 或者其他 UI 库中的组件去实现 。
- 代码:
<el-table :data="tableData" height="500" style="width: 100%;margin-bottom: 20px;" row-key="id" border
:expand-row-keys="['1','2','16','17','18']" :tree-props="{children: 'children', hasChildren: 'hasChildren'}">
<el-table-column type="index" width="60" label="序号" align="center">
</el-table-column>
<el-table-column prop="menuText" label="功能名称" width="180" align="center">
</el-table-column>
<el-table-column prop="menuIsmodify" label="是否启用" width="180" align="center">
</el-table-column>
<el-table-column prop="menuType" label="功能类型" align="center">
<template slot-scope="scope">
<el-tag :type="scope.row.menuType === '菜单' ? 'primary' : 'success'" disable-transitions>{{scope.row.menuType}}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="menuCreatetime" label="创建时间" align="center">
</el-table-column>
<el-table-column prop="menuNote" label="备注" width="200" align="center">
</el-table-column>
<el-table-column label="操作" align="center">
<template slot-scope="scope">
<el-button style="color:#67C23A;" @click="addHandle(scope.row)" type="text" size="small"
v-if="scope.row.menuType === '菜单'" icon="el-icon-circle-plus-outline">新增</el-button>
<el-button type="text" size="small" @click="editHandle(scope.row)" icon="el-icon-edit">编辑</el-button>
<el-button style="color:red;" @click="delHandle(scope.row)" type="text" size="small" icon="el-icon-delete">删除
</el-button>
</template>
</el-table-column>
</el-table>
- 删除操作 。
- 思路:遍历 defaultData 中的数据,找到删除的那一项进行删除,在使用 formatData 函数对数据进行格式化树形数据,重新赋值给 tableData ,进行表格的重新渲染。
- 代码:
delHandle(row) {
this.$confirm('此操作将永久删除此项, 是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
for (let i = 0; i < this.defaultData.length; i++) {
if (this.defaultData[i].id == row.id) {
this.defaultData.splice(i, 1)
}
}
this.tableData = this.formatData(this.defaultData)
this.$message({
type: 'success',
message: '删除成功!'
});
}).catch(() => {
this.$message({
type: 'info',
message: '已取消删除'
});
});
}
- 编辑操作 。
- 思路:遍历 defaultData 中的数据,找到要编辑的那一项进行修改,在使用 formatData 函数对数据进行格式化树形数据,重新赋值给 tableData ,进行表格的重新渲染。
- 代码:
editHandle(row) {
this.isExit = true
this.timer = new Date().getTime()
for (let i = 0; i < this.defaultData.length; i++) {
if (this.defaultData[i].id == row.id) {
this.currentEdit = this.defaultData[i]
}
}
},
eventHandle($event) {
for (let i = 0; i < this.defaultData.length; i++) {
if (this.defaultData[i].id == $event.id) {
this.defaultData[i] = $event
}
}
this.tableData = this.formatData(this.defaultData)
}
- 新增操作 。
- 思路:遍历 defaultData 中的数据,找到要新增项的父级,使得新增项的 menuParentid 等于新增项的父级的 menuId ,同时将新增项添加到 defaultData 中 。在使用 formatData 函数对数据进行格式化树形数据,重新赋值给 tableData ,进行表格的重新渲染。
- 代码:
addHandle(row) {
this.isExit1 = true
this.timer1 = new Date().getTime()+'1'
for (let i = 0; i < this.defaultData.length; i++) {
if (this.defaultData[i].id == row.id) {
this.currentAdd = this.defaultData[i]
}
}
},
eventHandle1($event) {
this.defaultData.push($event)
$event.id = this.defaultData.length
$event.menuParentid = this.currentAdd.menuId
this.tableData = this.formatData(this.defaultData)
}
}