react echarts tree树图搜索展开功能示例详解

目录
  • 前言
    • 最终效果
    • 版本信息
    • 核心功能:
    • 关键思路:
  • 附上代码
    • 数据data.js
    • 功能:
    • TreeUtils
  • 总结:

前言

umi+antd-admin 框架中使用类组件+antd结合echarts完成树图数据展示和搜索展开功能

最终效果

版本信息

"antd": "3.24.2",

"umi": "^2.7.7",

"echarts": "^4.4.0",

"echarts-for-react": "^2.0.15-beta.1",

核心功能:

  • 左右树图数据展示,并且基本的展开功能
  • 左树图的搜索功能,搜索后结果自动展开默认显示上级
  • 点击左树图,右边树图对应节点展开
  • 右树图背景水印,全屏功能
  • 右树图定制节点样式

关键思路:

点击左边树状结构时,首先通过递归找到具体哪一个分支中包含对应结果,然后再通过递归一层一层的添加属性collapsed,如果改分支下面包含结果添加collapsed:false,其他分支添加collapsed:true

重新渲染时echarts tree的options中initialTreeDepth属性需要设置一下,为结果点所在层级

附上代码

数据data.js

export const lineLabel = { //树图中部分节点展示样式为线上样式
  fontSize: 14,
  color: '#333333',
  offset: [ 5, -15],
  borderRadius: 0,
  borderColor: 'transparent',
  backgroundColor: 'transparent',
  position: 'left',
  verticalAlign: 'middle',
  align: 'right',
}
// 数据
export const data = [
  { yybid: '1', fid: '0', grade: '0', yybmc: '课程',itemStyle: { color: "#DE4A3C" } },
  { yybid: '101', fid: '1', grade: '1', yybmc: '语文',itemStyle: { color: "#DE4A3C" } },
  { yybid: '1011', fid: '101', grade: '2', yybmc: '听写',itemStyle: { color: "#DE4A3C" } },
  { yybid: '10111', fid: '1011', grade: '3', yybmc: '生字',itemStyle: { color: "#DE4A3C" }},
  { yybid: '10111-1', fid: '10111', grade: '4', yybmc: '文字',itemStyle: { color: "#DE4A3C" },label:lineLabel }, // 比如这个就是显示在线上的意思,可以定制节点样式
  { yybid: '10111-1-1', fid: '10111-1', grade: '5', yybmc: '同音字',itemStyle: { color: "#DE4A3C" } },
  { yybid: '10111-1-1-1', fid: '10111-1-1', grade: '6', yybmc: '...',itemStyle: { color: "#FFFFFF" } },
  { yybid: '10111-1-1-2', fid: '10111-1-1', grade: '6', yybmc: '...',itemStyle: { color: "#FFFFFF" } },
  { yybid: '10111-1-1-3', fid: '10111-1-1', grade: '6', yybmc: '...',itemStyle: { color: "#FFFFFF" } },
  { yybid: '10111-1-1-4', fid: '10111-1-1', grade: '6', yybmc: '...',itemStyle: { color: "#FFFFFF" } },
  { yybid: '10111-1-2', fid: '10111-1', grade: '5', yybmc: '多音字',itemStyle: { color: "#DE4A3C" } },
    { yybid: '102', fid: '1', grade: '1', yybmc: '数学',itemStyle: { color: "#DE4A3C" } },
    .....
]

功能:

