美文网首页
Vue@人功能

Vue@人功能

作者: EverglowLyu | 来源:发表于2024-08-05 13:56 被阅读0次

atDialog.vue

<template>
  <div class="atDialog" v-if="atDialogObj && atDialogObj.status == true">

    <!--manual-->
    <el-popover @show="checkShowPopover()" :appendToBody="true" @hide="checkAndHidePopover($event)" placement="right-start"
      width="450" :popper-class="atDialogObj.isList == 1 ? 'atDialogPopover2' : 'atDialogPopover'"
      :ref="refNamePopover + atDialogObj.id" trigger="manual" v-model="atDialogObj.visible">
      <!-- :popper-options="{ boundariesElement: 'viewport', removeOnDestroy: true }" -->

      <div class="elPopoverHeader">
        <div class="comment-title">
          <span v-if="atDialogObj.isList !== 1">{{ languageSetting == 'zh' ? atDialogObj.modeNameCh :
            atDialogObj.modeName }} -</span>
          {{ $t("language.commentInfo0") }}
        </div>
        <div class="comment-close" @click="pClose(atDialogObj.id)">
          <i class="el-icon-close"></i>
        </div>
      </div>
      <el-form :append-to-body="true" v-if="atDialogObj.isList == 0">
        <div class="comment">
          <div class="comment-body" v-for="(item, index) in comments" :key="item.id + '' + index">
            <!-- 一级评论 -->
            <div class="first-comment" :class="{ 'commentDisabled': item.status === 1 }">
              <div class="flexBetween" style="margin-bottom:6px;">
                <div class="comment-left">
                  <div v-if="item.status == 1" class="textUnderLine">
                    <i class="el-icon-check"></i>
                    {{ $t("language.commentInfo3") }}
                  </div>
                </div>

              </div>
              <div style="display:flex;">
                <div class="avatar" :style="{ 'background': item.color }">{{ item.avatarUserName }}</div>
                <div class="content">
                  <div class="flexBetween">
                    <div class="itemUserName" :data-parentId="item.id">{{ item.userName }}</div>
                    <div>
                      <el-popover popper-class="atDialogTool" placement="bottom-start" trigger="hover">
                        <ul class="tools">
                          <li v-if="item.status == 0" @click="isShowSecReply(item.id)"><i
                              class="fa fa-mail-reply-all fa-1x"></i> {{ $t("language.commentInfo1") }}</li>
                          <li v-if="item.status == 0" @click="isShowSecEdit(item.id, undefined)"><i
                              class="fa fa-edit fa-1x"></i>{{ $t("language.t49") }}</li>
                          <li @click="isShowSecClose(item.id, undefined, '1')" v-if="item.status == 0"><i
                              class="fa fa-lock fa-1x"></i>{{ $t("language.closeBtn") }}</li>
                          <li @click="isShowSecClose(item.id, undefined, '0')" v-if="item.status == 1"><i
                              class="fa fa-unlock-alt fa-1x"></i>{{ $t("language.commentInfo2") }}</li>
                          <li @click="deleteComment(item.id, undefined)"><i class="fa fa-trash fa-1x"></i>{{
                            $t("language.t50") }}</li>
                        </ul>
                        <i v-show="!isShowRightIcon" slot="reference" class="el-icon-more"></i>
                      </el-popover>
                    </div>
                  </div>

                  <!--一级编辑开始-->
                  <div v-show="!showEditCont || showEditCont != item.id" class="itemContent" v-html="formatHTML(item.commentsHtml)">

                  </div>
                  <div class="reply-comment" v-show="showEditCont === item.id">
                    <!-- <at-ta :members="members" at="@" name-key="name">
                          <template slot="item" slot-scope="s">
                            <div class="atName" v-text="s.item.name"></div>
                            <div class="avatar1" v-text="s.item.avatar"></div>

                          </template>
