shardingsphere-ui-frontend/src/views/rule-config/module/schema.vue (428 lines of code) (raw):
<!--
  - Licensed to the Apache Software Foundation (ASF) under one or more
  - contributor license agreements.  See the NOTICE file distributed with
  - this work for additional information regarding copyright ownership.
  - The ASF licenses this file to You under the Apache License, Version 2.0
  - (the "License"); you may not use this file except in compliance with
  - the License.  You may obtain a copy of the License at
  -
  -     http://www.apache.org/licenses/LICENSE-2.0
  -
  - Unless required by applicable law or agreed to in writing, software
  - distributed under the License is distributed on an "AS IS" BASIS,
  - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  - See the License for the specific language governing permissions and
  - limitations under the License.
  -->
<template>
  <div class="schema">
    <el-row :gutter="10">
      <el-col
        v-for="(item, index) in schemaData"
        :key="index"
        :xs="24"
        :sm="12"
        :md="6"
        :lg="4"
        :xl="3"
      >
        <el-card class="box-card">
          <div slot="header" class="clearfix">
            <span>{{ item.title }}</span>
            <i v-if="schemaData.length > 1" class="el-icon-delete" @click="handleDelete(item.title)"/>
          </div>
          <div v-for="(itm, idex) in item.children" :key="idex" class="coll-item">
            <div :class="'itm icon-' + idex" />
            <div class="txt">{{ itm }}</div>
            <i class="icon-edit" @click="handlerClick(item.title, itm)" />
          </div>
        </el-card>
      </el-col>
    </el-row>
    <el-row>
      <el-button type="primary" icon="el-icon-plus" @click="add" />
    </el-row>
    <el-dialog :visible.sync="showMetadataDialogVisible" :title="type" width="60%" top="3vh">
      <el-row :gutter="20">
        <el-col :span="24">
          <el-input
            :rows="20"
            :placeholder="$t('ruleConfig.form.inputPlaceholder')"
            v-model="textarea"
            type="textarea"
            readonly
            class="show-text"
          />
        </el-col>
      </el-row>
      <span slot="footer" class="dialog-footer">
        <el-button @click="showMetadataDialogVisible = false">{{ $t('btn.cancel') }}</el-button>
      </span>
    </el-dialog>
    <el-dialog :visible.sync="centerDialogVisible" :title="type" width="60%" top="3vh">
      <el-row :gutter="20">
        <el-col :span="24">
          <el-input
            :rows="20"
            :placeholder="$t('ruleConfig.form.inputPlaceholder')"
            v-model="textarea"
            type="textarea"
            class="edit-text"
          />
        </el-col>
      </el-row>
      <span slot="footer" class="dialog-footer">
        <el-button @click="centerDialogVisible = false">{{ $t('btn.cancel') }}</el-button>
        <el-button type="primary" @click="onConfirm">{{ $t('btn.submit') }}</el-button>
      </span>
    </el-dialog>
    <el-dialog
      :visible.sync="addSchemaDialogVisible"
      :title="$t('ruleConfig.schema.title')"
      width="80%"
      top="3vh">
      <el-form ref="form" :model="form" :rules="rules" label-width="170px">
        <el-form-item :label="$t('ruleConfig.schema.name')" prop="name">
          <el-input
            :placeholder="$t('ruleConfig.schemaRules.name')"
            v-model="form.name"
            autocomplete="off"
          />
        </el-form-item>
        <el-form-item :label="$t('ruleConfig.schema.ruleConfig')" prop="ruleConfig">
          <el-input
            :placeholder="$t('ruleConfig.schemaRules.ruleConfig')"
            :rows="8"
            v-model="form.ruleConfig"
            autocomplete="off"
            type="textarea"
            class="edit-text"
          />
        </el-form-item>
        <el-form-item :label="$t('ruleConfig.schema.dataSourceConfig')" prop="dataSourceConfig">
          <el-input
            :placeholder="$t('ruleConfig.schemaRules.dataSourceConfig')"
            :rows="8"
            v-model="form.dataSourceConfig"
            autocomplete="off"
            type="textarea"
            class="edit-text"
          />
        </el-form-item>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button @click="addSchemaDialogVisible = false">{{ $t('btn.cancel') }}</el-button>
        <el-button type="primary" @click="addSchema('form')">{{ $t('btn.submit') }}</el-button>
      </div>
    </el-dialog>
    <el-dialog
      :visible.sync="deleteDialogVisible"
      :title="$t('common.notify.title')"
      width="30%">
      <span>{{ $t('common.notify.confirmDelOperator') }} {{ schemaName }} ?</span>
      <span slot="footer" class="dialog-footer">
        <el-button @click="deleteDialogVisible = false">{{ $t('btn.cancel') }}</el-button>
        <el-button type="primary" @click="deleteSchema()">{{ $t('btn.confirm') }}</el-button>
      </span>
    </el-dialog>
  </div>
