🎨 organize code structure
This commit is contained in:
23
components.d.ts
vendored
23
components.d.ts
vendored
@@ -19,6 +19,7 @@ declare module 'vue' {
|
|||||||
AForm: typeof import('ant-design-vue/es')['Form']
|
AForm: typeof import('ant-design-vue/es')['Form']
|
||||||
AFormItem: typeof import('ant-design-vue/es')['FormItem']
|
AFormItem: typeof import('ant-design-vue/es')['FormItem']
|
||||||
AImage: typeof import('ant-design-vue/es')['Image']
|
AImage: typeof import('ant-design-vue/es')['Image']
|
||||||
|
AImagePreviewGroup: typeof import('ant-design-vue/es')['ImagePreviewGroup']
|
||||||
AInput: typeof import('ant-design-vue/es')['Input']
|
AInput: typeof import('ant-design-vue/es')['Input']
|
||||||
AInputPassword: typeof import('ant-design-vue/es')['InputPassword']
|
AInputPassword: typeof import('ant-design-vue/es')['InputPassword']
|
||||||
AMenu: typeof import('ant-design-vue/es')['Menu']
|
AMenu: typeof import('ant-design-vue/es')['Menu']
|
||||||
@@ -35,17 +36,17 @@ declare module 'vue' {
|
|||||||
ATextarea: typeof import('ant-design-vue/es')['Textarea']
|
ATextarea: typeof import('ant-design-vue/es')['Textarea']
|
||||||
ATooltip: typeof import('ant-design-vue/es')['Tooltip']
|
ATooltip: typeof import('ant-design-vue/es')['Tooltip']
|
||||||
AUpload: typeof import('ant-design-vue/es')['Upload']
|
AUpload: typeof import('ant-design-vue/es')['Upload']
|
||||||
BoxDog: typeof import('./src/Components/BoxDog/BoxDog.vue')['default']
|
BoxDog: typeof import('./src/components/BoxDog/BoxDog.vue')['default']
|
||||||
Card3D: typeof import('./src/Components/Card3D/Card3D.vue')['default']
|
Card3D: typeof import('./src/components/Card3D/Card3D.vue')['default']
|
||||||
CloseCircleOutlined: typeof import('@ant-design/icons-vue')['CloseCircleOutlined']
|
CloseCircleOutlined: typeof import('@ant-design/icons-vue')['CloseCircleOutlined']
|
||||||
Clouds: typeof import('./src/Components/Clouds/Clouds.vue')['default']
|
Clouds: typeof import('./src/components/Clouds/Clouds.vue')['default']
|
||||||
CommentInput: typeof import('./src/Components/CommentReply/Components/CommentInput/CommentInput.vue')['default']
|
CommentInput: typeof import('./src/components/CommentReply/Components/CommentInput/CommentInput.vue')['default']
|
||||||
CommentList: typeof import('./src/Components/CommentReply/Components/CommentList/CommentList.vue')['default']
|
CommentList: typeof import('./src/components/CommentReply/Components/CommentList/CommentList.vue')['default']
|
||||||
CommentReply: typeof import('./src/Components/CommentReply/index.vue')['default']
|
CommentReply: typeof import('./src/components/CommentReply/index.vue')['default']
|
||||||
CopyOutlined: typeof import('@ant-design/icons-vue')['CopyOutlined']
|
CopyOutlined: typeof import('@ant-design/icons-vue')['CopyOutlined']
|
||||||
DeleteOutlined: typeof import('@ant-design/icons-vue')['DeleteOutlined']
|
DeleteOutlined: typeof import('@ant-design/icons-vue')['DeleteOutlined']
|
||||||
DynamicTitle: typeof import('./src/Components/DynamicTitle/DynamicTitle.vue')['default']
|
DynamicTitle: typeof import('./src/components/DynamicTitle/DynamicTitle.vue')['default']
|
||||||
EffectsCard: typeof import('./src/Components/EffectsCard/EffectsCard.vue')['default']
|
EffectsCard: typeof import('./src/components/EffectsCard/EffectsCard.vue')['default']
|
||||||
EyeOutlined: typeof import('@ant-design/icons-vue')['EyeOutlined']
|
EyeOutlined: typeof import('@ant-design/icons-vue')['EyeOutlined']
|
||||||
ForgetPage: typeof import('./src/views/Forget/ForgetPage.vue')['default']
|
ForgetPage: typeof import('./src/views/Forget/ForgetPage.vue')['default']
|
||||||
GithubOutlined: typeof import('@ant-design/icons-vue')['GithubOutlined']
|
GithubOutlined: typeof import('@ant-design/icons-vue')['GithubOutlined']
|
||||||
@@ -58,9 +59,9 @@ declare module 'vue' {
|
|||||||
QqOutlined: typeof import('@ant-design/icons-vue')['QqOutlined']
|
QqOutlined: typeof import('@ant-design/icons-vue')['QqOutlined']
|
||||||
QRLogin: typeof import('./src/views/QRLogin/QRLogin.vue')['default']
|
QRLogin: typeof import('./src/views/QRLogin/QRLogin.vue')['default']
|
||||||
QRLoginFooter: typeof import('./src/views/QRLogin/QRLoginFooter.vue')['default']
|
QRLoginFooter: typeof import('./src/views/QRLogin/QRLoginFooter.vue')['default']
|
||||||
ReplyInput: typeof import('./src/Components/CommentReply/Components/ReplyInput/ReplyInput.vue')['default']
|
ReplyInput: typeof import('./src/components/CommentReply/Components/ReplyInput/ReplyInput.vue')['default']
|
||||||
ReplyList: typeof import('./src/Components/CommentReply/Components/ReplyList/ReplyList.vue')['default']
|
ReplyList: typeof import('./src/components/CommentReply/Components/ReplyList/ReplyList.vue')['default']
|
||||||
ReplyReply: typeof import('./src/Components/CommentReply/Components/ReplyReplyInput/ReplyReply.vue')['default']
|
ReplyReply: typeof import('./src/components/CommentReply/Components/ReplyReplyInput/ReplyReply.vue')['default']
|
||||||
RouterLink: typeof import('vue-router')['RouterLink']
|
RouterLink: typeof import('vue-router')['RouterLink']
|
||||||
RouterView: typeof import('vue-router')['RouterView']
|
RouterView: typeof import('vue-router')['RouterView']
|
||||||
SafetyOutlined: typeof import('@ant-design/icons-vue')['SafetyOutlined']
|
SafetyOutlined: typeof import('@ant-design/icons-vue')['SafetyOutlined']
|
||||||
|
@@ -35,28 +35,30 @@
|
|||||||
method="post"
|
method="post"
|
||||||
:directory="false"
|
:directory="false"
|
||||||
:show-upload-list="false"
|
:show-upload-list="false"
|
||||||
:custom-request="customUploadRequest"
|
:custom-request="comment.customUploadRequest"
|
||||||
:before-upload="beforeUpload"
|
:before-upload="comment.beforeUpload"
|
||||||
:disabled="imageList.length >= 3"
|
:disabled="comment.imageList.length >= 3"
|
||||||
>
|
>
|
||||||
<ABadge :count="imageList.length">
|
<ABadge :count="comment.imageList.length">
|
||||||
<AButton type="text" size="small" :icon="h(PictureOutlined)"
|
<AButton type="text" size="small" :icon="h(PictureOutlined)"
|
||||||
class="comment-action-icon">
|
class="comment-action-icon">
|
||||||
{{ t('comment.picture') }}
|
{{ t('comment.picture') }}
|
||||||
</AButton>
|
</AButton>
|
||||||
</ABadge>
|
</ABadge>
|
||||||
</AUpload>
|
</AUpload>
|
||||||
<template v-if="imageList.length > 0">
|
<template v-if="comment.imageList.length > 0">
|
||||||
<ABadge style="margin-left: 10px;" v-for="(item, index) in imageList" :key="index">
|
<AImagePreviewGroup>
|
||||||
<template #count>
|
<ABadge style="margin-left: 10px;" v-for="(item, index) in comment.imageList" :key="index">
|
||||||
<CloseCircleOutlined @click="removeBase64Image(index)" style="color: #f5222d"/>
|
<template #count>
|
||||||
</template>
|
<CloseCircleOutlined @click="comment.removeBase64Image(index)" style="color: #f5222d"/>
|
||||||
<AAvatar shape="square" size="small">
|
|
||||||
<template #icon>
|
|
||||||
<AImage v-if="item" :width="24" :height="24" :src="item"/>
|
|
||||||
</template>
|
</template>
|
||||||
</AAvatar>
|
<AAvatar shape="square" size="small">
|
||||||
</ABadge>
|
<template #icon>
|
||||||
|
<AImage v-if="item" :width="24" :height="24" :src="item"/>
|
||||||
|
</template>
|
||||||
|
</AAvatar>
|
||||||
|
</ABadge>
|
||||||
|
</AImagePreviewGroup>
|
||||||
</template>
|
</template>
|
||||||
</AFlex>
|
</AFlex>
|
||||||
</AFlex>
|
</AFlex>
|
||||||
@@ -90,15 +92,14 @@ import useStore from "@/store";
|
|||||||
import {message} from "ant-design-vue";
|
import {message} from "ant-design-vue";
|
||||||
import {commentSubmitApi} from "@/api/comment";
|
import {commentSubmitApi} from "@/api/comment";
|
||||||
import {useDebounceFn, useThrottleFn} from "@vueuse/core";
|
import {useDebounceFn, useThrottleFn} from "@vueuse/core";
|
||||||
import imageCompression from "browser-image-compression";
|
|
||||||
import {useRouter} from "vue-router";
|
import {useRouter} from "vue-router";
|
||||||
|
|
||||||
const {t} = useI18n();
|
const {t} = useI18n();
|
||||||
|
|
||||||
const showCommentActions = ref<boolean>(false);
|
const showCommentActions = ref<boolean>(false);
|
||||||
const commentContent = ref<string>("");
|
const commentContent = ref<string>("");
|
||||||
const fileList = ref<any[]>([]);
|
|
||||||
const imageList = ref<any[]>([]);
|
|
||||||
const user = useStore().user;
|
const user = useStore().user;
|
||||||
const commentTextAreaPlaceholder = ref<string>(t('comment.placeholder'));
|
const commentTextAreaPlaceholder = ref<string>(t('comment.placeholder'));
|
||||||
const topicId = ref<string>("123");
|
const topicId = ref<string>("123");
|
||||||
@@ -146,7 +147,7 @@ async function commentSubmit(point: any) {
|
|||||||
message.error(t('comment.commentContentNotEmpty'));
|
message.error(t('comment.commentContentNotEmpty'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (imageList.value.length > 3) {
|
if (comment.imageList.length > 3) {
|
||||||
message.error(t('comment.maxImageCount'));
|
message.error(t('comment.maxImageCount'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -156,7 +157,7 @@ async function commentSubmit(point: any) {
|
|||||||
user_id: user.user.uid,
|
user_id: user.user.uid,
|
||||||
topic_id: topicId.value,
|
topic_id: topicId.value,
|
||||||
content: content,
|
content: content,
|
||||||
images: imageList.value,
|
images: comment.imageList,
|
||||||
author: user.user.uid,
|
author: user.user.uid,
|
||||||
point: [point.x, point.y],
|
point: [point.x, point.y],
|
||||||
key: comment.slideCaptchaData.key,
|
key: comment.slideCaptchaData.key,
|
||||||
@@ -165,8 +166,7 @@ async function commentSubmit(point: any) {
|
|||||||
if (result.code === 200 && result.success) {
|
if (result.code === 200 && result.success) {
|
||||||
message.success(t('comment.commentSuccess'));
|
message.success(t('comment.commentSuccess'));
|
||||||
commentContent.value = "";
|
commentContent.value = "";
|
||||||
fileList.value = [];
|
await comment.clearFileList();
|
||||||
imageList.value = [];
|
|
||||||
showSubmitCaptcha.value = false;
|
showSubmitCaptcha.value = false;
|
||||||
await getCommentList();
|
await getCommentList();
|
||||||
} else {
|
} else {
|
||||||
@@ -188,47 +188,6 @@ async function getCommentList(page: number = 1, size: number = 5, hot: boolean =
|
|||||||
await comment.getCommentList(params);
|
await comment.getCommentList(params);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 压缩图片配置
|
|
||||||
const options = {
|
|
||||||
maxSizeMB: 0.4,
|
|
||||||
maxWidthOrHeight: 750,
|
|
||||||
maxIteration: 2
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 上传文件前置
|
|
||||||
* @param file
|
|
||||||
*/
|
|
||||||
async function beforeUpload(file: any) {
|
|
||||||
if (!window.FileReader) return false; // 判断是否支持FileReader
|
|
||||||
const compressedFile = await imageCompression(file, options);
|
|
||||||
const reader = new FileReader();
|
|
||||||
reader.readAsDataURL(compressedFile); // 文件转换
|
|
||||||
reader.onloadend = async function () {
|
|
||||||
if (fileList.value.length >= 5) {
|
|
||||||
message.error(t('comment.maxImageCount'));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
fileList.value.push(reader.result);
|
|
||||||
};
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 自定义上传图片请求
|
|
||||||
*/
|
|
||||||
async function customUploadRequest() {
|
|
||||||
imageList.value = fileList.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 移除图片
|
|
||||||
* @param index
|
|
||||||
*/
|
|
||||||
async function removeBase64Image(index: number) {
|
|
||||||
fileList.value.splice(index, 1);
|
|
||||||
imageList.value.splice(index, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
const getSlideCaptchaDataThrottled = useThrottleFn(comment.getSlideCaptchaData, 1000);
|
const getSlideCaptchaDataThrottled = useThrottleFn(comment.getSlideCaptchaData, 1000);
|
||||||
|
|
||||||
|
@@ -49,16 +49,18 @@
|
|||||||
<div class="reply-text" v-html="item.content">
|
<div class="reply-text" v-html="item.content">
|
||||||
</div>
|
</div>
|
||||||
<AFlex :vertical="false" align="center" class="reply-images" v-if="item.images">
|
<AFlex :vertical="false" align="center" class="reply-images" v-if="item.images">
|
||||||
<AAvatar shape="square" size="large"
|
<AImagePreviewGroup>
|
||||||
v-for="(image, index) in item.images" :key="index">
|
<AAvatar shape="square" size="large"
|
||||||
<template #icon>
|
v-for="(image, index) in item.images" :key="index">
|
||||||
<AImage :width="40" :height="40" :src="image">
|
<template #icon>
|
||||||
<template #previewMask>
|
<AImage :width="40" :height="40" :src="image">
|
||||||
<EyeOutlined style="font-size: 18px;"/>
|
<template #previewMask>
|
||||||
</template>
|
<EyeOutlined style="font-size: 18px;"/>
|
||||||
</AImage>
|
</template>
|
||||||
</template>
|
</AImage>
|
||||||
</AAvatar>
|
</template>
|
||||||
|
</AAvatar>
|
||||||
|
</AImagePreviewGroup>
|
||||||
</AFlex>
|
</AFlex>
|
||||||
<AFlex :vertical="false" justify="space-between" align="center">
|
<AFlex :vertical="false" justify="space-between" align="center">
|
||||||
<!--评论操作按钮 -->
|
<!--评论操作按钮 -->
|
||||||
|
@@ -48,28 +48,30 @@
|
|||||||
method="post"
|
method="post"
|
||||||
:directory="false"
|
:directory="false"
|
||||||
:show-upload-list="false"
|
:show-upload-list="false"
|
||||||
:custom-request="customUploadRequest"
|
:custom-request="comment.customUploadRequest"
|
||||||
:before-upload="beforeUpload"
|
:before-upload="comment.beforeUpload"
|
||||||
:disabled="imageList.length >= 3"
|
:disabled="comment.imageList.length >= 3"
|
||||||
>
|
>
|
||||||
<ABadge :count="imageList.length">
|
<ABadge :count="comment.imageList.length">
|
||||||
<AButton type="text" size="small" :icon="h(PictureOutlined)"
|
<AButton type="text" size="small" :icon="h(PictureOutlined)"
|
||||||
class="comment-action-icon-reply">
|
class="comment-action-icon-reply">
|
||||||
{{ t('comment.picture') }}
|
{{ t('comment.picture') }}
|
||||||
</AButton>
|
</AButton>
|
||||||
</ABadge>
|
</ABadge>
|
||||||
</AUpload>
|
</AUpload>
|
||||||
<template v-if="imageList.length > 0">
|
<template v-if="comment.imageList.length > 0">
|
||||||
<ABadge style="margin-left: 10px;" v-for="(item, index) in imageList" :key="index">
|
<AImagePreviewGroup>
|
||||||
<template #count>
|
<ABadge style="margin-left: 10px;" v-for="(item, index) in comment.imageList" :key="index">
|
||||||
<CloseCircleOutlined @click="removeBase64Image(index)" style="color: #f5222d"/>
|
<template #count>
|
||||||
</template>
|
<CloseCircleOutlined @click="comment.removeBase64Image(index)" style="color: #f5222d"/>
|
||||||
<AAvatar shape="square" size="small">
|
|
||||||
<template #icon>
|
|
||||||
<AImage v-if="item" :width="24" :height="24" :src="item"/>
|
|
||||||
</template>
|
</template>
|
||||||
</AAvatar>
|
<AAvatar shape="square" size="small">
|
||||||
</ABadge>
|
<template #icon>
|
||||||
|
<AImage v-if="item" :width="24" :height="24" :src="item"/>
|
||||||
|
</template>
|
||||||
|
</AAvatar>
|
||||||
|
</ABadge>
|
||||||
|
</AImagePreviewGroup>
|
||||||
</template>
|
</template>
|
||||||
</AFlex>
|
</AFlex>
|
||||||
</AFlex>
|
</AFlex>
|
||||||
@@ -98,7 +100,6 @@
|
|||||||
|
|
||||||
import {h, ref} from "vue";
|
import {h, ref} from "vue";
|
||||||
import {message} from "ant-design-vue";
|
import {message} from "ant-design-vue";
|
||||||
import imageCompression from "browser-image-compression";
|
|
||||||
import {CloseOutlined, PictureOutlined, SmileOutlined} from "@ant-design/icons-vue";
|
import {CloseOutlined, PictureOutlined, SmileOutlined} from "@ant-design/icons-vue";
|
||||||
import EMOJI from "@/constant/emoji.ts";
|
import EMOJI from "@/constant/emoji.ts";
|
||||||
import {useI18n} from "vue-i18n";
|
import {useI18n} from "vue-i18n";
|
||||||
@@ -110,8 +111,6 @@ const {t} = useI18n();
|
|||||||
const comment = useStore().comment;
|
const comment = useStore().comment;
|
||||||
const user = useStore().user;
|
const user = useStore().user;
|
||||||
const commentTextAreaPlaceholder = ref<string>(t('comment.placeholder'));
|
const commentTextAreaPlaceholder = ref<string>(t('comment.placeholder'));
|
||||||
const fileList = ref<any[]>([]);
|
|
||||||
const imageList = ref<any[]>([]);
|
|
||||||
const replyContent = ref<string>("");
|
const replyContent = ref<string>("");
|
||||||
const topicId = ref<string>("123");
|
const topicId = ref<string>("123");
|
||||||
const showSubmitCaptcha = ref<boolean>(false);
|
const showSubmitCaptcha = ref<boolean>(false);
|
||||||
@@ -141,47 +140,6 @@ async function insertEmojiToReplyContent(emoji: string) {
|
|||||||
replyContent.value += emoji;
|
replyContent.value += emoji;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 压缩图片配置
|
|
||||||
const options = {
|
|
||||||
maxSizeMB: 0.4,
|
|
||||||
maxWidthOrHeight: 750,
|
|
||||||
maxIteration: 2
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 上传文件前置
|
|
||||||
* @param file
|
|
||||||
*/
|
|
||||||
async function beforeUpload(file: any) {
|
|
||||||
if (!window.FileReader) return false; // 判断是否支持FileReader
|
|
||||||
const compressedFile = await imageCompression(file, options);
|
|
||||||
const reader = new FileReader();
|
|
||||||
reader.readAsDataURL(compressedFile); // 文件转换
|
|
||||||
reader.onloadend = async function () {
|
|
||||||
if (fileList.value.length >= 5) {
|
|
||||||
message.error(t('comment.maxImageCount'));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
fileList.value.push(reader.result);
|
|
||||||
};
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 自定义上传图片请求
|
|
||||||
*/
|
|
||||||
async function customUploadRequest() {
|
|
||||||
imageList.value = fileList.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 移除图片
|
|
||||||
* @param index
|
|
||||||
*/
|
|
||||||
async function removeBase64Image(index: number) {
|
|
||||||
fileList.value.splice(index, 1);
|
|
||||||
imageList.value.splice(index, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 回复提交 throttled
|
* 回复提交 throttled
|
||||||
@@ -196,7 +154,7 @@ async function replySubmit(point: any) {
|
|||||||
message.error(t('comment.commentContentNotEmpty'));
|
message.error(t('comment.commentContentNotEmpty'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (imageList.value.length > 3) {
|
if (comment.imageList.length > 3) {
|
||||||
message.error(t('comment.maxImageCount'));
|
message.error(t('comment.maxImageCount'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -216,7 +174,7 @@ async function replySubmit(point: any) {
|
|||||||
user_id: user.user.uid,
|
user_id: user.user.uid,
|
||||||
topic_id: topicId.value,
|
topic_id: topicId.value,
|
||||||
content: content,
|
content: content,
|
||||||
images: imageList.value,
|
images: comment.imageList,
|
||||||
author: user.user.uid,
|
author: user.user.uid,
|
||||||
reply_id: props.item.id,
|
reply_id: props.item.id,
|
||||||
reply_user: props.item.user_id,
|
reply_user: props.item.user_id,
|
||||||
@@ -226,8 +184,7 @@ async function replySubmit(point: any) {
|
|||||||
const result: any = await replySubmitApi(replyParams);
|
const result: any = await replySubmitApi(replyParams);
|
||||||
if (result.code === 200 && result.success) {
|
if (result.code === 200 && result.success) {
|
||||||
replyContent.value = "";
|
replyContent.value = "";
|
||||||
fileList.value = [];
|
await comment.clearFileList();
|
||||||
imageList.value = [];
|
|
||||||
showSubmitCaptcha.value = false;
|
showSubmitCaptcha.value = false;
|
||||||
await getReplyList();
|
await getReplyList();
|
||||||
comment.closeReplyInput();
|
comment.closeReplyInput();
|
||||||
|
@@ -28,16 +28,18 @@
|
|||||||
<div class="reply-text-child" v-html="child.content">
|
<div class="reply-text-child" v-html="child.content">
|
||||||
</div>
|
</div>
|
||||||
<AFlex :vertical="false" align="center" class="reply-images" v-if="child.images">
|
<AFlex :vertical="false" align="center" class="reply-images" v-if="child.images">
|
||||||
<AAvatar shape="square" size="large"
|
<AImagePreviewGroup>
|
||||||
v-for="(image, index) in child.images" :key="index">
|
<AAvatar shape="square" size="large"
|
||||||
<template #icon>
|
v-for="(image, index) in child.images" :key="index">
|
||||||
<AImage :width="40" :height="40" :src="image">
|
<template #icon>
|
||||||
<template #previewMask>
|
<AImage :width="40" :height="40" :src="image">
|
||||||
<EyeOutlined style="font-size: 18px;"/>
|
<template #previewMask>
|
||||||
</template>
|
<EyeOutlined style="font-size: 18px;"/>
|
||||||
</AImage>
|
</template>
|
||||||
</template>
|
</AImage>
|
||||||
</AAvatar>
|
</template>
|
||||||
|
</AAvatar>
|
||||||
|
</AImagePreviewGroup>
|
||||||
</AFlex>
|
</AFlex>
|
||||||
<AFlex :vertical="false" justify="space-between" align="center">
|
<AFlex :vertical="false" justify="space-between" align="center">
|
||||||
<!--评论操作按钮 -->
|
<!--评论操作按钮 -->
|
||||||
@@ -141,9 +143,8 @@ const props = defineProps({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 格式化时间
|
* 格式化时间
|
||||||
* @param dateString
|
* @param dateString
|
||||||
*/
|
*/
|
||||||
function formatTimeAgo(dateString: string) {
|
function formatTimeAgo(dateString: string) {
|
||||||
@@ -169,6 +170,7 @@ function formatTimeAgo(dateString: string) {
|
|||||||
return `${seconds} 秒前`;
|
return `${seconds} 秒前`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const commentLikeThrottled = useThrottleFn(commentLike, 1000);
|
const commentLikeThrottled = useThrottleFn(commentLike, 1000);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -51,30 +51,32 @@
|
|||||||
method="post"
|
method="post"
|
||||||
:directory="false"
|
:directory="false"
|
||||||
:show-upload-list="false"
|
:show-upload-list="false"
|
||||||
:custom-request="customUploadRequest"
|
:custom-request="comment.customUploadRequest"
|
||||||
:before-upload="beforeUpload"
|
:before-upload="comment.beforeUpload"
|
||||||
:disabled="imageList.length >= 3"
|
:disabled="comment.imageList.length >= 3"
|
||||||
>
|
>
|
||||||
<ABadge :count="imageList.length">
|
<ABadge :count="comment.imageList.length">
|
||||||
<AButton type="text" size="small" :icon="h(PictureOutlined)"
|
<AButton type="text" size="small" :icon="h(PictureOutlined)"
|
||||||
class="comment-action-icon-reply-child">
|
class="comment-action-icon-reply-child">
|
||||||
{{ t('comment.picture') }}
|
{{ t('comment.picture') }}
|
||||||
</AButton>
|
</AButton>
|
||||||
</ABadge>
|
</ABadge>
|
||||||
</AUpload>
|
</AUpload>
|
||||||
<template v-if="imageList.length > 0">
|
<template v-if="comment.imageList.length > 0">
|
||||||
<ABadge style="margin-left: 10px;" v-for="(item, index) in imageList"
|
<AImagePreviewGroup>
|
||||||
:key="index">
|
<ABadge style="margin-left: 10px;" v-for="(item, index) in comment.imageList"
|
||||||
<template #count>
|
:key="index">
|
||||||
<CloseCircleOutlined @click="removeBase64Image(index)"
|
<template #count>
|
||||||
style="color: #f5222d"/>
|
<CloseCircleOutlined @click="comment.removeBase64Image(index)"
|
||||||
</template>
|
style="color: #f5222d"/>
|
||||||
<AAvatar shape="square" size="small">
|
|
||||||
<template #icon>
|
|
||||||
<AImage v-if="item" :width="24" :height="24" :src="item"/>
|
|
||||||
</template>
|
</template>
|
||||||
</AAvatar>
|
<AAvatar shape="square" size="small">
|
||||||
</ABadge>
|
<template #icon>
|
||||||
|
<AImage v-if="item" :width="24" :height="24" :src="item"/>
|
||||||
|
</template>
|
||||||
|
</AAvatar>
|
||||||
|
</ABadge>
|
||||||
|
</AImagePreviewGroup>
|
||||||
</template>
|
</template>
|
||||||
</AFlex>
|
</AFlex>
|
||||||
</AFlex>
|
</AFlex>
|
||||||
@@ -110,14 +112,12 @@ import {message} from "ant-design-vue";
|
|||||||
import {replyReplySubmitApi} from "@/api/comment";
|
import {replyReplySubmitApi} from "@/api/comment";
|
||||||
import {ReplyCommentParams} from "@/types/comment";
|
import {ReplyCommentParams} from "@/types/comment";
|
||||||
import {useThrottleFn} from "@vueuse/core";
|
import {useThrottleFn} from "@vueuse/core";
|
||||||
import imageCompression from "browser-image-compression";
|
|
||||||
|
|
||||||
const {t} = useI18n();
|
const {t} = useI18n();
|
||||||
const commentTextAreaPlaceholder = ref<string>(t('comment.placeholder'));
|
const commentTextAreaPlaceholder = ref<string>(t('comment.placeholder'));
|
||||||
const comment = useStore().comment;
|
const comment = useStore().comment;
|
||||||
const replyReplyContent = ref<string>("");
|
const replyReplyContent = ref<string>("");
|
||||||
const fileList = ref<any[]>([]);
|
|
||||||
const imageList = ref<any[]>([]);
|
|
||||||
const user = useStore().user;
|
const user = useStore().user;
|
||||||
const topicId = ref<string>("123");
|
const topicId = ref<string>("123");
|
||||||
const showSubmitCaptcha = ref<boolean>(false);
|
const showSubmitCaptcha = ref<boolean>(false);
|
||||||
@@ -153,48 +153,6 @@ async function insertEmojiToReplyReplyContent(emoji: string) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 压缩图片配置
|
|
||||||
const options = {
|
|
||||||
maxSizeMB: 0.4,
|
|
||||||
maxWidthOrHeight: 750,
|
|
||||||
maxIteration: 2
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 上传文件前置
|
|
||||||
* @param file
|
|
||||||
*/
|
|
||||||
async function beforeUpload(file: any) {
|
|
||||||
if (!window.FileReader) return false; // 判断是否支持FileReader
|
|
||||||
const compressedFile = await imageCompression(file, options);
|
|
||||||
const reader = new FileReader();
|
|
||||||
reader.readAsDataURL(compressedFile); // 文件转换
|
|
||||||
reader.onloadend = async function () {
|
|
||||||
if (fileList.value.length >= 5) {
|
|
||||||
message.error(t('comment.maxImageCount'));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
fileList.value.push(reader.result);
|
|
||||||
};
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 自定义上传图片请求
|
|
||||||
*/
|
|
||||||
async function customUploadRequest() {
|
|
||||||
imageList.value = fileList.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 移除图片
|
|
||||||
* @param index
|
|
||||||
*/
|
|
||||||
async function removeBase64Image(index: number) {
|
|
||||||
fileList.value.splice(index, 1);
|
|
||||||
imageList.value.splice(index, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 回复提交 throttled
|
* 回复提交 throttled
|
||||||
*/
|
*/
|
||||||
@@ -208,7 +166,7 @@ async function replyReplySubmit(point: any) {
|
|||||||
message.error(t('comment.commentContentNotEmpty'));
|
message.error(t('comment.commentContentNotEmpty'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (imageList.value.length > 3) {
|
if (comment.imageList.length > 3) {
|
||||||
message.error(t('comment.maxImageCount'));
|
message.error(t('comment.maxImageCount'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -218,7 +176,7 @@ async function replyReplySubmit(point: any) {
|
|||||||
user_id: user.user.uid,
|
user_id: user.user.uid,
|
||||||
topic_id: topicId.value,
|
topic_id: topicId.value,
|
||||||
content: content,
|
content: content,
|
||||||
images: imageList.value,
|
images: comment.imageList,
|
||||||
author: user.user.uid,
|
author: user.user.uid,
|
||||||
reply_to: props.child.id,
|
reply_to: props.child.id,
|
||||||
reply_id: props.item.id,
|
reply_id: props.item.id,
|
||||||
@@ -230,8 +188,7 @@ async function replyReplySubmit(point: any) {
|
|||||||
if (result.code === 200 && result.success) {
|
if (result.code === 200 && result.success) {
|
||||||
|
|
||||||
replyReplyContent.value = "";
|
replyReplyContent.value = "";
|
||||||
fileList.value = [];
|
await comment.clearFileList();
|
||||||
imageList.value = [];
|
|
||||||
showSubmitCaptcha.value = false;
|
showSubmitCaptcha.value = false;
|
||||||
await getReplyList();
|
await getReplyList();
|
||||||
comment.closeReplyInput();
|
comment.closeReplyInput();
|
||||||
|
@@ -4,7 +4,7 @@ import {Comment} from "@/types/comment";
|
|||||||
import {cancelCommentLikeApi, commentLikeApi, commentListApi, replyListApi} from "@/api/comment";
|
import {cancelCommentLikeApi, commentLikeApi, commentListApi, replyListApi} from "@/api/comment";
|
||||||
import {message} from "ant-design-vue";
|
import {message} from "ant-design-vue";
|
||||||
import {getSlideCaptchaDataApi} from "@/api/captcha";
|
import {getSlideCaptchaDataApi} from "@/api/captcha";
|
||||||
|
import imageCompression from "browser-image-compression";
|
||||||
|
|
||||||
export const useCommentStore = defineStore(
|
export const useCommentStore = defineStore(
|
||||||
'comment',
|
'comment',
|
||||||
@@ -25,6 +25,8 @@ export const useCommentStore = defineStore(
|
|||||||
thumbX: 0,
|
thumbX: 0,
|
||||||
thumbY: 0
|
thumbY: 0
|
||||||
});
|
});
|
||||||
|
const fileList = ref<any[]>([]);
|
||||||
|
const imageList = ref<any[]>([]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取评论列表
|
* 获取评论列表
|
||||||
@@ -147,6 +149,115 @@ export const useCommentStore = defineStore(
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清空文件列表
|
||||||
|
*/
|
||||||
|
async function clearFileList() {
|
||||||
|
fileList.value = [];
|
||||||
|
imageList.value = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传文件前置
|
||||||
|
* @param file
|
||||||
|
*/
|
||||||
|
async function beforeUpload(file: any) {
|
||||||
|
|
||||||
|
// 压缩图片配置
|
||||||
|
const options = {
|
||||||
|
maxSizeMB: 0.4,
|
||||||
|
maxWidthOrHeight: 750,
|
||||||
|
maxIteration: 2
|
||||||
|
};
|
||||||
|
if (!window.FileReader) return false; // 判断是否支持FileReader
|
||||||
|
const compressedFile = await imageCompression(file, options);
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.readAsDataURL(compressedFile); // 文件转换
|
||||||
|
reader.onloadend = async function () {
|
||||||
|
if (fileList.value.length < 3) {
|
||||||
|
const img: HTMLImageElement = document.createElement('img');
|
||||||
|
img.src = reader.result as string;
|
||||||
|
img.onload = () => {
|
||||||
|
const canvas = document.createElement('canvas');
|
||||||
|
canvas.width = img.naturalWidth;
|
||||||
|
canvas.height = img.naturalHeight;
|
||||||
|
const ctx = canvas.getContext('2d');
|
||||||
|
if (!ctx) {
|
||||||
|
console.error('Failed to get canvas context');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ctx.drawImage(img, 0, 0);
|
||||||
|
ctx.fillStyle = 'rgba(255, 255, 255, 0.5)';
|
||||||
|
ctx.textBaseline = 'middle';
|
||||||
|
|
||||||
|
// 动态设置字体大小,假设字体大小为画布高度的 5%
|
||||||
|
const fontSize = canvas.height * 0.05; // 可以根据需要调整比例
|
||||||
|
ctx.font = `${fontSize}px Microsoft Yahei`;
|
||||||
|
// 计算文本的宽度和高度,以便将其放置在右下角
|
||||||
|
const text = 'schisandra';
|
||||||
|
const textWidth = ctx.measureText(text).width;
|
||||||
|
const textHeight = fontSize; // 字体大小
|
||||||
|
|
||||||
|
// 设置文本的位置到右下角
|
||||||
|
const x: number = canvas.width - textWidth - 5; // 距离右边缘 5 像素
|
||||||
|
const y: number = canvas.height - textHeight / 2 - 5; // 距离下边缘 5 像素
|
||||||
|
|
||||||
|
ctx.fillText('schisandra', x, y);
|
||||||
|
fileList.value.push(canvas.toDataURL());
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return true;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自定义上传图片请求
|
||||||
|
*/
|
||||||
|
async function customUploadRequest() {
|
||||||
|
imageList.value = fileList.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移除图片
|
||||||
|
* @param index
|
||||||
|
*/
|
||||||
|
async function removeBase64Image(index: number) {
|
||||||
|
fileList.value.splice(index, 1);
|
||||||
|
imageList.value.splice(index, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化时间
|
||||||
|
* @param dateString
|
||||||
|
*/
|
||||||
|
function formatTimeAgo(dateString: string) {
|
||||||
|
const now: any = new Date();
|
||||||
|
const date: any = new Date(dateString);
|
||||||
|
const seconds = Math.floor((now - date) / 1000);
|
||||||
|
|
||||||
|
const intervals = [
|
||||||
|
{label: '年', seconds: 31536000},
|
||||||
|
{label: '个月', seconds: 2592000},
|
||||||
|
{label: '天', seconds: 86400},
|
||||||
|
{label: '小时', seconds: 3600},
|
||||||
|
{label: '分钟', seconds: 60}
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const interval of intervals) {
|
||||||
|
const count = Math.floor(seconds / interval.seconds);
|
||||||
|
if (count > 0) {
|
||||||
|
return `${count} ${interval.label}前`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${seconds} 秒前`;
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
commentList,
|
commentList,
|
||||||
commentLoading,
|
commentLoading,
|
||||||
@@ -156,6 +267,8 @@ export const useCommentStore = defineStore(
|
|||||||
replyLoading,
|
replyLoading,
|
||||||
slideCaptchaData,
|
slideCaptchaData,
|
||||||
commentMap,
|
commentMap,
|
||||||
|
fileList,
|
||||||
|
imageList,
|
||||||
getCommentList,
|
getCommentList,
|
||||||
handleShowReplyInput,
|
handleShowReplyInput,
|
||||||
closeReplyInput,
|
closeReplyInput,
|
||||||
@@ -164,6 +277,11 @@ export const useCommentStore = defineStore(
|
|||||||
commentLike,
|
commentLike,
|
||||||
cancelCommentLike,
|
cancelCommentLike,
|
||||||
getSlideCaptchaData,
|
getSlideCaptchaData,
|
||||||
|
beforeUpload,
|
||||||
|
customUploadRequest,
|
||||||
|
removeBase64Image,
|
||||||
|
clearFileList,
|
||||||
|
formatTimeAgo,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
Reference in New Issue
Block a user