<template>
  <div class="dataset-edit">
    <!-- 表字段预览弹框 -->
    <el-dialog
      append-to-body
      title="字段预览"
      width="400px"
      :visible.sync="showViewFields"
      class="dataset-edit-table-fields"
    >
      <div class="title" v-if="nowTable">
        {{ nowTable.tableComments + " " + nowTable.tableViewName }}
      </div>
      <el-table
        size="mini"
        border
        :data="nowTable && nowTable.fields"
        height="400px"
        v-loading="nowTable && !nowTable.fields.length"
      >
        <el-table-column label="字段名" prop="columnName"></el-table-column>
        <el-table-column label="注释" prop="columnComment"></el-table-column>
        <el-table-column label="类型" prop="viewDataType"></el-table-column>
      </el-table>
    </el-dialog>
    <!-- 文件上传抽屉 -->
    <el-drawer
      title="上传文件"
      :visible.sync="showUpload"
      direction="rtl"
      :wrapperClosable="false"
      size="500px"
      class="upload-drawer"
    >
      <div
        v-loading="uploadLoading"
        element-loading-text="上传中"
        element-loading-spinner="el-icon-loading"
      >
        <div class="tips">
          <i class="el-icon-info"></i>
          支持 xlsx、xls 格式文件，默认选择第一个sheet
        </div>
        <el-upload
          class="upload"
          :action="fileUploadUrl + '/api/cube/file/structure'"
          :headers="fileUploadHeaders"
          :before-upload="beforeUpload"
          :on-success="uploadSuccess"
          ref="upload"
        >
          <el-button size="small" type="primary">点击上传</el-button>
        </el-upload>
        <el-form size="mini" label-width="70px" style="padding-top: 30px">
          <div class="tips">
            <i class="el-icon-info"></i>
            物理表名字段：由英文，数字,下划线组成，字母开头
          </div>
          <el-form-item label="物理表名">
            <el-input
              placeholder="请使用英文/数字/下划线"
              v-model="fileForm.tableName"
              :maxlength="30"
              show-word-limit
            ></el-input>
          </el-form-item>
          <el-form-item label="备注表名">
            <el-input v-model="fileForm.tableComment"></el-input>
          </el-form-item>
        </el-form>
        <div style="font-size: 14px; color: #606266; margin-bottom: 20px">
          字段配置
        </div>
        <el-table size="mini" :data="fileForm.tableColumnList">
          <el-table-column label="原始字段">
            <template slot-scope="field">
              <el-input size="mini" v-model="field.row.comment"></el-input>
            </template>
          </el-table-column>
          <el-table-column label="物理字段">
            <template slot-scope="field">
              <el-input size="mini" v-model="field.row.columnName"></el-input>
            </template>
          </el-table-column>
          <el-table-column label="数据类型">
            <template slot-scope="field">
              <el-select size="mini" v-model="field.row.columnType">
                <el-option label="数字" value="NUMBER"></el-option>
                <el-option label="日期" value="DATETIME"></el-option>
                <el-option label="字符串" value="STRING"></el-option>
              </el-select>
            </template>
          </el-table-column>
        </el-table>
        <div class="btns">
          <el-button size="small" @click="onUploadCancel">取消</el-button>
          <el-button size="small" type="primary" @click="onUploadConfirm">
            确定
          </el-button>
        </div>
      </div>
    </el-drawer>
    <!-- 表连接编辑抽屉 -->
    <el-drawer
      title="编辑表关联"
      :visible.sync="showEditJoin"
      direction="rtl"
      :show-close="false"
      class="join-drawer"
      size="680px"
      v-if="nowNode && rightNode"
    >
      <div class="field-panel">
        <div class="panel">
          <div class="title">
            {{ nowNode.target }}
          </div>
          <el-table
            size="mini"
            :data="nowNode.fields"
            height="400px"
            v-loading="!nowNode.fields.length"
          >
            <el-table-column label="展示" width="50" align="center">
              <template slot-scope="field">
                <el-checkbox
                  v-model="field.row.hiddenStatus"
                  :true-label="0"
                  :false-label="1"
                ></el-checkbox>
              </template>
            </el-table-column>
            <el-table-column label="字段名" prop="columnName"></el-table-column>
            <el-table-column
              label="注释"
              prop="columnComment"
            ></el-table-column>
          </el-table>
        </div>
        <div style="width: 20px"></div>
        <div class="panel">
          <div class="title">{{ rightNode.target }}</div>
          <el-table
            size="mini"
            :data="rightNode.fields"
            height="400px"
            v-loading="!rightNode.fields.length"
          >
            <el-table-column label="展示" width="50" align="center">
              <template slot-scope="field">
                <el-checkbox
                  v-model="field.row.hiddenStatus"
                  :true-label="0"
                  :false-label="1"
                ></el-checkbox>
              </template>
            </el-table-column>
            <el-table-column label="字段名" prop="columnName"></el-table-column>
            <el-table-column
              label="注释"
              prop="columnComment"
            ></el-table-column>
          </el-table>
        </div>
      </div>
      <div class="join-title">
        <span style="flex: 1">数据关联</span>
        <el-select v-model="joinForm.joinType" size="mini">
          <el-option label="左关联" value="LEFT_JOIN"></el-option>
          <el-option label="右关联" value="RIGHT_JOIN"></el-option>
          <el-option label="内关联" value="INNER_JOIN"></el-option>
        </el-select>
        <el-button
          size="mini"
          @click="onAddJoinKey"
          type="primary"
          icon="el-icon-plus"
          plain
        >
          添加关联字段
        </el-button>
      </div>
      <div class="join-key" v-for="(key, i) of joinForm.joinKeys" :key="i">
        <el-select
          size="mini"
          v-model="key.sourceKey"
          @change="onJoinKeyChange(key)"
        >
          <el-option
            v-for="(k, j) of nowNode.fields"
            :key="j"
            :label="k.columnName + '【' + k.columnTitle + '】'"
            :value="k.columnName"
          ></el-option>
        </el-select>
        <el-select
          size="mini"
          v-model="key.targetKey"
          @change="onJoinKeyChange(key)"
        >
          <el-option
            v-for="(k, j) of rightNode.fields"
            :key="j"
            :label="k.columnName + '【' + k.columnTitle + '】'"
            :value="k.columnName"
          ></el-option>
        </el-select>
        <i class="el-icon-delete" @click="onDelJoinKey(i)"></i>
      </div>
      <div class="btns">
        <el-button size="small" @click="showEditJoin = false">取消</el-button>
        <el-button size="small" type="primary" @click="onConfirmJoin">
          确定
        </el-button>
      </div>
    </el-drawer>
    <!-- 节点字段选择抽屉 -->
    <el-drawer
      title="字段选择"
      :visible.sync="showFieldsSelect"
      direction="rtl"
      size="500px"
      class="fields-drawer"
      v-if="nowNode"
    >
      <div class="title">表名 : {{ nowNode.name }}</div>
      <el-table size="mini" :data="nowNode.fields">
        <el-table-column label="展示" width="50" align="center">
          <template slot-scope="field">
            <el-checkbox
              v-model="field.row.hiddenStatus"
              :true-label="0"
              :false-label="1"
            ></el-checkbox>
          </template>
        </el-table-column>
        <el-table-column label="物理字段" prop="columnName"> </el-table-column>
        <el-table-column label="数据类型" prop="viewDataType">
        </el-table-column>
        <el-table-column label="注释">
          <template slot-scope="field">
            <el-input size="mini" v-model="field.row.columnComment"></el-input>
          </template>
        </el-table-column>
        <el-table-column label="操作" width="80px" align="center">
          <template slot-scope="field">
            <el-button
              type="danger"
              plain
              size="mini"
              @click="onFieldDel(field.row)"
            >
              删除
            </el-button>
          </template>
        </el-table-column>
      </el-table>
    </el-drawer>
    <!-- 计算字段编辑弹窗 -->
    <el-dialog
      append-to-body
      title="计算字段编辑"
      width="920px"
      :visible.sync="showEditComputedField"
      class="dataset-edit-computed-fields"
      :close-on-click-modal="false"
      :close-on-press-escape="false"
    >
      <div class="name">
        <div class="label">字段展示名</div>
        <el-input
          size="mini"
          v-model="nowComputedField.columnTitle"
          placeholder="请输入字段名称"
        ></el-input>
      </div>
      <div class="panels">
        <div class="main">
          <div class="label">字段表达式</div>
          <b-ace-editor
            v-model="nowComputedField.columnFormula"
            height="240"
            lang="sql"
            wrap
            snippets
            ref="editor"
          ></b-ace-editor>
          <div class="form-item">
            <div class="label">字段类型</div>
            <el-radio-group v-model="nowComputedField.dimensionType">
              <el-radio label="WEIDU">维度</el-radio>
              <el-radio label="ZHIBIAO">指标</el-radio>
            </el-radio-group>
          </div>
          <div class="form-item">
            <div class="label">计算字段数据类型</div>
            <el-radio-group v-model="nowComputedField.oldViewDataType">
              <el-radio label="STRING">字符串</el-radio>
              <el-radio label="DATETIME">日期时间</el-radio>
              <el-radio label="NUMBER">数字</el-radio>
            </el-radio-group>
          </div>
          <div class="form-item">
            <div class="label">展示数据类型</div>
            <el-radio-group v-model="nowComputedField.viewDataType">
              <el-radio label="STRING">字符串</el-radio>
              <el-radio label="DATETIME">日期时间</el-radio>
              <el-radio label="NUMBER">数字</el-radio>
            </el-radio-group>
          </div>
          <div class="form-item">
            <div class="label">字段描述</div>
            <el-input
              type="textarea"
              size="mini"
              v-model="nowComputedField.columnComment"
            ></el-input>
          </div>
        </div>
        <div class="panel" style="margin-right: 10px">
          <div class="label">点击引用字段</div>
          <div class="search-box">
            <el-input
              placeholder="输入以搜索"
              size="mini"
              v-model="computedSearchKey"
              @input="onInputComputedSearchKey"
              clearable
            ></el-input>
          </div>
          <div class="fields">
            <div class="tab" v-show="tobeChooseWeidu.length">维度</div>
            <div
              class="weidu"
              v-for="field of tobeChooseWeidu"
              :key="field.columnTitle"
              @click="onComputedAddField(field)"
              :title="field.columnName"
            >
              {{ field.columnTitle + "【" + field.columnName + "】" }}
            </div>
            <div class="tab" v-show="tobeChooseZhibiao.length">指标</div>
            <div
              class="zhibiao"
              v-for="field of tobeChooseZhibiao"
              :key="field.columnTitle"
              @click="onComputedAddField(field)"
              :title="field.columnName"
            >
              {{ field.columnTitle + "【" + field.columnName + "】" }}
            </div>
          </div>
        </div>
        <div class="panel">
          <div class="label">点击引用函数</div>
          <div class="functions">
            <div
              class="func"
              v-for="(func, i) of functionList"
              :key="i"
              :title="func.description"
              @click="onComputedAddFunc(func)"
            >
              {{ func.method }}
            </div>
          </div>
        </div>
      </div>
      <div class="btns">
        <el-button size="mini" @click="showEditComputedField = false">
          取消
        </el-button>
        <el-button
          type="primary"
          size="mini"
          @click="onEditComputedFieldConfirm"
        >
          确定
        </el-button>
      </div>
    </el-dialog>
    <div class="left-panel" :style="{ flex: `0 0 ${leftPanelWidth}px` }">
      <div class="datasource-info">
        所用数据源：
        <div class="name" v-if="datasource">
          {{ datasource.dsDb + " " + datasource.remark || "" }}
        </div>
      </div>
      <div class="table-type-list">
        <div
          class="type"
          @click="tableTypeIndex = 0"
          :class="{ active: tableTypeIndex == 0 }"
        >
          数据表
        </div>
        <div
          class="type"
          @click="tableTypeIndex = 1"
          :class="{ active: tableTypeIndex == 1 }"
        >
          来自文件
        </div>
        <div style="flex: 1"></div>
        <i
          class="el-icon-upload2"
          v-show="tableTypeIndex == 1"
          @click="showUpload = true"
        ></i>
      </div>
      <el-input
        class="table-search"
        size="mini"
        v-model="tableSearchKey"
        placeholder="搜索数据表"
        clearable
        @input="onSelectTable"
      ></el-input>
      <!-- 数据表列表 -->
      <div class="table-list" v-show="tableTypeIndex == 0">
        <div
          class="item"
          v-for="(table, i) of tableListSelected"
          :key="i"
          draggable
          @dragstart="onTableDragStart(table)"
          :title="table.tableComments + ' ' + table.tableViewName"
        >
          <i class="iconfont icon-table"></i>
          <span class="name">{{
            table.tableComments + " " + table.tableViewName
          }}</span>
          <i class="iconfont icon-info" @click="onSearchFields(table)"></i>
        </div>
      </div>
      <!-- 来自文件的表列表 -->
      <div class="table-list" v-show="tableTypeIndex == 1">
        <div
          class="item"
          v-for="(table, i) of fileTableListSelected"
          :key="i"
          draggable
          @dragstart="onTableDragStart(table)"
          :title="table.tableComments + ' ' + table.tableViewName"
        >
          <i class="iconfont icon-table"></i>
          <span class="name">{{
            table.tableComments + " " + table.tableViewName
          }}</span>
          <i class="iconfont icon-info" @click="onSearchFields(table)"></i>
        </div>
      </div>
      <div class="cube-form">
        <el-form size="mini" label-width="90px">
          <el-form-item label="数据集名称">
            <el-input v-model="cubeForm.cubeName"></el-input>
          </el-form-item>
          <el-form-item label="数据集分类">
            <el-cascader
              v-model="cubeForm.workspaceId"
              :options="categoryList"
              :props="{
                expandTrigger: 'click',
                emitPath: false,
                value: 'id',
                label: 'wsName',
              }"
              :show-all-levels="false"
            ></el-cascader>
          </el-form-item>
          <el-form-item label="数据集描述">
            <el-input type="textarea" v-model="cubeForm.remark"></el-input>
          </el-form-item>
          <el-button round size="small" type="primary" @click="onSubmit">
            保存
          </el-button>
        </el-form>
      </div>
    </div>
    <div class="right-wrapper">
      <div class="left-toggle" @click="onLeftPanelToggle">
        <i class="el-icon el-icon-caret-right" v-if="leftPanelWidth == 0"></i>
        <i class="el-icon el-icon-caret-left" v-else></i>
      </div>
      <!-- 表连接面板 -->
      <div class="top-panel" :style="{ height: topPanelHeight + 'px' }">
        <div
          id="panel"
          :style="{ height: bottomPosition + 'px' }"
          @drop="onInitTableDragEnd"
        >
          <div class="empty" v-show="!nodes.length">拖拽添加一张表</div>
          <div
            v-for="(node, i) of nodes"
            :key="i"
            :class="{ node: 1, unuse: node.unuse }"
            :style="{
              left: node.x + 'px',
              top: node.y + 'px',
              opacity: !node.unuse || showDragProcess ? 1 : 0,
            }"
            @dragenter="onTableDragEnter(node)"
            @dragover="onDragOver"
            @drop="onTableDragEnd"
            :title="node.name"
          >
            <i
              class="iconfont icon-table"
              style="color: #409eff"
              v-if="!node.unuse"
            ></i>
            <div class="name" :class="{ unuse: node.unuse }">
              {{ node.unuse ? "添加表关联" : node.name }}
            </div>
            <el-popover
              placement="bottom-end"
              popper-class="dataset-edit-node-popover"
              width="170"
              trigger="hover"
              v-if="!node.unuse"
            >
              <i class="iconfont icon-point-more" slot="reference"></i>
              <div class="btn" @click="onJoinEditFields(node)">字段选择</div>
              <div
                class="btn"
                @click="onJoinDel(node)"
                v-if="node.chidren.length <= 1"
              >
                删除
              </div>
            </el-popover>
            <div class="line1" v-if="node.sourceId">
              <div class="left"></div>
              <div class="text" v-if="node.unuse"></div>
              <el-tooltip content="编辑关联关系" placement="left" v-else>
                <div
                  class="icon"
                  :class="[node.joinType]"
                  @click="onEditJoin(node)"
                ></div>
              </el-tooltip>
              <div class="right"></div>
            </div>
            <div
              class="line2"
              v-if="node.sourceId"
              :style="countLineStyle(node)"
            ></div>
          </div>
        </div>
      </div>
      <div
        class="top-toggle"
        @click="onTopPanelToggle"
        :style="{ top: topPanelHeight - 9 + 'px' }"
      >
        <i class="el-icon el-icon-caret-top" v-if="topPanelHeight > 240"></i>
        <i class="el-icon el-icon-caret-bottom" v-else></i>
      </div>
      <div
        class="drag-line"
        draggable
        @drag="onLineDrag"
        :style="{ top: topPanelHeight - 2 + 'px' }"
      ></div>
      <div class="main-wrapper">
        <!-- 面板头部 -->
        <div class="main-header">
          <div
            class="tab"
            :class="{ active: mainIndex == 0 }"
            @click="mainIndex = 0"
          >
            数据预览
          </div>
          <div
            class="tab"
            :class="{ active: mainIndex == 1 }"
            @click="mainIndex = 1"
          >
            批量配置
          </div>
          <div style="flex: 1"></div>
          <el-button
            size="mini"
            icon="el-icon-plus"
            @click="onAddComputedField"
          >
            新建计算字段
          </el-button>
          <el-button
            size="mini"
            type="primary"
            icon="el-icon-refresh"
            @click="onPreview"
          >
            刷新预览
          </el-button>
          <el-button
            v-show="this.cubeId"
            size="mini"
            type="success"
            icon="el-icon-share"
            @click="onCreateBigdata"
          >
            创建模板
          </el-button>
        </div>
        <!-- 预览区域 -->
        <div class="data-wrapper" v-show="mainIndex == 0">
          <div class="left-fields">
            <template v-if="tableFieldsWeidu.length">
              <div class="title WEIDU">维度</div>
              <vuedraggable
                v-model="tableFieldsWeidu"
                :animation="150"
                @change="setFieldsSort"
              >
                <div
                  :class="['item', field.hiddenStatus ? 'unuse' : 'weidu']"
                  v-for="field of tableFieldsWeidu"
                  :key="field.columnTitle"
                >
                  <div class="text">
                    {{ field.columnTitle + "【" + field.columnName + " 】" }}
                  </div>
                  <i class="el-icon-close" @click="onFieldDel(field)"></i>
                </div>
              </vuedraggable>
            </template>
            <template v-if="tableFieldsZhibiao.length">
              <div class="title ZHIBIAO">指标</div>
              <vuedraggable
                v-model="tableFieldsZhibiao"
                :animation="150"
                @change="setFieldsSort"
              >
                <div
                  :class="['item', field.hiddenStatus ? 'unuse' : 'zhibiao']"
                  v-for="field of tableFieldsZhibiao"
                  :key="field.columnTitle"
                >
                  <div class="text">
                    {{ field.columnTitle + "【" + field.columnName + " 】" }}
                  </div>
                  <i class="el-icon-close" @click="onFieldDel(field)"></i>
                </div>
              </vuedraggable>
            </template>
          </div>
          <!-- 预览表格 -->
          <el-table
            size="mini"
            border
            :data="previewData"
            :header-cell-style="countPreviewTableHeader"
            height="100%"
          >
            <el-table-column
              v-for="(field, i) of tableFields"
              :key="i"
              :label="field.columnTitle"
              :prop="field.columnTitle"
            ></el-table-column>
          </el-table>
        </div>
        <!-- 字段配置区域 -->
        <div class="field-wrapper" v-show="mainIndex == 1">
          <field-table
            :tableData="tableFieldsWeidu"
            header-color="#468cff"
            type="WEIDU"
            @fieldTypeToggle="fieldTypeToggle"
            @editComputedField="editComputedField"
            @onFieldDel="onFieldDel"
            title="维度"
          ></field-table>
          <field-table
            :tableData="tableFieldsZhibiao"
            header-color="#00c13a"
            type="ZHIBIAO"
            @fieldTypeToggle="fieldTypeToggle"
            @editComputedField="editComputedField"
            @onFieldDel="onFieldDel"
            title="指标"
          ></field-table>
        </div>
      </div>
    </div>
  </div>