<el-input :placeholder="placeholderText" autosize spellcheck="false" v-model="item.comments" type="textarea"></el-input>
</at-ta> -->
                    <div :placeholder="placeholderText" v-html="item.commentsHtml"
                      @focus="editorFocus(item.id, undefined, undefined, item.commentsHtml)" :ref="editorEdit + item.id"
                      :id="editorEdit + item.id" class="message" spellcheck="false" :contenteditable="true"
                      @keyup="handkeKeyUp" @keydown="handleKeyDown" @paste="handlePaste" />
                    <div class="comment-header-button">
                      <el-button class="cancelButton"
                        @click="cancelButton(item.id, undefined, undefined, item.comments)">{{
                          $t("language.Cancel")
                        }}</el-button>
                      <el-button size="mini" class="reply-button"
                        @click="editComment(item.id, undefined, undefined, item.commentsHtml,$event)">{{
                          $t("language.Submit")
                        }}</el-button>
                    </div>
                  </div>
                  <!--一级编辑结束-->

                  <span class="itemDate">{{ item.createTime }}</span>
                  <!-- 次级评论 -->
                  <div class="second-comment" v-for="(reply, index) in item.applicationCommentsModelVo"
                    :key="reply.id + '' + index">
                    <div class="avatar" :style="{ 'background': reply.color }">{{ reply.avatarUserName }}</div>
                    <div class="content">
                      <!--次级编辑开始-->
                      <div class="itemUserName" :data-parentId="reply.id">{{ reply.userName }}</div>
                      <div v-show="!showEditCont2 || showEditCont2 != reply.id" v-if="!reply.showCont"
                        class="itemContent" v-html="formatHTML(reply.commentsHtml)">

                      </div>
                      <div class="reply-comment" v-show="showEditCont2 === reply.id">
                        <!-- 二级的编辑 -->
                        <div :placeholder="placeholderText" v-html="item.commentsHtml"
                          @focus="editorFocus(item.id, reply.id, reply.userName, reply.commentsHtml)"
                          :ref="editorEdit + reply.id" :id="editorEdit + reply.id" class="message" spellcheck="false"
                          :contenteditable="true" @keyup="handkeKeyUp" @keydown="handleKeyDown" @paste="handlePaste" />
                        <div class="comment-header-button">
                          <el-button class="cancelButton"
                            @click="cancelButton(item.id, reply.id, reply.userName, reply.comments)">{{
                              $t("language.Cancel")
                            }}</el-button>
                          <el-button size="mini" class="reply-button"
                            @click="editComment(item.id, reply.id, reply.userName, reply.commentsHtml)">{{
                              $t("language.Submit")
                            }}</el-button>
                        </div>
                      </div>
                      <!--次级编辑结束-->
                      <span class="itemDate">{{ reply.createTime }}</span>
                      <div class="comment-right" v-if="reply.status == 0">
                        <el-popover popper-class="atDialogTool" placement="bottom-start" trigger="hover">
                          <ul class="tools">
                            <li @click="isShowSecEdit(item.id, reply.id)"><i class="fa fa-edit fa-1x"></i>{{
                              $t("language.t49") }}</li>
                            <li @click="deleteComment(item.id, reply.id)"><i class="fa fa-trash fa-1x"></i>{{
                              $t("language.t50") }}</li>
                          </ul>
                          <i v-show="!isShowRightIcon" slot="reference" class="el-icon-more"></i>
                        </el-popover>
                      </div>
                      <div class="reply-comment" v-show="isShowSec === reply.id">
                        <div :placeholder="placeholderText" v-html="item.commentsHtml"
                          @focus="editorFocus(item.id, reply.id, reply.userName, reply.commentsHtml)"
                          :ref="editorAdd + reply.id" :id="editorAdd + reply.id" class="message" spellcheck="false"
                          :contenteditable="true" @keyup="handkeKeyUp" @keydown="handleKeyDown" @paste="handlePaste" />
                        <div class="comment-header-button">
                          <el-button class="cancelButton"
                            @click="cancelButton(item.id, reply.id, reply.userName, reply.comments)">{{
                              $t("language.Cancel")
                            }}</el-button>
                          <el-button size="mini" class="reply-button" @click="addComment(item.id, reply.userName,$event)">{{
                            $t("language.Submit")
                          }}</el-button>
                        </div>
                      </div>
                    </div>
                  </div>
                  <!-- 一级回复 -->
                  <div class="reply-comment" v-show="isShowSec === item.id">
                    <div :placeholder="placeholderText"
                      @focus="editorFocus(undefined, undefined, undefined, item.commentsHtml)"
                      :ref="editorAdd + item.id" :id="editorAdd + item.id" class="message" spellcheck="false"
                      :contenteditable="true" @keyup="handkeKeyUp" @keydown="handleKeyDown" @paste="handlePaste" />
                    <div class="comment-header-button">
                      <el-button class="cancelButton"
                        @click="cancelButton(item.id, undefined, undefined, item.commentsHtml)">{{
                          $t("language.Cancel")
                        }}</el-button>
                      <el-button size="mini" class="reply-button" @click="addComment(item.id, item.userName,$event)">{{
                        $t("language.Submit")
                      }}</el-button>
                    </div>
                  </div>
                </div>
              </div>
            </div>
          </div>
          <!--只有一个输入框评论-->

          <div class="comment-header" v-if="!isShowSec">

            <div :placeholder="placeholderText" @focus="editorFocus()" ref="editor" class="message" spellcheck="false"
              :contenteditable="true" @keyup="handkeKeyUp" @keydown="handleKeyDown" @paste="handlePaste"/>

            <div class="comment-header-button">
              <el-button class="cancelButton" @click="cancelButton()">{{ $t("language.Cancel")
                }}</el-button>
              <el-button class="addComment" @click="addCommentBottom($event)">{{ $t("language.Submit")
                }}</el-button>
            </div>

          </div>
          <atDialogList v-if="showDialog" ref="atDialogList" :visible="showDialog" :position="position"
            :query-string="queryString" @onPickUser="handlePickUser" @onHide="handleHide" @onShow="handleShow"
            @close="showDialog = false" />
        </div>
      </el-form>

      <el-form v-if="atDialogObj.isList == 1">
        <div class="comment">
          <div class="emptyView" v-if="JSON.stringify(comments) === '{}'">
            <span>{{ $t("language.nodata") }}</span>
          </div>
          <div v-else>
            <div class="comment-body" v-for="(value, key) in comments" :key="key">
              <div class="comment-body-title">{{ key }}</div>
              <div class="first-comment first-comment-list">

                <div :class="it.status === 1 ? 'commentDisabled islist-list' : 'islist-list'" v-for="it in value"
                  :key="it.id">
                  <div v-if="it.status == 1" class="textUnderLine">
                    <i class="el-icon-check"></i> {{ $t("language.commentInfo3") }}
                  </div>
                  <div class="flexBetween" style="align-items: flex-start;">
                    <div class="avatar" :style="{ 'background': it.color }">{{ it.avatarUserName }}</div>
                    <div class="content">
                      <div class="flexBetween">
                        <div class="itemUserName" :data-parentId="it.id">{{ it.userName }}</div>
                      </div>
                      <div class="itemContent" v-html="it.commentsHtml"></div>
                      <span class="itemDate">{{ it.createTime }}</span>

                      <div class="second-comment" v-for="(reply, index) in it.applicationCommentsModelVo"
                        :key="reply.id + '' + index">
                        <div class="avatar" :style="{ 'background': reply.color }">{{ reply.avatarUserName }}</div>
                        <div class="content">
                          <div class="itemUserName" :data-parentId="reply.id">{{ reply.userName }}</div>
                          <div class="itemContent" v-html="reply.commentsHtml"></div>
                          <span class="itemDate">{{ reply.createTime }}</span>
                        </div>
                      </div>
                    </div>
                  </div>

                </div>
              </div>
            </div>
          </div>


        </div>
      </el-form>
      <div slot="reference" class="flexBetween" @click="handleClick">
        <el-tooltip effect="dark" :content="$t('language.commentInfo0')" placement="bottom-start">
          <el-badge :value="atDialogObj.isStatus === 1 && atDialogObj.count >= 0 ? atDialogObj.count : ' '"
            :type="(atDialogObj.isStatus == 0 && atDialogObj.count == 0) ? null : (atDialogObj.isStatus == 1 && atDialogObj.count > 0 ? 'danger' : (atDialogObj.isStatus == 2 && atDialogObj.count == 0 ? 'success' : null))"
            :hidden="atDialogObj.isStatus == 0">
            <i class="el-icon-chat-line-square atDialogIcon" :data-dataId="atDialogObj.dataId"
              :data-code="atDialogObj.id" :ref="reference + atDialogObj.id"></i>
          </el-badge>
        </el-tooltip>
      </div>
    </el-popover>

  </div>
</template>
<script>

