wangEditor高级编辑器实现智能写作

image

功能解析:
左侧是一个高级编辑器,右侧是一个表单,输入表单内容,点击【生成内容】按钮,连接ws,ws会一直放回生成内容,每次返回都赋值给高级编辑器,这样就达到一直自动输入的效果了。

高级编辑器采用wangEditor编辑器

import { Editor, Toolbar } from '@wangeditor/editor-for-vue'

Editor是高级编辑器的输入框,Toolbar是高级编辑器的工具栏

image

默认的编辑器选中效果与需要的效果不一样

image

这个时候就需要自定义处理

wangEditor编辑器通过Boot向外暴露了一个API import { Boot } from '@wangeditor/editor'

通过Boot可以注册自定义的菜单

const menu1Conf = {
  key: 'aiToolbar', // 定义 menu key :要保证唯一、不重复(重要)
  factory() {
    return new MyButtonMenu('AI功能', `<img class="toolbarTitleImg" src="${aiTypeIcon}">`, 'button', '() => {}') // 把 `MyButtonMenu` 替换为你菜单的 class
  }
}
Boot.registerMenu(menu1Conf)

主要就是调用Boot.registerMenu()注册一个自定义菜单,new MyButtonMenu主要是处理这个菜单的一些方法(点击,激活)之类的

const toolsList = [
  {
    title: '内容润色',
    type: 'polishing',
    select: true
  },
  {
    title: '内容扩写',
    type: 'expansion',
    select: true
  },
  {
    title: '内容精简',
    type: 'simplify',
    select: true
  },
  {
    title: '语气转换',
    type: 'tone',
    select: true
  },
  {
    title: '内容摘要',
    type: 'summary',
    select: true
  },
  {
    title: '继续写作',
    type: 'continuation',
    select: true
  },
  {
    title: '内容纠错',
    type: 'polishing',
    select: true
  }
]
let clickTools = ''
let clickToneStyle = ''
let timer = ''
class MyButtonMenu { // JS 语法
  constructor(title, iconSvg, tag, exec) {
    this.title = title // 自定义菜单标题
    this.iconSvg = iconSvg // 自定义菜单的svg
    this.tag = tag // 自定义菜单的类型
    // this.exec = exec // 自定义菜单点击的方法
  }

  // 获取菜单执行时的 value ,用不到则返回空 字符串或 false
  getValue(editor) { // JS 语法
    return editor.getSelectionText()
  }

  // 菜单是否需要激活(如选中加粗文本,“加粗”菜单会激活),用不到则返回 false
  isActive(editor) { // JS 语法
    return false
  }

  // 菜单是否需要禁用(如选中 H1 ,“引用”菜单被禁用),用不到则返回 false
  isDisabled(editor) { // JS 语法
    return false
  }

