소스 검색

立方体重构

rsbi 4 년 전
부모
커밋
3c0b5dba31
7개의 변경된 파일989개의 추가작업 그리고 21개의 파일을 삭제
  1. 23 2
      src/view/model/Cube.vue
  2. 592 8
      src/view/model/CubeAdd.vue
  3. 68 0
      src/view/model/CubeAddGroup.vue
  4. 82 0
      src/view/model/DimkpiModify.vue
  5. 13 8
      src/view/model/DsetAdd.vue
  6. 198 0
      src/view/model/ExpressAdd.vue
  7. 13 3
      src/view/model/Index.vue

+ 23 - 2
src/view/model/Cube.vue

@@ -74,7 +74,8 @@ export default {
   data() {
     return {
       tableData:[],
-      checked:null
+      checked:null,
+      isupdate:false
     }
   },
   components: {
@@ -98,11 +99,31 @@ export default {
       this.checked = a.cubeId;
     },
     addCube(isupdate){
+      this.isupdate = isupdate;
       let o = this.$parent.$parent.$parent;
       let oper =  o.$refs['cubeOper'];
       o.cubeOperTitle = isupdate===false?"创建立方体":"编辑立方体";
       oper.showDailog();
-      //o.$refs["dsetAddForm"].addDset(isupdate, this.checked);
+      o.$refs["cubeForm"].addCube(isupdate, this.checked);
+    },
+    delCube(){
+      if(!this.checked){
+        this.$notify.error({
+          title: "未勾选数据",
+          offset: 50,
+        });
+        return;
+      }
+      if(confirm("是否确认?")){
+        ajax({
+          url:"model/delCube.action",
+          type:"GET",
+          data:{cubeId:this.checked},
+          success:(resp)=>{
+            this.loadData();
+          }
+        });
+      }
     }
   }
 };

+ 592 - 8
src/view/model/CubeAdd.vue

@@ -1,39 +1,623 @@
 <template>
-  <div>
-    ccc
-  </div>
+  <el-form :model="cube" :rules="rules" ref="cubeForm">
+      <el-tabs v-model="active" type="card">
+        <el-tab-pane label="基本信息" name="base">
+          <el-form-item label="立方体名称" label-width="100px" prop="name">
+            <el-input v-model="cube.name"></el-input>
+          </el-form-item>
+          <el-form-item label="立方体说明" label-width="100px" prop="desc">
+            <el-input type="textarea" v-model="cube.desc"></el-input>
+          </el-form-item>
+          <el-form-item label="对应数据集" label-width="100px" prop="dsetId">
+            <el-select
+              v-model="cube.dsetId"
+              placeholder="请选择"
+              @change="selectdSet"
+            >
+              <el-option
+                v-for="item in dsetList"
+                :key="item.dsetId"
+                :label="item.dsetName"
+                :value="item.dsetId"
+              >
+              </el-option>
+            </el-select>
+          </el-form-item>
+        </el-tab-pane>
+        <el-tab-pane label="立方体信息" name="cubeInfo">
+            <div class="row">
+              <div class="col-sm-4" >
+                <div class="ibox">
+                  <div class="ibox-title" style="line-height: normal">
+                    待选字段
+                  </div>
+                  <div class="ibox-content" style="padding: 0px">
+                    <div
+                      id="cubelefttree"
+                      style="height: 300px; overflow: auto"
+                    ></div>
+                  </div>
+                </div>
+              </div>
+              <div class="col-sm-2" style="text-align: center">
+                <button
+                  type="button"
+                  @click="ds2cube"
+                  style="margin-top: 120px"
+                  class="btn btn-success btn-circle"
+                >
+                  <i class="fa fa-chevron-right"></i></button
+                ><br /><br /><button
+                  type="button"
+                  @click="cube2ds"
+                  class="btn btn-success btn-circle"
+                >
+                  <i class="fa fa-chevron-left"></i>
+                </button>
+              </div>
+              <div class="col-sm-4" style="padding-left: 0">
+                <div class="ibox">
+                  <div class="ibox-title" style="line-height: normal">
+                    维度和度量
+                  </div>
+                  <div class="ibox-content" style="padding: 0px">
+                    <div
+                      id="cuberighttree"
+                      style="height: 300px; overflow: auto"
+                    ></div>
+                  </div>
+                </div>
+              </div>
+              <div class="col-sm-2" style="text-align: center">
+                <button
+                  type="button"
+                  @click="addgroup()"
+                  class="btn btn-default btn-xs"
+                >
+                  <i class="fa fa-plus"></i> 维度分组</button
+                ><br />
+                <button type="button" class="btn btn-default btn-xs" @click="editCalcKpi();">
+                  <i class="fa fa-plus-square"></i> 计算度量</button>
+                <br />
+                  <button type="button" @click="editcubecol();" class="btn btn-default btn-xs">
+                    <i class="fa fa-edit"></i> 编辑</button>
+<br />
+                    <button type="button" @click="cube2ds();" class="btn btn-default btn-xs">
+                      <i class="fa fa-remove"></i> 删除</button>
+              </div>
+            </div>
+        </el-tab-pane>
+      </el-tabs>
+  </el-form>
 </template>
 
 <script>
 import { baseUrl, ajax } from "@/common/biConfig";
 import operationDailog from '@/components/OperationDailog'