import At from 'vue-at'
import AtTa from 'vue-at/dist/vue-at-textarea'
import atDialogList from './atDialogList'
export default {
  name: "atDialog",
  //props: ['atDialogProps'],
  props: {
    atDialogProps: {
      type: Object
    },
    codeProps: {
      type: String
    },
    placeholderText: {
      type: String,
      default: "Please enter comments, you can @ others"
    }
  },
  components: { At, AtTa, atDialogList },
  data() {
    return {
      languageSetting: window.localStorage.languageSetting,
      highlightClass: 'highlighted',
      EditId: '',
      EditReplyId: '',
      clickId: '',
      members: [],
      isPlaceholderVisible: true,
      atDialogObj: {},
      reference: 'reference-',
      fileList: [],
      allowSize: 50 * 1024,
      limit: 5,
      operateType: null,
      node: '',
      user: '',
      endIndex: '',
      queryString: '',
      showDialog: false,
      comments: [],
      editorEdit: 'editorEdit-',
      editorAdd: 'editorAdd-',
      refNamePopover: 'popover-', // popover ref名称前缀
      context: "",
      replyContext: "",
      isShowSec: "",
      isShowRightIcon: "",
      showEditCont: '',
      showEditCont2: '',
      isClickId: "",
      popoverPosition: '',
      firstIdx: 1,
      secIdx: 1,
      showingConfirmation: false,
    };
  },
  created() {

  },
  mounted() {
    if (this.atDialogProps == undefined) {
      this.atDialogObj = {
        status: false,
      }
    }
  },
  beforeDestroy() {
    // 清理事件监听器
    document.removeEventListener('click', this.closePopoverIfClickOutside);
  },
  watch: {
    atDialogProps: {
      handler(newVal) {
        if (newVal == undefined) {

        } else {
          this.$nextTick(() => {
            this.atDialogObj = newVal
            this.removeContentEditableTipDivs()
          });
        }
      },
      deep: true,
      immediate: true,
    },
    context:{
      handler(newVal) {
        if(!newVal)
        this.removeContentEditableTipDivs()
      },
      deep: true,
      immediate: true,
    }
  },
  methods: {
    // 删除所有输入框的红色提示
    removeContentEditableTipDivs() {
      const contentEditableTipDivs = document.querySelectorAll('.contenteditabletip');
      if(contentEditableTipDivs){
        contentEditableTipDivs.forEach(div => {
          div.remove(); // 删除选中的元素
        });
        const divs= document.querySelectorAll('.message');
        divs.forEach(div => {
          div.style.border = '1px solid #BBBECA'
        });
      }
      
    },
    formatHTML(str) {
      const sessionInfo = JSON.parse(localStorage.getItem('sessionInfo'));
      if (!sessionInfo || !sessionInfo.kaccount) {
        return str;
      }
      const parser = new DOMParser();
      const doc = parser.parseFromString(str, 'text/html');
      const userSpans = doc.querySelectorAll('span[data-user]');
      userSpans.forEach(span => {
        const userData = JSON.parse(span.getAttribute('data-user'));
        if (userData.kaccount === sessionInfo.kaccount) {
          span.classList.remove('bpmAtBtn');
          span.classList.add('bpmAtSelfBtn');
        }
      });
      return doc.body.innerHTML;
    },
    handleClick(e) {
      // const screenHeight = window.innerHeight;
      // const clickY = e.clientY;
      // const distanceToBottom = screenHeight - clickY;
      // if (distanceToBottom > 500) {
      //   this.popoverPosition = 'right-start';
      // }
      // else if (distanceToBottom < 500 && distanceToBottom > 400) {
      //   this.popoverPosition = 'right';
      // } else {
      //   this.popoverPosition = 'right-end';
      // }
      //this.firstOpenPopover()
      this.atDialogObj.visible = true
    },
    getName(name, chunk, at) {
      console.log(name, chunk)
    },
    filterMatch(name, chunk, at) {
      return name.toLowerCase().indexOf(chunk.toLowerCase()) > -1
    },
    firstOpenPopover() {
      let refName = this.refNamePopover + this.atDialogObj.id
      this.$refs[refName].updatePopper()
    },
    checkShowPopover() {
      this.getApplicationComments()
      this.firstOpenPopover()
    },
    getApplicationComments() {
      let url = this.atDialogObj.isList == 1 ? 'appModel/listApplicationComments' : 'appModel/getApplicationComments'
      this.api.post(this.hostUrl + url, {
        studyId: this.$route.query.studyId, //上游返回id
        applicationId: this.$route.query.applicationId, //上游返回id
        dataId: this.atDialogObj.dataId,
        modeId: this.atDialogObj.isList == 1 ? undefined : this.codeProps,
        parentId: this.atDialogObj.isList == 1 ? this.codeProps : undefined
      })
        .then((res) => {
          this.comments = res ? res : []
          this.$nextTick(() => {
            if (this.$refs[this.refNamePopover + this.atDialogObj.id])
              this.$refs[this.refNamePopover + this.atDialogObj.id].updatePopper()
              document.addEventListener('click', this.closePopoverIfClickOutside);
          })

        })
    },
    closePopoverIfClickOutside(event) {
      if (!this.$refs[this.refNamePopover + this.atDialogObj.id].$el.contains(event.target) && this.atDialogObj.visible) {
        this.checkAndHidePopover(event)
      }
    },
    processString(str) {
      // 将字符串中的 ~...~ 替换为带样式的 @...
      // return str.replace(/~(.*?)~/g, (match, p1) => {
      //   const highlightedText = `<span class="${this.highlightClass}">@${p1}</span>`;
      //   return highlightedText;
      // });
      return str.replace(/(@[\w\s,]+)/g, (match, p1) => {
        const highlightedText = `<span class="${this.highlightClass}">${p1}</span> `;
        return highlightedText;
      });
      // return str.replace(/@(\S+)/g, (match, p1) => {
      //   const highlightedText = `<span class="${this.highlightClass}">@${p1}</span>`;
      //   return highlightedText;
      // });
    },
    checkAndHidePopover(event) {
      if (this.context != '' || this.isShowSec || this.isShowRightIcon) {
        this.setComment(event)
        this.atDialogObj.visible = true;
        if (!this.showingConfirmation) {
          this.showingConfirmation = true;
          // this.showConfirmationDialog();
        }
      } else {
        this.atDialogObj.visible = false;
      }

    },
    showConfirmationDialog() {
      this.$confirm(this.$t("language.commentInfo4"), this.$t("language.tsr"), {
        confirmButtonText: this.$t("language.Ok"),
        cancelButtonText: this.$t("language.Cancel"),
        showCancelButton: false,
        showClose: false,
        type: 'warning'
      }).then(() => {
        this.showingConfirmation = false;
      }).catch(() => {
        this.showingConfirmation = false;
      });
    },

    atDialogClick(id) {
      this.atDialogObj.visible = !this.atDialogObj.visible
      // let refName = this.refNamePopover + id
      // this.$refs[refName].doShow()
      // 切换当前点击的 atDialogPopover
      // let refName = this.refNamePopover + id
      // if (this.$refs[refName]) {
      //   this.$refs[refName].doToggle(); // 假设有一个 doToggle 方法来切换显示状态
      // }
    },
    pClose(id) {
      let refName = this.refNamePopover + id
      this.$refs[refName].doClose()
    },

    cancelButton(id, replyId, replyName, replayContent) {


      if (replyId) {
        let refName = this.editorAdd + replyId
        console.log("二级取消")
        const temp = this.comments.find(item => item.id == id).applicationCommentsModelVo;
        for (let i = 0; i < temp.length; i++) {
          if (temp[i].id == replyId) {
            temp[i].showCont = '';
            this.isShowSec = false;
            this.context = ''
            this.showEditCont2 = ''
            this.isShowRightIcon = false;
            this.replyContext = ''
            this.$refs[refName][0].innerText = ''
            this.$refs[refName][0].innerHTML = ''
            break;
          }
        }
      }
      else if (id) {
        console.log("一级取消")
        let refName = this.editorAdd + id
        for (let i = 0; i < this.comments.length; i++) {
          if (this.comments[i].id == id) {
            this.comments[i].showCont = false;
            this.showEditCont = ''
            this.isShowRightIcon = false;
            //this.isShowSec = false;
            this.isShowSec = '';
            this.isClickId = ''
            this.context = ''
            this.replyContext = ''
            this.$refs[refName][0].innerText = ''
            this.$refs[refName][0].innerHTML = ''
          }
        }
      }
      else {
        console.log("回复取消")
        this.context = ''
        this.replyContext = ''
        this.isShowRightIcon = false;
        this.isShowSec = id;
        this.isClickId = ''
        this.showEditCont = ''
        this.showEditCont2 = ''
        const editor = this.$refs.editor;
        editor.innerText = ''
        editor.innerHTML = ''
      }
    },

    isShowSecReply(id) {
      //console.log(id, 'isShowSecReply')
      if (id) {
        this.isShowSec = id;
        if (this.isClickId === this.isShowSec) {

          this.isShowSec = "";
          console.log("aaa")
        } else {
          this.isShowSec = id;
          console.log("bbb")
        }
        this.isClickId = this.isShowSec;
        this.isShowRightIcon = true;
      } else {
        console.log("ccc")
        this.isShowSec = this.isClickId = "";
      }
      this.context = ''
      console.log(this.isClickId, 'this.isClickId')
    },
    //编辑只是弹出输入框逻辑
    isShowSecEdit(id, replyId) {


      if (replyId) {
        this.EditReplyId = replyId;
        this.EditId = '';
        console.log("二级编辑", this.comments)
        const temp = this.comments.find(item => item.id == id).applicationCommentsModelVo;
        for (let i = 0; i < temp.length; i++) {
          console.log(temp[i].id, 'temp[i].id ')
          if (temp[i].id == replyId) {
            temp[i].showCont = true;
            this.isShowSec = true;
            this.isShowRightIcon = true;
            this.showEditCont2 = replyId
            let refName = this.editorEdit + temp[i].id
            this.$refs[refName][0].innerHTML = temp[i].commentsHtml;
            //break;
          }
        }
      } else {
        console.log("一级编辑")
        this.EditId = id;
        this.EditReplyId = "";
        for (let i = 0; i < this.comments.length; i++) {
          if (this.comments[i].id == id) {
            this.isShowSec = true;
            this.isShowRightIcon = true;
            this.showEditCont = id
            let refName = this.editorEdit + id
            this.$refs[refName][0].innerHTML = this.comments[i].commentsHtml;
          }
        }
      }
    },
    // 关闭
    isShowSecClose(id, replyId, status) {
      console.log(id, replyId);
      this.$confirm(status == '0' ? this.$t("language.commentInfo5") : this.$t("language.commentInfo6"))
        .then(() => {
          //done();
          if (replyId) {

            const temp = this.comments.find(item => item.id == id).applicationCommentsModelVo;
            for (let i = 0; i < temp.length; i++) {
              if (temp[i].id == replyId) {
                temp.splice(i, 1);
                break;
              }
            }
          } else {

            this.api.get(this.hostUrl + `appModel/shutDownComments/${id}?status=${status}`)
              .then((res) => {
                console.log('appModel/deleteComments', res);
                if (res == 'ok') {
                  this.updateatlist()
                  this.getApplicationComments()
                }
              })

          }
          this.atDialogObj.visible = true;
        })
        .catch(() => { });
    },
    deleteComment(id, replyId) {
      console.log(id, replyId);
      this.$confirm(this.$t("language.Areyousuretodeletethisitem"))
        .then(() => {
          //done();
          if (replyId) {

            this.api.get(this.hostUrl + `appModel/deleteComments/${replyId}`)
              .then((res) => {
                console.log('appModel/deleteComments', res);
                if (res == 'ok') {
                  this.updateatlist()
                  this.getApplicationComments()
                }
              })

          } else {

            this.api.get(this.hostUrl + `appModel/deleteComments/${id}`)
              .then((res) => {
                console.log('appModel/deleteComments', res);
                if (res == 'ok') {
                  this.updateatlist()
                  this.getApplicationComments()
                }
              })

          }
          this.atDialogObj.visible = true;
        })
        .catch(() => { });
    },
    setComment(event, item) {
      let contentEditableTipDivs = document.querySelectorAll('.contenteditabletip')
      
      const messageDivs = document.querySelectorAll('.message');
    let visibleMessageDiv = null;

    messageDivs.forEach(div => {
    let currentDiv = div;
    let isVisible = true;

    // 检查当前 div 及其所有父节点的 display 样式属性
    while (currentDiv && isVisible) {
      const displayStyle = window.getComputedStyle(currentDiv).getPropertyValue('display');
      if (displayStyle === 'none') {
        isVisible = false;
      }
      currentDiv = currentDiv.parentElement;
    }

      if (isVisible) {
          visibleMessageDiv = div;
        }
      });
      if(visibleMessageDiv && contentEditableTipDivs.length == 0){
        visibleMessageDiv.style.border = '1px solid red'; // 添加红色边框
      const tipDiv = document.createElement('div'); // 创建提示语的 div
      tipDiv.className  = 'contenteditabletip';
      tipDiv.textContent = this.$t("language.commentInfo4"); // 设置提示语
      visibleMessageDiv.insertAdjacentElement('afterend', tipDiv); // 在 messageDiv 下方插入提示语的 div
      }
      

      return

      const targetButton = event.target; // 获取当前点击的按钮
      const messageDiv = targetButton.parentElement.parentElement.previousElementSibling; // 找到父节点的兄弟节点
      console.log(messageDiv)
      if (messageDiv.classList.contains('message')) {
        messageDiv.style.border = '1px solid red'; // 添加红色边框
        const tipDiv = document.createElement('div'); // 创建提示语的 div
        tipDiv.className  = 'contenteditabletip';
        tipDiv.textContent = this.$t("language.commentInfo4"); // 设置提示语
        messageDiv.insertAdjacentElement('afterend', tipDiv); // 在 messageDiv 下方插入提示语的 div
      }
    },


    async editComment(id, replyId, replyName, replayContent,event) {
      console.log('event',event);
      if (replyName) {
        let refName = this.editorEdit + replyId
        console.log("二级编辑提交")
        if (!this.$refs[refName][0].innerHTML) {
          this.$message.warning(this.$t('language.commentInfo7'));
          
          return;
        }
        const userSpans = this.$refs[refName][0].querySelectorAll('span[data-user]');
        const userList = [];
        userSpans.forEach(span => {
          const userDataAttr = span.getAttribute('data-user');
          try {
            const userData = JSON.parse(userDataAttr);
            userList.push({
              account: userData.kaccount,
              userName: userData.userName,
              email: userData.email
            });
          } catch (error) {
            console.error('Error parsing JSON data-user attribute:', error);
          }
        });
        let data = {
          parentId: id,
          id: replyId,
          studyId: this.$route.query.studyId,
          applicationId: this.$route.query.applicationId,
          dataId: this.atDialogObj.dataId,
          modeId: this.codeProps,
          commentsHtml: this.$refs[refName][0].innerHTML,
          comments: this.formatStringWithUsers(this.$refs[refName][0].innerHTML),
          status: 0,
          applicationCommentsUsers: userList,
          emailRouting: `${window.location.href}&tocode=${this.codeProps}${this.atDialogObj.isList == 1 ? '-' + this.atDialogObj.dataId : ''}`,
        }
        console.log(data, 'data')
        this.api.post(this.hostUrl + `appModel/addApplicationComments`, data)
          .then((res) => {
            console.log('addApplicationComments', res);
            if (res == 'ok') {
              this.context = "";
              this.$refs[refName][0].innerText = '';
              this.updateatlist()
              this.getApplicationComments()
            }
          })
        const temp = this.comments.find(item => item.id == id).applicationCommentsModelVo;
        for (let i = 0; i < temp.length; i++) {
          if (temp[i].id == replyId) {
            temp[i].showCont = false;
            this.isShowRightIcon = false;
            this.isShowSec = "";
            this.showEditCont2 = ''
            break;
          }
        }
      }
      else {
        console.log("一级编辑提交")
        let refName = this.editorEdit + id
        if (!this.$refs[refName][0].innerHTML) {
          this.$message.warning(this.$t('language.commentInfo7'));
          
          return;
        }

        const userSpans = this.$refs[refName][0].querySelectorAll('span[data-user]');
        const userList = [];
        userSpans.forEach(span => {
          const userDataAttr = span.getAttribute('data-user');
          console.log(userDataAttr, 'userDataAttr')
          try {
            const userData = JSON.parse(userDataAttr);

            userList.push({
              account: userData.kaccount,
              userName: userData.userName,
              email: userData.email
            });
          } catch (error) {
            console.error('Error parsing JSON data-user attribute:', error);
          }
        });
        let data = {
          id: this.EditId,
          studyId: this.$route.query.studyId,
          applicationId: this.$route.query.applicationId,
          dataId: this.atDialogObj.dataId,
          modeId: this.codeProps,
          commentsHtml: this.$refs[refName][0].innerHTML,
          comments: this.formatStringWithUsers(this.$refs[refName][0].innerHTML),
          status: 0,
          applicationCommentsUsers: userList,
          emailRouting: `${window.location.href}&tocode=${this.codeProps}${this.atDialogObj.isList == 1 ? '-' + this.atDialogObj.dataId : ''}`,

        }
        console.log(data, 'data')
        this.api.post(this.hostUrl + `appModel/addApplicationComments`, data)
          .then((res) => {
            console.log('addApplicationComments', res);
            if (res == 'ok') {
              this.context = "";
              this.$refs[refName][0].innerText = '';
              this.updateatlist()
              this.getApplicationComments()
            }
          })
        // return
        for (let i = 0; i < this.comments.length; i++) {
          if (this.comments[i].id == id) {
            this.comments[i].showCont = false;
            this.isShowRightIcon = false;
            this.isShowSec = ''
            this.showEditCont = ''
            break;
          }
        }
      }
      //this.isShowRightIcon = this.isClickId = "";
    },
    async addCommentBottom(event) {
      const editor = this.$refs.editor;

      if (!editor.innerText) {
        this.$message.warning(this.$t('language.commentInfo7'));
        
        return;
      }
      const userSpans = editor.querySelectorAll('span[data-user]');
      const userList = [];
      userSpans.forEach(span => {
        const userDataAttr = span.getAttribute('data-user');
        try {
          const userData = JSON.parse(userDataAttr);
          console.log('userData', userData);
          userList.push({
            account: userData.kaccount,
            userName: userData.userName,
            email: userData.email
          });
        } catch (error) {
          console.error('Error parsing JSON data-user attribute:', error);
        }
      });
      let data = {
        studyId: this.$route.query.studyId,
        applicationId: this.$route.query.applicationId,
        dataId: this.atDialogObj.dataId,
        modeId: this.codeProps,
        comments: this.formatStringWithUsers(editor.innerHTML),
        status: 0,
        applicationCommentsUsers: userList,
        emailRouting: `${window.location.href}&tocode=${this.codeProps}${this.atDialogObj.isList == 1 ? '-' + this.atDialogObj.dataId : ''}`,
        commentsHtml: editor.innerHTML.replace(/[\u200B-\u200D\uFEFF]/g, '')
      }
      //  console.log(encodeURIComponent(editor.innerHTML),'111');//转码,得到结果就OK了吧,下边是解码,这里是用不到的
      //console.log(decodeURIComponent(encodeURIComponent(editor.innerHTML)),'222');
      //console.log(decodeURIComponent(editor.innerHTML), 'editor.innerHTML')

      this.api.post(this.hostUrl + `appModel/addApplicationComments`, data)
        .then((res) => {
          console.log('addApplicationComments', res);
          if (res == 'ok') {
            this.context = "";
            this.$refs.editor.innerText = '';
            this.updateatlist()
            this.getApplicationComments()
            this.firstOpenPopover()
          }
        })
      return
    },

    formatStringWithUsers(str) {
      var dataUserRegex = /<span[^>]*data-user="([^"]*)"[^>]*>@([^<]*)<\/span>/g;
      // 替换匹配到的data-user内容为指定格式
      var formattedStr = str.replace(dataUserRegex, function (match, userData, userName) {
        // 解析data-user属性中的JSON数据
        var userDataObj = JSON.parse(userData.replace(/&quot;/g, '"'));
        //var userDataObj = JSON.parse(userData);
        // 返回指定格式的文本
        return "[@" + userDataObj.userName + "]";
      });
      var htmlRegex = /<[^>]*>/g;
      // 移除 HTML 标签
      var textOnly = formattedStr.replace(htmlRegex, '');
      var htmlAndNbspRegex = /<[^>]*>|&nbsp;/g;
      // 移除 HTML 标签和 &nbsp;
      var cleanText = textOnly.replace(htmlAndNbspRegex, '');
      return cleanText;
    },
    async addComment(id, replyName,event) {
      let refName = this.editorAdd + id
      console.log(this.$refs[refName][0].innerHTML, '  this.$refs[refName][0].innerText')
      //本地更新评论列表
      if (replyName) {
        console.log("添加二级")
        // 添加二级评论
        if (!this.$refs[refName][0].innerText) {
          this.$message.warning(this.$t('language.commentInfo7'));
          
          return;
        }
        // 模拟数据提交成功后返回数据
        const userSpans = this.$refs[refName][0].querySelectorAll('span[data-user]');
        const userList = [];
        userSpans.forEach(span => {
          const userDataAttr = span.getAttribute('data-user');
          try {
            const userData = JSON.parse(userDataAttr);

            userList.push({
              account: userData.kaccount,
              userName: userData.userName,
              email: userData.email
            });
          } catch (error) {
            console.error('Error parsing JSON data-user attribute:', error);
          }
        });
        let data = {
          parentId: id,
          studyId: this.$route.query.studyId,
          applicationId: this.$route.query.applicationId,
          dataId: this.atDialogObj.dataId,
          modeId: this.codeProps,
          commentsHtml: this.$refs[refName][0].innerHTML,
          comments: this.formatStringWithUsers(this.$refs[refName][0].innerHTML),
          status: 0,
          applicationCommentsUsers: userList,
          emailRouting: `${window.location.href}&tocode=${this.codeProps}${this.atDialogObj.isList == 1 ? '-' + this.atDialogObj.dataId : ''}`,

        }
        console.log(data, 'addComment')
        this.api.post(this.hostUrl + `appModel/addApplicationComments`, data)
          .then((res) => {
            console.log('addApplicationComments', res);
            if (res == 'ok') {
              this.$refs[refName][0].innerText = '';
              this.getApplicationComments()
            }
          })
        this.context = "";
      } else {
        console.log("添加一级")
        // 添加一级评论,提交数据到后端
        if (!this.context) {
          this.$message.warning(this.$t('language.commentInfo7'));
          
          return;
        }
        const userSpans = this.$refs[refName][0].querySelectorAll('span[data-user]');
        const userList = [];
        userSpans.forEach(span => {
          const userDataAttr = span.getAttribute('data-user');
          try {
            const userData = JSON.parse(userDataAttr);

            userList.push({
              account: userData.kaccount,
              userName: userData.userName,
              email: userData.email
            });
          } catch (error) {
            console.error('Error parsing JSON data-user attribute:', error);
          }
        });
        let data = {
          parentId: id,
          id: '',
          studyId: this.$route.query.studyId,
          applicationId: this.$route.query.applicationId,
          dataId: this.atDialogObj.dataId,
          modeId: this.codeProps,
          commentsHtml: this.$refs[refName][0].innerHTML,
          comments: this.formatStringWithUsers(this.$refs[refName][0].innerHTML),
          status: 0,
          applicationCommentsUsers: userList,
          emailRouting: `${window.location.href}&tocode=${this.codeProps}${this.atDialogObj.isList == 1 ? '-' + this.atDialogObj.dataId : ''}`,

        }
        console.log(data, 'addComment')
        this.api.post(this.hostUrl + `appModel/addApplicationComments`, data)
          .then((res) => {
            console.log('addApplicationComments', res);
            if (res == 'ok') {
              this.$refs[refName][0].innerText = '';
              this.getApplicationComments()
            }
          })
        this.context = "";
      }
      this.isShowSec = this.isClickId = "";
      this.isShowRightIcon = false;
    },
    //编辑器focus
    editorFocus(id, replyId, replyName, replayContent) {
      if (replyId) {
        console.log("二级editorFocus")
      }
      else if (id) {
        console.log("一级editorFocus")
      }
      else {
        console.log("最底下editorFocus")
      }
    },
    // 限制输入框换行操作
    handleTextareaKeydown() {
      const e = window.event || arguments[0]
      if (e.key === 'Enter' || e.code === 'Enter' || e.keyCode === 13) {
        e.returnValue = false
        return false
      }
    },
    openDialog() {
      this.dialogVisible = true
    },
    // 获取光标位置
    getCursorIndex() {
      const selection = window.getSelection()
      return selection.focusOffset // 选择开始处 focusNode 的偏移量
    },
    // 获取节点
    getRangeNode() {
      const selection = window.getSelection()
      return selection.focusNode // 选择的结束节点
    },
    // 弹窗出现的位置
    getRangeRect() {
      const selection = window.getSelection()
      const range = selection.getRangeAt(0) // 是用于管理选择范围的通用对象
      const rect = range.getClientRects()[0] // 择一些文本并将获得所选文本的范围
      const LINE_HEIGHT = -100
      return {
        x: rect.x,
        y: rect.y + LINE_HEIGHT
      }
    },
    // 是否展示 @
    showAt() {
      // const node = this.getRangeNode()
      // if (!node || node.nodeType !== Node.TEXT_NODE) return false
      // const content = node.textContent || ''
      // const regx = /@([^@\s]*)$/
      // const match = regx.exec(content.slice(0, this.getCursorIndex()))
      // console.log('match', match)
      // return match && match.length === 2
      const node = this.getRangeNode();
      if (!node || node.nodeType !== Node.TEXT_NODE) return false;

      const content = node.textContent || '';
      const cursorIndex = this.getCursorIndex();

      // 从光标位置向前查找最近的一个 @ 符号
      let startIndex = content.lastIndexOf('@', cursorIndex - 1);
      if (startIndex === -1) return false; // 如果找不到 @ 符号,则返回 false

      // 从 @ 符号之后的位置开始匹配
      const match = /@([^@\s]*)$/.exec(content.slice(startIndex));

      // console.log('match', match);

      return match && match.length === 2;
    },
    // 获取 @ 用户
    getAtUser() {
      const content = this.getRangeNode().textContent || ''
      const regx = /@([^@\s]*)$/
      const match = regx.exec(content.slice(0, this.getCursorIndex()))
      if (match && match.length === 2) {
        return match[1]
      }
      return undefined
    },
    // 创建标签
    createAtButton(user) {
      const btn = document.createElement('span')
      btn.style.display = 'inline-block'
      btn.dataset.user = JSON.stringify(user)
      // if (user.kaccount == JSON.parse(localStorage.getItem('sessionInfo')).kaccount) {
      //   btn.setAttribute("data-isSelf",true)
      // }
      // else {
      //   btn.setAttribute("data-isSelf",false)
      // }
      btn.className = 'bpmAtBtn'
      btn.contentEditable = 'false'
      btn.style.whiteSpace = 'pre'
      btn.textContent = `@${user.userName}`
      const wrapper = document.createElement('span')
      wrapper.style.display = 'inline-block'
      wrapper.contentEditable = 'false'
      const spaceElem = document.createElement('span')
      spaceElem.style.whiteSpace = 'pre'
      spaceElem.textContent = '\u200b'
      spaceElem.contentEditable = 'false'
      const clonedSpaceElem = spaceElem.cloneNode(true)
      wrapper.appendChild(spaceElem)
      wrapper.appendChild(btn)
      wrapper.appendChild(clonedSpaceElem)
      return wrapper
    },
    replaceString(raw, replacer) {
      return raw.replace(/@([^@\s]*)$/, replacer)
    },
    // 插入@标签
    replaceAtUser(user) {

      const node = this.node
      if (node && user) {
        const content = node.textContent || ''
        const endIndex = this.endIndex
        const preSlice = this.replaceString(content.slice(0, endIndex), '')
        const restSlice = content.slice(endIndex)
        const parentNode = node.parentNode
        const nextNode = node.nextSibling
        const previousTextNode = new Text(preSlice)
        const nextTextNode = new Text('\u200b' + restSlice) // 添加 0 宽字符
        const atButton = this.createAtButton(user)
        parentNode.removeChild(node)
        // 插在文本框中
        if (nextNode) {
          parentNode.insertBefore(previousTextNode, nextNode)
          parentNode.insertBefore(atButton, nextNode)
          parentNode.insertBefore(nextTextNode, nextNode)
        } else {
          parentNode.appendChild(previousTextNode)
          parentNode.appendChild(atButton)
          parentNode.appendChild(nextTextNode)
        }
        // 重置光标的位置
        const range = new Range()
        const selection = window.getSelection()
        range.setStart(nextTextNode, 0)
        range.setEnd(nextTextNode, 0)
        selection.removeAllRanges()
        selection.addRange(range)
      }
    },
    // 键盘抬起事件
    handkeKeyUp(event) {
      if (event.target.innerHTML == '<br>') {
        event.target.innerText = ''
        event.target.innerHTML = ''
      }
      this.context = event.target.innerText;

      if (this.showAt()) {
        const node = this.getRangeNode()
        const endIndex = this.getCursorIndex()
        this.node = node
        this.endIndex = endIndex
        this.position = this.getRangeRect()
        this.queryString = this.getAtUser() || ''
        this.showDialog = true
      } else {
        this.showDialog = false
      }
    },
    handlePaste(event) {
      // 取消默认粘贴行为,以便手动处理
      event.preventDefault();

      // 获取粘贴的纯文本内容
      const pastedText = event.clipboardData.getData('text/plain');

      // 在这里可以进行文本过滤和格式化
      const cleanedText = this.cleanPasteText(pastedText);

      // 将清理后的文本插入到编辑器中
      document.execCommand('insertText', false, cleanedText);
    },
    cleanPasteText(text) {
      // 过滤文本,移除所有 HTML 标签和样式
      const cleanText = text.replace(/<[^>]+>/g, '');

      return cleanText;
    },
    // 键盘按下事件
    handleKeyDown(e) {
      if (this.showDialog) {
        if (e.code === 'ArrowUp' ||
          e.code === 'ArrowDown' ||
          e.code === 'Enter') {
          e.preventDefault()
        }
      }
      else {
        if (e.code === "Backspace") {
          // 获取光标位置
          const cursorIndex = this.getCursorIndex();
          // 获取文本节点内容
          const content = this.getRangeNode().textContent || '';
          // 从光标位置向前查找最近的一个 @ 符号
          const lastIndex = content.lastIndexOf('@', cursorIndex - 1);
          if (lastIndex !== -1) {
            // 获取被删除的 @ 符号
            const deletedAtSymbol = content.charAt(lastIndex);
            console.log('Deleted @ symbol:', deletedAtSymbol);
          }
        }
      }
    },
    resetAtDialog() {
      this.queryString = ''
      this.$refs.index = -1
    },
    // 插入标签后隐藏选择框
    handlePickUser(user) {
      this.replaceAtUser(user)
      this.user = user
      this.showDialog = false
    },
    // 隐藏选择框
    handleHide() {
      this.showDialog = false
    },
    // 显示选择框
    handleShow() {
      console.log('handleShow')
      this.queryString = ''
      this.$refs.index = -1
      this.showDialog = true
    },
    // 全局发送消息
    updateatlist() {
      this.$bus.$emit('updateatlist', { code: this.atDialogObj.id, dataId: this.atDialogObj.dataId })
    },

  }
};
</script>
<style>
.atDialog {
  display: flex;
  align-items: center;
}

