前端实现人员关系图谱

前端实现人员关系图谱入职前端工作到现在差不多有一年半的时间了,和朋友偶然聊天的时候被问到,能不能用所学的前端知识做一个家族关系的族谱,可以使家族关系更加简单明了。当时听完这个需求,觉得可能还是蛮简单的,后来动手做的时候,发现族谱的连线,是需要根据返回的数据动态生成的,这就是我这个小前端,有点头秃了????。解决技术困难当时阻碍我前进的就是如何实现族谱的连线以及根据数据渲染它们的对应关系,后来在逛博客的过程中,发现了antdesign的charts图表组件。利用这个组件,如果可以进行一些改造,可能就可以实现族谱的关系图。

大家好,又见面了,我是你们的朋友全栈君。

入职前端工作到现在差不多有一年半的时间了,和朋友偶然聊天的时候被问到,能不能用所学的前端知识做一个家族关系的族谱,可以使家族关系更加简单明了。当时听完这个需求,觉得可能还是蛮简单的,后来动手做的时候,发现族谱的连线,是需要根据返回的数据动态生成的,这就是我这个小前端,有点头秃了?。

解决技术困难

当时阻碍我前进的就是如何实现族谱的连线以及根据数据渲染它们的对应关系,后来在逛博客的过程中,发现了antdesign的charts图表组件。利用这个组件,如果可以进行一些改造,可能就可以实现族谱的关系图。
在这里插入图片描述

开始动手

首先需要安装ant-design/charts,具体安装过程请参考官方文档

安装完成以后,就要根据数据渲染出想要的视觉效果,由于svg图相关的知识比较薄弱,所以实现的视觉可能有点丑陋,大家将就着看看,我写这篇文章的目的就是总结自己的技术探索历程,可能这篇文章下周就修改了。
先看视觉效果。
在这里插入图片描述
代码:

import React, { 
    useEffect, useState } from "react";
import "./Treechart.css";
import { 
    Modal } from "antd";
import { 
    OrganizationGraph } from "@ant-design/charts";
import * as _ from "lodash"


// const data = { 
   
// id: "joel",
// value: { 
   
// text: "李伟峰",
// value: "陈兰兰",
// // 建议使用 bae64 数据
// icon: "https://avatars.githubusercontent.com/u/31396322?v=4",
// style: { 
   
// fill: "#00CED1",
// width: "200",
// },
// img: "/api/img/%E8%80%81%E4%BA%BA.jpeg",
// },
// style: { 
   
// width: 110,
// height: 40,
// // stroke: "#87ceeb",
// fill: " #FFC0CB",
// radius: "8",
// // textAlign: "center",
// },
// children: [
// { 
   
// id: "c1",
// value: { 
   
// text: "李伟峰",
// value: "陈兰兰",
// // 建议使用 bae64 数据
// icon: "https://avatars.githubusercontent.com/u/31396322?v=4",
// },
// children: [
// { 
   
// id: "c1-1",
// value: { 
   
// text: "李伟峰",
// value: "陈兰兰",
// // 建议使用 bae64 数据
// icon: "https://avatars.githubusercontent.com/u/31396322?v=4",
// },
// },
// { 
   
// id: "c1-2",
// value: { 
   
// text: "李伟峰",
// value: "陈兰兰",
// // 建议使用 bae64 数据
// icon: "https://avatars.githubusercontent.com/u/31396322?v=4",
// },
// children: [
// { 
   
// id: "c1-2-1",
// value: { 
   
// text: "李伟峰",
// value: "陈兰兰",
// // 建议使用 bae64 数据
// icon: "https://avatars.githubusercontent.com/u/31396322?v=4",
// },
// },
// { 
   
// id: "c1-2-2",
// value: { 
   
// text: "李伟峰",
// value: "陈兰兰",
// // 建议使用 bae64 数据
// icon: "https://lwfcll.oss-cn-hangzhou.aliyuncs.com/img/1630238333160.png",
// },
// },
// ],
// },
// ],
// },
// { 
   
// id: "c2",
// value: { 
   
// text: "李伟峰",
// value: "陈兰兰",
// // 建议使用 bae64 数据
// icon: "https://avatars.githubusercontent.com/u/31396322?v=4",
// },
// },
// { 
   
// id: "c3",
// value: { 
   
// text: "李伟峰",
// value: "陈兰兰",
// // 建议使用 bae64 数据
// icon: "https://avatars.githubusercontent.com/u/31396322?v=4",
// },
// children: [
// { 
   
// id: "c3-1",
// value: { 
   