+import { Loading } from "element-ui";
 import $ from "jquery"
 
 export default {
    name: 'cubeAdd',
   data() {
     return {
-      base:{
+      cube:{
         cubeId:null,
-        cubeName:null,
+        name:null,
         desc:null,
         dsetId:null,
         dsetName:null
-      }
+      },
+      rules:{
+        name: [{ required: true, message: "必填", trigger: "blur" }],
+        dsetId: [{ required: true, message: "必填", trigger: "blur" }],
+      },
+      active:"base",
+      delObj:[],
+      dsetList:null,
+      dset:null  //当前选择的数据集信息
     }
   },
   components: {
 		
 	},
   mounted() {
+    this.loadDset();
   },
   computed: {},
   methods: {
+    loadDset(){
+      ajax({
+        type:"GET",
+        url:"model/listDataset.action",
+        data:{},
+        success:(resp)=>{
+          this.dsetList = resp.rows.map(r=>{
+            return {dsetId:r.dsetId, dsetName:r.name};
+          });
+        }
+      });
+    },
+    addCube(isupdate, cubeId){
+      if(this.$refs['cubeForm']){
+        this.$refs['cubeForm'].resetFields();
+     }
+      if(isupdate){
+        ajax({
+          type:"GET",
+          url:"model/getCube.action",
+          data:{cubeId:cubeId},
+          success:resp=>{
+            let o = resp.rows;
+            this.cube.cubeId = o.cubeId;
+            this.cube.name = o.cubeName;
+            this.cube.desc = o.desc;
+            this.cube.dsetId = o.dsetId;
+            this.cube.dsetName = o.dsetName;
+            //更新leftTree
+            this.selectdSet(o);
+            //更新rightTree
+            this.initRightTree(o);
+          }
+        });
+      }else{
+        this.initRightTree();
+      }
+    },
+    saveCube(isupdate){
+      let ret = false;
+      let ts = this;
+      this.$refs['cubeForm'].validate(v=>{
+        if(v){
+          var rightRef = $("#cuberighttree").jstree(true);
+          const exec = function(node, allnodes){
+            let nodes = node.children;
+            if(!nodes){
+              return;
+            }
+            $(nodes).each(function(a, b){
+              let tnode = rightRef.get_node(b);
+              allnodes.push(tnode);
+              exec(tnode, allnodes);
+            });
+          }	
 
+          var dims = []
+          exec(rightRef.get_node('cubewd'), dims);
+          var cubeDim = [];
+          if(dims.length == 0){
+            ts.$notify.error("未选择维度");
+            return false;
+          }
+          var curGroup = null;
+          for(let i=0; i<dims.length; i++){
+            var d = dims[i];
+            if(d.li_attr.tp == "group"){
+              curGroup = d;
+            }else{
+              var obj = {name:d.li_attr.dispName, type:d.li_attr.dimtype,col:d.li_attr.col, tname:d.li_attr.tname, alias:d.li_attr.alias, vtype: d.li_attr.vtype, colTable:d.li_attr.colTable,colkey:d.li_attr.colkey,coltext:d.li_attr.coltext,dimord:d.li_attr.dimord, dateformat:d.li_attr.dateformat,calc:(d.li_attr.calc&&d.li_attr.calc==true?1:0),targetId:d.li_attr.targetId,isupdate:d.li_attr.isupdate};
+              var p = rightRef.get_node(rightRef.get_node(d.id).parent);
+              if(p.li_attr && p.li_attr.tp == "group"){
+                obj.groupName = p.text;
+                obj.groupId = p.id;
+              }else{
+                obj.groupName = "";
+                obj.groupId = "";
+              }
+              cubeDim.push(obj);
+            }
+          }
+         var cubeKpi = [];
+          var kpis = [];
+          exec(rightRef.get_node('cubedl'), kpis);
+          if(kpis.length == 0){
+             ts.$notify.error("还未配置度量。");
+            return false;
+          }
+          for(let i=0; i<kpis.length; i++){
+            var t = kpis[i];
+            cubeKpi.push({name:t.li_attr.dispName,col:t.li_attr.col,tname:t.li_attr.tname,alias:t.li_attr.alias,fmt:t.li_attr.fmt,unit:t.li_attr.unit,aggre:t.li_attr.aggre,kpinote:t.li_attr.kpinote,calc:(t.li_attr.calc&&t.li_attr.calc==true?1:0),calcKpi:t.li_attr.calcKpi,targetId:t.li_attr.targetId,isupdate:t.li_attr.isupdate});
+          }
+          let cube = ts.cube;
+          let pageJson = {cubeName:cube.name, dbName:"", desc:cube.desc, dsId:"", dsetId:cube.dsetId, dsetName:cube.dsetName};
+          pageJson.dims = cubeDim;
+          pageJson.kpis = cubeKpi;
+          if(isupdate){
+            pageJson.delObj = this.delObj;
+            pageJson.cubeId = this.cube.cubeId;
+          }
+          var json = JSON.stringify(pageJson);
+          let load = Loading.service({ fullscreen: true });
+          ajax({
+              type:"POST",
+              url:isupdate?"model/updateCube.action":"model/saveCube.action",
+              postJSON:true,
+              dataType:"JSON",
+              data:json,
+              success:function(resp){
+                ts.$notify.success("立方体配置成功。");
+                ts.$parent.$parent.$refs["cubeInfo"].loadData();
+              }
+            }, ts, load);
+          ret = true;
+        }
+      });
+      return ret;
+    },
+    selectdSet(cube){
+      ajax({
+        type:"GET",
+        url:"model/getDatasetCfg.action",
+        data:{dsetId:this.cube.dsetId},
+        success:(resp)=>{
+          resp = resp.rows;
+          this.dset = resp; //放入data对象
+          //获取表
+          var tbs = [];
+          const tabExist = (tname) => {
+            let ret = false;
+            for(let k=0; k<tbs.length; k++){
+              if(tbs[k] === tname){
+                ret = true;
+                break;
+              }
+            }
+            return ret;
+          }
+          let c = resp;
+          for(let i=0; i<c.cols.length; i++){
+            let r = c.cols[i];
+            if(!tabExist(r.tname)){
+              tbs.push(r.tname);
+            }
+          }
+          const findcols = (tname) => {
+            var ret = [];
+            for(let j=0; j<c.cols.length; j++){
+              if(c.cols[j].tname == tname){
+                ret.push(c.cols[j]);
+              }
+            }
+            return ret;
+          }
+          //获取表
+          var dt = [];
+          for(let i=0; i<tbs.length; i++){
+            var ccld = [];
+            var nd = {id:tbs[i],text:tbs[i],icon:'fa fa-table', state:{opened:true}, children:ccld};
+            dt.push(nd);
+            var cols = findcols(tbs[i]);
+            for(let l=0; l<cols.length; l++){
+              var r = cols[l];
+              var node = {id:r.name,text:r.name,icon:'glyphicon glyphicon-menu-hamburger',li_attr:{tp:'node', vtype:r.type, col:r.name, tname:r.tname, expression:r.expression}}
+              ccld.push(node);
+            }
+          }
+          //动态字段
+          if(c.dynamic && c.dynamic != null && c.dynamic.length > 0){
+            var dynas = {id:'dynaroot', text:"动态字段", icon:'fa fa-table', state:{opened:true}, children:[]};
+            dt.push(dynas);
+            for(let i=0; i<c.dynamic.length; i++){
+              var r = c.dynamic[i];
+              var node = {id:r.name,text:r.name,icon:'glyphicon glyphicon-menu-hamburger',li_attr:{tp:'node', vtype:r.type, col:r.name, tname:r.tname, expression:r.expression}}
+              dynas.children.push(node);
+            }
+          }
+          this.initLeftTree(dt);
+        }
+      });
+    },
+    //初始化表字段Tree
+    initLeftTree(dts){
+      let ref = $("#cubelefttree").jstree(true);
+      if (ref) {
+        ref.destroy();
+      }
+      let ts = this;
+      $("#cubelefttree").jstree({
+          core: {
+            check_callback: true,
+            data: dts,
+          },
+          plugins: ["wholerow"],
+        }).bind("ready.jstree", function () {
+          
+        });
+    },
+    initRightTree(cube){
+      //加载立方体字段
+      var targdt = [{id:'cbroot', text:'数据立方体', icon:'fa fa-cubes', state:{opened:true}, children:[]}];
+      targdt[0].children.push({id:"cubewd", text:"维度",icon:'fa fa-gears', state:{opened:true}, children:[]});
+      targdt[0].children.push({id:"cubedl", text:"度量",icon:'glyphicon glyphicon-signal', state:{opened:true}, children:[]});
+      if(cube && cube.dims && cube.kpis){ //给立方体添加维度及指标
+        var dims = targdt[0].children[0].children;
+        var groupexist = function(grouptype){
+          var ls = dims;
+          var ret = null;
+          for(k=0; k<ls.length; k++){
+            if(ls[k].id == grouptype){
+              ret = ls[k];
+              break;
+            }
+          }
+          return ret;
+        }
+        for(let i=0; i<cube.dims.length; i++){
+          var d = cube.dims[i];
+          var obj = {id:d.id, text:d.text, 
+            li_attr:{tp:"dim",drag:true,col:d.col_name,tname:d.tname,dispName:d.text,vtype:d.valType,alias:d.alias, dimtype:d.dim_type,
+            colTable:(d.tableName==null?"":d.tableName),
+            colkey:(d.tableColKey==null?"":d.tableColKey), 
+            coltext:(d.tableColName==null?"":d.tableColName), 
+            dimord:(d.dimord==null?"":d.dimord), 
+            dateformat:(d.dateformat==null?"":d.dateformat), 
+            calc:d.iscalc==1?true:false, targetId:d.col_id
+            },
+            icon:"glyphicon glyphicon-stop icon_dim"
+          };
+          
+          if(d.grouptype != "" && d.grouptype != null){
+            var group = groupexist(d.grouptype);
+            if(group == null){
+              obj = {id:d.grouptype,text:d.groupname, icon:"fa fa-tasks", state:{opened:true}, children:[obj],li_attr:{tp:'group',dispName:d.groupname,drag:true,targetId:d.grouptype}};
+              targdt[0].children[0].children.push(obj);
+            }else{
+              group.children.push(obj); 
+            }
+          }else{
+            targdt[0].children[0].children.push(obj);
+          }
+        }
+        var kpis = targdt[0].children[1].children;
+        for(let i=0; i<cube.kpis.length; i++){
+          var k = cube.kpis[i];
+          //对于计算指标,colname 存的是计算公式,而对于非计算指标,需要取alias来代替colname, 在保存的时候会自动拼接
+          var col = k.alias;
+          if(k.calcKpi == 1){  //新增度量那创建的计算指标
+            col = k.colname;
+          }else if(k.calc == 1){  //数据集创建的动态字段
+            col = k.colname.substring(k.colname.indexOf('(')+1, k.colname.indexOf(')'));
+          }
+          var obj = {id:k.colId, text:k.aggre+'('+k.text+")",state:{opened:true},
+            li_attr:{tp:"kpi",drag:true,aggre:k.aggre,col:col,tname:k.tname, 
+            unit:(k.unit==null?"":k.unit), fmt:(k.fmt==null?"":k.fmt), 
+            dispName:k.text, alias:k.alias,
+            kpinote:(k.kpi_desc_key==null?"":k.kpi_desc_key),
+            calc:(k.calc==0?false:true),calcKpi:k.calcKpi,targetId:k.colid},
+            icon:(k.calcKpi==0?"glyphicon glyphicon-stop icon_kpi":"fa fa-circle icon_kpi")
+          };
+          kpis.push(obj);
+          
+        }
+      }
+      var rightRef = $("#cuberighttree").jstree(true);
+      if(rightRef){
+        rightRef.destroy();
+      }
+      $("#cuberighttree").jstree({
+        core:{
+            check_callback:function(operation, source, node_parent, node_position, more){
+              if(operation == 'move_node'){  //控制 dnd
+                var point = node_position == 0 ? "append":"";
+                if(!more.ref){
+                  return true;
+                }
+                var node = more.ref;
+                if(!node.li_attr || !node.li_attr.drag || node.li_attr.drag ==false ){
+                  return false;
+                }
+                var s = source.li_attr.tp, c = node.li_attr.tp;
+                //指标和分类不能放到维度区域
+                if((s == 'kpigroup' || s=="kpi") && (c == 'dim' || c == 'group')){
+                  return false;
+                }
+                //维度和分组不能拖到指标区域
+                if((s == 'dim' || s == 'group') && ( c == "kpigroup" || c == "kpi")){
+                  return false;
+                }
+                //分组不能拖放到维度下
+                if(((s == "group" && c == "dim") || (s == "kpigroup" && c == "kpi")) && point == "append"){
+                  return false;
+                }
+                //指标不能放到指标下,维度不能放到维度下,分类不能拖放到分类下
+                if((s=="kpi" && c == "kpi" && point == "append") || (s == "dim" && c == "dim" && point == "append") || (s=="group" && c=="group" && point=="append") || (s=="kpigroup" && c=="kpigroup" && point=="append" )){
+                  return false;
+                }
+                source.li_attr.isupdate = 'y'
+                return true;
+              }else{
+                return true;
+              }
+            },
+            dblclick_toggle:false,
+            data:targdt
+          },
+          dnd:{
+            is_draggable:function(node){
+              node = node[0];
+              if(node.li_attr && node.li_attr.drag){
+              return true;
+            }else{
+              return false;
+            }
+            },
+            large_drop_target:'selected',
+            large_drag_target:'selected'
+          },	
+          "plugins" : [
+              "wholerow","dnd"
+            ]
+      }).bind("ready.jstree", function(){
+       
+      }).bind("dblclick.jstree", function(e, data){
+        //editcubecol(cube.tid);
+      });
+    },
+    ds2cube(){
+        let ts = this;
+        var leftRef = $("#cubelefttree").jstree(true);
+        var left = leftRef.get_selected(true);
+        if(left.length == 0){
+          ts.$notify.error("您还未从左边选择字段。");
+          return;
+        }
+        left = left[0];
+        if(!left.li_attr){
+          ts.$notify.error("请选择字段。");
+          return;
+        }
+        if(leftRef.is_hidden(left)){
+          return;
+        }
+        var rightRef = $("#cuberighttree").jstree(true);
+        var right = rightRef.get_selected(true);
+        if(right.length == 0){
+          ts.$notify.error("您还未选择右边度量或维度。");
+          return;
+        }
+        right = right[0];
+        var parent = right.parent;
+        if(!parent){
+          ts.$notify.error("您还未选择右边度量或维度。");
+          return;
+        }
+        parent = rightRef.get_node(parent);
+        var isCalc = true; //是否是公式?
+        if(!left.li_attr.expression||left.li_attr.expression==null||left.li_attr.expression==""){
+          isCalc = false;
+        }	
+        if(right.id == 'cubedl' || parent.id == 'cubedl' || (parent.li_attr && parent.li_attr.tp == "kpigroup")){
+          //生成ID
+          var cid = ts.findCubeMaxId();
+          //calc 表示是否是动态字段,
+          //calcKpi 表示是否是计算指标
+          var o = {id:cid.id, text:'sum('+left.text+")",li_attr:{tp:"kpi",drag:true,aggre:"sum",col:(!isCalc?left.li_attr.col:left.li_attr.expression), tname:left.li_attr.tname,dispName:left.text,alias:left.id,calc:isCalc,calcKpi:0},icon:"glyphicon glyphicon-stop icon_kpi"};
+          if(right.id == 'cubedl' || (parent.id=="cubedl" && right.li_attr.tp == "kpigroup")){
+            rightRef.create_node(right.id, o);
+            rightRef.open_node(right.id);
+          }else{
+            //获取位置
+            var cnodes = rightRef.get_node(right.parent);
+            var idx = -1;
+            for(j=0; j<cnodes.children.length; j++){
+              if(cnodes.children[j] == right.id){
+                idx = j;
+                break;
+              }
+            }
+            rightRef.create_node(right.parent, o, idx + 1);
+          }
+          leftRef.hide_node(left.id);
+        }else if(right.id == 'cubewd' || parent.id == 'cubewd' || (parent.li_attr && parent.li_attr.tp == 'group')){
+          var cid = ts.findCubeMaxId();
+          var o = {id:cid.id, text:left.text, li_attr:{tp:"dim",drag:true,col:!isCalc?left.li_attr.col:left.li_attr.expression,tname:left.li_attr.tname,dispName:left.text,tname:left.li_attr.tname,vtype:left.li_attr.vtype,alias:left.li_attr.col,calc:isCalc},icon:"glyphicon glyphicon-stop icon_dim", targetId:""};  //通过targetId 来指引对应数据库的的字段 ID, 用在修改上
+          if(right.id == 'cubewd' || (parent.id == 'cubewd' && right.li_attr.tp == 'group')){
+            rightRef.create_node(right.id, o);
+            rightRef.open_node(right.id);
+          }else{
+            //获取位置
+            var cnodes = rightRef.get_node(right.parent);
+            var idx = -1;
+            for(j=0; j<cnodes.children.length; j++){
+              if(cnodes.children[j] == right.id){
+                idx = j;
+                break;
+              }
+            }
+            rightRef.create_node(right.parent, o, idx + 1);
+          }
+          leftRef.hide_node(left.id);
+        }
+    },
+    cube2ds(){
+      var rightRef = $("#cuberighttree").jstree(true);
+      var leftRef = $("#cubelefttree").jstree(true);
+      var right = rightRef.get_selected(true);
+      if(right.length == 0 || !(right[0].li_attr) || !right[0].li_attr.tp){
+        ts.$notify.error("您还未选择需要删除的度量或维度。");
+        return;
+      }
+      right = right[0];
+      if(right.li_attr.tp == 'group'){
+        if(right.children && right.children.length > 0){
+          ts.$notify.error("您要删除的分组含有维度,不能删除。");
+          return;
+        }
+      }
+      if(right.li_attr.tp == 'kpigroup'){
+        if(right.children && right.children.length > 0){
+          ts.$notify.error("您要删除的分类下含有度量,不能删除。");
+          return;
+        }
+      }
+      if(right.li_attr.tp != 'group'){ //分组删除不用关联左边树
+        var id = right.li_attr.fromCol;   //通过 refId 引用s数据集的字段ID
+        leftRef.show_node(id);
+      }
+      if(this.delObj){
+        this.delObj.push({'type':right.li_attr.tp, id: right.li_attr.targetId}); //在修改立方体时用来删除的内容
+      }
+      rightRef.delete_node(right);
+    },
+    findCubeMaxId(){
+      var ret = 0;
+      var maxAliasId = 0;
+      var ref = $("#cuberighttree").jstree(true);
+      var node = ref.get_node('#');
+      var exec = function(node){
+        var nodes = node.children;
+        if(!nodes){
+          return;
+        }
+        $(nodes).each(function(a, b){
+          var tnode = ref.get_node(b);
+          if(tnode.id > ret){
+            ret = Number(tnode.id);
+          }
+          if(tnode.li_attr && tnode.li_attr.alias) {
+            var alias = Number(tnode.li_attr.alias.replace(/[d|k]/g, ''));
+            if(!isNaN(alias) && alias > maxAliasId) {
+              maxAliasId = alias;
+            }
+          }
+          exec(tnode);
+        });
+      }
+      exec(node);
+      return {id:ret + 1, aliasId:maxAliasId + 1};
+    },
+    addgroup(){
+      this.$parent.$parent.$refs['groupForm'].create();
+    },
+    editCalcKpi(){
+      this.$parent.$parent.$refs['expressionForm'].create(false, this.dset);
+    },
+    editcubecol(){
+      let ref = $("#cuberighttree").jstree(true);
+      if(!ref){
+        return;
+      }
+      let node = ref.get_selected(true);
+      if(node.length === 0){
+        this.$notify.error("未勾选维度或度量。");
+      }
+      node = node[0];
+      //计算指标特殊处理
+      if(node.li_attr.tp == 'kpi' && node.li_attr.calcKpi == 1){
+        this.$parent.$parent.$refs['expressionForm'].create(true, this.dset);
+        return;
+      }
+      this.$parent.$parent.$refs['dimKpiForm'].modify(node);
+    }
   }
 };
 </script>
 