  // 点击菜单时触发的函数
  exec(editor, value) { // JS 语法
    const weHoverBar = document.getElementsByClassName('w-e-hover-bar')[0]
    const divDom = document.createElement('div')
    divDom.classList.add('toolvarHover')
    divDom.innerHTML = `
      <div>
        <div class="toolvarTitle"><img class="toolbarImg" src="${polishIcon}">内容润色</div>
        <div class="toolvarTitle"><img class="toolbarImg" src="${addTextIcon}">内容扩写</div>
        <div class="toolvarTitle"><img class="toolbarImg" src="${removeTextIcon}">内容精简</div>
        <div class="toolvarTitle" id="toolvarTitleAdd"><img class="toolbarImg" src="${convertIcon}">语气转换<span class="toolvarTitleAdd"></span></div>
        <div class="toolvarTitle"><img class="toolbarImg" src="${contentIcon}">内容摘要</div>
        <div class="toolvarTitle"><img class="toolbarImg" src="${continueIcon}">继续写作</div>
      </div>
    `
    // <div class="toolvarTitle"><img class="toolbarImg" src="${errorIcon}">内容纠错</div>
    const secondaryTypeDiv = document.createElement('div')
    secondaryTypeDiv.classList.add('secondaryTypeClass')
    secondaryTypeDiv.innerHTML = `
      <div class="secondaryTitle">正式</div>
      <div class="secondaryTitle">轻松</div>
      <div class="secondaryTitle">亲切</div>
      <div class="secondaryTitle">幽默</div>
      <div class="secondaryTitle">营销</div>
    `
    weHoverBar.append(divDom)
    weHoverBar.append(secondaryTypeDiv)
    const toolvarTitleAdd = document.getElementById('toolvarTitleAdd')
    const secondaryTypeClass = document.getElementsByClassName('secondaryTypeClass')[0]
    setTimeout(() => {
      toolvarTitleAdd.addEventListener('mouseover', () => {
        secondaryTypeClass.style.display = 'inline-block'
      })
      toolvarTitleAdd.addEventListener('mouseleave', () => {
        secondaryTypeClass.style.display = 'none'
      })
      secondaryTypeClass.addEventListener('mouseover', () => {
        secondaryTypeClass.style.display = 'inline-block'
      })
      secondaryTypeClass.addEventListener('mouseleave', () => {
        secondaryTypeClass.style.display = 'none'
      })
      const toolvarTitleList = document.getElementsByClassName('toolvarTitle')
      const secondaryTitleList = document.getElementsByClassName('secondaryTitle')
      Array.from(toolvarTitleList).forEach(item => {
        item.addEventListener('click', (evt) => {
          toolsList.forEach(v => {
            if (v.title === evt.target.innerText) {
              if (timer) return
              timer = setTimeout(() => {
                timer = ''
                clickTools(v)
              }, 1000)
            }
          })
        })
      })
      Array.from(secondaryTitleList).forEach(item => {
        item.addEventListener('click', e => {
          if (timer) return
          timer = setTimeout(() => {
            clickToneStyle(e.target.innerText)
            timer = ''
          }, 1000)
        })
      })
    }, 0)
  }
}
const menu1Conf = {
  key: 'aiToolbar', // 定义 menu key :要保证唯一、不重复(重要)
  factory() {
    return new MyButtonMenu('AI功能', `<img class="toolbarTitleImg" src="${aiTypeIcon}">`, 'button', '() => {}') // 把 `MyButtonMenu` 替换为你菜单的 class
  }
}
Boot.registerMenu(menu1Conf)

右侧ws生成内容

点击【生成内容】主要就是创建ws连接

          this.createWs({
            data: {
              type: this.writeType,
              scene: JSON.stringify(scene)
            },
            start: () => {
              this.showMask = true
            },
            change: (res) => {
              if (res.success) {
                this.generatedContent = res.data.message
                this.generatedContent = this.generatedContent.replace(/\n\n/g, '\n')
                this.$refs.contentOverview.setContentValue(this.generatedContent)
              } else {
                this.ws.breakSocket()
                this.ws = null
                this.loading = false
                this.showMask = false
                this.$message.warning(res.message)
              }
            },
            end: () => {
              this.generated = true
              this.loading = false
              this.showMask = false
            },
            error: (val) => {
              this.loading = false
              this.showMask = false
              this.$message.warning(val || '连接超时,请检查网络后重新尝试。')
            }
          })
    createWs (wsData) {
      this.wsToken = new Date().getTime()
      const protocol = location.protocol === 'https:' ? 'wss://' : 'ws://'
      let urlHead = protocol + location.host + '/api-websocket'
      if (process.env.NODE_ENV === 'development') {
        // urlHead = 'ws://172.16.7.234:8083'
        urlHead = 'ws://xxxxxxxxxxx/api-websocket'
      }
      this.clearTimer = setTimeout(() => {
        this.breakeWs()
        wsData.error()
      }, 10000)
      this.ws = new InitSocket({
        url: `${urlHead}/jpaas-jwrite-server/ws/write/${this.wsToken}`,
        openHeartBeat: false,
        change: (res, num) => {
          if (res.success) {
            this.flag = res.data.flag
            if (num === 1) {
              // 请求成功清除定9时器
              this.destoryTimer()
              wsData.start()
            }
            // 如果结束
            if (res.data.flag === 'end') {
              this.breakeWs()
              wsData.end()
            } else {
              wsData.change(res)
            }
          } else {
            this.destoryTimer()
            this.breakeWs()
            wsData.error(res.message)
          }
        },
        success: () => {
          this.ws.sendMessage(wsData.data)
        },
        error: () => {
          this.breakeWs()
          wsData.error()
        }
      })
    },