// text: "李伟峰",
// value: "陈兰兰",
// // 建议使用 bae64 数据
// icon: "https://avatars.githubusercontent.com/u/31396322?v=4",
// },
// },
// { 
   
// id: "c3-2",
// value: { 
   
// text: "李伟峰",
// value: "陈兰兰",
// // 建议使用 bae64 数据
// icon: "https://lwfcll.oss-cn-hangzhou.aliyuncs.com/img/%E4%B8%AD%E5%B9%B4%E5%A4%AB%E5%A6%872.jpeg",
// },
// children: [
// { 
   
// id: "c3-2-1",
// value: { 
   
// text: "李伟峰",
// value: "陈兰兰",
// // 建议使用 bae64 数据
// icon: "https://avatars.githubusercontent.com/u/31396322?v=4",
// },
// },
// { 
   
// id: "c3-2-2",
// value: { 
   
// text: "李伟峰",
// value: "陈兰兰",
// // 建议使用 bae64 数据
// icon: "https://avatars.githubusercontent.com/u/31396322?v=4",
// },
// },
// { 
   
// id: "c3-2-3",
// value: { 
   
// text: "李伟峰",
// value: "陈兰兰",
// // 建议使用 bae64 数据
// icon: "https://avatars.githubusercontent.com/u/31396322?v=4",
// },
// },
// ],
// },
// { 
   
// id: "c3-3",
// value: { 
   
// text: "李伟峰",
// value: "陈兰兰",
// // 建议使用 bae64 数据
// icon: "https://avatars.githubusercontent.com/u/31396322?v=4",
// },
// },
// ],
// },
// ],
// };

const colorArr = ["#00CED1", "#FFA07A", "#87CEFA", "#BA55D3", "#00FA9A"];

export default function TreeChart() { 
   
  /** * 遍历树的方法 */
  // const traverseTree = (data, fn) => { 
   
  // if (typeof fn !== "function") { 
   
  // return;
  // }

  // if (fn(data) === false) { 
   
  // return false;
  // }

  // if (data && data.children) { 
   
  // for (let i = data.children.length - 1; i >= 0; i--) { 
   
  // if (!traverseTree(data.children[i], fn)) return false;
  // }
  // }
  // return true;
  // };

  // traverseTree(data, (d) => { 
   
  // d.leftIcon = { 
   
  // style: { 
   
  // fill: "#e6fffb",
  // stroke: "#e6fffb",
  // },
  // img:
  // "https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*Q_FQT6nwEC8AAAAAAAAAAABkARQnAQ",
  // };
  // d.rightIcon = { 
   
  // style: { 
   
  // fill: "#e6fffb",
  // stroke: "#e6fffb",
  // },
  // img:
  // "https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*Q_FQT6nwEC8AAAAAAAAAAABkARQnAQ",
  // };
  // return true;
  // });
  const [data, setData] = useState({ 
   })

  const randomNum = (minNum: number, maxNum: number) => { 
   
    switch (arguments.length) { 
   
      case 1:
        return parseInt(String(Math.random() * minNum + 1), 10);
        break;
      case 2:
        return parseInt(String(Math.random() * (maxNum - minNum + 1) + minNum), 10);
        break;
      default:
        return 0;
        break;
    }
  };

  const fetchTableData = () => { 
   
    fetch("http://localhost:9091/genealogy/info/selectUserPage", { 
   
      method: "POST",
      body: JSON.stringify({ 
   
        size: 20,
        current: 1,
      }),
      headers: { 
   
        "content-type": "application/json",
      },
    }).then((res) => { 
   
      res.json().then((data) => { 
   
        setData(arrayToTree(data.data)[0])
        console.log(456, arrayToTree(data.data)[0])
      });
    });
  };

  const arrayToTree = (items: []) => { 
   
    const result: any = [];   // 存放结果集
    const itemMap: any = { 
   };
    let newItem: any = _.cloneDeep(items);
    newItem = _.map(newItem, (item: any) => { 
   
      let compain = newItem.findIndex((i: any) => item?.companionId === i.id)
      let value = ''
      if (compain !== -1) { 
   
        value = newItem[compain]?.name;
        newItem.splice(compain, 1)

      }
      return { 
   
        ...item,
        value
      }
    })
    items = newItem.filter((item: any) => item?.id)
    console.log(123, items)
    items.sort((x: any, y: any) => { 
   
      return x.id - y.id
    })
    console.log(items)
    // 
    for (const item: any of items) { 
   
      const id = item.id;
      const parentId = item.parentId;

      if (!itemMap[id]) { 
   
        itemMap[id] = { 
   
          children: [],
        }
      }

      itemMap[id] = { 
   
        ...item,
        id: String(item?.id),
        value: { 
   
          text: item?.name,
          value: item?.value,
          // 建议使用 bae64 数据
          icon: "https://avatars.githubusercontent.com/u/31396322?v=4",
          style: { 
   
            fill: "#00CED1",
            width: "200",
          },
        },
        children: itemMap[id]['children']
      }

      const treeItem = itemMap[id];

      if (parentId === 0) { 
   
        result.push(treeItem);
      } else { 
   
        if (!itemMap[parentId]) { 
   
          itemMap[parentId] = { 
   
            children: [],
          }
        }
        itemMap[parentId].children.push(treeItem)
      }

    }
    return result;
  }

  useEffect(() => { 
   
    fetchTableData()
  }, [])


  return (
    <div className="content">
      <OrganizationGraph
        width={ 
   1000}
        height={ 
   1000}
        data={ 
   data}
        nodeCfg={ 
   { 
   
          // type:'circle',
          padding: 0,
          size: [150, 40],
          style: (node) => { 
   
            const num = randomNum(0, 4);
            console.log('node', node)
            return { 
   
              fill: colorArr[num],
            };
          },
          label: { 
   
            style: (node: any, group, type: any) => { 
   
              const styles = { 
   
                icon: { 
   
                  width: 40,
                  height: 40,
                  x: 0,
                  y: 0,
                },
                value: { 
   
                  fill: "#fff",
                  x: 100,
                  // y: 4,
                },
                text: { 
   
                  fill: "#fff",
                  x: 100,
                  y: node?.value?.value ? 4 : 16,
                },
              };
              return styles[type];
            },
          },
        }}
      />
    </div>
  );
}