.atDialogIcon {
  font-size: 20px !important;
  color: #8993A5;
  cursor: pointer;
  padding-top:4px;
}

.atDialogIcon:hover {
  color: #1C2242;
}

.atDialog .el-badge__content--success {
  width: 14px;
  height: 14px;
  background-color: #11B82C !important;

}

.atDialog .el-badge__content--danger {
  padding: 0px 4px;
  font-size: 10px;
  background-color: #EC3327 !important;
  border: 2px solid #f4f5fa;
  line-height: normal;
  height: auto;
}

.comment {
  max-height: 65vh;
  overflow: hidden;
  overflow-y: auto;
}


.comment-header,
.reply-comment {
  margin-top: 6px;

}

.comment-body {
  min-height: 70px;
  font-size: 14px;
}

.first-comment {
  background: #fff;
  padding: 10px;
  margin-bottom: 12px;
  border: 1px solid #ddd;
  border-radius: 4px;
}

.commentDisabled {
  background: #eee;
  border: 1px solid #eee;
}

.first-comment i {
  margin-right: 5px;
  margin-left: 8px;
  cursor: pointer;
}



.first-comment .content {
  margin-left: 10px;
  position: relative;
  flex: 1;
}

.first-comment .content>span {
  font-size: 12px;
  color: rgb(130, 129, 129);
}