-<style lang="less" scoped>
-@import "../../style/mixin";
+<style lang="css">
+.icon_kpi {
+	color:#e07900;
+}
+.icon_dim {
+	color:#006ae1;
+}
 </style>

+ 68 - 0
src/view/model/CubeAddGroup.vue

@@ -0,0 +1,68 @@
+<template>
+  <el-dialog title="创建维度分组" :visible.sync="show">
+    <el-form :model="group" ref="groupForm" :rules="rule">
+         <el-form-item label="分组名称" label-width="100px" prop="groupName">
+          <el-input v-model="group.groupName"></el-input>
+          </el-form-item>
+    </el-form>
+    <div slot="footer" class="dialog-footer">
+      <el-button type="primary" @click="save()">确 定</el-button>
+      <el-button @click="show = false">取 消</el-button>
+    </div>
+  </el-dialog>
+</template>
+
+<script>
+import { baseUrl, ajax, newGuid } from "@/common/biConfig";
+import $ from "jquery"
+
+export default {
+   name: 'cubeGroup',
+  data() {
+    return {
+      show:false,
+     group: {
+       groupName:""
+     },
+      rule:{
+        groupName: [{ required: true, message: "必填", trigger: "blur" }],
+      }
+    }
+  },
+  components: {
+		
+	},
+  mounted() {
+    
+  },
+  computed: {},
+  methods: {
+    save(){
+      this.$refs['groupForm'].validate(v=>{
+        if(v){
+          var cid = newGuid();
+          var dt = {id:cid,text:this.group.groupName, "icon": "fa fa-tasks", li_attr:{tp:'group',dispName:name,drag:true}};
+          var ref = $("#cuberighttree").jstree(true);
+          ref.create_node('cubewd', dt);
+
+          if (ref.is_closed('cubewd')) {
+            ref.open_node('cubewd');
+          }
+          this.show = false;
+        }
+      });
+        
+    },
+    create(){
+      this.show = true;
+      if(this.$refs['groupForm']){
+        this.$refs['groupForm'].resetFields();
+     }
+    }
+  }
+};
+</script>
+
+<style lang="less" scoped>
+@import "../../style/mixin";
+</style>