</template>
<script>
import {
  listViewsByDataSource,
  getDatasourceDetail,
} from "../../../api/report/report";
import {
  tableFileUpload,
  tableFileList,
  tableFieldList,
  tableFunctionList,
  cubePreview,
  cubeSave,
  cubeDetail,
  cubeFieldList,
  categoryList,
  cubeFieldDel,
} from "../../../api/newbi/dataset";
import { baseUrl } from "../../../config/env";
import website from "../../../config/website";
import { getToken } from "../../../util/auth";
import util from "../../../util/wey";
import fieldTable from "../comps/fieldTable.vue";
import vuedraggable from "vuedraggable";
export default {
  components: { fieldTable, vuedraggable },
  data() {
    return {
      fileUploadUrl: baseUrl,
      fileUploadHeaders: {
        [website.tokenHeader]: getToken(),
      },
      showUpload: false,
      fileForm: {
        file: null,
        tableName: "",
        tableComment: "",
        tableColumnList: [],
        dsId: 0,
      },
      uploadLoading: false,
      leftPanelWidth: 240,
      topPanelHeight: 240,
      cubeId: 0,
      editType: "add",
      tableList: [],
      tableListSelected: [],
      fileTableList: [],
      fileTableListSelected: [],
      datasource: null,
      tableSearchKey: "",
      tableTypeIndex: 0, // 左侧数据表列表类型
      showViewFields: false,
      cubeForm: { cubeName: "", remark: "", workspaceId: "" },
      nodes: [], // 选中的表组成的节点数组
      bottomPosition: 240,
      tree: null, // 节点数组计算出的树结构
      nowTable: null, // 预览时选择的表
      selectedNode: null, // 拖拽时所放置的节点
      showDragProcess: false,
      joinForm: {},
      showEditJoin: false,
      nowNode: null, //编辑连接时的左边源表
      rightNode: null, // 编辑连接时的右边目标表
      editJoinType: "add", // 编辑连接时方式 新增/修改
      showFieldsSelect: false,
      mainIndex: 0,
      tableFieldsWeidu: [], // 自定义字段列表-维度
      tableFieldsZhibiao: [], // 自定义字段列表-指标
      computedFields: [], // 计算字段列表
      functionList: [], // 用于创建计算字段的函数列表
      nowComputedField: {}, // 当前计算字段
      showEditComputedField: false,
      editComputedFieldType: "add",
      previewData: [], // 预览数据
      categoryList: [],
      saveStatus: true,
      tobeChooseWeidu: [],
      tobeChooseZhibiao: [],
      computedSearchKey: "",
    };
  },
  watch: {
    nodes() {
      this.computeField();
    },
    computedFields() {
      this.computeField();
    },
    showUpload(val) {
      if (!val) {
        this.fileForm = {
          ...this.fileForm,
          file: null,
          tableName: "",
          tableComment: "",
          tableColumnList: [],
        };
        this.$refs.upload.clearFiles();
      }
    },
  },
  computed: {
    tableFields() {
      this.previewData = [];
      return this.tableFieldsWeidu
        .concat(this.tableFieldsZhibiao)
        .filter((i) => !i.hiddenStatus);
    },
  },
  async created() {
    categoryList({ current: 1, size: 10000 }).then((res) => {
      let list = res.data.data.records;
      let categoryList = list.filter((i) => !i.parentId);
      list.forEach((i) => {
        if (i.parentId) {
          let parent = categoryList.find((j) => j.id == i.parentId);
          if (!parent) return;
          if (!parent.children) {
            parent.children = [];
          }
          parent.children.push(i);
        }
      });
      this.categoryList = categoryList;
    });
    // 创建时，传入数据源id，获取数据表
    let { sourceid, id, ws } = this.$route.query;
    if (id) {
      this.cubeId = id;
      this.editType = "edit";
      // 获取数据集详情
      let cubeRes = await cubeDetail({ cubeDataId: id });
      if (!cubeRes.data.code == 200) return;
      let {
        cubeName,
        remark,
        linksJoin,
        datasourceId,
        cubeTableNames,
        workspaceId,
      } = cubeRes.data.data;
      cubeTableNames = JSON.parse(cubeTableNames);
      this.getTables(datasourceId);
      this.cubeForm = { cubeName, remark, workspaceId };
      // 获取数据源详情
      getDatasourceDetail({ id: datasourceId }).then((res) => {
        this.datasource = res.data.data;
        this.fileForm.dsId = this.datasource.id;
        // 获取当前数据源类型下计算函数
        tableFunctionList({ dsType: this.datasource.dsTypeSyl }).then((res) => {
          this.functionList = res.data.data;
        });
      });
      let links = JSON.parse(linksJoin);
      // 获取之前的字段配置
      let cubeFieldRes = await cubeFieldList({ cubeDataId: id });
      if (!cubeFieldRes.data.code == 200) return;
      let wholeFields = cubeFieldRes.data.data;
      // 整理节点信息
      let nodes = links.map((i) => ({
        ...i,
        name: i.target,
        chidren: [],
        x: 0,
        y: 0,
      }));
      if (nodes.length) {
        let { source, sourceId } = links[0];
        nodes.unshift({
          chidren: [],
          x: 0,
          y: 0,
          target: source,
          targetId: sourceId,
          sourceId: 0,
          name: source,
        });
      } else {
        let { tableName, tableAliasName } = cubeTableNames[0];
        nodes.unshift({
          chidren: [],
          x: 0,
          y: 0,
          target: tableName,
          targetId: tableAliasName,
          sourceId: 0,
          name: tableName,
        });
      }
      nodes.forEach((node) => {
        node.fields = wholeFields.filter(
          (i) => i.tableAliasName == node.targetId
        );
      });
      this.computedFields = wholeFields.filter(
        (i) => i.columnAttr == "VIRTUAL"
      );
      this.nodes = nodes;
      this.countTree();
    }
    if (sourceid) {
      this.cubeForm.workspaceId = parseInt(ws);
      getDatasourceDetail({ id: sourceid }).then((res) => {
        this.datasource = res.data.data;
        this.fileForm.dsId = this.datasource.id;
        // 获取当前数据源类型下计算函数
        tableFunctionList({ dsType: this.datasource.dsTypeSyl }).then((res) => {
          this.functionList = res.data.data;
        });
      });
      this.getTables(sourceid);
    }
  },
  mounted() {
    document.addEventListener("dragover", function (e) {
      e.preventDefault();
    });
    document.addEventListener("drop", () => {
      this.showDragProcess = false;
    });
  },
  methods: {
    // 过滤计算字段弹窗中待选字段列表
    onInputComputedSearchKey(key) {
      key = key.toUpperCase();
      this.tobeChooseWeidu = this.tableFieldsWeidu.filter((i) => {
        return (
          i.columnName.toUpperCase().includes(key) ||
          i.columnTitle.includes(key)
        );
      });
      this.tobeChooseZhibiao = this.tableFieldsZhibiao.filter((i) => {
        return (
          i.columnName.toUpperCase().includes(key) ||
          i.columnTitle.includes(key)
        );
      });
    },
    onCreateBigdata() {
      let routeUrl = this.$router.resolve({
        path: "/newbi/bigdata/edit?ds=" + this.cubeId,
      });
      window.open(routeUrl.href, "_blank");
    },
    onJoinKeyChange(key) {
      let { sourceKey, targetKey } = key;
      if (!sourceKey || !targetKey) return;
      let left = this.nowNode.fields.find((i) => i.columnName == sourceKey);
      let right = this.rightNode.fields.find((i) => i.columnName == targetKey);
      if (left.viewDataType != right.viewDataType) {
        this.$message({
          type: "warning",
          message: `${sourceKey} 字段和 .${targetKey} 字段数据类型不一致，不能进行关联，请检查`,
        });
      }
    },
    // 提交保存
    onSubmit() {
      if (!this.cubeForm.cubeName) {
        return this.$message({ type: "warning", message: "请填写名称" });
      }
      if (!this.cubeForm.workspaceId) {
        return this.$message({ type: "warning", message: "请选择分类" });
      }
      if (!this.saveStatus) return;
      let data = this.countWholeData();
      if (!data) return;
      console.log(data);
      this.saveStatus = false;
      setTimeout(() => (this.saveStatus = true), 3000);
      cubeSave(data).then((res) => {
        this.$message({ type: "success", message: "保存成功" });
        if (this.editType == "add") {
          this.$router.replace("/newbi/dataset/edit?id=" + res.data.data);
        } else {
          location.reload();
        }
      });
    },
    // 预览数据
    onPreview() {
      let data = this.countWholeData();
      if (!data) return;
      cubePreview(data).then((res) => {
        let { dataList, fieldDict } = res.data.data;
        this.previewData = util.arrReplaceKeys(dataList, fieldDict);
      });
    },
    // 整理预览/提交数据
    countWholeData() {
      let linksJoin = this.nodes
        .filter((i) => i.sourceId && !i.unuse)
        .map((i) => ({
          joinType: i.joinType,
          joinKeys: i.joinKeys,
          source: i.source,
          target: i.target,
          targetId: i.targetId,
          sourceId: i.sourceId,
        }));
      let tableFields = this.tableFieldsWeidu.concat(this.tableFieldsZhibiao);
      let cubeTableNames = this.nodes
        .filter((i) => !i.unuse)
        .map((i) => ({ tableName: i.target, tableAliasName: i.targetId }));
      let columnList = JSON.parse(JSON.stringify(tableFields));
      columnList.forEach((i) => {
        if (this.editType == "add") {
          delete i.id;
        }
        delete i.cubeDataId;
        delete i.createTime;
        delete i.createBy;
        delete i.createUserId;
      });
      let checkedJoins = linksJoin.reduce((r, i) => {
        let { source, target } = i;
        return r.concat(
          i.joinKeys.map((k) => Object.assign({ source, target }, k))
        );
      }, []);
      for (let i = 0; i < checkedJoins.length; i++) {
        let { source, sourceKey, target, targetKey } = checkedJoins[i];
        let left = columnList.find(
          (j) => j.tableName == source && j.columnName == sourceKey
        );
        let right = columnList.find(
          (j) => j.tableName == target && j.columnName == targetKey
        );
        if (left.viewDataType != right.viewDataType) {
          this.$message({
            type: "warning",
            message: `${source}.${sourceKey} 字段和 ${target}.${targetKey} 字段数据类型不一致，不能进行关联，请检查`,
          });
          return false;
        }
      }
      let cubeData = {
        ...this.cubeForm,
        cubeTableNames: JSON.stringify(cubeTableNames),
        datasourceId: this.datasource.id,
        filterJson: "[]",
        tableType: linksJoin.length ? "LOGIC" : "PHYSICS",
        linksJoin: JSON.stringify(linksJoin),
      };
      if (this.editType == "edit") {
        cubeData.id = this.cubeId;
      }
      let data = {
        columnList,
        cubeData,
        datasourceId: this.datasource.id,
        orderByList: [],
      };
      return data;
    },
    // 获取数据源下的表
    async getTables(id) {
      let fileRes = await tableFileList({ dsId: id });
      let fileTables = fileRes.data.data.map((i) => ({
        datasourceId: i.datasourceId,
        tableComments: i.tableComment,
        tableViewName: i.tableName,
        fields: [],
      }));
      let tableRes = await listViewsByDataSource(id);
      let tables = tableRes.data.data.filter(
        (i) => !fileTables.find((j) => j.tableViewName == i.tableViewName)
      );
      this.tableList = tables.map((i) => ({ ...i, fields: [] }));
      this.tableListSelected = [...this.tableList];
      this.fileTableList = fileTables;
      this.fileTableListSelected = [...this.fileTableList];
    },
    // 判断删除某字段
    async onFieldDel(field) {
      if (this.editType == "add") {
        this.onFieldDelConfirm(field);
      } else {
        this.$confirm("确定删除此字段?", "提示", {
          confirmButtonText: "确定",
          cancelButtonText: "取消",
          type: "warning",
        })
          .then(() => {
            if (!field.id) {
              this.onFieldDelConfirm(field);
              return;
            }
            cubeFieldDel({ columnId: field.id }).then((_) => {
              this.$message({ type: "success", message: "删除成功!" });
              this.onFieldDelConfirm(field);
            });
          })
          .catch(() => {});
        // if (await this.countFieldCanDel(field)) {
        //   this.onFieldDelConfirm(field);
        // } else {
        //   this.$message({ type: "error", message: "该字段目前使用中" });
        // }
      }
    },
    // 判断某字段是否可删除
    async countFieldCanDel(field) {
      return true;
    },
    // 确定可以删除某字段时
    onFieldDelConfirm(field) {
      if (field.columnAttr == "NORMAL") {
        let node = this.nodes.find((i) => i.targetId == field.tableAliasName);
        let index = node.fields.findIndex((i) => i == field);
        node.fields.splice(index, 1);
        this.computeField();
      } else {
        let index = this.computedFields.findIndex((i) => i == field);
        this.computedFields.splice(index, 1);
      }
    },
    // 确定保存计算字段时
    onEditComputedFieldConfirm() {
      let { columnTitle, columnFormula } = this.nowComputedField;
      if (!columnTitle || !columnFormula) {
        return this.$message({ message: "请补充完整", type: "warning" });
      }
      if (this.editComputedFieldType == "add") {
        let flag = this.tableFieldsWeidu
          .concat(this.tableFieldsZhibiao)
          .find((i) => i.columnTitle == columnTitle);
        if (flag) {
          return this.$message({ message: "字段展示名重复", type: "warning" });
        }
        this.computedFields.push(this.nowComputedField);
      }
      this.showEditComputedField = false;
    },
    // 编辑计算字段
    editComputedField(field) {
      this.nowComputedField = field;
      this.editComputedFieldType = "edit";
      this.showEditComputedField = true;
      this.onInputComputedSearchKey("");
    },
    // 计算字段拼入函数
    onComputedAddFunc(func) {
      let { column } = this.$refs.editor.editor.getPosition();
      let left = this.nowComputedField.columnFormula.substr(0, column - 1);
      let right = this.nowComputedField.columnFormula.substr(column - 1);
      this.nowComputedField.columnFormula = left + func.method + right;
    },
    // 计算字段拼入物理字段
    onComputedAddField(field) {
      function getCursortPosition(textDom) {
        var cursorPos = 0;
        if (document.selection) {
          // IE Support
          textDom.focus();
          var selectRange = document.selection.createRange();
          selectRange.moveStart("character", -textDom.value.length);
          cursorPos = selectRange.text.length;
        } else if (textDom.selectionStart || textDom.selectionStart == "0") {
          // Firefox support
          cursorPos = textDom.selectionStart;
        }
        return cursorPos;
      }
      let { column } = getCursortPosition(this.$refs.editor);
      let left = this.nowComputedField.columnFormula.substr(0, column - 1);
      let right = this.nowComputedField.columnFormula.substr(column - 1);
      this.nowComputedField.columnFormula =
        left + `[${field.columnTitle}]` + right;
    },
    // 点击新建计算字段时
    onAddComputedField() {
      this.nowComputedField = {
        columnTitle: "",
        columnComment: "",
        columnFormula: "",
        columnAttr: "VIRTUAL",
        hiddenStatus: 0,
        numberDataFormat: "AUTO",
        sortNum: 0,
        viewDataType: "STRING",
        dimensionType: "ZHIBIAO",
        timeDataFormat: "yyyy_MM_ddhh$mm$ss",
        oldViewDataType: "STRING",
      };
      this.editComputedFieldType = "add";
      this.showEditComputedField = true;
      this.onInputComputedSearchKey("");
    },
    // 计算预览表格头部样式
    countPreviewTableHeader({ columnIndex }) {
      let flag = this.tableFields[columnIndex].dimensionType == "WEIDU";
      return { color: flag ? "#468cff" : "#00c13a" };
    },
    // 字段按顺序设置排序
    setFieldsSort() {
      this.tableFieldsWeidu.forEach(
        (item, index) => (item.sortNum = index + 1)
      );
      this.tableFieldsZhibiao.forEach(
        (item, index) => (item.sortNum = index + 1)
      );
    },
    // 字段类型转换
    fieldTypeToggle(index, field) {
      if (field.dimensionType == "WEIDU") {
        field.dimensionType = "ZHIBIAO";
        this.tableFieldsWeidu.splice(index, 1);
        this.tableFieldsZhibiao.push(field);
      } else if (field.dimensionType == "ZHIBIAO") {
        field.dimensionType = "WEIDU";
        this.tableFieldsZhibiao.splice(index, 1);
        this.tableFieldsWeidu.push(field);
      }
      this.setFieldsSort();
    },
    // 计算当前自定义字段列表
    computeField() {
      let newFields = this.nodes
        .filter((i) => !i.unuse)
        .reduce((r, i) => {
          return r.concat(i.fields);
        }, [])
        .concat(this.computedFields)
        .map((i) => {
          i.columnTitle = i.columnTitle || i.columnComment;
          return i;
        });
      let res = [];
      newFields.forEach((i) => {
        if (res.find((j) => j.columnTitle == i.columnTitle)) {
          i.columnTitle += 1;
        }
        res.push(i);
      });
      this.tableFieldsWeidu = res
        .filter((i) => i.dimensionType == "WEIDU")
        .sort((a, b) => a.sortNum - b.sortNum);
      this.tableFieldsZhibiao = res
        .filter((i) => i.dimensionType == "ZHIBIAO")
        .sort((a, b) => a.sortNum - b.sortNum);
      this.setFieldsSort();
    },
    onDelJoinKey(i) {
      if (this.joinForm.joinKeys.length <= 1) return;
      this.joinForm.joinKeys.splice(i, 1);
    },
    onAddJoinKey() {
      this.joinForm.joinKeys.push({ sourceKey: "", targetKey: "" });
    },
    // 查看节点字段
    onJoinEditFields(node) {
      this.nowNode = node;
      this.showFieldsSelect = true;
    },
    // 判断删除连接节点
    async onJoinDel(node) {
      if (this.editType == "add") {
        this.onJoinDelConfirm(node);
      } else {
        // 循环判断该节点下某字段是否使用中
        for (let i = 0; i < node.fields.length; i++) {
          if (!(await this.countFieldCanDel(node.fields[i])))
            return this.$message({
              message:
                node.fields[i].columnTitle + " 字段目前使用中，不允许删除节点",
              type: "error",
            });
        }
        this.onJoinDelConfirm(node);
      }
    },
    onJoinDelConfirm(node) {
      let index = this.nodes.findIndex((i) => i.targetId == node.targetId);
      this.nodes.splice(index, 1);
      let newNodes = this.nodes.filter((i) => !i.unuse);
      newNodes.forEach((i) => (i.chidren = []));
      this.nodes = newNodes;
      this.countTree();
    },
    // 拖拽添加第一个节点时
    async onInitTableDragEnd() {
      if (!this.rightNode) return;
      // 如果所选表没有字段先获取
      if (!this.rightNode.fields.length) {
        let res = await tableFieldList({
          tableName: this.rightNode.target,
          dsId: this.datasource.id,
        });
        this.rightNode.fields = res.data.data;
      }
      if (!this.nodes.length) {
        let { name, fields, targetId, target } = this.rightNode;
        fields.forEach((i) => (i.tableAliasName = targetId));
        this.nodes.push({
          name,
          target,
          fields: JSON.parse(JSON.stringify(fields)),
          targetId,
          sourceId: 0,
          chidren: [],
          x: 0,
          y: 0,
        });
        this.countTree();
      }
    },
    // 点击编辑连接关系时
    onEditJoin(node) {
      this.rightNode = node;
      this.nowNode = this.nodes.find((i) => i.targetId == node.sourceId);
      this.joinForm = node;
      this.editJoinType = "edit";
      this.showEditJoin = true;
    },
    // 拖拽结束，添加连接关系前组织表单
    async onTableDragEnd() {
      // 如果所选表没有字段先获取
      if (!this.rightNode.fields.length) {
        let res = await tableFieldList({
          tableName: this.rightNode.target,
          dsId: this.datasource.id,
        });
        this.rightNode.fields = res.data.data;
      }
      if (!this.selectedNode.unuse) return;
      this.selectedNode = this.nodes.find(
        (i) => i.targetId == this.selectedNode.sourceId
      );
      this.nowNode = this.selectedNode;
      this.joinForm = {
        joinType: "LEFT_JOIN",
        source: this.nowNode.target,
        target: this.rightNode.target,
        joinKeys: [{ sourceKey: "", targetKey: "" }],
      };
      this.editJoinType = "add";
      this.showEditJoin = true;
    },
    // 确认编辑关联关系，分新增和修改两种情况处理
    onConfirmJoin() {
      if (this.joinForm.joinKeys.find((i) => !i.sourceKey || !i.targetKey)) {
        return this.$message({ type: "warning", message: "请补充连接字段" });
      }
      // 添加新关联关系时
      if (this.editJoinType == "add") {
        let newNodes = this.nodes.filter((i) => !i.unuse);
        newNodes = JSON.parse(JSON.stringify(newNodes));
        newNodes.forEach((i) => (i.chidren = []));
        let { targetId, name, fields } = this.rightNode;
        fields.forEach((i) => (i.tableAliasName = targetId));
        newNodes.push({
          ...this.joinForm,
          name,
          targetId,
          fields: JSON.parse(JSON.stringify(fields)),
          sourceId: this.selectedNode.targetId,
          chidren: [],
          x: 0,
          y: 0,
        });
        this.nodes = newNodes;
        this.countTree();
        this.showEditJoin = false;
      } else {
        // 修改关联关系时
        this.showEditJoin = false;
      }
    },
    onDragOver(e) {
      e.preventDefault();
    },
    // 拖拽到想关联的节点时记录下来
    onTableDragEnter(node) {
      this.selectedNode = node;
    },
    // 拖拽开始，根据选择的表生成右边节点
    onTableDragStart(table) {
      this.rightNode = {
        name: table.tableComments || table.tableViewName,
        target: table.tableViewName,
        targetId: util.getRandomStr(5),
        fields: table.fields,
      };
      this.showDragProcess = true;
    },
    // 计算左边连接线
    countLineStyle(item) {
      if (!item.sourceId) return;
      let parent = this.nodes.find((i) => i.targetId == item.sourceId);
      if (parent.y == item.y) {
        return { width: "20px", height: "1px" };
      } else {
        return { width: "1px", height: item.y - parent.y + "px" };
      }
    },
    // 计算节点树形结构
    countTree() {
      this.nodes.forEach((item) => {
        let parent = this.nodes.find((i) => i.targetId == item.sourceId);
        parent && parent.chidren.push(item);
      });
      let unuseNodes = [];
      this.nodes.forEach((i) => {
        let unuseNode = {
          unuse: true,
          x: 0,
          y: 0,
          chidren: [],
          sourceId: i.targetId,
        };
        i.chidren.push(unuseNode);
        unuseNodes.push(unuseNode);
      });
      this.nodes = this.nodes.concat(unuseNodes);
      this.tree = this.nodes.find((i) => !i.sourceId);
      this.countNodePosition(0, 0, this.tree);
      this.bottomPosition = Math.max(
        this.nodes.reduce((r, i) => Math.max(r, i.y), 0) + 50,
        240
      );
    },
    // 计算节点位置
    countNodePosition(xLevel, yLevel, node) {
      if (!node) return;
      node.x = xLevel * 270 + 20;
      node.y = yLevel * 40 + 20;
      let startYLevel = yLevel;
      if (node.chidren.length) {
        node.chidren.forEach((item) => {
          startYLevel =
            this.countNodePosition(xLevel + 1, startYLevel, item) + 1;
        });
        startYLevel--;
      }
      return startYLevel;
    },
    // 文件上传成功后
    uploadSuccess(response, file) {
      if (response.code == 200) {
        this.fileForm.file = file.raw;
        this.fileForm.tableColumnList = response.data;
        this.fileForm.tableComment = file.name.split(".")[0];
        this.fileForm.tableName =
          "file_table_" + util.dateFormat(new Date(), "YYYYmmdd_HHMMSS");
      } else {
        this.$message({ type: "error", message: response.msg || "服务器错误" });
      }
    },
    // excel文件上传校验
    beforeUpload(file) {
      let nameArr = file.name.split(".");
      if (!["xlsx", "xls"].includes(nameArr[nameArr.length - 1])) {
        this.$message({ type: "warning", message: "请检查文件格式" });
        return false;
      } else {
        return true;
      }
    },
    // 数据表字段预览，如果表字段不存在，先请求获取
    onSearchFields(table) {
      this.nowTable = table;
      this.showViewFields = true;
      if (!table.fields.length) {
        tableFieldList({
          tableName: table.tableViewName,
          dsId: this.datasource.id,
        }).then((res) => {
          table.fields = res.data.data;
        });
      }
    },
    // 关键字过滤数据表
    onSelectTable(val) {
      this.tableListSelected = this.tableList.filter(
        (i) => i.tableComments.includes(val) || i.tableViewName.includes(val)
      );
      this.fileTableListSelected = this.fileTableList.filter(
        (i) => i.tableComments.includes(val) || i.tableViewName.includes(val)
      );
    },
    // 文件上传确认提交
    onUploadConfirm() {
      if (!this.fileForm.tableName) {
        return this.$message({ type: "warning", message: "请填写表名" });
      }
      if (!this.fileForm.tableComment) {
        return this.$message({ type: "warning", message: "请填写表注释名" });
      }
      if (!/^[a-zA-Z][_\d\w]*$/g.test(this.fileForm.tableName)) {
        return this.$message({ type: "warning", message: "请检查表名拼写" });
      }
      let data = { ...this.fileForm };
      delete data.tableColumnList;
      let formData = new FormData();
      Object.keys(data).forEach((k) => {
        formData.append(k, data[k]);
      });
      formData.append(
        "tableColumnList",
        JSON.stringify(this.fileForm.tableColumnList)
      );
      this.uploadLoading = true;
      tableFileUpload(formData)
        .then((res) => {
          if (res.data.code == 200) {
            this.$message({ type: "success", message: "上传成功" });
            this.showUpload = false;
            // 更新文件表列表
            tableFileList({ dsId: this.datasource.id }).then((fileRes) => {
              let fileTables = fileRes.data.data.map((i) => ({
                datasourceId: i.datasourceId,
                tableComments: i.tableComment,
                tableViewName: i.tableName,
                fields: [],
              }));
              let newTable = fileTables.find(
                (i) =>
                  !this.fileTableList.find(
                    (j) => j.tableViewName == i.tableViewName
                  )
              );
              if (newTable) {
                this.fileTableList.push(newTable);
                this.fileTableListSelected = this.fileTableList.filter(
                  (i) =>
                    i.tableComments.includes(this.tableSearchKey) ||
                    i.tableViewName.includes(this.tableSearchKey)
                );
              }
            });
          } else {
            this.$message({ type: "error", message: res.data.msg });
          }
        })
        .finally(() => (this.uploadLoading = false));
    },
    // 文件上传取消
    onUploadCancel() {
      this.showUpload = false;
    },
    onLeftPanelToggle() {
      this.leftPanelWidth = this.leftPanelWidth == 0 ? 240 : 0;
    },
    onTopPanelToggle() {
      if (this.topPanelHeight > 240) {
        this.topPanelHeight = 240;
      } else {
        this.topPanelHeight = document
          .getElementsByClassName("right-wrapper")[0]
          .getBoundingClientRect().height;
      }
    },
    onLineDrag(e) {
      this.topPanelHeight += e.layerY;
    },
  },
};
</script>
<style lang="scss">
@import "./edit.scss";
</style>
