ct
This commit is contained in:
		@@ -40,7 +40,8 @@
 | 
				
			|||||||
    "vue": "^2.6.11",
 | 
					    "vue": "^2.6.11",
 | 
				
			||||||
    "vuedraggable": "^2.24.3",
 | 
					    "vuedraggable": "^2.24.3",
 | 
				
			||||||
    "vuex": "^3.2.0",
 | 
					    "vuex": "^3.2.0",
 | 
				
			||||||
    "vuex-persistedstate": "^4.0.0-beta.3"
 | 
					    "vuex-persistedstate": "^4.0.0-beta.3",
 | 
				
			||||||
 | 
					    "wangeditor": "^4.7.10"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "devDependencies": {
 | 
					  "devDependencies": {
 | 
				
			||||||
    "@babel/runtime": "~7.12.0",
 | 
					    "@babel/runtime": "~7.12.0",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,7 +5,7 @@
 | 
				
			|||||||
        <u-input v-model="form.title" placeholder="请输入,最多30字" maxlength="30"/>
 | 
					        <u-input v-model="form.title" placeholder="请输入,最多30字" maxlength="30"/>
 | 
				
			||||||
      </u-form-item>
 | 
					      </u-form-item>
 | 
				
			||||||
      <u-form-item label="详情描述" prop="content">
 | 
					      <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>
 | 
				
			||||||
      <u-form-item label="图片(最多9张)" class="files">
 | 
					      <u-form-item label="图片(最多9张)" class="files">
 | 
				
			||||||
        <AiUploader multiple :limit="9" :def.sync="form.fileList" action="/admin/file/add2"/>
 | 
					        <AiUploader multiple :limit="9" :def.sync="form.fileList" action="/admin/file/add2"/>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -60,7 +60,7 @@ export default {
 | 
				
			|||||||
  },
 | 
					  },
 | 
				
			||||||
  data() {
 | 
					  data() {
 | 
				
			||||||
    return {
 | 
					    return {
 | 
				
			||||||
      fullArea: [{name: '全国', id: 0}],
 | 
					      fullArea: [{name: '全部', id: 0}],
 | 
				
			||||||
      index: '',
 | 
					      index: '',
 | 
				
			||||||
      list: [],
 | 
					      list: [],
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -86,7 +86,7 @@ export default {
 | 
				
			|||||||
          } else {
 | 
					          } else {
 | 
				
			||||||
            this.fullArea = res.data
 | 
					            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