数据下钻

本例中我们将制作一个可以下钻的自定义组件。

首先我们需要准备一个可以下钻的excel数据源,例如:

Application

然后需要对数据下钻的操作有一定了解,点击这里查看数据下钻相关的内容介绍

接下来我们开始开发自定义组件,本例中我们制作一个表格,为了快速开发,我们暂时引用了vuejs,帮我实现双向绑定。

config.json

{
"develop":{
"html":"index.html",
"css":["./index.css"],
"entry":"./main.js",
"scripts":{
}
},
"plugins":[],
"services":["$customService"],
"libs":["https://cdn.jsdelivr.net/npm/vue/dist/vue.js"],
"customAttributes":{
"pro":[
],
"con":[
{
"type":"BiDatasourceConfigComponent",
"customInput":{
"dimensionCount":1,
"measureCount":1,
"canDrillDown":true,
"widgetCanDrillDown":true
},
"setDataParams":{
"simple":true
}
},{
"type":"ShadowTitleDisplay",
"name":"参与组件过滤"
}
]
}
}

系统默认创建的文件,不包含index.css文件,这里我们需要更改样式,所以需要手动创建css文件。

index.css

._#customWidget#_{
color: #fff;
}
._#customWidget#_ table{
margin: 0 auto;
}
._#customWidget#_ table tr{
border-bottom: 1px solid #ccc;
}
._#customWidget#_ table tr:first-child{
border-top: 1px solid #ccc;
}
._#customWidget#_ table tr td,._#customWidget#_ table tr th{
border-left: 1px solid #ccc;
padding: 2px;
}
._#customWidget#_ table tr td:last-child,._#customWidget#_ table tr th:last-child{
border-right: 1px solid #ccc;
}
._#customWidget#_ table trth.clickItem{
cursor:pointer;
}
._#customWidget#_ .drillupBtn{
cursor:pointer;
}

我们配置了$customService,并在libs中引入了vuejs。

想要进行关联,必须添加参与组件过滤属性栏组件。

index.html

<body>
<div id="_#visroot#_" class="_#customWidget#_">
<div class="drillupBtn">
上钻
</div>
<table>
<thead>
<tr>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td></td>
</tr>
</tbody>
</table>
</div>
</body>

main.js

(function(option){
return {
$data:{
$customService:null
},
$hooks:{
onInit(){
this.$customService=this.$customService=window.services.$customService;
},
onDestroy(){
console.log('销毁')
},
setData(value){
},
outSizeCallBack(value){
},
},
$methods:{
}
}
})

添加完$customService的引用后,我们可以初始化vue的代码。这里需要注意,在libs中引入的第三方库,不需要声明。

首先我们需要添加一个对象,用来存放在html上渲染的数据。

(function(option){
return {
$data:{
$customService:null,
data:{
table1:{
data:{},
dimensions:[],
measures:[],
drilldown:false,
drillup:false,
requesting:false
}
}
},
$hooks:{
onInit(){
this.$customService=this.$customService=window.services.$customService;
this.vueInit();
},
onDestroy(){
console.log('销毁')
},
setData(value){
},
outSizeCallBack(value){
},
},
$methods:{
vueInit(){
new Vue({
el: '#_#visroot#_',
data: this.data,
methods: {}
});
Vue.config.productionTip=false;
}
}
}
})

接下来先对返回数据进行处理。

(function(option){
return {
$data:{
$customService:null,
datasource1:null,
data:{
table1:{
data:{},
dimensions:[],
measures:[],
drillup:false,//上钻按钮是否显示
requesting:false//数据正在请求
}
}
},
$hooks:{
onInit(){
this.$customService=this.$customService=window.services.$customService;
this.vueInit();
},
onDestroy(){
console.log('销毁')
},
setData(data,obj){
if(data&&obj){
let dimensionList=this.$customService.getDataSourceDimensionColumns(obj.data);
let measureList=this.$customService.getDataSourceMeasureColumns(obj.data);
this.data.table1.dimensions.splice(0,this.data.table1.dimensions.length);//清空数组的长度
this.data.table1.measures.splice(0,this.data.table1.measures.length);//清空数组的长度
let index=dimensionList.findIndex(item=>item.type==='drill-folder');
if(index>-1){
let drilldownItem=dimensionList[0];
this.data.table1.dimensions.push(drilldownItem.list[drilldownItem.index]);
}else{
this.data.table1.dimensions.push(...dimensionList);
}
this.data.table1.measures.push(...measureList);
Object.keys(data.data).forEach(key=>{
this.data.table1.data[key]=data.data[key];
});
this.datasource1=obj.data;
}
},
outSizeCallBack(value){
},
},
$methods:{
vueInit(){
new Vue({
el: '#_#visroot#_',
data: this.data,
methods: {}
});
Vue.config.productionTip=false;
}
}
}
})

