? 一、效果展示
? ? ? ?实验室横向课题中的一个需求,做的是一个文件上传和下载的树形控件文件。要求按照阶段和任务段展示,即第一层是阶段数,第二层是任务段数,第三层是具体的文件。在文件后面有文件上传和下载的按钮。直接上图说明。
?二、树形控件<el-tree>
基础的树形结构:
?实现代码:
el-tree :data="data" :props="defaultProps" @node-click="handleNodeClick"></el-tree>
<script>
export default {
data() {
return {
data: [{
label: '一级 1',
children: [{
label: '二级 1-1',
children: [{
label: '三级 1-1-1'
}]
}]
}, {
label: '一级 2',
children: [{
label: '二级 2-1',
children: [{
label: '三级 2-1-1'
}]
}, {
label: '二级 2-2',
children: [{
label: '三级 2-2-1'
}]
}]
}, {
label: '一级 3',
children: [{
label: '二级 3-1',
children: [{
label: '三级 3-1-1'
}]
}, {
label: '二级 3-2',
children: [{
label: '三级 3-2-1'
}]
}]
}],
defaultProps: {
children: 'children',
label: 'label'
}
};
},
methods: {
handleNodeClick(data) {
console.log(data);
}
}
};
</script>
我们不难发现组件比较简单,重要的对数据的处理。按照官网给出的数据格式,数据应该也是分层给出。若后端返回的数据不是处理好的树形数据,那么前端需要按照设计自己处理数据。很遗憾,这次我的后端也没有给我处理好分层数据,我也是自己熬夜处理好的数据,具体处理过程都在下面的代码中,必要的已经加了注释!
先看后端返回的数据吧!
后端只返回了一个一维列表,列表中每个元素包含一个文件号apqpNo、一个文件名fileName和一个状态值state.....需要把这个一维列表处理好分成三层数据,那么开始处理数据!
处理过程:
(1)新建 projectFileDate = [], 存后端返回的项目的数据信息,数据信息包含据后端返回的文件编号、文件名、状态等。
? ?新建realMap?=?[] ,?新建realMap存取处理的数据。realMap中有6个子map,初始化为空,分别为0-5阶段的文件数据。
? ?取到projectFileDate 中 每个元素中的文件号apqpNo,对文件号进行处理。例子:apqp值为1.2.5时,则可以取出文件号中的三个数(1 2 5),第一个数是文件树的第一层,根据第二个数和第三个数可以确定阶段数dest(根据已经建好fileMap映射)。
const firstDigst = projectFileDate[i].apqpNo
const fileId = firstDigst.split('.') // id: 9.2.1
// 分割字符串 fileId = ["9", "2", "1"]
var matrix_i = fileId[0]
const matrix_j = fileId[1]
const matrix_k = fileId[2]
// 因第0阶段数据库中的主id为9,故改为0
if (matrix_i == = '9') {
matrix_i = '0'
}
// 每一个叶子文件属于一个 串联节点(映射关系定义在 .\mapMatrix.js)
const dest = matrix[matrix_i][matrix_j][matrix_k]
if (realMap[matrix_i][dest] == = undefined) {
// eslint-disable-next-line no-array-constructor
realMap[matrix_i][dest] = new Array()
}
?遍历projectFileDate,把projectFileDate中的每个元素push进realMap[matrix_i][dest]中,得到的real即为处理好的文件数据。
(2)对realMap处理成分层的树的数据。
? ?遍历realmap,在一层循环中加上两个属性值:label和chiledren[].其中label为确定的文件数据第几阶段数。
? ?curLabel保存realMap[i]值,对curLabel进行添加两个元素,同样是label和chiledren[].其中label为确定的文件数据第几任务段数。
? finalChild保存realMap[i][j]值,对finalChild进行添加三个元素,label、status和id.其中label为确定的文件名称。
?各层保存完依次push进父层,最中得到已建立好的树的数据finalFileTree。
下面是具体的实现代码
<template>
<div>
<h2 style="margin:15px 10px 10px 16%">{{ titleContext }}</h2>
<hr>
<div class="treea">
<div ref="treeDiv" class="tree-container tree-src">
<el-tree
id="tree"
ref="tree"
class="tree tree-dis"
:indent="0"
:data="data"
:props="defaultProps"
:render-content="renderContent"
@node-click="treeNodeClick"
>
/>
<!--
data绑的值用来展示数据,
props用来配置选项(树的子节点,树节点文本展示,叶子节点),
@node-click节点被点击时的事件
render-content:指定渲染函数,文件上传下载在此函数操作
-->
</el-tree></div>
</div>
</div>
</template>
<script>
export default {
props: ['currentProNo'],
data() {
return {
// 当前打开的文件气泡
preOpenCus: 'none',
// 项目号
projectNo: '',
// 项目名称
projectName: '',
// 文件树标题
titleContext: '',
// 文件树数据
direction: 'btt',
treeFileData: [],
changeVisible: false,
fillFileVisible: false,
apqpNoForTriple: '',
infoBoardTitle: '相关信息',
visible_popover: false,
dialogUploadFile: false,
peoMap: {},
apqpNo: '',
apqpMarkArr: '',
data: [],
defaultProps: {
// 树形控件的树形绑定对象
children: 'children', // 设置通过chiledren树形展示节点信息
label: 'label', // 通过label设置树形节点文本展示
disabled: function(data, node) {
if (data.type === 9) {
return true
}
}
},
// 记录每层的打开节点
preOpenNode: {
// 打开节点的最大层数
max: 0
}
}
},
watch: {
},
created() {
this.getProjectDate()
},
methods: {
async getProjectDate() {
// 获取项目的项目号 后面根据项目号查询项目的数据构建项目树
var project_no1 = this.$route.query.projectNo
if (typeof project_no1 !== 'undefined' && project_no1 !== null && project_no1.length !== 0) {
this.projectNo = this.$route.query.projectNo // this.$route.query.projectNo是项目列表点击继续上传按钮传的项目编号
}
var project_no2 = this.$store.getters.curProjectNo
if (typeof project_no2 !== 'undefined' && project_no2 !== null && project_no2.length !== 0) {
this.projectNo = this.$store.getters.curProjectNo // this.$store.getters.curProjectNo是从步骤二获取的项目编号
}
// 调用接口获取该项目需要上传的文件的数据
// 数据包括以下部分
/**
* 项目名称
* 项目文件列表[文件号fileNumber、文件名称filename、状态state]
*/
// 项目名称为构建的文件树的标题
// 文件号1.1.1 此文件为1阶段,判断是属于那个文件段
// 文件树是三层结构:
/**
* 第1阶段 ------- 1层
* 0.1-0.2 ------- 2层
* 市场调研意见书 -------3层
*/
if (typeof this.projectNo === 'undefined' && this.projectNo === null && this.projectNo.length === 0) {
this.$message('没有项目编号!')
return
}
await this.$http.get('/dare/document/selectFileTree?projectNo=' + this.projectNo).then(res => {
const projectFileDate = [] // projectFileDate存项目的数据信息,数据信息包含据信息后端返回的文件编号、文件名等
// 渲染文件树标题
this.titleContext = '项目名:' + res.data.result.projectName + '\xa0\xa0\xa0\xa0\xa0\xa0\xa0' + '项目编号:' + this.projectNo
// 项目文件的数据映射到构建的树中
const realMap = [] // 新建数组projectFileNoList接收文件编号数据
for (let i = 0; i < 6; i++) {
realMap[i] = {} // realMap中有6个子map,分别为0-5阶段的文件数据
}
realMap[matrix_i] = {}
// spacial case
if (res.data.result.fileTreeDTOList.length === 0) {
this.$message('没有需要上传的文件')
return
}
for (let i = 0; i < res.data.result.fileTreeDTOList.length; i++) {
// 为每个文件号后面添加一个文件名
projectFileDate.push(res.data.result.fileTreeDTOList[i])
// 按照阶段分为0-5个阶段,按照文件号的第一个字符分组
const firstDigst = projectFileDate[i].apqpNo
const fileId = firstDigst.split('.') // id: 9.2.1
// 分割字符串 fileId = ["9", "2", "1"]
var matrix_i = fileId[0]
const matrix_j = fileId[1]
const matrix_k = fileId[2]
// 因第0阶段数据库中的主id为9,故改为0
if (matrix_i === '9') {
matrix_i = '0'
}
// 每一个叶子文件属于一个 串联节点(映射关系定义在 .\mapMatrix.js)
const dest = matrix[matrix_i][matrix_j][matrix_k]
if (realMap[matrix_i][dest] === undefined) {
// eslint-disable-next-line no-array-constructor
realMap[matrix_i][dest] = new Array()
}
realMap[matrix_i][dest].push(projectFileDate[i])
}
// console.log(realMap) // 已经处理好的文件数据
// 处理第0阶段
const finalFileTree = []
const mapStr = ['第0阶段', '第1阶段', '第2阶段', '第3阶段', '第4阶段', '第5阶段']
for (const i in realMap) { // 遍历循环realMap 空数据直接跳过
const curMap = realMap[i] // cur保存每个阶段的数据
var curLabel = mapStr[i]
const curPar = { // 阶段层的数据
label: curLabel,
type: '0',
children: []
}
for (const j in curMap) {
const curObj = { // 阶段下的任务段的数据
label: stepMap[j],
type: '0',
children: []
}
for (const k in realMap[i][j]) {
const cur = realMap[i][j][k] // cur保存阶段下的任务段的子文件数据
const finalChild = { // cur保存阶段下的任务段的子文件数据
label: cur.fileName,
status: cur.state,
id: cur.apqpNo
}
curObj.children.push(finalChild)
}
curPar.children.push(curObj)
}
// 一个阶段构造完成, 例如, 第一阶段, 第二阶段
if (curPar.children.length !== 0) {
finalFileTree.push(curPar)
}
}
this.data = finalFileTree // 已建立好的树的数据
})
},
// 文件树的相关操作 对文件上传和下载的操作在此函数中操作
renderContent(h, { node, data, store }) {
// 。。。。。。
},
// 树节点点击函数,一层只展开一个节点,关闭同层节点时,使其展开的子节点也关闭,若无此需求可以在标签内添加accordion手风琴属性
async treeNodeClick(data, node, el) {
// console.log(node.level,'层打开的是',node)
let preExpended = true; let maxLevel = node.level
// 只有非叶节点出发展开收起控制
for (let lel = node.level; lel <= this.preOpenNode.max && !node.isLeaf; lel++) {
this.preOpenNode[lel].expanded = false
// 若有文件气泡开启,关闭此气泡,若未开启不会开启
this.popVisiableController(this.preOpenCus)
if (this.preOpenNode[lel].id === node.id) {
preExpended = false
maxLevel = node.level - 1
}
}
// level层打开的是这个node,只记录非叶节点
if (!node.isLeaf) {
this.preOpenNode[node.level] = node
this.preOpenNode.max = maxLevel
node.expanded = preExpended
}
},
cusVisiable() {
},
popVisiableController(id) {
if (id === this.preOpenCus) {
// console.log("这是再次点击关闭,或者来自节点点击")
this.preOpenCus = 'none'
this.lastOpenCus = 'none'
return
}
this.lastOpenCus = this.preOpenCus
this.preOpenCus = id
}
}
}
</script>
<style scoped>
.butUpload + div {
display: inline-block;
}
.clearfix:before,
.clearfix:after{
content: "";
display: table;
clear: both;
}
.grid-content {
font-size: 23px;
border-radius: 4px;
min-height: 36px;
font-weight: bold;
padding-left: 20px;
line-height: 36px;
}
.bg-purple {
background: #d3dce6;
}
.tree /deep/ .el-tree-node {
position: relative;
padding-left: 16px;
}
.tree /deep/ .el-tree-node__children {
padding-left: 16px;
}
.tree > .el-tree-node:not(:first-child) .el-tree-node__content {
height: 90px;
}
.tree-src /deep/ .tree .el-tree-node .el-tree-node__content {
height: 90px;
}
.tree-src /deep/ .tree-dis >.el-tree-node .el-tree-node__content {
height: 30px;
}
.tree-src /deep/ .tree-dis >.el-tree-node .el-tree-node__children .el-tree-node__content {
height: 30px;
margin-bottom: 0px;
}
.tree-src /deep/ .tree-dis >.el-tree-node .el-tree-node__children .el-tree-node:not(:first-child)
.el-tree-node__content {
height: 55px;
}
.tree-src /deep/ .tree-dis >.el-tree-node .el-tree-node__children .el-tree-node__children
.el-tree-node .el-tree-node__content {
height: 30px;
margin-bottom: 0px;
}
.tree-container {
font-size: 16px;
}
.el-tree /deep/ .el-tree-node__expand-icon.expanded {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
/*有子节点,但是未展开*/
.el-tree /deep/ .el-icon-caret-right:before {
background: url("./folder.png") no-repeat 0 3px;
content: '';
display: block;
width: 16px;
height: 16px;
font-size: 16px;
background-size: 16px;
}
/*有子节点,并且已经展开*/
.el-tree /deep/ .el-tree-node__expand-icon.expanded.el-icon-caret-right:before {
background: url("./folder_open.png") no-repeat 0 3px;
content: '';
display: block;
width: 16px;
height: 16px;
font-size: 16px;
background-size: 16px;
}
/*没有子节点,就是一个文件*/
.el-tree /deep/.el-tree-node__expand-icon.is-leaf::before {
background: url("./file.png") no-repeat 0 3px;
content: '';
display: block;
width: 16px;
height: 21px;
font-size: 16px;
background-size: 16px;
}
.fillForm {
margin-left: 99px;
font-weight: bold;
font-size: 14px;
}
.fillFormHidden {
margin-left: 99px;
font-weight: bold;
font-size: 14px;
visibility: hidden;
}
.menu-title {
margin-top: 14px;
margin-left: 5px;
margin-bottom: 20px;
}
/* 右侧信息栏 */
.right-wrapper {
border-radius: 8px;
box-shadow: 0 0 8px rgba(0,0,0,0.2);
padding: 1px 10px 9px;
margin-top: 15px;
}
</style>
|