chatarea实现仿豆包输入框

123 阅读2分钟

image.png

npm i --save chatarea
<template>
  <div class="demo-wrap">
    <!-- 创建目标载体容器 -->
    <div class="editor-container" ref="rich">
      <div class="textarea">
          <div ref="elmRef" class="chat-elm"></div>
      </div>
      <div class="expand_btn_wrapper" v-show="show">
        <span class="icon"><svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="none" viewBox="0 0 24 24"><path fill="currentColor" d="M22 3v7a1 1 0 1 1-2 0V4h-6a1 1 0 1 1 0-2h7a1 1 0 0 1 1 1M11.005 21a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1v-7a1 1 0 0 1 2.002 0v6h6.003a1 1 0 0 1 1 1"></path></svg></span>
        <span style="font-size:14px;">模板</span></div>
    </div>
    <div class="rich-text-bottom">
      <div class="left-area">
        <span :class="['deepthink', {active: deepthink}]" @click="handleDeepThink">深度搜索</span>
        <span :class="['knowledge', {active: show}]" @click="handleShow">知识库</span>
      </div>
      <div class="right-area">
        <div :class="['send', {active: query.trim().length != 0} ]" @click="sendMsg">发送</div>
      </div>
    </div>
  </div>
</template>
<script>
  import ChatArea from 'chatarea'
  import 'chatarea/lib/ChatArea.css'
  export default {
    name: 'demo',
    data () {
      return {
        chat: null,
        query: '',
        deepthink: false,
        show: false
      }
    },
    mounted () {
      this.initChat()
    },
    methods: {
      initChat () {
        // 实例chat对象
        this.chat = new ChatArea({
          elm: this.$refs.elmRef,
          placeholder: '请输入问题,Shift+Enter换行',
          needCallEvery: false,
          device: 'pc',
          wrapKeyFun: (event) => event.shiftKey && event.key === 'Enter',
        //   sendKeyFun: (event) => !event.shiftKey && event.key === 'Enter', // 仅按 Enter 发送
          // userList: [
          //   { id: '1', name: 'JianFv' },
          //   { id: '2', name: '松松' }
          // ],
          selectList: [
            {
                dialogTitle: '工作类型', // 弹窗标题
                key: 'job', // 唯一标识值
                options: [ // 弹窗下拉选项
                { id: '1', name: '前端工程师',  }, // name:选择项文案 preview:预览图
                { id: '2', name: '后端工程师',  },
                { id: '3', name: '运维工程师', },
                { id: '4', name: 'Python人工智能' },
                ]
            }, {
                dialogTitle: '风格', // 弹窗标题
                key: 'style', // 唯一标识值
                options: [ // 弹窗下拉选项
                { id: '1', name: '人像摄影',  }, // name:选择项文案 preview:预览图
                { id: '2', name: '电影写真',  },
                { id: '3', name: '中国风',  },
                { id: '4', name: '动漫',  },
                { id: '5', name: '3D渲染',},
                { id: '6', name: '赛博朋克',  }
                ]
            }
          ],
        })
        document.addEventListener('keydown',(e)=>{
          if (e.keyCode === 27 && this.chat != null) {
            console.log('ESC');
            this.chat.closeTipTag()
          }
        })
        console.log(this.chat);
        // this.chat.updateConfig({
        //   needCallEvery: false
        // })
        //  this.chat.setSelectTag(
        //     { id: '1', name: '人像摄影' }, // 配置项selectList数据,这里传入默认选中项
        //     'style' // 对应key
        // )
        // this.chat.setInputTag(
        // 'title', // 输入内容对应的key值
        // '请输入文章题目', // placeholder
        // '花儿为什么这样红' // 默认值(非必传)
        // )
        const opNode = this.chat.createOperateNode()
        // 在聊天框开头插入新的一行内容
        opNode.insertNode({
                type: 'gridBox', // 标识其类型 目前类型共有:gridBox girdInput userTag customTag selectTag htmlTag
                rank: '0001', // 用于快速标识上下级关系
                children: [
                    {
                        type: 'gridInput',
                        rank: '00010001',
                        text: '我的工作是' // gridInput 会记录其内容文本
                    },
                    {
                        type: 'selectTag',
                        rank: '00010002',
                        dataset: { 
                            id: '1', 
                            name: '前端工程师',
                            key: 'job' 
                        } // userTag customTag selectTag 会记录下其绑定的属性值
                    },
                    {
                        type: 'gridInput',
                        rank: '00010003',
                        text: ',然后又'
                    },
                    {
                        type: 'customTag',
                        rank: '00010004',
                        dataset: { id: '1', name: '股票趋势', prefix: '#' }
                    },
                    {
                        type: 'gridInput',
                        rank: '00010005',
                        text: ',图片风格为'
                    },
                    {
                        type: 'selectTag',
                        rank: '00010006',
                        dataset:{
                            id: '1', 
                            name: '人像摄影', // 配置项selectList数据,这里传入默认选中项
                            key: 'style' // 对应key
                        },
                    },
                    {
                        type: 'gridInput',
                        rank: '00010007',
                        text: ''
                    },
                    {
                        type: 'inputTag',
                        rank: '00010008',
                        dataset: {
                          key: 'title', // 输入内容对应的key值
                          placeholder: '[请输入文章题目]', // placeholder
                          value: '花儿为什么这样红' // 默认值(非必传)
                        }
                    },
                    {
                        type: 'gridInput',
                        rank: '00010009',
                        text: ''
                    }
                ]
        })
        // 绑定键盘发送事件
        this.chat.addEventListener('enterSend', this.sendMsg)
        this.chat.richText.addEventListener('input',(e)=>{
            // console.log('e',e.shiftKey);
            try {
              this.query = this.chat.getText()
            } catch (e) {
              this.query = ''
                // console.log(e);
            }
          // if (e.target.scrollHeight > 18.4*3) {
          //   return false  // 114是输入框6行的高度,超过6行就使用滚动条
          // }
          // console.log(this.$refs.rich);
          // this.$refs.rich.style.height = 88 - e.target.scrollHeight + 'px' // 200是评论列表+输入框高度
          // e.target.style.height = 'auto'
          // e.target.scrollTop = 0 //防抖动
          // e.target.style.height = e.target.scrollHeight + 'px' 
          if (e.code=== 'Enter'&&!e.shiftKey) {
              e.preventDefault();
          }
        })
      },
      template(){
      },
      handleDeepThink(){
        this.deepthink = !this.deepthink
      },
      handleShow(){
        this.show = !this.show
        if (this.show) {
          this.chat.openTipTag({
            tagLabel: '写作',
            popoverLabel: '点击退出技能',
            codeLabel: 'ESC'
          })
        }
      },
      // 发送消息
      sendMsg () {
        if (!this.chat || !this.query) return
        console.log('send',this.query);
        // 获取html
        const htmlMsg = this.chat.getHtml({
            identifyLink: true
        })
        // 获取纯文本
        const textMsg = this.chat.getText({
            imgToText: true
        })
        // 获取聊天框中@人员
        const callUserList = this.chat.getCallUserList()
      }
    },
    beforeDestroy () {
      // 释放实例
      if (this.chat) {
         this.chat.dispose()
         this.chat = null
      }
    }

} </script>

