ct
This commit is contained in:
		@@ -5,7 +5,7 @@
 | 
			
		||||
        <u-input v-model="form.title" placeholder="请输入,最多30字" maxlength="30"/>
 | 
			
		||||
      </u-form-item>
 | 
			
		||||
      <u-form-item label="详情描述" prop="content">
 | 
			
		||||
        <AiTextarea v-model="form.content" placeholder="请输入,最多500字" :maxlength="500"/>
 | 
			
		||||
        <AiEditor v-model="form.content" placeholder="请输入,最多500字" :maxlength="500"/>
 | 
			
		||||
      </u-form-item>
 | 
			
		||||
      <u-form-item label="图片(最多9张)" class="files">
 | 
			
		||||
        <AiUploader multiple :limit="9" :def.sync="form.fileList" action="/admin/file/add2"/>
 | 
			
		||||
 
 | 
			
		||||
@@ -60,7 +60,7 @@ export default {
 | 
			
		||||
  },
 | 
			
		||||
  data() {
 | 
			
		||||
    return {
 | 
			
		||||
      fullArea: [{name: '全国', id: 0}],
 | 
			
		||||
      fullArea: [{name: '全部', id: 0}],
 | 
			
		||||
      index: '',
 | 
			
		||||
      list: [],
 | 
			
		||||
    }
 | 
			
		||||
@@ -86,7 +86,7 @@ export default {
 | 
			
		||||
          } else {
 | 
			
		||||
            this.fullArea = res.data
 | 
			
		||||
          }
 | 
			
		||||
          this.fullArea.unshift({name: '全国', id: 0})
 | 
			
		||||
          this.fullArea.unshift({name: '全部', id: 0})
 | 
			
		||||
        }
 | 
			
		||||
      })
 | 
			
		||||
    },
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										265
									
								
								src/components/AiEditor.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										265
									
								
								src/components/AiEditor.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,265 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <div class="AiEditor" @click="editing=true">
 | 
			
		||||
    <div ref="AiEditorInstance"/>
 | 
			
		||||
    <div class="bottomPanel" :class="{fixed:isFullScreen}">
 | 
			
		||||
      <slot v-if="$slots.bottom" name="bottom"/>
 | 
			
		||||
      <div v-else-if="maxlength" class="fontCount">{{ [editorText.length, maxlength].join(" / ") }}</div>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
/**
 | 
			
		||||
 * 原组件wangeditor封装
 | 
			
		||||
 * 修改者:Kubbo
 | 
			
		||||
 */
 | 
			
		||||
