<template>
  <div
    v-show="permission_sub != 'n'"
    :class="[
      'inputs',
      formInputsDisplay == 'block'
        ? 'ht-form-inputs__block'
        : 'ht-form-inputs__inline',
      disabled ? 'is-disabled' : '',
      isMobile ? 'mobile-wangEditor-container' : '',
    ]"
    :style="styleEditor"
  >
    <input
      v-if="inputWriteable && !hasTextValue"
      v-model="value"
      v-validate="validateRules"
      :name="inputName"
      :data-vv-as="name"
      type="hidden"
    />
    <div
      ref="editor_parent"
      v-show="inputWriteable && !hasTextValue && type !== 'property-text'"
    >
      <div :id="editorId" ref="editorElem" class="ht-editor"></div>
    </div>

    <div
      v-if="inputWriteable && !hasTextValue && type !== 'property-text'"
      style="display: none"
    ></div>
    <div v-else>
      <div
        v-if="hasHyperlink()"
        :style="showBorder && !isMobile ? textDivStyle : {}"
        :class="{ 'empty-div': type === 'property-text' && !hasTextValue }"
        class="wangEditor-table-style hyperlink_content"
        @click="openHyperlink"
        v-html="inputVal"
      ></div>
      <div
        v-else
        :style="showBorder && !isMobile ? textDivStyle : {}"
        :class="{ 'empty-div': type === 'property-text' && !hasTextValue }"
        class="wangEditor-table-style"
        v-html="inputVal"
      ></div>
    </div>

    <ht-field-tail
      :field-name="inputName"
      :readonly="false"
      :hyperlink="hyperlink"
    />
    <!-- 超链接弹框 -->
    <ht-template-iframe-dialog ref="dialogIframe"></ht-template-iframe-dialog>
  </div>