.comment-right {
  position: absolute;
  right: 0;
  top: 0;
}



.second-comment {
  display: flex;
  padding: 10px 0 10px 5px;

}

.second-comment .to_reply {
  color: rgb(126, 127, 128);
}

.bpmAtBtn {
  color: #860454;
  font-size: 14px;
  margin:1px;
}

.bpmAtSelfBtn {
  color: #000000;
  font-size: 14px;
  background: #ddd;
  padding: 2px;
  border-radius: 2px;
  margin:1px;
}

.cancelButton,
.cancelButton:hover {
  font-size: 13px;
  color: #000;
  border-radius: 4px;
  border: 1px solid #BBBECA;
  padding: 8px 10px;
  background: #fff;
}

.addComment,
.addComment:hover,
.addComment:focus,
.reply-button,
.reply-button:hover,
.reply-button:focus {
  font-size: 13px;
  background: #1C2242;
  color: #fff;
  padding: 8px 10px;
  border-radius: 4px;
  border: 1px solid #BBBECA
}

.comment-header-button {
  margin-top: 10px;
  display: flex;
  justify-content: flex-end;
}

.avatar {
  width: 24px;
  height: 24px;
  border-radius: 24px;
  background: #1C2242;
  color: #fff;
  text-align: center;
  line-height: 24px;
  font-size: 12px;
}