import E from 'wangeditor'
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
  name: "AiEditor",
 | 
			
		||||
  inject: {
 | 
			
		||||
    elFormItem: {default: ""},
 | 
			
		||||
    elForm: {default: ''},
 | 
			
		||||
  },
 | 
			
		||||
  model: {
 | 
			
		||||
    prop: "value",
 | 
			
		||||
    event: "change"
 | 
			
		||||
  },
 | 
			
		||||
  props: {
 | 
			
		||||
    value: {type: String, required: true, default: ""},
 | 
			
		||||
    placeholder: {default: '请输入正文'},
 | 
			
		||||
    conf: Object,
 | 
			
		||||
    instance: {type: Function},
 | 
			
		||||
    maxlength: Number,
 | 
			
		||||
    valid: {type: Boolean, default: true}
 | 
			
		||||
  },
 | 
			
		||||
  data() {
 | 
			
		||||
    return {
 | 
			
		||||
      ins: null,
 | 
			
		||||
      isFullScreen: false,
 | 
			
		||||
      isPasteStyle: true,//粘贴是否携带格式
 | 
			
		||||
      origin: "",
 | 
			
		||||
      editorText: "",
 | 
			
		||||
      editing: false//自动赋值问题,
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  computed: {
 | 
			
		||||
    validateState() {
 | 
			
		||||
      return ['', 'success'].includes(this.elFormItem?.validateState)
 | 
			
		||||
    },
 | 
			
		||||
    extra() {
 | 
			
		||||
      return {
 | 
			
		||||
        fullscreen(editor) {
 | 
			
		||||
          if (editor?.ins.$toolbarElem) {
 | 
			
		||||
            let btn = E.$(`<div class="w-e-menu" data-title="全屏"><i class="w-e-icon-fullscreen"/></div>`)
 | 
			
		||||
            btn.on("click", () => {
 | 
			
		||||
              editor.isFullScreen = !editor.isFullScreen
 | 
			
		||||
              editor.isFullScreen ? editor.ins.fullScreen() : editor.ins.unFullScreen()
 | 
			
		||||
            })
 | 
			
		||||
            editor.ins.$toolbarElem.append(btn)
 | 
			
		||||
          }
 | 
			
		||||
        },
 | 
			
		||||
        preview(editor) {
 | 
			
		||||
          if (editor?.ins.$toolbarElem) {
 | 
			
		||||
            let btn = E.$(`<div class="w-e-menu" data-title="预览"><i class="el-icon-monitor"/></div>`)
 | 
			
		||||
            btn.on("click", () => {
 | 
			
		||||
              editor.$refs.preview.dialog = true
 | 
			
		||||
            })
 | 
			
		||||
            editor.ins.$toolbarElem.append(btn)
 | 
			
		||||
          }
 | 
			
		||||
        },
 | 
			
		||||
        pasteWithStyle(editor) {
 | 
			
		||||
          if (editor?.ins.$toolbarElem) {
 | 
			
		||||
            let btn = E.$(`<div class="w-e-menu w-e-active" data-title="粘贴格式"><i class="iconfont iconCopy"/></div>`)
 | 
			
		||||
            editor.origin = JSON.parse(JSON.stringify(editor.value))
 | 
			
		||||
            btn.on("click", () => {
 | 
			
		||||
              editor.isPasteStyle = !editor.isPasteStyle
 | 
			
		||||
              if (editor.isPasteStyle) {
 | 
			
		||||
                editor.setContent(editor.origin)
 | 
			
		||||
                btn.addClass("w-e-active")
 | 
			
		||||
              } else {
 | 
			
		||||
                editor.origin = JSON.parse(JSON.stringify(editor.value))
 | 
			
		||||
                editor.setContent(editor.editorText)
 | 
			
		||||
                btn.removeClass("w-e-active")
 | 
			
		||||
              }
 | 
			
		||||
            })
 | 
			
		||||
            editor.ins.$toolbarElem.append(btn)
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    customConfig() {
 | 
			
		||||
      let init = ["fullscreen", "preview", "pasteWithStyle"]
 | 
			
		||||
      if (this.maxlength > 0) init = init.slice(0, 2)
 | 
			
		||||
      return {
 | 
			
		||||
        debug: true,
 | 
			
		||||
        pasteFilterStyle: !this.isPasteStyle,
 | 
			
		||||
        showFullScreen: false,
 | 
			
		||||
        zIndexFullScreen: 1000,
 | 
			
		||||
        zIndex: 98,
 | 
			
		||||
        focus: false,
 | 
			
		||||
        menus: [],
 | 
			
		||||
        init,
 | 
			
		||||
        customUploadImg: (files, insert) => {
 | 
			
		||||
          this.uploadFile(files, insert)
 | 
			
		||||
        },
 | 
			
		||||
        customUploadVideo: (files, insert) => {
 | 
			
		||||
          this.uploadFile(files, insert)
 | 
			
		||||
        },
 | 
			
		||||
        onfocus: () => {
 | 
			
		||||
          this.editing = true
 | 
			
		||||
        },
 | 
			
		||||
        onblur: () => {
 | 
			
		||||
          this.editing = false
 | 
			
		||||
        },
 | 
			
		||||
        onchange: html => {
 | 
			
		||||
          if (this.maxlength > 0) {
 | 
			
		||||
            this.editorText = html?.replace(/<\/?.+?\/?>/g, '')
 | 
			
		||||
            if (this.editorText.length > this.maxlength) {
 | 
			
		||||
              this.ins.history.revoke()
 | 
			
		||||
              // this.editorText = this.editorText.substring(0, this.maxlength)
 | 
			
		||||
              // this.setContent(this.editorText)
 | 
			
		||||
            }
 | 
			
		||||
          } else {
 | 
			
		||||
            this.editorText = html
 | 
			
		||||
          }
 | 
			
		||||
          this.$emit("change", this.editorText)
 | 
			
		||||
        },
 | 
			
		||||
        pasteTextHandle: str => this.isPasteStyle ? str : str?.replace(/<\/?.+?\/?>/g, ''),
 | 
			
		||||
        ...this.conf
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  methods: {
 | 
			
		||||
    initEditor() {
 | 
			
		||||
      let {placeholder, customConfig} = this
 | 
			
		||||
      this.ins = new E(this.$refs.AiEditorInstance)
 | 
			
		||||
      this.ins.config = {...this.ins.config, ...customConfig, placeholder}
 | 
			
		||||
      this.ins.create()
 | 
			
		||||
      customConfig.init.map(e => this.extra?.[e]?.(this))
 | 
			
		||||
    },
 | 
			
		||||
    setContent(data) {
 | 
			
		||||
      if (this.ins) {
 | 
			
		||||
        this.ins.txt.html(data)
 | 
			
		||||
        this.editing = false
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    uploadFile(files, insert) {
 | 
			
		||||
      files && files.map(e => {
 | 
			
		||||
        let formData = new FormData()
 | 
			
		||||
        formData.append('file', e)
 | 
			
		||||
        this?.instance?.post(`/admin/file/add`, formData).then(res => {
 | 
			
		||||
          if (res && res.data) {
 | 
			
		||||
            res.data.map(m => {
 | 
			
		||||
              let item = m.split(";")
 | 
			
		||||
              insert(item[0])
 | 
			
		||||
            })
 | 
			
		||||
          }
 | 
			
		||||
        })
 | 
			
		||||
      })
 | 
			
		||||
    },
 | 
			
		||||
    /**
 | 
			
		||||
     * 表单验证
 | 
			
		||||
     * @param componentName
 | 
			
		||||
     * @param eventName
 | 
			
		||||
     * @param params
 | 
			
		||||
     */
 | 
			
		||||
    dispatch(componentName, eventName, params) {
 | 
			
		||||
      let parent = this.$parent || this.$root;
 | 
			
		||||
      let name = parent.$options.componentName;
 | 
			
		||||
 | 
			
		||||
      while (parent && (!name || name !== componentName)) {
 | 
			
		||||
        parent = parent.$parent;
 | 
			
		||||
 | 
			
		||||
        if (parent) {
 | 
			
		||||
          name = parent.$options.componentName;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      if (parent) {
 | 
			
		||||
        parent.$emit.apply(parent, [eventName].concat(params));
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
  watch: {
 | 
			
		||||
    value(v) {
 | 
			
		||||
      this.dispatch('ElFormItem', 'el.form.change', [v]);
 | 
			
		||||
      if (v && !this.editing) {
 | 
			
		||||
        this.setContent(v)
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    placeholder(v) {
 | 
			
		||||
      if (this.ins) {
 | 
			
		||||
        document.querySelector('.AiEditor .placeholder').innerHTML = v
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    editing(v) {
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  mounted() {
 | 
			
		||||
    !this.ins && this.initEditor()
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="scss" scoped>
 | 
			
		||||
::v-deep.AiEditor {
 | 
			
		||||
  width: 100%;
 | 
			
		||||
  position: relative;
 | 
			
		||||
 | 
			
		||||
  /* 菜单区 */
 | 
			
		||||
  .w-e-toolbar {
 | 
			
		||||
    align-items: center;
 | 
			
		||||
    flex-wrap: wrap;
 | 
			
		||||
    -webkit-box-lines: multiple;
 | 
			
		||||
 | 
			
		||||
    .w-e-menu {
 | 
			
		||||
      line-height: 24px;
 | 
			
		||||
 | 
			
		||||
      a {
 | 
			
		||||
        text-decoration: none;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      &:hover {
 | 
			
		||||
        z-index: 10002 !important;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /* 编辑区 */
 | 
			
		||||
  .w-e-text {
 | 
			
		||||
    overflow: auto;
 | 
			
		||||
    padding: 10px;
 | 
			
		||||
 | 
			
		||||
    p {
 | 
			
		||||
      margin: 0;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .fontCount {
 | 
			
		||||
    pointer-events: none;
 | 
			
		||||
    color: #999;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .bottomPanel {
 | 
			
		||||
    position: absolute;
 | 
			
		||||
    bottom: 8px;
 | 
			
		||||
    right: 24px;
 | 
			
		||||
    z-index: 1999;
 | 
			
		||||
 | 
			
		||||
    &.fixed {
 | 
			
		||||
      position: fixed;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  &.invalid {
 | 
			
		||||
    .w-e-text-container, .w-e-toolbar {
 | 
			
		||||
      border-color: red !important;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .fontCount {
 | 
			
		||||
      color: red
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
		Reference in New Issue
	
	Block a user