<template>
  <div>
    <el-row>
      <el-col :span="8">
        <el-button
          type="text"
          @click="addValidata"
          size="medium"
          icon="el-icon-circle-plus-outline"
          >添加校验规则</el-button
        >
      </el-col>
      <el-col :offset="2" :span="10" v-if="isAddValidata">
        <el-tooltip effect="light">
          <div slot="content">
            <h3>校验规则：</h3>
            <div style="margin-top: 15px; margin-bottom: 25px">
              <span style="color: red">*正则表达式：</span>
              <span
                >按照Object格式来编写校验表达式,两个参数，第一个参数为正则表达式，第二个参数为校验未通过时的提示信息</span
              >
              <br />
              <span style="color: red">例如：</span>
              <span
                >"{regex:{exp:'^[0-9]+$',message:'校验失败，请输入数字。'}}"</span
              >
              <br />
              <span style="color: red">注意：</span>
              <span>字符串格式的表达式以英文逗号‘,’来分割参数</span>
              <div>
                <span style="color: red">后端校验：</span
                >两个参数，第一个参数为后端url地址，第二个参数为校验未通过时的提示信息,两个参数以英文逗号隔开
                <div>
                  <span style="color: red">例如：</span
                  >${portal}/sys/test/v1/user/validIsAccountExist?param=,后端校验失败
                </div>
                <div>
                  <span style="color: red">注意：</span
                  >接口是post请求，参数在url中使用param接收，返回格式{valid:
                  false/true}
                </div>
              </div>
            </div>
          </div>
          <span><i class="property-tip icon-question" /> 规则说明</span>
        </el-tooltip>
      </el-col>
    </el-row>
    <el-form data-vv-scope="validateRulesForm" v-form>
      <draggable
        tag="ul"
        class="column-ul"
        :list="data.options.validateList"
        v-bind="{
          group: {name: 'options'},
          ghostClass: 'ghost',
          handle: '.drag-item'
        }"
        @end="end"
        handle=".drag-item"
      >
        <li v-for="(item, index) in data.options.validateList" :key="index">
          <i class="drag-item icon-drag1"></i>
          <div class="validate-rule__container">
            <ht-select
              v-model="item.key"
              :options="validateList"
              :props="{key: 'key', value: 'name'}"
              placeholder="请选择"
              validate="required"
              name="validatorSelect"
              :popper-class="validateList.length ? 'has-options':''"
              filterable
              @visible-change="visibleChangeValidate"
              @change="changeValidate(item)"
            ></ht-select>
            <el-button
              class="el-icon-edit"
              @click="methodClick(index)"
              v-if="item.inputType == 'button'"
              >编写方法</el-button
            >
            <div
              class="two-input__container"
              v-if="item.inputType == 'twoInput'"
            >
              <ht-input
                @input="appendValidate(item)"
                name="validatorValueStart"
                v-model="item.start"
                validate="required"
              />
              <ht-input
                @input="appendValidate(item)"
                name="validatorValueEnd"
                v-model="item.end"
                validate="required"
              />
            </div>
            <ht-input
              @input="appendValidate(item)"
              name="validatorValue"
              v-model="item.value"
              validate="required"
              v-if="item.inputType == 'input' && item.key !== 'decimal'"
            />
            <ht-select
              :validate="item.key == 'row_unique' ? '' : 'required'"
              v-model="item.value"
              :options="boData"
              :multiple="item.key == 'row_unique' ? true : false"
              :props="{key: 'name', value: 'desc'}"
              @change="appendValidate(item)"
              name="validatorSelectInput"
              v-if="item.inputType == 'select' || item.key == 'row_unique'"
            >
              <el-tooltip
                content="多个字段组合进行唯一校验时，字段名用逗号分隔。不填则只校验当前字段"
              >
                <i
                  style="margin-right: -10px"
                  class="property-tip icon-question"
                />
              </el-tooltip>
              <template slot-scope="{options, propKey, propValue}">
                <el-option-group
                  v-for="group in options"
                  :key="group.name"
                  :label="group.desc"
                >
                  <el-option
                    v-for="obj in filterBoAttr(group.children)"
                    :key="'data.' + group.path + '.' + obj[propKey]"
                    :label="obj[propValue]"
                    :value="'data.' + group.path + '.' + obj[propKey]"
                  ></el-option>
                </el-option-group>
              </template>
            </ht-select>
          </div>
          <el-button
            v-if="index == requiredIndex"
            circle
            disabled
            plain
            class="el-icon-close"
            size="mini"
          ></el-button>
          <el-button
            v-else
            @click="removeValidata(index)"
            circle
            plain
            class="el-icon-close"
            size="mini"
          ></el-button>
        </li>
      </draggable>
    </el-form>

    <el-dialog
      title="方法校验"
      :visible.sync="dialogMethodVisible"
      v-if="dialogMethodVisible"
      append-to-body
      class="urgent-text"
      :close-on-click-modal="false"
    >
      <div style="margin-top: 15px; margin-bottom: 25px">
        <code>
          <span style="color: red; margin-left: 15px">
            编辑的脚本内容会直接填充到function(value,data,index,sunIndex){...}里面；value
            是当前控件输入值 data是表单数据对象 index是子表下标 sunIndex是孙表下标
          </span>
          <br />
          <span style="color: red; margin-left: 15px"
            >如需使用到表单BO数据例：data.实体表名.实体表字段(可以直接选择表单变量)</span
          >
          <br />
          <span style="color: red; margin-left: 15px"
            >返回true 校验通过 false 校验不通过</span
          >
          <br />
          <span style="color: red; margin-left: 15px">
            返回值也可以是一个对象 {valid:true} 或者 {valid:false,data:{message:
            i18n.t("login.login") }} 使用国际化的方式
          </span>
          <br />
          <span style="color: red; margin-left: 15px">
            返回值也可以是一个对象 {valid:false,data:{message:
            "身份证号和出生日期不一致，请重新填写" }}
          </span>
          <br />
          <span style="color: red; margin-left: 15px">
            例如： if(data.jasonBo.fieldName == value ){ return true; }else{
            return false;}
          </span>
          <br />
          <span style="color: red; margin-left: 15px">
            子表示例： if(data.jasonBo.tableName[index].fieldName
            == value ){ return true; }else{ return false;}
          </span>
          <br />
          <span style="color: red; margin-left: 15px">
            孙表示例：
            if(data.jasonBo.tableName[index].sunTableName[sunIndex].fieldName ==
            value ){ return true; }else{ return false;}
          </span>
        </code>
      </div>
      <ht-form-item label="表单变量" label-width>
        <ht-select
          clearable
          placeholder="选择表单变量"
          v-model="diyScript"
          :options="boData"
          :props="{key: 'name', value: 'desc'}"
          @change="diyScriptChange"
        >
          <template slot-scope="{options}">
            <el-option-group
              v-for="(group, idenx) in options"
              :key="idenx"
              :label="group.desc"
            >
              <el-option
                v-for="item in group.children"
                :key="item.id"
                :label="item.desc"
                :value="'data.' + item.path + '.' + item.name"
              ></el-option>
            </el-option-group>
          </template>
        </ht-select>
      </ht-form-item>
      <div style="width: 100%; height: 100%">
        <codemirror
          ref="scriptText"
          v-model="toolmethod"
          :options="cmOptions"
          class="code"
          style="width: 99%; height: 100%"
        ></codemirror>
      </div>
      <div slot="footer" class="dialog-footer">
        <el-button @click="dialogMethodVisible = false">取 消</el-button>
        <el-button type="primary" @click="methodOk()">确 定</el-button>
      </div>
    </el-dialog>
  </div>