<style scoped lang="less">
  .demo-wrap {
    width: 800px;
    margin: 0 auto;
    margin-top: 300px;
    border: 1px solid #ccc;
    border-radius: 10px;
    & .editor-container{
      & .textarea{
        padding: 10px 0;
        width: 100%;
      }
      display: flex;
      .expand_btn_wrapper{
        user-select: none;
        cursor: pointer;
        width: 68px;
        height: 32px;
        display: flex;
        justify-content: space-around;
        align-items: center;
        .icon{
          line-height: 32px;
          display: flex;
          align-items: center;
        }
      }
    }
    /** 聊天框样式 **/
    .chat-elm {
      // flex: 1;
      max-height: 100px;
      overflow-y: auto;
      overflow-x: hidden;
      text-align: left;
      // border: 1px solid #c6c6c6;
      background: #fff;
      /** @标签在聊天框内的样式 **/
      :deep(.at-user) {
        color: #269aff;
      }
      /deep/ .chat-tip-tag {
        transform: translateY(5px) !important;
      }
      :deep(.chat-select-dialog-main::-webkit-scrollbar){
        display: none;
      }
      .chat-select-dialog-main::-webkit-scrollbar{
        display: none !important;
      }
      /** 聊天框输入提示语样式 **/
      :deep(.chat-placeholder-wrap) {
        color: #ccc;
      }
    }
    .chat-elm::-webkit-scrollbar{
      width: none;
      background-color: transparent;
    }
    .rich-text-bottom{
      display: flex;
      font-size: 14px;
      justify-content: space-between;
      padding: 10px;
      & .left-area{
        margin-left: 10px;
        .deepthink{
          cursor: pointer;
          border: 1px solid #ccc;
          border-radius: 10px;
          padding: 10px;
          background-color: #fff;
        }
        .deepthink.active{
          background-color: #e5eeff;
          color: #0057ff;
        }
        .knowledge{
          cursor: pointer; 
          margin-left: 10px;
          border-radius: 10px;
          padding: 10px;
          background-color: #fff;
        }
        .knowledge.active {
          
          background-color: #e5eeff;
          color: #0057ff;
        }
      }
      & .right-area{
        margin-right: 10px;
        & .send{
          cursor: not-allowed;
          user-select: none;
          border-radius: 15px;
          width: 80px;
          line-height: 30px;
          background-color: #c3c3c3;
        }
        & .send.active{
          cursor: pointer;
          color: #fff;
          background-color: #0057ff;
        }
      }
    }
  }
</style>

```
```