</template>
<script>
  import Vue from 'vue'
  import utils from '@/utils.js'
  import permission from '@/mixins/permission.js'
  import inputName from '@/mixins/inputName.js'
  import form from '@/mixins/form.js'
  import { encode, decode } from '@/util/base64'
  import mobileMode from '@/mixins/mobileMode.js'
  import hyperlinkHandler from '@/mixins/hyperlinkHandler.js'
  import _ from 'lodash'
  export default {
    name: 'HtEditor',
    mixins: [inputName, permission, form, mobileMode, hyperlinkHandler],
    props: {
      value: String,
      textValue: String,
      showBorder: {
        type: Boolean,
        default: true,
      },
      borderColor: {
        type: String,
        default: '#ccc',
      },
      borderWidth: {
        type: Number,
        default: 1,
      },
      // 文本最大长度限制，大于0才生效
      maxLength: {
        type: Number,
        default: 0,
      },
      disabled: {
        type: Boolean,
        default: false,
      },
      isValidate: {
        type: Boolean,
        default: false,
      },
      config: {
        type: Object,
        default: () => {
          return {}
        },
      },
      encode: {
        type: Boolean,
        default: false,
      },
      noToolbar: {
        type: String,
        default: 'false',
      },
      initialFrameWidth: {
        type: String,
        default: '',
      },
      type: {
        type: String,
        default: '',
      },
      initialFrameHeight: {
        type: String,
        default: '300',
      },
      customMenus: {
        type: Array,
        default: () => {
          return [
            'head',
            'bold',
            'fontSize',
            'fontName',
            'italic',
            'underline',
            'strikeThrough',
            'indent',
            'lineHeight',
            'foreColor',
            'backColor',
            'link',
            'list',
            'todo',
            'justify',
            'quote',
            'emoticon',
            'image',
            'video',
            'table',
            'code',
            'splitLine',
            'undo',
            'redo',
          ]
        },
      },
    },
    data() {
      return {
        editor: null,
        editorId: `editor_${utils.getName()}`,
        compiledTextValue: null,
        formInstance: null,
      }
    },
    computed: {
      hasTextValue() {
        return this.textValue != null && this.textValue != undefined
      },
      inputVal() {
        return this.hasTextValue
          ? this.compiledTextValue
          : this.getValue(this.value)
      },
      textDivStyle() {
        let borderColor = this.isBase64(this.borderColor)
          ? decode(this.borderColor)
          : this.borderColor
        return {
          border: `${this.borderWidth}px solid ${borderColor}`,
          padding: '0 10px',
        }
      },
      styleEditor() {
        let style = {}
        if (this.initialFrameWidth && !this.isMobile) {
          style = { width: this.initialFrameWidth + 'px' }
        } else if (this.isMobile) {
          style = { width: 'calc(100vw - 32px);' }
        }
        return style
      },
      validateRules() {
        return this.disabled && !this.isValidate ? '' : this.inputValidate
      },
    },
    watch: {
      value: function (newVal) {
        if (this.editor && newVal) {
          let text = this.editor.txt.html()
          if (text !== newVal && encode(text) !== newVal) {
            this.editor.txt.html(this.getValue(newVal))
          }
        }
      },
      textValue: {
        handler: function (newVal) {
          this.loadCompiledTextValue(newVal)
        },
        immediate: true,
      },
      permission_sub: function (newVal, oldVal) {
        if (newVal !== oldVal) {
          // 现在是无权限，需要销毁
          newVal == 'n' && this.destroyEditor()
        }
      },
      inputWriteable: function (newVal, oldVal) {
        if (newVal !== oldVal) {
          // 可编辑，渲染
          if (newVal && !this.hasTextValue) {
            this.initEditor()
          }
          // 不可编辑，销毁
          if (!newVal || this.hasTextValue) {
            this.destroyEditor()
          }
        }
      },
      formInstance: function (newVal, oldVal) {
        if (!oldVal && newVal && newVal.$on) {
          newVal.$off('updateData', this.loadCompiledTextValue)
          newVal.$on('updateData', this.loadCompiledTextValue)
        }
      },
    },
    mounted() {
      this.initEditor()
      if (this.isMobile) {
        this.addMobileTopLabelClass()
      }
    },
    beforeDestroy() {
      this.formInstance &&
        this.formInstance.$off &&
        this.formInstance.$off('updateData', this.loadCompiledTextValue)
      this.destroyEditor()
    },
    methods: {
      isBase64(vals) {
        let val = _.cloneDeep(vals)
        if (!val || typeof val !== 'string' || val.trim() === '') {
          return false
        }
        const base64BasicRegex = /^[A-Za-z0-9+/=_-]*$/
        if (!base64BasicRegex.test(val)) {
          return false
        }
        const cleanVal = val.replace(/=+$/, '')
        if (val.length % 4 !== 0) {
          if (![0, 2, 3].includes(cleanVal.length % 4)) {
            return false
          }
        }
        try {
          let decodedVal = val
          if (val.includes('-') || val.includes('_')) {
            decodedVal = decodedVal.replace(/-/g, '+').replace(/_/g, '/')
          }
          const decoded = decodeURIComponent(escape(window.atob(decodedVal)))
          const reEncoded = window
            .btoa(unescape(encodeURIComponent(decoded)))
            .replace(/=+$/, '')
          const valWithoutPadding = val.replace(/=+$/, '')
          return valWithoutPadding === reEncoded
        } catch (e) {
          return false
        }
      },
      isHtml(input) {
        return /<[a-z]+\d?(\s+[\w-]+=("[^"]*"|'[^']*'))*\s*\/?>|&#?\w+;/i.test(
          input
        )
      },
      destroyEditor() {
        if (this.editor) {
          this.editor.destroy()
          this.editor = null
        }
      },
      initEditor() {
        if (!this.$WangEditor) {
          throw '要使用富文本组件，必须引入wangeditor，并安装插件hotent-ui/packages/plugins/wangeditor'
        }
        if (this.editor || !this.inputWriteable || this.hasTextValue) {
          return
        }
        const that = this
        this.$nextTick(() => {
          if (!this.inputWriteable) {
            return
          }
          this.editor = new this.$WangEditor(this.$refs.editorElem)

          var lengthDisplay = null
          if (that.maxLength > 0) {
            // 创建长度显示元素
            lengthDisplay = document.createElement('div')
            lengthDisplay.style.position = 'absolute'
            lengthDisplay.style.right = '10px'
            lengthDisplay.style.bottom = '10px'
            lengthDisplay.style.fontSize = '12px'
            lengthDisplay.style.color = '#999'
            this.$refs.editor_parent.appendChild(lengthDisplay)
          }

          // 更新长度显示
          const updateLengthDisplay = () => {
            if (that.maxLength <= 0 || !lengthDisplay) {
              return
            }
            const html = that.editor.txt.html()
            const text =
              new DOMParser().parseFromString(html, 'text/html').body
                .textContent || ''
            const currentLength = text.length
            lengthDisplay.textContent = `${currentLength}/${that.maxLength}`
          }

          Object.assign(
            this.editor.config,
            this.$WangEditorConfig,
            this.config,
            {
              onchange() {
                let html = that.editor.txt.html()
                if (that.maxLength > 0) {
                  const text =
                    new DOMParser().parseFromString(html, 'text/html').body
                      .textContent || ''
                  if (text.length > that.maxLength) {
                    // 截断纯文本
                    const truncatedText = text.slice(0, that.maxLength)
                    // 创建一个新的临时元素来存放截断后的文本
                    const tempDiv = document.createElement('div')
                    tempDiv.textContent = truncatedText
                    // 获取截断后的 HTML 内容
                    const truncatedHtml = tempDiv.innerHTML
                    that.editor.txt.html(truncatedHtml) // 更新编辑器内容
                    html = truncatedHtml
                  }
                }
                that.$emit('change', that.setValue(html))
                that.$emit('input', that.setValue(html))
                updateLengthDisplay() // 更新长度显示
              },
              onblur(newHtml) {
                that.$emit('onblur', that.setValue(newHtml))
                updateLengthDisplay() // 更新长度显示
              },
              onfocus(newHtml) {
                that.$emit('onfocus', that.setValue(newHtml))
                updateLengthDisplay() // 更新长度显示
              },
            }
          )
          let menus = [...this.customMenus]
          if (eval(this.noToolbar)) {
            menus = []
          }
          this.editor.config.zIndex = 0
          if (this.isMobile) {
            this.initialFrameHeight = '150'
          }
          this.editor.config.height = eval(this.initialFrameHeight)

          this.editor.config.menus = menus
          if (
            this.editor.config.uploadImgHeaders &&
            !this.editor.config.uploadImgHeaders.Authorization
          ) {
            this.editor.config.uploadImgHeaders.Authorization =
              this.$requestConfig.getToken()
          }
          // 不要自动聚焦
          this.editor.config.focus = false
          this.editor.create()
          if (this.disabled) {
            this.editor.disable()
          } else {
            this.editor.enable()
          }
          // 首次赋值
          this.editor.txt.html(this.inputVal)
          updateLengthDisplay() // 初始化长度显示
        })
      },
      setValue(val) {
        return this.encode ? (this.isBase64(val) ? val : encode(val)) : val
      },
      getValue(val) {
        return this.encode ? (this.isBase64(val) ? decode(val) : val) : val
      },
      loadCompiledTextValue() {
        if (!this.textValue) {
          return
        }
        console.log(
          'loadCompiledTextValue',
          this.textValue,
          this.isBase64(this.textValue)
        )
        let textValue = this.isBase64(this.textValue)
          ? decode(this.textValue)
          : this.textValue
        this.$nextTick(() => {
          this.getTextValue(textValue).then((result) => {
            this.compiledTextValue = result
          })
        })
      },
      getTextValue(val) {
        if (this.formInstance == null) {
          this.formInstance = utils.getOnlineFormInstance(this)
        }
        const instance = this.formInstance || this
        return new Promise((resolve) => {
          const tempComponent = Vue.extend({
            data() {
              return {
                data: instance.data,
              }
            },
            template: `<div>${val}</div>`,
          })
          const component = new tempComponent().$mount()
          component.$nextTick(() => {
            resolve(component.$el.innerHTML)
          })
        })
      },
    },
  }
