fix:完善评论组件@圈人功能

This commit is contained in:
liangdong
2026-01-05 21:25:45 +08:00
parent 822c4e9f90
commit b03db2d89e
5 changed files with 346 additions and 61 deletions

View File

@@ -2,35 +2,40 @@
<div class="comment-app">
<section class="main-publisher">
<div class="input-wrapper">
<el-input
v-model="mainInput"
type="textarea"
:rows="4"
:placeholder="t('comment.placeholder')"
resize="none"
/>
<div class="input-tools">
<div class="left-icons">
<!-- 提到-后续迭代 -->
<!-- <el-button link title="提到">@</el-button> -->
<!-- 图片功能-后续迭代 -->
<!-- <el-button link title="图片"><el-icon><Picture /></el-icon></el-button> -->
<!-- 附件功能-后续迭代 -->
<!-- <el-button link title="附件"><el-icon><Paperclip /></el-icon></el-button> -->
<!-- 表情功能 -->
<emoji-picker @select="(e) => onSelectEmoji(e, 'main')" />
<mentionEditor v-model="mainInput" :users="userList" @select="onUserMentioned">
<el-input
v-model="mainInput"
type="textarea"
:rows="4"
:placeholder="t('comment.placeholder')"
resize="none"
/>
</mentionEditor>
<div class="input-tools">
<div class="left-icons">
<!-- 提到-后续迭代 -->
<!-- <el-button link title="提到">@</el-button> -->
<!-- 图片功能-后续迭代 -->
<!-- <el-button link title="图片"><el-icon><Picture /></el-icon></el-button> -->
<!-- 附件功能-后续迭代 -->
<!-- <el-button link title="附件"><el-icon><Paperclip /></el-icon></el-button> -->
<!-- 表情功能 -->
<emoji-picker @select="(e) => onSelectEmoji(e, 'main')" />
</div>
<el-divider direction="vertical" />
<el-button
class="send-btn"
type="primary"
link
@click="submitMainComment"
>
<el-icon :size="20" :title="t('comment.send')"
><Promotion
/></el-icon>
</el-button>
</div>
<el-divider direction="vertical" />
<el-button
class="send-btn"
type="primary"
link
@click="submitMainComment"
>
<el-icon :size="20" :title="t('comment.send')"><Promotion /></el-icon>
</el-button>
</div>
</div>
</section>
@@ -127,7 +132,7 @@
</div>
<span class="time">{{ formatTime(reply.time) }}</span>
</div>
<div class="content-body">{{ reply.content }}</div>
<div class="content-body" v-html="parseMention(reply.content)"></div>
<!-- 回复 删除功能 -->
<div class="actions">
<el-button link @click="openReply(item, item)">
@@ -153,13 +158,17 @@
class="inline-publisher"
>
<div class="input-wrapper">
<mentionEditor v-model="replyInput" :users="userList" @select="onUserMentioned">
<el-input
v-model="replyInput"
:placeholder="`${t('comment.reply')} @${activeReply.targetName}...`"
:placeholder="`${t('comment.reply')} @${
activeReply.targetName
}...`"
type="textarea"
:autosize="{ minRows: 2, maxRows: 4 }"
resize="none"
/>
</mentionEditor>
<div class="input-tools">
<div class="left-icons">
<!-- 表情功能 -->
@@ -174,7 +183,9 @@
link
@click="submitReply"
>
<el-icon :size="20" :title="t('comment.send')"><Promotion /></el-icon>
<el-icon :size="20" :title="t('comment.send')"
><Promotion
/></el-icon>
</el-button>
</div>
</div>
@@ -194,8 +205,9 @@ import EmojiPicker from "./EmojiPicker.vue";
import NameAvatar from "@/components/nameAvatar/index.vue";
import { useI18n } from "vue-i18n";
import { useUserStore } from "@/store";
import { useRelativeTime } from '@/hooks/useRelativeTime'
const { formatTime } = useRelativeTime()
import { useRelativeTime } from "@/hooks/useRelativeTime";
import mentionEditor from "./mentionEditor.vue";
const { formatTime } = useRelativeTime();
const userStore = useUserStore();
const { t } = useI18n();
// 当前用户信息
@@ -272,47 +284,60 @@ const cancelReply = () => {
activeReply.groupId = null;
};
// @ 识别解析函数
const parseMention = (text) => {
if (!text) return "";
// 基础转义
let safeText = text.replace(/</g, "&lt;").replace(/>/g, "&gt;");
// 正则匹配 @用户,包裹为 span
return safeText.replace(
/@([\u4e00-\u9fa5\w-]+)/g,
'<span class="mention-link">@$1</span>'
);
};
// @提及 圈人操作
const handleMentionAction = (name) => {
const mentionStr = typeof name === "string" ? `@${name} ` : "@";
mainInput.value += mentionStr;
// @圈人的操作
const userList = [
{ id: 1, nickname: "李星倩" },
{ id: 2, nickname: "冯娜" },
{ id: 3, nickname: "张三" }
];
const onUserMentioned = (user) => {
console.log('Mentioned:', user.nickname);
};
const submitMainComment = () => {
if (!mainInput.value.trim()) return ElMessage.warning("内容不能为空");
const formattedContent = mainInput.value.replace(
/@([^\s@]+)/g,
'<span class="mention-highlight">@$1</span>'
);
commentData.value.unshift({
id: Date.now(),
...currentUser.value,
content: mainInput.value,
content: formattedContent,
time: new Date().valueOf(),
canDelete: true,
children: [],
});
mainInput.value = "";
};
const submitReply = () => {
const targetGroup = commentData.value.find(
(i) => i.id === activeReply.groupId
// 通用的内容转换函数
const parseMention = (text) => {
if (!text) return "";
return text.replace(
/@([^\s@]+)/g,
'<span class="mention-highlight">@$1</span>'
);
};
const submitReply = () => {
const targetGroup = commentData.value.find(i => i.id === activeReply.groupId);
if (targetGroup) {
const formattedReply = replyInput.value.replace(
/@([^\s@]+)/g,
'<span class="mention-highlight">@$1</span>'
);
targetGroup.children.push({
id: Date.now(),
...currentUser.value,
replyTo: activeReply.targetName,
content: replyInput.value,
time: "刚刚",
content: formattedReply, // 使用处理后的 HTML
time: new Date().valueOf(),
});
cancelReply();
}
@@ -506,6 +531,23 @@ $color-white: #fff;
margin-top: 8px;
}
}
/* 在 style 标签内添加 */
:deep(.mention-highlight) {
background-color: rgba(64, 158, 255, 0.1); /* 浅蓝色背景 */
color: #409eff; /* 蓝色文字 */
padding: 2px 4px;
border-radius: 4px;
font-weight: 500;
margin: 0 2px;
cursor: pointer;
display: inline-block;
&:hover {
background-color: rgba(64, 158, 255, 0.2);
text-decoration: underline;
}
}
}
// 评论组件骨架屏