<template>
  <div class="def-editor">
    <!--toolbar工具栏配置-->
    <Toolbar class="tool-bar" :editor="editorRef" :defaultConfig="toolbarConfig" :mode="mode" />
    <!--editor编辑器配置-->
    <Editor
      class="editor"
      v-model="editorVal"
      :mode="mode"
      :defaultConfig="editorConfigVal"
      :style="editorStyle"
      @onCreated="handleCreated"
      @onChange="handleChange"
      @onDestroyed="handleDestroyed"
      @onFocus="handleFocus"
      @onBlur="handleBlur"
      @customAlert="customAlert"
      @customPaste="customPaste" />
  </div>
</template>

<script lang="ts" setup>
import '@wangeditor/editor/dist/css/style.css' // 引入 css
import { PropType, ref, shallowRef, onBeforeUnmount, watch, CSSProperties, watchEffect } from 'vue'
import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
import { IToolbarConfig, IEditorConfig, IDomEditor } from '@wangeditor/editor'
import { ElMessage, ElLoading } from 'element-plus/es'
import NProgress from '@/plugins/NProgress' // 引入顶部进度条插件

/* 参数与事件声明 */
type TMode = 'default' | 'simple'
const props = defineProps({
  content: {
    // 回显内容
    type: String,
    required: true
  },
  mode: {
    // 模式 default | simple
    type: String as PropType<TMode>,
    default: 'default'
  },
  disabled: {
    // 是否禁用表单
    type: Boolean,
    default: false
  },
  toolbarConfig: {
    // toolbar工具栏配置
    type: Object as PropType<Partial<IToolbarConfig>>,
    default: () => ({})
  },
  editorConfig: {
    // editor编辑器配置
    type: Object as PropType<Partial<IEditorConfig>>,
    default: () => ({})
  },
  editorStyle: {
    // editor编辑器样式
    type: Object as PropType<CSSProperties>,
    default: () => ({})
  }
})
const emits = defineEmits(['update:content'])

/* 逻辑处理 */
// 编辑器实例，必须用 shallowRef
const editorRef = shallowRef()
// 内容 HTML
const editorVal = ref<string>(props.content)
watch(
  () => props.content,
  (val: string) => {
    editorVal.value = val
  }
)
// 编辑器配置
type InsertFnType = (url: string, alt?: string, href?: string) => void
// 文件地址前缀
const VUE_APP_FILE_PREFIX = process.env.VUE_APP_FILE_PREFIX
const loading = ref<any>()
const editorConfigDefault = {
  placeholder: '请输入内容',
  autoFocus: false,
  readOnly: false,
  // 菜单配置
  MENU_CONF: {
    // 上传图片的配置
    uploadImage: {
      server: process.env.VUE_APP_FILE_URL,
      // form-data fieldName ，默认值 'wangeditor-uploaded-image'
      fieldName: 'file',
      // 单个文件的最大体积限制，默认为 2M
      // maxFileSize: 1 * 1024 * 1024, // 1M
      // 最多可上传几个文件，默认为 100
      // maxNumberOfFiles: 10,
      // 选择文件时的类型限制，默认为 ['image/*'] 。如不想限制，则设置为 []
      allowedFileTypes: ['image/*'],
      // 自定义上传参数，例如传递验证的 token 等。参数会被添加到 formData 中，一起上传到服务端。
      /* meta: {
        token: 'xxx',
        otherKey: 'yyy'
      }, */
      // 将 meta 拼接到 url 参数中，默认 false
      metaWithUrl: false,
      // 自定义增加 http  header
      /* headers: {
        Accept: 'text/x-json',
        otherKey: 'xxx'
      }, */
      // 跨域是否传递 cookie ，默认为 false
      withCredentials: true,
      // 超时时间，默认为 10 秒
      timeout: 5 * 1000, // 5 秒
      // 上传之前触发
      onBeforeUpload(file: any) {
        // 开启进度条
        NProgress.start()
        // 开启loading效果
        loading.value = ElLoading.service({
          lock: true,
          text: '上传中...',
          background: 'rgba(0, 0, 0, 0.7)'
        })
        // file 选中的文件，格式如 { key: file }
        return file
        // 可以 return
        // 1. return file 或者 new 一个 file ，接下来将上传
        // 2. return false ，不上传这个 file
      },
      // 上传进度的回调函数
      onProgress(progress: number) {
        // progress 是 0-100 的数字
        console.log('progress', progress)
      },
      /* // 单个文件上传成功之后
      onSuccess (file: File, res: any) {
        console.log(`${file.name} 上传成功`, res)
      },
      // 单个文件上传失败
      onFailed (file: File, res: any) {
        console.log(`${file.name} 上传失败`, res)
      },
      // 上传错误，或者触发 timeout 超时
      onError (file: File, err: any, res: any) {
        console.log(`${file.name} 上传出错`, err, res)
      }, */
      // 自定义插入图片
      customInsert(res: any, insertFn: InsertFnType) {
        // 关闭进度条
        NProgress.done()
        // 关闭loading
        loading.value.close()
        // res 即服务端的返回结果
        console.log('服务端的返回结果', res.data.name, res.data.url)
        // 从 res 中找到 url alt href ，然后插图图片
        const url = VUE_APP_FILE_PREFIX + res.data.url
        const href = VUE_APP_FILE_PREFIX + res.data.url
        const alt = res.data.name
        insertFn(url, alt, href)
      }
    },
    // 上传视频的配置
    uploadVideo: {
      server: process.env.VUE_APP_FILE_URL,
      // form-data fieldName ，默认值 'wangeditor-uploaded-video'
      fieldName: 'file',
      // 单个文件的最大体积限制，默认为 10M
      // maxFileSize: 5 * 1024 * 1024, // 5M
      // 最多可上传几个文件，默认为 5
      // maxNumberOfFiles: 3,
      // 选择文件时的类型限制，默认为 ['video/*'] 。如不想限制，则设置为 []
      allowedFileTypes: ['video/*'],
      // 自定义上传参数，例如传递验证的 token 等。参数会被添加到 formData 中，一起上传到服务端。
      /* meta: {
        token: 'xxx',
        otherKey: 'yyy'
      }, */
      // 将 meta 拼接到 url 参数中，默认 false
      metaWithUrl: false,
      // 自定义增加 http  header
      /* headers: {
        Accept: 'text/x-json',
        otherKey: 'xxx'
      }, */
      // 跨域是否传递 cookie ，默认为 false
      withCredentials: true,
      // 超时时间，默认为 30 秒
      timeout: 15 * 1000, // 15 秒
      // 视频不支持 base64 格式插入
      // 上传之前触发
      onBeforeUpload(file: any) {
        // 开启进度条
        NProgress.start()
        // 开启loading效果
        loading.value = ElLoading.service({
          lock: true,
          text: '上传中...',
          background: 'rgba(0, 0, 0, 0.7)'
        })
        console.log(file)
        // 可以 return
        // 1. return file 或者 new 一个 file ，接下来将上传
        // 2. return false ，不上传这个 file
      },
      // 上传进度的回调函数
      onProgress(progress: number) {
        // progress 是 0-100 的数字
        console.log('progress', progress)
      },
      /* // 单个文件上传成功之后
      onSuccess (file: File, res: any) {
        console.log(`${file.name} 上传成功`, res)
      },
      // 单个文件上传失败
      onFailed (file: File, res: any) {
        console.log(`${file.name} 上传失败`, res)
      },
      // 上传错误，或者触发 timeout 超时
      onError (file: File, err: any, res: any) {
        console.log(`${file.name} 上传出错`, err, res)
      }, */
      // 自定义插入视频
      customInsert(res: any, insertFn: InsertFnType) {
        // 关闭进度条
        NProgress.done()
        // 关闭loading
        loading.value.close()
        // res 即服务端的返回结果
        console.log('服务端的返回结果', res)
        // 从 res 中找到 url ，然后插入视频
        const url = VUE_APP_FILE_PREFIX + res.data.url
        insertFn(url)
      }
    }
  }
}
const editorConfigVal = ref<Partial<IEditorConfig>>({
  ...editorConfigDefault,
  ...props.editorConfig
})
watchEffect(() => {
  if (props.disabled) {
    editorConfigVal.value.readOnly = props.disabled
  }
})

