|
|
@ -3,11 +3,7 @@
|
|
|
|
<div class="compose-wrap" v-if="store.state.userInfo.id > 0">
|
|
|
|
<div class="compose-wrap" v-if="store.state.userInfo.id > 0">
|
|
|
|
<div class="compose-line">
|
|
|
|
<div class="compose-line">
|
|
|
|
<div class="compose-user">
|
|
|
|
<div class="compose-user">
|
|
|
|
<n-avatar
|
|
|
|
<n-avatar round :size="30" :src="store.state.userInfo.avatar" />
|
|
|
|
round
|
|
|
|
|
|
|
|
:size="30"
|
|
|
|
|
|
|
|
:src="store.state.userInfo.avatar"
|
|
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<n-mention
|
|
|
|
<n-mention
|
|
|
|
type="textarea"
|
|
|
|
type="textarea"
|
|
|
@ -21,6 +17,7 @@
|
|
|
|
@search="handleSearch"
|
|
|
|
@search="handleSearch"
|
|
|
|
@update:value="changeContent"
|
|
|
|
@update:value="changeContent"
|
|
|
|
placeholder="讲讲今天和AiMo AI聊天的新发现吧"
|
|
|
|
placeholder="讲讲今天和AiMo AI聊天的新发现吧"
|
|
|
|
|
|
|
|
@paste="handlePaste"
|
|
|
|
/>
|
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
@ -49,8 +46,7 @@
|
|
|
|
<n-upload-trigger #="{ handleClick }" abstract>
|
|
|
|
<n-upload-trigger #="{ handleClick }" abstract>
|
|
|
|
<n-button
|
|
|
|
<n-button
|
|
|
|
:disabled="
|
|
|
|
:disabled="
|
|
|
|
(fileQueue.length > 0 &&
|
|
|
|
(fileQueue.length > 0 && uploadType === 'public/video') ||
|
|
|
|
uploadType === 'public/video') ||
|
|
|
|
|
|
|
|
fileQueue.length === 9
|
|
|
|
fileQueue.length === 9
|
|
|
|
"
|
|
|
|
"
|
|
|
|
@click="
|
|
|
|
@click="
|
|
|
@ -64,10 +60,7 @@
|
|
|
|
type="primary"
|
|
|
|
type="primary"
|
|
|
|
>
|
|
|
|
>
|
|
|
|
<template #icon>
|
|
|
|
<template #icon>
|
|
|
|
<n-icon
|
|
|
|
<n-icon size="20" color="var(--primary-color)">
|
|
|
|
size="20"
|
|
|
|
|
|
|
|
color="var(--primary-color)"
|
|
|
|
|
|
|
|
>
|
|
|
|
|
|
|
|
<image-outline />
|
|
|
|
<image-outline />
|
|
|
|
</n-icon>
|
|
|
|
</n-icon>
|
|
|
|
</template>
|
|
|
|
</template>
|
|
|
@ -76,11 +69,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
<n-upload-trigger
|
|
|
|
<n-upload-trigger
|
|
|
|
v-if="allowTweetVideo"
|
|
|
|
v-if="allowTweetVideo"
|
|
|
|
#="{ handleClick }" abstract>
|
|
|
|
#="{ handleClick }"
|
|
|
|
|
|
|
|
abstract
|
|
|
|
|
|
|
|
>
|
|
|
|
<n-button
|
|
|
|
<n-button
|
|
|
|
:disabled="
|
|
|
|
:disabled="
|
|
|
|
(fileQueue.length > 0 &&
|
|
|
|
(fileQueue.length > 0 && uploadType !== 'public/video') ||
|
|
|
|
uploadType !== 'public/video') ||
|
|
|
|
|
|
|
|
fileQueue.length === 9
|
|
|
|
fileQueue.length === 9
|
|
|
|
"
|
|
|
|
"
|
|
|
|
@click="
|
|
|
|
@click="
|
|
|
@ -94,10 +88,7 @@
|
|
|
|
type="primary"
|
|
|
|
type="primary"
|
|
|
|
>
|
|
|
|
>
|
|
|
|
<template #icon>
|
|
|
|
<template #icon>
|
|
|
|
<n-icon
|
|
|
|
<n-icon size="20" color="var(--primary-color)">
|
|
|
|
size="20"
|
|
|
|
|
|
|
|
color="var(--primary-color)"
|
|
|
|
|
|
|
|
>
|
|
|
|
|
|
|
|
<videocam-outline />
|
|
|
|
<videocam-outline />
|
|
|
|
</n-icon>
|
|
|
|
</n-icon>
|
|
|
|
</template>
|
|
|
|
</template>
|
|
|
@ -134,12 +125,7 @@
|
|
|
|
</n-button>
|
|
|
|
</n-button>
|
|
|
|
</n-upload-trigger> -->
|
|
|
|
</n-upload-trigger> -->
|
|
|
|
|
|
|
|
|
|
|
|
<n-button
|
|
|
|
<n-button quaternary circle type="primary" @click.stop="switchLink">
|
|
|
|
quaternary
|
|
|
|
|
|
|
|
circle
|
|
|
|
|
|
|
|
type="primary"
|
|
|
|
|
|
|
|
@click.stop="switchLink"
|
|
|
|
|
|
|
|
>
|
|
|
|
|
|
|
|
<template #icon>
|
|
|
|
<template #icon>
|
|
|
|
<n-icon size="20" color="var(--primary-color)">
|
|
|
|
<n-icon size="20" color="var(--primary-color)">
|
|
|
|
<compass-outline />
|
|
|
|
<compass-outline />
|
|
|
@ -276,10 +262,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
|
<script setup lang="ts">
|
|
|
|
import { ref, onMounted } from 'vue';
|
|
|
|
import { ref, onMounted } from "vue";
|
|
|
|
import { useStore } from 'vuex';
|
|
|
|
import { useStore } from "vuex";
|
|
|
|
import { debounce } from 'lodash';
|
|
|
|
import { debounce } from "lodash";
|
|
|
|
import { getSuggestUsers, getSuggestTags } from '@/api/user';
|
|
|
|
import { getSuggestUsers, getSuggestTags } from "@/api/user";
|
|
|
|
|
|
|
|
|
|
|
|
import {
|
|
|
|
import {
|
|
|
|
ImageOutline,
|
|
|
|
ImageOutline,
|
|
|
@ -287,19 +273,17 @@ import {
|
|
|
|
AttachOutline,
|
|
|
|
AttachOutline,
|
|
|
|
CompassOutline,
|
|
|
|
CompassOutline,
|
|
|
|
EyeOutline,
|
|
|
|
EyeOutline,
|
|
|
|
} from '@vicons/ionicons5';
|
|
|
|
} from "@vicons/ionicons5";
|
|
|
|
import { createPost } from '@/api/post';
|
|
|
|
import { createPost, uploadImage } from "@/api/post";
|
|
|
|
import { parsePostTag } from '@/utils/content';
|
|
|
|
import { parsePostTag } from "@/utils/content";
|
|
|
|
import { isZipFile } from '@/utils/isZipFile';
|
|
|
|
import { isZipFile } from "@/utils/isZipFile";
|
|
|
|
import type { MentionOption, UploadFileInfo, UploadInst } from 'naive-ui';
|
|
|
|
import type { MentionOption, UploadFileInfo, UploadInst } from "naive-ui";
|
|
|
|
import { VisibilityEnum, PostItemTypeEnum } from '@/utils/IEnum';
|
|
|
|
import { VisibilityEnum, PostItemTypeEnum } from "@/utils/IEnum";
|
|
|
|
import { userLogin, userRegister, userInfo } from '@/api/auth';
|
|
|
|
import { userLogin, userRegister, userInfo } from "@/api/auth";
|
|
|
|
import { useRouter } from "vue-router";
|
|
|
|
import { useRouter } from "vue-router";
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const emit = defineEmits<{
|
|
|
|
const emit = defineEmits<{
|
|
|
|
(e: 'post-success', post: Item.PostProps): void;
|
|
|
|
(e: "post-success", post: Item.PostProps): void;
|
|
|
|
}>();
|
|
|
|
}>();
|
|
|
|
|
|
|
|
|
|
|
|
const store = useStore();
|
|
|
|
const store = useStore();
|
|
|
@ -310,44 +294,56 @@ const loading = ref(false);
|
|
|
|
const submitting = ref(false);
|
|
|
|
const submitting = ref(false);
|
|
|
|
const showLinkSet = ref(false);
|
|
|
|
const showLinkSet = ref(false);
|
|
|
|
const showEyeSet = ref(false);
|
|
|
|
const showEyeSet = ref(false);
|
|
|
|
const content = ref('');
|
|
|
|
const content = ref("");
|
|
|
|
const links = ref([]);
|
|
|
|
const links = ref([]);
|
|
|
|
|
|
|
|
|
|
|
|
const uploadRef = ref<UploadInst>();
|
|
|
|
const uploadRef = ref<UploadInst>();
|
|
|
|
const attachmentPrice = ref(0);
|
|
|
|
const attachmentPrice = ref(0);
|
|
|
|
const uploadType = ref('public/image');
|
|
|
|
const uploadType = ref("public/image");
|
|
|
|
const fileQueue = ref<UploadFileInfo[]>([]);
|
|
|
|
const fileQueue = ref<UploadFileInfo[]>([]);
|
|
|
|
const imageContents = ref<Item.CommentItemProps[]>([]);
|
|
|
|
const imageContents = ref<Item.CommentItemProps[]>([]);
|
|
|
|
const videoContents = ref<Item.CommentItemProps[]>([]);
|
|
|
|
const videoContents = ref<Item.CommentItemProps[]>([]);
|
|
|
|
const attachmentContents = ref<Item.AttachmentProps[]>([]);
|
|
|
|
const attachmentContents = ref<Item.AttachmentProps[]>([]);
|
|
|
|
const visitType = ref<VisibilityEnum>(VisibilityEnum.FRIEND);
|
|
|
|
const visitType = ref<VisibilityEnum>(VisibilityEnum.FRIEND);
|
|
|
|
const defaultVisitType = ref<VisibilityEnum>(VisibilityEnum.FRIEND)
|
|
|
|
const defaultVisitType = ref<VisibilityEnum>(VisibilityEnum.FRIEND);
|
|
|
|
const visibilities = [
|
|
|
|
const visibilities = [
|
|
|
|
{value: VisibilityEnum.PUBLIC, label: "公开"}
|
|
|
|
{ value: VisibilityEnum.PUBLIC, label: "公开" },
|
|
|
|
, {value: VisibilityEnum.PRIVATE, label: "私密"}
|
|
|
|
{ value: VisibilityEnum.PRIVATE, label: "私密" },
|
|
|
|
, {value: VisibilityEnum.FRIEND, label: "好友可见"}
|
|
|
|
{ value: VisibilityEnum.FRIEND, label: "好友可见" },
|
|
|
|
];
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
const defaultTweetMaxLength = Number(import.meta.env.VITE_DEFAULT_TWEET_MAX_LENGTH)
|
|
|
|
const defaultTweetMaxLength = Number(
|
|
|
|
const allowUserRegister = ref(import.meta.env.VITE_ALLOW_USER_REGISTER.toLowerCase() === 'true')
|
|
|
|
import.meta.env.VITE_DEFAULT_TWEET_MAX_LENGTH
|
|
|
|
const allowTweetVideo = ref(import.meta.env.VITE_ALLOW_TWEET_VIDEO.toLowerCase() === 'true')
|
|
|
|
);
|
|
|
|
const allowTweetAttachment = ref(import.meta.env.VITE_ALLOW_TWEET_ATTACHMENT.toLowerCase() === 'true')
|
|
|
|
const allowUserRegister = ref(
|
|
|
|
const allowTweetAttachmentPrice = ref(import.meta.env.VITE_ALLOW_TWEET_ATTACHMENT_PRICE.toLowerCase() === 'true')
|
|
|
|
import.meta.env.VITE_ALLOW_USER_REGISTER.toLowerCase() === "true"
|
|
|
|
const allowTweetVisibility = ref(import.meta.env.VITE_ALLOW_TWEET_VISIBILITY.toLowerCase() === 'true')
|
|
|
|
);
|
|
|
|
const uploadGateway = import.meta.env.VITE_HOST + '/v1/attachment';
|
|
|
|
const allowTweetVideo = ref(
|
|
|
|
|
|
|
|
import.meta.env.VITE_ALLOW_TWEET_VIDEO.toLowerCase() === "true"
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
const allowTweetAttachment = ref(
|
|
|
|
|
|
|
|
import.meta.env.VITE_ALLOW_TWEET_ATTACHMENT.toLowerCase() === "true"
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
const allowTweetAttachmentPrice = ref(
|
|
|
|
|
|
|
|
import.meta.env.VITE_ALLOW_TWEET_ATTACHMENT_PRICE.toLowerCase() === "true"
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
const allowTweetVisibility = ref(
|
|
|
|
|
|
|
|
import.meta.env.VITE_ALLOW_TWEET_VISIBILITY.toLowerCase() === "true"
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
const uploadGateway = import.meta.env.VITE_HOST + "/v1/attachment";
|
|
|
|
const uploadToken = ref();
|
|
|
|
const uploadToken = ref();
|
|
|
|
|
|
|
|
|
|
|
|
const switchLink = () => {
|
|
|
|
const switchLink = () => {
|
|
|
|
showLinkSet.value = !showLinkSet.value;
|
|
|
|
showLinkSet.value = !showLinkSet.value;
|
|
|
|
if (showLinkSet.value && showEyeSet.value) {
|
|
|
|
if (showLinkSet.value && showEyeSet.value) {
|
|
|
|
showEyeSet.value = false
|
|
|
|
showEyeSet.value = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const switchEye = () => {
|
|
|
|
const switchEye = () => {
|
|
|
|
showEyeSet.value = !showEyeSet.value;
|
|
|
|
showEyeSet.value = !showEyeSet.value;
|
|
|
|
if (showEyeSet.value && showLinkSet.value) {
|
|
|
|
if (showEyeSet.value && showLinkSet.value) {
|
|
|
|
showLinkSet.value = false
|
|
|
|
showLinkSet.value = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
@ -398,7 +394,7 @@ const handleSearch = (k: string, prefix: string) => {
|
|
|
|
return;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
loading.value = true;
|
|
|
|
loading.value = true;
|
|
|
|
if (prefix === '@') {
|
|
|
|
if (prefix === "@") {
|
|
|
|
loadSuggestionUsers(k);
|
|
|
|
loadSuggestionUsers(k);
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
loadSuggestionTags(k);
|
|
|
|
loadSuggestionTags(k);
|
|
|
@ -418,10 +414,15 @@ const setUploadType = (type: string) => {
|
|
|
|
const updateUpload = (list: UploadFileInfo[]) => {
|
|
|
|
const updateUpload = (list: UploadFileInfo[]) => {
|
|
|
|
for (let i = 0; i < list.length; i++) {
|
|
|
|
for (let i = 0; i < list.length; i++) {
|
|
|
|
var name = list[i].name;
|
|
|
|
var name = list[i].name;
|
|
|
|
var basename: string = name.split('.').slice(0, -1).join('.');
|
|
|
|
var basename: string = name.split(".").slice(0, -1).join(".");
|
|
|
|
var ext: string = name.split('.').pop()!;
|
|
|
|
var ext: string = name.split(".").pop()!;
|
|
|
|
if (basename.length > 30) {
|
|
|
|
if (basename.length > 30) {
|
|
|
|
list[i].name = basename.substring(0, 18) + "..." + basename.substring(basename.length-9) + "." + ext;
|
|
|
|
list[i].name =
|
|
|
|
|
|
|
|
basename.substring(0, 18) +
|
|
|
|
|
|
|
|
"..." +
|
|
|
|
|
|
|
|
basename.substring(basename.length - 9) +
|
|
|
|
|
|
|
|
"." +
|
|
|
|
|
|
|
|
ext;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fileQueue.value = list;
|
|
|
|
fileQueue.value = list;
|
|
|
@ -429,51 +430,46 @@ const updateUpload = (list: UploadFileInfo[]) => {
|
|
|
|
const beforeUpload = async (data: any) => {
|
|
|
|
const beforeUpload = async (data: any) => {
|
|
|
|
// 图片类型校验
|
|
|
|
// 图片类型校验
|
|
|
|
if (
|
|
|
|
if (
|
|
|
|
uploadType.value === 'public/image' &&
|
|
|
|
uploadType.value === "public/image" &&
|
|
|
|
!['image/png', 'image/jpg', 'image/jpeg', 'image/gif'].includes(
|
|
|
|
!["image/png", "image/jpg", "image/jpeg", "image/gif"].includes(
|
|
|
|
data.file.file?.type
|
|
|
|
data.file.file?.type
|
|
|
|
)
|
|
|
|
)
|
|
|
|
) {
|
|
|
|
) {
|
|
|
|
window.$message.warning('图片仅允许 png/jpg/gif 格式');
|
|
|
|
window.$message.warning("图片仅允许 png/jpg/gif 格式");
|
|
|
|
return false;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (uploadType.value === 'image' && data.file.file?.size > 10485760) {
|
|
|
|
if (uploadType.value === "image" && data.file.file?.size > 10485760) {
|
|
|
|
window.$message.warning('图片大小不能超过10MB');
|
|
|
|
window.$message.warning("图片大小不能超过10MB");
|
|
|
|
return false;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if(uploadType.value === 'public/image') {
|
|
|
|
if (uploadType.value === "public/image") {
|
|
|
|
const compressedFile = await compressionFile(data.file.file);
|
|
|
|
const compressedFile = await compressionFile(data.file.file);
|
|
|
|
data.file.file = compressedFile;
|
|
|
|
data.file.file = compressedFile;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 视频类型校验
|
|
|
|
// 视频类型校验
|
|
|
|
if (
|
|
|
|
if (
|
|
|
|
uploadType.value === 'public/video' &&
|
|
|
|
uploadType.value === "public/video" &&
|
|
|
|
!['video/mp4', 'video/quicktime'].includes(data.file.file?.type)
|
|
|
|
!["video/mp4", "video/quicktime"].includes(data.file.file?.type)
|
|
|
|
) {
|
|
|
|
) {
|
|
|
|
window.$message.warning('视频仅允许 mp4/mov 格式');
|
|
|
|
window.$message.warning("视频仅允许 mp4/mov 格式");
|
|
|
|
return false;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (
|
|
|
|
if (uploadType.value === "public/video" && data.file.file?.size > 8388608) {
|
|
|
|
uploadType.value === 'public/video' &&
|
|
|
|
window.$message.warning("视频大小不能超过8MB");
|
|
|
|
data.file.file?.size > 8388608
|
|
|
|
|
|
|
|
) {
|
|
|
|
|
|
|
|
window.$message.warning('视频大小不能超过8MB');
|
|
|
|
|
|
|
|
return false;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// 附件类型校验
|
|
|
|
// 附件类型校验
|
|
|
|
if (
|
|
|
|
if (uploadType.value === "attachment" && !(await isZipFile(data.file.file))) {
|
|
|
|
uploadType.value === 'attachment' && !(await isZipFile(data.file.file))
|
|
|
|
window.$message.warning("附件仅允许 zip 格式");
|
|
|
|
) {
|
|
|
|
|
|
|
|
window.$message.warning('附件仅允许 zip 格式');
|
|
|
|
|
|
|
|
return false;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (uploadType.value === 'attachment' && data.file.file?.size > 104857600) {
|
|
|
|
if (uploadType.value === "attachment" && data.file.file?.size > 104857600) {
|
|
|
|
window.$message.warning('附件大小不能超过100MB');
|
|
|
|
window.$message.warning("附件大小不能超过100MB");
|
|
|
|
return false;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -489,14 +485,14 @@ const beforeUpload = async (data: any) => {
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
const compressionFile = async (
|
|
|
|
const compressionFile = async (
|
|
|
|
file: Blob,
|
|
|
|
file: Blob,
|
|
|
|
type: string = 'image/jpeg',
|
|
|
|
type: string = "image/jpeg",
|
|
|
|
quality: number = 0.4,
|
|
|
|
quality: number = 0.4,
|
|
|
|
maxWidth: number = 1920, // 设置压缩后的最大宽度
|
|
|
|
maxWidth: number = 1920, // 设置压缩后的最大宽度
|
|
|
|
maxHeight: number = 1920 // 设置压缩后的最大高度
|
|
|
|
maxHeight: number = 1920 // 设置压缩后的最大高度
|
|
|
|
) => {
|
|
|
|
) => {
|
|
|
|
const fileName = file.name;
|
|
|
|
const fileName = file.name;
|
|
|
|
const canvas = document.createElement('canvas');
|
|
|
|
const canvas = document.createElement("canvas");
|
|
|
|
const context = canvas.getContext('2d') as CanvasRenderingContext2D;
|
|
|
|
const context = canvas.getContext("2d") as CanvasRenderingContext2D;
|
|
|
|
|
|
|
|
|
|
|
|
const base64 = await fileToDataURL(file);
|
|
|
|
const base64 = await fileToDataURL(file);
|
|
|
|
const img = await dataURLToImage(base64);
|
|
|
|
const img = await dataURLToImage(base64);
|
|
|
@ -525,14 +521,14 @@ const compressionFile = async (
|
|
|
|
const newFile = new File([blob], fileName, { type });
|
|
|
|
const newFile = new File([blob], fileName, { type });
|
|
|
|
|
|
|
|
|
|
|
|
return newFile;
|
|
|
|
return newFile;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
const fileToDataURL = (file: Blob): Promise<any> => {
|
|
|
|
const fileToDataURL = (file: Blob): Promise<any> => {
|
|
|
|
return new Promise((resolve) => {
|
|
|
|
return new Promise((resolve) => {
|
|
|
|
const reader = new FileReader();
|
|
|
|
const reader = new FileReader();
|
|
|
|
reader.onloadend = (e) => resolve((e.target as FileReader).result);
|
|
|
|
reader.onloadend = (e) => resolve((e.target as FileReader).result);
|
|
|
|
reader.readAsDataURL(file);
|
|
|
|
reader.readAsDataURL(file);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const dataURLToImage = (dataURL: string): Promise<HTMLImageElement> => {
|
|
|
|
const dataURLToImage = (dataURL: string): Promise<HTMLImageElement> => {
|
|
|
|
return new Promise((resolve) => {
|
|
|
|
return new Promise((resolve) => {
|
|
|
@ -540,32 +536,36 @@ const dataURLToImage = (dataURL: string): Promise<HTMLImageElement> => {
|
|
|
|
img.onload = () => resolve(img);
|
|
|
|
img.onload = () => resolve(img);
|
|
|
|
img.src = dataURL;
|
|
|
|
img.src = dataURL;
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const canvasToFile = (canvas: HTMLCanvasElement, type: string, quality: number): Promise<Blob | null> => {
|
|
|
|
const canvasToFile = (
|
|
|
|
|
|
|
|
canvas: HTMLCanvasElement,
|
|
|
|
|
|
|
|
type: string,
|
|
|
|
|
|
|
|
quality: number
|
|
|
|
|
|
|
|
): Promise<Blob | null> => {
|
|
|
|
return new Promise((resolve) =>
|
|
|
|
return new Promise((resolve) =>
|
|
|
|
canvas.toBlob((blob) => resolve(blob), type, quality)
|
|
|
|
canvas.toBlob((blob) => resolve(blob), type, quality)
|
|
|
|
);
|
|
|
|
);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const finishUpload = ({ file, event }: any): any => {
|
|
|
|
const finishUpload = ({ file, event }: any): any => {
|
|
|
|
try {
|
|
|
|
try {
|
|
|
|
let data = JSON.parse(event.target?.response);
|
|
|
|
let data = JSON.parse(event.target?.response);
|
|
|
|
|
|
|
|
|
|
|
|
if (data.code === 0) {
|
|
|
|
if (data.code === 0) {
|
|
|
|
if (uploadType.value === 'public/image') {
|
|
|
|
if (uploadType.value === "public/image") {
|
|
|
|
imageContents.value.push({
|
|
|
|
imageContents.value.push({
|
|
|
|
id: file.id,
|
|
|
|
id: file.id,
|
|
|
|
content: data.data.content,
|
|
|
|
content: data.data.content,
|
|
|
|
} as Item.CommentItemProps);
|
|
|
|
} as Item.CommentItemProps);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (uploadType.value === 'public/video') {
|
|
|
|
if (uploadType.value === "public/video") {
|
|
|
|
videoContents.value.push({
|
|
|
|
videoContents.value.push({
|
|
|
|
id: file.id,
|
|
|
|
id: file.id,
|
|
|
|
content: data.data.content,
|
|
|
|
content: data.data.content,
|
|
|
|
} as Item.CommentItemProps);
|
|
|
|
} as Item.CommentItemProps);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (uploadType.value === 'attachment') {
|
|
|
|
if (uploadType.value === "attachment") {
|
|
|
|
attachmentContents.value.push({
|
|
|
|
attachmentContents.value.push({
|
|
|
|
id: file.id,
|
|
|
|
id: file.id,
|
|
|
|
content: data.data.content,
|
|
|
|
content: data.data.content,
|
|
|
@ -573,7 +573,7 @@ const finishUpload = ({ file, event }: any): any => {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} catch (error) {
|
|
|
|
} catch (error) {
|
|
|
|
window.$message.error('上传失败');
|
|
|
|
window.$message.error("上传失败");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
};
|
|
|
|
const failUpload = ({ file, event }: any): any => {
|
|
|
|
const failUpload = ({ file, event }: any): any => {
|
|
|
@ -581,16 +581,16 @@ const failUpload = ({ file, event }: any): any => {
|
|
|
|
let data = JSON.parse(event.target?.response);
|
|
|
|
let data = JSON.parse(event.target?.response);
|
|
|
|
|
|
|
|
|
|
|
|
if (data.code !== 0) {
|
|
|
|
if (data.code !== 0) {
|
|
|
|
let errMsg = data.msg || '上传失败';
|
|
|
|
let errMsg = data.msg || "上传失败";
|
|
|
|
if (data.details && data.details.length > 0) {
|
|
|
|
if (data.details && data.details.length > 0) {
|
|
|
|
data.details.map((detail: string) => {
|
|
|
|
data.details.map((detail: string) => {
|
|
|
|
errMsg += ':' + detail;
|
|
|
|
errMsg += ":" + detail;
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
window.$message.error(errMsg);
|
|
|
|
window.$message.error(errMsg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} catch (error) {
|
|
|
|
} catch (error) {
|
|
|
|
window.$message.error('上传失败');
|
|
|
|
window.$message.error("上传失败");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
};
|
|
|
|
const removeUpload = ({ file }: any) => {
|
|
|
|
const removeUpload = ({ file }: any) => {
|
|
|
@ -611,7 +611,7 @@ const removeUpload = ({ file }: any) => {
|
|
|
|
// 发布动态
|
|
|
|
// 发布动态
|
|
|
|
const submitPost = () => {
|
|
|
|
const submitPost = () => {
|
|
|
|
if (content.value.trim().length === 0) {
|
|
|
|
if (content.value.trim().length === 0) {
|
|
|
|
window.$message.warning('请输入内容哦');
|
|
|
|
window.$message.warning("请输入内容哦");
|
|
|
|
return;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -668,19 +668,19 @@ const submitPost = () => {
|
|
|
|
tags: Array.from(new Set(tags)),
|
|
|
|
tags: Array.from(new Set(tags)),
|
|
|
|
users: Array.from(new Set(users)),
|
|
|
|
users: Array.from(new Set(users)),
|
|
|
|
attachment_price: +attachmentPrice.value * 100,
|
|
|
|
attachment_price: +attachmentPrice.value * 100,
|
|
|
|
visibility: visitType.value
|
|
|
|
visibility: visitType.value,
|
|
|
|
})
|
|
|
|
})
|
|
|
|
.then((res) => {
|
|
|
|
.then((res) => {
|
|
|
|
window.$message.success('发布成功');
|
|
|
|
window.$message.success("发布成功");
|
|
|
|
submitting.value = false;
|
|
|
|
submitting.value = false;
|
|
|
|
emit('post-success', res);
|
|
|
|
emit("post-success", res);
|
|
|
|
|
|
|
|
|
|
|
|
// 置空
|
|
|
|
// 置空
|
|
|
|
showLinkSet.value = false;
|
|
|
|
showLinkSet.value = false;
|
|
|
|
showEyeSet.value = false;
|
|
|
|
showEyeSet.value = false;
|
|
|
|
uploadRef.value?.clear();
|
|
|
|
uploadRef.value?.clear();
|
|
|
|
fileQueue.value = [];
|
|
|
|
fileQueue.value = [];
|
|
|
|
content.value = '';
|
|
|
|
content.value = "";
|
|
|
|
links.value = [];
|
|
|
|
links.value = [];
|
|
|
|
imageContents.value = [];
|
|
|
|
imageContents.value = [];
|
|
|
|
videoContents.value = [];
|
|
|
|
videoContents.value = [];
|
|
|
@ -688,7 +688,7 @@ const submitPost = () => {
|
|
|
|
visitType.value = defaultVisitType.value;
|
|
|
|
visitType.value = defaultVisitType.value;
|
|
|
|
//转到初始页面
|
|
|
|
//转到初始页面
|
|
|
|
router.push({
|
|
|
|
router.push({
|
|
|
|
name: 'home',
|
|
|
|
name: "home",
|
|
|
|
});
|
|
|
|
});
|
|
|
|
})
|
|
|
|
})
|
|
|
|
.catch((err) => {
|
|
|
|
.catch((err) => {
|
|
|
@ -696,34 +696,137 @@ const submitPost = () => {
|
|
|
|
});
|
|
|
|
});
|
|
|
|
};
|
|
|
|
};
|
|
|
|
const triggerAuth = (key: string) => {
|
|
|
|
const triggerAuth = (key: string) => {
|
|
|
|
store.commit('triggerAuth', true);
|
|
|
|
store.commit("triggerAuth", true);
|
|
|
|
store.commit('triggerAuthKey', key);
|
|
|
|
store.commit("triggerAuthKey", key);
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const generateRandomFileName = (extension: any) => {
|
|
|
|
|
|
|
|
const timestamp = new Date().getTime(); // 获取当前时间戳
|
|
|
|
|
|
|
|
const randomString = Math.random().toString(36).substring(2, 15); // 生成随机字符串
|
|
|
|
|
|
|
|
return `${timestamp}-${randomString}.${extension}`;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//粘贴事件监听
|
|
|
|
|
|
|
|
const handlePaste = (event: ClipboardEvent) => {
|
|
|
|
|
|
|
|
if (event && event.clipboardData) {
|
|
|
|
|
|
|
|
const items = event.clipboardData.items;
|
|
|
|
|
|
|
|
let file: File | null = null;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (items && items.length) {
|
|
|
|
|
|
|
|
for (let i = 0; i < items.length; i++) {
|
|
|
|
|
|
|
|
if (items[i].type.indexOf("image") !== -1) {
|
|
|
|
|
|
|
|
file = items[i].getAsFile();
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (file) {
|
|
|
|
|
|
|
|
const fileExtension = file.name.split(".").pop();
|
|
|
|
|
|
|
|
const randomFileName = generateRandomFileName(fileExtension);
|
|
|
|
|
|
|
|
const fileSize = file.size;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 以二进制形式读取文件内容
|
|
|
|
|
|
|
|
const reader = new FileReader();
|
|
|
|
|
|
|
|
reader.onload = function (event) {
|
|
|
|
|
|
|
|
if (
|
|
|
|
|
|
|
|
event &&
|
|
|
|
|
|
|
|
event.target &&
|
|
|
|
|
|
|
|
event.target.result instanceof ArrayBuffer
|
|
|
|
|
|
|
|
) {
|
|
|
|
|
|
|
|
// 获取文件内容
|
|
|
|
|
|
|
|
const fileContent = new Uint8Array(event.target.result);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 创建 Blob
|
|
|
|
|
|
|
|
const blob = new Blob([fileContent], { type: "image/jpeg" });
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 这里可以使用 blob 了
|
|
|
|
|
|
|
|
const formData = new FormData();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 添加文件到 FormData 对象
|
|
|
|
|
|
|
|
formData.append('type', 'public/image');
|
|
|
|
|
|
|
|
formData.append("file", blob, randomFileName);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const uploadImageReq: NetParams.UploadImageReq = {
|
|
|
|
|
|
|
|
type: formData.get('type') as string,
|
|
|
|
|
|
|
|
file: formData.get('file') as File,
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 声明一个 Ref 对象
|
|
|
|
|
|
|
|
// const uploadToken: Ref<string> = ref('your_upload_token_here');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 获取 Ref 对象的值并赋给 string 类型的变量
|
|
|
|
|
|
|
|
// const token: string = uploadToken.value;
|
|
|
|
|
|
|
|
// const token = ref(uploadToken)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
uploadImage(uploadImageReq, uploadToken.value)
|
|
|
|
|
|
|
|
.then((response) => {
|
|
|
|
|
|
|
|
// 处理响应
|
|
|
|
|
|
|
|
const pasteTimestamp = new Date().getTime()
|
|
|
|
|
|
|
|
const pasteFile : UploadFileInfo[] = [{
|
|
|
|
|
|
|
|
id: pasteTimestamp.toString(),
|
|
|
|
|
|
|
|
name: randomFileName,
|
|
|
|
|
|
|
|
batchId: pasteTimestamp.toString(),
|
|
|
|
|
|
|
|
percentage: 100,
|
|
|
|
|
|
|
|
status: 'finished',
|
|
|
|
|
|
|
|
url: null,
|
|
|
|
|
|
|
|
file: file,
|
|
|
|
|
|
|
|
thumbnailUrl: null,
|
|
|
|
|
|
|
|
type: "image/jpeg",
|
|
|
|
|
|
|
|
fullPath: "/" + randomFileName,
|
|
|
|
|
|
|
|
}]
|
|
|
|
|
|
|
|
const combinedFileList = fileQueue.value.concat(pasteFile);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
updateUpload(combinedFileList)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
imageContents.value.push({
|
|
|
|
|
|
|
|
id: pasteTimestamp.toString(),
|
|
|
|
|
|
|
|
content: response.content,
|
|
|
|
|
|
|
|
} as Item.CommentItemProps);
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
.catch((error) => {
|
|
|
|
|
|
|
|
// 处理错误
|
|
|
|
|
|
|
|
console.error('上传失败', error);
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
reader.readAsArrayBuffer(file);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
onMounted(() => {
|
|
|
|
onMounted(() => {
|
|
|
|
if (import.meta.env.VITE_DEFAULT_TWEET_VISIBILITY.toLowerCase() === 'friend') {
|
|
|
|
if (
|
|
|
|
defaultVisitType.value = VisibilityEnum.FRIEND
|
|
|
|
import.meta.env.VITE_DEFAULT_TWEET_VISIBILITY.toLowerCase() === "friend"
|
|
|
|
} else if (import.meta.env.VITE_DEFAULT_TWEET_VISIBILITY.toLowerCase() === 'public') {
|
|
|
|
) {
|
|
|
|
defaultVisitType.value = VisibilityEnum.PUBLIC
|
|
|
|
defaultVisitType.value = VisibilityEnum.FRIEND;
|
|
|
|
|
|
|
|
} else if (
|
|
|
|
|
|
|
|
import.meta.env.VITE_DEFAULT_TWEET_VISIBILITY.toLowerCase() === "public"
|
|
|
|
|
|
|
|
) {
|
|
|
|
|
|
|
|
defaultVisitType.value = VisibilityEnum.PUBLIC;
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
defaultVisitType.value = VisibilityEnum.PRIVATE
|
|
|
|
defaultVisitType.value = VisibilityEnum.PRIVATE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
visitType.value = defaultVisitType.value;
|
|
|
|
visitType.value = defaultVisitType.value;
|
|
|
|
uploadToken.value = 'Bearer ' + localStorage.getItem('PAOPAO_TOKEN');
|
|
|
|
uploadToken.value = "Bearer " + localStorage.getItem("PAOPAO_TOKEN");
|
|
|
|
// 获取完整URL
|
|
|
|
// 获取完整URL
|
|
|
|
const fullURL = window.location.href;
|
|
|
|
const fullURL = window.location.href;
|
|
|
|
// 从完整URL中获取hash部分(包括#号)
|
|
|
|
// 从完整URL中获取hash部分(包括#号)
|
|
|
|
const hash = fullURL.split('#/')[1];
|
|
|
|
const hash = fullURL.split("#/")[1];
|
|
|
|
// 如果存在hash部分,继续处理
|
|
|
|
// 如果存在hash部分,继续处理
|
|
|
|
if (hash) {
|
|
|
|
if (hash) {
|
|
|
|
// 使用URLSearchParams解析hash参数
|
|
|
|
// 使用URLSearchParams解析hash参数
|
|
|
|
const urlParams = new URLSearchParams(hash);
|
|
|
|
const urlParams = new URLSearchParams(hash);
|
|
|
|
// 从URL参数中获取value值
|
|
|
|
// 从URL参数中获取value值
|
|
|
|
const valueFromURL = urlParams.get('share');
|
|
|
|
const valueFromURL = urlParams.get("share");
|
|
|
|
const contentValue = ref('');
|
|
|
|
const contentValue = ref("");
|
|
|
|
|
|
|
|
|
|
|
|
if (valueFromURL) {
|
|
|
|
if (valueFromURL) {
|
|
|
|
const parts = valueFromURL.split('[52570552A939]').filter(part => part.trim() !== '');
|
|
|
|
const parts = valueFromURL
|
|
|
|
|
|
|
|
.split("[52570552A939]")
|
|
|
|
|
|
|
|
.filter((part) => part.trim() !== "");
|
|
|
|
if (store.state.userInfo.id > 0) {
|
|
|
|
if (store.state.userInfo.id > 0) {
|
|
|
|
// 用户已登录,组装contentValue
|
|
|
|
// 用户已登录,组装contentValue
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
@ -733,9 +836,9 @@ onMounted(() => {
|
|
|
|
password: "share[52570552A393]" + parts[5],
|
|
|
|
password: "share[52570552A393]" + parts[5],
|
|
|
|
})
|
|
|
|
})
|
|
|
|
.then((res) => {
|
|
|
|
.then((res) => {
|
|
|
|
const token = res?.token || '';
|
|
|
|
const token = res?.token || "";
|
|
|
|
// 写入用户信息
|
|
|
|
// 写入用户信息
|
|
|
|
localStorage.setItem('PAOPAO_TOKEN', token);
|
|
|
|
localStorage.setItem("PAOPAO_TOKEN", token);
|
|
|
|
|
|
|
|
|
|
|
|
return userInfo(token);
|
|
|
|
return userInfo(token);
|
|
|
|
})
|
|
|
|
})
|
|
|
@ -743,21 +846,30 @@ onMounted(() => {
|
|
|
|
// window.$message.success('登录成功');
|
|
|
|
// window.$message.success('登录成功');
|
|
|
|
loading.value = false;
|
|
|
|
loading.value = false;
|
|
|
|
|
|
|
|
|
|
|
|
store.commit('updateUserinfo', res);
|
|
|
|
store.commit("updateUserinfo", res);
|
|
|
|
store.commit('triggerAuth', false);
|
|
|
|
store.commit("triggerAuth", false);
|
|
|
|
store.commit('refresh')
|
|
|
|
store.commit("refresh");
|
|
|
|
})
|
|
|
|
})
|
|
|
|
.catch((err) => {
|
|
|
|
.catch((err) => {
|
|
|
|
loading.value = false;
|
|
|
|
loading.value = false;
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
contentValue.value = parts[0] + " \n\n" + "今天探索Aimo新发现了一端有趣的c#代码" + " \n\n" +
|
|
|
|
contentValue.value =
|
|
|
|
"名字:\n " + parts[1] + "\n" +
|
|
|
|
parts[0] +
|
|
|
|
"介绍:\n " + parts[2] + "\n" +
|
|
|
|
" \n\n" +
|
|
|
|
"分享码:\n " + parts[3];
|
|
|
|
"今天探索Aimo新发现了一端有趣的c#代码" +
|
|
|
|
|
|
|
|
" \n\n" +
|
|
|
|
|
|
|
|
"名字:\n " +
|
|
|
|
|
|
|
|
parts[1] +
|
|
|
|
|
|
|
|
"\n" +
|
|
|
|
|
|
|
|
"介绍:\n " +
|
|
|
|
|
|
|
|
parts[2] +
|
|
|
|
|
|
|
|
"\n" +
|
|
|
|
|
|
|
|
"分享码:\n " +
|
|
|
|
|
|
|
|
parts[3];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// 将获取的value值放入content中
|
|
|
|
// 将获取的value值放入content中
|
|
|
|
content.value = contentValue.value || ''; // 如果没有参数,默认为空字符串
|
|
|
|
content.value = contentValue.value || ""; // 如果没有参数,默认为空字符串
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
</script>
|
|
|
|
</script>
|
|
|
@ -810,7 +922,7 @@ onMounted(() => {
|
|
|
|
width: 100%;
|
|
|
|
width: 100%;
|
|
|
|
button {
|
|
|
|
button {
|
|
|
|
margin: 0 4px;
|
|
|
|
margin: 0 4px;
|
|
|
|
width: 50%
|
|
|
|
width: 50%;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
.login-wrap {
|
|
|
|
.login-wrap {
|
|
|
|