web source add biome to format/chec code

pull/628/head
alimy 1 year ago
parent cd686e4080
commit 8183c8ebdd

1
web/.gitignore vendored

@ -23,6 +23,7 @@ dist-ssr
*.sln
*.sw?
bun.lock
yarn.lock
package-lock.json
components.d.ts

@ -0,0 +1,30 @@
{
"$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
"organizeImports": {
"enabled": true
},
"vcs": {
"enabled": true,
"clientKind": "git",
"useIgnoreFile": true
},
"formatter": {
"indentStyle": "space"
},
"javascript": {
"formatter": {
"quoteStyle": "single"
}
},
"css": {
"parser": {
"cssModules": true
}
},
"linter": {
"enabled": true,
"rules": {
"recommended": true
}
}
}

@ -6,6 +6,8 @@
"dev": "vite",
"build": "vite build",
"preview": "vite preview",
"check": "biome check --write",
"format": "biome format --write",
"tauri": "tauri"
},
"dependencies": {
@ -32,6 +34,7 @@
"vuex": "^4.1.0"
},
"devDependencies": {
"@biomejs/biome": "^1.9.4",
"@tauri-apps/cli": "^1.5.10",
"@types/node": "^22.14.1",
"@types/qrcode": "^1.5.5",

@ -1,10 +1,9 @@
self.addEventListener("install", (event) => {
event.waitUntil((async () => {})());
});
self.addEventListener("activate", (event) => {
event.waitUntil((async () => {})());
});
self.addEventListener("fetch", (event) => {});
self.addEventListener('install', (event) => {
event.waitUntil((async () => {})());
});
self.addEventListener('activate', (event) => {
event.waitUntil((async () => {})());
});
self.addEventListener('fetch', (event) => {});

@ -52,18 +52,20 @@ const store = useStore();
const theme = computed(() => (store.state.theme === 'dark' ? darkTheme : null));
function loadSiteProfile() {
store.commit('loadDefaultSiteProfile');
if (import.meta.env.VITE_USE_WEB_PROFILE.toLowerCase() === "true") {
getSiteProfile().then((res) => {
store.commit('updateSiteProfile', res);
}).catch((err) => {
console.log(err);
});
}
store.commit('loadDefaultSiteProfile');
if (import.meta.env.VITE_USE_WEB_PROFILE.toLowerCase() === 'true') {
getSiteProfile()
.then((res) => {
store.commit('updateSiteProfile', res);
})
.catch((err) => {
console.log(err);
});
}
}
onMounted(() => {
loadSiteProfile();
loadSiteProfile();
});
</script>

@ -1,39 +1,47 @@
import { request } from '@/utils/request';
/** 用户登录 */
export const userLogin = (params: NetParams.AuthUserLogin): Promise<NetReq.AuthUserLogin> => {
return request({
method: 'post',
url: '/v1/auth/login',
data: params,
});
export const userLogin = (
params: NetParams.AuthUserLogin,
): Promise<NetReq.AuthUserLogin> => {
return request({
method: 'post',
url: '/v1/auth/login',
data: params,
});
};
/** 注册用户 */
export const userRegister = (params: NetParams.AuthUserRegister): Promise<NetReq.AuthUserRegister> => {
return request({
method: 'post',
url: '/v1/auth/register',
data: params,
});
export const userRegister = (
params: NetParams.AuthUserRegister,
): Promise<NetReq.AuthUserRegister> => {
return request({
method: 'post',
url: '/v1/auth/register',
data: params,
});
};
/** 用户信息 */
export const userInfo = (token: NetParams.AuthUserInfo = ""): Promise<NetReq.AuthUserInfo> => {
return request({
method: 'get',
url: '/v1/user/info',
headers: {
Authorization: `Bearer ${token}`,
},
});
export const userInfo = (
token: NetParams.AuthUserInfo = '',
): Promise<NetReq.AuthUserInfo> => {
return request({
method: 'get',
url: '/v1/user/info',
headers: {
Authorization: `Bearer ${token}`,
},
});
};
/** 修改用户密码,该接口暂时未使用 */
export const updateUserPassword = (data: NetParams.AuthUpdateUserPassword): Promise<NetReq.AuthUpdateUserPassword> => {
return request({
method: 'post',
url: '/v1/api/user/password',
data,
});
export const updateUserPassword = (
data: NetParams.AuthUpdateUserPassword,
): Promise<NetReq.AuthUpdateUserPassword> => {
return request({
method: 'post',
url: '/v1/api/user/password',
data,
});
};

@ -1,320 +1,320 @@
import { request } from "@/utils/request";
import { request } from '@/utils/request';
/** 获取动态列表 */
export const getPosts = (
params: NetParams.PostGetPosts
params: NetParams.PostGetPosts,
): Promise<NetReq.PostGetPosts> => {
return request({
method: "get",
url: "/v1/posts",
method: 'get',
url: '/v1/posts',
params,
});
};
/** 获取标签列表 */
export const getTags = (
params: NetParams.PostGetTags
params: NetParams.PostGetTags,
): Promise<NetReq.PostGetTags> => {
return request({
method: "get",
url: "/v1/tags",
method: 'get',
url: '/v1/tags',
params,
});
};
/** 获取动态详情 */
export const getPost = (
params: NetParams.PostGetPost
params: NetParams.PostGetPost,
): Promise<NetReq.PostGetPost> => {
return request({
method: "get",
url: "/v1/post",
method: 'get',
url: '/v1/post',
params,
});
};
/** 获取动态点赞状态 */
export const getPostStar = (
params: NetParams.PostPostStar
params: NetParams.PostPostStar,
): Promise<NetReq.PostGetPostStar> => {
return request({
method: "get",
url: "/v1/post/star",
method: 'get',
url: '/v1/post/star',
params,
});
};
/** 动态点赞 */
export const postStar = (
data: NetParams.PostPostStar
data: NetParams.PostPostStar,
): Promise<NetReq.PostPostStar> => {
return request({
method: "post",
url: "/v1/post/star",
method: 'post',
url: '/v1/post/star',
data,
});
};
/** 获取动态收藏状态 */
export const getPostCollection = (
params: NetParams.PostGetPostCollection
params: NetParams.PostGetPostCollection,
): Promise<NetReq.PostGetPostCollection> => {
return request({
method: "get",
url: "/v1/post/collection",
method: 'get',
url: '/v1/post/collection',
params,
});
};
/** 动态收藏 */
export const postCollection = (
data: NetParams.PostPostCollection
data: NetParams.PostPostCollection,
): Promise<NetReq.PostPostCollection> => {
return request({
method: "post",
url: "/v1/post/collection",
method: 'post',
url: '/v1/post/collection',
data,
});
};
/** 获取动态评论列表 */
export const getPostComments = (
params: NetParams.PostGetPostComments
params: NetParams.PostGetPostComments,
): Promise<NetReq.PostGetPostComments> => {
return request({
method: "get",
url: "/v1/post/comments",
method: 'get',
url: '/v1/post/comments',
params,
});
};
/** 获取联系人列表 */
export const getContacts = (
params: NetParams.GetContacts
params: NetParams.GetContacts,
): Promise<NetReq.GetContacts> => {
return request({
method: "get",
url: "/v1/user/contacts",
method: 'get',
url: '/v1/user/contacts',
params,
});
};
/** 获取联系人列表 */
export const getIndexTrends = (
params: NetParams.IndexTrendsReq
params: NetParams.IndexTrendsReq,
): Promise<NetReq.IndexTrendsResp> => {
return request({
method: "get",
url: "/v1/trends/index",
method: 'get',
url: '/v1/trends/index',
params,
});
};
/** 发布动态 */
export const createPost = (
data: NetParams.PostCreatePost
data: NetParams.PostCreatePost,
): Promise<NetReq.PostCreatePost> => {
return request({
method: "post",
url: "/v1/post",
method: 'post',
url: '/v1/post',
data,
});
};
/** 删除动态 */
export const deletePost = (
data: NetParams.PostDeletePost
data: NetParams.PostDeletePost,
): Promise<NetReq.PostDeletePost> => {
return request({
method: "delete",
url: "/v1/post",
method: 'delete',
url: '/v1/post',
data,
});
};
/** 锁定/解锁动态 */
export const lockPost = (
data: NetParams.PostLockPost
data: NetParams.PostLockPost,
): Promise<NetReq.PostLockPost> => {
return request({
method: "post",
url: "/v1/post/lock",
method: 'post',
url: '/v1/post/lock',
data,
});
};
/** 置顶/取消置顶动态 */
export const stickPost = (
data: NetParams.PostStickPost
data: NetParams.PostStickPost,
): Promise<NetReq.PostStickPost> => {
return request({
method: "post",
url: "/v1/post/stick",
method: 'post',
url: '/v1/post/stick',
data,
});
};
/** 设为亮点/取消亮点动态 */
export const highlightPost = (
data: NetParams.PostHighlightPost
data: NetParams.PostHighlightPost,
): Promise<NetReq.PostHighlightPost> => {
return request({
method: "post",
url: "/v1/post/highlight",
method: 'post',
url: '/v1/post/highlight',
data,
});
};
/** 置顶/取消置顶动态 */
export const visibilityPost = (
data: NetParams.PostVisibilityPost
data: NetParams.PostVisibilityPost,
): Promise<NetReq.PostVisibilityPost> => {
return request({
method: "post",
url: "/v1/post/visibility",
method: 'post',
url: '/v1/post/visibility',
data,
});
};
/** 点赞评论 */
export const thumbsUpTweetComment = (
data: NetParams.PostTweetCommentThumbs
data: NetParams.PostTweetCommentThumbs,
): Promise<NetReq.PostTweetCommentThumbs> => {
return request({
method: "post",
url: "/v1/tweet/comment/thumbsup",
method: 'post',
url: '/v1/tweet/comment/thumbsup',
data,
});
};
/** 点踩评论 */
export const thumbsDownTweetComment = (
data: NetParams.PostTweetCommentThumbs
data: NetParams.PostTweetCommentThumbs,
): Promise<NetReq.PostTweetCommentThumbs> => {
return request({
method: "post",
url: "/v1/tweet/comment/thumbsdown",
method: 'post',
url: '/v1/tweet/comment/thumbsdown',
data,
});
};
/** 点赞评论回复 */
export const thumbsUpTweetReply = (
data: NetParams.PostTweetReplyThumbs
data: NetParams.PostTweetReplyThumbs,
): Promise<NetReq.PostTweetReplyThumbs> => {
return request({
method: "post",
url: "/v1/tweet/reply/thumbsup",
method: 'post',
url: '/v1/tweet/reply/thumbsup',
data,
});
};
/** 点踩评论回复 */
export const thumbsDownTweetReply = (
data: NetParams.PostTweetReplyThumbs
data: NetParams.PostTweetReplyThumbs,
): Promise<NetReq.PostTweetReplyThumbs> => {
return request({
method: "post",
url: "/v1/tweet/reply/thumbsdown",
method: 'post',
url: '/v1/tweet/reply/thumbsdown',
data,
});
};
/** 发布动态评论 */
export const createComment = (
data: NetParams.PostCreateComment
data: NetParams.PostCreateComment,
): Promise<NetReq.PostCreateComment> => {
return request({
method: "post",
url: "/v1/post/comment",
method: 'post',
url: '/v1/post/comment',
data,
});
};
/** 删除评论 */
export const deleteComment = (
data: NetParams.PostDeleteComment
data: NetParams.PostDeleteComment,
): Promise<NetReq.PostDeleteComment> => {
return request({
method: "delete",
url: "/v1/post/comment",
method: 'delete',
url: '/v1/post/comment',
data,
});
};
/** 精选评论 */
export const highlightComment = (
data: NetParams.PostHighlightComment
data: NetParams.PostHighlightComment,
): Promise<NetReq.PostHighlightComment> => {
return request({
method: "post",
url: "/v1/post/comment/highlight",
method: 'post',
url: '/v1/post/comment/highlight',
data,
});
};
/** 发布评论回复 */
export const createCommentReply = (
data: NetParams.PostCreateCommentReply
data: NetParams.PostCreateCommentReply,
): Promise<NetReq.PostCreateCommentReply> => {
return request({
method: "post",
url: "/v1/post/comment/reply",
method: 'post',
url: '/v1/post/comment/reply',
data,
});
};
/** 删除评论回复 */
export const deleteCommentReply = (
data: NetParams.PostDeleteCommentReply
data: NetParams.PostDeleteCommentReply,
): Promise<NetReq.PostDeleteCommentReply> => {
return request({
method: "delete",
url: "/v1/post/comment/reply",
method: 'delete',
url: '/v1/post/comment/reply',
data,
});
};
/** 置顶/取消置顶话题 */
export const stickTopic = (
data: NetParams.PostStickTopic
data: NetParams.PostStickTopic,
): Promise<NetReq.PostStickTopic> => {
return request({
method: "post",
url: "/v1/topic/stick",
method: 'post',
url: '/v1/topic/stick',
data,
});
};
/** 置顶/取消置顶话题 */
export const pinTopic = (
data: NetParams.PostPinTopic
data: NetParams.PostPinTopic,
): Promise<NetReq.PostPinTopic> => {
return request({
method: "post",
url: "/v1/topic/pin",
method: 'post',
url: '/v1/topic/pin',
data,
});
};
/** 关注话题 */
export const followTopic = (
data: NetParams.PostFollowTopic
data: NetParams.PostFollowTopic,
): Promise<NetReq.PostFollowTopic> => {
return request({
method: "post",
url: "/v1/topic/follow",
method: 'post',
url: '/v1/topic/follow',
data,
});
};
/** 取消关注话题 */
export const unfollowTopic = (
data: NetParams.PostUnfollowTopic
data: NetParams.PostUnfollowTopic,
): Promise<NetReq.PostUnfollowTopic> => {
return request({
method: "post",
url: "/v1/topic/unfollow",
method: 'post',
url: '/v1/topic/unfollow',
data,
});
};

@ -1,9 +1,9 @@
import { request } from "@/utils/request";
import { request } from '@/utils/request';
/** 获取站点概要信息 */
export const getSiteProfile = (): Promise<NetReq.SiteProfile> => {
return request({
method: "get",
url: "/v1/site/profile",
method: 'get',
url: '/v1/site/profile',
});
};

@ -1,12 +1,12 @@
import { request } from "@/utils/request";
import { request } from '@/utils/request';
/** 获取验证码 */
export const getCaptcha = (
params: NetParams.UserGetCaptcha = {}
params: NetParams.UserGetCaptcha = {},
): Promise<NetReq.UserGetCaptcha> => {
return request({
method: "get",
url: "/v1/captcha",
method: 'get',
url: '/v1/captcha',
params,
});
};
@ -18,8 +18,8 @@ export const getCaptcha = (
*/
export const sendCaptcha = (data: any) => {
return request({
method: "post",
url: "/v1/captcha",
method: 'post',
url: '/v1/captcha',
data,
});
};
@ -30,11 +30,11 @@ export const sendCaptcha = (data: any) => {
* @returns Promise
*/
export const sendUserWhisper = (
data: NetParams.UserWhisper
data: NetParams.UserWhisper,
): Promise<NetParams.UserWhisper> => {
return request({
method: "post",
url: "/v1/user/whisper",
method: 'post',
url: '/v1/user/whisper',
data,
});
};
@ -45,11 +45,11 @@ export const sendUserWhisper = (
* @returns Promise
*/
export const requestingFriend = (
data: NetParams.RequestingFriend
data: NetParams.RequestingFriend,
): Promise<NetReq.RequestingFriend> => {
return request({
method: "post",
url: "/v1/friend/requesting",
method: 'post',
url: '/v1/friend/requesting',
data,
});
};
@ -60,33 +60,33 @@ export const requestingFriend = (
* @returns Promise
*/
export const addFriend = (
data: NetParams.AddFriend
data: NetParams.AddFriend,
): Promise<NetReq.AddFriend> => {
return request({
method: "post",
url: "/v1/friend/add",
method: 'post',
url: '/v1/friend/add',
data,
});
};
// 关注 用户
export const followUser = (
data: NetParams.FollowUserReq
data: NetParams.FollowUserReq,
): Promise<NetReq.FollowUserResp> => {
return request({
method: "post",
url: "/v1/user/follow",
method: 'post',
url: '/v1/user/follow',
data,
});
};
// 取消关注 用户
export const unfollowUser = (
data: NetParams.UnfollowUserReq
data: NetParams.UnfollowUserReq,
): Promise<NetReq.UnfollowUserResp> => {
return request({
method: "post",
url: "/v1/user/unfollow",
method: 'post',
url: '/v1/user/unfollow',
data,
});
};
@ -97,11 +97,11 @@ export const unfollowUser = (
* @returns Promise
*/
export const getUserFollows = (
params: NetParams.GetUserFollows
params: NetParams.GetUserFollows,
): Promise<NetReq.GetContacts> => {
return request({
method: "get",
url: "/v1/user/follows",
method: 'get',
url: '/v1/user/follows',
params,
});
};
@ -112,11 +112,11 @@ export const getUserFollows = (
* @returns Promise
*/
export const getUserFollowings = (
params: NetParams.GetUserFollowings
params: NetParams.GetUserFollowings,
): Promise<NetReq.GetContacts> => {
return request({
method: "get",
url: "/v1/user/followings",
method: 'get',
url: '/v1/user/followings',
params,
});
};
@ -127,11 +127,11 @@ export const getUserFollowings = (
* @returns Promise
*/
export const rejectFriend = (
data: NetParams.RejectFriend
data: NetParams.RejectFriend,
): Promise<NetReq.RejectFriend> => {
return request({
method: "post",
url: "/v1/friend/reject",
method: 'post',
url: '/v1/friend/reject',
data,
});
};
@ -142,11 +142,11 @@ export const rejectFriend = (
* @returns Promise
*/
export const deleteFriend = (
data: NetParams.DeleteFriend
data: NetParams.DeleteFriend,
): Promise<NetReq.DeleteFriend> => {
return request({
method: "post",
url: "/v1/friend/delete",
method: 'post',
url: '/v1/friend/delete',
data,
});
};
@ -157,11 +157,11 @@ export const deleteFriend = (
* @returns Promise
*/
export const getContacts = (
data: NetParams.GetContacts
data: NetParams.GetContacts,
): Promise<NetReq.GetContacts> => {
return request({
method: "post",
url: "/v1/user/contacts",
method: 'post',
url: '/v1/user/contacts',
data,
});
};
@ -172,11 +172,11 @@ export const getContacts = (
* @returns Promise
*/
export const bindUserPhone = (
data: NetParams.UserBindUserPhone
data: NetParams.UserBindUserPhone,
): Promise<NetParams.UserBindUserPhone> => {
return request({
method: "post",
url: "/v1/user/phone",
method: 'post',
url: '/v1/user/phone',
data,
});
};
@ -187,33 +187,33 @@ export const bindUserPhone = (
* @returns Promise
*/
export const activateUser = (
data: NetParams.UserActivation
data: NetParams.UserActivation,
): Promise<NetParams.UserActivation> => {
return request({
method: "post",
url: "/v1/user/activate",
method: 'post',
url: '/v1/user/activate',
data,
});
};
/** 更改密码 */
export const changePassword = (
data: NetParams.UserChangePassword
data: NetParams.UserChangePassword,
): Promise<NetReq.UserChangePassword> => {
return request({
method: "post",
url: "/v1/user/password",
method: 'post',
url: '/v1/user/password',
data,
});
};
/** 更改昵称 */
export const changeNickname = (
data: NetParams.UserChangeNickname
data: NetParams.UserChangeNickname,
): Promise<NetReq.UserChangeNickname> => {
return request({
method: "post",
url: "/v1/user/nickname",
method: 'post',
url: '/v1/user/nickname',
data,
});
};
@ -225,30 +225,30 @@ export const changeNickname = (
*/
export const changeAvatar = (data: any) => {
return request({
method: "post",
url: "/v1/user/avatar",
method: 'post',
url: '/v1/user/avatar',
data,
});
};
/** 获取未读消息数 */
export const getUnreadMsgCount = (
params: NetParams.UserGetUnreadMsgCount = {}
params: NetParams.UserGetUnreadMsgCount = {},
): Promise<NetReq.UserGetUnreadMsgCount> => {
return request({
method: "get",
url: "/v1/user/msgcount/unread",
method: 'get',
url: '/v1/user/msgcount/unread',
params,
});
};
/** 获取消息列表 */
export const getMessages = (
params: NetParams.UserGetMessages
params: NetParams.UserGetMessages,
): Promise<NetReq.UserGetMessages> => {
return request({
method: "get",
url: "/v1/user/messages",
method: 'get',
url: '/v1/user/messages',
params,
});
};
@ -259,11 +259,11 @@ export const getMessages = (
* @returns Promise
*/
export const readMessage = (
data: NetParams.ReadMessageReq
data: NetParams.ReadMessageReq,
): Promise<NetReq.ReadMessageResp> => {
return request({
method: "post",
url: "/v1/user/message/read",
method: 'post',
url: '/v1/user/message/read',
data,
});
};
@ -274,51 +274,51 @@ export const readMessage = (
*/
export const readAllMessage = (): Promise<NetReq.ReadAllMessageResp> => {
return request({
method: "post",
url: "/v1/user/message/readall",
method: 'post',
url: '/v1/user/message/readall',
});
};
/** 获取收藏列表 */
export const getCollections = (
params: NetParams.UserGetCollections
params: NetParams.UserGetCollections,
): Promise<NetReq.UserGetCollections> => {
return request({
method: "get",
url: "/v1/user/collections",
method: 'get',
url: '/v1/user/collections',
params,
});
};
/** 获取用户基础信息 */
export const getUserProfile = (
params: NetParams.UserGetUserProfile
params: NetParams.UserGetUserProfile,
): Promise<NetReq.UserGetUserProfile> => {
return request({
method: "get",
url: "/v1/user/profile",
method: 'get',
url: '/v1/user/profile',
params,
});
};
/** 获取用户帖子列表 */
export const getUserPosts = (
params: NetParams.UserGetUserPosts
params: NetParams.UserGetUserPosts,
): Promise<NetReq.UserGetUserPosts> => {
return request({
method: "get",
url: "/v1/user/posts",
method: 'get',
url: '/v1/user/posts',
params,
});
};
/** 获取账单列表 */
export const getBills = (
params: NetParams.UserGetBills
params: NetParams.UserGetBills,
): Promise<NetReq.UserGetBills> => {
return request({
method: "get",
url: "/v1/user/wallet/bills",
method: 'get',
url: '/v1/user/wallet/bills',
params,
});
};
@ -329,11 +329,11 @@ export const getBills = (
* @returns Promise
*/
export const reqRecharge = (
data: NetParams.UserReqRecharge
data: NetParams.UserReqRecharge,
): Promise<NetReq.UserReqRecharge> => {
return request({
method: "post",
url: "/v1/user/recharge",
method: 'post',
url: '/v1/user/recharge',
data,
});
};
@ -344,11 +344,11 @@ export const reqRecharge = (
* @returns Promise
*/
export const getRecharge = (
params: NetParams.UserGetRecharge
params: NetParams.UserGetRecharge,
): Promise<NetReq.UserGetRecharge> => {
return request({
method: "get",
url: "/v1/user/recharge",
method: 'get',
url: '/v1/user/recharge',
params,
});
};
@ -362,8 +362,8 @@ export const getSuggestUsers = (params: {
k: string;
}): Promise<NetReq.UserGetSuggestUsers> => {
return request({
method: "get",
url: "/v1/suggest/users",
method: 'get',
url: '/v1/suggest/users',
params,
});
};
@ -377,8 +377,8 @@ export const getSuggestTags = (params: {
k: string;
}): Promise<NetReq.UserGetSuggestTags> => {
return request({
method: "get",
url: "/v1/suggest/tags",
method: 'get',
url: '/v1/suggest/tags',
params,
});
};
@ -389,11 +389,11 @@ export const getSuggestTags = (params: {
* @returns Promise
*/
export const precheckAttachment = (
params: NetParams.UserPrecheckAttachment
params: NetParams.UserPrecheckAttachment,
): Promise<NetReq.UserPrecheckAttachment> => {
return request({
method: "get",
url: "/v1/attachment/precheck",
method: 'get',
url: '/v1/attachment/precheck',
params,
});
};
@ -404,11 +404,11 @@ export const precheckAttachment = (
* @returns Promise
*/
export const getAttachment = (
params: NetParams.UserGetAttachment
params: NetParams.UserGetAttachment,
): Promise<NetReq.UserGetAttachment> => {
return request({
method: "get",
url: "/v1/attachment",
method: 'get',
url: '/v1/attachment',
params,
});
};
@ -419,11 +419,11 @@ export const getAttachment = (
* @returns Promise
*/
export const changeUserStatus = (
data: NetParams.UserStatusReq
data: NetParams.UserStatusReq,
): Promise<NetReq.UserChangeStatus> => {
return request({
method: "post",
url: "/v1/admin/user/status",
method: 'post',
url: '/v1/admin/user/status',
data,
});
};
@ -434,7 +434,7 @@ export const changeUserStatus = (
*/
export const getSiteInfo = (): Promise<NetReq.SiteInfoResp> => {
return request({
method: "get",
url: "/v1/admin/site/status",
method: 'get',
url: '/v1/admin/site/status',
});
};

@ -164,134 +164,134 @@ const store = useStore();
const loading = ref(false);
const loginRef = ref<FormInst>();
const loginForm = reactive({
username: '',
password: '',
username: '',
password: '',
});
const registerRef = ref<FormInst>();
const registerForm = reactive({
username: '',
password: '',
repassword: '',
username: '',
password: '',
repassword: '',
});
const registerRule = {
username: {
required: true,
message: '',
username: {
required: true,
message: '',
},
password: {
required: true,
message: '',
},
repassword: [
{
required: true,
message: '',
},
password: {
required: true,
message: '',
{
validator: (rule: FormItemRule, value: any) => {
return (
!!registerForm.password &&
registerForm.password.startsWith(value) &&
registerForm.password.length >= value.length
);
},
message: '',
trigger: 'input',
},
repassword: [
{
required: true,
message: '',
},
{
validator: (rule: FormItemRule, value: any) => {
return (
!!registerForm.password &&
registerForm.password.startsWith(value) &&
registerForm.password.length >= value.length
);
},
message: '',
trigger: 'input',
},
],
],
};
const handleLogin = (e: Event) => {
e.preventDefault();
e.stopPropagation();
e.preventDefault();
e.stopPropagation();
loginRef.value?.validate((errors) => {
if (!errors) {
loading.value = true;
loginRef.value?.validate((errors) => {
if (!errors) {
loading.value = true;
userLogin({
username: loginForm.username,
password: loginForm.password,
})
.then((res) => {
const token = res?.token || '';
// 写入用户信息
localStorage.setItem('PAOPAO_TOKEN', token);
userLogin({
username: loginForm.username,
password: loginForm.password,
})
.then((res) => {
const token = res?.token || '';
// 写入用户信息
localStorage.setItem('PAOPAO_TOKEN', token);
return userInfo(token);
})
.then((res) => {
window.$message.success('');
loading.value = false;
return userInfo(token);
})
.then((res) => {
window.$message.success('');
loading.value = false;
store.commit('updateUserinfo', res);
store.commit('triggerAuth', false);
store.commit('refresh')
loginForm.username = '';
loginForm.password = '';
})
.catch((err) => {
loading.value = false;
});
}
});
store.commit('updateUserinfo', res);
store.commit('triggerAuth', false);
store.commit('refresh');
loginForm.username = '';
loginForm.password = '';
})
.catch((err) => {
loading.value = false;
});
}
});
};
const handleRegister = (e: Event) => {
e.preventDefault();
e.stopPropagation();
e.preventDefault();
e.stopPropagation();
registerRef.value?.validate((errors) => {
if (!errors) {
loading.value = true;
registerRef.value?.validate((errors) => {
if (!errors) {
loading.value = true;
userRegister({
username: registerForm.username,
password: registerForm.password,
})
.then((res) => {
return userLogin({
username: registerForm.username,
password: registerForm.password,
});
})
.then((res) => {
const token = res?.token || '';
// 写入用户信息
localStorage.setItem('PAOPAO_TOKEN', token);
userRegister({
username: registerForm.username,
password: registerForm.password,
})
.then((res) => {
return userLogin({
username: registerForm.username,
password: registerForm.password,
});
})
.then((res) => {
const token = res?.token || '';
// 写入用户信息
localStorage.setItem('PAOPAO_TOKEN', token);
return userInfo(token);
})
.then((res) => {
window.$message.success('');
loading.value = false;
return userInfo(token);
})
.then((res) => {
window.$message.success('');
loading.value = false;
store.commit('updateUserinfo', res);
store.commit('triggerAuth', false);
registerForm.username = '';
registerForm.password = '';
registerForm.repassword = '';
})
.catch((err) => {
loading.value = false;
});
}
});
store.commit('updateUserinfo', res);
store.commit('triggerAuth', false);
registerForm.username = '';
registerForm.password = '';
registerForm.repassword = '';
})
.catch((err) => {
loading.value = false;
});
}
});
};
onMounted(() => {
const token = localStorage.getItem('PAOPAO_TOKEN') || '';
if (token) {
userInfo(token)
.then((res) => {
store.commit('updateUserinfo', res);
store.commit('triggerAuth', false);
})
.catch((err) => {
store.commit('userLogout');
});
} else {
const token = localStorage.getItem('PAOPAO_TOKEN') || '';
if (token) {
userInfo(token)
.then((res) => {
store.commit('updateUserinfo', res);
store.commit('triggerAuth', false);
})
.catch((err) => {
store.commit('userLogout');
}
});
} else {
store.commit('userLogout');
}
});
</script>

@ -142,90 +142,93 @@ const replyAtUsername = ref('');
const replyComposeRef = ref();
const emit = defineEmits<{
(e: 'reload'): void
(e: 'reload'): void;
}>();
const props = withDefaults(defineProps<{
comment: Item.CommentProps,
postUserId: number
}>(), {})
const props = withDefaults(
defineProps<{
comment: Item.CommentProps;
postUserId: number;
}>(),
{},
);
const comment = computed(() => {
let comment: Item.CommentComponentProps = Object.assign(
{
texts: [],
imgs: [],
},
props.comment
);
comment.contents.map((content :any) => {
if (+content.type === 1 || +content.type === 2) {
comment.texts.push(content);
}
if (+content.type === 3) {
comment.imgs.push(content);
}
});
return comment;
let comment: Item.CommentComponentProps = Object.assign(
{
texts: [],
imgs: [],
},
props.comment,
);
comment.contents.map((content: any) => {
if (+content.type === 1 || +content.type === 2) {
comment.texts.push(content);
}
if (+content.type === 3) {
comment.imgs.push(content);
}
});
return comment;
});
const doClickText = (e: MouseEvent, id: number | string) => {
let _target = e.target as any;
if (_target.dataset.detail) {
const d = _target.dataset.detail.split(':');
if (d.length === 2) {
store.commit('refresh');
if (d[0] === 'tag') {
window.$message.warning('');
} else {
router.push({
name: 'user',
query: {
s: d[1],
},
});
}
}
let _target = e.target as any;
if (_target.dataset.detail) {
const d = _target.dataset.detail.split(':');
if (d.length === 2) {
store.commit('refresh');
if (d[0] === 'tag') {
window.$message.warning('');
} else {
router.push({
name: 'user',
query: {
s: d[1],
},
});
}
}
}
};
const focusReply = (reply: Item.ReplyProps) => {
replyAtUserID.value = reply.user_id;
replyAtUsername.value = reply.user?.username || '';
replyComposeRef.value?.switchReply(true);
replyAtUserID.value = reply.user_id;
replyAtUsername.value = reply.user?.username || '';
replyComposeRef.value?.switchReply(true);
};
const reload = () => {
emit('reload');
emit('reload');
};
const resetReply = () => {
replyAtUserID.value = 0;
replyAtUsername.value = '';
replyAtUserID.value = 0;
replyAtUsername.value = '';
};
const execDelAction = () => {
deleteComment({
id: comment.value.id,
deleteComment({
id: comment.value.id,
})
.then((_res) => {
window.$message.success('');
setTimeout(() => {
reload();
}, 50);
})
.then((_res) => {
window.$message.success('');
setTimeout(() => {
reload();
}, 50);
})
.catch((_err) => {});
.catch((_err) => {});
};
const execHightlightAction = () => {
highlightComment({
id: comment.value.id,
highlightComment({
id: comment.value.id,
})
.then((res) => {
comment.value.is_essence = res.highlight_status;
window.$message.success('');
setTimeout(() => {
reload();
}, 50);
})
.then((res) => {
comment.value.is_essence = res.highlight_status;
window.$message.success("操作成功");
setTimeout(() => {
reload();
}, 50);
})
.catch((_err) => {});
.catch((_err) => {});
};
</script>

@ -170,26 +170,24 @@
import { onMounted, computed, ref } from 'vue';
import { useStore } from 'vuex';
import { debounce } from 'lodash';
import {
ImageOutline,
} from '@vicons/ionicons5';
import { ImageOutline } from '@vicons/ionicons5';
import { createComment } from '@/api/post';
import { getSuggestUsers } from '@/api/user';
import { parsePostTag } from '@/utils/content';
import type { MentionOption, UploadFileInfo, UploadInst } from 'naive-ui';
const emit = defineEmits<{
(e: 'post-success'): void;
(e: 'post-success'): void;
}>();
const props = withDefaults(
defineProps<{
lock: number;
postId: number;
}>(),
{
lock: 0,
postId: 0,
}
defineProps<{
lock: number;
postId: number;
}>(),
{
lock: 0,
postId: 0,
},
);
const store = useStore();
@ -203,185 +201,194 @@ const uploadRef = ref<UploadInst>();
const uploadType = ref('public/image');
const fileQueue = ref<UploadFileInfo[]>([]);
const imageContents = ref<Item.CommentItemProps[]>([]);
const allowUserRegister = ref(import.meta.env.VITE_ALLOW_USER_REGISTER.toLowerCase() === 'true')
const defaultCommentMaxLength = Number(import.meta.env.VITE_DEFAULT_COMMENT_MAX_LENGTH)
const allowUserRegister = ref(
import.meta.env.VITE_ALLOW_USER_REGISTER.toLowerCase() === 'true',
);
const defaultCommentMaxLength = Number(
import.meta.env.VITE_DEFAULT_COMMENT_MAX_LENGTH,
);
const uploadGateway = import.meta.env.VITE_HOST + '/v1/attachment';
const uploadToken = computed(() => {
return 'Bearer ' + localStorage.getItem('PAOPAO_TOKEN');
return 'Bearer ' + localStorage.getItem('PAOPAO_TOKEN');
});
// 加载at用户列表
const loadSuggestionUsers = debounce((k) => {
getSuggestUsers({
k,
})
.then((res) => {
let options: MentionOption[] = [];
res.suggest.map((i) => {
options.push({
label: i,
value: i,
});
});
optionsRef.value = options;
loading.value = false;
})
.catch((err) => {
loading.value = false;
getSuggestUsers({
k,
})
.then((res) => {
let options: MentionOption[] = [];
res.suggest.map((i) => {
options.push({
label: i,
value: i,
});
});
optionsRef.value = options;
loading.value = false;
})
.catch((err) => {
loading.value = false;
});
}, 200);
const handleSearch = (k: string, prefix: string) => {
if (loading.value) {
return;
}
loading.value = true;
if (prefix === '@') {
loadSuggestionUsers(k);
}
if (loading.value) {
return;
}
loading.value = true;
if (prefix === '@') {
loadSuggestionUsers(k);
}
};
const changeContent = (v: string) => {
if (v.length > defaultCommentMaxLength) {
content.value = v.substring(0, defaultCommentMaxLength);
} else {
content.value = v;
}
if (v.length > defaultCommentMaxLength) {
content.value = v.substring(0, defaultCommentMaxLength);
} else {
content.value = v;
}
};
const setUploadType = (type: string) => {
uploadType.value = type;
uploadType.value = type;
};
const updateUpload = (list: UploadFileInfo[]) => {
for (let i = 0; i < list.length; i++) {
var name = list[i].name;
var basename: string = name.split('.').slice(0, -1).join('.');
var ext: string = name.split('.').pop()!;
if (basename.length > 30) {
list[i].name = basename.substring(0, 18) + "..." + basename.substring(basename.length-9) + "." + ext;
}
for (let i = 0; i < list.length; i++) {
var name = list[i].name;
var basename: string = name.split('.').slice(0, -1).join('.');
var ext: string = name.split('.').pop()!;
if (basename.length > 30) {
list[i].name =
basename.substring(0, 18) +
'...' +
basename.substring(basename.length - 9) +
'.' +
ext;
}
fileQueue.value = list;
}
fileQueue.value = list;
};
const beforeUpload = async (data: any) => {
// 图片类型校验
if (
uploadType.value === 'public/image' &&
!['image/png', 'image/jpg', 'image/jpeg', 'image/gif'].includes(
(data.file as any).file?.type
)
) {
window.$message.warning(' png/jpg/gif ');
return false;
}
// 图片类型校验
if (
uploadType.value === 'public/image' &&
!['image/png', 'image/jpg', 'image/jpeg', 'image/gif'].includes(
(data.file as any).file?.type,
)
) {
window.$message.warning(' png/jpg/gif ');
return false;
}
if (
uploadType.value === 'image' &&
(data.file as any).file?.size > 10485760
) {
window.$message.warning('10MB');
return false;
}
if (
uploadType.value === 'image' &&
(data.file as any).file?.size > 10485760
) {
window.$message.warning('10MB');
return false;
}
return true;
return true;
};
const finishUpload = ({ file, event }: any): any => {
try {
let data = JSON.parse(event.target?.response);
try {
let data = JSON.parse(event.target?.response);
if (data.code === 0) {
if (uploadType.value === 'public/image') {
imageContents.value.push({
id: file.id,
content: data.data.content,
} as Item.CommentItemProps);
}
}
} catch (error) {
window.$message.error('');
if (data.code === 0) {
if (uploadType.value === 'public/image') {
imageContents.value.push({
id: file.id,
content: data.data.content,
} as Item.CommentItemProps);
}
}
} catch (error) {
window.$message.error('');
}
};
const failUpload = ({ file, event }: any): any => {
try {
let data = JSON.parse(event.target?.response);
try {
let data = JSON.parse(event.target?.response);
if (data.code !== 0) {
let errMsg = data.msg || '';
if (data.details && data.details.length > 0) {
data.details.map((detail: string) => {
errMsg += ':' + detail;
});
}
window.$message.error(errMsg);
}
} catch (error) {
window.$message.error('');
if (data.code !== 0) {
let errMsg = data.msg || '';
if (data.details && data.details.length > 0) {
data.details.map((detail: string) => {
errMsg += ':' + detail;
});
}
window.$message.error(errMsg);
}
} catch (error) {
window.$message.error('');
}
};
const removeUpload = ({ file }: any) => {
let idx = imageContents.value.findIndex((item) => item.id === file.id);
if (idx > -1) {
imageContents.value.splice(idx, 1);
}
let idx = imageContents.value.findIndex((item) => item.id === file.id);
if (idx > -1) {
imageContents.value.splice(idx, 1);
}
};
const focusComment = () => {
showBtn.value = true;
showBtn.value = true;
};
const cancelComment = () => {
showBtn.value = false;
// 置空
uploadRef.value?.clear();
fileQueue.value = [];
content.value = '';
imageContents.value = [];
showBtn.value = false;
// 置空
uploadRef.value?.clear();
fileQueue.value = [];
content.value = '';
imageContents.value = [];
};
// 发布动态
const submitPost = () => {
if (content.value.trim().length === 0) {
window.$message.warning('');
return;
}
if (content.value.trim().length === 0) {
window.$message.warning('');
return;
}
// 解析用户at
let { users } = parsePostTag(content.value);
// 解析用户at
let { users } = parsePostTag(content.value);
const contents = [];
let sort = 100;
const contents = [];
let sort = 100;
contents.push({
content: content.value,
type: 2, // 文字
sort,
});
imageContents.value.map((img) => {
sort++;
contents.push({
content: content.value,
type: 2, // 文字
sort,
});
imageContents.value.map((img) => {
sort++;
contents.push({
content: img.content,
type: 3, // 图片
sort,
});
content: img.content,
type: 3, // 图片
sort,
});
});
submitting.value = true;
createComment({
contents,
post_id: props.postId,
users: Array.from(new Set(users)),
})
.then((res) => {
window.$message.success('');
submitting.value = false;
emit('post-success');
submitting.value = true;
createComment({
contents,
post_id: props.postId,
users: Array.from(new Set(users)),
})
.then((res) => {
window.$message.success('');
submitting.value = false;
emit('post-success');
// 置空
cancelComment();
})
.catch((err) => {
submitting.value = false;
});
// 置空
cancelComment();
})
.catch((err) => {
submitting.value = false;
});
};
const triggerAuth = (key: string) => {
store.commit('triggerAuth', true);
store.commit('triggerAuthKey', key);
store.commit('triggerAuth', true);
store.commit('triggerAuthKey', key);
};
</script>

@ -57,103 +57,112 @@
import { ref } from 'vue';
import { useStore } from 'vuex';
import { formatPrettyTime } from '@/utils/formatTime';
import { createCommentReply, thumbsUpTweetComment, thumbsDownTweetComment } from '@/api/post';
import {
createCommentReply,
thumbsUpTweetComment,
thumbsDownTweetComment,
} from '@/api/post';
import { InputInst } from 'naive-ui';
import {
ThumbUpTwotone,
ThumbUpOutlined,
ThumbDownTwotone,
ThumbDownOutlined,
ThumbUpTwotone,
ThumbUpOutlined,
ThumbDownTwotone,
ThumbDownOutlined,
} from '@vicons/material';
import { YesNoEnum } from '@/utils/IEnum';
const props = withDefaults(defineProps<{
comment: Item.CommentProps,
atUserid: number,
atUsername: string,
}>(), {
const props = withDefaults(
defineProps<{
comment: Item.CommentProps;
atUserid: number;
atUsername: string;
}>(),
{
atUserid: 0,
atUsername: ''
});
atUsername: '',
},
);
const store = useStore();
const emit = defineEmits<{
(e: 'reload'): void,
(e: 'reset'): void
(e: 'reload'): void;
(e: 'reset'): void;
}>();
const inputInstRef = ref<InputInst>();
const showReply = ref(false);
const replyContent = ref('');
const submitting = ref(false);
const defaultReplyMaxLength = Number(import.meta.env.VITE_DEFAULT_REPLY_MAX_LENGTH)
const hasThumbsUp = ref(props.comment.is_thumbs_up == YesNoEnum.YES)
const hasThumbsDown = ref(props.comment.is_thumbs_down == YesNoEnum.YES)
const thumbsUpCount = ref(props.comment.thumbs_up_count)
const defaultReplyMaxLength = Number(
import.meta.env.VITE_DEFAULT_REPLY_MAX_LENGTH,
);
const hasThumbsUp = ref(props.comment.is_thumbs_up == YesNoEnum.YES);
const hasThumbsDown = ref(props.comment.is_thumbs_down == YesNoEnum.YES);
const thumbsUpCount = ref(props.comment.thumbs_up_count);
const handleThumbsUp = () => {
thumbsUpTweetComment({
tweet_id: props.comment.post_id,
comment_id: props.comment.id,
thumbsUpTweetComment({
tweet_id: props.comment.post_id,
comment_id: props.comment.id,
})
.then((_res) => {
hasThumbsUp.value = !hasThumbsUp.value;
if (hasThumbsUp.value) {
thumbsUpCount.value++;
hasThumbsDown.value = false;
} else {
thumbsUpCount.value--;
}
})
.then((_res) => {
hasThumbsUp.value = !hasThumbsUp.value
if (hasThumbsUp.value) {
thumbsUpCount.value++
hasThumbsDown.value = false
} else {
thumbsUpCount.value--
}
})
.catch((err) => {
console.log(err);
});
.catch((err) => {
console.log(err);
});
};
const handleThumbsDown = () => {
thumbsDownTweetComment({
tweet_id: props.comment.post_id,
comment_id: props.comment.id,
thumbsDownTweetComment({
tweet_id: props.comment.post_id,
comment_id: props.comment.id,
})
.then((_res) => {
hasThumbsDown.value = !hasThumbsDown.value;
if (hasThumbsDown.value) {
if (hasThumbsUp.value) {
thumbsUpCount.value--;
hasThumbsUp.value = false;
}
}
})
.then((_res) => {
hasThumbsDown.value = !hasThumbsDown.value
if (hasThumbsDown.value) {
if (hasThumbsUp.value) {
thumbsUpCount.value--
hasThumbsUp.value = false
}
}
})
.catch((err) => {
console.log(err);
});
.catch((err) => {
console.log(err);
});
};
const switchReply = (status: boolean) => {
showReply.value = status;
if (status) {
setTimeout(() => {
inputInstRef.value?.focus();
}, 10);
} else {
submitting.value = false;
replyContent.value = '';
emit('reset');
}
showReply.value = status;
if (status) {
setTimeout(() => {
inputInstRef.value?.focus();
}, 10);
} else {
submitting.value = false;
replyContent.value = '';
emit('reset');
}
};
const submitReply = () => {
submitting.value = true;
createCommentReply({
comment_id: props.comment.id,
at_user_id: props.atUserid,
content: replyContent.value,
submitting.value = true;
createCommentReply({
comment_id: props.comment.id,
at_user_id: props.atUserid,
content: replyContent.value,
})
.then((res) => {
switchReply(false);
window.$message.success('');
emit('reload');
})
.then((res) => {
switchReply(false);
window.$message.success('');
emit('reload');
})
.catch((err) => {
submitting.value = false;
});
.catch((err) => {
submitting.value = false;
});
};
defineExpose({ switchReply });
</script>

@ -281,11 +281,11 @@ import { debounce } from 'lodash';
import { getSuggestUsers, getSuggestTags } from '@/api/user';
import {
ImageOutline,
VideocamOutline,
AttachOutline,
CompassOutline,
EyeOutline,
ImageOutline,
VideocamOutline,
AttachOutline,
CompassOutline,
EyeOutline,
} from '@vicons/ionicons5';
import { createPost } from '@/api/post';
import { parsePostTag } from '@/utils/content';
@ -294,7 +294,7 @@ import type { MentionOption, UploadFileInfo, UploadInst } from 'naive-ui';
import { VisibilityEnum, PostItemTypeEnum } from '@/utils/IEnum';
const emit = defineEmits<{
(e: 'post-success', post: Item.PostProps): void;
(e: 'post-success', post: Item.PostProps): void;
}>();
const store = useStore();
@ -315,323 +315,329 @@ const imageContents = ref<Item.CommentItemProps[]>([]);
const videoContents = ref<Item.CommentItemProps[]>([]);
const attachmentContents = ref<Item.AttachmentProps[]>([]);
const visitType = ref<VisibilityEnum>(VisibilityEnum.PUBLIC);
const defaultVisitType = ref<VisibilityEnum>(VisibilityEnum.PUBLIC)
const defaultVisitType = ref<VisibilityEnum>(VisibilityEnum.PUBLIC);
const allowTweetVisibility = ref(import.meta.env.VITE_ALLOW_TWEET_VISIBILITY.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 = computed(() => {
return 'Bearer ' + localStorage.getItem('PAOPAO_TOKEN');
return 'Bearer ' + localStorage.getItem('PAOPAO_TOKEN');
});
const visibilities = computed(()=> {
let res = [
{value: VisibilityEnum.PUBLIC, label: "公开"},
{value: VisibilityEnum.PRIVATE, label: "私密"},
{value: VisibilityEnum.Following, label: "关注可见"},
];
if (store.state.profile.useFriendship) {
res.push({value: VisibilityEnum.FRIEND, label: "好友可见"});
}
return res;
const visibilities = computed(() => {
let res = [
{ value: VisibilityEnum.PUBLIC, label: '' },
{ value: VisibilityEnum.PRIVATE, label: '' },
{ value: VisibilityEnum.Following, label: '' },
];
if (store.state.profile.useFriendship) {
res.push({ value: VisibilityEnum.FRIEND, label: '' });
}
return res;
});
const switchLink = () => {
showLinkSet.value = !showLinkSet.value;
if (showLinkSet.value && showEyeSet.value) {
showEyeSet.value = false
}
showLinkSet.value = !showLinkSet.value;
if (showLinkSet.value && showEyeSet.value) {
showEyeSet.value = false;
}
};
const switchEye = () => {
showEyeSet.value = !showEyeSet.value;
if (showEyeSet.value && showLinkSet.value) {
showLinkSet.value = false
}
showEyeSet.value = !showEyeSet.value;
if (showEyeSet.value && showLinkSet.value) {
showLinkSet.value = false;
}
};
// 加载at用户列表
const loadSuggestionUsers = debounce((k) => {
getSuggestUsers({
k,
})
.then((res) => {
let options: MentionOption[] = [];
res.suggest.map((i) => {
options.push({
label: i,
value: i,
});
});
optionsRef.value = options;
loading.value = false;
})
.catch((err) => {
loading.value = false;
getSuggestUsers({
k,
})
.then((res) => {
let options: MentionOption[] = [];
res.suggest.map((i) => {
options.push({
label: i,
value: i,
});
});
optionsRef.value = options;
loading.value = false;
})
.catch((err) => {
loading.value = false;
});
}, 200);
// 加载推荐tag列表
const loadSuggestionTags = debounce((k) => {
getSuggestTags({
k,
})
.then((res) => {
let options: MentionOption[] = [];
res.suggest.map((i) => {
options.push({
label: i,
value: i,
});
});
optionsRef.value = options;
loading.value = false;
})
.catch((err) => {
loading.value = false;
getSuggestTags({
k,
})
.then((res) => {
let options: MentionOption[] = [];
res.suggest.map((i) => {
options.push({
label: i,
value: i,
});
});
optionsRef.value = options;
loading.value = false;
})
.catch((err) => {
loading.value = false;
});
}, 200);
const handleSearch = (k: string, prefix: string) => {
if (loading.value) {
return;
}
loading.value = true;
if (prefix === '@') {
loadSuggestionUsers(k);
} else {
loadSuggestionTags(k);
}
if (loading.value) {
return;
}
loading.value = true;
if (prefix === '@') {
loadSuggestionUsers(k);
} else {
loadSuggestionTags(k);
}
};
const changeContent = (v: string) => {
if (v.length > store.state.profile.defaultTweetMaxLength) {
content.value = v.substring(0, store.state.profile.defaultTweetMaxLength);
} else {
content.value = v;
}
if (v.length > store.state.profile.defaultTweetMaxLength) {
content.value = v.substring(0, store.state.profile.defaultTweetMaxLength);
} else {
content.value = v;
}
};
const setUploadType = (type: string) => {
uploadType.value = type;
uploadType.value = type;
};
const updateUpload = (list: UploadFileInfo[]) => {
for (let i = 0; i < list.length; i++) {
var name = list[i].name;
var basename: string = name.split('.').slice(0, -1).join('.');
var ext: string = name.split('.').pop()!;
if (basename.length > 30) {
list[i].name = basename.substring(0, 18) + "..." + basename.substring(basename.length-9) + "." + ext;
}
for (let i = 0; i < list.length; i++) {
var name = list[i].name;
var basename: string = name.split('.').slice(0, -1).join('.');
var ext: string = name.split('.').pop()!;
if (basename.length > 30) {
list[i].name =
basename.substring(0, 18) +
'...' +
basename.substring(basename.length - 9) +
'.' +
ext;
}
fileQueue.value = list;
}
fileQueue.value = list;
};
const beforeUpload = async (data: any) => {
// 图片类型校验
if (
uploadType.value === 'public/image' &&
!['image/webp', 'image/png', 'image/jpg', 'image/jpeg', 'image/gif'].includes(
data.file.file?.type
)
) {
window.$message.warning(' webp/png/jpg/gif ');
return false;
}
if (uploadType.value === 'image' && data.file.file?.size > 10485760) {
window.$message.warning('10MB');
return false;
}
// 视频类型校验
if (
uploadType.value === 'public/video' &&
!['video/mp4', 'video/quicktime'].includes(data.file.file?.type)
) {
window.$message.warning(' mp4/mov ');
return false;
}
if (
uploadType.value === 'public/video' &&
data.file.file?.size > 104857600
) {
window.$message.warning('100MB');
return false;
}
// 附件类型校验
if (
uploadType.value === 'attachment' && !(await isZipFile(data.file.file))
) {
window.$message.warning(' zip ');
return false;
}
if (uploadType.value === 'attachment' && data.file.file?.size > 104857600) {
window.$message.warning('100MB');
return false;
}
return true;
// 图片类型校验
if (
uploadType.value === 'public/image' &&
![
'image/webp',
'image/png',
'image/jpg',
'image/jpeg',
'image/gif',
].includes(data.file.file?.type)
) {
window.$message.warning(' webp/png/jpg/gif ');
return false;
}
if (uploadType.value === 'image' && data.file.file?.size > 10485760) {
window.$message.warning('10MB');
return false;
}
// 视频类型校验
if (
uploadType.value === 'public/video' &&
!['video/mp4', 'video/quicktime'].includes(data.file.file?.type)
) {
window.$message.warning(' mp4/mov ');
return false;
}
if (uploadType.value === 'public/video' && data.file.file?.size > 104857600) {
window.$message.warning('100MB');
return false;
}
// 附件类型校验
if (uploadType.value === 'attachment' && !(await isZipFile(data.file.file))) {
window.$message.warning(' zip ');
return false;
}
if (uploadType.value === 'attachment' && data.file.file?.size > 104857600) {
window.$message.warning('100MB');
return false;
}
return true;
};
const finishUpload = ({ file, event }: any): any => {
try {
let data = JSON.parse(event.target?.response);
if (data.code === 0) {
if (uploadType.value === 'public/image') {
imageContents.value.push({
id: file.id,
content: data.data.content,
} as Item.CommentItemProps);
}
if (uploadType.value === 'public/video') {
videoContents.value.push({
id: file.id,
content: data.data.content,
} as Item.CommentItemProps);
}
if (uploadType.value === 'attachment') {
attachmentContents.value.push({
id: file.id,
content: data.data.content,
} as Item.AttachmentProps);
}
}
} catch (error) {
window.$message.error('');
try {
let data = JSON.parse(event.target?.response);
if (data.code === 0) {
if (uploadType.value === 'public/image') {
imageContents.value.push({
id: file.id,
content: data.data.content,
} as Item.CommentItemProps);
}
if (uploadType.value === 'public/video') {
videoContents.value.push({
id: file.id,
content: data.data.content,
} as Item.CommentItemProps);
}
if (uploadType.value === 'attachment') {
attachmentContents.value.push({
id: file.id,
content: data.data.content,
} as Item.AttachmentProps);
}
}
} catch (error) {
window.$message.error('');
}
};
const failUpload = ({ file, event }: any): any => {
try {
let data = JSON.parse(event.target?.response);
if (data.code !== 0) {
let errMsg = data.msg || '';
if (data.details && data.details.length > 0) {
data.details.map((detail: string) => {
errMsg += ':' + detail;
});
}
window.$message.error(errMsg);
}
} catch (error) {
window.$message.error('');
try {
let data = JSON.parse(event.target?.response);
if (data.code !== 0) {
let errMsg = data.msg || '';
if (data.details && data.details.length > 0) {
data.details.map((detail: string) => {
errMsg += ':' + detail;
});
}
window.$message.error(errMsg);
}
} catch (error) {
window.$message.error('');
}
};
const removeUpload = ({ file }: any) => {
let idx = imageContents.value.findIndex((item) => item.id === file.id);
if (idx > -1) {
imageContents.value.splice(idx, 1);
}
idx = videoContents.value.findIndex((item) => item.id === file.id);
if (idx > -1) {
videoContents.value.splice(idx, 1);
}
idx = attachmentContents.value.findIndex((item) => item.id === file.id);
if (idx > -1) {
attachmentContents.value.splice(idx, 1);
}
let idx = imageContents.value.findIndex((item) => item.id === file.id);
if (idx > -1) {
imageContents.value.splice(idx, 1);
}
idx = videoContents.value.findIndex((item) => item.id === file.id);
if (idx > -1) {
videoContents.value.splice(idx, 1);
}
idx = attachmentContents.value.findIndex((item) => item.id === file.id);
if (idx > -1) {
attachmentContents.value.splice(idx, 1);
}
};
// 发布动态
const submitPost = () => {
if (content.value.trim().length === 0) {
window.$message.warning('');
return;
}
if (content.value.trim().length === 0) {
window.$message.warning('');
return;
}
// 解析用户at及tag
let { tags, users } = parsePostTag(content.value);
// 解析用户at及tag
let { tags, users } = parsePostTag(content.value);
const contents = [];
let sort = 100;
const contents = [];
let sort = 100;
contents.push({
content: content.value,
type: PostItemTypeEnum.TEXT, // 文字
sort,
});
imageContents.value.map((img) => {
sort++;
contents.push({
content: content.value,
type: PostItemTypeEnum.TEXT, // 文字
sort,
content: img.content,
type: PostItemTypeEnum.IMAGEURL, // 图片
sort,
});
imageContents.value.map((img) => {
sort++;
contents.push({
content: img.content,
type: PostItemTypeEnum.IMAGEURL, // 图片
sort,
});
});
videoContents.value.map((video) => {
sort++;
contents.push({
content: video.content,
type: PostItemTypeEnum.VIDEOURL, // 视频
sort,
});
videoContents.value.map((video) => {
sort++;
contents.push({
content: video.content,
type: PostItemTypeEnum.VIDEOURL, // 视频
sort,
});
});
attachmentContents.value.map((attachment) => {
sort++;
contents.push({
content: attachment.content,
type: PostItemTypeEnum.ATTACHMENT, // 附件
sort,
});
attachmentContents.value.map((attachment) => {
sort++;
contents.push({
content: attachment.content,
type: PostItemTypeEnum.ATTACHMENT, // 附件
sort,
});
});
if (links.value.length > 0) {
links.value.map((link) => {
sort++;
contents.push({
content: link,
type: PostItemTypeEnum.LINKURL, // 链接
sort,
});
});
if (links.value.length > 0) {
links.value.map((link) => {
sort++;
contents.push({
content: link,
type: PostItemTypeEnum.LINKURL, // 链接
sort,
});
});
}
submitting.value = true;
createPost({
contents,
tags: Array.from(new Set(tags)),
users: Array.from(new Set(users)),
attachment_price: +attachmentPrice.value * 100,
visibility: visitType.value
}
submitting.value = true;
createPost({
contents,
tags: Array.from(new Set(tags)),
users: Array.from(new Set(users)),
attachment_price: +attachmentPrice.value * 100,
visibility: visitType.value,
})
.then((res) => {
window.$message.success('');
submitting.value = false;
emit('post-success', res);
// 置空
showLinkSet.value = false;
showEyeSet.value = false;
uploadRef.value?.clear();
fileQueue.value = [];
content.value = '';
links.value = [];
imageContents.value = [];
videoContents.value = [];
attachmentContents.value = [];
visitType.value = defaultVisitType.value;
})
.then((res) => {
window.$message.success('');
submitting.value = false;
emit('post-success', res);
// 置空
showLinkSet.value = false;
showEyeSet.value = false;
uploadRef.value?.clear();
fileQueue.value = [];
content.value = '';
links.value = [];
imageContents.value = [];
videoContents.value = [];
attachmentContents.value = [];
visitType.value = defaultVisitType.value;
})
.catch((err) => {
submitting.value = false;
});
.catch((err) => {
submitting.value = false;
});
};
const triggerAuth = (key: string) => {
store.commit('triggerAuth', true);
store.commit('triggerAuthKey', key);
store.commit('triggerAuth', true);
store.commit('triggerAuthKey', key);
};
onMounted(() => {
const defaultVisibility = store.state.profile.defaultTweetVisibility
if (store.state.profile.useFriendship && defaultVisibility === 'friend') {
defaultVisitType.value = VisibilityEnum.FRIEND
} else if (defaultVisibility === 'following') {
defaultVisitType.value = VisibilityEnum.Following
} else if (defaultVisibility === 'public') {
defaultVisitType.value = VisibilityEnum.PUBLIC
} else {
defaultVisitType.value = VisibilityEnum.PRIVATE
}
visitType.value = defaultVisitType.value;
const defaultVisibility = store.state.profile.defaultTweetVisibility;
if (store.state.profile.useFriendship && defaultVisibility === 'friend') {
defaultVisitType.value = VisibilityEnum.FRIEND;
} else if (defaultVisibility === 'following') {
defaultVisitType.value = VisibilityEnum.Following;
} else if (defaultVisibility === 'public') {
defaultVisitType.value = VisibilityEnum.PUBLIC;
} else {
defaultVisitType.value = VisibilityEnum.PRIVATE;
}
visitType.value = defaultVisitType.value;
});
</script>

@ -57,65 +57,64 @@
<script setup lang="ts">
import { h, computed } from 'vue';
import { NIcon } from 'naive-ui'
import type { Component } from 'vue'
import { NIcon } from 'naive-ui';
import type { Component } from 'vue';
import { DropdownOption } from 'naive-ui';
import { formatDate } from '@/utils/formatTime';
import { MoreHorizFilled } from '@vicons/material';
import {
PaperPlaneOutline,
} from '@vicons/ionicons5';
import { PaperPlaneOutline } from '@vicons/ionicons5';
const emit = defineEmits<{
(e: 'send-whisper', user: Item.UserInfo): void;
(e: 'send-whisper', user: Item.UserInfo): void;
}>();
const renderIcon = (icon: Component) => {
return () => {
return h(NIcon, null, {
default: () => h(icon)
})
}
default: () => h(icon),
});
};
};
const props = withDefaults(defineProps<{
contact: Item.ContactItemProps
}>(), {})
const props = withDefaults(
defineProps<{
contact: Item.ContactItemProps;
}>(),
{},
);
const actionOpts = computed(() => {
let options: DropdownOption[] = [
{
label: ' @' + props.contact.username,
key: 'whisper',
icon: renderIcon(PaperPlaneOutline)
},
];
return options;
let options: DropdownOption[] = [
{
label: ' @' + props.contact.username,
key: 'whisper',
icon: renderIcon(PaperPlaneOutline),
},
];
return options;
});
const handleAction = (
item: 'whisper'
) => {
switch (item) {
case 'whisper':
const user: Item.UserInfo = {
id: props.contact.user_id,
avatar: props.contact.avatar,
username: props.contact.username,
nickname: props.contact.nickname,
is_admin: false,
is_friend: true,
is_following: false,
created_on: 0,
follows: 0,
followings: 0,
status: 1,
}
emit('send-whisper', user);
break;
default:
break;
}
const handleAction = (item: 'whisper') => {
switch (item) {
case 'whisper':
const user: Item.UserInfo = {
id: props.contact.user_id,
avatar: props.contact.avatar,
username: props.contact.username,
nickname: props.contact.nickname,
is_admin: false,
is_friend: true,
is_following: false,
created_on: 0,
follows: 0,
followings: 0,
status: 1,
};
emit('send-whisper', user);
break;
default:
break;
}
};
</script>

@ -57,120 +57,122 @@
<script setup lang="ts">
import { h, computed } from 'vue';
import { NIcon } from 'naive-ui'
import type { Component } from 'vue'
import { NIcon } from 'naive-ui';
import type { Component } from 'vue';
import { useDialog, DropdownOption } from 'naive-ui';
import { followUser, unfollowUser } from '@/api/user';
import { formatDate } from '@/utils/formatTime';
import { MoreHorizFilled } from '@vicons/material';
import {
PaperPlaneOutline,
BodyOutline,
WalkOutline
} from '@vicons/ionicons5';
import { PaperPlaneOutline, BodyOutline, WalkOutline } from '@vicons/ionicons5';
const dialog = useDialog();
const emit = defineEmits<{
(e: 'send-whisper', user: Item.UserInfo): void;
(e: 'send-whisper', user: Item.UserInfo): void;
}>();
const renderIcon = (icon: Component) => {
return () => {
return h(NIcon, null, {
default: () => h(icon)
})
}
default: () => h(icon),
});
};
};
const handleFollowUser = () => {
dialog.success({
title: '',
content:
'' + (props.contact.is_following ? ' @' : ' @') + props.contact.username +' ',
positiveText: '',
negativeText: '',
onPositiveClick: () => {
if (props.contact.is_following) {
unfollowUser({
user_id: props.contact.user_id,
}).then((_res) => {
window.$message.success('');
props.contact.is_following=false;
})
.catch((err) => {
console.log(err);
});
} else {
followUser({
user_id: props.contact.user_id,
}).then((_res) => {
window.$message.success('');
props.contact.is_following=true;
})
.catch((err) => {
console.log(err);
});
}
},
});
dialog.success({
title: '',
content:
'' +
(props.contact.is_following ? ' @' : ' @') +
props.contact.username +
' ',
positiveText: '',
negativeText: '',
onPositiveClick: () => {
if (props.contact.is_following) {
unfollowUser({
user_id: props.contact.user_id,
})
.then((_res) => {
window.$message.success('');
props.contact.is_following = false;
})
.catch((err) => {
console.log(err);
});
} else {
followUser({
user_id: props.contact.user_id,
})
.then((_res) => {
window.$message.success('');
props.contact.is_following = true;
})
.catch((err) => {
console.log(err);
});
}
},
});
};
const props = withDefaults(defineProps<{
contact: Item.ContactItemProps
}>(), {})
const props = withDefaults(
defineProps<{
contact: Item.ContactItemProps;
}>(),
{},
);
const actionOpts = computed(() => {
let options: DropdownOption[] = [
{
label: ' @' + props.contact.username,
key: 'whisper',
icon: renderIcon(PaperPlaneOutline)
},
];
if (props.contact.is_following) {
options.push({
label: ' @' + props.contact.username,
key: 'unfollow',
icon: renderIcon(WalkOutline)
})
} else {
options.push({
label: ' @' + props.contact.username,
key: 'follow',
icon: renderIcon(BodyOutline)
})
}
return options;
let options: DropdownOption[] = [
{
label: ' @' + props.contact.username,
key: 'whisper',
icon: renderIcon(PaperPlaneOutline),
},
];
if (props.contact.is_following) {
options.push({
label: ' @' + props.contact.username,
key: 'unfollow',
icon: renderIcon(WalkOutline),
});
} else {
options.push({
label: ' @' + props.contact.username,
key: 'follow',
icon: renderIcon(BodyOutline),
});
}
return options;
});
const handleAction = (
item: 'follow' | 'unfollow' | 'whisper'
) => {
switch (item) {
case 'follow':
case 'unfollow':
handleFollowUser();
break;
case 'whisper':
const user: Item.UserInfo = {
id: props.contact.user_id,
avatar: props.contact.avatar,
username: props.contact.username,
nickname: props.contact.nickname,
is_admin: false,
is_friend: true,
is_following: false,
created_on: 0,
follows: 0,
followings: 0,
status: 1,
}
emit('send-whisper', user);
break;
default:
break;
}
const handleAction = (item: 'follow' | 'unfollow' | 'whisper') => {
switch (item) {
case 'follow':
case 'unfollow':
handleFollowUser();
break;
case 'whisper':
const user: Item.UserInfo = {
id: props.contact.user_id,
avatar: props.contact.avatar,
username: props.contact.username,
nickname: props.contact.nickname,
is_admin: false,
is_friend: true,
is_following: false,
created_on: 0,
follows: 0,
followings: 0,
status: 1,
};
emit('send-whisper', user);
break;
default:
break;
}
};
</script>

@ -64,57 +64,62 @@
import { onMounted, ref } from 'vue';
import { useStore } from 'vuex';
import { useRouter } from 'vue-router';
import { useMessage, useOsTheme, DrawerPlacement} from 'naive-ui';
import { LightModeOutlined, DarkModeOutlined, ChevronLeftRound, DehazeRound } from '@vicons/material';
import { useMessage, useOsTheme, DrawerPlacement } from 'naive-ui';
import {
LightModeOutlined,
DarkModeOutlined,
ChevronLeftRound,
DehazeRound,
} from '@vicons/material';
const store = useStore();
const router = useRouter();
const activeDrawerRef = ref(false)
const placementRef = ref<DrawerPlacement>('left')
const activeDrawerRef = ref(false);
const placementRef = ref<DrawerPlacement>('left');
const props = withDefaults(
defineProps<{
title: string;
back?: boolean;
theme?: boolean;
}>(),
{
title: '',
back: false,
theme: true,
}
defineProps<{
title: string;
back?: boolean;
theme?: boolean;
}>(),
{
title: '',
back: false,
theme: true,
},
);
const switchTheme = (theme: boolean) => {
if (theme) {
localStorage.setItem('PAOPAO_THEME', 'dark');
store.commit('triggerTheme', 'dark');
} else {
localStorage.setItem('PAOPAO_THEME', 'light');
store.commit('triggerTheme', 'light');
}
if (theme) {
localStorage.setItem('PAOPAO_THEME', 'dark');
store.commit('triggerTheme', 'dark');
} else {
localStorage.setItem('PAOPAO_THEME', 'light');
store.commit('triggerTheme', 'light');
}
};
const goBack = () => {
if (window.history.length <= 1) {
router.push({
path: '/',
});
} else {
router.go(-1);
}
if (window.history.length <= 1) {
router.push({
path: '/',
});
} else {
router.go(-1);
}
};
const activeDrawer = () => {
activeDrawerRef.value = true
activeDrawerRef.value = true;
};
onMounted(() => {
if (!localStorage.getItem('PAOPAO_THEME')) {
switchTheme((useOsTheme() as unknown as string) === 'dark');
}
// 移动端特殊处理
if (!store.state.desktopModelShow) {
window.$store = store;
window.$message = useMessage();
}
if (!localStorage.getItem('PAOPAO_THEME')) {
switchTheme((useOsTheme() as unknown as string) === 'dark');
}
// 移动端特殊处理
if (!store.state.desktopModelShow) {
window.$store = store;
window.$message = useMessage();
}
});
</script>

@ -138,208 +138,237 @@
<script setup lang="ts">
import { h, computed } from 'vue';
import type { Component } from 'vue'
import { NIcon, useDialog } from 'naive-ui'
import type { Component } from 'vue';
import { NIcon, useDialog } from 'naive-ui';
import { useStore } from 'vuex';
import { useRouter } from 'vue-router';
import { DropdownOption } from 'naive-ui';
import { ShareOutline, CheckmarkOutline, CloseOutline, CheckmarkDoneOutline } from '@vicons/ionicons5';
import { readMessage, addFriend, rejectFriend, followUser, unfollowUser } from '@/api/user';
import {
ShareOutline,
CheckmarkOutline,
CloseOutline,
CheckmarkDoneOutline,
} from '@vicons/ionicons5';
import {
readMessage,
addFriend,
rejectFriend,
followUser,
unfollowUser,
} from '@/api/user';
import { formatRelativeTime } from '@/utils/formatTime';
import { MoreHorizFilled } from '@vicons/material';
import {
PaperPlaneOutline,
CheckmarkCircle,
BodyOutline,
WalkOutline,
} from '@vicons/ionicons5'
import {
PaperPlaneOutline,
CheckmarkCircle,
BodyOutline,
WalkOutline,
} from '@vicons/ionicons5';
const defaultavatar = 'https://assets.paopao.info/public/avatar/default/admin.png';
const defaultavatar =
'https://assets.paopao.info/public/avatar/default/admin.png';
const router = useRouter();
const store = useStore();
const dialog = useDialog();
const props = withDefaults(
defineProps<{
message: Item.MessageProps;
}>(),
{}
defineProps<{
message: Item.MessageProps;
}>(),
{},
);
const renderIcon = (icon: Component) => {
return () => {
return h(NIcon, null, {
default: () => h(icon)
})
}
default: () => h(icon),
});
};
};
const actionOpts = computed(() => {
let user = props.message.type == 4 && props.message.sender_user_id == store.state.userInfo.id
? props.message.receiver_user
: props.message.sender_user;
let options: DropdownOption[] = [
{
label: ' @' + user.username,
key: 'whisper',
icon: renderIcon(PaperPlaneOutline)
},
]
if (store.state.userInfo.id != user.id) {
if (user.is_following) {
options.push({
label: ' @' + user.username,
key: 'unfollow',
icon: renderIcon(WalkOutline)
})
} else {
options.push({
label: ' @' + user.username,
key: 'follow',
icon: renderIcon(BodyOutline)
})
}
let user =
props.message.type == 4 &&
props.message.sender_user_id == store.state.userInfo.id
? props.message.receiver_user
: props.message.sender_user;
let options: DropdownOption[] = [
{
label: ' @' + user.username,
key: 'whisper',
icon: renderIcon(PaperPlaneOutline),
},
];
if (store.state.userInfo.id != user.id) {
if (user.is_following) {
options.push({
label: ' @' + user.username,
key: 'unfollow',
icon: renderIcon(WalkOutline),
});
} else {
options.push({
label: ' @' + user.username,
key: 'follow',
icon: renderIcon(BodyOutline),
});
}
return options;
}
return options;
});
const emit = defineEmits<{
(e: 'send-whisper', user: Item.UserInfo): void
(e: 'reload'): void
(e: 'send-whisper', user: Item.UserInfo): void;
(e: 'reload'): void;
}>();
const onHandleFollowAction = (message: Item.MessageProps) => {
let user = message.type == 4 && message.sender_user_id == store.state.userInfo.id
? message.receiver_user
: message.sender_user;
dialog.success({
title: '',
content:
'' + (user.is_following ? ' @' : ' @') + user.username + ' ',
positiveText: '',
negativeText: '',
onPositiveClick: () => {
if (user.is_following) {
unfollowUser({
user_id: user.id,
}).then((_res) => {
window.$message.success('');
user.is_following = false;
// TODO: 这里暴力处理简单重新加载更好的做法是遍历所有message如果是对应user就更新到新状态
setTimeout(() => {
emit('reload');
}, 50);
})
.catch((_err) => {});
} else {
followUser({
user_id: user.id,
}).then((_res) => {
window.$message.success('');
user.is_following = true;
// TODO: 这里暴力处理简单重新加载更好的做法是遍历所有message如果是对应user就更新到新状态
setTimeout(() => {
emit('reload');
}, 50);
})
.catch((_err) => {});
}
},
});
let user =
message.type == 4 && message.sender_user_id == store.state.userInfo.id
? message.receiver_user
: message.sender_user;
dialog.success({
title: '',
content:
'' +
(user.is_following ? ' @' : ' @') +
user.username +
' ',
positiveText: '',
negativeText: '',
onPositiveClick: () => {
if (user.is_following) {
unfollowUser({
user_id: user.id,
})
.then((_res) => {
window.$message.success('');
user.is_following = false;
// TODO: 这里暴力处理简单重新加载更好的做法是遍历所有message如果是对应user就更新到新状态
setTimeout(() => {
emit('reload');
}, 50);
})
.catch((_err) => {});
} else {
followUser({
user_id: user.id,
})
.then((_res) => {
window.$message.success('');
user.is_following = true;
// TODO: 这里暴力处理简单重新加载更好的做法是遍历所有message如果是对应user就更新到新状态
setTimeout(() => {
emit('reload');
}, 50);
})
.catch((_err) => {});
}
},
});
};
const handleAction = (
item: 'whisper' | 'follow' | 'unfollow'
) => {
switch (item) {
case 'whisper':
const message = props.message
if (message.type != 99) {
let user = message.type == 4 && message.sender_user_id == store.state.userInfo.id
? message.receiver_user
: message.sender_user;
emit('send-whisper', user);
}
break;
case 'follow':
case 'unfollow':
onHandleFollowAction(props.message);
break;
default:
break;
}
const handleAction = (item: 'whisper' | 'follow' | 'unfollow') => {
switch (item) {
case 'whisper':
const message = props.message;
if (message.type != 99) {
let user =
message.type == 4 && message.sender_user_id == store.state.userInfo.id
? message.receiver_user
: message.sender_user;
emit('send-whisper', user);
}
break;
case 'follow':
case 'unfollow':
onHandleFollowAction(props.message);
break;
default:
break;
}
};
const isNotWhisperSender = computed(() => {
return props.message.type !== 4 || props.message.sender_user_id !== store.state.userInfo.id
return (
props.message.type !== 4 ||
props.message.sender_user_id !== store.state.userInfo.id
);
});
const isWhisperReceiver = computed(() => {
return props.message.type == 4 && props.message.receiver_user_id == store.state.userInfo.id
return (
props.message.type == 4 &&
props.message.receiver_user_id == store.state.userInfo.id
);
});
const isWhisperSender = computed(() => {
return props.message.type == 4 && props.message.sender_user_id == store.state.userInfo.id
return (
props.message.type == 4 &&
props.message.sender_user_id == store.state.userInfo.id
);
});
const viewDetail = (message: Item.MessageProps) => {
handleReadMessage(message);
if (message.type === 1 || message.type === 2 || message.type === 3) {
if (message.post && message.post.id > 0) {
router.push({
name: 'post',
query: {
id: message.post_id,
},
});
} else {
window.$message.error('');
}
handleReadMessage(message);
if (message.type === 1 || message.type === 2 || message.type === 3) {
if (message.post && message.post.id > 0) {
router.push({
name: 'post',
query: {
id: message.post_id,
},
});
} else {
window.$message.error('');
}
}
};
const agreeAddFriend = (message: Item.MessageProps) => {
handleReadMessage(message);
addFriend({
user_id: message.sender_user_id,
handleReadMessage(message);
addFriend({
user_id: message.sender_user_id,
})
.then((res) => {
message.reply_id = 2;
window.$message.success('');
})
.then((res) => {
message.reply_id = 2;
window.$message.success('');
})
.catch((err) => {
console.log(err);
});
}
.catch((err) => {
console.log(err);
});
};
const rejectAddFriend = (message: Item.MessageProps) => {
handleReadMessage(message);
rejectFriend({
user_id: message.sender_user_id,
handleReadMessage(message);
rejectFriend({
user_id: message.sender_user_id,
})
.then((res) => {
message.reply_id = 3;
window.$message.success('');
})
.then((res) => {
message.reply_id = 3;
window.$message.success('');
})
.catch((err) => {
console.log(err);
});
}
.catch((err) => {
console.log(err);
});
};
const handleReadMessage = (message: Item.MessageProps) => {
if (props.message.receiver_user_id != store.state.userInfo.id) {
return
}
if (message.is_read === 0) {
readMessage({
id: message.id,
}).then((_res) => {
message.is_read = 1;
})
.catch((err) => {
console.log(err);
});
}
if (props.message.receiver_user_id != store.state.userInfo.id) {
return;
}
if (message.is_read === 0) {
readMessage({
id: message.id,
})
.then((_res) => {
message.is_read = 1;
})
.catch((err) => {
console.log(err);
});
}
};
</script>

@ -8,11 +8,14 @@
</template>
<script setup lang="ts">
const props = withDefaults(defineProps<{
num: number
}>(), {
num: 1
});
const props = withDefaults(
defineProps<{
num: number;
}>(),
{
num: 1,
},
);
</script>
<style lang="less" scoped>

@ -128,247 +128,259 @@
<script setup lang="ts">
import { h, ref, computed } from 'vue';
import type { Component } from 'vue'
import { NIcon } from 'naive-ui'
import type { Component } from 'vue';
import { NIcon } from 'naive-ui';
import { useStore } from 'vuex';
import type { DropdownOption } from 'naive-ui';
import { useRouter } from 'vue-router';
import { formatPrettyDate } from '@/utils/formatTime';
import { preparePost } from '@/utils/content';
import { postStar, postCollection } from '@/api/post';
import {
postStar,
postCollection,
} from '@/api/post';
import {
PaperPlaneOutline,
HeartOutline,
BookmarkOutline,
ChatboxOutline,
ShareSocialOutline,
PersonAddOutline,
PersonRemoveOutline,
BodyOutline,
WalkOutline,
PaperPlaneOutline,
HeartOutline,
BookmarkOutline,
ChatboxOutline,
ShareSocialOutline,
PersonAddOutline,
PersonRemoveOutline,
BodyOutline,
WalkOutline,
} from '@vicons/ionicons5';
import { MoreHorizFilled } from '@vicons/material';
import copy from "copy-to-clipboard";
import copy from 'copy-to-clipboard';
const router = useRouter();
const store = useStore();
const inFoldStyle = ref<boolean>(true)
const props = withDefaults(defineProps<{
post: Item.PostProps,
isOwner: boolean,
addFriendAction: boolean,
addFollowAction: boolean,
}>(), {});
const inFoldStyle = ref<boolean>(true);
const props = withDefaults(
defineProps<{
post: Item.PostProps;
isOwner: boolean;
addFriendAction: boolean;
addFollowAction: boolean;
}>(),
{},
);
const emit = defineEmits<{
(e: 'send-whisper', user: Item.UserInfo): void
(e: 'handle-follow-action', user: Item.PostProps): void
(e: 'handle-friend-action', user: Item.PostProps): void
(e: 'send-whisper', user: Item.UserInfo): void;
(e: 'handle-follow-action', user: Item.PostProps): void;
(e: 'handle-friend-action', user: Item.PostProps): void;
}>();
const renderIcon = (icon: Component) => {
return () => {
return h(NIcon, null, {
default: () => h(icon)
})
}
default: () => h(icon),
});
};
};
const tweetOptions = computed(() => {
let options: DropdownOption[] = [];
if (!props.isOwner) {
options.push({
label: ' @' + props.post.user.username,
key: 'whisper',
icon: renderIcon(PaperPlaneOutline)
});
}
if (!props.isOwner && props.addFollowAction) {
if (props.post.user.is_following) {
options.push({
label: ' @' + props.post.user.username,
key: 'unfollow',
icon: renderIcon(WalkOutline)
})
} else {
options.push({
label: ' @' + props.post.user.username,
key: 'follow',
icon: renderIcon(BodyOutline)
})
}
}
if (!props.isOwner && props.addFriendAction) {
if (props.post.user.is_friend) {
options.push({
label: ' @' + props.post.user.username,
key: 'delete',
icon: renderIcon(PersonRemoveOutline)
});
} else {
options.push({
label: ' @' + props.post.user.username,
key: 'requesting',
icon: renderIcon(PersonAddOutline)
});
}
}
let options: DropdownOption[] = [];
if (!props.isOwner) {
options.push({
label: '',
key: 'copyTweetLink',
icon: renderIcon(ShareSocialOutline),
label: ' @' + props.post.user.username,
key: 'whisper',
icon: renderIcon(PaperPlaneOutline),
});
return options;
}
if (!props.isOwner && props.addFollowAction) {
if (props.post.user.is_following) {
options.push({
label: ' @' + props.post.user.username,
key: 'unfollow',
icon: renderIcon(WalkOutline),
});
} else {
options.push({
label: ' @' + props.post.user.username,
key: 'follow',
icon: renderIcon(BodyOutline),
});
}
}
if (!props.isOwner && props.addFriendAction) {
if (props.post.user.is_friend) {
options.push({
label: ' @' + props.post.user.username,
key: 'delete',
icon: renderIcon(PersonRemoveOutline),
});
} else {
options.push({
label: ' @' + props.post.user.username,
key: 'requesting',
icon: renderIcon(PersonAddOutline),
});
}
}
options.push({
label: '',
key: 'copyTweetLink',
icon: renderIcon(ShareSocialOutline),
});
return options;
});
const handleTweetAction = async (
item: 'copyTweetLink' | 'whisper' | 'follow' | 'unfollow' | 'delete' | 'requesting'
item:
| 'copyTweetLink'
| 'whisper'
| 'follow'
| 'unfollow'
| 'delete'
| 'requesting',
) => {
switch (item) {
case 'copyTweetLink':
copy(`${window.location.origin}/#/post?id=${post.value.id}&share=copy_link&t=${new Date().getTime()}`);
window.$message.success('');
break;
case 'whisper':
emit('send-whisper', props.post.user);
break;
case 'delete':
case 'requesting':
emit('handle-friend-action', props.post);
break;
case 'follow':
case 'unfollow':
emit('handle-follow-action', props.post);
break;
default:
break;
}
switch (item) {
case 'copyTweetLink':
copy(
`${window.location.origin}/#/post?id=${post.value.id}&share=copy_link&t=${new Date().getTime()}`,
);
window.$message.success('');
break;
case 'whisper':
emit('send-whisper', props.post.user);
break;
case 'delete':
case 'requesting':
emit('handle-friend-action', props.post);
break;
case 'follow':
case 'unfollow':
emit('handle-follow-action', props.post);
break;
default:
break;
}
};
const post = computed({
get: () => {
let post: Item.PostComponentProps = Object.assign(
{
texts: [],
imgs: [],
videos: [],
links: [],
attachments: [],
charge_attachments: [],
},
props.post
);
post.contents.map((content) => {
if (+content.type === 1 || +content.type === 2) {
post.texts.push(content);
}
if (+content.type === 3) {
post.imgs.push(content);
}
if (+content.type === 4) {
post.videos.push(content);
}
if (+content.type === 6) {
post.links.push(content);
}
if (+content.type === 7) {
post.attachments.push(content);
}
if (+content.type === 8) {
post.charge_attachments.push(content);
}
});
return post;
},
set: (newVal) => {
props.post.upvote_count = newVal.upvote_count;
props.post.collection_count = newVal.collection_count;
},
get: () => {
let post: Item.PostComponentProps = Object.assign(
{
texts: [],
imgs: [],
videos: [],
links: [],
attachments: [],
charge_attachments: [],
},
props.post,
);
post.contents.map((content) => {
if (+content.type === 1 || +content.type === 2) {
post.texts.push(content);
}
if (+content.type === 3) {
post.imgs.push(content);
}
if (+content.type === 4) {
post.videos.push(content);
}
if (+content.type === 6) {
post.links.push(content);
}
if (+content.type === 7) {
post.attachments.push(content);
}
if (+content.type === 8) {
post.charge_attachments.push(content);
}
});
return post;
},
set: (newVal) => {
props.post.upvote_count = newVal.upvote_count;
props.post.collection_count = newVal.collection_count;
},
});
const handlePostStar = () => {
postStar({
id: post.value.id,
postStar({
id: post.value.id,
})
.then((res) => {
if (res.status) {
post.value = {
...post.value,
upvote_count: post.value.upvote_count + 1,
};
} else {
post.value = {
...post.value,
upvote_count:
post.value.upvote_count > 0 ? post.value.upvote_count - 1 : 0,
};
}
})
.then((res) => {
if (res.status) {
post.value = {
...post.value,
upvote_count: post.value.upvote_count + 1,
};
} else {
post.value = {
...post.value,
upvote_count: post.value.upvote_count > 0 ? post.value.upvote_count - 1 : 0,
};
}
})
.catch((err) => {
console.log(err);
});
.catch((err) => {
console.log(err);
});
};
const handlePostCollection = () => {
postCollection({
id: post.value.id,
postCollection({
id: post.value.id,
})
.then((res) => {
if (res.status) {
post.value = {
...post.value,
collection_count: post.value.collection_count + 1,
};
} else {
post.value = {
...post.value,
collection_count:
post.value.collection_count > 0
? post.value.collection_count - 1
: 0,
};
}
})
.then((res) => {
if (res.status) {
post.value = {
...post.value,
collection_count: post.value.collection_count + 1,
};
} else {
post.value = {
...post.value,
collection_count: post.value.collection_count > 0 ? post.value.collection_count - 1 : 0,
};
}
})
.catch((err) => {
console.log(err);
});
.catch((err) => {
console.log(err);
});
};
const goPostDetail = (id: number) => {
router.push({
name: 'post',
query: {
id,
},
});
router.push({
name: 'post',
query: {
id,
},
});
};
const doClickText = (e: MouseEvent, id: number) => {
const detail = (e.target as any).dataset.detail
if (detail && detail !== 'post') {
const d = detail.split(':');
if (d.length === 2) {
store.commit('refresh');
if (d[0] === 'tag') {
router.push({
name: 'home',
query: {
q: d[1],
t: 'tag',
},
});
} else {
router.push({
name: 'user',
query: {
s: d[1],
},
});
}
}
} else if (detail && detail === 'post') {
inFoldStyle.value = !inFoldStyle.value
} else {
goPostDetail(id);
const detail = (e.target as any).dataset.detail;
if (detail && detail !== 'post') {
const d = detail.split(':');
if (d.length === 2) {
store.commit('refresh');
if (d[0] === 'tag') {
router.push({
name: 'home',
query: {
q: d[1],
t: 'tag',
},
});
} else {
router.push({
name: 'user',
query: {
s: d[1],
},
});
}
}
} else if (detail && detail === 'post') {
inFoldStyle.value = !inFoldStyle.value;
} else {
goPostDetail(id);
}
};
</script>

@ -41,66 +41,62 @@ import { CloudDownloadOutline } from '@vicons/ionicons5';
import { precheckAttachment, getAttachment } from '@/api/user';
const props = withDefaults(
defineProps<{
attachments: Item.PostItemProps[];
price?: number;
}>(),
{
attachments: () => [],
price: 0,
}
defineProps<{
attachments: Item.PostItemProps[];
price?: number;
}>(),
{
attachments: () => [],
price: 0,
},
);
const showDownloadModal = ref(false);
const downloadTip = ref<any>('');
const attachmentID = ref(0);
const download = (attachment: Item.PostItemProps) => {
showDownloadModal.value = true;
attachmentID.value = attachment.id;
showDownloadModal.value = true;
attachmentID.value = attachment.id;
downloadTip.value = '';
if (attachment.type === 8) {
downloadTip.value = () =>
downloadTip.value = '';
if (attachment.type === 8) {
downloadTip.value = () =>
h('div', {}, [
h(
'p',
{},
'' +
(props.price / 100).toFixed(2) +
'元',
),
]);
precheckAttachment({
id: attachmentID.value,
})
.then((res) => {
if (res.paid) {
downloadTip.value = () =>
h('div', {}, [
h(
'p',
{},
'' +
(props.price / 100).toFixed(2) +
'元'
),
h('p', {}, ''),
]);
precheckAttachment({
id: attachmentID.value,
})
.then((res) => {
if (res.paid) {
downloadTip.value = () =>
h('div', {}, [
h(
'p',
{},
''
),
]);
}
})
.catch((err) => {
showDownloadModal.value = false;
});
}
}
})
.catch((err) => {
showDownloadModal.value = false;
});
}
};
const execDownloadAction = () => {
getAttachment({
id: attachmentID.value,
getAttachment({
id: attachmentID.value,
})
.then((res) => {
window.open(res.signed_url.replace('http://', 'https://'), '_blank');
})
.then((res) => {
window.open(res.signed_url.replace('http://', 'https://'), '_blank');
})
.catch((err) => {
console.log(err);
});
.catch((err) => {
console.log(err);
});
};
</script>

@ -216,49 +216,50 @@
<script setup lang="ts">
import { h, ref, onMounted, computed } from 'vue';
import type { Component } from 'vue'
import { NIcon, useDialog } from 'naive-ui'
import type { Component } from 'vue';
import { NIcon, useDialog } from 'naive-ui';
import { useStore } from 'vuex';
import { useRouter } from 'vue-router';
import { formatPrettyTime } from '@/utils/formatTime';
import { parsePostTag } from '@/utils/content';
import {
PaperPlaneOutline,
Heart,
HeartOutline,
Bookmark,
BookmarkOutline,
ShareSocialOutline,
ChatboxOutline,
PushOutline,
TrashOutline,
LockClosedOutline,
LockOpenOutline,
EyeOutline,
EyeOffOutline,
BodyOutline,
WalkOutline,
PersonOutline,
FlameOutline,
PaperPlaneOutline,
Heart,
HeartOutline,
Bookmark,
BookmarkOutline,
ShareSocialOutline,
ChatboxOutline,
PushOutline,
TrashOutline,
LockClosedOutline,
LockOpenOutline,
EyeOutline,
EyeOffOutline,
BodyOutline,
WalkOutline,
PersonOutline,
FlameOutline,
} from '@vicons/ionicons5';
import { MoreHorizFilled } from '@vicons/material';
import {
getPostStar,
postStar,
getPostCollection,
postCollection,
deletePost,
lockPost,
stickPost,
highlightPost,
visibilityPost
getPostStar,
postStar,
getPostCollection,
postCollection,
deletePost,
lockPost,
stickPost,
highlightPost,
visibilityPost,
} from '@/api/post';
import { followUser, unfollowUser } from '@/api/user';
import type { DropdownOption } from 'naive-ui';
import { VisibilityEnum } from '@/utils/IEnum';
import copy from "copy-to-clipboard";
import copy from 'copy-to-clipboard';
const useFriendship = (import.meta.env.VITE_USE_FRIENDSHIP.toLowerCase() === 'true')
const useFriendship =
import.meta.env.VITE_USE_FRIENDSHIP.toLowerCase() === 'true';
const store = useStore();
const router = useRouter();
@ -266,10 +267,10 @@ const dialog = useDialog();
const hasStarred = ref(false);
const hasCollected = ref(false);
const props = withDefaults(
defineProps<{
post: Item.PostProps;
}>(),
{}
defineProps<{
post: Item.PostProps;
}>(),
{},
);
const showDelModal = ref(false);
const showLockModal = ref(false);
@ -280,462 +281,490 @@ const loading = ref(false);
const tempVisibility = ref<VisibilityEnum>(VisibilityEnum.PUBLIC);
const showWhisper = ref(false);
const whisperReceiver = ref<Item.UserInfo>({
id: 0,
avatar: '',
username: '',
nickname: '',
is_admin: false,
is_friend: true,
is_following: false,
created_on: 0,
follows: 0,
followings: 0,
status: 1,
id: 0,
avatar: '',
username: '',
nickname: '',
is_admin: false,
is_friend: true,
is_following: false,
created_on: 0,
follows: 0,
followings: 0,
status: 1,
});
const onSendWhisper = (user: Item.UserInfo) => {
whisperReceiver.value = user;
showWhisper.value = true;
const onSendWhisper = (user: Item.UserInfo) => {
whisperReceiver.value = user;
showWhisper.value = true;
};
const whisperSuccess = () => {
showWhisper.value = false;
showWhisper.value = false;
};
const emit = defineEmits<{
(e: 'reload', post_id: number): void;
(e: 'reload', post_id: number): void;
}>();
const post = computed({
get: () => {
let post: Item.PostComponentProps = Object.assign(
{
texts: [],
imgs: [],
videos: [],
links: [],
attachments: [],
charge_attachments: [],
},
props.post
);
post.contents.map((content) => {
if (+content.type === 1 || +content.type === 2) {
post.texts.push(content);
}
if (+content.type === 3) {
post.imgs.push(content);
}
if (+content.type === 4) {
post.videos.push(content);
}
if (+content.type === 6) {
post.links.push(content);
}
if (+content.type === 7) {
post.attachments.push(content);
}
if (+content.type === 8) {
post.charge_attachments.push(content);
}
});
return post;
},
set: (newVal) => {
props.post.upvote_count = newVal.upvote_count;
props.post.comment_count = newVal.comment_count;
props.post.collection_count = newVal.collection_count;
props.post.is_essence = newVal.is_essence;
},
get: () => {
let post: Item.PostComponentProps = Object.assign(
{
texts: [],
imgs: [],
videos: [],
links: [],
attachments: [],
charge_attachments: [],
},
props.post,
);
post.contents.map((content) => {
if (+content.type === 1 || +content.type === 2) {
post.texts.push(content);
}
if (+content.type === 3) {
post.imgs.push(content);
}
if (+content.type === 4) {
post.videos.push(content);
}
if (+content.type === 6) {
post.links.push(content);
}
if (+content.type === 7) {
post.attachments.push(content);
}
if (+content.type === 8) {
post.charge_attachments.push(content);
}
});
return post;
},
set: (newVal) => {
props.post.upvote_count = newVal.upvote_count;
props.post.comment_count = newVal.comment_count;
props.post.collection_count = newVal.collection_count;
props.post.is_essence = newVal.is_essence;
},
});
const renderIcon = (icon: Component) => {
return () => {
return h(NIcon, null, {
default: () => h(icon)
})
}
default: () => h(icon),
});
};
};
const adminOptions = computed(() => {
let options: DropdownOption[] = [];
if (!store.state.userInfo.is_admin && store.state.userInfo.id != props.post.user.id) {
options.push({
label: ' @' + props.post.user.username,
key: 'whisper',
icon: renderIcon(PaperPlaneOutline)
});
if (props.post.user.is_following) {
options.push({
label: ' @' + props.post.user.username,
key: 'unfollow',
icon: renderIcon(WalkOutline)
})
} else {
options.push({
label: ' @' + props.post.user.username,
key: 'follow',
icon: renderIcon(BodyOutline)
})
}
return options;
}
let options: DropdownOption[] = [];
if (
!store.state.userInfo.is_admin &&
store.state.userInfo.id != props.post.user.id
) {
options.push({
label: '',
key: 'delete',
icon: renderIcon(TrashOutline)
})
if (post.value.is_lock === 0) {
options.push({
label: '',
key: 'lock',
icon: renderIcon(LockClosedOutline)
});
} else {
options.push({
label: '',
key: 'unlock',
icon: renderIcon(LockOpenOutline)
});
}
if (store.state.userInfo.is_admin) {
if (post.value.is_top === 0) {
options.push({
label: '',
key: 'stick',
icon: renderIcon(PushOutline)
});
} else {
options.push({
label: '',
key: 'unstick',
icon: renderIcon(PushOutline)
});
}
}
if (post.value.is_essence === 0) {
options.push({
label: '',
key: 'highlight',
icon: renderIcon(FlameOutline)
});
label: ' @' + props.post.user.username,
key: 'whisper',
icon: renderIcon(PaperPlaneOutline),
});
if (props.post.user.is_following) {
options.push({
label: ' @' + props.post.user.username,
key: 'unfollow',
icon: renderIcon(WalkOutline),
});
} else {
options.push({
label: '',
key: 'unhighlight',
icon: renderIcon(FlameOutline)
});
options.push({
label: ' @' + props.post.user.username,
key: 'follow',
icon: renderIcon(BodyOutline),
});
}
let visitMenu: DropdownOption
if (post.value.visibility === VisibilityEnum.PUBLIC) {
visitMenu = {
label: '',
key: 'vpublic',
icon: renderIcon(EyeOutline),
children: [
{ label: '', key: 'vprivate', icon: renderIcon(EyeOffOutline) },
{ label: '', key: 'vfollowing', icon: renderIcon(BodyOutline) }
]
};
} else if (post.value.visibility === VisibilityEnum.PRIVATE) {
visitMenu = {
label: '',
key: 'vprivate',
icon: renderIcon(EyeOffOutline),
children: [
{ label: '', key: 'vpublic', icon: renderIcon(EyeOutline) },
{ label: '', key: 'vfollowing', icon: renderIcon(BodyOutline) }
]
};
} else if (useFriendship && post.value.visibility === VisibilityEnum.FRIEND) {
visitMenu = {
label: '',
key: 'vfriend',
icon: renderIcon(PersonOutline),
children: [
{ label: '', key: 'vpublic', icon: renderIcon(EyeOutline) },
{ label: '', key: 'vprivate', icon: renderIcon(EyeOffOutline) },
{ label: '', key: 'vfollowing', icon: renderIcon(BodyOutline) }
]
};
return options;
}
options.push({
label: '',
key: 'delete',
icon: renderIcon(TrashOutline),
});
if (post.value.is_lock === 0) {
options.push({
label: '',
key: 'lock',
icon: renderIcon(LockClosedOutline),
});
} else {
options.push({
label: '',
key: 'unlock',
icon: renderIcon(LockOpenOutline),
});
}
if (store.state.userInfo.is_admin) {
if (post.value.is_top === 0) {
options.push({
label: '',
key: 'stick',
icon: renderIcon(PushOutline),
});
} else {
visitMenu = {
label: '',
key: 'vfollowing',
icon: renderIcon(BodyOutline),
children: [
{ label: '', key: 'vpublic', icon: renderIcon(EyeOutline) },
{ label: '', key: 'vprivate', icon: renderIcon(EyeOffOutline) }
]
};
}
if (useFriendship && post.value.visibility !== VisibilityEnum.FRIEND) {
visitMenu.children?.push({ label: '', key: 'vfriend', icon: renderIcon(PersonOutline) })
options.push({
label: '',
key: 'unstick',
icon: renderIcon(PushOutline),
});
}
options.push(visitMenu);
return options;
}
if (post.value.is_essence === 0) {
options.push({
label: '',
key: 'highlight',
icon: renderIcon(FlameOutline),
});
} else {
options.push({
label: '',
key: 'unhighlight',
icon: renderIcon(FlameOutline),
});
}
let visitMenu: DropdownOption;
if (post.value.visibility === VisibilityEnum.PUBLIC) {
visitMenu = {
label: '',
key: 'vpublic',
icon: renderIcon(EyeOutline),
children: [
{ label: '', key: 'vprivate', icon: renderIcon(EyeOffOutline) },
{ label: '', key: 'vfollowing', icon: renderIcon(BodyOutline) },
],
};
} else if (post.value.visibility === VisibilityEnum.PRIVATE) {
visitMenu = {
label: '',
key: 'vprivate',
icon: renderIcon(EyeOffOutline),
children: [
{ label: '', key: 'vpublic', icon: renderIcon(EyeOutline) },
{ label: '', key: 'vfollowing', icon: renderIcon(BodyOutline) },
],
};
} else if (useFriendship && post.value.visibility === VisibilityEnum.FRIEND) {
visitMenu = {
label: '',
key: 'vfriend',
icon: renderIcon(PersonOutline),
children: [
{ label: '', key: 'vpublic', icon: renderIcon(EyeOutline) },
{ label: '', key: 'vprivate', icon: renderIcon(EyeOffOutline) },
{ label: '', key: 'vfollowing', icon: renderIcon(BodyOutline) },
],
};
} else {
visitMenu = {
label: '',
key: 'vfollowing',
icon: renderIcon(BodyOutline),
children: [
{ label: '', key: 'vpublic', icon: renderIcon(EyeOutline) },
{ label: '', key: 'vprivate', icon: renderIcon(EyeOffOutline) },
],
};
}
if (useFriendship && post.value.visibility !== VisibilityEnum.FRIEND) {
visitMenu.children?.push({
label: '',
key: 'vfriend',
icon: renderIcon(PersonOutline),
});
}
options.push(visitMenu);
return options;
});
const onHandleFollowAction = (post: Item.PostProps) => {
dialog.success({
title: '',
content:
'' + (post.user.is_following ? ' @' : ' @') + props.post.user.username + ' ',
positiveText: '',
negativeText: '',
onPositiveClick: () => {
if (post.user.is_following) {
unfollowUser({
user_id: post.user.id,
}).then((_res) => {
window.$message.success('');
post.user.is_following = false;
})
.catch((_err) => {});
} else {
followUser({
user_id: post.user.id,
}).then((_res) => {
window.$message.success('');
post.user.is_following = true;
})
.catch((_err) => {});
}
},
});
dialog.success({
title: '',
content:
'' +
(post.user.is_following ? ' @' : ' @') +
props.post.user.username +
' ',
positiveText: '',
negativeText: '',
onPositiveClick: () => {
if (post.user.is_following) {
unfollowUser({
user_id: post.user.id,
})
.then((_res) => {
window.$message.success('');
post.user.is_following = false;
})
.catch((_err) => {});
} else {
followUser({
user_id: post.user.id,
})
.then((_res) => {
window.$message.success('');
post.user.is_following = true;
})
.catch((_err) => {});
}
},
});
};
const goPostDetail = (id: number) => {
router.push({
name: 'post',
query: {
id,
},
});
router.push({
name: 'post',
query: {
id,
},
});
};
const doClickText = (e: MouseEvent, id: number) => {
if ((e.target as any).dataset.detail) {
const d = (e.target as any).dataset.detail.split(':');
if (d.length === 2) {
store.commit('refresh');
if (d[0] === 'tag') {
router.push({
name: 'home',
query: {
q: d[1],
t: 'tag',
},
});
} else {
router.push({
name: 'user',
query: {
s: d[1],
},
});
}
return;
}
if ((e.target as any).dataset.detail) {
const d = (e.target as any).dataset.detail.split(':');
if (d.length === 2) {
store.commit('refresh');
if (d[0] === 'tag') {
router.push({
name: 'home',
query: {
q: d[1],
t: 'tag',
},
});
} else {
router.push({
name: 'user',
query: {
s: d[1],
},
});
}
return;
}
goPostDetail(id);
}
goPostDetail(id);
};
const handlePostAction = (
item: 'whisper' | 'follow' | 'unfollow' | 'delete' | 'lock' | 'unlock' | 'stick' | 'unstick' | 'highlight' | 'unhighlight' | 'vpublic' | 'vprivate' | 'vfriend' | 'vfollowing'
item:
| 'whisper'
| 'follow'
| 'unfollow'
| 'delete'
| 'lock'
| 'unlock'
| 'stick'
| 'unstick'
| 'highlight'
| 'unhighlight'
| 'vpublic'
| 'vprivate'
| 'vfriend'
| 'vfollowing',
) => {
switch (item) {
case 'whisper':
onSendWhisper(props.post.user);
break;
case 'follow':
case 'unfollow':
onHandleFollowAction(props.post);
break;
case 'delete':
showDelModal.value = true;
break;
case 'lock':
case 'unlock':
showLockModal.value = true;
break;
case 'stick':
case 'unstick':
showStickModal.value = true;
break;
case 'highlight':
case 'unhighlight':
showHighlightModal.value = true;
break;
case 'vpublic':
tempVisibility.value = 0;
showVisibilityModal.value = true;
break;
case 'vprivate':
tempVisibility.value = 1;
showVisibilityModal.value = true;
break;
case 'vfriend':
tempVisibility.value = 2;
showVisibilityModal.value = true;
break;
case 'vfollowing':
tempVisibility.value = 3;
showVisibilityModal.value = true;
break;
default:
break;
}
switch (item) {
case 'whisper':
onSendWhisper(props.post.user);
break;
case 'follow':
case 'unfollow':
onHandleFollowAction(props.post);
break;
case 'delete':
showDelModal.value = true;
break;
case 'lock':
case 'unlock':
showLockModal.value = true;
break;
case 'stick':
case 'unstick':
showStickModal.value = true;
break;
case 'highlight':
case 'unhighlight':
showHighlightModal.value = true;
break;
case 'vpublic':
tempVisibility.value = 0;
showVisibilityModal.value = true;
break;
case 'vprivate':
tempVisibility.value = 1;
showVisibilityModal.value = true;
break;
case 'vfriend':
tempVisibility.value = 2;
showVisibilityModal.value = true;
break;
case 'vfollowing':
tempVisibility.value = 3;
showVisibilityModal.value = true;
break;
default:
break;
}
};
const execDelAction = () => {
deletePost({
id: post.value.id,
})
.then((_res) => {
window.$message.success('');
router.replace('/');
deletePost({
id: post.value.id,
})
.then((_res) => {
window.$message.success('');
router.replace('/');
setTimeout(() => {
store.commit('refresh');
}, 50);
})
.catch((_err) => {
loading.value = false;
});
setTimeout(() => {
store.commit('refresh');
}, 50);
})
.catch((_err) => {
loading.value = false;
});
};
const execLockAction = () => {
lockPost({
id: post.value.id,
lockPost({
id: post.value.id,
})
.then((res) => {
emit('reload', post.value.id);
if (res.lock_status === 1) {
window.$message.success('');
} else {
window.$message.success('');
}
})
.then((res) => {
emit('reload', post.value.id);
if (res.lock_status === 1) {
window.$message.success('');
} else {
window.$message.success('');
}
})
.catch((_err) => {
loading.value = false;
});
.catch((_err) => {
loading.value = false;
});
};
const execStickAction = () => {
stickPost({
id: post.value.id,
stickPost({
id: post.value.id,
})
.then((res) => {
emit('reload', post.value.id);
if (res.top_status === 1) {
window.$message.success('');
} else {
window.$message.success('');
}
})
.then((res) => {
emit('reload', post.value.id);
if (res.top_status === 1) {
window.$message.success('');
} else {
window.$message.success('');
}
})
.catch((_err) => {
loading.value = false;
});
.catch((_err) => {
loading.value = false;
});
};
const execHighlightAction = () => {
highlightPost({
id: post.value.id,
highlightPost({
id: post.value.id,
})
.then((res) => {
post.value = {
...post.value,
is_essence: res.highlight_status,
};
if (res.highlight_status === 1) {
window.$message.success('');
} else {
window.$message.success('');
}
})
.then((res) => {
post.value = {
...post.value,
is_essence: res.highlight_status,
};
if (res.highlight_status === 1) {
window.$message.success('');
} else {
window.$message.success('');
}
})
.catch((_err) => {
loading.value = false;
});
.catch((_err) => {
loading.value = false;
});
};
const execVisibilityAction = () => {
visibilityPost({
id: post.value.id,
visibility: tempVisibility.value
visibilityPost({
id: post.value.id,
visibility: tempVisibility.value,
})
.then((_res) => {
emit('reload', post.value.id);
window.$message.success('');
})
.then((_res) => {
emit('reload', post.value.id);
window.$message.success('');
})
.catch((_err) => {
loading.value = false;
});
.catch((_err) => {
loading.value = false;
});
};
const handlePostStar = () => {
postStar({
id: post.value.id,
postStar({
id: post.value.id,
})
.then((res) => {
hasStarred.value = res.status;
if (res.status) {
post.value = {
...post.value,
upvote_count: post.value.upvote_count + 1,
};
} else {
post.value = {
...post.value,
upvote_count: post.value.upvote_count - 1,
};
}
})
.then((res) => {
hasStarred.value = res.status;
if (res.status) {
post.value = {
...post.value,
upvote_count: post.value.upvote_count + 1,
};
} else {
post.value = {
...post.value,
upvote_count: post.value.upvote_count - 1,
};
}
})
.catch((err) => {
console.log(err);
});
.catch((err) => {
console.log(err);
});
};
const handlePostCollection = () => {
postCollection({
id: post.value.id,
postCollection({
id: post.value.id,
})
.then((res) => {
hasCollected.value = res.status;
if (res.status) {
post.value = {
...post.value,
collection_count: post.value.collection_count + 1,
};
} else {
post.value = {
...post.value,
collection_count: post.value.collection_count - 1,
};
}
})
.then((res) => {
hasCollected.value = res.status;
if (res.status) {
post.value = {
...post.value,
collection_count: post.value.collection_count + 1,
};
} else {
post.value = {
...post.value,
collection_count: post.value.collection_count - 1,
};
}
})
.catch((err) => {
console.log(err);
});
.catch((err) => {
console.log(err);
});
};
const handlePostShare = () => {
copy(`${window.location.origin}/#/post?id=${post.value.id}&share=copy_link&t=${new Date().getTime()}`);
window.$message.success('');
copy(
`${window.location.origin}/#/post?id=${post.value.id}&share=copy_link&t=${new Date().getTime()}`,
);
window.$message.success('');
};
onMounted(() => {
if (store.state.userInfo.id > 0) {
getPostStar({
id: post.value.id,
})
.then((res) => {
hasStarred.value = res.status;
})
.catch((err) => {
console.log(err);
});
if (store.state.userInfo.id > 0) {
getPostStar({
id: post.value.id,
})
.then((res) => {
hasStarred.value = res.status;
})
.catch((err) => {
console.log(err);
});
getPostCollection({
id: post.value.id,
})
.then((res) => {
hasCollected.value = res.status;
})
.catch((err) => {
console.log(err);
});
}
getPostCollection({
id: post.value.id,
})
.then((res) => {
hasCollected.value = res.status;
})
.catch((err) => {
console.log(err);
});
}
});
</script>

@ -227,11 +227,14 @@ import { ref, onMounted } from 'vue';
const defaultImg = import.meta.env.VITE_DEFAULT_TWEET_IMAGE_404;
const thumbnail = import.meta.env.VITE_TWEET_IMAGE_THUMBNAIL;
const props = withDefaults(defineProps<{
imgs: Item.PostItemProps[],
}>(), {
imgs: () => []
});
const props = withDefaults(
defineProps<{
imgs: Item.PostItemProps[];
}>(),
{
imgs: () => [],
},
);
</script>
<style lang="less">

@ -128,242 +128,254 @@
import { h, ref, computed } from 'vue';
import { useStore } from 'vuex';
import { useRouter } from 'vue-router';
import { NIcon } from 'naive-ui'
import type { Component } from 'vue'
import { NIcon } from 'naive-ui';
import type { Component } from 'vue';
import type { DropdownOption } from 'naive-ui';
import { formatPrettyDate } from '@/utils/formatTime';
import { preparePost } from '@/utils/content';
import { postStar, postCollection } from '@/api/post';
import {
postStar,
postCollection,
} from '@/api/post';
import {
PaperPlaneOutline,
HeartOutline,
BookmarkOutline,
ChatboxOutline,
ShareSocialOutline,
PersonAddOutline,
PersonRemoveOutline,
BodyOutline,
WalkOutline,
PaperPlaneOutline,
HeartOutline,
BookmarkOutline,
ChatboxOutline,
ShareSocialOutline,
PersonAddOutline,
PersonRemoveOutline,
BodyOutline,
WalkOutline,
} from '@vicons/ionicons5';
import { MoreHorizFilled } from '@vicons/material';
import copy from "copy-to-clipboard";
import copy from 'copy-to-clipboard';
const router = useRouter();
const store = useStore();
const inFoldStyle = ref<boolean>(true)
const props = withDefaults(defineProps<{
post: Item.PostProps,
isOwner: boolean,
addFriendAction: boolean,
addFollowAction: boolean,
}>(), {});
const inFoldStyle = ref<boolean>(true);
const props = withDefaults(
defineProps<{
post: Item.PostProps;
isOwner: boolean;
addFriendAction: boolean;
addFollowAction: boolean;
}>(),
{},
);
const emit = defineEmits<{
(e: 'send-whisper', user: Item.UserInfo): void
(e: 'handle-follow-action', user: Item.PostProps): void
(e: 'handle-friend-action', user: Item.PostProps): void
(e: 'send-whisper', user: Item.UserInfo): void;
(e: 'handle-follow-action', user: Item.PostProps): void;
(e: 'handle-friend-action', user: Item.PostProps): void;
}>();
const renderIcon = (icon: Component) => {
return () => {
return h(NIcon, null, {
default: () => h(icon)
})
}
default: () => h(icon),
});
};
};
const tweetOptions = computed(() => {
let options: DropdownOption[] = [];
if (!props.isOwner) {
options.push({
label: ' @' + props.post.user.username,
key: 'whisper',
icon: renderIcon(PaperPlaneOutline)
});
}
if (!props.isOwner && props.addFollowAction) {
if (props.post.user.is_following) {
options.push({
label: ' @' + props.post.user.username,
key: 'unfollow',
icon: renderIcon(WalkOutline)
})
} else {
options.push({
label: ' @' + props.post.user.username,
key: 'follow',
icon: renderIcon(BodyOutline)
})
}
}
if (!props.isOwner && props.addFriendAction) {
if (props.post.user.is_friend) {
options.push({
label: ' @' + props.post.user.username,
key: 'delete',
icon: renderIcon(PersonRemoveOutline)
});
} else {
options.push({
label: ' @' + props.post.user.username,
key: 'requesting',
icon: renderIcon(PersonAddOutline)
});
}
}
let options: DropdownOption[] = [];
if (!props.isOwner) {
options.push({
label: '',
key: 'copyTweetLink',
icon: renderIcon(ShareSocialOutline),
label: ' @' + props.post.user.username,
key: 'whisper',
icon: renderIcon(PaperPlaneOutline),
});
return options;
}
if (!props.isOwner && props.addFollowAction) {
if (props.post.user.is_following) {
options.push({
label: ' @' + props.post.user.username,
key: 'unfollow',
icon: renderIcon(WalkOutline),
});
} else {
options.push({
label: ' @' + props.post.user.username,
key: 'follow',
icon: renderIcon(BodyOutline),
});
}
}
if (!props.isOwner && props.addFriendAction) {
if (props.post.user.is_friend) {
options.push({
label: ' @' + props.post.user.username,
key: 'delete',
icon: renderIcon(PersonRemoveOutline),
});
} else {
options.push({
label: ' @' + props.post.user.username,
key: 'requesting',
icon: renderIcon(PersonAddOutline),
});
}
}
options.push({
label: '',
key: 'copyTweetLink',
icon: renderIcon(ShareSocialOutline),
});
return options;
});
const handleTweetAction = async (
item: 'copyTweetLink' | 'whisper' | 'follow' | 'unfollow' | 'delete' | 'requesting'
item:
| 'copyTweetLink'
| 'whisper'
| 'follow'
| 'unfollow'
| 'delete'
| 'requesting',
) => {
switch (item) {
case 'copyTweetLink':
copy(`${window.location.origin}/#/post?id=${post.value.id}&share=copy_link&t=${new Date().getTime()}`);
window.$message.success('');
break;
case 'whisper':
emit('send-whisper', props.post.user);
break;
case 'delete':
case 'requesting':
emit('handle-friend-action', props.post);
break;
case 'follow':
case 'unfollow':
emit('handle-follow-action', props.post);
break;
default:
break;
}
switch (item) {
case 'copyTweetLink':
copy(
`${window.location.origin}/#/post?id=${post.value.id}&share=copy_link&t=${new Date().getTime()}`,
);
window.$message.success('');
break;
case 'whisper':
emit('send-whisper', props.post.user);
break;
case 'delete':
case 'requesting':
emit('handle-friend-action', props.post);
break;
case 'follow':
case 'unfollow':
emit('handle-follow-action', props.post);
break;
default:
break;
}
};
const post = computed({
get: () => {
let post: Item.PostComponentProps = Object.assign(
{
texts: [],
imgs: [],
videos: [],
links: [],
attachments: [],
charge_attachments: [],
},
props.post
);
post.contents.map((content) => {
if (+content.type === 1 || +content.type === 2) {
post.texts.push(content);
}
if (+content.type === 3) {
post.imgs.push(content);
}
if (+content.type === 4) {
post.videos.push(content);
}
if (+content.type === 6) {
post.links.push(content);
}
if (+content.type === 7) {
post.attachments.push(content);
}
if (+content.type === 8) {
post.charge_attachments.push(content);
}
});
return post;
},
set: (newVal) => {
props.post.upvote_count = newVal.upvote_count;
props.post.collection_count = newVal.collection_count;
},
get: () => {
let post: Item.PostComponentProps = Object.assign(
{
texts: [],
imgs: [],
videos: [],
links: [],
attachments: [],
charge_attachments: [],
},
props.post,
);
post.contents.map((content) => {
if (+content.type === 1 || +content.type === 2) {
post.texts.push(content);
}
if (+content.type === 3) {
post.imgs.push(content);
}
if (+content.type === 4) {
post.videos.push(content);
}
if (+content.type === 6) {
post.links.push(content);
}
if (+content.type === 7) {
post.attachments.push(content);
}
if (+content.type === 8) {
post.charge_attachments.push(content);
}
});
return post;
},
set: (newVal) => {
props.post.upvote_count = newVal.upvote_count;
props.post.collection_count = newVal.collection_count;
},
});
const handlePostStar = () => {
postStar({
id: post.value.id,
postStar({
id: post.value.id,
})
.then((res) => {
if (res.status) {
post.value = {
...post.value,
upvote_count: post.value.upvote_count + 1,
};
} else {
post.value = {
...post.value,
upvote_count:
post.value.upvote_count > 0 ? post.value.upvote_count - 1 : 0,
};
}
})
.then((res) => {
if (res.status) {
post.value = {
...post.value,
upvote_count: post.value.upvote_count + 1,
};
} else {
post.value = {
...post.value,
upvote_count: post.value.upvote_count > 0 ? post.value.upvote_count - 1 : 0,
};
}
})
.catch((err) => {
console.log(err);
});
.catch((err) => {
console.log(err);
});
};
const handlePostCollection = () => {
postCollection({
id: post.value.id,
postCollection({
id: post.value.id,
})
.then((res) => {
if (res.status) {
post.value = {
...post.value,
collection_count: post.value.collection_count + 1,
};
} else {
post.value = {
...post.value,
collection_count:
post.value.collection_count > 0
? post.value.collection_count - 1
: 0,
};
}
})
.then((res) => {
if (res.status) {
post.value = {
...post.value,
collection_count: post.value.collection_count + 1,
};
} else {
post.value = {
...post.value,
collection_count: post.value.collection_count > 0 ? post.value.collection_count - 1 : 0,
};
}
})
.catch((err) => {
console.log(err);
});
.catch((err) => {
console.log(err);
});
};
const goPostDetail = (id: number) => {
router.push({
name: 'post',
query: {
id,
},
});
router.push({
name: 'post',
query: {
id,
},
});
};
const doClickText = (e: MouseEvent, id: number) => {
const detail = (e.target as any).dataset.detail
if (detail && detail !== 'post') {
const d = detail.split(':');
if (d.length === 2) {
store.commit('refresh');
if (d[0] === 'tag') {
router.push({
name: 'home',
query: {
q: d[1],
t: 'tag',
},
});
} else {
router.push({
name: 'user',
query: {
s: d[1],
},
});
}
}
} else if (detail && detail === 'post') {
inFoldStyle.value = !inFoldStyle.value
} else {
goPostDetail(id);
const detail = (e.target as any).dataset.detail;
if (detail && detail !== 'post') {
const d = detail.split(':');
if (d.length === 2) {
store.commit('refresh');
if (d[0] === 'tag') {
router.push({
name: 'home',
query: {
q: d[1],
t: 'tag',
},
});
} else {
router.push({
name: 'user',
query: {
s: d[1],
},
});
}
}
} else if (detail && detail === 'post') {
inFoldStyle.value = !inFoldStyle.value;
} else {
goPostDetail(id);
}
};
</script>

@ -13,11 +13,14 @@
<script setup lang="ts">
import { LinkOutline } from '@vicons/ionicons5';
const props = withDefaults(defineProps<{
links: Item.PostItemProps[]
}>(), {
links: () => []
});
const props = withDefaults(
defineProps<{
links: Item.PostItemProps[];
}>(),
{
links: () => [],
},
);
</script>
<style lang="less" scoped>

@ -11,11 +11,14 @@
</template>
<script setup lang="ts">
const props = withDefaults(defineProps<{
num: number,
}>(), {
num: 1
});
const props = withDefaults(
defineProps<{
num: number;
}>(),
{
num: 1,
},
);
</script>
<style lang="less" scoped>

@ -13,13 +13,13 @@
import PaopaoVideoPlayer from 'paopao-video-player';
const props = withDefaults(
defineProps<{
videos: Item.PostItemProps[];
full?: boolean;
}>(),
{
videos: () => [],
full: false,
}
defineProps<{
videos: Item.PostItemProps[];
full?: boolean;
}>(),
{
videos: () => [],
full: false,
},
);
</script>

@ -87,84 +87,91 @@ import { ref } from 'vue';
import { useStore } from 'vuex';
import { Trash } from '@vicons/tabler';
import { formatPrettyTime } from '@/utils/formatTime';
import { deleteCommentReply, thumbsUpTweetReply, thumbsDownTweetReply } from '@/api/post';
import {
ThumbUpTwotone,
ThumbUpOutlined,
ThumbDownTwotone,
ThumbDownOutlined,
deleteCommentReply,
thumbsUpTweetReply,
thumbsDownTweetReply,
} from '@/api/post';
import {
ThumbUpTwotone,
ThumbUpOutlined,
ThumbDownTwotone,
ThumbDownOutlined,
} from '@vicons/material';
import { YesNoEnum } from '@/utils/IEnum';
const props = withDefaults(defineProps<{
tweetId: number,
reply: Item.ReplyProps,
}>(), {});
const props = withDefaults(
defineProps<{
tweetId: number;
reply: Item.ReplyProps;
}>(),
{},
);
const store = useStore();
const emit = defineEmits<{
(e: 'focusReply', reply: Item.ReplyProps): void,
(e: 'reload'): void
(e: 'focusReply', reply: Item.ReplyProps): void;
(e: 'reload'): void;
}>();
const hasThumbsUp = ref(props.reply.is_thumbs_up == YesNoEnum.YES)
const hasThumbsDown = ref(props.reply.is_thumbs_down == YesNoEnum.YES)
const thumbsUpCount = ref(props.reply.thumbs_up_count)
const hasThumbsUp = ref(props.reply.is_thumbs_up == YesNoEnum.YES);
const hasThumbsDown = ref(props.reply.is_thumbs_down == YesNoEnum.YES);
const thumbsUpCount = ref(props.reply.thumbs_up_count);
const handleThumbsUp = () => {
thumbsUpTweetReply({
tweet_id: props.tweetId,
comment_id: props.reply.comment_id,
reply_id: props.reply.id,
thumbsUpTweetReply({
tweet_id: props.tweetId,
comment_id: props.reply.comment_id,
reply_id: props.reply.id,
})
.then((_res) => {
hasThumbsUp.value = !hasThumbsUp.value;
if (hasThumbsUp.value) {
thumbsUpCount.value++;
hasThumbsDown.value = false;
} else {
thumbsUpCount.value--;
}
})
.then((_res) => {
hasThumbsUp.value = !hasThumbsUp.value
if (hasThumbsUp.value) {
thumbsUpCount.value++
hasThumbsDown.value = false
} else {
thumbsUpCount.value--
}
})
.catch((err) => {
console.log(err);
});
.catch((err) => {
console.log(err);
});
};
const handleThumbsDown = () => {
thumbsDownTweetReply({
tweet_id: props.tweetId,
comment_id: props.reply.comment_id,
reply_id: props.reply.id,
thumbsDownTweetReply({
tweet_id: props.tweetId,
comment_id: props.reply.comment_id,
reply_id: props.reply.id,
})
.then((_res) => {
hasThumbsDown.value = !hasThumbsDown.value;
if (hasThumbsDown.value) {
if (hasThumbsUp.value) {
thumbsUpCount.value--;
hasThumbsUp.value = false;
}
}
})
.then((_res) => {
hasThumbsDown.value = !hasThumbsDown.value
if (hasThumbsDown.value) {
if (hasThumbsUp.value) {
thumbsUpCount.value--
hasThumbsUp.value = false
}
}
})
.catch((err) => {
console.log(err);
});
.catch((err) => {
console.log(err);
});
};
const focusReply = () => {
emit('focusReply', props.reply);
emit('focusReply', props.reply);
};
const execDelAction = () => {
deleteCommentReply({
id: props.reply.id,
})
.then((res) => {
window.$message.success('');
deleteCommentReply({
id: props.reply.id,
})
.then((res) => {
window.$message.success('');
setTimeout(() => {
emit('reload');
}, 50);
})
.catch((err) => {
console.log(err);
});
setTimeout(() => {
emit('reload');
}, 50);
})
.catch((err) => {
console.log(err);
});
};
</script>

@ -99,97 +99,104 @@ const loading = ref(false);
const keyword = ref('');
const store = useStore();
const router = useRouter();
const registerUserCount = ref(0)
const onlineUserCount = ref(0)
const historyMaxOnline = ref(0)
const serverUpTime = ref(0)
const registerUserCount = ref(0);
const onlineUserCount = ref(0);
const historyMaxOnline = ref(0);
const serverUpTime = ref(0);
const userInfoElement = ref<HTMLElement | null>(null);
const rightFollowTopicMaxSize = Number(import.meta.env.VITE_RIGHT_FOLLOW_TOPIC_MAX_SIZE)
const rightHotTopicMaxSize = Number(import.meta.env.VITE_RIGHT_HOT_TOPIC_MAX_SIZE)
const rightFollowTopicMaxSize = Number(
import.meta.env.VITE_RIGHT_FOLLOW_TOPIC_MAX_SIZE,
);
const rightHotTopicMaxSize = Number(
import.meta.env.VITE_RIGHT_HOT_TOPIC_MAX_SIZE,
);
const loadSiteInfo = () => {
getSiteInfo()
.then((res) => {
registerUserCount.value = res.register_user_count;
onlineUserCount.value = res.online_user_count;
historyMaxOnline.value = res.history_max_online;
serverUpTime.value = res.server_up_time;
})
.catch((_err) => {
// do nothing
});
observer.disconnect()
getSiteInfo()
.then((res) => {
registerUserCount.value = res.register_user_count;
onlineUserCount.value = res.online_user_count;
historyMaxOnline.value = res.history_max_online;
serverUpTime.value = res.server_up_time;
})
.catch((_err) => {
// do nothing
});
observer.disconnect();
};
const loadHotTags = () => {
loading.value = true;
getTags({
type: 'hot_extral',
num: rightHotTopicMaxSize,
extral_num: rightFollowTopicMaxSize,
loading.value = true;
getTags({
type: 'hot_extral',
num: rightHotTopicMaxSize,
extral_num: rightFollowTopicMaxSize,
})
.then((res) => {
hotTags.value = res.topics;
followTags.value = res.extral_topics ?? [];
showFollowTopics.value = true;
loading.value = false;
})
.then((res) => {
hotTags.value = res.topics;
followTags.value = res.extral_topics??[];
showFollowTopics.value = true
loading.value = false;
})
.catch((_err) => {
loading.value = false;
});
.catch((_err) => {
loading.value = false;
});
};
const formatQuoteNum = (num: number) => {
if (num >= 1000) {
return (num / 1000).toFixed(1) + 'k';
}
return num;
if (num >= 1000) {
return (num / 1000).toFixed(1) + 'k';
}
return num;
};
const handleSearch = () => {
router.push({
name: 'home',
query: {
q: keyword.value,
},
});
};
const showFollowTopics = computed({
get: () => {
return store.state.userLogined && followTags.value.length !==0;
},
set: (newVal) => {
// do nothing
router.push({
name: 'home',
query: {
q: keyword.value,
},
});
};
const showFollowTopics = computed({
get: () => {
return store.state.userLogined && followTags.value.length !== 0;
},
set: (newVal) => {
// do nothing
},
});
watch(
() => ({
refreshTopicFollow: store.state.refreshTopicFollow,
userLogined: store.state.userLogined
}),
(to, from) => {
if (to.refreshTopicFollow !== from.refreshTopicFollow || to.userLogined) {
loadHotTags();
}
if (store.state.userInfo.is_admin) {
loadSiteInfo();
}
() => ({
refreshTopicFollow: store.state.refreshTopicFollow,
userLogined: store.state.userLogined,
}),
(to, from) => {
if (to.refreshTopicFollow !== from.refreshTopicFollow || to.userLogined) {
loadHotTags();
}
if (store.state.userInfo.is_admin) {
loadSiteInfo();
}
},
);
const observer = new IntersectionObserver((entries: IntersectionObserverEntry[]) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
loadSiteInfo();
}
const observer = new IntersectionObserver(
(entries: IntersectionObserverEntry[]) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
loadSiteInfo();
}
});
}, {
},
{
root: null,
rootMargin: '0px',
threshold: 1
});
threshold: 1,
},
);
onMounted(() => {
// 不知道为什么 store.state.userInfo.is_admin 在这里就是不起作用f*k所以才用这么一种蹩脚的法子来凑合
if (userInfoElement.value) {
observer.observe(userInfoElement.value);
}
loadHotTags();
// 不知道为什么 store.state.userInfo.is_admin 在这里就是不起作用f*k所以才用这么一种蹩脚的法子来凑合
if (userInfoElement.value) {
observer.observe(userInfoElement.value);
}
loadHotTags();
});
</script>

@ -59,15 +59,15 @@ import { useRoute, useRouter } from 'vue-router';
import { useStore } from 'vuex';
import { NIcon, NBadge, useMessage } from 'naive-ui';
import {
HomeOutline,
BookmarksOutline,
MegaphoneOutline,
ChatbubblesOutline,
LeafOutline,
PeopleOutline,
WalletOutline,
SettingsOutline,
LogOutOutline,
HomeOutline,
BookmarksOutline,
MegaphoneOutline,
ChatbubblesOutline,
LeafOutline,
PeopleOutline,
WalletOutline,
SettingsOutline,
LogOutOutline,
} from '@vicons/ionicons5';
import { Hash } from '@vicons/tabler';
import { getUnreadMsgCount } from '@/api/user';
@ -80,184 +80,186 @@ const hasUnreadMsg = ref(false);
const selectedPath = ref<any>(route.name || '');
const msgLoop = ref();
const enableAnnoucement = (import.meta.env.VITE_ENABLE_ANOUNCEMENT.toLowerCase() === 'true');
const enableAnnoucement =
import.meta.env.VITE_ENABLE_ANOUNCEMENT.toLowerCase() === 'true';
watch(route, () => {
selectedPath.value = route.name;
selectedPath.value = route.name;
});
watch(store.state, () => {
hasUnreadMsg.value = store.state.unreadMsgCount > 0;
if (store.state.userInfo.id > 0) {
if (!msgLoop.value) {
getUnreadMsgCount()
.then((res) => {
hasUnreadMsg.value = res.count > 0;
store.commit("updateUnreadMsgCount", res.count)
})
.catch((err) => {
console.log(err);
});
msgLoop.value = setInterval(() => {
getUnreadMsgCount()
.then((res) => {
hasUnreadMsg.value = res.count > 0;
store.commit("updateUnreadMsgCount", res.count)
})
.catch((err) => {
console.log(err);
});
}, store.state.profile.defaultMsgLoopInterval);
}
} else {
if (msgLoop.value) {
clearInterval(msgLoop.value);
}
hasUnreadMsg.value = store.state.unreadMsgCount > 0;
if (store.state.userInfo.id > 0) {
if (!msgLoop.value) {
getUnreadMsgCount()
.then((res) => {
hasUnreadMsg.value = res.count > 0;
store.commit('updateUnreadMsgCount', res.count);
})
.catch((err) => {
console.log(err);
});
msgLoop.value = setInterval(() => {
getUnreadMsgCount()
.then((res) => {
hasUnreadMsg.value = res.count > 0;
store.commit('updateUnreadMsgCount', res.count);
})
.catch((err) => {
console.log(err);
});
}, store.state.profile.defaultMsgLoopInterval);
}
} else {
if (msgLoop.value) {
clearInterval(msgLoop.value);
}
}
});
onMounted(() => {
window.onresize = () => {
store.commit('triggerCollapsedLeft', document.body.clientWidth <= 821);
store.commit('triggerCollapsedRight', document.body.clientWidth <= 821);
};
window.onresize = () => {
store.commit('triggerCollapsedLeft', document.body.clientWidth <= 821);
store.commit('triggerCollapsedRight', document.body.clientWidth <= 821);
};
});
const menuOptions = computed(() => {
const options = [
{
label: '广',
key: 'home',
icon: () => h(HomeOutline),
href: '/',
},
{
label: '',
key: 'topic',
icon: () => h(Hash),
href: '/topic',
},
];
if (enableAnnoucement) {
options.push({
label: '',
key: 'anouncement',
icon: () => h(MegaphoneOutline),
href: '/anouncement',
});
}
const options = [
{
label: '广',
key: 'home',
icon: () => h(HomeOutline),
href: '/',
},
{
label: '',
key: 'topic',
icon: () => h(Hash),
href: '/topic',
},
];
if (enableAnnoucement) {
options.push({
label: '',
key: 'profile',
icon: () => h(LeafOutline),
href: '/profile',
label: '',
key: 'anouncement',
icon: () => h(MegaphoneOutline),
href: '/anouncement',
});
}
options.push({
label: '',
key: 'profile',
icon: () => h(LeafOutline),
href: '/profile',
});
options.push({
label: '',
key: 'messages',
icon: () => h(ChatbubblesOutline),
href: '/messages',
});
options.push({
label: '',
key: 'collection',
icon: () => h(BookmarksOutline),
href: '/collection',
});
if (store.state.profile.useFriendship) {
options.push({
label: '',
key: 'messages',
icon: () => h(ChatbubblesOutline),
href: '/messages',
})
options.push({
label: '',
key: 'collection',
icon: () => h(BookmarksOutline),
href: '/collection',
label: '',
key: 'contacts',
icon: () => h(PeopleOutline),
href: '/contacts',
});
if (store.state.profile.useFriendship) {
options.push({
label: '',
key: 'contacts',
icon: () => h(PeopleOutline),
href: '/contacts',
});
}
if (store.state.profile.enableWallet) {
options.push({
label: '',
key: 'wallet',
icon: () => h(WalletOutline),
href: '/wallet',
});
}
}
if (store.state.profile.enableWallet) {
options.push({
label: '',
key: 'setting',
icon: () => h(SettingsOutline),
href: '/setting',
label: '',
key: 'wallet',
icon: () => h(WalletOutline),
href: '/wallet',
});
return store.state.userInfo.id > 0
? options
: [
{
label: '广',
key: 'home',
icon: () => h(HomeOutline),
href: '/',
},
{
label: '',
key: 'topic',
icon: () => h(Hash),
href: '/topic',
},
];
}
options.push({
label: '',
key: 'setting',
icon: () => h(SettingsOutline),
href: '/setting',
});
return store.state.userInfo.id > 0
? options
: [
{
label: '广',
key: 'home',
icon: () => h(HomeOutline),
href: '/',
},
{
label: '',
key: 'topic',
icon: () => h(Hash),
href: '/topic',
},
];
});
const renderMenuLabel = (option: AnyObject) => {
if ('href' in option) {
return h('div', {}, option.label);
}
return option.label;
if ('href' in option) {
return h('div', {}, option.label);
}
return option.label;
};
const renderMenuIcon = (option: AnyObject) => {
if (option.key === 'messages') {
return h(
NBadge,
if (option.key === 'messages') {
return h(
NBadge,
{
dot: true,
show: hasUnreadMsg.value,
processing: true,
},
{
default: () =>
h(
NIcon,
{
dot: true,
show: hasUnreadMsg.value,
processing: true,
color:
option.key === selectedPath.value
? 'var(--n-item-icon-color-active)'
: 'var(--n-item-icon-color)',
},
{
default: () =>
h(
NIcon,
{
color:
option.key === selectedPath.value
? 'var(--n-item-icon-color-active)'
: 'var(--n-item-icon-color)',
},
{ default: option.icon }
),
}
);
}
return h(NIcon, null, { default: option.icon });
{ default: option.icon },
),
},
);
}
return h(NIcon, null, { default: option.icon });
};
const goRouter = (name: string, item: any = {}) => {
selectedPath.value = name;
router.push({
name, query: {
t: (new Date().getTime())
}
});
selectedPath.value = name;
router.push({
name,
query: {
t: new Date().getTime(),
},
});
};
const goHome = () => {
if (route.path === '/') {
store.commit('refresh');
}
goRouter('home');
if (route.path === '/') {
store.commit('refresh');
}
goRouter('home');
};
const triggerAuth = (key: string) => {
store.commit('triggerAuth', true);
store.commit('triggerAuthKey', key);
store.commit('triggerAuth', true);
store.commit('triggerAuthKey', key);
};
const handleLogout = () => {
store.commit('userLogout');
store.commit('refresh')
goHome()
store.commit('userLogout');
store.commit('refresh');
goHome();
};
window.$store = store;
window.$message = useMessage();

@ -59,147 +59,146 @@ import type { DropdownOption } from 'naive-ui';
import { pinTopic, stickTopic, followTopic, unfollowTopic } from '@/api/post';
import defaultUserAvatar from '@/assets/img/logo.png';
const hasFollowing= ref(false);
const hasFollowing = ref(false);
const props = withDefaults(
defineProps<{
tag: Item.TagProps;
showAction: boolean;
checkFollowing: boolean;
checkPin: boolean;
}>(),
{}
defineProps<{
tag: Item.TagProps;
showAction: boolean;
checkFollowing: boolean;
checkPin: boolean;
}>(),
{},
);
const tagUserAvatar = computed(() => {
if (props.tag.user) {
return props.tag.user.avatar
} else {
return defaultUserAvatar
}
if (props.tag.user) {
return props.tag.user.avatar;
} else {
return defaultUserAvatar;
}
});
const tagOptions = computed(() => {
let options: DropdownOption[] = [];
if (props.tag.is_following === 0) {
options.push({
label: '',
key: 'follow',
});
let options: DropdownOption[] = [];
if (props.tag.is_following === 0) {
options.push({
label: '',
key: 'follow',
});
} else {
if (props.tag.is_pin === 0) {
options.push({
label: '',
key: 'pin',
});
} else {
if (props.tag.is_pin === 0) {
options.push({
label: '',
key: 'pin',
});
} else {
options.push({
label: '',
key: 'unpin',
});
}
if (props.tag.is_top === 0) {
options.push({
label: '',
key: 'stick',
});
} else {
options.push({
label: '',
key: 'unstick',
});
}
options.push({
label: '',
key: 'unfollow',
});
options.push({
label: '',
key: 'unpin',
});
}
if (props.tag.is_top === 0) {
options.push({
label: '',
key: 'stick',
});
} else {
options.push({
label: '',
key: 'unstick',
});
}
return options;
options.push({
label: '',
key: 'unfollow',
});
}
return options;
});
const handleTagAction = (
item: 'follow' | 'unfollow' | 'pin' | 'unpin' | 'stick' | 'unstick'
item: 'follow' | 'unfollow' | 'pin' | 'unpin' | 'stick' | 'unstick',
) => {
switch (item) {
case 'follow':
followTopic({
topic_id: props.tag.id
})
.then((_res) => {
props.tag.is_following = 1
window.$message.success(`关注成功`);
})
.catch((err) => {
console.log(err);
});
break;
case 'unfollow':
unfollowTopic({
topic_id: props.tag.id
})
.then((_res) => {
props.tag.is_following = 0
window.$message.success(`取消关注`);
})
.catch((err) => {
console.log(err);
});
break;
case 'pin':
pinTopic({
topic_id: props.tag.id
})
.then((_res) => {
props.tag.is_pin = 1;
window.$message.success(`钉住成功`);
})
.catch((err) => {
console.log(err);
});
break;
case 'unpin':
pinTopic({
topic_id: props.tag.id
})
.then((_res) => {
props.tag.is_pin = 0;
window.$message.success(`取消钉住`);
})
.catch((err) => {
console.log(err);
});
break;
case 'stick':
stickTopic({
topic_id: props.tag.id
})
.then((res) => {
props.tag.is_top = res.top_status
window.$message.success(`置顶成功`);
})
.catch((err) => {
console.log(err);
});
break;
case 'unstick':
stickTopic({
topic_id: props.tag.id
})
.then((res) => {
props.tag.is_top = res.top_status
window.$message.success(`取消置顶`);
})
.catch((err) => {
console.log(err);
});
break;
default:
break;
}
switch (item) {
case 'follow':
followTopic({
topic_id: props.tag.id,
})
.then((_res) => {
props.tag.is_following = 1;
window.$message.success(`关注成功`);
})
.catch((err) => {
console.log(err);
});
break;
case 'unfollow':
unfollowTopic({
topic_id: props.tag.id,
})
.then((_res) => {
props.tag.is_following = 0;
window.$message.success(`取消关注`);
})
.catch((err) => {
console.log(err);
});
break;
case 'pin':
pinTopic({
topic_id: props.tag.id,
})
.then((_res) => {
props.tag.is_pin = 1;
window.$message.success(`钉住成功`);
})
.catch((err) => {
console.log(err);
});
break;
case 'unpin':
pinTopic({
topic_id: props.tag.id,
})
.then((_res) => {
props.tag.is_pin = 0;
window.$message.success(`取消钉住`);
})
.catch((err) => {
console.log(err);
});
break;
case 'stick':
stickTopic({
topic_id: props.tag.id,
})
.then((res) => {
props.tag.is_top = res.top_status;
window.$message.success(`置顶成功`);
})
.catch((err) => {
console.log(err);
});
break;
case 'unstick':
stickTopic({
topic_id: props.tag.id,
})
.then((res) => {
props.tag.is_top = res.top_status;
window.$message.success(`取消置顶`);
})
.catch((err) => {
console.log(err);
});
break;
default:
break;
}
};
onMounted(() => {
hasFollowing.value = false
hasFollowing.value = false;
});
</script>

@ -54,39 +54,39 @@ import { ref } from 'vue';
import { requestingFriend } from '@/api/user';
const props = withDefaults(
defineProps<{
show: boolean;
user: Item.UserInfo;
}>(),
{
show: false,
}
defineProps<{
show: boolean;
user: Item.UserInfo;
}>(),
{
show: false,
},
);
const content = ref('');
const loading = ref(false);
const emit = defineEmits<{
(e: 'success'): void;
(e: 'success'): void;
}>();
const closeModal = () => {
emit('success');
emit('success');
};
const sendWhisper = () => {
loading.value = true;
requestingFriend({
user_id: props.user.id,
greetings: content.value,
})
.then((res: any) => {
window.$message.success('');
loading.value = false;
content.value = '';
loading.value = true;
requestingFriend({
user_id: props.user.id,
greetings: content.value,
})
.then((res: any) => {
window.$message.success('');
loading.value = false;
content.value = '';
closeModal();
})
.catch((err: any) => {
loading.value = false;
});
closeModal();
})
.catch((err: any) => {
loading.value = false;
});
};
</script>

@ -54,39 +54,39 @@ import { ref } from 'vue';
import { sendUserWhisper } from '@/api/user';
const props = withDefaults(
defineProps<{
show: boolean;
user: Item.UserInfo;
}>(),
{
show: false,
}
defineProps<{
show: boolean;
user: Item.UserInfo;
}>(),
{
show: false,
},
);
const content = ref('');
const loading = ref(false);
const emit = defineEmits<{
(e: 'success'): void;
(e: 'success'): void;
}>();
const closeModal = () => {
emit('success');
emit('success');
};
const sendWhisper = () => {
loading.value = true;
sendUserWhisper({
user_id: props.user.id,
content: content.value,
})
.then((res: any) => {
window.$message.success('');
loading.value = false;
content.value = '';
loading.value = true;
sendUserWhisper({
user_id: props.user.id,
content: content.value,
})
.then((res: any) => {
window.$message.success('');
loading.value = false;
content.value = '';
closeModal();
})
.catch((err: any) => {
loading.value = false;
});
closeModal();
})
.catch((err: any) => {
loading.value = false;
});
};
</script>

@ -1,20 +1,20 @@
import { createApp } from 'vue'
import router from './router'
import store from './store'
import App from './App.vue'
import { createApp } from 'vue';
import router from './router';
import store from './store';
import App from './App.vue';
import type { MessageApiInjection } from 'naive-ui/lib/message/src/MessageProvider'
import type { MessageApiInjection } from 'naive-ui/lib/message/src/MessageProvider';
// 通用字体
import 'vfonts/Lato.css'
import 'vfonts/Lato.css';
// 等宽字体
import 'vfonts/FiraCode.css'
import 'vfonts/FiraCode.css';
createApp(App).use(router).use(store).mount('#app')
createApp(App).use(router).use(store).mount('#app');
declare global {
interface Window {
$message: MessageApiInjection,
$store: any
}
interface Window {
$message: MessageApiInjection;
$store: any;
}
}

@ -1,114 +1,114 @@
import { createRouter, createWebHashHistory } from "vue-router";
import { createRouter, createWebHashHistory } from 'vue-router';
const routes = [
{
path: "/",
name: "home",
path: '/',
name: 'home',
meta: {
title: "广场",
title: '广',
keepAlive: true,
},
component: () => import("@/views/Home.vue"),
component: () => import('@/views/Home.vue'),
},
{
path: "/post",
name: "post",
path: '/post',
name: 'post',
meta: {
title: "泡泡详情",
title: '',
},
component: () => import("@/views/Post.vue"),
component: () => import('@/views/Post.vue'),
},
{
path: "/topic",
name: "topic",
path: '/topic',
name: 'topic',
meta: {
title: "话题",
title: '',
},
component: () => import("@/views/Topic.vue"),
component: () => import('@/views/Topic.vue'),
},
{
path: "/anouncement",
name: "anouncement",
path: '/anouncement',
name: 'anouncement',
meta: {
title: "公告",
title: '',
},
component: () => import("@/views/Anouncement.vue"),
component: () => import('@/views/Anouncement.vue'),
},
{
path: "/profile",
name: "profile",
path: '/profile',
name: 'profile',
meta: {
title: "主页",
title: '',
},
component: () => import("@/views/Profile.vue"),
component: () => import('@/views/Profile.vue'),
},
{
path: "/u",
name: "user",
path: '/u',
name: 'user',
meta: {
title: "用户详情",
title: '',
},
component: () => import("@/views/User.vue"),
component: () => import('@/views/User.vue'),
},
{
path: "/messages",
name: "messages",
path: '/messages',
name: 'messages',
meta: {
title: "消息",
title: '',
},
component: () => import("@/views/Messages.vue"),
component: () => import('@/views/Messages.vue'),
},
{
path: "/collection",
name: "collection",
path: '/collection',
name: 'collection',
meta: {
title: "收藏",
title: '',
},
component: () => import("@/views/Collection.vue"),
component: () => import('@/views/Collection.vue'),
},
{
path: "/contacts",
name: "contacts",
path: '/contacts',
name: 'contacts',
meta: {
title: "好友",
title: '',
},
component: () => import("@/views/Contacts.vue"),
component: () => import('@/views/Contacts.vue'),
},
{
path: "/following",
name: "following",
path: '/following',
name: 'following',
meta: {
title: "关注",
title: '',
},
component: () => import("@/views/Following.vue"),
component: () => import('@/views/Following.vue'),
},
{
path: "/wallet",
name: "wallet",
path: '/wallet',
name: 'wallet',
meta: {
title: "钱包",
title: '',
},
component: () => import("@/views/Wallet.vue"),
component: () => import('@/views/Wallet.vue'),
},
{
path: "/setting",
name: "setting",
path: '/setting',
name: 'setting',
meta: {
title: "设置",
title: '',
},
component: () => import("@/views/Setting.vue"),
component: () => import('@/views/Setting.vue'),
},
{
path: "/404",
name: "404",
path: '/404',
name: '404',
meta: {
title: "404",
title: '404',
},
component: () => import("@/views/404.vue"),
component: () => import('@/views/404.vue'),
},
{
path: "/:pathMatch(.*)",
redirect: "/404",
path: '/:pathMatch(.*)',
redirect: '/404',
},
];

@ -1,19 +1,19 @@
declare module '*.vue' {
import type { DefineComponent } from 'vue'
const component: DefineComponent<{}, {}, any>
export default component
import type { DefineComponent } from 'vue';
const component: DefineComponent<{}, {}, any>;
export default component;
}
declare module '*.svg'
declare module '*.png'
declare module '*.jpg'
declare module '*.jpeg'
declare module '*.gif'
declare module '*.bmp'
declare module '*.tiff'
declare module '*.json'
declare module 'paopao-video-player'
declare module '*.svg';
declare module '*.png';
declare module '*.jpg';
declare module '*.jpeg';
declare module '*.gif';
declare module '*.bmp';
declare module '*.tiff';
declare module '*.json';
declare module 'paopao-video-player';
interface AnyObject {
[key: string]: any
}
[key: string]: any;
}

@ -1,22 +1,22 @@
import { createStore } from "vuex";
import { createStore } from 'vuex';
export default createStore({
state: {
refresh: Date.now(),
refreshTopicFollow: Date.now(),
theme: localStorage.getItem("PAOPAO_THEME"),
theme: localStorage.getItem('PAOPAO_THEME'),
collapsedLeft: document.body.clientWidth <= 821,
collapsedRight: document.body.clientWidth <= 821,
drawerModelShow: document.body.clientWidth <= 821,
desktopModelShow: document.body.clientWidth > 821,
authModalShow: false,
authModelTab: "signin",
authModelTab: 'signin',
unreadMsgCount: 0,
userLogined: false,
userInfo: {
id: 0,
username: "",
nickname: "",
username: '',
nickname: '',
created_on: 0,
follows: 0,
followings: 0,
@ -35,13 +35,13 @@ export default createStore({
defaultTweetMaxLength: 2000,
tweetWebEllipsisSize: 400,
tweetMobileEllipsisSize: 300,
defaultTweetVisibility: "friend",
defaultTweetVisibility: 'friend',
defaultMsgLoopInterval: 5000,
copyrightTop: "2023 paopao.info",
copyrightTop: '2023 paopao.info',
copyrightLeft: "Roc's Me",
copyrightLeftLink: "",
copyrightRight: "泡泡(PaoPao)开源社区",
copyrightRightLink: "https://www.paopao.info",
copyrightLeftLink: '',
copyrightRight: '(PaoPao)',
copyrightRightLink: 'https://www.paopao.info',
},
},
mutations: {
@ -79,47 +79,47 @@ export default createStore({
},
loadDefaultSiteProfile(state) {
state.profile.useFriendship =
import.meta.env.VITE_USE_FRIENDSHIP.toLowerCase() === "true";
import.meta.env.VITE_USE_FRIENDSHIP.toLowerCase() === 'true';
state.profile.enableTrendsBar =
import.meta.env.VITE_ENABLE_TRENDS_BAR.toLowerCase() === "true";
import.meta.env.VITE_ENABLE_TRENDS_BAR.toLowerCase() === 'true';
state.profile.enableWallet =
import.meta.env.VITE_ENABLE_WALLET.toLocaleLowerCase() === "true";
import.meta.env.VITE_ENABLE_WALLET.toLocaleLowerCase() === 'true';
state.profile.allowTweetAttachment =
import.meta.env.VITE_ALLOW_TWEET_ATTACHMENT.toLowerCase() === "true";
import.meta.env.VITE_ALLOW_TWEET_ATTACHMENT.toLowerCase() === 'true';
state.profile.allowTweetAttachmentPrice =
import.meta.env.VITE_ALLOW_TWEET_ATTACHMENT_PRICE.toLowerCase() ===
"true";
'true';
state.profile.allowTweetVideo =
import.meta.env.VITE_ALLOW_TWEET_VIDEO.toLowerCase() === "true";
import.meta.env.VITE_ALLOW_TWEET_VIDEO.toLowerCase() === 'true';
state.profile.allowUserRegister =
import.meta.env.VITE_ALLOW_USER_REGISTER.toLowerCase() === "true";
import.meta.env.VITE_ALLOW_USER_REGISTER.toLowerCase() === 'true';
state.profile.allowPhoneBind =
import.meta.env.VITE_ALLOW_PHONE_BIND.toLowerCase() === "true";
import.meta.env.VITE_ALLOW_PHONE_BIND.toLowerCase() === 'true';
state.profile.defaultTweetMaxLength = Number(
import.meta.env.VITE_DEFAULT_TWEET_MAX_LENGTH
import.meta.env.VITE_DEFAULT_TWEET_MAX_LENGTH,
);
state.profile.tweetWebEllipsisSize = Number(
import.meta.env.VITE_TWEET_WEB_ELLIPSIS_SIZE
import.meta.env.VITE_TWEET_WEB_ELLIPSIS_SIZE,
);
state.profile.tweetMobileEllipsisSize = Number(
import.meta.env.VITE_TWEET_MOBILE_ELLIPSIS_SIZE
import.meta.env.VITE_TWEET_MOBILE_ELLIPSIS_SIZE,
);
state.profile.defaultTweetVisibility =
import.meta.env.VITE_DEFAULT_TWEET_VISIBILITY.toLowerCase();
state.profile.defaultMsgLoopInterval = Number(
import.meta.env.VITE_DEFAULT_MSG_LOOP_INTERVAL
import.meta.env.VITE_DEFAULT_MSG_LOOP_INTERVAL,
);
state.profile.copyrightTop = import.meta.env.VITE_COPYRIGHT_TOP;
@ -182,11 +182,11 @@ export default createStore({
data.copyright_right_link ?? p.copyrightRightLink;
},
userLogout(state) {
localStorage.removeItem("PAOPAO_TOKEN");
localStorage.removeItem('PAOPAO_TOKEN');
state.userInfo = {
id: 0,
nickname: "",
username: "",
nickname: '',
username: '',
created_on: 0,
follows: 0,
followings: 0,

@ -41,7 +41,7 @@ declare module Item {
/** 评论者UID */
user_id: number;
/** 类别1为标题2为文字段落3为图片地址4为视频地址5为语音地址6为链接地址 */
type: import("@/utils/IEnum").CommentItemTypeEnum;
type: import('@/utils/IEnum').CommentItemTypeEnum;
/** 内容 */
content: string;
/** 排序,越小越靠前 */
@ -73,13 +73,13 @@ declare module Item {
/** 评论者城市地址 */
ip_loc: string;
/** 是否精选 */
is_essence: import("@/utils/IEnum").YesNoEnum;
is_essence: import('@/utils/IEnum').YesNoEnum;
/** 点赞数 */
thumbs_up_count: number;
/** 是否点赞0为未点赞1为已点赞 */
is_thumbs_up: import("@/utils/IEnum").YesNoEnum;
is_thumbs_up: import('@/utils/IEnum').YesNoEnum;
/** 是否反对0为未反对1为已反对 */
is_thumbs_down: import("@/utils/IEnum").YesNoEnum;
is_thumbs_down: import('@/utils/IEnum').YesNoEnum;
/** 创建时间 */
created_on: number;
/** 修改时间 */
@ -120,9 +120,9 @@ declare module Item {
/** 点赞数 */
thumbs_up_count: number;
/** 是否点赞0为未点赞1为已点赞 */
is_thumbs_up: import("@/utils/IEnum").YesNoEnum;
is_thumbs_up: import('@/utils/IEnum').YesNoEnum;
/** 是否反对0为未反对1为已反对 */
is_thumbs_down: import("@/utils/IEnum").YesNoEnum;
is_thumbs_down: import('@/utils/IEnum').YesNoEnum;
/** 创建时间 */
created_on: number;
/** 修改时间 */
@ -165,7 +165,7 @@ declare module Item {
/** 内容ID */
id: number;
/** 类型1为标题2为文字段落3为图片地址4为视频地址5为语音地址6为链接地址7为附件资源8为收费资源 */
type: import("@/utils/IEnum").PostItemTypeEnum;
type: import('@/utils/IEnum').PostItemTypeEnum;
/** POST ID */
post_id: number;
/** 内容 */
@ -218,7 +218,7 @@ declare module Item {
/** 标签列表 */
tags: { [key: string]: number } | string;
/** 可见性0为公开1为私密2为好友可见 */
visibility: import("@/utils/IEnum").VisibilityEnum;
visibility: import('@/utils/IEnum').VisibilityEnum;
/** 是否锁定 */
is_lock: number;
/** 是否置顶 */
@ -248,7 +248,7 @@ declare module Item {
interface MessageProps {
id: number;
/** 类型1为动态2为评论3为回复4为私信5为好友申请 99为系统通知 */
type: import("@/utils/IEnum").MessageTypeEnum;
type: import('@/utils/IEnum').MessageTypeEnum;
/** 摘要说明 */
brief: string;
/** 详细内容 */
@ -302,7 +302,7 @@ declare module Item {
interface AttachmentProps {
id: number;
/** 类别1为图片2为视频3为其他附件 */
type: import("@/utils/IEnum").AttachmentTypeEnum;
type: import('@/utils/IEnum').AttachmentTypeEnum;
/** 发布者用户UID */
user_id: number;
/** 发布者用户数据 */

@ -42,7 +42,7 @@ declare module NetParams {
}
interface UserGetMessages {
style: "all" | "system" | "whisper" | "requesting" | "unread";
style: 'all' | 'system' | 'whisper' | 'requesting' | 'unread';
page: number;
page_size: number;
}
@ -162,7 +162,7 @@ declare module NetParams {
interface PostGetPosts {
query: string | null;
type: string;
style: "newest" | "hots" | "following" | "search";
style: 'newest' | 'hots' | 'following' | 'search';
page: number;
page_size: number;
}
@ -182,7 +182,7 @@ declare module NetParams {
interface PostVisibilityPost {
id: number;
/** 可见性0为公开1为私密2为好友可见 */
visibility: import("@/utils/IEnum").VisibilityEnum;
visibility: import('@/utils/IEnum').VisibilityEnum;
}
interface PostGetPostStar {
@ -202,14 +202,14 @@ declare module NetParams {
}
interface PostGetTags {
type: "hot" | "new" | "follow" | "pin" | "hot_extral";
type: 'hot' | 'new' | 'follow' | 'pin' | 'hot_extral';
num: number;
extral_num?: number;
}
interface PostGetPostComments {
id: number;
style: "default" | "hots" | "newest";
style: 'default' | 'hots' | 'newest';
page?: number;
page_size?: number;
}
@ -226,7 +226,7 @@ declare module NetParams {
/** 附件价格 */
attachment_price: number;
/** 可见性0为公开1为私密2为好友可见 */
visibility: import("@/utils/IEnum").VisibilityEnum;
visibility: import('@/utils/IEnum').VisibilityEnum;
}
interface PostDeletePost {

@ -135,7 +135,7 @@ declare module NetReq {
interface PostVisibilityPost {
/** 可见性0为公开1为私密2为好友可见 */
visibility_status: import("@/utils/IEnum").VisibilityEnum;
visibility_status: import('@/utils/IEnum').VisibilityEnum;
}
interface PostGetPostStar {
@ -179,7 +179,7 @@ declare module NetReq {
interface PostDeleteComment {}
interface PostHighlightComment {
highlight_status: import("@/utils/IEnum").YesNoEnum;
highlight_status: import('@/utils/IEnum').YesNoEnum;
}
type PostCreateCommentReply = Item.ReplyProps;

@ -4,8 +4,8 @@ export const parsePostTag = (content: string) => {
var tagExp = /(#|)([^#@\s])+?\s+?/g; // 这⾥中⽂#和英⽂#都会识别
var atExp = /@([a-zA-Z0-9])+?\s+?/g; // 这⾥中⽂#和英⽂#都会识别
content = content
.replace(/<[^>]*?>/gi, "")
.replace(/(.*?)<\/[^>]*?>/gi, "")
.replace(/<[^>]*?>/gi, '')
.replace(/(.*?)<\/[^>]*?>/gi, '')
.replace(tagExp, (item) => {
tags.push(item.substr(1).trim());
return (
@ -13,7 +13,7 @@ export const parsePostTag = (content: string) => {
encodeURIComponent(item.substr(1).trim()) +
'">' +
item.trim() +
"</a> "
'</a> '
);
})
.replace(atExp, (item) => {
@ -23,7 +23,7 @@ export const parsePostTag = (content: string) => {
encodeURIComponent(item.substr(1).trim()) +
'">' +
item.trim() +
"</a> "
'</a> '
);
});
return { content, tags, users };
@ -34,28 +34,28 @@ export const preparePost = (
foldHint: string,
unfoldHint: string,
maxSize: number,
isFold: boolean = true
isFold: boolean = true,
) => {
const isEllipsis = content.length > maxSize;
if (isFold && isEllipsis) {
content = content.substring(0, maxSize);
let latestChar = content.charAt(maxSize - 1);
if (latestChar == "#" || latestChar == "#" || latestChar == "@") {
if (latestChar == '#' || latestChar == '#' || latestChar == '@') {
content = content.substring(0, maxSize - 1);
}
}
const tagExp = /(#|)([^#@\s])+?\s+?/g; // 这⾥中⽂#和英⽂#都会识别
const atExp = /@([a-zA-Z0-9])+?\s+?/g; // 这⾥中⽂#和英⽂#都会识别
content = content
.replace(/<[^>]*?>/gi, "")
.replace(/(.*?)<\/[^>]*?>/gi, "")
.replace(/<[^>]*?>/gi, '')
.replace(/(.*?)<\/[^>]*?>/gi, '')
.replace(tagExp, (item) => {
return (
'<a class="hash-link" data-detail="tag:' +
encodeURIComponent(item.substring(1).trim()) +
'">' +
item.trim() +
"</a> "
'</a> '
);
})
.replace(atExp, (item) => {
@ -64,16 +64,16 @@ export const preparePost = (
encodeURIComponent(item.substring(1).trim()) +
'">' +
item.trim() +
"</a> "
'</a> '
);
});
if (isEllipsis) {
content =
content.trimEnd() +
(isFold ? "...&nbsp;" : "&nbsp;") +
(isFold ? '...&nbsp;' : '&nbsp;') +
'<a class="hash-link" data-detail="post">' +
(isFold ? foldHint : unfoldHint) +
"</a> ";
'</a> ';
}
return content;
};

@ -1,8 +1,8 @@
export const prettyQuoteNum = (num: number) => {
if (num >= 1000) {
return (num / 1000).toFixed(1) + "千";
return (num / 1000).toFixed(1) + '千';
} else if (num >= 10000) {
return (num / 10000).toFixed(1) + "万";
return (num / 10000).toFixed(1) + '万';
}
return num;
};

@ -2,12 +2,12 @@
* @file
*/
import moment from "moment";
import "moment/dist/locale/zh-cn";
moment.locale("zh-cn");
import moment from 'moment';
import 'moment/dist/locale/zh-cn';
moment.locale('zh-cn');
export const formatTime = (time: number) => {
return moment.unix(time).utc(true).format("YYYY-MM-DD HH:mm");
return moment.unix(time).utc(true).format('YYYY-MM-DD HH:mm');
};
export const formatHumanTime = (time: number) => {
@ -22,9 +22,9 @@ export const formatPrettyTime = (time: number) => {
let mt = moment.unix(time);
let now = moment();
if (mt.year() != now.year()) {
return mt.utc(true).format("YYYY-MM-DD HH:mm");
} else if (moment().diff(mt, "month") > 3) {
return mt.utc(true).format("MM-DD HH:mm");
return mt.utc(true).format('YYYY-MM-DD HH:mm');
} else if (moment().diff(mt, 'month') > 3) {
return mt.utc(true).format('MM-DD HH:mm');
}
return mt.fromNow();
};
@ -33,13 +33,13 @@ export const formatPrettyDate = (time: number) => {
let mt = moment.unix(time);
let now = moment();
if (mt.year() != now.year()) {
return mt.utc(true).format("YYYY-MM-DD");
} else if (moment().diff(mt, "month") > 3) {
return mt.utc(true).format("MM-DD");
return mt.utc(true).format('YYYY-MM-DD');
} else if (moment().diff(mt, 'month') > 3) {
return mt.utc(true).format('MM-DD');
}
return mt.fromNow();
};
export const formatDate = (time: number) => {
return moment.unix(time).utc(true).format("YYYY年MM月");
return moment.unix(time).utc(true).format('YYYYMM');
};

@ -1,47 +1,51 @@
export const isZipFile = (file: File): Promise<unknown> => {
const fileReader = new FileReader();
const fileReader = new FileReader();
const isValidZipFileType = (fileType: string): boolean => {
const zipFileTypes = ['application/zip', 'application/x-zip', 'application/octet-stream', 'application/x-zip-compressed'];
return zipFileTypes.includes(fileType);
};
const isValidZipFileType = (fileType: string): boolean => {
const zipFileTypes = [
'application/zip',
'application/x-zip',
'application/octet-stream',
'application/x-zip-compressed',
];
return zipFileTypes.includes(fileType);
};
const checkFileType = (): boolean => {
const arr = new Uint8Array(fileReader.result as ArrayBuffer).subarray(0, 4);
let header = '';
for (let i = 0; i < arr.length; i++) {
header += arr[i].toString(16);
}
const checkFileType = (): boolean => {
const arr = new Uint8Array(fileReader.result as ArrayBuffer).subarray(0, 4);
let header = '';
for (let i = 0; i < arr.length; i++) {
header += arr[i].toString(16);
}
switch (header) {
case '504b0304':
case '504b0506':
case '504b0708':
return isValidZipFileType('application/zip');
case '504b030414':
return isValidZipFileType('application/x-zip-compressed');
case '504b0508':
return isValidZipFileType('application/x-zip');
case '504b5370':
return isValidZipFileType('application/octet-stream');
default:
return false;
}
};
switch (header) {
case '504b0304':
case '504b0506':
case '504b0708':
return isValidZipFileType('application/zip');
case '504b030414':
return isValidZipFileType('application/x-zip-compressed');
case '504b0508':
return isValidZipFileType('application/x-zip');
case '504b5370':
return isValidZipFileType('application/octet-stream');
default:
return false;
}
};
return new Promise((resolve, reject) => {
fileReader.onloadend = () => {
const fileType = file.type;
if (fileType === '' || fileType === 'application/octet-stream') {
// 如果浏览器不能识别文件类型,则进行手动检查
resolve(checkFileType());
} else {
// 如果浏览器可以识别文件类型,则根据文件类型进行检查
resolve(isValidZipFileType(fileType));
}
};
return new Promise((resolve, reject) => {
fileReader.onloadend = () => {
const fileType = file.type;
if (fileType === '' || fileType === 'application/octet-stream') {
// 如果浏览器不能识别文件类型,则进行手动检查
resolve(checkFileType());
} else {
// 如果浏览器可以识别文件类型,则根据文件类型进行检查
resolve(isValidZipFileType(fileType));
}
};
fileReader.readAsArrayBuffer(file.slice(0, 4));
});
}
fileReader.readAsArrayBuffer(file.slice(0, 4));
});
};

@ -1,54 +1,55 @@
import axios, { AxiosRequestConfig, AxiosRequestHeaders, Method } from 'axios';
const service = axios.create({
baseURL: import.meta.env.VITE_HOST,
timeout: 30000,
baseURL: import.meta.env.VITE_HOST,
timeout: 30000,
});
service.interceptors.request.use(
config => {
// 鉴权Header
if (localStorage.getItem('PAOPAO_TOKEN')) {
(config.headers as any)['Authorization'] = 'Bearer ' + localStorage.getItem('PAOPAO_TOKEN');
}
return config;
},
error => {
return Promise.reject(error);
(config) => {
// 鉴权Header
if (localStorage.getItem('PAOPAO_TOKEN')) {
(config.headers as any)['Authorization'] =
'Bearer ' + localStorage.getItem('PAOPAO_TOKEN');
}
return config;
},
(error) => {
return Promise.reject(error);
},
);
service.interceptors.response.use(
response => {
const { data = {}, code = 0 } = response?.data || {};
if (+code === 0) {
return data || {};
} else {
Promise.reject(response?.data || {});
}
},
(error = {}) => {
const { response = {} } = error || {};
// 重定向
if (+response?.status === 401) {
localStorage.removeItem('PAOPAO_TOKEN');
(response) => {
const { data = {}, code = 0 } = response?.data || {};
if (+code === 0) {
return data || {};
} else {
Promise.reject(response?.data || {});
}
},
(error = {}) => {
const { response = {} } = error || {};
// 重定向
if (+response?.status === 401) {
localStorage.removeItem('PAOPAO_TOKEN');
if (response?.data.code !== 10005) {
window.$message.warning(response?.data.msg || '');
} else {
// 打开登录弹窗
window.$store.commit('triggerAuth', true);
}
} else {
window.$message.error(response?.data?.msg || '');
}
return Promise.reject(response?.data || {});
if (response?.data.code !== 10005) {
window.$message.warning(response?.data.msg || '');
} else {
// 打开登录弹窗
window.$store.commit('triggerAuth', true);
}
} else {
window.$message.error(response?.data?.msg || '');
}
return Promise.reject(response?.data || {});
},
);
export default service;
export function request<T, R>(config: AxiosRequestConfig<T>): Promise<R> {
return service(config) as unknown as Promise<R>;
return service(config) as unknown as Promise<R>;
}

@ -1,19 +1,18 @@
// 滚动到顶部
export const scrollToTop = (scrollDuration: number) => {
var cosParameter = window.scrollY / 2;
var scrollCount = 0;
var oldTimestamp = performance.now();
function step(newTimestamp: number) {
scrollCount +=
Math.PI / (scrollDuration / (newTimestamp - oldTimestamp));
if (scrollCount >= Math.PI) window.scrollTo(0, 0);
if (window.scrollY === 0) return;
window.scrollTo(
0,
Math.round(cosParameter + cosParameter * Math.cos(scrollCount))
);
oldTimestamp = newTimestamp;
window.requestAnimationFrame(step);
}
var cosParameter = window.scrollY / 2;
var scrollCount = 0;
var oldTimestamp = performance.now();
function step(newTimestamp: number) {
scrollCount += Math.PI / (scrollDuration / (newTimestamp - oldTimestamp));
if (scrollCount >= Math.PI) window.scrollTo(0, 0);
if (window.scrollY === 0) return;
window.scrollTo(
0,
Math.round(cosParameter + cosParameter * Math.cos(scrollCount)),
);
oldTimestamp = newTimestamp;
window.requestAnimationFrame(step);
};
}
window.requestAnimationFrame(step);
};

@ -21,9 +21,9 @@ import { useRouter } from 'vue-router';
const router = useRouter();
const goHome = () => {
router.push({
path: '/',
});
router.push({
path: '/',
});
};
</script>

@ -58,12 +58,12 @@ const pageSize = ref(20);
const totalPage = ref(0);
const updatePage = (p: number) => {
page.value = p;
// TODO
page.value = p;
// TODO
};
onMounted(() => {
// TODO
// TODO
});
</script>

@ -51,7 +51,7 @@ import { ref, onMounted } from 'vue';
import { useStore } from 'vuex';
import { useRoute } from 'vue-router';
import { useDialog } from 'naive-ui';
import InfiniteLoading from "v3-infinite-loading";
import InfiniteLoading from 'v3-infinite-loading';
import { getCollections, followUser, unfollowUser } from '@/api/user';
const store = useStore();
@ -66,101 +66,104 @@ const pageSize = ref(20);
const totalPage = ref(0);
const showWhisper = ref(false);
const whisperReceiver = ref<Item.UserInfo>({
id: 0,
avatar: '',
username: '',
nickname: '',
is_admin: false,
is_friend: true,
is_following: false,
created_on: 0,
follows: 0,
followings: 0,
status: 1,
id: 0,
avatar: '',
username: '',
nickname: '',
is_admin: false,
is_friend: true,
is_following: false,
created_on: 0,
follows: 0,
followings: 0,
status: 1,
});
const onSendWhisper = (user: Item.UserInfo) => {
whisperReceiver.value = user;
showWhisper.value = true;
const onSendWhisper = (user: Item.UserInfo) => {
whisperReceiver.value = user;
showWhisper.value = true;
};
const whisperSuccess = () => {
showWhisper.value = false;
showWhisper.value = false;
};
const onHandleFollowAction = (post: Item.PostProps) => {
dialog.success({
title: '',
content:
'' + (post.user.is_following ? '' : '') + '',
positiveText: '',
negativeText: '',
onPositiveClick: () => {
if (post.user.is_following) {
unfollowUser({
user_id: post.user.id,
}).then((_res) => {
window.$message.success('');
postFollowAction(post.user_id, false);
})
.catch((_err) => {});
} else {
followUser({
user_id: post.user.id,
}).then((_res) => {
window.$message.success('');
postFollowAction(post.user_id, true);
})
.catch((_err) => {});
}
},
});
dialog.success({
title: '',
content:
'' + (post.user.is_following ? '' : '') + '',
positiveText: '',
negativeText: '',
onPositiveClick: () => {
if (post.user.is_following) {
unfollowUser({
user_id: post.user.id,
})
.then((_res) => {
window.$message.success('');
postFollowAction(post.user_id, false);
})
.catch((_err) => {});
} else {
followUser({
user_id: post.user.id,
})
.then((_res) => {
window.$message.success('');
postFollowAction(post.user_id, true);
})
.catch((_err) => {});
}
},
});
};
function postFollowAction(userId:number, isFollowing: boolean) {
for (let index in list.value) {
if (list.value[index].user_id == userId) {
list.value[index].user.is_following = isFollowing;
}
function postFollowAction(userId: number, isFollowing: boolean) {
for (let index in list.value) {
if (list.value[index].user_id == userId) {
list.value[index].user.is_following = isFollowing;
}
}
}
const loadPosts = () => {
loading.value = true;
getCollections({
page: page.value,
page_size: pageSize.value,
}).then((res) => {
loading.value = false;
if (res.list.length === 0) {
noMore.value = true
}
if (page.value > 1) {
list.value = list.value.concat(res.list);
} else {
list.value = res.list;
window.scrollTo(0, 0);
}
totalPage.value = Math.ceil(res.pager.total_rows / pageSize.value);
loading.value = true;
getCollections({
page: page.value,
page_size: pageSize.value,
})
.then((res) => {
loading.value = false;
if (res.list.length === 0) {
noMore.value = true;
}
if (page.value > 1) {
list.value = list.value.concat(res.list);
} else {
list.value = res.list;
window.scrollTo(0, 0);
}
totalPage.value = Math.ceil(res.pager.total_rows / pageSize.value);
})
.catch((_err) => {
loading.value = false;
if (page.value > 1) {
page.value--
}
loading.value = false;
if (page.value > 1) {
page.value--;
}
});
};
const nextPage = () => {
if (page.value < totalPage.value || totalPage.value == 0) {
noMore.value = false;
page.value++;
loadPosts();
} else {
noMore.value = true;
}
if (page.value < totalPage.value || totalPage.value == 0) {
noMore.value = false;
page.value++;
loadPosts();
} else {
noMore.value = true;
}
};
onMounted(() => {
loadPosts();
loadPosts();
});
</script>

@ -34,7 +34,7 @@
<script setup lang="ts">
import { ref, onMounted } from 'vue';
import { getContacts } from '@/api/post';
import InfiniteLoading from "v3-infinite-loading";
import InfiniteLoading from 'v3-infinite-loading';
import { useRoute } from 'vue-router';
const route = useRoute();
@ -46,73 +46,74 @@ const pageSize = ref(20);
const totalPage = ref(0);
const showWhisper = ref(false);
const whisperReceiver = ref<Item.UserInfo>({
id: 0,
avatar: '',
username: '',
nickname: '',
is_admin: false,
is_friend: true,
is_following: false,
created_on: 0,
follows: 0,
followings: 0,
status: 1,
id: 0,
avatar: '',
username: '',
nickname: '',
is_admin: false,
is_friend: true,
is_following: false,
created_on: 0,
follows: 0,
followings: 0,
status: 1,
});
const onSendWhisper = (user: Item.UserInfo) => {
whisperReceiver.value = user;
showWhisper.value = true;
const onSendWhisper = (user: Item.UserInfo) => {
whisperReceiver.value = user;
showWhisper.value = true;
};
const whisperSuccess = () => {
showWhisper.value = false;
showWhisper.value = false;
};
const nextPage = () => {
if (page.value < totalPage.value || totalPage.value == 0) {
noMore.value = false;
page.value++;
loadContacts();
} else {
noMore.value = true;
}
if (page.value < totalPage.value || totalPage.value == 0) {
noMore.value = false;
page.value++;
loadContacts();
} else {
noMore.value = true;
}
};
onMounted(() => {
loadContacts()
loadContacts();
});
const loadContacts = (scrollToBottom: boolean = false) => {
if (list.value.length === 0) {
loading.value = true;
}
getContacts({
page: page.value,
page_size: pageSize.value,
}).then((res) => {
loading.value = false;
if (res.list.length === 0) {
noMore.value = true
}
if (page.value > 1) {
list.value = list.value.concat(res.list);
} else {
list.value = res.list;
if (scrollToBottom) {
setTimeout(() => {
window.scrollTo(0, 99999);
}, 50);
}
if (list.value.length === 0) {
loading.value = true;
}
getContacts({
page: page.value,
page_size: pageSize.value,
})
.then((res) => {
loading.value = false;
if (res.list.length === 0) {
noMore.value = true;
}
if (page.value > 1) {
list.value = list.value.concat(res.list);
} else {
list.value = res.list;
if (scrollToBottom) {
setTimeout(() => {
window.scrollTo(0, 99999);
}, 50);
}
totalPage.value = Math.ceil(res.pager.total_rows / pageSize.value);
}
totalPage.value = Math.ceil(res.pager.total_rows / pageSize.value);
})
.catch((_err) => {
loading.value = false;
if (page.value > 1) {
page.value--;
}
loading.value = false;
if (page.value > 1) {
page.value--;
}
});
}
};
</script>
<style lang="less" scoped>

@ -38,151 +38,153 @@
<script setup lang="ts">
import { ref, onMounted, computed } from 'vue';
import { getUserFollows, getUserFollowings } from '@/api/user';
import InfiniteLoading from "v3-infinite-loading";
import InfiniteLoading from 'v3-infinite-loading';
import { useRoute } from 'vue-router';
const route = useRoute();
const loading = ref(false);
const noMore = ref(false);
const list = ref<Item.ContactItemProps[]>([]);
const nickname= route.query.n as string || "粉丝详情";
const username = route.query.s as string || "";
const tabler = ref(route.query.t as string || "follows")
const nickname = (route.query.n as string) || '';
const username = (route.query.s as string) || '';
const tabler = ref((route.query.t as string) || 'follows');
const page = ref(+(route.query.p as string) || 1);
const pageSize = ref(20);
const totalPage = ref(0);
const showWhisper = ref(false);
const whisperReceiver = ref<Item.UserInfo>({
id: 0,
avatar: '',
username: '',
nickname: '',
is_admin: false,
is_friend: true,
is_following: false,
created_on: 0,
follows: 0,
followings: 0,
status: 1,
id: 0,
avatar: '',
username: '',
nickname: '',
is_admin: false,
is_friend: true,
is_following: false,
created_on: 0,
follows: 0,
followings: 0,
status: 1,
});
function resetPage(tab: "follows" | "followings") {
list.value = [];
loading.value = false;
noMore.value = false;
page.value = 1;
totalPage.value = 0;
tabler.value = tab;
function resetPage(tab: 'follows' | 'followings') {
list.value = [];
loading.value = false;
noMore.value = false;
page.value = 1;
totalPage.value = 0;
tabler.value = tab;
}
const completeStr = computed(() => {
if (tabler.value == "follows") {
return ''
} else {
return ''
}
if (tabler.value == 'follows') {
return '';
} else {
return '';
}
});
const onSendWhisper = (user: Item.UserInfo) => {
whisperReceiver.value = user;
showWhisper.value = true;
const onSendWhisper = (user: Item.UserInfo) => {
whisperReceiver.value = user;
showWhisper.value = true;
};
const whisperSuccess = () => {
showWhisper.value = false;
showWhisper.value = false;
};
const nextPage = () => {
if (page.value < totalPage.value || totalPage.value == 0) {
noMore.value = false;
page.value++;
loadPage();
} else {
noMore.value = true;
}
if (page.value < totalPage.value || totalPage.value == 0) {
noMore.value = false;
page.value++;
loadPage();
} else {
noMore.value = true;
}
};
const changeTab = (tab: "follows" | "followings") => {
resetPage(tab);
loadPage();
const changeTab = (tab: 'follows' | 'followings') => {
resetPage(tab);
loadPage();
};
const loadPage = () => {
if (tabler.value === "follows") {
loadFollows(username);
} else if (tabler.value === "followings") {
loadFollowings(username);
}
}
if (tabler.value === 'follows') {
loadFollows(username);
} else if (tabler.value === 'followings') {
loadFollowings(username);
}
};
const loadFollows = (username: string, scrollToBottom: boolean = false) => {
if (list.value.length === 0) {
loading.value = true;
}
getUserFollows({
username: username,
page: page.value,
page_size: pageSize.value,
}).then((res) => {
loading.value = false;
if (res.list.length === 0) {
noMore.value = true
}
if (page.value > 1) {
list.value = list.value.concat(res.list);
} else {
list.value = res.list;
if (scrollToBottom) {
setTimeout(() => {
window.scrollTo(0, 99999);
}, 50);
}
if (list.value.length === 0) {
loading.value = true;
}
getUserFollows({
username: username,
page: page.value,
page_size: pageSize.value,
})
.then((res) => {
loading.value = false;
if (res.list.length === 0) {
noMore.value = true;
}
if (page.value > 1) {
list.value = list.value.concat(res.list);
} else {
list.value = res.list;
if (scrollToBottom) {
setTimeout(() => {
window.scrollTo(0, 99999);
}, 50);
}
totalPage.value = Math.ceil(res.pager.total_rows / pageSize.value);
}
totalPage.value = Math.ceil(res.pager.total_rows / pageSize.value);
})
.catch((_err) => {
loading.value = false;
if (page.value > 1) {
page.value--;
}
loading.value = false;
if (page.value > 1) {
page.value--;
}
});
};
const loadFollowings = (username: string, scrollToBottom: boolean = false) => {
if (list.value.length === 0) {
loading.value = true;
}
getUserFollowings({
username: username,
page: page.value,
page_size: pageSize.value,
}).then((res) => {
loading.value = false;
if (res.list.length === 0) {
noMore.value = true
}
if (page.value > 1) {
list.value = list.value.concat(res.list);
} else {
list.value = res.list;
if (scrollToBottom) {
setTimeout(() => {
window.scrollTo(0, 99999);
}, 50);
}
if (list.value.length === 0) {
loading.value = true;
}
getUserFollowings({
username: username,
page: page.value,
page_size: pageSize.value,
})
.then((res) => {
loading.value = false;
if (res.list.length === 0) {
noMore.value = true;
}
if (page.value > 1) {
list.value = list.value.concat(res.list);
} else {
list.value = res.list;
if (scrollToBottom) {
setTimeout(() => {
window.scrollTo(0, 99999);
}, 50);
}
totalPage.value = Math.ceil(res.pager.total_rows / pageSize.value);
}
totalPage.value = Math.ceil(res.pager.total_rows / pageSize.value);
})
.catch((_err) => {
loading.value = false;
if (page.value > 1) {
page.value--;
}
loading.value = false;
if (page.value > 1) {
page.value--;
}
});
};
onMounted(() => {
loadPage();
loadPage();
});
</script>

@ -104,9 +104,14 @@ import { ref, onMounted, reactive, computed, watch } from 'vue';
import { useStore } from 'vuex';
import { useRoute, useRouter } from 'vue-router';
import { useDialog } from 'naive-ui';
import InfiniteLoading from "v3-infinite-loading";
import InfiniteLoading from 'v3-infinite-loading';
import { getPosts, getIndexTrends } from '@/api/post';
import { getUserPosts, deleteFriend, followUser, unfollowUser } from '@/api/user';
import {
getUserPosts,
deleteFriend,
followUser,
unfollowUser,
} from '@/api/user';
import SlideBar from '@opentiny/vue-slide-bar';
import allTweets from '@/assets/img/fresh-tweets.png';
import discoverTweets from '@/assets/img/discover-tweets.jpeg';
@ -117,58 +122,70 @@ const route = useRoute();
const router = useRouter();
const dialog = useDialog();
const newestTweetsStyle = ref<'newest' | 'hots' | 'following'>('newest')
const newestTweetsStyle = ref<'newest' | 'hots' | 'following'>('newest');
const onNewestTweets = () => {
newestTweetsStyle.value='newest'
handleBarClick(slideBarList.value[0], 0)
newestTweetsStyle.value = 'newest';
handleBarClick(slideBarList.value[0], 0);
};
const onHotTweets = () => {
newestTweetsStyle.value='hots'
handleBarClick(slideBarList.value[1], 1)
newestTweetsStyle.value = 'hots';
handleBarClick(slideBarList.value[1], 1);
};
const onFollowingTweets = () => {
newestTweetsStyle.value='following'
handleBarClick(slideBarList.value[2], 2)
newestTweetsStyle.value = 'following';
handleBarClick(slideBarList.value[2], 2);
};
const initBlocks = ref(9)
const wheelBlocks = ref(8)
const initBlocks = ref(9);
const wheelBlocks = ref(8);
const slideBarList = ref<Item.SlideBarItem[]>([
{ title: '', style: 1, username: '', avatar: allTweets, show: true },
{ title: '', style: 2, username: '', avatar: discoverTweets, show: false },
{ title: '', style: 3, username: '', avatar: followingTweets, show: false },
// TODO: 不知道SlideBar抽什么疯如果没有填充下面这些伪数据的话直接设置initBlocks为9而给的数据又不足后面动态添加数据后吖的竟然不能后划了
// f*k不知道哪姿势不对总之先凑合着用吧后期再优化。
{ title: '', style: 1, username: '', avatar: '', show: true },
{ title: '', style: 1, username: '', avatar: '', show: true },
{ title: '', style: 1, username: '', avatar: '', show: true },
{ title: '', style: 1, username: '', avatar: '', show: true },
{ title: '', style: 1, username: '', avatar: '', show: true },
{ title: '', style: 1, username: '', avatar: '', show: true },
{ title: '', style: 1, username: '', avatar: '', show: true },
{ title: '', style: 1, username: '', avatar: '', show: true },
{ title: '', style: 1, username: '', avatar: '', show: true }
{ title: '', style: 1, username: '', avatar: allTweets, show: true },
{
title: '',
style: 2,
username: '',
avatar: discoverTweets,
show: false,
},
{
title: '',
style: 3,
username: '',
avatar: followingTweets,
show: false,
},
// TODO: 不知道SlideBar抽什么疯如果没有填充下面这些伪数据的话直接设置initBlocks为9而给的数据又不足后面动态添加数据后吖的竟然不能后划了
// f*k不知道哪姿势不对总之先凑合着用吧后期再优化。
{ title: '', style: 1, username: '', avatar: '', show: true },
{ title: '', style: 1, username: '', avatar: '', show: true },
{ title: '', style: 1, username: '', avatar: '', show: true },
{ title: '', style: 1, username: '', avatar: '', show: true },
{ title: '', style: 1, username: '', avatar: '', show: true },
{ title: '', style: 1, username: '', avatar: '', show: true },
{ title: '', style: 1, username: '', avatar: '', show: true },
{ title: '', style: 1, username: '', avatar: '', show: true },
{ title: '', style: 1, username: '', avatar: '', show: true },
]);
const user = reactive<Item.UserInfo>({
id: 0,
avatar: '',
username: '',
nickname: '',
is_admin: false,
is_friend: false,
is_following: false,
created_on: 0,
follows: 0,
followings: 0,
status: 1,
id: 0,
avatar: '',
username: '',
nickname: '',
is_admin: false,
is_friend: false,
is_following: false,
created_on: 0,
follows: 0,
followings: 0,
status: 1,
});
const inActionPost = ref<Item.PostProps | null>(null)
const inActionPost = ref<Item.PostProps | null>(null);
const title = ref<string>("泡泡广场")
const title = ref<string>('广');
const loading = ref(false);
const noMore = ref(false);
const targetStyle = ref<number>(1)
const targetUsername = ref<string>("")
const targetStyle = ref<number>(1);
const targetUsername = ref<string>('');
const list = ref<any[]>([]);
const page = ref(1);
const pageSize = ref(20);
@ -176,356 +193,378 @@ const totalPage = ref(0);
const showWhisper = ref(false);
const showAddFriendWhisper = ref(false);
const whisperReceiver = ref<Item.UserInfo>({
id: 0,
avatar: '',
username: '',
nickname: '',
is_admin: false,
is_friend: true,
is_following: false,
created_on: 0,
follows: 0,
followings: 0,
status: 1,
id: 0,
avatar: '',
username: '',
nickname: '',
is_admin: false,
is_friend: true,
is_following: false,
created_on: 0,
follows: 0,
followings: 0,
status: 1,
});
const onSendWhisper = (user: Item.UserInfo) => {
whisperReceiver.value = user;
showWhisper.value = true;
const onSendWhisper = (user: Item.UserInfo) => {
whisperReceiver.value = user;
showWhisper.value = true;
};
const whisperSuccess = () => {
showWhisper.value = false;
showWhisper.value = false;
};
const openAddFriendWhisper = () => {
showAddFriendWhisper.value = true;
showAddFriendWhisper.value = true;
};
const openDeleteFriend = (post: Item.PostProps) => {
dialog.warning({
title: '',
content: ' ' + post.user.nickname + ' / ',
positiveText: '',
negativeText: '',
onPositiveClick: () => {
deleteFriend({
user_id: user.id,
}).then((res) => {
window.$message.success('');
post.user.is_friend = false;
})
.catch((_err) => {});
},
});
dialog.warning({
title: '',
content:
' ' +
post.user.nickname +
' / ',
positiveText: '',
negativeText: '',
onPositiveClick: () => {
deleteFriend({
user_id: user.id,
})
.then((res) => {
window.$message.success('');
post.user.is_friend = false;
})
.catch((_err) => {});
},
});
};
const addFriendWhisperSuccess = () => {
showAddFriendWhisper.value = false;
inActionPost.value = null;
showAddFriendWhisper.value = false;
inActionPost.value = null;
};
const onHandleFriendAction = (post: Item.PostProps) => {
inActionPost.value = post;
user.id = post.user.id;
user.username = post.user.username;
user.nickname = post.user.nickname;
if (post.user.is_friend) {
openDeleteFriend(post);
} else {
openAddFriendWhisper();
}
inActionPost.value = post;
user.id = post.user.id;
user.username = post.user.username;
user.nickname = post.user.nickname;
if (post.user.is_friend) {
openDeleteFriend(post);
} else {
openAddFriendWhisper();
}
};
const onHandleFollowAction = (post: Item.PostProps) => {
dialog.success({
title: '',
content:
'' + (post.user.is_following ? ' @' : ' @') + post.user.username + ' ',
positiveText: '',
negativeText: '',
onPositiveClick: () => {
if (post.user.is_following) {
unfollowUser({
user_id: post.user.id,
}).then((_res) => {
window.$message.success('');
postFollowAction(post.user_id, false);
})
.catch((_err) => {});
} else {
followUser({
user_id: post.user.id,
}).then((_res) => {
window.$message.success('');
postFollowAction(post.user_id, true);
})
.catch((_err) => {});
}
},
});
dialog.success({
title: '',
content:
'' +
(post.user.is_following ? ' @' : ' @') +
post.user.username +
' ',
positiveText: '',
negativeText: '',
onPositiveClick: () => {
if (post.user.is_following) {
unfollowUser({
user_id: post.user.id,
})
.then((_res) => {
window.$message.success('');
postFollowAction(post.user_id, false);
})
.catch((_err) => {});
} else {
followUser({
user_id: post.user.id,
})
.then((_res) => {
window.$message.success('');
postFollowAction(post.user_id, true);
})
.catch((_err) => {});
}
},
});
};
function postFollowAction(userId:number, isFollowing: boolean) {
for (let index in list.value) {
if (list.value[index].user_id == userId) {
list.value[index].user.is_following = isFollowing;
}
function postFollowAction(userId: number, isFollowing: boolean) {
for (let index in list.value) {
if (list.value[index].user_id == userId) {
list.value[index].user.is_following = isFollowing;
}
}
}
const updateTitle = () => {
title.value = '广';
if (route.query && route.query.q) {
if (route.query.t && route.query.t === 'tag') {
title.value = '#' + decodeURIComponent(route.query.q as string);
} else {
title.value = ': ' + decodeURIComponent(route.query.q as string);
}
title.value = '广';
if (route.query && route.query.q) {
if (route.query.t && route.query.t === 'tag') {
title.value = '#' + decodeURIComponent(route.query.q as string);
} else {
title.value = ': ' + decodeURIComponent(route.query.q as string);
}
}
};
const showTrendsTag = computed(() => {
return store.state.userInfo.id > 0 && !store.state.profile.enableTrendsBar && store.state.desktopModelShow
})
return (
store.state.userInfo.id > 0 &&
!store.state.profile.enableTrendsBar &&
store.state.desktopModelShow
);
});
const showTrendsBar = computed(() => {
return store.state.profile.useFriendship && store.state.profile.enableTrendsBar && store.state.desktopModelShow && store.state.userInfo.id > 0;
return (
store.state.profile.useFriendship &&
store.state.profile.enableTrendsBar &&
store.state.desktopModelShow &&
store.state.userInfo.id > 0
);
});
const reset = () => {
loading.value = false;
noMore.value = false;
list.value = [];
page.value = 1;
totalPage.value = 0;
}
loading.value = false;
noMore.value = false;
list.value = [];
page.value = 1;
totalPage.value = 0;
};
const handleBarClick = (data: Item.SlideBarItem, index: number) => {
reset();
targetStyle.value = data.style
if (route.query.q) {
route.query.q = null;
updateTitle();
}
switch (data.style) {
reset();
targetStyle.value = data.style;
if (route.query.q) {
route.query.q = null;
updateTitle();
}
switch (data.style) {
case 1:
loadPosts("newest");
break;
loadPosts('newest');
break;
case 2:
loadPosts("hots");
break;
loadPosts('hots');
break;
case 3:
route.query.q=null
loadPosts("following");
break;
route.query.q = null;
loadPosts('following');
break;
case 21:
targetUsername.value = data.username;
loadUserPosts();
break;
targetUsername.value = data.username;
loadUserPosts();
break;
default:
break;
}
slideBarList.value[index].show = false;
break;
}
slideBarList.value[index].show = false;
};
const loadContacts = () => {
slideBarList.value = slideBarList.value.slice(0, 3)
if (!store.state.profile.useFriendship || !store.state.profile.enableTrendsBar || store.state.userInfo.id === 0) {
return
}
getIndexTrends({
page: 1,
page_size: 50,
}).then((res) => {
var i = 0;
const list = res.list || []
let barItems: Item.SlideBarItem[] = []
for (; i < list.length; i++) {
let item: Item.IndexTrendsItem = list[i];
barItems.push({
title: item.nickname,
style: 21,
username: item.username,
avatar: item.avatar,
show: item.is_fresh,
});
}
if (barItems.length > 0) {
slideBarList.value = slideBarList.value.concat(barItems);
}
slideBarList.value = slideBarList.value.slice(0, 3);
if (
!store.state.profile.useFriendship ||
!store.state.profile.enableTrendsBar ||
store.state.userInfo.id === 0
) {
return;
}
getIndexTrends({
page: 1,
page_size: 50,
})
.then((res) => {
var i = 0;
const list = res.list || [];
let barItems: Item.SlideBarItem[] = [];
for (; i < list.length; i++) {
let item: Item.IndexTrendsItem = list[i];
barItems.push({
title: item.nickname,
style: 21,
username: item.username,
avatar: item.avatar,
show: item.is_fresh,
});
}
if (barItems.length > 0) {
slideBarList.value = slideBarList.value.concat(barItems);
}
})
.catch((err) => {
console.log(err);
console.log(err);
});
};
const loadPosts = (style : "newest" | "hots" | "following" | "search") => {
loading.value = true;
getPosts({
query: route.query.q ? decodeURIComponent(route.query.q as string) : null,
type: route.query.t as string,
style: style,
page: page.value,
page_size: pageSize.value,
const loadPosts = (style: 'newest' | 'hots' | 'following' | 'search') => {
loading.value = true;
getPosts({
query: route.query.q ? decodeURIComponent(route.query.q as string) : null,
type: route.query.t as string,
style: style,
page: page.value,
page_size: pageSize.value,
})
.then((rsp) => {
loading.value = false;
if (rsp.list.length === 0) {
noMore.value = true;
}
if (page.value > 1) {
list.value = list.value.concat(rsp.list);
} else {
list.value = rsp.list;
window.scrollTo(0, 0);
}
totalPage.value = Math.ceil(rsp.pager.total_rows / pageSize.value);
})
.then((rsp) => {
loading.value = false;
if (rsp.list.length === 0) {
noMore.value = true
}
if (page.value > 1) {
list.value = list.value.concat(rsp.list);
} else {
list.value = rsp.list;
window.scrollTo(0, 0);
}
totalPage.value = Math.ceil(rsp.pager.total_rows / pageSize.value);
})
.catch((err) => {
loading.value = false;
if (page.value > 1) {
page.value--
}
});
.catch((err) => {
loading.value = false;
if (page.value > 1) {
page.value--;
}
});
};
const loadUserPosts = () => {
loading.value = true;
getUserPosts({
username: targetUsername.value,
style: "post",
page: page.value,
page_size: pageSize.value,
loading.value = true;
getUserPosts({
username: targetUsername.value,
style: 'post',
page: page.value,
page_size: pageSize.value,
})
.then((rsp) => {
loading.value = false;
if (rsp.list.length === 0) {
noMore.value = true;
}
if (page.value > 1) {
list.value = list.value.concat(rsp.list);
} else {
list.value = rsp.list || [];
window.scrollTo(0, 0);
}
totalPage.value = Math.ceil(rsp.pager.total_rows / pageSize.value);
})
.then((rsp) => {
loading.value = false;
if (rsp.list.length === 0) {
noMore.value = true
}
if (page.value > 1) {
list.value = list.value.concat(rsp.list);
} else {
list.value = rsp.list || [];
window.scrollTo(0, 0);
}
totalPage.value = Math.ceil(rsp.pager.total_rows / pageSize.value);
})
.catch((err) => {
list.value = [];
if (page.value > 1) {
page.value--;
}
loading.value = false;
});
.catch((err) => {
list.value = [];
if (page.value > 1) {
page.value--;
}
loading.value = false;
});
};
const onPostSuccess = (post: Item.PostProps) => {
// 暂时统统跳到详情页面,后续再精细化分场景优化
router.push({
name: 'post',
query: {
id: post.id,
},
});
// // 如果不在第一页,需要跳转到详情页面
// if (targetStyle.value != 1) {
// router.push({
// name: 'post',
// query: {
// id: post.id,
// },
// });
// return;
// }
// // 如果是在第一页,就地插入新推文到文章列表中
// let items = [];
// let length = list.value.length;
// if (length == pageSize.value) {
// length--;
// }
// var i = 0;
// for (; i < length; i++) {
// let item: Item.PostProps = list.value[i];
// if (!item.is_top) {
// break;
// }
// items.push(item);
// }
// items.push(post);
// for (; i < length; i++) {
// items.push(list.value[i]);
// }
// list.value = items;
// 暂时统统跳到详情页面,后续再精细化分场景优化
router.push({
name: 'post',
query: {
id: post.id,
},
});
// // 如果不在第一页,需要跳转到详情页面
// if (targetStyle.value != 1) {
// router.push({
// name: 'post',
// query: {
// id: post.id,
// },
// });
// return;
// }
// // 如果是在第一页,就地插入新推文到文章列表中
// let items = [];
// let length = list.value.length;
// if (length == pageSize.value) {
// length--;
// }
// var i = 0;
// for (; i < length; i++) {
// let item: Item.PostProps = list.value[i];
// if (!item.is_top) {
// break;
// }
// items.push(item);
// }
// items.push(post);
// for (; i < length; i++) {
// items.push(list.value[i]);
// }
// list.value = items;
};
const loadMorePosts = () => {
switch (targetStyle.value) {
switch (targetStyle.value) {
case 1:
loadPosts("newest");
break;
loadPosts('newest');
break;
case 2:
loadPosts("hots");
break;
loadPosts('hots');
break;
case 3:
loadPosts("following");
break;
loadPosts('following');
break;
case 21:
if (route.query.q) {
loadPosts("search");
} else {
loadUserPosts();
}
break;
if (route.query.q) {
loadPosts('search');
} else {
loadUserPosts();
}
break;
default:
break;
}
break;
}
};
const nextPage = () => {
if (page.value < totalPage.value || totalPage.value == 0) {
noMore.value = false;
page.value++;
loadMorePosts();
} else {
noMore.value = true;
}
if (page.value < totalPage.value || totalPage.value == 0) {
noMore.value = false;
page.value++;
loadMorePosts();
} else {
noMore.value = true;
}
};
onMounted(() => {
reset();
loadContacts()
loadPosts("newest");
reset();
loadContacts();
loadPosts('newest');
});
watch(
() => ({
path: route.path,
query: route.query,
refresh: store.state.refresh,
}),
(to, from) => {
updateTitle();
if (to.refresh !== from.refresh) {
reset();
setTimeout(() => {
loadContacts()
loadMorePosts();
}, 0);
return;
}
if (from.path !== '/post' && to.path === '/') {
reset();
setTimeout(() => {
loadContacts()
loadMorePosts();
}, 0);
}
() => ({
path: route.path,
query: route.query,
refresh: store.state.refresh,
}),
(to, from) => {
updateTitle();
if (to.refresh !== from.refresh) {
reset();
setTimeout(() => {
loadContacts();
loadMorePosts();
}, 0);
return;
}
if (from.path !== '/post' && to.path === '/') {
reset();
setTimeout(() => {
loadContacts();
loadMorePosts();
}, 0);
}
},
);
</script>
<style lang="less" scoped>

@ -65,20 +65,20 @@
<script setup lang="ts">
import { h, ref, onMounted, computed } from 'vue';
import type { Component } from 'vue'
import { NIcon, DropdownOption } from 'naive-ui'
import type { Component } from 'vue';
import { NIcon, DropdownOption } from 'naive-ui';
import { useStore } from 'vuex';
import { useRoute } from 'vue-router';
import InfiniteLoading from "v3-infinite-loading";
import InfiniteLoading from 'v3-infinite-loading';
import { getMessages, readAllMessage } from '@/api/user';
import {
LayersOutline as AllIcon,
AtOutline as SystemIcon,
PaperPlaneOutline as WhisperIcon,
PersonAddOutline as RequestingIcon,
ChatbubbleEllipsesOutline as UnreadIcon,
OptionsOutline as OptionsIcon,
} from '@vicons/ionicons5'
import {
LayersOutline as AllIcon,
AtOutline as SystemIcon,
PaperPlaneOutline as WhisperIcon,
PersonAddOutline as RequestingIcon,
ChatbubbleEllipsesOutline as UnreadIcon,
OptionsOutline as OptionsIcon,
} from '@vicons/ionicons5';
const store = useStore();
const route = useRoute();
@ -88,266 +88,272 @@ const page = ref(+(route.query.p as string) || 1);
const pageSize = ref(20);
const totalPage = ref(0);
const list = ref<Item.MessageProps[]>([]);
const messageStyle = ref<'' | '' | '' | '' | ''>('')
const messageStyleVal = ref<'all' | 'system' | 'whisper' | 'requesting' | 'unread'>('all')
const messageStyle = ref<
'' | '' | '' | '' | ''
>('');
const messageStyleVal = ref<
'all' | 'system' | 'whisper' | 'requesting' | 'unread'
>('all');
const showWhisper = ref(false);
const whisperReceiver = ref<Item.UserInfo>({
id: 0,
avatar: '',
username: '',
nickname: '',
is_admin: false,
is_friend: true,
is_following: false,
created_on: 0,
follows: 0,
followings: 0,
status: 1,
id: 0,
avatar: '',
username: '',
nickname: '',
is_admin: false,
is_friend: true,
is_following: false,
created_on: 0,
follows: 0,
followings: 0,
status: 1,
});
const reset = () => {
noMore.value = false;
page.value = 1;
totalPage.value = 0;
list.value = [];
}
noMore.value = false;
page.value = 1;
totalPage.value = 0;
list.value = [];
};
const renderIcon = (icon: Component) => {
return () => {
return h(NIcon, null, {
default: () => h(icon)
})
}
}
default: () => h(icon),
});
};
};
const options = computed(() => {
let opts: DropdownOption[];
switch (messageStyle.value) {
let opts: DropdownOption[];
switch (messageStyle.value) {
case '':
opts = [
{
label: '',
key: 'system',
icon: renderIcon(SystemIcon)
},
{
label: '',
key: 'whisper',
icon: renderIcon(WhisperIcon)
},
{
label: '',
key: 'requesting',
icon: renderIcon(RequestingIcon)
},
{
label: '',
key: 'unread',
icon: renderIcon(UnreadIcon)
}
]
break;
opts = [
{
label: '',
key: 'system',
icon: renderIcon(SystemIcon),
},
{
label: '',
key: 'whisper',
icon: renderIcon(WhisperIcon),
},
{
label: '',
key: 'requesting',
icon: renderIcon(RequestingIcon),
},
{
label: '',
key: 'unread',
icon: renderIcon(UnreadIcon),
},
];
break;
case '':
opts = [
{
label: '',
key: 'all',
icon: renderIcon(AllIcon)
},
{
label: '',
key: 'whisper',
icon: renderIcon(WhisperIcon)
},
{
label: '',
key: 'requesting',
icon: renderIcon(RequestingIcon)
},
{
label: '',
key: 'unread',
icon: renderIcon(UnreadIcon)
}
]
break;
opts = [
{
label: '',
key: 'all',
icon: renderIcon(AllIcon),
},
{
label: '',
key: 'whisper',
icon: renderIcon(WhisperIcon),
},
{
label: '',
key: 'requesting',
icon: renderIcon(RequestingIcon),
},
{
label: '',
key: 'unread',
icon: renderIcon(UnreadIcon),
},
];
break;
case '':
opts = [
{
label: '',
key: 'all',
icon: renderIcon(AllIcon)
},
{
label: '',
key: 'system',
icon: renderIcon(SystemIcon)
},
{
label: '',
key: 'requesting',
icon: renderIcon(RequestingIcon)
},
{
label: '',
key: 'unread',
icon: renderIcon(UnreadIcon)
}
]
break;
opts = [
{
label: '',
key: 'all',
icon: renderIcon(AllIcon),
},
{
label: '',
key: 'system',
icon: renderIcon(SystemIcon),
},
{
label: '',
key: 'requesting',
icon: renderIcon(RequestingIcon),
},
{
label: '',
key: 'unread',
icon: renderIcon(UnreadIcon),
},
];
break;
case '':
opts = [
{
label: '',
key: 'all',
icon: renderIcon(AllIcon)
},
{
label: '',
key: 'system',
icon: renderIcon(SystemIcon)
},
{
label: '',
key: 'whisper',
icon: renderIcon(WhisperIcon)
},
{
label: '',
key: 'unread',
icon: renderIcon(UnreadIcon)
}
]
break;
opts = [
{
label: '',
key: 'all',
icon: renderIcon(AllIcon),
},
{
label: '',
key: 'system',
icon: renderIcon(SystemIcon),
},
{
label: '',
key: 'whisper',
icon: renderIcon(WhisperIcon),
},
{
label: '',
key: 'unread',
icon: renderIcon(UnreadIcon),
},
];
break;
case '':
opts = [
{
label: '',
key: 'all',
icon: renderIcon(AllIcon)
},
{
label: '',
key: 'system',
icon: renderIcon(SystemIcon)
},
{
label: '',
key: 'whisper',
icon: renderIcon(WhisperIcon)
},
{
label: '',
key: 'requesting',
icon: renderIcon(RequestingIcon)
}
]
break;
opts = [
{
label: '',
key: 'all',
icon: renderIcon(AllIcon),
},
{
label: '',
key: 'system',
icon: renderIcon(SystemIcon),
},
{
label: '',
key: 'whisper',
icon: renderIcon(WhisperIcon),
},
{
label: '',
key: 'requesting',
icon: renderIcon(RequestingIcon),
},
];
break;
default:
opts = [];
break;
}
return opts;
opts = [];
break;
}
return opts;
});
const handleAction = (
item: 'all' | 'system' | 'whisper' | 'requesting' | 'unread'
item: 'all' | 'system' | 'whisper' | 'requesting' | 'unread',
) => {
switch (item) {
switch (item) {
case 'all':
messageStyle.value = '';
break;
messageStyle.value = '';
break;
case 'system':
messageStyle.value = '';
break;
messageStyle.value = '';
break;
case 'whisper':
messageStyle.value = '';
break;
messageStyle.value = '';
break;
case 'requesting':
messageStyle.value = '';
break;
messageStyle.value = '';
break;
case 'unread':
messageStyle.value = '';
break;
}
messageStyleVal.value = item
reset();
loadMessages();
messageStyle.value = '';
break;
}
messageStyleVal.value = item;
reset();
loadMessages();
};
const handleUnreadMessage = () => {
handleAction('unread')
}
handleAction('unread');
};
const handleReadAll = () => {
if (store.state.unreadMsgCount > 0 && list.value.length > 0) {
readAllMessage().then((_res) => {
if (messageStyleVal.value != "unread") {
for (let idx in list.value) {
list.value[idx].is_read = 1;
}
} else {
list.value = [];
}
store.commit("updateUnreadMsgCount", 0)
})
.catch((err) => {
console.log(err);
});
}
}
if (store.state.unreadMsgCount > 0 && list.value.length > 0) {
readAllMessage()
.then((_res) => {
if (messageStyleVal.value != 'unread') {
for (let idx in list.value) {
list.value[idx].is_read = 1;
}
} else {
list.value = [];
}
store.commit('updateUnreadMsgCount', 0);
})
.catch((err) => {
console.log(err);
});
}
};
const onSendWhisper = (user: Item.UserInfo) => {
whisperReceiver.value = user;
showWhisper.value = true;
const onSendWhisper = (user: Item.UserInfo) => {
whisperReceiver.value = user;
showWhisper.value = true;
};
const whisperSuccess = () => {
showWhisper.value = false;
showWhisper.value = false;
};
const reloadMessages = () => {
reset();
loadMessages();
reset();
loadMessages();
};
const loadMessages = () => {
loading.value = true;
getMessages({
style: messageStyleVal.value,
page: page.value,
page_size: pageSize.value,
}).then((res) => {
loading.value = false;
if (res.list.length === 0) {
noMore.value = true
}
if (page.value > 1) {
list.value = list.value.concat(res.list);
} else {
list.value = res.list;
window.scrollTo(0, 0);
}
totalPage.value = Math.ceil(res.pager.total_rows / pageSize.value);
loading.value = true;
getMessages({
style: messageStyleVal.value,
page: page.value,
page_size: pageSize.value,
})
.then((res) => {
loading.value = false;
if (res.list.length === 0) {
noMore.value = true;
}
if (page.value > 1) {
list.value = list.value.concat(res.list);
} else {
list.value = res.list;
window.scrollTo(0, 0);
}
totalPage.value = Math.ceil(res.pager.total_rows / pageSize.value);
})
.catch((_err) => {
loading.value = false;
if (page.value > 1) {
page.value--
}
loading.value = false;
if (page.value > 1) {
page.value--;
}
});
};
const nextPage = () => {
if (page.value < totalPage.value || totalPage.value == 0) {
noMore.value = false;
page.value++;
loadMessages();
} else {
noMore.value = true;
}
if (page.value < totalPage.value || totalPage.value == 0) {
noMore.value = false;
page.value++;
loadMessages();
} else {
noMore.value = true;
}
};
onMounted(() => {
loadMessages();
loadMessages();
});
</script>

@ -61,8 +61,8 @@
import { ref, watch, onMounted, computed } from 'vue';
import { useRoute } from 'vue-router';
import { getPost, getPostComments } from '@/api/post';
import InfiniteLoading from "v3-infinite-loading";
import "v3-infinite-loading/lib/style.css";
import InfiniteLoading from 'v3-infinite-loading';
import 'v3-infinite-loading/lib/style.css';
const route = useRoute();
const post = ref<Item.PostProps>({} as Item.PostProps);
@ -70,11 +70,11 @@ const loading = ref(false);
const commentLoading = ref(false);
const comments = ref<Item.CommentProps[]>([]);
const postId = computed(() => +(route.query.id as string));
const sortStrategy = ref<"default" | "hots" | "newest">('default');
const defaultCommentsSort = ref<boolean>(true)
const pageSize = 20
const sortStrategy = ref<'default' | 'hots' | 'newest'>('default');
const defaultCommentsSort = ref<boolean>(true);
const pageSize = 20;
let stateHandler = ({
let stateHandler = {
loading() {
//nothing
},
@ -87,207 +87,209 @@ let stateHandler = ({
error() {
// nothing
},
});
};
const commentTab = (tab: "default" | "hots" | "newest") => {
sortStrategy.value = tab;
if (tab === "default") {
defaultCommentsSort.value = true
}
loadComments(stateHandler);
const commentTab = (tab: 'default' | 'hots' | 'newest') => {
sortStrategy.value = tab;
if (tab === 'default') {
defaultCommentsSort.value = true;
}
loadComments(stateHandler);
};
const reloadPost = (post_id: number) => {
getPost({
id: post_id,
}).then((res) => {
post.value = res;
}).catch((_err) => {});
getPost({
id: post_id,
})
.then((res) => {
post.value = res;
})
.catch((_err) => {});
};
const loadPost = () => {
post.value = {
id: 0,
} as Item.PostProps;
loading.value = true;
getPost({
id: postId.value,
})
.then((res) => {
loading.value = false;
post.value = res;
post.value = {
id: 0,
} as Item.PostProps;
loading.value = true;
getPost({
id: postId.value,
})
.then((res) => {
loading.value = false;
post.value = res;
// 加载评论
loadComments(stateHandler);
})
.catch((err) => {
loading.value = false;
});
// 加载评论
loadComments(stateHandler);
})
.catch((err) => {
loading.value = false;
});
};
let defaultCommmentsPage = 1;
const defaultNoMore = ref<boolean>(false)
const defaultNoMore = ref<boolean>(false);
const defaultComments = ref<Item.CommentProps[]>([]);
const loadDefaultComments = ($state: any) => {
if (defaultNoMore.value) {
return
}
getPostComments({
id: post.value.id as number,
style: 'default',
page: defaultCommmentsPage,
page_size: pageSize,
})
if (defaultNoMore.value) {
return;
}
getPostComments({
id: post.value.id as number,
style: 'default',
page: defaultCommmentsPage,
page_size: pageSize,
})
.then((res) => {
if ($state !== null) {
stateHandler = $state
}
if (res.list.length < pageSize) {
defaultNoMore.value = true
if ($state !== null) {
stateHandler = $state;
}
if (res.list.length < pageSize) {
defaultNoMore.value = true;
} else {
defaultCommmentsPage++;
}
if (res.list.length > 0) {
if (defaultCommmentsPage === 1) {
defaultComments.value = res.list;
} else {
defaultCommmentsPage++
}
if (res.list.length > 0) {
if (defaultCommmentsPage === 1) {
defaultComments.value = res.list;
} else {
defaultComments.value.push(...res.list);
}
comments.value = defaultComments.value
defaultComments.value.push(...res.list);
}
stateHandler.loaded();
commentLoading.value = false;
comments.value = defaultComments.value;
}
stateHandler.loaded();
commentLoading.value = false;
})
.catch((err) => {
commentLoading.value = false;
stateHandler.error();
commentLoading.value = false;
stateHandler.error();
});
};
let hotsCommmentsPage = 1;
let hotsNoMore = ref<boolean>(false)
const hotsComments=ref<Item.CommentProps[]>([]);
let hotsNoMore = ref<boolean>(false);
const hotsComments = ref<Item.CommentProps[]>([]);
const loadHotsComments = ($state: any) => {
if (hotsNoMore.value) {
return
}
getPostComments({
id: post.value.id as number,
style: 'hots',
page: hotsCommmentsPage,
page_size: pageSize,
})
if (hotsNoMore.value) {
return;
}
getPostComments({
id: post.value.id as number,
style: 'hots',
page: hotsCommmentsPage,
page_size: pageSize,
})
.then((res) => {
if ($state !== null) {
stateHandler = $state
}
if (res.list.length < pageSize) {
hotsNoMore.value = true
if ($state !== null) {
stateHandler = $state;
}
if (res.list.length < pageSize) {
hotsNoMore.value = true;
} else {
hotsCommmentsPage++;
}
if (res.list.length > 0) {
if (hotsCommmentsPage === 1) {
hotsComments.value = res.list;
} else {
hotsCommmentsPage++
}
if (res.list.length > 0) {
if (hotsCommmentsPage === 1) {
hotsComments.value = res.list;
} else {
hotsComments.value.push(...res.list);
}
comments.value = hotsComments.value
hotsComments.value.push(...res.list);
}
stateHandler.loaded();
commentLoading.value = false;
comments.value = hotsComments.value;
}
stateHandler.loaded();
commentLoading.value = false;
})
.catch((err) => {
commentLoading.value = false;
stateHandler.error();
commentLoading.value = false;
stateHandler.error();
});
};
let newestCommmentsPage = 1;
let newestNoMore = ref<boolean>(false)
const newestComments=ref<Item.CommentProps[]>([]);
let newestNoMore = ref<boolean>(false);
const newestComments = ref<Item.CommentProps[]>([]);
const loadNewestComments = ($state: any) => {
if (newestNoMore.value) {
return
}
getPostComments({
id: post.value.id as number,
style: 'newest',
page: newestCommmentsPage,
page_size: pageSize,
})
if (newestNoMore.value) {
return;
}
getPostComments({
id: post.value.id as number,
style: 'newest',
page: newestCommmentsPage,
page_size: pageSize,
})
.then((res) => {
if ($state !== null) {
stateHandler = $state
}
if (res.list.length < pageSize) {
newestNoMore.value = true
if ($state !== null) {
stateHandler = $state;
}
if (res.list.length < pageSize) {
newestNoMore.value = true;
} else {
newestCommmentsPage++;
}
if (res.list.length > 0) {
if (newestCommmentsPage === 1) {
newestComments.value = res.list;
} else {
newestCommmentsPage++
}
if (res.list.length > 0) {
if (newestCommmentsPage === 1) {
newestComments.value = res.list;
} else {
newestComments.value.push(...res.list);
}
comments.value = newestComments.value
newestComments.value.push(...res.list);
}
stateHandler.loaded();
commentLoading.value = false;
comments.value = newestComments.value;
}
stateHandler.loaded();
commentLoading.value = false;
})
.catch((err) => {
commentLoading.value = false;
stateHandler.error();
commentLoading.value = false;
stateHandler.error();
});
};
const loadComments = ($state: any) => {
if (postId.value < 1) {
return
}
if (comments.value.length === 0) {
commentLoading.value = true;
}
if (sortStrategy.value === 'default') {
comments.value = defaultComments.value
loadDefaultComments($state)
} else if (sortStrategy.value === 'hots') {
comments.value = hotsComments.value
loadHotsComments($state)
} else {
comments.value = newestComments.value
loadNewestComments($state)
}
commentLoading.value = false;
if (postId.value < 1) {
return;
}
if (comments.value.length === 0) {
commentLoading.value = true;
}
if (sortStrategy.value === 'default') {
comments.value = defaultComments.value;
loadDefaultComments($state);
} else if (sortStrategy.value === 'hots') {
comments.value = hotsComments.value;
loadHotsComments($state);
} else {
comments.value = newestComments.value;
loadNewestComments($state);
}
commentLoading.value = false;
};
const reloadComments = () => {
// 这里需要做特殊处理,目前暴力处理,一切都重新加载
// TODO后续持续优化 这里有大bug
defaultCommmentsPage = 1;
defaultNoMore.value = false
defaultComments.value = []
hotsCommmentsPage = 1;
hotsNoMore.value = false
hotsComments.value = []
// 这里需要做特殊处理,目前暴力处理,一切都重新加载
// TODO后续持续优化 这里有大bug
defaultCommmentsPage = 1;
defaultNoMore.value = false;
defaultComments.value = [];
newestCommmentsPage = 1;
newestNoMore.value = false
newestComments.value = []
hotsCommmentsPage = 1;
hotsNoMore.value = false;
hotsComments.value = [];
loadComments(stateHandler)
}
newestCommmentsPage = 1;
newestNoMore.value = false;
newestComments.value = [];
loadComments(stateHandler);
};
onMounted(() => {
loadPost();
loadPost();
});
watch(postId, () => {
if (postId.value > 0 && route.name === 'post') {
loadPost();
}
if (postId.value > 0 && route.name === 'post') {
loadPost();
}
});
</script>

@ -213,10 +213,8 @@ import { useDialog, DropdownOption } from 'naive-ui';
import { getUserPosts, followUser, unfollowUser } from '@/api/user';
import { formatDate } from '@/utils/formatTime';
import { prettyQuoteNum } from '@/utils/count';
import InfiniteLoading from "v3-infinite-loading";
import {
SettingsOutline,
} from '@vicons/ionicons5';
import InfiniteLoading from 'v3-infinite-loading';
import { SettingsOutline } from '@vicons/ionicons5';
import { MoreHorizFilled } from '@vicons/material';
const store = useStore();
@ -231,11 +229,13 @@ const commentList = ref<Item.PostProps[]>([]);
const highlightList = ref<Item.PostProps[]>([]);
const mediaList = ref<Item.PostProps[]>([]);
const starList = ref<Item.PostProps[]>([]);
const pageType = ref<"post" | "comment" | "highlight" |"media" | "star">('post');
const pageType = ref<'post' | 'comment' | 'highlight' | 'media' | 'star'>(
'post',
);
const postPage = ref(+(route.query.p as string) || 1);
const commentPage = ref(1)
const highlightPage = ref(1)
const mediaPage = ref(1)
const commentPage = ref(1);
const highlightPage = ref(1);
const mediaPage = ref(1);
const starPage = ref(1);
const page = ref(+(route.query.p as string) || 1);
const pageSize = ref(20);
@ -247,375 +247,386 @@ const mediaTotalPage = ref(0);
const starTotalPage = ref(0);
const showWhisper = ref(false);
const whisperReceiver = ref<Item.UserInfo>({
id: 0,
avatar: '',
username: '',
nickname: '',
is_admin: false,
is_friend: true,
is_following: false,
created_on: 0,
follows: 0,
followings: 0,
status: 1,
id: 0,
avatar: '',
username: '',
nickname: '',
is_admin: false,
is_friend: true,
is_following: false,
created_on: 0,
follows: 0,
followings: 0,
status: 1,
});
const renderIcon = (icon: Component) => {
return () => {
return h(NIcon, null, {
default: () => h(icon)
})
}
default: () => h(icon),
});
};
};
const userOptions = computed(() => {
let options: DropdownOption[] = [{
label: '',
key: 'setting',
icon: renderIcon(SettingsOutline)
}];
return options;
let options: DropdownOption[] = [
{
label: '',
key: 'setting',
icon: renderIcon(SettingsOutline),
},
];
return options;
});
const handleUserAction = (
item: 'setting'
) => {
switch (item) {
case 'setting':
router.push({
name: 'setting',
query: {
t: (new Date().getTime())
},
});
break;
default:
break;
}
const handleUserAction = (item: 'setting') => {
switch (item) {
case 'setting':
router.push({
name: 'setting',
query: {
t: new Date().getTime(),
},
});
break;
default:
break;
}
};
const onSendWhisper = (user: Item.UserInfo) => {
whisperReceiver.value = user;
showWhisper.value = true;
const onSendWhisper = (user: Item.UserInfo) => {
whisperReceiver.value = user;
showWhisper.value = true;
};
const whisperSuccess = () => {
showWhisper.value = false;
showWhisper.value = false;
};
const onHandleFollowAction = (post: Item.PostProps) => {
dialog.success({
title: '',
content:
'' + (post.user.is_following ? ' @' : ' @') + post.user.username + ' ',
positiveText: '',
negativeText: '',
onPositiveClick: () => {
if (post.user.is_following) {
unfollowUser({
user_id: post.user.id,
}).then((_res) => {
window.$message.success('');
postFollowAction(post.user_id, false);
})
.catch((_err) => {});
} else {
followUser({
user_id: post.user.id,
}).then((_res) => {
window.$message.success('');
postFollowAction(post.user_id, true);
})
.catch((_err) => {});
}
},
});
dialog.success({
title: '',
content:
'' +
(post.user.is_following ? ' @' : ' @') +
post.user.username +
' ',
positiveText: '',
negativeText: '',
onPositiveClick: () => {
if (post.user.is_following) {
unfollowUser({
user_id: post.user.id,
})
.then((_res) => {
window.$message.success('');
postFollowAction(post.user_id, false);
})
.catch((_err) => {});
} else {
followUser({
user_id: post.user.id,
})
.then((_res) => {
window.$message.success('');
postFollowAction(post.user_id, true);
})
.catch((_err) => {});
}
},
});
};
function postFollowAction(userId: number, isFollowing: boolean) {
updateFolloing(postList.value, userId, isFollowing);
updateFolloing(commentList.value, userId, isFollowing);
updateFolloing(highlightList.value, userId, isFollowing);
updateFolloing(mediaList.value, userId, isFollowing);
updateFolloing(starList.value, userId, isFollowing);
updateFolloing(postList.value, userId, isFollowing);
updateFolloing(commentList.value, userId, isFollowing);
updateFolloing(highlightList.value, userId, isFollowing);
updateFolloing(mediaList.value, userId, isFollowing);
updateFolloing(starList.value, userId, isFollowing);
}
function updateFolloing(posts: Item.PostProps[], userId: number, isFollowing: boolean) {
if (posts && posts.length > 0) {
for (let index in posts) {
if (posts[index].user_id == userId) {
posts[index].user.is_following = isFollowing;
}
}
function updateFolloing(
posts: Item.PostProps[],
userId: number,
isFollowing: boolean,
) {
if (posts && posts.length > 0) {
for (let index in posts) {
if (posts[index].user_id == userId) {
posts[index].user.is_following = isFollowing;
}
}
}
}
const loadPage = () => {
switch(pageType.value) {
case "post":
loadPosts();
break;
case "comment":
loadCommentPosts();
break;
case "highlight":
loadHighlightPosts();
break;
case "media":
loadMediaPosts();
break;
case "star":
loadStarPosts();
break;
}
switch (pageType.value) {
case 'post':
loadPosts();
break;
case 'comment':
loadCommentPosts();
break;
case 'highlight':
loadHighlightPosts();
break;
case 'media':
loadMediaPosts();
break;
case 'star':
loadStarPosts();
break;
}
};
const loadPosts = () => {
loading.value = true;
getUserPosts({
username: store.state.userInfo.username,
style: "post",
page: page.value,
page_size: pageSize.value,
loading.value = true;
getUserPosts({
username: store.state.userInfo.username,
style: 'post',
page: page.value,
page_size: pageSize.value,
})
.then((rsp) => {
loading.value = false;
if (rsp.list.length === 0) {
noMore.value = true;
}
if (page.value > 1) {
list.value = list.value.concat(rsp.list);
} else {
list.value = rsp.list || [];
window.scrollTo(0, 0);
}
totalPage.value = Math.ceil(rsp.pager.total_rows / pageSize.value);
postList.value = list.value;
postTotalPage.value = totalPage.value;
})
.then((rsp) => {
loading.value = false;
if (rsp.list.length === 0) {
noMore.value = true
}
if (page.value > 1) {
list.value = list.value.concat(rsp.list);
} else {
list.value = rsp.list || [];
window.scrollTo(0, 0);
}
totalPage.value = Math.ceil(rsp.pager.total_rows / pageSize.value);
postList.value = list.value;
postTotalPage.value = totalPage.value;
})
.catch((err) => {
list.value = [];
if (page.value > 1) {
page.value--;
}
loading.value = false;
});
.catch((err) => {
list.value = [];
if (page.value > 1) {
page.value--;
}
loading.value = false;
});
};
const loadCommentPosts = () => {
loading.value = true;
getUserPosts({
username: store.state.userInfo.username,
style: "comment",
page: page.value,
page_size: pageSize.value,
loading.value = true;
getUserPosts({
username: store.state.userInfo.username,
style: 'comment',
page: page.value,
page_size: pageSize.value,
})
.then((rsp) => {
loading.value = false;
if (rsp.list.length === 0) {
noMore.value = true;
}
if (page.value > 1) {
list.value = list.value.concat(rsp.list);
} else {
list.value = rsp.list || [];
window.scrollTo(0, 0);
}
totalPage.value = Math.ceil(rsp.pager.total_rows / pageSize.value);
commentList.value = list.value;
commentTotalPage.value = totalPage.value;
})
.then((rsp) => {
loading.value = false;
if (rsp.list.length === 0) {
noMore.value = true
}
if (page.value > 1) {
list.value = list.value.concat(rsp.list);
} else {
list.value = rsp.list || [];
window.scrollTo(0, 0);
}
totalPage.value = Math.ceil(rsp.pager.total_rows / pageSize.value);
commentList.value = list.value;
commentTotalPage.value = totalPage.value;
})
.catch((err) => {
list.value = [];
if (page.value > 1) {
page.value--;
}
loading.value = false;
});
.catch((err) => {
list.value = [];
if (page.value > 1) {
page.value--;
}
loading.value = false;
});
};
const loadHighlightPosts = () => {
loading.value = true;
getUserPosts({
username: store.state.userInfo.username,
style: "highlight",
page: page.value,
page_size: pageSize.value,
loading.value = true;
getUserPosts({
username: store.state.userInfo.username,
style: 'highlight',
page: page.value,
page_size: pageSize.value,
})
.then((rsp) => {
loading.value = false;
if (rsp.list.length === 0) {
noMore.value = true;
}
if (page.value > 1) {
list.value = list.value.concat(rsp.list);
} else {
list.value = rsp.list || [];
window.scrollTo(0, 0);
}
totalPage.value = Math.ceil(rsp.pager.total_rows / pageSize.value);
highlightList.value = list.value;
highlightTotalPage.value = totalPage.value;
})
.then((rsp) => {
loading.value = false;
if (rsp.list.length === 0) {
noMore.value = true
}
if (page.value > 1) {
list.value = list.value.concat(rsp.list);
} else {
list.value = rsp.list || [];
window.scrollTo(0, 0);
}
totalPage.value = Math.ceil(rsp.pager.total_rows / pageSize.value);
highlightList.value = list.value;
highlightTotalPage.value = totalPage.value;
})
.catch((err) => {
list.value = [];
if (page.value > 1) {
page.value--;
}
loading.value = false;
});
.catch((err) => {
list.value = [];
if (page.value > 1) {
page.value--;
}
loading.value = false;
});
};
const loadMediaPosts = () => {
loading.value = true;
getUserPosts({
username: store.state.userInfo.username,
style: "media",
page: page.value,
page_size: pageSize.value,
loading.value = true;
getUserPosts({
username: store.state.userInfo.username,
style: 'media',
page: page.value,
page_size: pageSize.value,
})
.then((rsp) => {
loading.value = false;
if (rsp.list.length === 0) {
noMore.value = true;
}
if (page.value > 1) {
list.value = list.value.concat(rsp.list);
} else {
list.value = rsp.list || [];
window.scrollTo(0, 0);
}
totalPage.value = Math.ceil(rsp.pager.total_rows / pageSize.value);
mediaList.value = list.value;
mediaTotalPage.value = totalPage.value;
})
.then((rsp) => {
loading.value = false;
if (rsp.list.length === 0) {
noMore.value = true
}
if (page.value > 1) {
list.value = list.value.concat(rsp.list);
} else {
list.value = rsp.list || [];
window.scrollTo(0, 0);
}
totalPage.value = Math.ceil(rsp.pager.total_rows / pageSize.value);
mediaList.value = list.value;
mediaTotalPage.value = totalPage.value;
})
.catch((err) => {
list.value = [];
if (page.value > 1) {
page.value--;
}
loading.value = false;
});
.catch((err) => {
list.value = [];
if (page.value > 1) {
page.value--;
}
loading.value = false;
});
};
const loadStarPosts = () => {
loading.value = true;
getUserPosts({
username: store.state.userInfo.username,
style: "star",
page: page.value,
page_size: pageSize.value,
loading.value = true;
getUserPosts({
username: store.state.userInfo.username,
style: 'star',
page: page.value,
page_size: pageSize.value,
})
.then((rsp) => {
loading.value = false;
if (rsp.list.length === 0) {
noMore.value = true;
}
if (page.value > 1) {
list.value = list.value.concat(rsp.list);
} else {
list.value = rsp.list || [];
window.scrollTo(0, 0);
}
totalPage.value = Math.ceil(rsp.pager.total_rows / pageSize.value);
starList.value = list.value;
starTotalPage.value = totalPage.value;
})
.then((rsp) => {
loading.value = false;
if (rsp.list.length === 0) {
noMore.value = true
}
if (page.value > 1) {
list.value = list.value.concat(rsp.list);
} else {
list.value = rsp.list || [];
window.scrollTo(0, 0);
}
totalPage.value = Math.ceil(rsp.pager.total_rows / pageSize.value);
starList.value = list.value;
starTotalPage.value = totalPage.value;
})
.catch((err) => {
list.value = [];
if (page.value > 1) {
page.value--;
}
loading.value = false;
});
.catch((err) => {
list.value = [];
if (page.value > 1) {
page.value--;
}
loading.value = false;
});
};
const changeTab = (tab: "post" | "comment" | "highlight" | "media" | "star") => {
pageType.value = tab;
switch(pageType.value) {
case "post":
list.value = postList.value;
page.value = postPage.value;
totalPage.value = postTotalPage.value;
loadPosts();
break;
case "comment":
list.value = commentList.value;
page.value = commentPage.value;
totalPage.value = commentTotalPage.value;
loadCommentPosts();
break;
case "highlight":
list.value = highlightList.value;
page.value = highlightPage.value;
totalPage.value = highlightTotalPage.value;
loadHighlightPosts();
break;
case "media":
list.value = mediaList.value;
page.value = mediaPage.value;
totalPage.value = mediaTotalPage.value;
loadMediaPosts();
break;
case "star":
list.value = starList.value;
page.value = starPage.value;
totalPage.value = starTotalPage.value;
loadStarPosts();
break;
}
const changeTab = (
tab: 'post' | 'comment' | 'highlight' | 'media' | 'star',
) => {
pageType.value = tab;
switch (pageType.value) {
case 'post':
list.value = postList.value;
page.value = postPage.value;
totalPage.value = postTotalPage.value;
loadPosts();
break;
case 'comment':
list.value = commentList.value;
page.value = commentPage.value;
totalPage.value = commentTotalPage.value;
loadCommentPosts();
break;
case 'highlight':
list.value = highlightList.value;
page.value = highlightPage.value;
totalPage.value = highlightTotalPage.value;
loadHighlightPosts();
break;
case 'media':
list.value = mediaList.value;
page.value = mediaPage.value;
totalPage.value = mediaTotalPage.value;
loadMediaPosts();
break;
case 'star':
list.value = starList.value;
page.value = starPage.value;
totalPage.value = starTotalPage.value;
loadStarPosts();
break;
}
};
const updatePage = () => {
switch(pageType.value) {
case "post":
postPage.value = page.value;
loadPosts();
break;
case "comment":
commentPage.value = page.value;
loadCommentPosts();
break;
case "highlight":
highlightPage.value = page.value;
loadHighlightPosts();
break;
case "media":
mediaPage.value = page.value;
loadMediaPosts();
break;
case "star":
starPage.value = page.value;
loadStarPosts();
break;
}
switch (pageType.value) {
case 'post':
postPage.value = page.value;
loadPosts();
break;
case 'comment':
commentPage.value = page.value;
loadCommentPosts();
break;
case 'highlight':
highlightPage.value = page.value;
loadHighlightPosts();
break;
case 'media':
mediaPage.value = page.value;
loadMediaPosts();
break;
case 'star':
starPage.value = page.value;
loadStarPosts();
break;
}
};
const nextPage = () => {
if (page.value < totalPage.value || totalPage.value == 0) {
noMore.value = false;
page.value++;
updatePage();
} else {
noMore.value = true;
}
if (page.value < totalPage.value || totalPage.value == 0) {
noMore.value = false;
page.value++;
updatePage();
} else {
noMore.value = true;
}
};
onMounted(() => {
loadPage();
loadPage();
});
watch(
() => ({
path: route.path,
query: route.query,
refresh: store.state.refresh,
}),
(to, from) => {
if (to.refresh !== from.refresh) {
page.value = +(route.query.p as string) || 1;
setTimeout(() => {
loadPage();
}, 0);
return;
}
if (from.path !== '/post' && to.path === '/profile') {
page.value = +(route.query.p as string) || 1;
setTimeout(() => {
loadPage();
}, 0);
}
() => ({
path: route.path,
query: route.query,
refresh: store.state.refresh,
}),
(to, from) => {
if (to.refresh !== from.refresh) {
page.value = +(route.query.p as string) || 1;
setTimeout(() => {
loadPage();
}, 0);
return;
}
if (from.path !== '/post' && to.path === '/profile') {
page.value = +(route.query.p as string) || 1;
setTimeout(() => {
loadPage();
}, 0);
}
},
);
</script>

@ -344,30 +344,31 @@ import { onMounted, ref, reactive } from 'vue';
import { useStore } from 'vuex';
import { Edit } from '@vicons/tabler';
import {
getCaptcha,
sendCaptcha,
bindUserPhone,
activateUser,
changePassword,
changeNickname,
changeAvatar,
getCaptcha,
sendCaptcha,
bindUserPhone,
activateUser,
changePassword,
changeNickname,
changeAvatar,
} from '@/api/user';
import type {
UploadInst,
FormItemRule,
FormItemInst,
FormInst,
InputInst,
UploadInst,
FormItemRule,
FormItemInst,
FormInst,
InputInst,
} from 'naive-ui';
const uploadGateway = import.meta.env.VITE_HOST + '/v1/attachment';
const uploadToken = 'Bearer ' + localStorage.getItem('PAOPAO_TOKEN');
const uploadType = ref('public/avatar');
const allowActivation = (import.meta.env.VITE_ALLOW_ACTIVATION.toLowerCase() === 'true')
const allowActivation =
import.meta.env.VITE_ALLOW_ACTIVATION.toLowerCase() === 'true';
const store = useStore();
const sending = ref(false);
const binding = ref(false);
const activating = ref(false)
const activating = ref(false);
const avatarRef = ref<UploadInst>();
const inputInstRef = ref<InputInst>();
const showNicknameEdit = ref(false);
@ -382,331 +383,331 @@ const activateFormRef = ref<FormInst>();
const formRef = ref<FormInst>();
const rPasswordFormItemRef = ref<FormItemInst>();
const modelData = reactive({
id: '',
b64s: '',
imgCaptcha: '',
phone: '',
phone_captcha: '',
password: '',
old_password: '',
reenteredPassword: '',
id: '',
b64s: '',
imgCaptcha: '',
phone: '',
phone_captcha: '',
password: '',
old_password: '',
reenteredPassword: '',
});
const activateData = reactive({
id: '',
b64s: '',
imgCaptcha: '',
activate_code: '',
id: '',
b64s: '',
imgCaptcha: '',
activate_code: '',
});
const beforeUpload = async (data: any) => {
// 图片类型校验
if (
uploadType.value === 'public/avatar' &&
!['image/png', 'image/jpg', 'image/jpeg'].includes(data.file.file?.type)
) {
window.$message.warning(' png/jpg ');
return false;
}
if (uploadType.value === 'image' && data.file.file?.size > 1048576) {
window.$message.warning('1MB');
return false;
}
return true;
// 图片类型校验
if (
uploadType.value === 'public/avatar' &&
!['image/png', 'image/jpg', 'image/jpeg'].includes(data.file.file?.type)
) {
window.$message.warning(' png/jpg ');
return false;
}
if (uploadType.value === 'image' && data.file.file?.size > 1048576) {
window.$message.warning('1MB');
return false;
}
return true;
};
const finishUpload = ({ file, event }: any): any => {
try {
let data = JSON.parse(event.target?.response);
if (data.code === 0) {
if (uploadType.value === 'public/avatar') {
changeAvatar({
avatar: data.data.content,
})
.then((res) => {
window.$message.success('');
avatarRef.value?.clear();
store.commit('updateUserinfo', {
...store.state.userInfo,
avatar: data.data.content,
});
})
.catch((err) => {
console.log(err);
});
}
}
} catch (error) {
window.$message.error('');
try {
let data = JSON.parse(event.target?.response);
if (data.code === 0) {
if (uploadType.value === 'public/avatar') {
changeAvatar({
avatar: data.data.content,
})
.then((res) => {
window.$message.success('');
avatarRef.value?.clear();
store.commit('updateUserinfo', {
...store.state.userInfo,
avatar: data.data.content,
});
})
.catch((err) => {
console.log(err);
});
}
}
} catch (error) {
window.$message.error('');
}
};
const validatePasswordStartWith = (rule: FormItemRule, value: any) => {
return (
!!modelData.password &&
(modelData.password as any).startsWith(value) &&
(modelData.password as any).length >= value.length
);
return (
!!modelData.password &&
(modelData.password as any).startsWith(value) &&
(modelData.password as any).length >= value.length
);
};
const validatePasswordSame = (rule: FormItemRule, value: any) => {
return value === modelData.password;
return value === modelData.password;
};
const handlePasswordInput = () => {
if (modelData.reenteredPassword) {
rPasswordFormItemRef.value?.validate({ trigger: 'password-input' });
}
if (modelData.reenteredPassword) {
rPasswordFormItemRef.value?.validate({ trigger: 'password-input' });
}
};
const handleValidateButtonClick = (e: MouseEvent) => {
e.preventDefault();
formRef.value?.validate((errors) => {
if (!errors) {
passwordSetting.value = true;
changePassword({
password: modelData.password,
old_password: modelData.old_password,
})
.then((res) => {
passwordSetting.value = false;
showPasswordSetting.value = false;
window.$message.success('');
// 用户退出登录
store.commit('userLogout');
store.commit('triggerAuth', true);
store.commit('triggerAuthKey', 'signin');
})
.catch((err) => {
passwordSetting.value = false;
});
}
});
e.preventDefault();
formRef.value?.validate((errors) => {
if (!errors) {
passwordSetting.value = true;
changePassword({
password: modelData.password,
old_password: modelData.old_password,
})
.then((res) => {
passwordSetting.value = false;
showPasswordSetting.value = false;
window.$message.success('');
// 用户退出登录
store.commit('userLogout');
store.commit('triggerAuth', true);
store.commit('triggerAuthKey', 'signin');
})
.catch((err) => {
passwordSetting.value = false;
});
}
});
};
const handlePhoneBind = (e: MouseEvent) => {
e.preventDefault();
phoneFormRef.value?.validate((errors) => {
if (!errors) {
binding.value = true;
bindUserPhone({
phone: modelData.phone,
captcha: modelData.phone_captcha,
})
.then((res) => {
binding.value = false;
showPhoneBind.value = false;
window.$message.success('');
store.commit('updateUserinfo', {
...store.state.userInfo,
phone: modelData.phone,
});
modelData.id = '';
modelData.b64s = '';
modelData.imgCaptcha = '';
modelData.phone = '';
modelData.phone_captcha = '';
})
.catch((err) => {
binding.value = false;
});
}
});
e.preventDefault();
phoneFormRef.value?.validate((errors) => {
if (!errors) {
binding.value = true;
bindUserPhone({
phone: modelData.phone,
captcha: modelData.phone_captcha,
})
.then((res) => {
binding.value = false;
showPhoneBind.value = false;
window.$message.success('');
store.commit('updateUserinfo', {
...store.state.userInfo,
phone: modelData.phone,
});
modelData.id = '';
modelData.b64s = '';
modelData.imgCaptcha = '';
modelData.phone = '';
modelData.phone_captcha = '';
})
.catch((err) => {
binding.value = false;
});
}
});
};
const handleActivation = (e: MouseEvent) => {
e.preventDefault();
activateFormRef.value?.validate((errors) => {
e.preventDefault();
activateFormRef.value?.validate((errors) => {
if (activateData.imgCaptcha === '') {
window.$message.warning('');
return;
window.$message.warning('');
return;
}
sending.value = true;
if (!errors) {
activating.value = true;
activateUser({
activate_code: activateData.activate_code,
captcha_id: activateData.id,
imgCaptcha: activateData.imgCaptcha
})
.then((res) => {
activating.value = false;
showActivation.value = false;
window.$message.success('');
store.commit('updateUserinfo', {
...store.state.userInfo,
activation: activateData.activate_code,
});
activateData.id = '';
activateData.b64s = '';
activateData.imgCaptcha = '';
activateData.activate_code = '';
})
.catch((err) => {
activating.value = false;
if (err.code === 20012) {
loadCaptcha4Activate();
}
});
}
});
};
const loadCaptcha = () => {
getCaptcha()
if (!errors) {
activating.value = true;
activateUser({
activate_code: activateData.activate_code,
captcha_id: activateData.id,
imgCaptcha: activateData.imgCaptcha,
})
.then((res) => {
modelData.id = res.id;
modelData.b64s = res.b64s;
activating.value = false;
showActivation.value = false;
window.$message.success('');
store.commit('updateUserinfo', {
...store.state.userInfo,
activation: activateData.activate_code,
});
activateData.id = '';
activateData.b64s = '';
activateData.imgCaptcha = '';
activateData.activate_code = '';
})
.catch((err) => {
console.log(err);
activating.value = false;
if (err.code === 20012) {
loadCaptcha4Activate();
}
});
}
});
};
const loadCaptcha = () => {
getCaptcha()
.then((res) => {
modelData.id = res.id;
modelData.b64s = res.b64s;
})
.catch((err) => {
console.log(err);
});
};
const loadCaptcha4Activate = () => {
getCaptcha()
.then((res) => {
activateData.id = res.id;
activateData.b64s = res.b64s;
})
.catch((err) => {
console.log(err);
});
getCaptcha()
.then((res) => {
activateData.id = res.id;
activateData.b64s = res.b64s;
})
.catch((err) => {
console.log(err);
});
};
const handleNicknameChange = () => {
changeNickname({
nickname: store.state.userInfo.nickname || '',
changeNickname({
nickname: store.state.userInfo.nickname || '',
})
.then((res) => {
showNicknameEdit.value = false;
window.$message.success('');
})
.then((res) => {
showNicknameEdit.value = false;
window.$message.success('');
})
.catch((err) => {
showNicknameEdit.value = true;
});
.catch((err) => {
showNicknameEdit.value = true;
});
};
const sendPhoneCaptcha = () => {
if (smsCounter.value > 0 && smsDisabled.value) {
return;
}
if (modelData.imgCaptcha === '') {
window.$message.warning('');
return;
}
sending.value = true;
sendCaptcha({
phone: modelData.phone,
img_captcha: modelData.imgCaptcha,
img_captcha_id: modelData.id,
if (smsCounter.value > 0 && smsDisabled.value) {
return;
}
if (modelData.imgCaptcha === '') {
window.$message.warning('');
return;
}
sending.value = true;
sendCaptcha({
phone: modelData.phone,
img_captcha: modelData.imgCaptcha,
img_captcha_id: modelData.id,
})
.then((res) => {
smsDisabled.value = true;
sending.value = false;
window.$message.success('');
let s = setInterval(() => {
smsCounter.value--;
if (smsCounter.value === 0) {
clearInterval(s);
smsCounter.value = 60;
smsDisabled.value = false;
}
}, 1000);
})
.then((res) => {
smsDisabled.value = true;
sending.value = false;
window.$message.success('');
let s = setInterval(() => {
smsCounter.value--;
if (smsCounter.value === 0) {
clearInterval(s);
smsCounter.value = 60;
smsDisabled.value = false;
}
}, 1000);
})
.catch((err) => {
sending.value = false;
if (err.code === 20012) {
loadCaptcha();
}
console.log(err);
});
.catch((err) => {
sending.value = false;
if (err.code === 20012) {
loadCaptcha();
}
console.log(err);
});
};
const bindRules = {
phone: [
{
required: true,
message: '',
trigger: ['input'],
validator: (rule: FormItemRule, value: any) => {
return /^[1]+[3-9]{1}\d{9}$/.test(value);
},
},
],
phone_captcha: [
{
required: true,
message: '',
},
],
phone: [
{
required: true,
message: '',
trigger: ['input'],
validator: (rule: FormItemRule, value: any) => {
return /^[1]+[3-9]{1}\d{9}$/.test(value);
},
},
],
phone_captcha: [
{
required: true,
message: '',
},
],
};
const activateRules = {
activate_code: [
{
required: true,
message: '',
trigger: ['input'],
validator: (rule: FormItemRule, value: any) => {
return /\d{6}$/.test(value);
},
},
],
activate_code: [
{
required: true,
message: '',
trigger: ['input'],
validator: (rule: FormItemRule, value: any) => {
return /\d{6}$/.test(value);
},
},
],
};
const passwordRules = {
password: [
{
required: true,
message: '',
},
],
old_password: [
{
required: true,
message: '',
},
],
reenteredPassword: [
{
required: true,
message: '',
trigger: ['input', 'blur'],
},
{
validator: validatePasswordStartWith,
message: '',
trigger: 'input',
},
{
validator: validatePasswordSame,
message: '',
trigger: ['blur', 'password-input'],
},
],
password: [
{
required: true,
message: '',
},
],
old_password: [
{
required: true,
message: '',
},
],
reenteredPassword: [
{
required: true,
message: '',
trigger: ['input', 'blur'],
},
{
validator: validatePasswordStartWith,
message: '',
trigger: 'input',
},
{
validator: validatePasswordSame,
message: '',
trigger: ['blur', 'password-input'],
},
],
};
const handleNicknameShow = () => {
showNicknameEdit.value = true;
setTimeout(() => {
inputInstRef.value?.focus();
}, 30);
showNicknameEdit.value = true;
setTimeout(() => {
inputInstRef.value?.focus();
}, 30);
};
onMounted(() => {
if (store.state.userInfo.id === 0) {
store.commit('triggerAuth', true);
store.commit('triggerAuthKey', 'signin');
}
loadCaptcha();
loadCaptcha4Activate();
if (store.state.userInfo.id === 0) {
store.commit('triggerAuth', true);
store.commit('triggerAuthKey', 'signin');
}
loadCaptcha();
loadCaptcha4Activate();
});
</script>

@ -34,60 +34,60 @@
</template>
<script setup lang="ts">
import { ref, onMounted, computed, watch} from 'vue';
import { ref, onMounted, computed, watch } from 'vue';
import { getTags } from '@/api/post';
import { useStore } from 'vuex';
const store = useStore();
const tags = ref<Item.TagProps[]>([]);
const tagType = ref<"hot" | "new" | "follow" | "pin">('hot');
const tagType = ref<'hot' | 'new' | 'follow' | 'pin'>('hot');
const loading = ref(false);
const tagsChecked = ref(false)
const inFollowTab = ref(false)
const inPinTab = ref(false)
const tagsChecked = ref(false);
const inFollowTab = ref(false);
const inPinTab = ref(false);
watch(tagsChecked, () => {
if (!tagsChecked.value) {
window.$message.success("保存成功");
store.commit("refreshTopicFollow")
}
if (!tagsChecked.value) {
window.$message.success('');
store.commit('refreshTopicFollow');
}
});
const tagsEditText = computed({
get: () => {
let text = "编辑";
if (tagsChecked.value) {
text = "保存";
}
return text;
},
set: (newVal) => {
// do nothing
},
const tagsEditText = computed({
get: () => {
let text = '';
if (tagsChecked.value) {
text = '';
}
return text;
},
set: (newVal) => {
// do nothing
},
});
const loadTags = () => {
loading.value = true;
getTags({
type: tagType.value,
num: 50,
loading.value = true;
getTags({
type: tagType.value,
num: 50,
})
.then((res) => {
tags.value = res.topics;
loading.value = false;
})
.then((res) => {
tags.value = res.topics;
loading.value = false;
})
.catch((err) => {
tags.value = [];
console.log(err);
loading.value = false;
});
.catch((err) => {
tags.value = [];
console.log(err);
loading.value = false;
});
};
const changeTab = (tab: "hot" | "new" | "follow" | "pin") => {
tagType.value = tab;
inFollowTab.value = (tab === "follow")
inPinTab.value = (tab === "pin")
loadTags();
const changeTab = (tab: 'hot' | 'new' | 'follow' | 'pin') => {
tagType.value = tab;
inFollowTab.value = tab === 'follow';
inPinTab.value = tab === 'pin';
loadTags();
};
onMounted(() => {
loadTags();
loadTags();
});
</script>

File diff suppressed because it is too large Load Diff

@ -170,100 +170,96 @@ const totalPage = ref(0);
const openAmounts = ref([100, 200, 300, 500, 1000, 3000, 5000, 10000, 50000]);
const loadPosts = () => {
loading.value = true;
getBills({
page: page.value,
page_size: pageSize.value,
})
.then((rsp) => {
loading.value = false;
list.value = rsp.list;
totalPage.value = Math.ceil(rsp.pager.total_rows / pageSize.value);
loading.value = true;
getBills({
page: page.value,
page_size: pageSize.value,
})
.then((rsp) => {
loading.value = false;
list.value = rsp.list;
totalPage.value = Math.ceil(rsp.pager.total_rows / pageSize.value);
window.scrollTo(0, 0);
})
.catch((err) => {
loading.value = false;
});
window.scrollTo(0, 0);
})
.catch((err) => {
loading.value = false;
});
};
const updatePage = (p: number) => {
page.value = p;
loadPosts();
page.value = p;
loadPosts();
};
const loadWallet = () => {
// 获取最新
const token = localStorage.getItem('PAOPAO_TOKEN') || '';
if (token) {
userInfo(token)
.then((res) => {
store.commit('updateUserinfo', res);
store.commit('triggerAuth', false);
loadPosts();
})
.catch((err) => {
store.commit('triggerAuth', true);
store.commit('userLogout');
});
} else {
// 获取最新
const token = localStorage.getItem('PAOPAO_TOKEN') || '';
if (token) {
userInfo(token)
.then((res) => {
store.commit('updateUserinfo', res);
store.commit('triggerAuth', false);
loadPosts();
})
.catch((err) => {
store.commit('triggerAuth', true);
store.commit('userLogout');
}
});
} else {
store.commit('triggerAuth', true);
store.commit('userLogout');
}
};
const doRecharge = () => {
showRecharge.value = true;
showRecharge.value = true;
};
const handleRecharge = (amount: any) => {
recharging.value = true;
reqRecharge({
amount: selectedRechargeAmount.value,
})
.then((res) => {
recharging.value = false;
rechargeQrcode.value = res.pay;
recharging.value = true;
reqRecharge({
amount: selectedRechargeAmount.value,
})
.then((res) => {
recharging.value = false;
rechargeQrcode.value = res.pay;
// 生成二维码
QRCode.toCanvas(
document.querySelector('#qrcode-container'),
res.pay,
{
width: 150,
margin: 2,
}
);
// 生成二维码
QRCode.toCanvas(document.querySelector('#qrcode-container'), res.pay, {
width: 150,
margin: 2,
});
const s = setInterval(() => {
getRecharge({
id: res.id,
})
.then((res) => {
if (res.status === 'TRADE_SUCCESS') {
clearInterval(s);
window.$message.success('');
const s = setInterval(() => {
getRecharge({
id: res.id,
})
.then((res) => {
if (res.status === 'TRADE_SUCCESS') {
clearInterval(s);
window.$message.success('');
showRecharge.value = false;
rechargeQrcode.value = '';
showRecharge.value = false;
rechargeQrcode.value = '';
loadWallet();
}
})
.catch((err) => {
console.log(err);
});
}, 2000);
})
.catch((err) => {
recharging.value = false;
});
loadWallet();
}
})
.catch((err) => {
console.log(err);
});
}, 2000);
})
.catch((err) => {
recharging.value = false;
});
};
const doWithdraw = () => {
if (store.state.userInfo.balance == 0) {
window.$message.warning('');
} else {
window.$message.warning('');
}
if (store.state.userInfo.balance == 0) {
window.$message.warning('');
} else {
window.$message.warning('');
}
};
onMounted(() => {
loadWallet();
loadWallet();
});
</script>

22
web/src/vuex.d.ts vendored

@ -1,14 +1,14 @@
import { ComponentCustomProperties } from 'vue'
import { Store } from 'vuex'
import { ComponentCustomProperties } from 'vue';
import { Store } from 'vuex';
declare module '@vue/runtime-core' {
// 声明自己的 store state
interface State {
[key: string]: any
}
// 声明自己的 store state
interface State {
[key: string]: any;
}
// 为 `this.$store` 提供类型声明
interface ComponentCustomProperties {
$store: Store<State>
}
}
// 为 `this.$store` 提供类型声明
interface ComponentCustomProperties {
$store: Store<State>;
}
}

@ -1,21 +1,18 @@
{
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"moduleResolution": "node",
"strict": true,
"jsx": "preserve",
"sourceMap": true,
"resolveJsonModule": true,
"esModuleInterop": true,
"lib": ["esnext", "dom"],
"types": [
"node"
],
"paths": {
"@/*": ["./src/*"]
}
},
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"]
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"moduleResolution": "node",
"strict": true,
"jsx": "preserve",
"sourceMap": true,
"resolveJsonModule": true,
"esModuleInterop": true,
"lib": ["esnext", "dom"],
"types": ["node"],
"paths": {
"@/*": ["./src/*"]
}
},
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"]
}

@ -1,14 +1,14 @@
import { defineConfig, type PluginOption } from "vite";
import path from "path";
import vue from "@vitejs/plugin-vue";
import Components from "unplugin-vue-components/vite";
import { defineConfig, type PluginOption } from 'vite';
import path from 'path';
import vue from '@vitejs/plugin-vue';
import Components from 'unplugin-vue-components/vite';
import { visualizer } from "rollup-plugin-visualizer";
import { NaiveUiResolver } from "unplugin-vue-components/resolvers";
import { visualizer } from 'rollup-plugin-visualizer';
import { NaiveUiResolver } from 'unplugin-vue-components/resolvers';
// https://vitejs.dev/config/
export default defineConfig({
server: {
host: "0.0.0.0",
host: '0.0.0.0',
},
plugins: [
vue({
@ -21,7 +21,7 @@ export default defineConfig({
],
resolve: {
alias: {
"@": path.resolve(__dirname, "src"),
'@': path.resolve(__dirname, 'src'),
},
},
build: {
@ -29,11 +29,11 @@ export default defineConfig({
rollupOptions: {
output: {
manualChunks(id) {
if (id.includes("node_modules")) {
if (id.includes('node_modules')) {
return id
.toString()
.split("node_modules/")[1]
.split("/")[0]
.split('node_modules/')[1]
.split('/')[0]
.toString();
}
},

Loading…
Cancel
Save