import React from 'react';
import { Row, Col, Input, Tree, Button, Icon } from 'antd';
import TreeUtils from '../../../../../utils/treeUtils';
import ReactEcharts from 'echarts-for-react'
import styles from './index.less';
import { data } from './data'
const { Search } = Input;
class Map extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      cNameTreeData: [],
      cNameList: [],
      expandedKeys: [], // 展开节点key
      autoExpandParent: true,
      cmapOpt:{},
      cmapTreeData:{},
      cmapTreeDataOrigin:{},
      defaultExpandedKeys:[], // 默认展开节点key
      keyWord:'',//搜索关键字
      isFullScreen:false,
    };
  }
  componentDidMount() {
    this.fetchcName();
  }
  getOption =(data,initialTreeDepth)=>{
    // 设置水印
    let user =  { name :'管理员' , loginName :'admin'}
    const waterMarkText = `${user.name} ${user.loginName}`
    const canvas = document.createElement('canvas')
    canvas.width = 200
    canvas.height = 150
    const ctx = canvas.getContext('2d')
    ctx.textAlign = 'center'
    ctx.textBaseline = 'middle'
    ctx.globalAlpha = 0.09
    ctx.font = '16px sans-serif'
    ctx.translate(70,90)
    ctx.rotate(-Math.PI / 4)
    ctx.fillText(waterMarkText, 0, 0)
    const opt= {
      backgroundColor: {
        image:canvas
      },
      toolbox: {
          top:30,
          right:30,
          itemSize:24,
          feature: {
        //自定义toolbox,必须以my开头,全屏功能
        myFullScreen:{
          show:true,
          title:this.state.isFullScreen?'退出全屏':'全屏',
          icon:"image://" + require("../../../../../assets/fullScreen.png"),
          emphasis: {
            iconStyle: {
                textFill: '#DE4A3C',  //文本颜色,若未设定,则依次取图标 emphasis 时的填充色、描边色,若都不存在,则为'#000'
                textAlign: 'center',  //文本对齐方式,属性值:left/center/right
            }
          },
          onclick: (e) => {
              //isFullScreen定义在data中,初始化为false
              this.setState({
                isFullScreen:!this.state.isFullScreen
              },()=>{
                const element = document.getElementById('cmapTree');
                if (element.requestFullScreen) { // HTML W3C 提议
                  element.requestFullScreen();
                } else if (element.msRequestFullscreen) { // IE11
                  element.msRequestFullScreen();
                } else if (element.webkitRequestFullScreen) { // Webkit (works in Safari5.1 and Chrome 15)
                  element.webkitRequestFullScreen();
                } else if (element.mozRequestFullScreen) { // Firefox (works in nightly)
                  element.mozRequestFullScreen();
                }
                // 退出全屏
                if (element.requestFullScreen) {
                  document.exitFullscreen();
                } else if (element.msRequestFullScreen) {
                  document.msExitFullscreen();
                } else if (element.webkitRequestFullScreen) {
                  document.webkitCancelFullScreen();
                } else if (element.mozRequestFullScreen) {
                  document.mozCancelFullScreen();
                }
              })
          }
        }
      }
      },
      series: [
      {
          type: 'tree',
          // silent:true,
          data: [data],
          top: '10%',
          left: '10%',
          bottom: '10%',
          right: '15%',
          // edgeShape:'polyline',
          symbolSize: 10,
          // symbolSize: [30, 30],
          label: {
            color: '#FFFFFF',
            distance: 0,
            fontSize: 16,
            borderWidth: 0,
            borderRadius: 4,
            borderColor: 'rgba(222, 74, 60, 0.9)',
            backgroundColor: 'rgba(222, 74, 60, 0.9)',
            padding: [6, 10, 6 ,10], // 最开始的样式
            // padding: [6, 10],
            position: 'left',
            verticalAlign: 'middle',
            align: 'right', // 最开始的样式
            // align: 'insideRight',
          },
          leaves: {
              label: {
                  position: 'right', // 最开始的样式
                  // position: 'left',
                  verticalAlign: 'middle',
                  align: 'left',
                  color: '#333333',
                  distance: -15,
                  margin: 0,
                  fontSize: 16,
                  borderWidth: 0,
                  borderColor: 'rgba(222, 74, 60, 0.1)',
                  backgroundColor: 'rgba(222, 74, 60, 0.1)',
                  borderRadius: 4,
                  padding: [6, 10, 6 , 20], // 最开始的样式
                  // padding: [6, 10],
              }
          },
          itemStyle:{
            borderType : 'solid',
            borderWidth : 2,
            borderColor : '#DE4A3C',
          },
          lineStyle:{
            color:'#DE4A3C'
          },
          edgeForkPosition: "72%",
          emphasis: { // 高亮
            focus: 'descendant'
          },
          animationDuration: 300,
          animationDurationUpdate: 300,
          initialTreeDepth:initialTreeDepth, // 树图初始展开层级,树图初始展开的层级(深度)。根节点是第 0 层,然后是第 1 层、第 2 层,... ,直到叶子节点
          roam:true,//鼠标缩放,拖拽整颗树
          expandAndCollapse: true,//无关的子树折叠收起
          // width: "50%"//组件宽度
        }
      ]
    }
    this.setState({
        cmapOpt:opt
    })
  }
  // 获取名称
  fetchcName = () => {
    const cName = {};
    // 设置树数据
    const datas = TreeUtils.toTreeData(data, { keyName: 'yybid', pKeyName: 'fid', titleName: 'yybmc', normalizeTitleName: 'title', normalizeKeyName: 'key' }, true);
    cName.datas = [];
    datas.forEach((item) => {
      const { children } = item;
      cName.datas.push(...children);
    });
    cName.dataLoaded = true;
    // 设置树形图数据
    const optData = TreeUtils.toTreeMapData(data, { keyName: 'yybid', pKeyName: 'fid', titleName: 'yybmc', normalizeTitleName: 'name', normalizeKeyName: 'value' }, true);
    cName.optDatas = [];
    optData.forEach((item) => {
      const { children } = item;
      cName.optDatas.push(...children);
    });
    // 设置默认展开第一层
    const expandedKeys = []
    cName.datas.forEach(item =>{
      expandedKeys.push(item.key)
    })
    this.setState({
      cNameTreeData: cName.datas,
      cNameList: data,
      cmapTreeData:cName.optDatas[0],
      cmapTreeDataOrigin:cName.optDatas[0],
      expandedKeys,
    },()=>{
      this.getOption(cName.optDatas[0],1)
    });
  }
  // 关键字搜索
  handleOnkeyWord = (e) => {
    const keyWord = e.target.value;
    this.setState({
      keyWord:keyWord.trim()
    })
  }
  // 搜索功能
  searchTree = () =>{
    if(this.state.keyWord){
      this.getTreeExpand(this.state.keyWord)
    }else {
      // 设置默认展开第一层
      const cName = {};
      const datas = TreeUtils.toTreeData(data, { keyName: 'yybid', pKeyName: 'fid', titleName: 'yybmc', normalizeTitleName: 'title', normalizeKeyName: 'key' }, true);
      cName.datas = [];
      datas.forEach((item) => {
        const { children } = item;
        cName.datas.push(...children);
      });
      const expandedKeys = []
      cName.datas.forEach(item =>{
        expandedKeys.push(item.key)
      })
      this.setState({
        cNameTreeData: cName.datas,
        expandedKeys,
      });
    }
  }
  // 设置左树展开
  getTreeExpand = (keyWord) =>{
      // 筛选数据
      const { cNameList } = this.state;
      const newTreeList = cNameList.filter((item) => {
        if (item.yybmc.indexOf(keyWord) !== -1) {
          return true;
        }
        return false;
      });
      const newTreeParent = [];
      const expandedKeys = [];
      // 获取所有子节点的父节点
      newTreeList.forEach((item) => {
        expandedKeys.push(item.yybid);
        // 不是根节点
        newTreeParent.push(item);
        for (let i = item.grade; i > 0; i--) {
          const newParent = this.getByChildId(newTreeParent[newTreeParent.length - 1].fid);
          newTreeParent.push(newParent[0]);
        }
      });
      // 合并数组
      const tempNewData = [...newTreeParent, ...newTreeList];
      // 数组去重
      let newData = new Set(tempNewData);
      newData = [...newData];
      // 构造树形数据
      const newTreeData = [];
      const datas = TreeUtils.toTreeData(newData, { keyName: 'yybid', pKeyName: 'fid', titleName: 'yybmc', normalizeTitleName: 'title', normalizeKeyName: 'key' }, true);
      newTreeData.datas = [];
      datas.forEach((item) => {
        const { children } = item;
        newTreeData.datas.push(...children);
      });
      newTreeData.dataLoaded = true;
      this.setState({
        cNameTreeData: newTreeData.datas,
        expandedKeys,
        autoExpandParent: true,
      });
  }
  // 根据子节点找到父节点
  getByChildId(childId) {
    return this.state.cNameList.filter((item) => {
      return item.yybid === childId;
    });
  }
  // 选中树形数据
  onSelect = (selectedKeys) => {
    const select = this.state.cNameList.filter((item) => {
      return item.yybid === selectedKeys[0];
    });
    if (select && select.length > 0) {
      this.handleOnExpand(select[0])
    }
  }
  onExpand = (expandedKeys) => {
    this.setState({
      expandedKeys,
      autoExpandParent: false,
    });
  };
  // 处理data中的children添加属性展开显示
  addCollapsed = (children, selectItem) => {
    let {yybid} = selectItem;
    const newChildren = []
    children.forEach(obj =>{
      let newObj = {}
      // newObj = {...obj,collapsed :true}
      if(obj.value === yybid){
        newObj = {...obj,collapsed :false}
      }else if(obj.children && obj.children.length) {
        if(!this.isHaveChildren(obj.children,yybid)){
          let newChildren = {}
          newChildren = this.addCollapsed(obj.children,selectItem)
          newObj = {...obj,collapsed :true,children:newChildren}
        }else{
          let newChildren = {}
          newChildren = this.addCollapsed(obj.children,selectItem)
          newObj = {...obj,collapsed :false,children:newChildren}
        }
      }else {
        newObj = {...obj,collapsed :true}
      }
      newChildren.push({...obj,...newObj})
    })
    return newChildren
  }
  // 判断下面是否有子节点
  isHaveChildren = (arr,selectId) =>{
    const res = []
    arr.forEach(item =>{
      if(item.value === selectId){
        res.push('true')
      }else if(item.children && item.children.length) {
        res.push(String(this.isHaveChildren(item.children,selectId)))
      }else {
        res.push('false')
      }
    })
    return res.some(resObj => resObj === 'true')
  }
  // 树状图搜索展开
  handleOnExpand = (selectItem) =>{
    const { grade } = selectItem
    const { children } = this.state.cmapTreeDataOrigin
    let newChildren = []
    if(grade === '0'){
      // 设置树形图数据
      const optData = TreeUtils.toTreeMapData(data, { keyName: 'yybid', pKeyName: 'fid', titleName: 'yybmc', normalizeTitleName: 'name', normalizeKeyName: 'value' }, true);
      let optDatas = [];
      optData.forEach((item) => {
        const { children } = item;
        optDatas.push(...children);
      });
      return this.setState({
        cmapTreeData:optDatas[0]
      },()=>{
        this.getOption(optDatas[0],'1')
      })
    }else if(grade === '1'){
      children.forEach(obj =>{
        let newObj = {}
        if(obj.value === selectItem.yybid){
          newObj = {...obj,collapsed :false}
        }else {
          newObj = {...obj,collapsed :true}
        }
        newChildren.push({...obj,...newObj})
      })
    }else {
      newChildren = this.addCollapsed(children, selectItem)
      // console.log(selectItem)
    }
    this.setState({
      cmapTreeData:{
        ...this.state.cmapTreeData,
        children:newChildren
      }
    },()=>{
      this.getOption(this.state.cmapTreeData,grade)
    })
  }
  componentWillUnmount = () => {
    this.setState = (state,callback)=>{
      return;
    };
}
  render() {
    const { cNameTreeData, autoExpandParent, expandedKeys,cmapOpt } = this.state;
    return (
      <React.Fragment>
            <Row style={{height:'88vh'}}>
              <Col xs={5} sm={5} lg={5} xl={5} style={{height:'100%'}}>
                <div style={{width:'100%',display:'flex',height:'100%'}}>
                  <div style={{height:'100%',width:'90%',padding:'16px 20px 0px 20px'}}>
                    {/* 搜索框 */}
                    <div style={{fontSize:'16px',marginBottom:'10px',color:'#666666',fontWeight:'bold'}}>搜索功能</div>
                    <Search
                      allowClear
                      style={{ width: '100%',height:'40px', margin: '0px 10px 10px 0px',borderColor:'#999' }}
                      placeholder="请输入"
                      onChange={e => this.handleOnkeyWord(e)}
                      onPressEnter={this.searchTree}
                      onSearch={this.searchTree} />
                    {/* 树形组件 */}
                    <Tree
                      style={{height:'85%',overflowY:'auto',width:'100%'}}
                      className={styles.tree}
                      onSelect={this.onSelect}
                      onExpand={this.onExpand}
                      expandedKeys={expandedKeys}
                      autoExpandParent={autoExpandParent}
                      treeData={cNameTreeData}
                      height={800}
                    />
                  </div>
                </div>
              </Col>
                {/* tree图 */}
              {cmapOpt?
                  <Col xs={19} sm={19} lg={19} xl={19} style={{height:'100%'}}>
                  {/* <div style={{position:'absolute',top:'35px',right:'30px',fontSize:'16px',color:'#DE4A3C'}}>全屏</div> */}
                  <div  id="cmapTree" style={{backgroundColor:'white',height:'100%'}}>
                    <ReactEcharts
                      option={cmapOpt}
                      style={{ width: '100%', height: '100%' }}
                      notMerge />
                  </div>
                  </Col> : ''
              }
            </Row>
      </React.Fragment>
    );
  }
}
export default cMap;

