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(/"/g, '"'));
//var userDataObj = JSON.parse(userData);
// 返回指定格式的文本
return "[@" + userDataObj.userName + "]";
});
var htmlRegex = /<[^>]*>/g;
// 移除 HTML 标签
var textOnly = formattedStr.replace(htmlRegex, '');
var htmlAndNbspRegex = /<[^>]*>| /g;
// 移除 HTML 标签和
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>
网友评论