.avatar1 {

  margin-left: 6px;
  line-height: 18px;

}

.atName {
  margin-left: 6px;
}

.itemUserName {
  font-size: 16px;
  color: #333;
  margin-bottom: 4px;
}

.comment-left {
  font-size: 14px;
  color: #666;
  float: left;
}

.itemContent {
  font-size: 14px;
  color: rgba(0, 0, 0, 0.8);
  margin-bottom: 4px;
  text-align: left;

}

.commentDisabled .itemContent {
  color: rgba(0, 0, 0, 0.5);
}

.itemDate {
  font-size: 14px;
  color: rgba(0, 0, 0, 0.4);
  margin-bottom: 4px;
}

.elPopoverHeader {
  padding: 8px 0;
  display: flex;
  justify-content: space-between;
  align-items: center;
  color: #000;
}

.atDialogPopover {
  padding-top: 0px;
  /* z-index: 9 !important; */
}

.textUnderLine {
  text-decoration: underline;
  text-underline-offset: 5px;
}

.message {
  text-align: left;
  display: block;
  resize: vertical;
  padding: 5px 15px;
  line-height: 1.5;
  -webkit-box-sizing: border-box;
  box-sizing: border-box;
  width: 100%;
  font-size: 14px;
  color: #606266;
  background-color: #FFFFFF;
  background-image: none;
  border: 1px solid #BBBECA;
  border-radius: 4px;
  -webkit-transition: border-color 0.2s cubic-bezier(0.645, 0.045, 0.355, 1);
  transition: border-color 0.2s cubic-bezier(0.645, 0.045, 0.355, 1);
  position: relative;

}
.message:focus-visible{
  border:1px solid #BBBECA!important;
}
/* .message::after{
  content: "111111";
  position: absolute;
  white-space: pre;
  width: 0;
  height: 0;

}  */
.comment-body-title {
  font-size: 15px;
  color: #000;
  background: #f0f0f0;
  height: 40px;
  line-height: 20px;
  padding: 10px;
  border-radius: 4px 4px 0 0;
  border: 1px solid #ddd;
  border-bottom: 0;
}

