Power BI D3 Tree開發自定義可視化對象

開發環境:

Power BI Desktop 2017

API版本v1.9.0

導入離線版

 

導入在線版d3.js.(3.0以上的是可以安裝D3的,詳細的可以參考微軟官方demo介紹:https://docs.microsoft.com/zh-cn/power-bi/developer/visuals/custom-visual-develop-tutorial

以上兩種導入方案在https://blog.csdn.net/Javon_huang/article/details/105192597 有介紹,此不做講解

1.visual.ts

/*
 *  Power BI Visual CLI
 *
 *  Copyright (c) Microsoft Corporation
 *  All rights reserved.
 *  MIT License
 *
 *  Permission is hereby granted, free of charge, to any person obtaining a copy
 *  of this software and associated documentation files (the ""Software""), to deal
 *  in the Software without restriction, including without limitation the rights
 *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *  copies of the Software, and to permit persons to whom the Software is
 *  furnished to do so, subject to the following conditions:
 *
 *  The above copyright notice and this permission notice shall be included in
 *  all copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 *  THE SOFTWARE.
 */

module powerbi.extensibility.visual {
    "use strict";
    export class Visual implements IVisual {
        private target: HTMLElement;
        private tips:HTMLElement;
        private d3node: HTMLElement;
        private settings: VisualSettings;
        private textNode: Text;
        private d3: any;
        private rootData:any;
        private rootName:string;
        private width:number;
        private height:number;
        private d3Load:any;
        private rootList:Array<any>;
        private timer:any;
        private valueSources:Array<any>;
        private list:Array<any>

        constructor(options: VisualConstructorOptions) {
            console.log('Visual constructor', options);
            this.target = options.element;
            this.d3Load=false;
            if (typeof document !== "undefined") {
              this.tips = document.createElement("p");
              this.tips.appendChild(document.createTextNode("正在加載資源..."));
              this.target.appendChild(this.tips);

              this.d3node= document.createElement("div");
              this.d3node.id="product_tree";
              this.target.appendChild(this.d3node);
            }
            this.onloadD3();
        }

        public update(options: VisualUpdateOptions) {
          this.width = options.viewport.width;
          this.height= options.viewport.height;      
          this.settings = Visual.parseSettings(options && options.dataViews && options.dataViews[0]);

          let dataTree:any=options.dataViews[0].matrix;
          this.valueSources=options.dataViews[0].matrix.valueSources;
          this.list=dataTree.rows.root.children;
          console.log(options);
          clearTimeout(this.timer);
          this.timer=setTimeout(()=>{
            this.rootList=[{
              direction: "downward",
              name: "origin",
              value:"origin",
              children:this.list,
              level:-1
            }];
            this.readTree(this.rootList);
            this.mapTree(this.rootList);
            this.AddLastNode(this.rootList);
            
            this.target.removeChild(this.d3node);
            this.d3node= document.createElement("div");
            this.d3node.id="product_tree";
            this.target.appendChild(this.d3node);
            this.checkJsLoad();
          },500);
        }

        private readTree(list:Array<any>){
          let that=this;
          // debugger;
          list.forEach((item)=>{
            // item.name=item.level;
            item.name=item.value;
            item.amount="100";
            item.ratio="55%";
            item.hasHumanholding=false;
            item.hasChildren=false;
            item.isExpand=false;

            if(typeof item.children !="undefined"){
              item.count=0;
              that.readTree(item.children);
            }else{
              item.count=item.values[0].value;
            }
          });
        }

        private mapTree(list:Array<any>){
          let that=this;
          list.forEach((item)=>{
            if(typeof item.children !="undefined"){
              item.count=that.countNum(item);
              that.mapTree(item.children);
            }
          });
        }
        
        private countNum(node:any){
          let that=this;
          let result:Array<any>=new Array();
          getList(node.children,result);
          let count=0;
          result.forEach((item)=>{
            count+=item.count;
          });
          return count;
          function getList(list:Array<any>,result:Array<any>){
            list.forEach((item)=>{
              if(typeof item.children =="undefined"){
                result.push(item);
              }else{
                getList(item.children,result);
              }
            });
            return result;
          }
        }
        
        private AddLastNode(list:Array<any>){
          let that=this;
          // debugger;
          list.forEach((item)=>{
            if(typeof item.children !="undefined"){
              that.AddLastNode(item.children);
            }else{
              if(Object.keys(item.values).length>1){
                let keyList=Object.keys(item.values);
                let nextChildren=new Array();
                keyList.forEach((_item:any)=>{
                  if(_item>0&&item.values[_item].value!=null){
                    nextChildren.push({
                      level:item.level+1,
                      // name:item.level+1,
                      value:that.valueSources[item.values[_item].valueSourceIndex].displayName,
                      values:{0:{value:item.values[_item].value}},
                      count:item.values[_item].value,
                      name:that.valueSources[item.values[_item].valueSourceIndex].displayName,
                      amount:"100",
                      ratio:"55%",
                      hasHumanholding:false,
                      hasChildren:false,
                      isExpand:false
                    })
                  }
                });
                item.children=nextChildren;
              }
            }
          });
        }

        private onloadD3(){
          let script=document.createElement('script');
          script.type="text/javascript";
          script.src="https://cdn.bootcss.com/d3/3.2.7/d3.min.js";
          document.body.appendChild(script);
          let that=this;
          script.onload=function(){
            that.d3=d3;
            that.d3Load=true;
            that.tips.innerHTML="加載完成";
          }
        }

        private getData(){
          this.rootData ={};
          this.rootData={
            downward:this.rootList[0],
            upward:{
              direction: "upward",
              name: "origin",
              children: []
            }
          }
          console.log(this.rootData);
          this.rootName = '我是根節我是根節點點';
          this.drawing();
        }

        private drawing(){
          let _this=this;
          let rootRectWidth = 0; //根節點rect的寬度
          let downwardLength = 0,
              upwardLength = 0;
          let forUpward = true;
          let treeChart = function(d3Object) {
            this.d3 = d3Object;
            this.directions = ['upward', 'downward'];
          };
          treeChart.prototype.drawChart = function() {
            this.treeData = {};
            let self = this;
            self.directions.forEach(function(direction) {
              self.treeData[direction] = _this.rootData[direction];
            });
            // rootName = '上海冰鑑信息科技有限公司';
            rootRectWidth = _this.rootName.length * 15;
            //獲得upward第一級節點的個數
            upwardLength = _this.rootData.upward.children.length;
            //獲得downward第一級節點的個數
            downwardLength = _this.rootData.downward.children.length;
            self.graphTree(self.getTreeConfig());
          };
          treeChart.prototype.getTreeConfig = function() {
            let treeConfig:any = {
              'margin': {
                'top': 0,
                'right': 0,
                'bottom': 0,
                'left': 0
              }
            }
            treeConfig.chartWidth = (_this.width - treeConfig.margin.right - treeConfig.margin.left);
            treeConfig.chartHeight = (_this.height - treeConfig.margin.top - treeConfig.margin.bottom);
            treeConfig.centralHeight = treeConfig.chartHeight / 4;
            treeConfig.centralWidth = treeConfig.chartWidth / 2;
            treeConfig.linkLength = 120;
            treeConfig.duration = 500; //動畫時間
            return treeConfig;
          };
          treeChart.prototype.graphTree = function(config) {
            let self = this;
            let d3 = _this.d3;
            let linkLength = config.linkLength;
            let duration = config.duration;
            let hasChildNodeArr = [];
            let id = 0;
            let diagonal;
            let treeG:any =null;
            //折線
            let funLine = function(obj) {
              let d=obj.source;
              let c=obj.target;
              return `M${d.x},${d.y+0} ${d.x},${d.y+80} ${c.x},${d.y+80} ${c.x},${c.y}`;
            };
            diagonal = funLine;
            let zoom = d3.behavior.zoom()
            .scaleExtent([0.5, 2])
            .on('zoom', redraw);

            let svg = d3.select('#product_tree')
            .append('svg')
            .attr('width', config.chartWidth + config.margin.right + config.margin.left)
            .attr('height', config.chartHeight + config.margin.top + config.margin.bottom)
            .attr('xmlns','http://www.w3.org/2000/svg')
            .on('mousedown', disableRightClick)
            .call(zoom)
            .on('dblclick.zoom', null);
              treeG = svg.append('g')
                .attr('class', 'gbox')
            .attr('transform', 'translate(' + config.margin.left + ',' + config.margin.top + ')');

            //箭頭(下半部分)
            let markerDown = svg.append("marker")
            .attr("id", "resolvedDown")
            .attr("markerUnits", "strokeWidth") //設置爲strokeWidth箭頭會隨着線的粗細發生變化
            .attr("markerUnits", "userSpaceOnUse")
            .attr("viewBox", "0 -5 10 10") //座標系的區域
            .attr("refX", 0) //箭頭座標
            .attr("refY", 0)
            .attr("markerWidth", 12) //標識的大小
            .attr("markerHeight", 12)
            .attr("orient", "90") //繪製方向,可設定爲:auto(自動確認方向)和 角度值
            .attr("stroke-width", 2) //箭頭寬度
            .append("path")
            .attr("d", "M0,-5L10,0L0,5") //箭頭的路徑
            .attr('fill', _this.settings.dataPoint.arrowColor); //箭頭顏色  

            // Initialize the tree nodes and update chart.
            for(var d in this.directions) {
              var direction = this.directions[d];
              var data = self.treeData[direction];
              data.x0 = config.centralWidth;
              data.y0 = config.centralHeight;
              data.children.forEach(collapse);
              update(data, data, treeG);
            }

            function update(source, originalData, g) {
              var direction = originalData['direction'];
              forUpward = direction == 'upward';
              var node_class = direction + 'Node';
              var link_class = direction + 'Link';
              var downwardSign = (forUpward) ? -1 : 1;
              var nodeColor = (forUpward) ? '#37592b' : '#8b4513';
     
              var isExpand = false;
              var statusUp = true;
              var statusDown = true;
              var nodeSpace = 130;
              var tree = d3.layout.tree().sort(sortByDate).nodeSize([nodeSpace, 0]);
              var nodes = tree.nodes(originalData);
              var links = tree.links(nodes);
              var offsetX = -config.centralWidth;
              nodes.forEach(function(d) {
                d.y = downwardSign * (d.depth * linkLength) + config.centralHeight;
                d.x = d.x - offsetX;
                if(d.name == 'origin') {
                  d.x = config.centralWidth;
                  d.y += downwardSign * 0+50; // 上下兩樹圖根節點之間的距離
                }
              });
     
              // Update the node.
              var node = g.selectAll('g.' + node_class)
                .data(nodes, function(d) {
                  return d.id || (d.id = ++id);
                });
              var nodeEnter = node.enter().append('g')
                .attr('class', node_class)
                .attr('transform', function(d) {
                  return 'translate(' + source.x0 + ',' + source.y0 + ')';
                })
                .style('cursor', function(d) {
                  return(d.name == 'origin') ? '' : (d.children || d._children) ? 'pointer' : '';
                });
                // .on('click', click);
              
     
              nodeEnter.append("svg:rect")
                .attr("x", function(d) {
                  return(d.name == 'origin') ? -(rootRectWidth / 2) : -60;
                })
                .attr("y", function(d) {
                  return(d.name == 'origin') ? -20 : forUpward ? -52 : 12;
                })
                .attr("width", function(d) {
                  return(d.name == 'origin') ? rootRectWidth : 120;
                })
                .attr("height", 40)
                .attr("rx", 10)
                .style("stroke", function(d) {
                  // return(d.name == 'origin') ? "#1078AF" : "#CCC";
                  return "transparent";
                })
                .style("fill", function(d) {
                  //節點背景色
                  if(d.level%2!=0){
                    return _this.settings.dataPoint.singularColor;
                  }else{
                    return  _this.settings.dataPoint.dualColor;
                  }
                });
     
              nodeEnter.append('circle')
                .attr('r', 1e-6);
              nodeEnter.append("text")
                .attr("class", "linkname")
                .attr("x", function(d) {
                  return(d.name == 'origin') ? '0' : "0";
                })
                .attr('dy', function(d) {
                  return(d.name == 'origin') ? '.35em' : forUpward ? '-40' : '30';
                })
                .attr("text-anchor", function(d) {
                  return(d.name == 'origin') ? 'middle' : "middle";
                })
                .attr('fill', '#fff')
                .text(function(d) {
                  if(d.name == 'origin') {
                    // return ((forUpward) ? '根節點TOP' : '根節點Bottom');
                    return _this.rootName;
                  } 
                  if(d.repeated) {
                    return '[Recurring] ' + d.name;
                  }
                  return(d.name.length > 10) ? d.name.substr(0, 10) : d.name;
                })
                .style({
                  'fill-opacity': 1e-6,
                  'fill': function(d) {
                    if(d.level%2!=0){
                      return _this.settings.dataPoint.singularFontColor;
                    }else{
                      return  _this.settings.dataPoint.dualFontColor;
                    }
                  },
                  'font-size': function(d) {
                    if(d.level%2!=0){
                      return _this.settings.dataPoint.singularFontSize;
                    }else{
                      return  _this.settings.dataPoint.dualFontSize;
                    }
                  },
                  'cursor': "pointer"
                })
                .on('click', Change_modal);
     
              nodeEnter.append("text")
                .attr("class", "linkname")
                .attr("x", "-55")
                .attr("dy", function(d) {
                  return(d.name == 'origin') ? '.35em' : forUpward ? '-29' : '35';
                })
                .attr("text-anchor", function(d) {
                  return(d.name == 'origin') ? 'middle' : "start";
                })
                .text(function(d) {
                  return d.name.substr(10, d.name.length);
                })
                .style({
                  'fill': "#337ab7",
                  'font-size': function(d) {
                    return(d.name == 'origin') ? 14 : 11;
                  },
                  'cursor': "pointer"
                });
                // .on('click', Change_modal);
     
              nodeEnter.append("text")
                .attr("x", "0")
                .attr("dy", function(d) {
                  return(d.name == 'origin') ? '.35em' : forUpward ? '-16' : '45';
                })
                .attr("text-anchor", "middle")
                .attr("class", "linkname")
                .style("fill", _this.settings.dataPoint.numColor)
                .style('font-size',  _this.settings.dataPoint.numFontSize)
                .text(function(d) {
                  var str = (d.name == 'origin') ? '' :  d.count ;
                  return(str.length > 13) ? str.substr(0, 13) + ".." : str;
                });
              nodeEnter.append("text")
                .attr("x", "10")
                .attr("dy", function(d) {
                  return(d.name == 'origin') ? '.35em' : forUpward ? '0' : '10';
                })
                .attr("text-anchor", "start")
                .attr("class", "linkname")
                .style("fill", "green")
                .style('font-size', 10)
                // .text(function(d) {
                //   return(d.name == 'origin') ? "" : d.ratio;
                // });
     
              // Transition nodes to their new position.原有節點更新到新位置
              var nodeUpdate = node.transition()
                .duration(duration)
                .attr('transform', function(d) {
                  return 'translate(' + d.x + ',' + d.y + ')';
                });
              nodeUpdate.select('circle')
                .attr('r', function(d) {
                  return(d.name == 'origin') ? 0 : (hasChildNodeArr.indexOf(d) == -1) ? 0 : 6;
                })
                .attr('cy', function(d) {
                  return(d.name == 'origin') ? -20 : (forUpward) ? -59 : 59;
                })
                .style('fill', function(d) {
                  return hasChildNodeArr.indexOf(d) != -1 ? _this.settings.dataPoint.pointBackGroupColor : "";
                  // if (d._children || d.children) { return "#fff"; } else { return "rgba(0,0,0,0)"; }
                })
                .style('stroke', function(d) {
                  return hasChildNodeArr.indexOf(d) != -1 ? _this.settings.dataPoint.pointColor : "";
                  // if (d._children || d.children) { return "#8b4513"; } else { return "rgba(0,0,0,0)"; }
                })
                .style('fill-opacity', function(d) {
                  if(d.children) {
                    return 0.35;
                  }
                })
                // Setting summary node style as class as mass style setting is
                // not compatible to circles.
                .style('stroke-width', function(d) {
                  if(d.repeated) {
                    return 5;
                  }
                });
              //代表是否展開的+-號
              nodeEnter.append("svg:text")
                .attr("class", "isExpand")
                .attr("x", "0")
                .attr("dy", function(d) {
                  return forUpward ? -56 : 63;
                })
                .attr("text-anchor", "middle")
                .style("fill", _this.settings.dataPoint.pointColor)
                .text(function(d) {
                  if(d.name == 'origin') {
                    return '';
                  }
                  return hasChildNodeArr.indexOf(d) != -1 ? "+" : "";
                  // /* if (d._children || d.children) {
                  //   return "+";
                  // } */
                })
                .on('click',click)
     
              nodeUpdate.select('text').style('fill-opacity', 1)
              
              var nodeExit = node.exit().transition()
                .duration(duration)
                .attr('transform', function(d) {
                  return 'translate(' + source.x + ',' + source.y + ')';
                })
                .remove();
              nodeExit.select('circle')
                .attr('r', 1e-6)
              nodeExit.select('text')
                .style('fill-opacity', 1e-6);
     
              var link = g.selectAll('path.' + link_class)
                .data(links, function(d) {
                  return d.target.id;
                });
     
              link.enter().insert('path', 'g')
                .attr('class', link_class)
                .attr('stroke',function(d){
                  return _this.settings.dataPoint.lineColor;
                })
                .attr('fill',"none")
                .attr('stroke-width','1px')
                .attr('opacity', 1)
                .attr('d', function(d) {
                  var o = {
                    x: source.x0,
                    y: source.y0
                  };
                  return diagonal({
                    source: o,
                    target: o
                  });
                })
                .attr("marker-end", function(d) {
                  return forUpward ? "url(#resolvedUp)" : "url(#resolvedDown)";
                }) //根據箭頭標記的id號標記箭頭;
                .attr("id", function(d, i) {
                  return "mypath" + i;
                })
              link.transition()
                .duration(duration)
                .attr('d', diagonal);
              link.exit().transition()
                .duration(duration)
                .attr('d', function(d) {
                  var o = {
                    x: source.x,
                    y: source.y
                  };
                  return diagonal({
                    source: o,
                    target: o
                  });
                })
                .remove();
              nodes.forEach(function(d) {
                d.x0 = d.x;
                d.y0 = d.y;
              });
              function Change_modal () {
                // _this.Modal = true
              }
              function click(d) {
                if(forUpward) {
     
                } else {
                  if(d._children) {
                    console.log('對外投資--ok')
                  } else {
                    console.log('對外投資--no')
                  }
                }
                isExpand = !isExpand;
                if(d.name == 'origin') {
                  return;
                }
                if(d.children) {
                  d._children = d.children;
                  d.children = null;
                  d3.select(this).text('+')
                } else {
                  d.children = d._children;
                  d._children = null;
                  // expand all if it's the first node
                  if(d.name == 'origin') {
                    d.children.forEach(expand);
                  }
                  d3.select(this).text('-')
                }
                update(d, originalData, g);
              }
            }
            
            function redraw() {
              treeG.attr('transform', 'translate(' + d3.event.translate + ')' +' scale(' + d3.event.scale + ')');
            };
            function disableRightClick() {
              if(d3.event.button == 2) {
                console.log('No right click allowed');
                d3.event.stopImmediatePropagation();
              }
            };
            function collapse(d) {
              if(d.children && d.children.length != 0) {
                d._children = d.children;
                d._children.forEach(collapse);
                d.children = null;
                hasChildNodeArr.push(d);
              }
            };
          };
          function sortByDate(a, b) {
            var aNum = a.name.substr(a.name.lastIndexOf('(') + 1, 4);
            var bNum = b.name.substr(b.name.lastIndexOf('(') + 1, 4);
            return d3.ascending(aNum, bNum) ||
              d3.ascending(a.name, b.name) ||
              d3.ascending(a.id, b.id);
          };
          function expand(d) {
            if(d._children) {
              d.children = d._children;
              d.children.forEach(expand);
              d._children = null;
            }
          };
          let d3GenerationChart = new treeChart(d3);
          d3GenerationChart.drawChart();
        };

        //檢查第三方js加載
        private checkJsLoad(){
          let that=this;
          if(that.d3Load){
            that.tips.innerHTML="";
            that.getData();
          }
        }
                private static parseSettings(dataView: DataView): VisualSettings {
            return VisualSettings.parse(dataView) as VisualSettings;
        }

        /** 
         * This function gets called for each of the objects defined in the capabilities files and allows you to select which of the 
         * objects and properties you want to expose to the users in the property pane.
         * 
         */
        public enumerateObjectInstances(options: EnumerateVisualObjectInstancesOptions): VisualObjectInstance[] | VisualObjectInstanceEnumerationObject {
            return VisualSettings.enumerateObjectInstances(this.settings || VisualSettings.getDefault(), options);
        }
    }
}

2.capabilities.json

{
	"dataRoles": [{
			"name": "Category",
			"displayName": "Category",
			"displayNameKey": "Visual_Category",
			"kind": "Grouping"
		},
		{
			"name": "Measure",
			"displayName": "Measure",
			"displayNameKey": "Visual_Values",
			"kind": "Measure"
		}
	],
	"objects": {
		"dataPoint": {
			"displayName": "樹圖屬性",
			"properties": {
				"singularColor": {
					"displayName": "單節點背景",
					"type": {
						"fill": {
							"solid": {
								"color": true
							}
						}
					}
				},
				"singularFontSize": {
					"displayName": "單節點字體",
					"type": {
						"formatting": {
							"fontSize": true
						}
					}
				},
				"singularFontColor": {
					"displayName": "單節點字體顏色",
					"type": {
						"fill": {
							"solid": {
								"color": true
							}
						}
					}
				},
				"dualColor": {
					"displayName": "雙節點背景",
					"type": {
						"fill": {
							"solid": {
								"color": true
							}
						}
					}
				},
				"dualFontSize": {
					"displayName": "雙節點字體",
					"type": {
						"formatting": {
							"fontSize": true
						}
					}
				},
				"dualFontColor": {
					"displayName": "雙節點字體顏色",
					"type": {
						"fill": {
							"solid": {
								"color": true
							}
						}
					}
				},
				"numColor": {
					"displayName": "數字顏色",
					"type": {
						"fill": {
							"solid": {
								"color": true
							}
						}
					}
				},
				"numFontSize": {
					"displayName": "數字字體",
					"type": {
						"formatting": {
							"fontSize": true
						}
					}
				},
				"arrowColor": {
					"displayName": "箭頭顏色",
					"type": {
						"fill": {
							"solid": {
								"color": true
							}
						}
					}
				},
				"lineColor": {
					"displayName": "折線顏色",
					"type": {
						"fill": {
							"solid": {
								"color": true
							}
						}
					}
				},
				"pointColor": {
					"displayName": "折點顏色",
					"type": {
						"fill": {
							"solid": {
								"color": true
							}
						}
					}
				},
				"pointBackGroupColor": {
					"displayName": "折點背景顏色",
					"type": {
						"fill": {
							"solid": {
								"color": true
							}
						}
					}
				}
			}
		}
	},
	"dataViewMappings": [{
		"matrix": {
			"rows": {
				"for": {
					"in": "Category"
				}
			},
			"values": {
				"select": [{
					"for": {
						"in": "Measure"
					}
				}]
			}
		}
	}]
}

3.settings.ts

/*
 *  Power BI Visualizations
 *
 *  Copyright (c) Microsoft Corporation
 *  All rights reserved.
 *  MIT License
 *
 *  Permission is hereby granted, free of charge, to any person obtaining a copy
 *  of this software and associated documentation files (the ""Software""), to deal
 *  in the Software without restriction, including without limitation the rights
 *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *  copies of the Software, and to permit persons to whom the Software is
 *  furnished to do so, subject to the following conditions:
 *
 *  The above copyright notice and this permission notice shall be included in
 *  all copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 *  THE SOFTWARE.
 */

module powerbi.extensibility.visual {
    "use strict";
    import DataViewObjectsParser = powerbi.extensibility.utils.dataview.DataViewObjectsParser;

    export class VisualSettings extends DataViewObjectsParser {
      public dataPoint: dataPointSettings = new dataPointSettings();
      }

    export class dataPointSettings {
     // Default color
      public singularColor: string = "#67a244";
      public singularFontSize: number = 12;
      public singularFontColor: string = "#b1b1b1";
      public dualColor: string = "#b1b1b1";
      public dualFontSize: number = 12;
      public dualFontColor: string = "#67a244";
      public numColor: string = "#E066FF";
      public numFontSize: number = 12;
      public arrowColor: string = "#fff";
      public lineColor: string = "#8b4513";
      public pointColor: string = "#b1b1b1";
      public pointBackGroupColor: string = "#ffffff";
     // Show all
      public showAllDataPoints: boolean = true;
     // Fill
      public fill: string = "";
     // Color saturation
      public fillRule: string = "";
     // Text Size
      public fontSize: number = 12;
     }

}

4.test.d.ts

declare var d3: any;

5.tsconfig.json

{
  "compilerOptions": {
    "allowJs": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "target": "ES5",
    "sourceMap": true,
    "out": "./.tmp/build/visual.js"
  },
  "files": [
    ".api/v1.9.0/PowerBI-visuals.d.ts",
    "node_modules/powerbi-visuals-utils-dataviewutils/lib/index.d.ts",
    "src/settings.ts",
    "src/visual.ts",
    "src/test.d.ts"
  ]
}

6.效果圖

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章