</template>
<script>
import Draggable from 'vuedraggable'
import {Base64} from 'js-base64'
import utils from '@/hotent-ui-util.js'
import {validateRules} from '@/api/controlsConfig.js'

export default {
  name: 'layout-property',
  components: {Draggable},
  props: {
    isLinkage: {
      type: Boolean,
      default: false
    },
    data: {
      type: Object
    },
    boData: {
      type: Array
    },
    fieldIndexData: {
      type: Object
    }
  },
  data() {
    return {
      validateRules,
      isAddValidata: true,
      dialogMethodVisible: false,
      methodIndex: 0,
      diyScript: '',
      isFilter: false,
      specialValidate: ['confirmed', 'isAfter', 'isBefore', 'isStart', 'isEnd'],
      toolmethod: '',
      cmOptions: {
        value: '',
        mode: 'javascript',
        readOnly: false,
        tabSize: 2,
        theme: 'base16-light',
        lineNumbers: true,
        line: true
      },
      requiredIndex: -1
    }
  },
  computed: {
    validateList: function() {
      var rule = this.data.options.validateType
      if (!rule) {
        return []
      }
      //判断是否只有一个必填校验规并且选择了必填校验
      if (rule == 'required' && this.data.options.validate == "'required'") {
        return this.data.options.validateList
      }
      const filterRule = rule.split('|')
      return this.validateRules.filter(val => {
        var res = true
        if (filterRule.indexOf(val.key) != -1) {
          if (this.isFilter) {
            const showValidate = this.data.options.validateList.find(function(
              obj
            ) {
              return obj.key == val.key
            })
            if (showValidate != undefined) {
              res = false
            }
          }
        } else {
          res = false
        }
        // 如果是联动中添加校验规则，那添加校验规则时不能选择需要关联其他字段的规则。
        if (this.isLinkage) {
          if (this.specialValidate.indexOf(val.key) > -1) {
            res = false
          }
        }
        // 如果是多行文本富文本 只展示必填
        if (
          this.data.ctrlType === 'textarea' &&
          this.data.options.isEditor &&
          val.key != 'required'
        ) {
          res = false
        }
        return res
      })
    }
  },
  created() {
    this.$validator = this.$root.$validator
  },
  watch: {
    fieldIndexData: function(newVal) {
      if (newVal.fieldName) {
        this.data.options.validateList.forEach((v, i) => {
          if (v.key == 'required') {
            this.data.options.validateList.splice(i, 1)
          }
        })
        let k = {}
        k.key = 'required'
        k.name = '必填'
        this.data.options.validateList.push(k)
        this.changeValidate(k)
      }
      this.requiredIndex = -1
      for (let i = 0; i < this.data.options.validateList.length; i++) {
        if (
          this.data.options.validateList[i].key == 'required' &&
          newVal.fieldName
        ) {
          this.requiredIndex = i
          return
        } else if (this.data.options.validateList[i].key == 'required') {
          this.data.options.validateList.splice(i, 1)
        }
      }
    },
    'data.boAttrId': function(newVal) {
      if (newVal) {
        this.isFilter = false
      }
    }
  },
  methods: {
    async diyScriptChange(myValue) {
      const myField = this.$refs.scriptText
      var value = myValue
      if (myField.selectionStart || myField.selectionStart === 0) {
        var startPos = myField.selectionStart
        var endPos = myField.selectionEnd
        this.toolmethod =
          myField.value.substring(0, startPos) +
          value +
          myField.value.substring(endPos, myField.value.length)
        await this.$nextTick() // 这句是重点, 圈起来
        myField.focus()
        myField.setSelectionRange(endPos + value.length, endPos + value.length)
      } else {
        this.toolmethod += value
      }
    },
    methodOk() {
      let newToolMethod = Base64.encode(this.toolmethod)
      if(this.data.parentNodeType == 'sub'){
        newToolMethod = `${newToolMethod}&index=' + $index +'`
      }else if(this.data.parentNodeType == 'sun'){
        newToolMethod = `${newToolMethod}&index=' + $index + '&sunIndex=' + sunIndex +'`
      }
      this.data.options.validateList[this.methodIndex].value = newToolMethod
      this.data.options.validate = this.newMergeValidate(
        this.data.options.validateList
      )
      this.dialogMethodVisible = false
    },
    methodClick(index) {
      this.methodIndex = index
      this.dialogMethodVisible = true
      if (this.data.options.validateList[index].value) {
      const validateData =  this.data.options.validateList[index].value.split('&')

        this.toolmethod = Base64.decode(
          validateData[0]
        )
      } else {
        this.toolmethod = ''
      }
    },
    handleGridColumnRemove(index) {
      this.data.options.validateList.splice(index, 1)
    },
    filterBoAttr(obj) {
      return obj.filter(val => {
        return val.name != this.data.target
      })
    },
    end(evt) {
      this.isFilter = false
      let validateList = this.data.options.validateList
      this.data.options.validate = this.newMergeValidate(validateList)
    },
    appendValidate(item) {
      let validateList = this.data.options.validateList
      this.data.options.validate = this.newMergeValidate(validateList)
      
    },
    visibleChangeValidate() {
      this.isFilter = true
    },
    changeValidate(item) {
      const showValidate = this.validateRules.find(function(obj) {
        return obj.key == item.key
      })
      item.inputType = showValidate.inputType
      item.name = showValidate.name
      item.isInput = showValidate.isInput
      item.type = showValidate.type
      if (this.validateList.length != 1) {
        delete item.value
      }
      if (!item.isInput) {
        let validateList = this.data.options.validateList
        this.data.options.validate = this.newMergeValidate(validateList)
      }
    },

    addValidata() {
      this.isFilter = true

      utils
        .validateForm(this, 'validateRulesForm')
        .then(result => {
          if (!result) {
            return
          }
          if (!this.data.fieldPath) {
            this.$validator.validateAll('field')
            this.$message.warning('请选择需要修改的组件或绑定属性')
            return
          }
          this.isAddValidata = true
          //判断是否只有一个必填校验规
          if (
            this.data.options.validateType == 'required' &&
            this.data.options.validateList.length == 1
          ) {
            this.$message.warning('当前选择的控件只有一个必填校验')
            return
          }
          if (this.data.options.validateList == undefined) {
            this.data.options.validateList = []
          } else if (this.data.options.validateList.length > 0) {
            const validata = this.data.options.validateList[
              this.data.options.validateList.length - 1
            ]
            if (
              validata.inputType == 'button' &&
              (validata.value == undefined || validata.value.length == 0)
            ) {
              this.$message.warning('请输入js方法校验')
              return
            }
          }

          this.$nextTick(() => {
            setTimeout(() => {
              this.data.options.validateList.push({key: '', name: ''})
            })
          })
        })
        .catch(() => {})
    },
    getValidateFormResult() {
      return utils.validateForm(this, 'validateRulesForm')
    },
    removeValidata(index) {
      this.isFilter = false
      this.data.options.validateList.splice(index, 1)
      this.data.options.validate = this.newMergeValidate(
        this.data.options.validateList
      )
    },
    getValidate(item) {
      const {key, value, inputType, isInput, name} = item
      let validate = null
      if (isInput) {
        if (inputType == 'twoInput') {
          return `between:${item.start},${item.end}`
        } else if (inputType == 'select') {
          if (
            name == '相同的值' ||
            name == '日期不晚于' ||
            name == '日期晚于' ||
            name == '日期早于' ||
            name == '日期不早于'
          ) {
            if (value.split('.').length == 4) {
              //子表
              validate = `item.${item.value.split('.')[3]}`
            } else if (value.split('.').length == 5) {
              //孙表
              validate = `item.${item.value.split('.')[4]}`
            } else {
              validate = value
            }
          }
        }
        if (validate === null) {
          validate = value
        }
      }
      if (validate !== null) {
        // 如果是正则表达式，将表达式内容做base64编码
        if (key == 'regex') {
          const regexBase64 = Base64.encode(validate)
          validate = `${key}:${regexBase64}`
        } else {
          validate = `${key}:${validate}`
        }
      } else {
        validate = key
      }
      return validate
    },
    newMergeValidate(validateList) {
      var ref = false
      var validateObj1 = ''
      var validateObj2 = []
      const me_ = this
      validateList.forEach(obj => {
        const validate = me_.getValidate(obj)
        validateObj2.push(validate)
        const key = utils.stringSplit(validate, ':')[0]
        const value = utils.stringSplit(validate, ':')[1]
        var splitVar = ''
        var op = ''
        if (validateObj1 != '') {
          op = '|'
          splitVar = '+'
        }
        if (me_.specialValidate.indexOf(key) > -1) {
          validateObj1 =
            validateObj1 + splitVar + "'" + op + key + ":'+" + value
          ref = true
        } else {
          validateObj1 = validateObj1 + splitVar + "'" + op + validate + "'"
        }
      })
      var validateStr = ''
      if (ref) {
        validateStr = validateObj1
      } else {
        if (validateObj2.length == 0) {
          validateStr = ''
        } else {
          validateStr = "'" + validateObj2.join('|') + "'"
        }
      }
      return validateStr
    }
  }
}
</script>
<style lang="scss" scoped>
@import '@/assets/css/form-editor.scss';

.column-ul {
  list-style: none;
  padding: 0;
  margin: 4px -15px 0 0;

  li {
    display: flex;
    align-items: flex-start;
    flex-wrap: wrap;
    margin-bottom: 12px;
    background: #f9f9f9;
    padding: 8px 8px 3px 8px;
  }

  .icon-drag1 {
    font-weight: bold;
    font-size: 18px;
    cursor: move;
    margin-top: 10px;
    margin-right: 8px;
    color: #409eff;
  }
}

.el-icon-close {
  margin-left: 10px;
}

.ghost {
  background: #f56c6c;
  border: 2px solid #f56c6c;
  outline-width: 0;
  height: 3px;
  box-sizing: border-box;
  font-size: 0;
  content: '';
  overflow: hidden;
  padding: 0;
}

.validate-rule__container {
  width: calc(100% - 70px);

  .inputs {
    width: 100%;
    margin-bottom: 5px;
  }

  .two-input__container {
    display: flex;
    justify-content: space-between;

    .inputs {
      width: 48%;
    }
  }
}

</style>