.first-comment-list {
  border-radius: 0 0 4px 4px;
  padding: 10px;
}

.comment-list {
  margin-bottom: 20px;
}

.comment-list:last-child {
  margin-bottom: 0
}

.islist-list {
  background: #fff;
  padding: 20px;

  border: 1px solid #ddd;
  border-radius: 4px;
  margin-bottom: 12px;
  padding-top: 10px;

}

.islist-list:last-child {
  margin-bottom: 0;
}

.islist-list .textUnderLine {
  margin-bottom: 10px;
}

.commentDisabled.islist-list {
  background: #eee;
  border: 1px solid #eee;
}

.emptyView {
  display: flex;
  align-items: center;
  flex-direction: column;
  justify-content: center;
  padding: 20px;
}
.contenteditabletip{
  color:red;
  padding-top:3px;
}
</style>

atDialogList.vue

<template>
  <div class="wrapper" :style="{ position: 'fixed', top: position.y + 'px', left: position.x + 'px' }">
    <div v-if="!mockList.length" class="empty">
      <p>无搜索结果</p>
    </div>
    <div v-for="(item, i) in mockList" :key="item.kaccount" ref="usersRef" class="mockItem"
      :class="{ 'active': i === index }" @click="clickAt($event, item)" @mouseenter="hoverAt(i)">
      <div class="mockLiView">
        <div class="userName">{{ item.userName }}</div>
        <div>{{ item.email }}</div>
      </div>
    </div>
  </div>