我们通过$customService的getDataSourceDimensionColumns方法获取到了维度列表,这里的数组长度为1。

然后我们找到 type类型为drill-folder的那一项,表明该维度是可以下钻的。

{
"name": "下钻目录1",
"type": "drill-folder",
"index": 0,
"list": [{
"column": "c0",
"type": "string",
"name": "省",
"key": ""
}, {
"column": "c1",
"type": "string",
"name": "市",
"key": ""
}, {
"column": "c2",
"type": "string",
"name": "学校"
}]
}
  • type drill-folder表明该维度是下钻目录.
  • index 该项下的index表明下钻到了第几层。
  • list 中存储着可以下钻的维度列表。

接下来调整html代码

index.html

<body>
<div id="_#visroot#_" class="_#customWidget#_">
<table>
<thead>
<tr>
<template v-for="dimension in table1.dimensions">
<th>{{dimension.name}}</th>
<th v-for="name in table1.data[dimension.column]" class="clickItem" style="cursor:pointer;color:rgb(255,255,0)">{{name}}</th>
</template>
</tr>
</thead>
<tbody>
<tr v-for="measure in table1.measures">
<td>{{measure.name}}</td><td v-for="value in table1.data[measure.column]">{{value}}</td>
</tr>
</tbody>
</table>
</div>
</body>

这个时候点击运行,连接上数据源,可以看到数据显示了。 Application

下面我们添加下钻功能,在表头位置,我们加点击事件:v-on:click="drilldown(dimension,name)",在点击维度名称的时候,进行数据下钻。

<body>
<div id="_#visroot#_" class="_#customWidget#_">
<table>
<thead>
<tr>
<template v-for="dimension in table1.dimensions">
<th>{{dimension.name}}</th>
<th v-for="name in table1.data[dimension.column]" class="clickItem" style="cursor:pointer;color:rgb(255,255,0)" v-on:click="drilldown(dimension,name)">{{name}}</th>
</template>
</tr>
</thead>
<tbody>
<tr v-for="measure in table1.measures">
<td>{{measure.name}}</td><td v-for="value in table1.data[measure.column]">{{value}}</td>
</tr>
</tbody>
</table>
</div>
</body>

修改main.js,需要注意的是我们绑定的是vue的点击事件,它的函数应该在vue的methods中,同时也应该注意this的指向。

(function(option){
return {
$data:{
$customService:null,
properties:null,
datasource1:null,
data:{
table1:{
data:{},
dimensions:[],
measures:[]
}
}
},
$hooks:{
onInit(properties){
this.properties=properties;
this.$customService=this.$customService=window.services.$customService;
this.vueInit();
},
onDestroy(){
console.log('销毁')
},
setData(data,obj){
if(data&&obj){
let dimensionList=this.$customService.getDataSourceDimensionColumns(obj.data);
let measureList=this.$customService.getDataSourceMeasureColumns(obj.data);
this.data.table1.dimensions.splice(0,this.data.table1.dimensions.length);//清空数组的长度
this.data.table1.measures.splice(0,this.data.table1.measures.length);//清空数组的长度
let index=dimensionList.findIndex(item=>item.type==='drill-folder');
if(index>-1){
let drilldownItem=dimensionList[0];
this.data.table1.dimensions.push(drilldownItem.list[drilldownItem.index]);
console.log('//////////////');
console.log(JSON.stringify(dimensionList));
}else{
this.data.table1.dimensions.push(...dimensionList);
}
this.data.table1.measures.push(...measureList);
Object.keys(data.data).forEach(key=>{
this.data.table1.data[key]=data.data[key];
});
this.datasource1=obj.data;
}
},
outSizeCallBack(value){
},
},
$methods:{
vueInit(){
let that=this;
new Vue({
el: '#_#visroot#_',
data: this.data,
methods: {
drilldown(dimension,name){
that.$customService.multiDataSourceDrillDown(name,that.datasource1,that.properties.config.linkage,that.properties);
}
}
});
Vue.config.productionTip=false;
}
}
}
})

