123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509 |
- <template>
- <div style="background:#f1f2f3" id="full">
- <img style="width: 25px; height: 25px;float: right" v-if="!fullscreen" @click="enterFullscreen" src="@/assets/icons/amplify.svg" title="全屏">
- <img style="width: 25px; height: 25px;float: right" @click="backFullscreen" v-if="fullscreen" src="@/assets/icons/reduce.svg" title="还原">
- <div id="container"></div>
- </div>
- </template>
- <script>
- import G6 from '@antv/g6';
- import axios from 'axios'
- let graph;
- const fittingString = (str, maxWidth, fontSize) => {
- const ellipsis = "...";
- const ellipsisLength = G6.Util.getTextSize(ellipsis, fontSize)[0];
- let currentWidth = 0;
- let res = str;
- const pattern = new RegExp("[\u4E00-\u9FA5]+"); // distinguish the Chinese charactors and letters
- str.split("").forEach((letter, i) => {
- if (currentWidth > maxWidth - ellipsisLength) return;
- if (pattern.test(letter)) {
- // Chinese charactors
- currentWidth += fontSize;
- } else {
- // get the width of single letter according to the fontSize
- currentWidth += G6.Util.getLetterWidth(letter, fontSize);
- }
- if (currentWidth > maxWidth - ellipsisLength) {
- res = `${str.substr(0, i)}${ellipsis}`;
- }
- });
- return res;
- };
- const ERROR_COLOR = 'red';
- const getNodeConfig = (node) => {
- if (node.nodeUrl) {
- return {
- basicColor: ERROR_COLOR,
- fontColor: '#FFF',
- borderColor: ERROR_COLOR,
- bgColor: '#ff5722',
- };
- }
- let config = {
- basicColor: '#fff',
- fontColor: '#333',
- borderColor: '#fff',
- bgColor: '#fff',
- };
- switch (node.type) {
- case 'root': {
- config = {
- basicColor: '#E3E6E8',
- fontColor: 'rgba(0,0,0,0.85)',
- borderColor: '#E3E6E8',
- bgColor: '#5b8ff9',
- };
- break;
- }
- default:
- break;
- }
- return config;
- };
- const COLLAPSE_ICON = function COLLAPSE_ICON(x, y, r) {
- return [
- ['M', x - r, y],
- ['a', r, r, 0, 1, 0, r * 2, 0],
- ['a', r, r, 0, 1, 0, -r * 2, 0],
- ['M', x - r + 4, y],
- ['L', x - r + 2 * r - 4, y],
- ];
- };
- const EXPAND_ICON = function EXPAND_ICON(x, y, r) {
- return [
- ['M', x - r, y],
- ['a', r, r, 0, 1, 0, r * 2, 0],
- ['a', r, r, 0, 1, 0, -r * 2, 0],
- ['M', x - r + 4, y],
- ['L', x - r + 2 * r - 4, y],
- ['M', x - r + r, y - r + 4],
- ['L', x, y + r - 4],
- ];
- };
- const nodeBasicMethod = {
- createNodeBox: (group, config, w, h, isRoot) => {
- /* 最外面的大矩形 */
- const container = group.addShape('rect', {
- attrs: {
- x: 0,
- y: 0,
- width: w,
- height: h,
- },
- // must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type
- name: 'big-rect-shape',
- });
- if (!isRoot) {
- /* 左边的小圆点 */
- group.addShape('circle', {
- attrs: {
- x: 3,
- y: h / 2,
- r: 0,
- fill: config.basicColor,
- },
- // must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type
- name: 'left-dot-shape',
- });
- }
- /* 矩形 */
- group.addShape('rect', {
- attrs: {
- x: 3,
- y: 0,
- width: w - 19,
- height: h,
- fill: config.bgColor,
- stroke: config.borderColor,
- radius: 2,
- cursor: 'pointer',
- },
- // must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type
- name: 'rect-shape',
- });
- /* 左边的粗线 */
- group.addShape('rect', {
- attrs: {
- x: 3,
- y: 0,
- width: 3,
- height: h,
- fill: config.basicColor,
- radius: 1.5,
- },
- // must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type
- name: 'left-border-shape',
- });
- return container;
- },
- /* 生成树上的 marker */
- createNodeMarker: (group, collapsed, x, y) => {
- group.addShape('circle', {
- attrs: {
- x,
- y,
- r: 13,
- fill: 'rgba(47, 84, 235, 0.05)',
- opacity: 0,
- zIndex: -2,
- },
- // must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type
- name: 'collapse-icon-bg',
- });
- group.addShape('marker', {
- attrs: {
- x,
- y,
- r: 7,
- symbol: collapsed ? EXPAND_ICON : COLLAPSE_ICON,
- stroke: 'rgba(0,0,0,0.25)',
- fill: 'rgba(0,0,0,0)',
- lineWidth: 1,
- cursor: 'pointer',
- },
- // must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type
- name: 'collapse-icon',
- });
- },
- afterDraw: (cfg, group) => {
- /* 操作 marker 的背景色显示隐藏 */
- const icon = group.find((element) => element.get('id') === 'collapse-icon');
- if (icon) {
- const bg = group.find((element) => element.get('id') === 'collapse-icon-bg');
- icon.on('mouseenter', () => {
- bg.attr('opacity', 1);
- graph.get('canvas').draw();
- });
- icon.on('mouseleave', () => {
- bg.attr('opacity', 0);
- graph.get('canvas').draw();
- });
- }
- /* ip 显示 */
- const ipBox = group.find((element) => element.get('id') === 'ip-box');
- if (ipBox) {
- /* ip 复制的几个元素 */
- const ipLine = group.find((element) => element.get('id') === 'ip-cp-line');
- const ipBG = group.find((element) => element.get('id') === 'ip-cp-bg');
- const ipIcon = group.find((element) => element.get('id') === 'ip-cp-icon');
- const ipCPBox = group.find((element) => element.get('id') === 'ip-cp-box');
- const onMouseEnter = () => {
- ipLine.attr('opacity', 1);
- ipBG.attr('opacity', 1);
- ipIcon.attr('opacity', 1);
- graph.get('canvas').draw();
- };
- const onMouseLeave = () => {
- ipLine.attr('opacity', 0);
- ipBG.attr('opacity', 0);
- ipIcon.attr('opacity', 0);
- graph.get('canvas').draw();
- };
- ipBox.on('mouseenter', () => {
- onMouseEnter();
- });
- ipBox.on('mouseleave', () => {
- onMouseLeave();
- });
- ipCPBox.on('mouseenter', () => {
- onMouseEnter();
- });
- ipCPBox.on('mouseleave', () => {
- onMouseLeave();
- });
- ipCPBox.on('click', () => { });
- }
- },
- setState: (name, value, item) => {
- const hasOpacityClass = [
- 'ip-cp-line',
- 'ip-cp-bg',
- 'ip-cp-icon',
- 'ip-cp-box',
- 'ip-box',
- 'collapse-icon-bg',
- ];
- const group = item.getContainer();
- const childrens = group.get('children');
- graph.setAutoPaint(false);
- if (name === 'emptiness') {
- if (value) {
- childrens.forEach((shape) => {
- if (hasOpacityClass.indexOf(shape.get('id')) > -1) {
- return;
- }
- shape.attr('opacity', 0.4);
- });
- } else {
- childrens.forEach((shape) => {
- if (hasOpacityClass.indexOf(shape.get('id')) > -1) {
- return;
- }
- shape.attr('opacity', 1);
- });
- }
- }
- graph.setAutoPaint(true);
- },
- };
- G6.registerNode('card-node', {
- options: {
- style: {
- stroke: '#ccc',
- },
- },
- draw: (cfg, group) => {
- const config = getNodeConfig(cfg);
- const isRoot = cfg.dataType === 'root';
- const nodeUrl = cfg.nodeUrl;
- /* the biggest rect */
- const container = nodeBasicMethod.createNodeBox(group, config, 243, 64 + Number(25*cfg.infoSize) , isRoot);
- if (cfg.dataType !== 'root') {
- /* the type text */
- group.addShape('text', {
- attrs: {
- text: cfg.dataType,
- x: 3,
- y: -10,
- fontSize: 12,
- textAlign: 'left',
- textBaseline: 'middle',
- fill: 'rgba(0,0,0,0.65)',
- },
- // must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type
- name: 'type-text-shape',
- });
- }
- /* name */
- group.addShape('text', {
- attrs: {
- text: cfg.id,
- x: 19,
- y: 19,
- fontSize: 14,
- fontWeight: 700,
- textAlign: 'left',
- textBaseline: 'middle',
- fill: config.fontColor,
- cursor: 'pointer',
- },
- // must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type
- name: 'name-text-shape',
- });
- /* the description text */
- group.addShape('text', {
- attrs: {
- text: fittingString(cfg.value, 243 - 50, 14),
- x: 19,
- y: 45,
- fontSize: 14,
- textAlign: 'left',
- textBaseline: 'middle',
- fill: config.fontColor,
- cursor: 'pointer',
- },
- // must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type
- name: 'bottom-text-shape',
- });
- if (cfg.infoSize > 0) {
- Object.keys(cfg.info).forEach((key,index) => {
- console.log(key, cfg.info[key],index);
- group.addShape('text', {
- attrs: {
- text: fittingString(String(cfg.info[key]), 243 - 50, 14),
- x: 19,
- y: 65 + (25*index) ,
- fontSize: 12,
- textAlign: 'left',
- textBaseline: 'middle',
- fill: config.fontColor,
- cursor: 'pointer',
- },
- // must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type
- name: 'bottom-text-shape',
- });
- });
- }
- if (nodeUrl) {
- group.addShape('text', {
- attrs: {
- x: 191,
- y: 62,
- text: '⇢',
- fill: '#fff',
- fontSize: 18,
- },
- // must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type
- name: 'error-text-shape',
- });
- }
- const hasChildren = cfg.children && cfg.children.length > 0;
- if (hasChildren) {
- nodeBasicMethod.createNodeMarker(group, cfg.collapsed, 236, 32);
- }
- return container;
- },
- afterDraw: nodeBasicMethod.afterDraw,
- setState: nodeBasicMethod.setState,
- });
- export default {
- props:['id'],
- data () {
- return {
- fullscreen:false
- }
- },
- mounted () {
- // this.getData()
- document.addEventListener('fullscreenchange', this.handleFullscreenChange);
- document.addEventListener('mozfullscreenchange', this.handleFullscreenChange);
- document.addEventListener('webkitfullscreenchange', this.handleFullscreenChange);
- document.addEventListener('MSFullscreenChange',this.handleFullscreenChange)
- },
- methods:{
- createMenu (array) {
- var that = this
- let arr = []
- const HASLINKS = ['有效线索数','有效项目数']
- function convertToElementTree(node) {
- // 新节点
- var elNode = {
- id: node['name'],
- nodeid:node['id'],
- info:node['info']?node['info']:{},
- infoSize:node['infoSize']?node['infoSize']:1,
- value:node['labor']?node['labor'] !== node['name']?node['labor']:'-':'-',
- children: [],
- nodeUrl:HASLINKS.includes(node['name'])?'123':null
- }
- if (node.children && node.children.length > 0) {
- // 如果存在子节点
- for (var index = 0; index < node.children.length; index++) {
- // 遍历子节点, 把每个子节点看做一颗独立的树, 传入递归构造子树, 并把结果放回到新node的children中
- elNode.children.push(convertToElementTree(node.children[index]));
- }
- }
- return elNode;
- }
- array.forEach((element) => {
- arr.push(convertToElementTree(element))
- });
- return arr
- },
- async getData () {
- const res = await this.$api.requested({
- "id": 20230625101004,
- "content": {
- "hrid": this.id
- }
- })
- const container = document.getElementById('container');
- const width = container.scrollWidth;
- const height = container.scrollHeight || 500;
- const graph = new G6.TreeGraph({
- container: 'container',
- width,
- height,
- linkCenter: true,
- fitCenter: true,
- modes: {
- default: ['drag-canvas','zoom-canvas','collapse-expand','drag-node', 'lasso-select'],
- },
- defaultNode: {
- /* node type, the priority is lower than the type in the node data */
- type: 'card-node',
- },
- defaultEdge: {
- type: 'cubic-horizontal',
- },
- layout: {
- type: 'dendrogram',
- direction: 'RL',
- nodeSep: 100,
- rankSep: 250,
- radial: true,
- },
- });
- graph.node(function (node) {
- return {
- label: node.id,
- };
- });
- graph.data(this.createMenu([res.data])[0]);
- graph.render();
- graph.fitView();
- graph.on('node:click', evt => {
- const item = evt.item;
- console.log(item)
- })
- if (typeof window !== 'undefined')
- window.onresize = () => {
- if (!graph || graph.get('destroyed')) return;
- if (!container || !container.scrollWidth || !container.scrollHeight) return;
- graph.changeSize(container.scrollWidth, container.scrollHeight);
- };
- },
- enterFullscreen () {
- /* 获取(<html>)元素以全屏显示页面 */
- const full = document.getElementById('full')
- if (full.RequestFullScreen) {
- full.RequestFullScreen()
- //兼容Firefox
- } else if (full.mozRequestFullScreen) {
- full.mozRequestFullScreen()
- //兼容Chrome, Safari and Opera等
- } else if (full.webkitRequestFullScreen) {
- full.webkitRequestFullScreen()
- //兼容IE/Edge
- } else if (full.msRequestFullscreen) {
- full.msRequestFullscreen()
- }
- },
- handleFullscreenChange () {
- if (document.fullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement || document.msFullscreenElement) {
- // 全屏模式激活
- console.log('全屏模式已激活');
- this.fullscreen = true
- } else {
- // 全屏模式退出
- this.fullscreen = false
- console.log('全屏模式已退出');
- }
- },
- /*全屏还原*/
- backFullscreen(){
- if (document.exitFullscreen) {
- document.exitFullscreen();
- } else if (document.webkitCancelFullScreen) {
- document.webkitCancelFullScreen();
- } else if (document.mozCancelFullScreen) {
- document.mozCancelFullScreen();
- } else if (document.msExitFullscreen) {
- document.msExitFullscreen();
- }
- },
- },
- }
- </script>
- <style>
- #container{
- height: 100vh;
- width: 100vw;
- }
- </style>
|