</template>

<script>
export default {

  name: 'atDialogList',
  props: {
    visible: {
      type: Boolean
    },
    position: {
      type: Object,
      default: () => { }
    },
    queryString: {
      type: String,
      default: ''
    }
  },
  data() {
    return {
      users: [],
      mockData: [],
      index: -1,
      mockList: [],
      mockData: []
    }
  },
  watch: {
    queryString(val) {
      if (val) {
        // 去除字符串两端的空格
        val = val.trim();

        // 根据逗号分隔搜索条件
        const searchTerms = val.split(',').map(term => term.trim());

        // 创建大小写不敏感的正则表达式
        const regexes = searchTerms.map(term => new RegExp(term, 'i'));

        this.mockList = this.mockData.filter(({ userName, email }) => {
          // 检查每个搜索条件是否匹配
          return regexes.every(regex => regex.test(userName) || regex.test(email));
        });
      } else {
        this.mockList = this.mockData.slice(0);
      }
      // if (val) {
      //   const regex = new RegExp(val, 'i'); // 创建大小写不敏感的正则表达式
      //   this.mockList = this.mockData.filter(({ userName, email }) =>
      //     regex.test(userName) || regex.test(email)
      //   );
      // } else {
      //   this.mockList = this.mockData.slice(0);
      // }
      // if (val) {
      //   val = val.toLowerCase();
      //   this.mockList = this.mockData.filter(({ userName, email }) =>
      //     userName.toLowerCase().startsWith(val) || email.toLowerCase().startsWith(val)
      //   );
      // } else {
      //   this.mockList = this.mockData.slice(0);
      // }
    }
  },
  mounted() {
    document.addEventListener('keyup', this.keyDownHandler)
    this.getListApplicationCommentsUserName()
  },
  destroyed() {
    document.removeEventListener('keyup', this.keyDownHandler)
  },
  methods: {
    getListApplicationCommentsUserName() {
      let data = {
        studyId: this.$route.query.studyId,
      }
      this.api.post(this.hostUrl + `appModel/listApplicationCommentsUserName`, data)
        .then((res) => {
          this.mockList = res ? res : []
          this.mockData = res ? res : []
        })
    },

    closeDialog() {
      this.index = -1
      this.$emit('close')
    },
    keyDownHandler(e) {

      if (e.code === 'Escape') {
        this.$emit('onHide')
        return
      }
      // 键盘按下 => ↓
      if (e.code === 'ArrowDown') {
        if (this.index >= this.mockList.length - 1) {
          this.index = 0
        } else {
          this.index = this.index + 1
        }
      }
      // 键盘按下 => ↑
      if (e.code === 'ArrowUp') {
        if (this.index <= 0) {
          this.index = this.mockList.length - 1
        } else {
          this.index = this.index - 1
        }
      }
      // 键盘按下 => 回车
      if (e.code === 'Enter') {
        if (this.mockList.length) {
          const user = {
            userName: this.mockList[this.index].userName,
            kaccount: this.mockList[this.index].kaccount,
            email: this.mockList[this.index].email
          }
          console.log(user, 'onPickUser')
          this.$emit('onPickUser', user)
          this.index = -1
        }
      }
    },
    clickAt(e, item) {
      const user = {
        userName: item.userName,
        kaccount: item.kaccount,
        email: item.email,
      }
      console.log(user, 'clickAt')
      this.$emit('onPickUser', user)
      // 从 mockData 中移除选中的项
      // const index = this.mockList.findIndex(user => user.id === item.id);
      // if (index !== -1) {
      //   this.mockList.splice(index, 1);
      // }
      // 从 mockData 中移除选中的项

      this.index = -1
    },
    hoverAt(index) {
      this.index = index
    }
  }
}
</script>

<style>
.wrapper {
  width: 238px;
  border: 1px solid #e4e7ed;
  border-radius: 4px;
  background-color: #fff;
  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
  box-sizing: border-box;
  padding: 6px 0;
  max-height: 200px;
  overflow: hidden;
  overflow-y: auto;
}

.empty {
  font-size: 14px;
  padding: 0 20px;
  color: #999;
  height: 24px;
  line-height: 24px;
  position: relative;
}

.empty svg {
  position: absolute;
  width: 18px;
  height: 18px;
  right: 6px;
  top: 4px;
  cursor: pointer;
}

.mockItem {
  font-size: 14px;
  padding: 1px 11px;
  line-height: 20px;
  cursor: pointer;
  color: #606266;
  margin-bottom: 7px;
}

.mockItem.active {
  background: #eee;
  color: #860454;
}

.mockItem.active .id {
  color: #860454;
}

.mockItem:first-child {
  border-radius: 5px 5px 0 0;
}

.mockItem:last-child {
  border-radius: 0 0 5px 5px;
}

.userName {
  font-size: 12px;
  font-weight: bold;
}
</style>

 <atDialog ref="adDialogRef" :codeProps="$to('code338')" :atDialogProps="atDialogList[0]" :key="Math.random()"></atDialog>

相关文章

  • 人的功能

    人的功能是指人体能发挥的正常的有用的作用。 通俗地说,功能就是“动”。脏腑的“动”就是脏腑的功能。人...

  • Telegram机器人功能

    数字货币市场是一个每天24小时全时段无缝对接的市场,价格瞬息万变。贴近市场,把握市场脉搏,我们才能在这个市场上更加...

  • 如果人有删除功能

    日子一天天在过,经常会惆怅,惆怅那些曾经想往的美好基本无法实现。 我们一天天忙碌着,忙碌到忘记来的路,那一路的期待...

  • 未来的多功能课桌

    “多功能”三个字大家肯定是耳熟能详 ,多功能的电话手表、多功能的机器人、多功能的按摩椅……课桌也可以有很多的功能,...

  • 易智美AI头疗机具有哪些功能?

    1)易智美AI人工智能头部理疗机器人具有红光理疗功能:红光理疗功能是为了刺激头皮的血液循环的功能; 2)水疗功能:...

  • 人乳汁的功能与作用

    人乳汁 人乳汁 - 化学成分 每100克人乳汁含水分88克、蛋白质1.5克、脂肪3.7克、碳水化物6.4克、灰分0...

  • 如果人的记忆功能丧失

    如果人多的记忆功能丧失,你最想挽回些什么?人的记忆可以存多久呢?我想一个人突然丧失记忆会有几种可能呢?会是意外失...

  • 解码人胎儿肝脏造血功能

    单细胞入门-读一篇scRNA-seq综述[http://mp.weixin.qq.com/s?__biz=MzI1...

  • 这些功能让人上饮

    但我们不自知。纵然知道来龙去脉后,又有几个人会醒过来? 社交媒体被科技公司设计出来的许多功能,目的只有一个~ 都在...

  • CAD绘图大部份人不会用的超级刷子功能!

    CAD绘图大部份人不会用的超级刷子功能! CAD绘图大部份人不会用的超级刷子功能!

网友评论

      本文标题:Vue@人功能

      本文链接:https://www.haomeiwen.com/subject/xevxkjtx.html