</template>
<script>
import yaml from 'js-yaml'
import API from '../api'
export default {
  name: 'Schema',
  data() {
    return {
      treeData: [],
      textarea: ``,
      schemaData: [],
      centerDialogVisible: false,
      type: null,
      sname: '',
      scname: '',
      addSchemaDialogVisible: false,
      deleteDialogVisible: false,
      schemaName: ``,
      rueleConfigTextArea: ``,
      dataSourceConfigTextArea: ``,
      metadataConfigTextArea: ``,
      showMetadataDialogVisible: false,
      form: {
        name: '',
        ruleConfig: '',
        dataSourceConfig: ''
      },
      rules: {
        name: [
          {
            required: true,
            message: this.$t('ruleConfig').schemaRules.name,
            trigger: 'change'
          }
        ],
        ruleConfig: [
          {
            required: true,
            message: this.$t('ruleConfig').schemaRules.ruleConfig,
            trigger: 'change'
          }
        ],
        dataSourceConfig: [
          {
            required: true,
            message: this.$t('ruleConfig').schemaRules.dataSourceConfig,
            trigger: 'change'
          }
        ],
        metadataConfig: [
          {
            required: true,
            message: this.$t('ruleConfig').schemaRules.metadataConfig,
            trigger: 'change'
          }
        ]
      }
    }
  },
  computed: {
    textarea2() {
      const shardingYamlType = new yaml.Type(
        '!SHARDING',
        {
          kind: 'mapping',
          construct(data) {
            return data !== null ? data : {}
          }
        }
      )
      const encryptYamlType = new yaml.Type(
        '!ENCRYPT',
        {
          kind: 'mapping',
          construct(data) {
            return data !== null ? data : {}
          }
        }
      )
      const masterSlaveYamlType = new yaml.Type(
        '!READ_WRITE_SPLITTING',
        {
          kind: 'mapping',
          construct(data) {
            return data !== null ? data : {}
          }
        }
      )
      const shadowYamlType = new yaml.Type(
        '!SHADOW',
        {
          kind: 'mapping',
          construct(data) {
            return data !== null ? data : {}
          }
        }
      )
      const DS_SCHEMA = yaml.Schema.create([shardingYamlType, encryptYamlType, masterSlaveYamlType, shadowYamlType])
      return JSON.stringify(
        yaml.load(this.textarea, { schema: DS_SCHEMA }),
        null,
        '\t'
      )
    }
  },
  created() {
    this.getSchema()
  },
  methods: {
    add() {
      this.addSchemaDialogVisible = true
    },
    handlerClick(parent, child) {
      if (child === 'rule') {
        API.getSchemaRule(parent).then(res => {
          this.renderYaml(parent, child, res)
        })
      } else if (child === 'datasource') {
        API.getSchemaDataSource(parent).then(res => {
          this.renderYaml(parent, child, res)
        })
      } else {
        API.getSchemaMetadata(parent).then(res => {
          this.renderMetadataYaml(parent, child, res)
        })
      }
    },
    renderMetadataYaml(parent, child, res) {
      if (!res.success) return
      const model = res.model
      if (Object.prototype.toString.call(model) === '[object String]') {
        this.textarea = model
      } else {
        this.textarea = JSON.stringify(model, null, '\t')
      }
      this.sname = parent
      this.scname = child
      this.type = `${parent}-${child}`
      this.showMetadataDialogVisible = true
    },
    renderYaml(parent, child, res) {
      if (!res.success) return
      const model = res.model
      if (Object.prototype.toString.call(model) === '[object String]') {
        this.textarea = model
      } else {
        this.textarea = JSON.stringify(model, null, '\t')
      }
      this.sname = parent
      this.scname = child
      this.type = `${parent}-${child}`
      this.centerDialogVisible = true
    },
    getSchema() {
      API.getSchema().then(res => {
        const data = res.model
        const base = ['rule', 'datasource', 'metadata']
        const newData = []
        for (const v of data) {
          newData.push({
            title: v,
            children: base
          })
        }
        this.schemaData = newData
      })
    },
    onConfirm() {
      if (this.scname === 'rule') {
        API.putSchemaRule(this.sname, { ruleConfig: this.textarea }).then(
          res => {
            this._onConfirm(res)
          }
        )
      } else {
        API.putSchemaDataSource(this.sname, {
          dataSourceConfig: this.textarea
        }).then(res => {
          this._onConfirm(res)
        })
      }
    },
    _onConfirm(res, type) {
      if (res.success) {
        this.$notify({
          title: this.$t('common').notify.title,
          message: this.$t('common').notify.updateCompletedMessage,
          type: 'success'
        })
        this.centerDialogVisible = false
        if (type === 'ADD_SCHEMA') {
          this.addSchemaDialogVisible = false
          this.getSchema()
        }
      } else {
        this.$notify({
          title: this.$t('common').notify.title,
          message: this.$t('common').notify.updateFaildMessage,
          type: 'error'
        })
      }
    },
    addSchema(form) {
      this.$refs[form].validate(valid => {
        if (valid) {
          API.addSchema({
            name: this.form.name,
            ruleConfiguration: this.form.ruleConfig,
            dataSourceConfiguration: this.form.dataSourceConfig
          }).then(res => {
            this._onConfirm(res, 'ADD_SCHEMA')
          })
        } else {
          console.log('error submit!!')
          return false
        }
      })
    },
    handleDelete(schemaName) {
      this.deleteDialogVisible = true
      this.schemaName = schemaName
    },
    deleteSchema() {
      API.deleteSchema(this.schemaName).then(res => {
        this.$notify({
          title: this.$t('common').notify.title,
          message: this.$t('common').notify.delSucMessage,
          type: 'success'
        })
        this.deleteDialogVisible = false
        this.schemaName = ''
        this.getSchema()
      })
    }
  }
}
</script>
<style lang='scss'>
.schema {
  margin-top: 20px;
  .coll-item {
    height: 16px;
    line-height: 16px;
    width: 100%;
    float: left;
    margin-bottom: 22px;
    .txt {
      color: rgb(51, 51, 51);
      font-size: 14px;
      padding-left: 10px;
      float: left;
      margin-right: 10px;
    }
    .itm {
      float: left;
      width: 16px;
      height: 16px;
    }
    .icon-0 {
      background: url('../../../assets/img/rules.png') no-repeat left center;
    }
    .icon-1 {
      background: url('../../../assets/img/data-source.png') no-repeat left
        center;
    }
    .icon-2 {
      background: url('../../../assets/img/meta-data.png') no-repeat left
        center;
    }
    .edit-btn {
      float: right;
    }
  }
  .el-row {
    margin-bottom: 20px;
  }
  .el-collapse-item__header {
    font-size: 16px;
  }
  .edit-text {
    margin-top: 5px;
    textarea {
      background: #fffffb;
    }
  }
  .show-text {
    margin-top: 5px;
    textarea {
      background: rgb(246, 246, 246);
    }
  }
  .icon-edit {
    background: url('../../../assets/img/edit.png') no-repeat left center;
    width: 16px;
    height: 16px;
    display: inline-block;
    float: right;
    cursor: pointer;
  }
  .el-dialog__body {
    padding: 10px 20px;
  }
  .el-input {
    width: 30%;
  }
  .el-input__inner {
    height: 35px;
    line-height: 35px;
  }
  .el-icon-delete {
    color: #F56C6C;
    float: right;
    cursor: pointer;
  }
}
</style>