+ 82 - 0
src/view/model/DimkpiModify.vue

@@ -0,0 +1,82 @@
+<template>
+  <el-dialog title="编辑维度及度量" :visible.sync="show">
+    <el-form :model="node" ref="nodeForm" :rules="rule">
+        <template v-if="type === 'dim'">
+          <el-form-item label="维度字段" label-width="100px">
+            {{ node.col }}
+          </el-form-item>
+        </template>
+        <template v-if="type === 'kpi'">
+          <el-form-item label="度量字段" label-width="100px">
+            {{ node.col }}
+          </el-form-item>
+        </template>
+        <el-form-item label="别名" label-width="100px">
+            {{ node.alias }}
+          </el-form-item>
+         <el-form-item label="显示名称" label-width="100px" prop="dimname">
+          <el-input v-model="node.dimname"></el-input>
+         </el-form-item>
+    </el-form>
+    <div slot="footer" class="dialog-footer">
+      <el-button type="primary" @click="save()">确 定</el-button>
+      <el-button @click="show = false">取 消</el-button>
+    </div>
+  </el-dialog>
+</template>
+
+<script>
+import { ajax, newGuid } from "@/common/biConfig";
+import $ from "jquery"
+
+export default {
+   name: 'dimkpiModify',
+  data() {
+    return {
+      show:false,
+      tit:"",
+     node: {
+      dimname:"",
+      col:"",
+      alias:"",
+     },
+      rule:{
+        dimname: [{ required: true, message: "必填", trigger: "blur" }],
+      },
+      type:''  //dim/kpi/group 三种
+    }
+  },
+  components: {
+		
+	},
+  mounted() {
+    
+  },
+  computed: {},
+  methods: {
+    save(){
+      let ts = this;
+      this.$refs['nodeForm'].validate(v=>{
+       if(v){
+					
+          ts.show = false;
+       }
+      });
+    },
+    modify(selectNode){
+      this.show = true;
+      if(this.$refs['nodeForm']){
+        this.$refs['nodeForm'].resetFields();
+      }
+      this.type = selectNode.li_attr.tp;
+      this.node.col = selectNode.li_attr.col;
+      this.node.alias = selectNode.li_attr.alias;
+      this.node.dimname = selectNode.li_attr.dispName;
+    }
+  }
+};
+</script>
+
+<style lang="less" scoped>
+@import "../../style/mixin";
+</style>