我们在drilldown方法里调用了$customService中的multiDataSourceDrillDown方法。当我们点击维度后,它整理要展示的下钻参数,向后端重新发起请求。一般情况下,我们会再次在setData方法中获取到数据。

setData方法中我们通过$customService的getDataSourceDimensionColumns方法获取到了维度,需要注意的是,进行一次下钻后,index的值变为1

{
"name": "下钻目录1",
"type": "drill-folder",
"index": 1,
"list": [{
"column": "c0",
"type": "string",
"name": "省",
"key": ""
}, {
"column": "c1",
"type": "string",
"name": "市",
"key": ""
}, {
"column": "c2",
"type": "string",
"name": "学校"
}]
}

重新预览后,我们可以点击维度进行下钻了。

点击黄色的维度名称就可以下钻了

Application

点击河北,之后会进行下钻。

Application

加下来我们实现上钻功能,在index.html中添加上钻按钮。

index.html

<body>
<div id="_#visroot#_" class="_#customWidget#_">
<div class="drillupBtn" v-if="table1.drillup" v-on:click="drillup()">
上钻
</div>
<table>
<thead>
<tr>
<template v-for="dimension in table1.dimensions">
<th>{{dimension.name}}</th>
<th v-for="name in table1.data[dimension.column]" class="clickItem" style="cursor:pointer;color:rgb(255,255,0)" v-on:click="drilldown(dimension,name)">{{name}}</th>
</template>
</tr>
</thead>
<tbody>
<tr v-for="measure in table1.measures">
<td>{{measure.name}}</td><td v-for="value in table1.data[measure.column]">{{value}}</td>
</tr>
</tbody>
</table>
</div>
</body>

在main.js中添加drillup方法,并且设置在$data下的table1中添加drillup变量,初始值为false,用来控制按钮的显示和隐藏。

drilldownIndex用来存储下钻的层级。

main.js

(function(option){
return {
$data:{
$customService:null,
properties:null,
datasource1:null,
drilldownIndex:0,
data:{
table1:{
data:{},
dimensions:[],
measures:[],
drillup:false
}
}
},
$hooks:{
onInit(properties){
this.properties=properties;
this.$customService=this.$customService=window.services.$customService;
this.vueInit();
},
onDestroy(){
console.log('销毁')
},
setData(data,obj){
if(data&&obj){
let dimensionList=this.$customService.getDataSourceDimensionColumns(obj.data);
let measureList=this.$customService.getDataSourceMeasureColumns(obj.data);
this.data.table1.dimensions.splice(0,this.data.table1.dimensions.length);//清空数组的长度
this.data.table1.measures.splice(0,this.data.table1.measures.length);//清空数组的长度
let index=dimensionList.findIndex(item=>item.type==='drill-folder');
if(index>-1){
let drilldownItem=dimensionList[0];
this.data.table1.dimensions.push(drilldownItem.list[drilldownItem.index]);
}else{
this.data.table1.dimensions.push(...dimensionList);
}
this.data.table1.measures.push(...measureList);
Object.keys(data.data).forEach(key=>{
this.data.table1.data[key]=data.data[key];
});
this.datasource1=obj.data;
}
},
outSizeCallBack(value){
},
},
$methods:{
vueInit(){
let that=this;
new Vue({
el: '#_#visroot#_',
data: this.data,
methods: {
drilldown(dimension,name){
that.$customService.multiDataSourceDrillDown(name,that.datasource1,that.properties.config.linkage,that.properties);
if(that.drilldownIndex<2)that.drilldownIndex++;
if(that.drilldownIndex>0)that.data.table1.drillup=true;
},
drillup(){
if(that.drilldownIndex>0)that.drilldownIndex--;
if(that.drilldownIndex===0)that.data.table1.drillup=false;
that.$customService.multiDataSourceDrillUp(that.datasource1,that.properties.config.linkage,that.properties);
}
}
});
Vue.config.productionTip=false;
}
}
}
})

至此,上钻下钻功能开发完成。demo下载