// 创建构造函数
const InitSocket = function (obj) {
  const defaultObj = {
    url: '',
    lockReconnect: false,
    reconnectNum: 0,
    messageNum: 0,
    ws: null,
    tt: null,
    timeout: 3000, // 重连延时时间
    timeoutObj: null, // 重连定时器对象
    close: true, // 是否手动调用关闭
    openHeartBeat: true, // 是否开启心跳
    heartTimeOut: 15000, // 心跳间隔时间
    heartTimeOutObj: null // 心跳定时器对象
  }
  this.config = Object.assign(defaultObj, obj)
  if ('WebSocket' in window) {
    console.log('支持WebSocket')
    setTimeout(() => {
      this.createWebSocket()
    }, 100)
  } else {
    alert('该浏览器不支持WebSocket')
  }
}
InitSocket.prototype = {
  InitSocket,
  // 创建socket
  createWebSocket () {
    try {
      // 成功
      this.config.ws = new WebSocket(this.config.url)
      this.socketInit()
    } catch (e) {
      // 错误以后重连
      this.socketReConnect()
    }
  },
  // 初始化方法,成功后执行
  socketInit () {
    const that = this
    // 连接关闭->重连
    this.config.ws.onclose = function (e) {
      console.log('socket关闭', new Date(), e)
      if (that.config.close) return
      that.socketReConnect()
    }
    // 连接错误->重连
    this.config.ws.onerror = function (e) {
      console.log('socket错误', new Date(), e)
      if (that.config.close) return
      that.socketReConnect()
    }
    // 连接建立->发送消息&启动心跳
    this.config.ws.onopen = function (e) {
      console.log('socket连接成功', new Date())
      that.startSocket()
      that.config.success(that.config.reconnectNum)
    }
    // 业务订阅成功后接收的消息
    this.config.ws.onmessage = function (evt) {
      that.config.messageNum += 1
      that.config.change(JSON.parse(evt.data), that.config.messageNum)
    }
  },
  // 链接成功发送消息
  startSocket () {
    if (this.config.openHeartBeat) {
      this.startHeartBeat()
    }
  },
  // 启动心跳
  startHeartBeat () {
    this.config.heartTimeOutObj = setInterval(() => {
      const message = 'I'
      this.config.ws.send(message)
    }, this.config.heartTimeOut)
  },
  // 发送消息
  sendMessage (data) {
    if (this.config.ws) {
      this.config.ws.send(JSON.stringify(data))
    }
  },
  // 断开ws链接
  breakSocket () {
    if (this.config.ws) {
      this.config.close = true
      // 清除定时器
      if (this.config.heartTimeOutObj) {
        clearInterval(this.config.heartTimeOutObj)
        this.config.heartTimeOutObj = null
      }
      if (this.config.timeoutObj) {
        clearTimeout(this.config.timeoutObj)
        this.config.timeoutObj = null
      }
      this.config.ws.close && this.config.ws.close()
    }
  },
  // 重连
  socketReConnect () {
    if (this.config.heartTimeOutObj) {
      clearInterval(this.config.heartTimeOutObj)
      this.config.heartTimeOutObj = null
    }
    // 如果重连中->禁止
    if (this.lockReconnect) return
    // 如果重连了10次
    if (this.config.reconnectNum === 10) {
      this.breakSocket()
      this.config.error()
      return
    }
    this.config.reconnectNum += 1
    console.log('socket开始重连')
    this.lockReconnect = true
    this.config.tt && clearTimeout(this.config.tt)
    this.config.tt = setTimeout(() => {
      this.lockReconnect = false
      this.createWebSocket()
    }, this.config.timeout)
  }
}
export default InitSocket

posted @ 2023-12-29 16:56  嘿!那个姑娘  阅读(11)  评论(0编辑  收藏  举报