TreeUtils

/**
 * Object对象相关的自定义处理函数
 */
const TreeUtils = {
  toTreeData(datas, { keyName = 'id', pKeyName = 'pid', titleName = 'name', normalizeTitleName = 'title', normalizeKeyName = 'key', parentName = 'fid' }, normalize = true, persistPrimaryData = false) { // 将普通数据转成树状结构
    // persistPrimaryData:保留原来的数据
    const tree = [];
    const noParentTemp = []; // 临时存放所有父节点,一旦改父节点是另一个节点的子节点,那么就删掉,最后剩下的就是没有完整父节点的节点了
    const isChildTemp = []; // 存放所有曾为子节点的节点
    const relation = {}; // 存放节点数据及其之间的关系
    // 遍历数据
    datas.forEach((data) => {
      const key = data[keyName];
      const pKey = data[pKeyName];
      const title = data[titleName];
      // 记录所有的子节点信息
      isChildTemp.push(key);
      // 记录暂时还没有发现父节点的项
      if (!noParentTemp.includes(pKey)) {
        noParentTemp.push(pKey);
      }
      // 如果发现该项在"暂时没有完整父节点"数组中,那么就从数组中删除掉
      if (noParentTemp.includes(key)) {
        noParentTemp.splice(noParentTemp.indexOf(key), 1);
      }
      // 将当前项的数据存在relation中
      const itemTemp = normalize ? { [normalizeKeyName]: key, [normalizeTitleName]: title, [parentName]: pKey } : { ...data };
      if (persistPrimaryData) {
        Object.assign(itemTemp, { primaryData: data });
      }
      if (!relation[key]) {
        relation[key] = {};
      }
      Object.assign(relation[key], itemTemp);
      // 将当前项的父节点数据也存在relation中
      if (!relation[pKey]) {
        relation[pKey] = normalize ? { [normalizeKeyName]: pKey } : { [keyName]: pKey };
      }
      // 如果作为父节点,没有children.那么就加上
      if (!relation[pKey].children) {
        relation[pKey].children = [];
      }
      // 将父子节点通过children关联起来,形成父子关系
      relation[pKey].children.push(relation[key]);
    });
    // 将没有完整父节点的节点过滤一下,剩下的就是没有父节点的节点了(如果只有一个,那就是根节点根节点)
    noParentTemp.forEach((key) => {
      if (!isChildTemp.includes(key)) {
        tree.push(relation[key]);
      }
    });
    return tree;
  },
  toTreeMapData(datas, { keyName = 'id', pKeyName = 'pid', titleName = 'name', normalizeTitleName = 'title', normalizeKeyName = 'key', parentName = 'fid' }, normalize = true, persistPrimaryData = false) { // 将普通数据转成树状结构
    // persistPrimaryData:保留原来的数据
    const tree = [];
    const noParentTemp = []; // 临时存放所有父节点,一旦改父节点是另一个节点的子节点,那么就删掉,最后剩下的就是没有完整父节点的节点了
    const isChildTemp = []; // 存放所有曾为子节点的节点
    const relation = {}; // 存放节点数据及其之间的关系
    // 遍历数据
    datas.forEach((data) => {
      const key = data[keyName];
      const pKey = data[pKeyName];
      const title = data[titleName];
      const itemStyle = data['itemStyle']
      const label = data['label']
      // 记录所有的子节点信息
      isChildTemp.push(key);
      // 记录暂时还没有发现父节点的项
      if (!noParentTemp.includes(pKey)) {
        noParentTemp.push(pKey);
      }
      // 如果发现该项在"暂时没有完整父节点"数组中,那么就从数组中删除掉
      if (noParentTemp.includes(key)) {
        noParentTemp.splice(noParentTemp.indexOf(key), 1);
      }
      // 将当前项的数据存在relation中
      const itemTemp = normalize ? { [normalizeKeyName]: key, [normalizeTitleName]: title, [parentName]: pKey ,'itemStyle':itemStyle,'label':label} : { ...data };
      if (persistPrimaryData) {
        Object.assign(itemTemp, { primaryData: data });
      }
      if (!relation[key]) {
        relation[key] = {};
      }
      Object.assign(relation[key], itemTemp);
      // 将当前项的父节点数据也存在relation中
      if (!relation[pKey]) {
        relation[pKey] = normalize ? { [normalizeKeyName]: pKey } : { [keyName]: pKey };
      }
      // 如果作为父节点,没有children.那么就加上
      if (!relation[pKey].children) {
        relation[pKey].children = [];
      }
      // 将父子节点通过children关联起来,形成父子关系
      relation[pKey].children.push(relation[key]);
    });
    // 将没有完整父节点的节点过滤一下,剩下的就是没有父节点的节点了(如果只有一个,那就是根节点根节点)
    noParentTemp.forEach((key) => {
      if (!isChildTemp.includes(key)) {
        tree.push(relation[key]);
      }
    });
    return tree;
  },
};
export default TreeUtils;

