🎨 update comment

This commit is contained in:
landaiqing
2024-10-02 01:08:29 +08:00
parent a626704b2b
commit 40f2f0b2de
8 changed files with 105 additions and 46 deletions

View File

@@ -2,7 +2,7 @@
<div class="comment">
<AFlex :vertical="false">
<AFlex :vertical="true">
<AAvatar :size="50" shape="circle" src="https://api.multiavatar.com/Starcrasher.svg"/>
<AAvatar :size="50" class="comment-avatar" shape="circle" src="https://api.multiavatar.com/Starcrasher.svg"/>
</AFlex>
<AFlex :vertical="true" class="comment-content">
<ATextarea :rows="4" class="comment-text" @focus="onFocusHandler"
@@ -28,7 +28,8 @@
<AButton @click="insertEmoji(item.name)" type="text" shape="circle" size="large"
class="comment-emoji-item">
<template #icon>
<AAvatar shape="circle" size="default" :src="item.path"/>
<img :width="35" :height="35" loading="lazy" :src="item.path" v-lazy-load
:alt="item.name"/>
</template>
</AButton>
</template>
@@ -46,7 +47,8 @@
<AButton @click="insertEmoji(item.name)" type="text" shape="default" size="large"
class="comment-emoji-item" style="width: 75px;height: 75px;">
<template #icon>
<AAvatar shape="square" :size="70" :src="item.path"/>
<img :width="70" :height="70" loading="lazy" :src="item.path" v-lazy-load
:alt="item.name"/>
</template>
</AButton>
</template>
@@ -62,7 +64,7 @@
</AFlex>
<AFlex :vertical="false" align="center" class="comment-action-item">
<AUpload
:accept="'image/jpg, image/png, image/jpeg, image/gif, image/svg+xml, image/webp'"
:accept="'image/jpg, image/png, image/jpeg'"
name="images"
:max-count="3"
:multiple="true"
@@ -164,7 +166,13 @@ async function onFocusHandler() {
* @param emoji
*/
async function insertEmoji(emoji: string) {
commentContent.value += "[" + emoji + "]";
if (emojiType.value === "qq") {
commentContent.value += "[" + emoji + "]";
} else if (emojiType.value === "lottie") {
commentContent.value += ":" + emoji + ":";
} else {
return;
}
}
@@ -186,20 +194,18 @@ async function commentSubmit(point: any) {
return;
}
const content = commentContent.value.replace(/\r\n/g, '<br/>').replace(/\n/g, '<br/>').replace(/\s/g, ' ');
const regex = /\[((1[0-6][0-6]|[1-9]?[0-9])\.gif)\]/g; // 匹配 [1.gif] 的字符串
const contentWithEmoji = content.replace(regex, (match, p1) => {
if (emojiType.value === "qq") {
return `<img style="width: 30px; height: 30px;" src="/emoji/qq/gif/${p1}" alt="emoji ${p1}" />`;
} else if (emojiType.value === "lottie") {
return `<img style="width: 80px; height: 80px;" src="/emoji/qq/lottie/${p1}" alt="emoji ${p1}" />`;
} else {
return match;
}
const regex = /\[((1[0-6][0-6]|[1-9]?[0-9])\.gif)]/g; // 匹配 [1.gif] 的字符串
const contentWithEmoji = content.replace(regex, (_match, p1) => {
return `<img style="width: 30px; height: 30px;" loading="lazy" src="/emoji/qq/gif/${p1}" alt="emoji ${p1}" />`;
});
const regexWithLottieEmoji = /:((1[0-0-8]|[1-9]?[0-9])\.gif):/g; // 匹配 :1.gif: 的字符串
const contentWithLottieEmoji = contentWithEmoji.replace(regexWithLottieEmoji, (_match, p1) => {
return `<img style="width: 80px; height: 80px;" loading="lazy" src="/emoji/qq/lottie/${p1}" alt="emoji ${p1}" />`;
});
const commentParams: object = {
user_id: user.user.uid,
topic_id: topicId.value,
content: contentWithEmoji,
content: contentWithLottieEmoji,
images: comment.imageList,
author: user.user.uid,
point: [point.x, point.y],

View File

@@ -1,11 +1,16 @@
.comment-avatar {
cursor: pointer;
}
.comment-content {
margin-left: 20px;
.comment-text {
width: 600px;
}
.comment-editor{
.comment-editor {
border: 1px solid #d9d9d9;
border-radius: 8px;
padding: 10px;

View File

@@ -26,7 +26,7 @@
<AFlex :vertical="false" style="margin-top: 5px">
<!-- 评论头像 -->
<AFlex :vertical="true" class="reply-avatar" v-if="item.avatar">
<AAvatar :size="50" shape="circle" :src="item.avatar"/>
<AAvatar :size="50" class="reply-avatar-img" shape="circle" :src="item.avatar"/>
</AFlex>
<!-- 评论内容 -->
<AFlex :vertical="true" class="reply-content">

View File

@@ -9,6 +9,10 @@
.reply-list {
margin-top: 30px;
.reply-avatar-img {
cursor: pointer;
}
.reply-pagination {
display: flex;
justify-content: flex-end;

View File

@@ -37,7 +37,8 @@
<AButton @click="insertEmojiToReplyContent(item.name)" type="text" shape="circle" size="large"
class="comment-emoji-item">
<template #icon>
<AAvatar shape="circle" size="default" :src="item.path"/>
<img :width="35" :height="35" loading="lazy" :src="item.path" v-lazy-load
:alt="item.name"/>
</template>
</AButton>
</template>
@@ -56,7 +57,8 @@
size="large"
class="comment-emoji-item" style="width: 75px;height: 75px;">
<template #icon>
<AAvatar shape="square" :size="70" :src="item.path"/>
<img :width="70" :height="70" loading="lazy" :src="item.path" v-lazy-load
:alt="item.name"/>
</template>
</AButton>
</template>
@@ -73,7 +75,7 @@
</AFlex>
<AFlex :vertical="false" align="center" class="comment-action-item-reply">
<AUpload
:accept="'image/jpg, image/png, image/jpeg, image/gif, image/svg+xml, image/webp'"
:accept="'image/jpg, image/png, image/jpeg'"
name="images"
:max-count="3"
:multiple="true"
@@ -178,7 +180,13 @@ async function changeEmojiType(type: string) {
* @param emoji
*/
async function insertEmojiToReplyContent(emoji: string) {
replyContent.value += "[" + emoji + "]";
if (emojiType.value === "qq") {
replyContent.value += "[" + emoji + "]";
} else if (emojiType.value === "lottie") {
replyContent.value += ":" + emoji + ":";
} else {
return;
}
}
@@ -201,14 +209,12 @@ async function replySubmit(point: any) {
}
const content = replyContent.value.replace(/\r\n/g, '<br/>').replace(/\n/g, '<br/>').replace(/\s/g, ' ');
const regex = /\[((1[0-6][0-6]|[1-9]?[0-9])\.gif)\]/g; // 匹配 [1.gif] 的字符串
const contentWithEmoji = content.replace(regex, (match, p1) => {
if (emojiType.value === "qq") {
return `<img style="width: 30px; height: 30px;" src="/emoji/qq/gif/${p1}" alt="emoji ${p1}" />`;
} else if (emojiType.value === "lottie") {
return `<img style="width: 80px; height: 80px;" src="/emoji/qq/lottie/${p1}" alt="emoji ${p1}" />`;
} else {
return match;
}
const contentWithEmoji = content.replace(regex, (_match, p1) => {
return `<img style="width: 30px; height: 30px;" loading="lazy" src="/emoji/qq/gif/${p1}" alt="emoji ${p1}" />`;
});
const regexWithLottieEmoji = /\:((1[0-0-8]|[1-9]?[0-9])\.gif)\:/g; // 匹配 :1.gif: 的字符串
const contentWithLottieEmoji = contentWithEmoji.replace(regexWithLottieEmoji, (_match, p1) => {
return `<img style="width: 80px; height: 80px;" loading="lazy" src="/emoji/qq/lottie/${p1}" alt="emoji ${p1}" />`;
});
const replyParams: {
images: any;
@@ -223,7 +229,7 @@ async function replySubmit(point: any) {
} = {
user_id: user.user.uid,
topic_id: topicId.value,
content: contentWithEmoji,
content: contentWithLottieEmoji,
images: comment.imageList,
author: user.user.uid,
reply_id: props.item.id,

View File

@@ -37,10 +37,12 @@
</template>
<div style="width: 250px;height: 200px;overflow: auto;">
<template v-for="(item) in comment.emojiList" :key="item">
<AButton @click="insertEmojiToReplyReplyContent(item.name)" type="text" shape="circle" size="large"
<AButton @click="insertEmojiToReplyReplyContent(item.name)" type="text" shape="circle"
size="large"
class="comment-emoji-item">
<template #icon>
<AAvatar shape="circle" size="default" :src="item.path"/>
<img :width="35" :height="35" loading="lazy" :src="item.path" v-lazy-load
:alt="item.name"/>
</template>
</AButton>
</template>
@@ -55,10 +57,12 @@
<div style="width: 250px;height: 200px;overflow: auto;">
<AFlex :vertical="false" align="center" justify="space-between" wrap="wrap">
<template v-for="(item) in comment.lottieEmojiList" :key="item">
<AButton @click="insertEmojiToReplyReplyContent(item.name)" type="text" shape="default" size="large"
<AButton @click="insertEmojiToReplyReplyContent(item.name)" type="text" shape="default"
size="large"
class="comment-emoji-item" style="width: 75px;height: 75px;">
<template #icon>
<AAvatar shape="square" :size="70" :src="item.path"/>
<img :width="70" :height="70" loading="lazy" :src="item.path" v-lazy-load
:alt="item.name"/>
</template>
</AButton>
</template>
@@ -75,7 +79,7 @@
</AFlex>
<AFlex :vertical="false" align="center" class="comment-action-item-reply-child">
<AUpload
:accept="'image/jpg, image/png, image/jpeg, image/gif, image/svg+xml, image/webp'"
:accept="'image/jpg, image/png, image/jpeg'"
name="images"
:max-count="3"
:multiple="true"
@@ -183,12 +187,19 @@ const emojiType = ref<string>("qq");
async function changeEmojiType(type: string) {
emojiType.value = type;
}
/**
* 插入表情到回复内容
* @param emoji
*/
async function insertEmojiToReplyReplyContent(emoji: string) {
replyReplyContent.value += "[" + emoji + "]";
if (emojiType.value === "qq") {
replyReplyContent.value += "[" + emoji + "]";
} else if (emojiType.value === "lottie") {
replyReplyContent.value += ":" + emoji + ":";
} else {
return;
}
}
@@ -210,20 +221,18 @@ async function replyReplySubmit(point: any) {
return;
}
const content = replyReplyContent.value.replace(/\r\n/g, '<br/>').replace(/\n/g, '<br/>').replace(/\s/g, ' ');
const regex = /\[((1[0-6][0-6]|[1-9]?[0-9])\.gif)\]/g; // 匹配 [1.gif] 的字符串
const contentWithEmoji = content.replace(regex, (match, p1) => {
if (emojiType.value === "qq") {
return `<img style="width: 30px; height: 30px;" src="/emoji/qq/gif/${p1}" alt="emoji ${p1}" />`;
} else if (emojiType.value === "lottie") {
return `<img style="width: 80px; height: 80px;" src="/emoji/qq/lottie/${p1}" alt="emoji ${p1}" />`;
} else {
return match;
}
const regex = /\[((1[0-6][0-6]|[1-9]?[0-9])\.gif)]/g; // 匹配 [1.gif] 的字符串
const contentWithEmoji = content.replace(regex, (_match, p1) => {
return `<img style="width: 30px; height: 30px;" loading="lazy" src="/emoji/qq/gif/${p1}" alt="emoji ${p1}" />`;
});
const regexWithLottieEmoji = /:((1[0-0-8]|[1-9]?[0-9])\.gif):/g; // 匹配 :1.gif: 的字符串
const contentWithLottieEmoji = contentWithEmoji.replace(regexWithLottieEmoji, (_match, p1) => {
return `<img style="width: 80px; height: 80px;" loading="lazy" src="/emoji/qq/lottie/${p1}" alt="emoji ${p1}" />`;
});
const replyParams: ReplyCommentParams = {
user_id: user.user.uid,
topic_id: topicId.value,
content: contentWithEmoji,
content: contentWithLottieEmoji,
images: comment.imageList,
author: user.user.uid,
reply_to: props.child.id,

View File

@@ -0,0 +1,26 @@
const defaultImg: string = 'data:image/png;base64,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'
import {useIntersectionObserver} from '@vueuse/core';
export default {
mounted(el: HTMLImageElement) {
//如果不是图片的话就不触发懒加载
if (!(el instanceof HTMLImageElement)) return;
// 把真正的地址暂存起来
const _src = el.src;
//设置个缺省图片
el.setAttribute('src', defaultImg);
//如果你不想用vueuse,你也可以用原生的IntersectionObserver接口来实现
const {stop} = useIntersectionObserver(el, ([{isIntersecting}]) => {
if (isIntersecting) {
//说明到视口中了,那就把真实的地址还原回去
el.setAttribute('src', _src);
//同时关闭Observer观察
stop();
}
});
},
};

View File

@@ -9,11 +9,14 @@ import GoCaptcha from "go-captcha-vue";
import {createPinia, Pinia} from "pinia";
import piniaPluginPersistedstate from "pinia-plugin-persistedstate";
import clickOutside from '@/directives/v-click-outside.ts';
import lazyLoad from "@/directives/v-lazy-load.ts";
import VueDOMPurifyHTML from 'vue-dompurify-html';
const pinia: Pinia = createPinia();
pinia.use(piniaPluginPersistedstate);
const app = createApp(App);
app.directive('click-outside', clickOutside);
app.directive('lazy-load', lazyLoad);
app.use(pinia);
app.use(router);
app.use(i18n);