</script>
<style lang="scss" scoped>
  div.inputs.ht-form-inputs__inline {
    width: auto;
  }
  input[type='hidden'][aria-invalid='true'] + div[data-we-id] {
    outline: 1px solid #f56c6c;
  }
  .empty-div {
    height: 32px;
  }
  .is-disabled {
    cursor: not-allowed;
    ::v-deep {
      .w-e-text-container {
        background: #f5f7fa;
      }
    }
  }
  .mobile-wangEditor-container {
    ::v-deep .w-e-toolbar {
      border: none !important;
      background-color: #f9f9f9 !important;
      .w-e-menu {
        color: #262626;
      }
    }
    ::v-deep .w-e-text-container {
      border: none !important;
    }
  }
</style>
<style lang="scss">
  .ht-editor {
    border-radius: 8px;
  }
  .wangEditor-table-style {
    // 富文本查看表格样式(对查看的时候需要自己手动加table/td/th的样式,不然就只有内容没有线)
    table {
      border: none;
      border-collapse: collapse;
    }
    table td,
    table th {
      border: 1px solid #ccc;
      padding: 3px 5px;
      min-width: 50px;
      height: 20px;
    }
    table th {
      border-right: 1px solid #ccc;
      border-bottom: 2px solid #ccc;
      text-align: center;
      background-color: #f1f1f1;
    }
    blockquote {
      display: block;
      border-left: 8px solid #d0e5f2;
      padding: 5px 10px;
      margin: 10px 0;
      line-height: 1.4;
      font-size: 100%;
      background-color: #f1f1f1;
    }
    code {
      display: inline-block;
      *display: inline;
      *zoom: 1;
      background-color: #f1f1f1;
      border-radius: 3px;
      padding: 3px 5px;
      margin: 0 3px;
    }
    pre code {
      display: block;
    }
    ul,
    ol {
      margin: 10px 0 10px 20px;
    }
    pre {
      border: 1px solid #ccc;
      background-color: #f8f8f8;
      padding: 10px;
      margin: 5px 0px;
      font-size: 0.8em;
      border-radius: 3px;
    }
    .ql-editor ul li {
      list-style-type: disc; // 解决序列li前面的.不展示问题
    }
    .ql-editor ol li {
      list-style-type: decimal; // 解决序列li前面的数字不展示问题
    }
  }
  .hyperlink_content {
    color: blue;
    cursor: pointer;
  }
</style>