总结:

对两种树状图进行功能结合,并且添加搜索结果联动,感觉自己写的功能代码不够简洁,如果有问题欢迎大家积极提出讨论,共同进步~

以上就是react echarts tree树图搜索展开功能示例详解的详细内容,更多关于react echarts tree树图搜索的资料请关注我们其它相关文章!

(0)

相关推荐

  • react native图片解析流程详解

    目录 正文 1. 构建输出的产物 2. js bundle分析 3. 图片source拼接 3.1 如果bundle放在服务器(本地开发) 3.2 bundle内置在app中(app下载bundle和assets后执行) 4. Image style的witdh和height没有声明会发生什么? 正文 我们知道,在react-native中加载一张图片需要使用Image组件,其有两种使用方式 import bg from './bg.png'; // 1. 加载本地图片资源 <Image sou

  • react-native只保留3x图原理解析

    目录 引言 1. 输出构建产物 2. RN如何决定加载哪张scale图片? 3. repo中是否可以只保留3x图? 3.1 资源上传 3.2 资源下载 4. 结论 引言 我们的react-native项目中,一张图片一般会存在1x, 1.5x, 2x, 3x几种尺寸(1.5x是android特有的),以便在不同屏幕尺寸的手机加载对应尺寸的图片. 1. 输出构建产物 如果我们在代码中引入了一张图片,例如 // index.js import bg from './bg.png'; . ├── in

  • React 状态管理工具优劣势示例分析

    目录 什么是状态管理? React 状态管理方案 方案介绍 方案对比 Source 相关建议 什么是状态管理? “状态”是描述应用程序当前行为的任何数据.这可能包括诸如“从服务器获取的对象列表”.“当前选择的项目”.“当前登录用户的名称”和“此模式是否打开?”等值. 众所周知,我们在研发一个复杂应用的过程中,一套好的状态管理方案是必不可少的,既能提升研发效率,又能降低研发维护成本,那么状态管理方案那么多,它们有什么不同,我们又该如何选择适合当前应用的方案呢? 本期将主要就 react 的常用状态

  • React commit源码分析详解

    目录 总览 commitBeforeMutationEffects commitMutationEffects 插入 dom 节点 获取父节点及插入位置 判断当前节点是否为单节点 在对应位置插入节点 更新 dom 节点 更新 HostComponent 更新 HostText 删除 dom 节点 unmountHostComponents commitNestedUnmounts commitUnmount commitLayoutEffects 执行生命周期 处理回调 总结 总览 commit

  • React18系列reconciler从0实现过程详解

    目录 引言 React-Dom包 createRoot hostConfig.ts React-reconciler包 createContainer() 函数 render() 调用 创建更新createUpdate 将更新推进队列enqueueUpdate 开始调用scheduleUpdateOnFiber wookLoop beginWork开始 updateHostRoot reconcileChildren reconcileSingleElement 例子 completeWork开

  • react hooks d3实现企查查股权穿透图结构图效果详解

    目录 前言 最终效果: 版本信息: 股权穿透图基础功能: 股权结构图基础功能: 股权穿透图代码 股权结构图代码 总结: 前言 umi+antd-admin 框架中使用hooks结合d3完成类似股权穿透图和股权结构图(web) 最终效果: 股权穿透图 股权结构图 版本信息: "d3": "4.13.0", "antd": "3.24.2", "umi": "^2.7.7", 股权穿透图基础

  • React18从0实现dispatch update流程

    目录 引言 hooks原理 useState初始化(mount) mountState mountWorkInProgressHook 处理hook数据 生成dispatch 更新的总结 useState触发更新(dispatch) updateState hook数据从哪里来 updateWorkInProgressHook hook在条件语句中报错 useState计算 双缓存树 总结 引言 本系列是讲述从0开始实现一个react18的基本版本.由于React源码通过Mono-repo 管理

  • react echarts tree树图搜索展开功能示例详解

    目录 前言 最终效果 版本信息 核心功能: 关键思路: 附上代码 数据data.js 功能: TreeUtils 总结: 前言 umi+antd-admin 框架中使用类组件+antd结合echarts完成树图数据展示和搜索展开功能 最终效果 版本信息 "antd": "3.24.2", "umi": "^2.7.7", "echarts": "^4.4.0", "echart

  • React实现数字滚动组件numbers-scroll的示例详解

    目录 一.设计原理 二.实现方式 三.使用方式 四.参数说明 数字滚动组件,也可以叫数字轮播组件,这个名字一听就是非常普通常见的组件,第一反应就是想找找网上大佬的东西顶礼膜拜一下,这一搜,还真是没找到趁手的╮(╯▽╰)╭. 最近接了大屏的需求,数字滚动肯定是免不了的,所以开始撸袖子,造轮子了( numbers-scroll ). 首先给大家看下轮子的效果吧: 一.设计原理 如果要做到数字滚动效果,就一定要让数字有从下往上移动的感觉.如果只是纯粹的数字变化,显示出来的效果就会比较普通了,没有什么视

  • jquery实现搜索框功能实例详解

    搜索框实现搜索一个ul列表中的指定关键词的li. html代码: <ul class="todo-content"> <li class="todo-ltem"> <div class="todo-tip"> <p>fhasjfas</p> </div> <div class="todo-btnlist"> <button class=&

  • React Native 中实现确认码组件示例详解

    目录 正文 实现原理 开源方案 正文 确认码控件也是一个较为常见的组件了,乍一看,貌似较难实现,但实则主要是障眼法. 实现原理 上图 CodeInput 组件的 UI 结构如下: <View style={[styles.container]}> <TextInput autoFocus={true} /> <View style={[styles.cover, StyleSheet.absoluteFillObject]} pointerEvents="none&

  • RocketMQ实现随缘分BUG小功能示例详解

    目录 正文 实现过程 生产者: 正文 以前公司的产品已经上线20多年了,主要是维护,也就是改bug.每周我们Team会从Jira上拿我们可以改的bug,因为每个团队负责的业务范围不一样,我们团队只能改我们自己业务范围的.这样每周大概有20个左右的新bug,假如团队一共10个人,那么均分就是每人两个,改完下班. 但是这BUG肯定有难有简单,大家肯定都愿意改简单的,在家办公,任务量完了不就等于放假么.开始是自己给自己抢,就忒卷,是欧美项目,所以客服晚上报出来的bug多.有的哥们早上5点起来看有没有新

  • vue3使用自定义指令实现el dialog拖拽功能示例详解

    目录 实现el-dialog的拖拽功能 通过自定义指令实现拖拽功能 实现拖拽功能 使用方式 实现el-dialog的拖拽功能 这里指的是 element-plus 的el-dialog组件,一开始该组件并没有实现拖拽的功能,当然现在可以通过设置属性的方式实现拖拽. 自带的拖拽功能非常严谨,拖拽时判断是否拖拽出窗口,如果出去了会阻止拖拽. 如果自带的拖拽功能可以满足需求的话,可以跳过本文. 通过自定义指令实现拖拽功能 因为要自己操作dom(设置事件),所以感觉还是使用自定义指令更直接一些,而且对原

  • 使用纯JavaScript封装一个消息提示条功能示例详解

    目录 介绍 思路&布局 操作逻辑 完整代码 介绍 一个类似Element UI.Ant-Design UI等 UI 框架的消息提示功能,方便在任何网页环境中直接调用函数使用:区别在不依赖 js 及 css 引用,而是使用纯 js 进行封装实现,代码更精简,同时保持和 UI 框架一样的视觉效果(可自行修改成自己喜欢的样式) 代码仓库 在线预览效果(点击[登录].[点击复制]按钮时触发提示效果) 思路&布局 先来写单个提示条,并实现想要的过渡效果,最后再用逻辑操作输出节点即可:这里不需要父节点

  • 实现shallowReadonly和isProxy功能示例详解

    目录 一.实现shallowReadonly (一)单元测试 (二)代码实现 (三)额外的单测 二.实现isProxy (一)单元测试 (二)代码实现 一.实现shallowReadonly (一)单元测试 // src/reactivity/tests/shallowReadonly.spec.ts import { isReadonly, shallowReadonly } from '../reactive'; describe('shallowReadonly', () => { it(

  • Springboot Vue实现单点登陆功能示例详解

    目录 正文 简单上个图 先分析下登陆要做啥 怎么落实? 上代码 接口: token生成部分 刷新token 验证token 正文 登陆是系统最基础的功能之一.这么长时间了,一直在写业务,这个基础功能反而没怎么好好研究,都忘差不多了.今天没事儿就来撸一下. 以目前在接触和学习的一个开源系统为例,来分析一下登陆该怎么做.代码的话我就直接CV了. 简单上个图 (有水印.因为穷所以没开会员) 先分析下登陆要做啥 首先,搞清楚要做什么. 登陆了,系统就知道这是谁,他有什么权限,可以给他开放些什么业务功能,

  • react+axios实现github搜索用户功能(示例代码)

    加载 请求成功 请求失败 在文件路径点击cmd 回车 首先把服务器打开 npm start app.js import React, { Component } from 'react' import "./App.css" import Header from './conompents/Header' import List from './conompents/List' export default class App extends Component { // 初始化sta

随机推荐