// editor 初始化
const handleCreated = (editor: IDomEditor) => {
  editorRef.value = editor
}
// editor 内容改变
const handleChange = (editor: IDomEditor) => {
  const hasText = editor.children.length > 0 || (editor.children as any)[0]?.children.length > 0
  const text = editor.getText()
  const html = editor.getHtml()
  const content = hasText ? (html === '<p><br></p>' ? text : html) : text
  console.log('change:', content)
  emits('update:content', content)
}
// editor 销毁
const handleDestroyed = (editor: IDomEditor) => {
  console.log('destroyed', editor)
}
// editor 聚焦
const handleFocus = (editor: IDomEditor) => {
  console.log('focus', editor)
}
// editor 失焦
const handleBlur = (editor: IDomEditor) => {
  console.log('blur', editor)
}
// editor 用户自定义提示
const customAlert = (info: string, type: string) => {
  // alert(`【自定义提示】${type} - ${info}`)
  switch (type) {
    case 'success':
      ElMessage.success(info)
      break
    case 'info':
      ElMessage.info(info)
      break
    case 'warning':
      ElMessage.warning(info)
      break
    case 'error':
      ElMessage.error(info)
      break
    default:
      ElMessage.info(info)
      break
  }
}
// editor 用户剪切事件监听
const customPaste = (editor: IDomEditor, event: ClipboardEvent, callback: any) => {
  console.log('ClipboardEvent 粘贴事件对象', event)
  // const html = event.clipboardData.getData('text/html') // 获取粘贴的 html
  // const text = event.clipboardData.getData('text/plain') // 获取粘贴的纯文本
  // const rtf = event.clipboardData.getData('text/rtf') // 获取 rtf 数据（如从 word wsp 复制粘贴）

  // 自定义插入内容
  // editor.insertText('xxx')

  // 返回 false ，阻止默认粘贴行为
  // event.preventDefault()
  // callback(false) // 返回值（注意，vue 事件的返回值，不能用 return）

  // 返回 true ，继续默认的粘贴行为
  const flag = true
  callback(flag)
}
// editor 内容清空
const clear = () => {
  editorRef.value.clear()
  emits('update:content', '')
}
// 对外暴露内容清空方法
defineExpose({
  clear
})

// 组件销毁时，也及时销毁编辑器
onBeforeUnmount(() => {
  const editor = editorRef.value
  if (editor == null) return
  editor.destroy()
})
</script>

<style scoped lang="scss">
.def-editor {
  width: 100%;
  border: 1px solid #cccccc;
  line-height: normal;

  @include flexCol;
  .tool-bar {
    border-bottom: 1px solid #cccccc;
  }
  .editor {
    flex: 1;

    @include flexCol;

    overflow-y: hidden;
    :deep(.w-e-text-container) {
      flex: 1;
      .w-e-scroll {
        //@include scrollStyle;
      }
    }
  }
}
</style>