编辑页面的视觉效果:
在这里插入图片描述
代码:

import React, { 
    useEffect, useState } from "react";
import { 
   
  Table,
  Tag,
  Space,
  Modal,
  Button,
  Form,
  message,
  Input,
  Tooltip,
  DatePicker,
  Select
} from "antd";
import { 
    PlusCircleTwoTone, EditTwoTone } from "@ant-design/icons";
import moment from 'moment';
const { 
    Option } = Select;
export default function EditTree() { 
   
  const [tabelData, setTableData] = useState([]);
  const [newPersonVisible, setNewPersonVisible] = useState(false);
  const [modalOkLoading, setModalOkLoading] = useState(false);
  const [currentPerson, setCurrentPerson] = useState({ 
   });
  const [form] = Form.useForm();
  useEffect(() => { 
   
    fetchTableData();
  }, []);

  const fetchTableData = () => { 
   
    fetch("http://localhost:9091/genealogy/info/selectUserPage", { 
   
      method: "POST",
      body: JSON.stringify({ 
   
        size: 20,
        current: 1,
      }),
      headers: { 
   
        "content-type": "application/json",
      },
    }).then((res) => { 
   
      res.json().then((data) => { 
   
        setTableData(data.data);
      });
    });
  };

  const columns = [
    { 
   
      title: "姓名",
      dataIndex: "name",
      key: "name",
      render: (text) => <a>{ 
   text}</a>,
    },
    { 
   
      title: "年龄",
      dataIndex: "date",
      key: "date",
      render:(record:any)=>{ 
   
        let birthday = moment(record).year();
        let now = moment().year();
        return now - birthday
      }
    },
    { 
   
      title: "创建日期",
      dataIndex: "createTime",
      key: "createTime",
    },
    { 
   
      title: "子女",
      key: "childrenName",
      dataIndex: "childrenName",
      align: "center",
      render: (child) => (
        <>
          { 
   child && child.length > 0 ? (
            child.map((item, index) => { 
   
              let color = index > 0 ? "geekblue" : "green";

              return (
                <Tag color={ 
   color} key={ 
   item}>
                  { 
   item}
                </Tag>
              );
            })
          ) : (
            <a>暂无子女</a>
          )}
        </>
      ),
    },
    { 
   
      title: "操作",
      key: "action",
      render: (text, record) => (
        <Space size="middle">
          <Tooltip title={ 
   "新增子女"} placement="top">
            <PlusCircleTwoTone
              onClick={ 
   () => { 
   
                setNewPersonVisible(true);
                setCurrentPerson(record);
              }}
            />
          </Tooltip>
          <Tooltip title={ 
   "编辑个人信息"} placement="top">
            <EditTwoTone />
          </Tooltip>
        </Space>
      ),
    },
  ];

  const handlModalOk = () => { 
   
    const { 
    getFieldsValue,resetFields } = form;
    let result = getFieldsValue();
    
    if(result?.type === 'child') { 
   
      result = { 
   
        ...result,
        parentId: currentPerson?.id,
        date: moment(result?.date).format('X') + '000'
      };
      delete result?.type
    }else { 
   
      result = { 
   
        ...result,
        companionId: currentPerson?.id,
        date: moment(result?.date).format('X') + '000'
      };
      delete result?.type
    }
    setModalOkLoading(true);
    fetch("http://localhost:9091/genealogy/info/saveUser", { 
   
      method: "POST",
      body: JSON.stringify(result),
      headers: { 
   
        "content-type": "application/json",
      },
    }).then((res) =>
      res.json().then((data) => { 
   
        if (data.isSuccess) { 
   
          setModalOkLoading(false);
          setNewPersonVisible(false);
          message.success("新建成功");
          fetchTableData();
          resetFields()
        } else { 
   
          message.error(data.msg);
          setModalOkLoading(false);
        }
      })
    );
  };
  return (
    <div>
      <Table columns={ 
   columns} rowKey={ 
   (record)=>  record?.id} dataSource={ 
   tabelData} />
      <Modal
        title={ 
   `新建${ 
     currentPerson.name}信息`}
        visible={ 
   newPersonVisible}
        onCancel={ 
   () => { 
   
          setNewPersonVisible(false);
        }}
        onOk={ 
   handlModalOk}
        cancelText="取消"
        okText="确定"
        centered
        confirmLoading={ 
   modalOkLoading}
      >
        <Form form={ 
   form}>
          <Form.Item label="姓名" name={ 
   "name"} required>
            <Input placeholder="请输入姓名" />
          </Form.Item>

          <Form.Item label="生日" name={ 
   "date"} required>
            <DatePicker
              style={ 
   { 
    width: '100%' }}
              placeholder={ 
   '请选择出生日期'}
            />
          </Form.Item>
      
          <Form.Item label="类型" name={ 
   "type"} required>
            <Select
              style={ 
   { 
    width: '100%' }}
              placeholder="请选择新增的是伴侣还是子女"
            >
              <Option value="child">子女</Option>
              <Option value="companion">伴侣</Option>
            </Select>
          </Form.Item>

        </Form>
      </Modal>
    </div>
  );
}

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/153282.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • apache的安装与配置_apache2.4安装教程

    apache的安装与配置_apache2.4安装教程准备安装包到https://www.apachelounge.com/download/下载你需要的Apache安装包(注意需要相应的VC运行库)解压文件到指定安装目录3.替换安装路径使用文本编辑器打开Apache的配置文件conf/httpd.conf执行文本替换将”c:/Apache24”全部替换成(你自己解压的路径)“G:\web\apache2.4”4

  • Linux-dosbox使用「建议收藏」

    2019独角兽企业重金招聘Python工程师标准>>>…

  • 几种常用的矩阵范数表示_向量范数怎么求

    几种常用的矩阵范数表示_向量范数怎么求按道理讲,这些东西应该熟记于心的。但是自己真心不喜欢记这种东西,看到一个总结不错的博客,转载过来以便于自己查看把!原文1.几种范数矩阵X∈Rm×nX∈Rm×n,σi(X)σi(X)表示XX的第ii大奇异值(即XX′XX′的第ii大特征值的均方根){citerecht2010guaranteed}。rr表示矩阵XX的秩(R

  • JSP的include指令

    JSP的include指令在JSP中,可以使用include指令来包含其他jsp文件。例如,一个网站中,多个网页具有很大部分是相同元素时,可以使用include指令来完成。include的指令的语法如下:<%@inc

  • eplan激活码破解步骤-通用破解码【中文破解版】[通俗易懂]

    (eplan激活码破解步骤-通用破解码)好多小伙伴总是说激活码老是失效,太麻烦,关注/收藏全栈君太难教程,2021永久激活的方法等着你。https://javaforall.cn/100143.htmlIntelliJ2021最新激活注册码,破解教程可免费永久激活,亲测有效,上面是详细链接哦~2KLKA7BQFO-eyJsaWNlbnNlSWQiOi…

  • 单片机控制步进电机-AVR详细程序

    单片机控制步进电机-AVR详细程序单片机控制步进电机-单片机程序(avr)硬件线路连接图见上一篇文章软件:ICCV7FORAVR-写程序Progisp-烧程序速度S曲线生成器(后续后单独讲解)-生成S曲线数组代码硬件:Atmega16ASP下载线杜邦线控制原理:利用单片机定时器控制IO口高低电平产生脉冲,通过定时器控制每个脉冲的时间,以及脉冲的个数,从而控制步进电机速度以及转动角度,实现步进电机开环控制能…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号