+ 13 - 8
src/view/model/DsetAdd.vue

@@ -380,7 +380,7 @@ export default {
             dataType: "json",
             data: { dsetId: dsetId },
             success: function (resp) {
-              resolve(JSON.parse(resp.rows));
+              resolve(resp.rows);
             },
           });
         }).then((json) => {
@@ -537,7 +537,7 @@ export default {
 
       node = selRef.get_node(node[0]);
 
-      if (this.isupdate && node.id == dset.master) {
+      if (this.isupdate && node.id == this.dset.master) {
         msginfo("不能移除主表。");
         return;
       }
@@ -586,6 +586,9 @@ export default {
             //判断是否有关联字段
             const exist = (v) => {
               let ret = null;
+              if(!ds.joininfo){
+                return ret;
+              }
               for (let c of ds.joininfo) {
                 if (c.col === v) {
                   ret = c;
@@ -594,12 +597,14 @@ export default {
               }
               return ret;
             };
-            for (let o of d) {
-              let r = exist(o.id);
-              if (r) {
-                o.text = o.text + "->" + r.ref + "." + r.refKey;
-                o.icon = "glyphicon glyphicon-link";
-                o.li_attr = { ref: r.ref, refKey: r.refKey, jtype: r.jtype };
+            if(isupdate){
+              for (let o of d) {
+                let r = exist(o.id);
+                if (r) {
+                  o.text = o.text + "->" + r.ref + "." + r.refKey;
+                  o.icon = "glyphicon glyphicon-link";
+                  o.li_attr = { ref: r.ref, refKey: r.refKey, jtype: r.jtype };
+                }
               }
             }
 

+ 198 - 0
src/view/model/ExpressAdd.vue

@@ -0,0 +1,198 @@
+<template>
+  <el-dialog :title="tit" :visible.sync="show">
+    <el-form :model="express" ref="expressForm" :rules="rule">
+         <el-form-item label="度量标识" label-width="100px" prop="alias">
+          <el-input v-model="express.alias"></el-input>
+         </el-form-item>
+         <el-form-item label="显示名称" label-width="100px" prop="kpiname">
+          <el-input v-model="express.kpiname"></el-input>
+         </el-form-item>
+         <el-form-item label="表 达 式" label-width="100px" prop="expression">
+          <el-input id="mybds" type="textarea" v-model="express.expression"></el-input>
+         </el-form-item>
+         <div style="line-height:25px;">
+          <template v-for="c in cols">
+              <button @click="selectCol(c.col)" style="margin-right:5px;" type="button" :key="c.col" :name="c.col" class="btn btn-primary btn-xs">{{ c.col }}</button>
+          </template>
+          </div>
+          <el-form-item label="计算方式" label-width="100px" prop="kpiaggre">
+            <el-select
+              v-model="express.kpiaggre"
+              placeholder="请选择"
+            >
+              <el-option
+                v-for="item in opt.js"
+                :key="item"
+                :label="item"
+                :value="item"
+              >
+              </el-option>
+            </el-select>
+          </el-form-item>
+          <el-form-item label="度量单位" label-width="100px" prop="kpiunit">
+            <el-input v-model="express.kpiunit"></el-input>
+          </el-form-item>
+          <el-form-item label="格式化" label-width="100px" prop="kpifmt">
+            <el-select
+              v-model="express.kpifmt"
+              placeholder="请选择"
+            >
+              <el-option
+                v-for="item in opt.fmt"
+                :key="item.value"
+                :label="item.text"
+                :value="item.value"
+              >
+              </el-option>
+            </el-select>
+          </el-form-item>
+          <el-form-item label="指标解释" label-width="100px" prop="kpinote">
+            <el-input type="textarea" v-model="express.kpinote"></el-input>
+          </el-form-item>
+    </el-form>
+    <div slot="footer" class="dialog-footer">
+      <el-button type="primary" @click="save()">确 定</el-button>
+      <el-button @click="show = false">取 消</el-button>
+    </div>
+  </el-dialog>
+</template>
+
+<script>
+import { baseUrl, ajax, newGuid,insertText2focus } from "@/common/biConfig";
+import $ from "jquery"
+
+export default {
+   name: 'expressKpi',
+  data() {
+    return {
+      show:false,
+      tit:"",
+     express: {
+       alias:"",
+       kpiname:"",
+       expression:"",
+       kpiaggre:"",
+       kpiunit:"",
+       kpifmt:"",
+       kpinote:""
+     },
+      cols:[],
+      rule:{
+        alias: [{ required: true, message: "必填", trigger: "blur" }],
+        kpiname: [{ required: true, message: "必填", trigger: "blur" }],
+        expression: [{ required: true, message: "必填", trigger: "blur" }],
+        kpiaggre: [{ required: true, message: "必填", trigger: "blur" }],
+      },
+      dset:null,
+      opt:{
+        js:["sum","avg","count","count(distinct)", "max", "min"],
+        fmt:[{
+          value:"#,###", text:"整数"
+        },{
+          value:"#,###.00", text:"小数(保留两位)"
+        },{
+          value:"#,###.0000", text:"小数(保留四位)"
+        },{
+          value:"0.00%", text: "百分比"
+        }]
+      },
+      isupdate:false
+    }
+  },
+  components: {
+		
+	},
+  mounted() {
+    
+  },
+  computed: {},
+  methods: {
+    save(){
+      let ts = this;
+      this.$refs['expressForm'].validate(v=>{
+       if(v){
+					if(ts.ischinese(ts.express.alias)){
+            ts.$notify.error("度量标识只能是英文字符。");
+						return;
+					}
+          let ref = $("#cuberighttree").jstree(true);
+          let kpi = ref.get_selected(true)[0];
+					if(ts.isupdate){
+						kpi.li_attr.aggre = ts.express.kpiaggre;
+						kpi.li_attr.fmt = ts.express.kpifmt;
+						kpi.li_attr.unit = ts.express.kpiunit;
+						kpi.li_attr.dispName = ts.express.kpiname;
+						kpi.li_attr.kpinote =  ts.express.kpinote;
+						kpi.li_attr.col = ts.express.expression;
+						kpi.li_attr.alias = ts.express.alias;
+            kpi.li_attr.isupdate = "y";  //表示计算指标已经更改过了。
+            ref.rename_node(kpi, kpi.li_attr.aggre+"("+ts.express.kpiname+")");
+					}else{
+						var cid = ts.$parent.$refs['cubeForm'].findCubeMaxId();
+            var o = {id:cid.id, text:ts.express.kpiaggre+"("+ts.express.kpiname+")",
+            li_attr:{tp:"kpi",calc:true,drag:true,aggre:ts.express.kpiaggre,col:ts.express.expression, 
+            alias:ts.express.alias, dispName:ts.express.kpiname,tname:"",fmt:ts.express.kpifmt,
+            unit:ts.express.kpiunit,kpinote:ts.express.kpinote,calcKpi:1},icon:"fa fa-circle icon_kpi"};
+            ref.create_node("cubedl", o);
+          }
+          ts.show = false;
+       }
+      });
+    },
+    create(isupdate, dset){
+      if(isupdate){
+        this.tit = "修改表达式度量";
+      }else{
+        this.tit = "创建表达式度量";
+      }
+      this.show = true;
+      if(this.$refs['expressForm']){
+        this.$refs['expressForm'].resetFields();
+      }
+      this.dset = dset;
+      //初始化 cols
+      let ref = $("#cubelefttree").jstree(true);
+      if(ref){
+        let r = ref.get_node("#").children;
+        let cols = [];
+        $(r).each((a, b)=>{
+          $(ref.get_node(b).children).each((a, b)=>{
+            let t = ref.get_node(b);
+            cols.push({col:t.li_attr.col});
+          });
+        });
+        this.cols = cols;
+      }
+      if(!isupdate){
+        this.express.alias = "k_" + Math.round(Math.random() * 100000);
+      }else{
+        //回写值
+        let rightRef = $("#cuberighttree").jstree(true);
+        let node = rightRef.get_selected(true)[0];
+        this.express.alias = node.li_attr.alias;
+        this.express.expression = node.li_attr.col;
+        this.express.kpiname = node.li_attr.dispName;
+        this.express.kpiaggre = node.li_attr.aggre;
+        this.express.kpiunit = node.li_attr.unit;
+        this.express.kpifmt = node.li_attr.fmt;
+        this.express.kpinote = node.li_attr.kpinote;
+      }
+      this.isupdate = isupdate;
+    },
+    selectCol(v){
+     insertText2focus(document.getElementById("mybds"), v);
+   },
+   ischinese(a){
+      if (/[\u4E00-\u9FA5]/i.test(a)) {
+        return true;  
+      }else{   
+         return false 
+      }
+    }
+  }
+};
+</script>
+
+<style lang="less" scoped>
+@import "../../style/mixin";
+</style>

+ 13 - 3
src/view/model/Index.vue

@@ -11,7 +11,7 @@
             <dset ref="dsetGrid"></dset>
           </el-tab-pane>
           <el-tab-pane label="立方体">
-            <cube></cube>
+            <cube ref="cubeInfo"></cube>
           </el-tab-pane>
         </el-tabs>
       </div>
@@ -31,6 +31,9 @@
     <operationDailog mainDiv="mainDiv" :title="cubeOperTitle" ref="cubeOper" :callback="saveCube">
       <cubeAdd ref="cubeForm"></cubeAdd>
     </operationDailog>
+    <groupAdd ref="groupForm"></groupAdd>
+    <expressionAdd ref="expressionForm"></expressionAdd>
+    <dimKpiModify ref="dimKpiForm"></dimKpiModify>
   </div>
 </template>
 
@@ -46,6 +49,9 @@ import dsetTableJoin from "@/view/model/DsetTableJoin";
 import dsetColModify from "@/view/model/DsetColModify";
 import dynaCol from "@/view/model/DsetDynaCol";
 import cubeAdd from "@/view/model/CubeAdd";
+import groupAdd from "@/view/model/CubeAddGroup";
+import expressionAdd from "@/view/model/ExpressAdd";
+import dimKpiModify from "@/view/model/DimKpiModify";
 
 export default {
   name:"modelIndex",
@@ -64,7 +70,10 @@ export default {
     dsetTableJoin,
     dsetColModify,
     dynaCol,
-    cubeAdd
+    cubeAdd,
+    groupAdd,
+    expressionAdd,
+    dimKpiModify
   },
   mounted() {},
   computed: {},
@@ -82,7 +91,8 @@ export default {
       return this.$refs['dsetAddForm'].saveDset(update);
     },
     saveCube(){
-      alert(1);
+      let update = this.$refs['cubeInfo'].isupdate;
+      return this.$refs['cubeForm'].saveCube(update);
     }
   },
   watch: {},