最近在寫樹形結構,發現能夠直接拿來用又很舒服的很難找,就本身寫了兩種~css
如下是效果~html
前者是本身徹底用jquery寫的,後者藉助element-UI寫的,下邊是代碼。本人Node後端,大佬輕噴~node
<template> <div class="md-layout user-relation"> <!-- 分割線 --> <div class="md-layout-item md-size-100 line1"> <v-divider /> </div> <!-- 1.搜索(Safumax LIVE)待審覈入金記錄 --> <!-- <md-button class="md-primary md-sm padding" @click="$router.push({name:'agentForm'})">建立代理</md-button> --> <span class="buttonspace"></span> <!-- 2.代理管理表 --> <div class="md-layout-item md-size-100"> <md-card> <!-- 標題 --> <md-card-header class="md-card-header-icon md-card-header-green"> <div class="card-icon"> <md-icon>repeat</md-icon> </div> <h4 class="title">客戶層級關係</h4> </md-card-header> <md-card-header> <div class="md-layout"> <!-- 1.暱稱 --> <div class="input md-layout-item md-size-30 md-xsmall-size-100"> <el-select clearable filterable v-model="filter.id" remote placeholder="請輸入中文名或MT帳號" :remote-method="remoteMethod" @change="getData" :loading="loading" > <el-option v-for="item in users" :key="item.id" :label="`${item.chineseName}--${item.phone}--${item.email}`" :value="item.id" ></el-option> </el-select> </div> <!-- 7.表單提交按鈕 --> <div class="md-layout-item md-size-70"> <el-button type="success" size="" style="float:left;margin-top:0px" @click="getData()" >查詢</el-button> </div> </div> </md-card-header> <!-- 內容 --> <md-card-content> <div v-show="powershow && item" :class="isbeyong? 'powerleft':'powertop'" id="powerdiv"> <div class="item_div"> <md-icon>face</md-icon> {{item.chineseName}} </div> <div class="item_div"> <span class="item_span">帳戶ID: {{item.id}}</span> <span class="item_span">註冊時間: {{$method.timeformatYMD(item.create_time)}}</span> <span class="item_span">手機號碼: {{item.phone}}</span> </div> <!-- <div class="item_div"> </div> --> <div class="item_div"> <span class="item_span">電子郵箱: {{item.email}}</span> </div> <div class="item_div"> <span class="item_span mtid">MT帳號: {{(item.mtids && item.mtids.length>5)? item.mtids.slice(0,5).join()+'......' : item.mtids && item.mtids.join()}}</span> </div> <div class="item_div"> <md-icon>hdr_weak</md-icon> </div> </div> <div class="all"> <div class="first-main"> <div class="item firstItem" v-for="item in data" :key="item.id" :id="item.id" @click="loadAndCreate(item)"> {{item.chineseName}} <span :id="`${item.id}content`" style="display:none">{{JSON.stringify(item)}}</span> <i v-show="item.agentCount>0" class="icon el-icon-arrow-right"></i> </div> </div> </div> </md-card-content> </md-card> </div> </div></template><script>import $ from "jquery";import { setTimeout, setInterval } from 'timers';export default { data() { return { loading: false, data: [], getRelation: false, users: [], powershow:false, isbeyong: false, createtime:0, destroytime:0, item:{}, filter: { id: "" } }; }, async mounted() { await this.loadAndCreate(); this.bindEvent() }, methods: { async getData() { this.data = await this.list(); $('.main').remove() }, // 查找 async list(id) { if (this.getRelation) return; this.getRelation = true; const data = { ...this.filter }; if (id) data.id = id; const res = await this.$axios.get("userRelation", { params: data }); this.getRelation = false; this.addchildren(res.data.result.user.children); return [...[res.data.result.user]]; }, // 處理children addchildren(list) { for (let item of list) { if (item.agentCount > 0) { item.children = [{}]; } } }, // 遠程搜索 async remoteMethod(query) { this.loading = true; this.loading = false; const res = await this.$axios.get("getUsersByName", { params: { content: query } }); this.users = res.data.result.users; }, // html拼接 async loadAndCreate(data){ const _this = this; const id = (data && data.id) || (data && data.currentTarget && data.currentTarget.id) || ''; let result = await this.list(id); if(!result) return; if(!id) this.data = result let html = '<div class="main">' const user = result[0]; $(`#${user.id}`).siblings().css({'background-color': '','color': ''}) $(`#${user.id}`).css({'background-color': '#f2f7fa','color': '#5992c8'}) if( user.children.length < 1) return user.children.forEach(item=>{ html += ` <div class="item" id="${item.id}"> ${item.chineseName} <span id="${item.id}content" style="display:none">${JSON.stringify(item)}</span> ${item.children? `<i class="icon el-icon-arrow-right"></i>`:``} </div>` }) html += '</div>' $(`#${user.id}`).parent().nextAll().remove() $(`#${user.id}`).parent().parent().append(html) this.bindEvent() }, // 給須要的元素動態綁定事件 bindEvent(){ const _this = this; $('.item').click(this.loadAndCreate) $('.item').mouseover(function(){ // 時間判斷 const now = new Date().getTime(); // if(now-_this.destroytime<1000) return; _this.powershow = true _this.createtime = new Date().getTime(); // 獲取頁面寬度 const isBeyong = $(this).position().left > document.body.clientWidth/2 let thistop,thisleft; // 判斷是否是當前列的第一個元素 if(isBeyong){ _this.isbeyong = true thistop = $(this).position().top thisleft = $(this).position().left - parseInt($('#powerdiv').css('width')) - 10; }else{ _this.isbeyong = false thistop = $(this).position().top - parseInt($('#powerdiv').css('height')) - 10; thisleft = $(this).position().left } // 判斷不是第一個元素則往下移動10 if($('.firstItem').position().top !== $(this).position().top){ thistop = thistop + 10 } $('#powerdiv').css({'top': thistop, 'left': thisleft}); _this.item = JSON.parse($(`#${$(this).attr('id')}content`).text()); }).mouseout(function(event){ // 消失的時候延時消失 _this.destroytime = new Date().getTime(); setTimeout(()=>{ const now = new Date().getTime(); if(now - _this.createtime > 1000){ _this.powershow = false } },1000) }) $('#powerdiv').hover(function(){ _this.powershow = true _this.createtime = new Date().getTime(); },function(event){ // 消失的時候延時消失 setTimeout(()=>{ const now = new Date().getTime(); if(now - _this.createtime > 1000){ _this.powershow = false } },1000) }) } }};</script><style lang="scss">.user-relation { .powerleft,.powertop{ border: 1px solid #F5F4F4; border-radius: 3px; position: relative; width: 400px; font-size: 12px; line-height: 1.5; position: absolute; background: #fff; box-sizing: border-box; box-shadow: 2px 2px 2px 1px rgba(0, 0, 0, 0.08); } .powertop:after{ content: ""; display: block; width: 10px; height: 10px; background: #F5F4F4; border-left: 1px solid rgba(226, 222, 222, 0.849); border-bottom: 1px solid rgba(226, 222, 222, 0.849); position: absolute; /* 加上border的高度 */ top: 98%; left: 10%; transform: rotate(-45deg); } .powerleft:after{ content: ""; display: block; width: 10px; height: 10px; background: #F5F4F4; border-left: 1px solid rgba(226, 222, 222, 0.849); border-bottom: 1px solid rgba(226, 222, 222, 0.849); position: absolute; /* 加上border的高度 */ top: 10%; left: 99%; transform: rotate(225deg); } .item_div{ font-size:12px; border-bottom: 1px solid #F5F4F4; height: 40px; line-height: 40px; padding:0px 10px; } .item_div:first-child { background-color: #F5F4F4; } .item_div:last-child { float: right; border: none; height: 100%; width:100%; background-color: #F5F4F4; .md-icon{ margin-left:90%; line-height: 40px; } } .item_div:nth-child(4) > .item_span { width: 100%; } .item_span{ display: inline-block; padding: 10px; // width:50%; } .all{ border: 1px solid rgba(226, 222, 222, 0.849); padding: 20px; width: 100%; overflow: auto; display: flex; } .first-main{ min-height: 500px; width: 160px; border: 1px solid rgba(226, 222, 222, 0.849); float: left; } .main { min-height: 500px; width: 160px; margin-left: -1px; border: 1px solid rgba(226, 222, 222, 0.849); float: left; } .item { margin-top:-1px; width: 160px; line-height: 42px; text-align: left; height: 40px; font-size: 12px; display: inline-block; padding-left: 5px; } .icon { margin-right:8px; float: right; line-height: 42px; }}</style>複製代碼
<--------------------------------------第二種實現方式-------------------------------------->
<template> <div class="md-layout"> <!-- 分割線 --> <div class="md-layout-item md-size-100 line1"> <v-divider /> </div> <!-- 1.搜索(Safumax LIVE)待審覈入金記錄 --> <!-- <md-button class="md-primary md-sm padding" @click="$router.push({name:'agentForm'})">建立代理</md-button> --> <span class="buttonspace"></span> <!-- 2.代理管理表 --> <div class="md-layout-item md-size-100"> <md-card> <!-- 標題 --> <md-card-header class="md-card-header-icon md-card-header-green"> <div class="card-icon"> <md-icon>repeat</md-icon> </div> <h4 class="title">客戶層級關係</h4> </md-card-header> <md-card-header> <div class="md-layout"> <!-- 1.暱稱 --> <div class="input md-layout-item md-size-30 md-xsmall-size-100"> <el-select clearable filterable v-model="filter.id" remote placeholder="請輸入中文名或MT帳號" :remote-method="remoteMethod" :loading="loading" > <el-option v-for="item in users" :key="item.id" :label="`${item.chineseName}--${item.phone}--${item.email}`" :value="item.id" ></el-option> </el-select> </div> <!-- 7.表單提交按鈕 --> <div class="md-layout-item md-size-70"> <el-button type="success" size="" style="float:left;margin-top:0px" @click="getData()" >查詢</el-button> </div> </div> </md-card-header> <!-- 內容 --> <md-card-content> <el-tree highlight-current ref="tree" :data="data" :render-after-expand="false" node-key="id" check-strictly @node-expand="nodeClick" :expand-on-click-node="true" > <span slot-scope="{ data }"> <div> <md-icon style="color:#f173ac;margin-top:-5px" type="md-warning" v-if="data.agentCount<1" >person</md-icon> <md-icon style="color:#FFC125;margin-top:-5px" type="md-warning" v-else>group</md-icon> <el-popover placement="top-start" title="MT帳號詳情" width="200" trigger="hover" :content="data.mtids.join(',')"> <span class="tree-span" slot="reference">{{ formatData(data) }}</span> </el-popover> </div> </span> </el-tree> <!-- </el-popover> --> </md-card-content> </md-card> </div> </div></template><script>import { isArray } from 'util';export default { data() { return { loading: false, data: [], getRelation: false, users: [], filter: { id: "" } }; }, async mounted() { this.data = await this.list(); console.log("data", this.data); }, methods: { async getData() { this.data = await this.list(); }, // 查找 async list(id) { if (this.getRelation) { return; } this.getRelation = true; const data = { ...this.filter }; if (id) data.id = id; const res = await this.$axios.get("userRelation", { params: data }); this.getRelation = false; this.addchildren(res.data.result.user.children); return [...[res.data.result.user]]; }, // node樹拼接 async nodeClick(data, node) { // console.log(1,data); if (data.children) { data.children = []; let result = await this.list(data.id); // console.log(2,result) if (result && result.length > 0) { this.$nextTick(() => { // console.log(3,data.id, result[0].children) this.$refs["tree"].updateKeyChildren(data.id, result[0].children); }); } } }, // 數據處理 formatData(data) { let str = `${data.chineseName} [直屬客戶數: ${data.agentCount};`; if (isArray(data.mtids) && data.mtids.length === 1) str += `MT帳號: ${data.mtids[0]}`; if (isArray(data.mtids) && data.mtids.length > 1) str += `MT帳號: ${data.mtids[0]}...`; return str += ` ]`; }, // 處理children addchildren(list) { for (let item of list) { if (item.agentCount > 0) { item.children = [{}]; } } }, // 遠程搜索 remoteMethod(query) { const res = await this.$axios.get("getUsersByName", { params: { content: query } }); // console.log(res.data.result.users) this.users = res.data.result.users; } }};</script><style>.md-menu-content.md-select-menu { z-index: 9999 !important; width: 100% !important;}.el-tree { color: rgb(1, 26, 3); text-align: center; font-size: 15px; margin: 0px !important; font-weight: 500;}.el-tree-node__content { height: 45px !important;}.el-tree-node__children { border-style: none none none none; border-width: 0.05em; border-color: #aaa;}.tree-span { margin-top: 0px; margin-left: 5px; display: inline-block;}.md-rose md-simple table-button tree-icon { margin-left: -30px;}</style>複製代碼