diff --git a/backend/src/main/java/com/openisle/controller/AuthController.java b/backend/src/main/java/com/openisle/controller/AuthController.java index bad3abcfe..d4a7a07da 100644 --- a/backend/src/main/java/com/openisle/controller/AuthController.java +++ b/backend/src/main/java/com/openisle/controller/AuthController.java @@ -47,13 +47,14 @@ public class AuthController { return ResponseEntity.badRequest().body(Map.of("error", "Invalid captcha")); } if (req.getInviteToken() != null && !req.getInviteToken().isEmpty()) { - if (!inviteService.validate(req.getInviteToken())) { + InviteService.InviteValidateResult result = inviteService.validate(req.getInviteToken()); + if (!result.isValidate()) { return ResponseEntity.badRequest().body(Map.of("error", "邀请码使用次数过多")); } try { User user = userService.registerWithInvite( req.getUsername(), req.getEmail(), req.getPassword()); - inviteService.consume(req.getInviteToken()); + inviteService.consume(req.getInviteToken(), user.getUsername()); emailService.sendEmail(user.getEmail(), "在网站填写验证码以验证", "您的验证码是 " + user.getVerificationCode()); return ResponseEntity.ok(Map.of( "token", jwtService.generateToken(user.getUsername()), @@ -144,7 +145,8 @@ public class AuthController { @PostMapping("/google") public ResponseEntity loginWithGoogle(@RequestBody GoogleLoginRequest req) { boolean viaInvite = req.getInviteToken() != null && !req.getInviteToken().isEmpty(); - if (viaInvite && !inviteService.validate(req.getInviteToken())) { + InviteService.InviteValidateResult inviteValidateResult = inviteService.validate(req.getInviteToken()); + if (viaInvite && !inviteValidateResult.isValidate()) { return ResponseEntity.badRequest().body(Map.of("error", "Invalid invite token")); } Optional resultOpt = googleAuthService.authenticate( @@ -154,7 +156,7 @@ public class AuthController { if (resultOpt.isPresent()) { AuthResult result = resultOpt.get(); if (viaInvite && result.isNewUser()) { - inviteService.consume(req.getInviteToken()); + inviteService.consume(req.getInviteToken(), inviteValidateResult.getInviteToken().getInviter().getUsername()); return ResponseEntity.ok(Map.of( "token", jwtService.generateToken(result.getUser().getUsername()), "reason_code", "INVITE_APPROVED" @@ -218,7 +220,8 @@ public class AuthController { @PostMapping("/github") public ResponseEntity loginWithGithub(@RequestBody GithubLoginRequest req) { boolean viaInvite = req.getInviteToken() != null && !req.getInviteToken().isEmpty(); - if (viaInvite && !inviteService.validate(req.getInviteToken())) { + InviteService.InviteValidateResult inviteValidateResult = inviteService.validate(req.getInviteToken()); + if (viaInvite && !inviteValidateResult.isValidate()) { return ResponseEntity.badRequest().body(Map.of("error", "Invalid invite token")); } Optional resultOpt = githubAuthService.authenticate( @@ -229,7 +232,7 @@ public class AuthController { if (resultOpt.isPresent()) { AuthResult result = resultOpt.get(); if (viaInvite && result.isNewUser()) { - inviteService.consume(req.getInviteToken()); + inviteService.consume(req.getInviteToken(), inviteValidateResult.getInviteToken().getInviter().getUsername()); return ResponseEntity.ok(Map.of( "token", jwtService.generateToken(result.getUser().getUsername()), "reason_code", "INVITE_APPROVED" @@ -265,7 +268,8 @@ public class AuthController { @PostMapping("/discord") public ResponseEntity loginWithDiscord(@RequestBody DiscordLoginRequest req) { boolean viaInvite = req.getInviteToken() != null && !req.getInviteToken().isEmpty(); - if (viaInvite && !inviteService.validate(req.getInviteToken())) { + InviteService.InviteValidateResult inviteValidateResult = inviteService.validate(req.getInviteToken()); + if (viaInvite && !inviteValidateResult.isValidate()) { return ResponseEntity.badRequest().body(Map.of("error", "Invalid invite token")); } Optional resultOpt = discordAuthService.authenticate( @@ -276,7 +280,7 @@ public class AuthController { if (resultOpt.isPresent()) { AuthResult result = resultOpt.get(); if (viaInvite && result.isNewUser()) { - inviteService.consume(req.getInviteToken()); + inviteService.consume(req.getInviteToken(), inviteValidateResult.getInviteToken().getInviter().getUsername()); return ResponseEntity.ok(Map.of( "token", jwtService.generateToken(result.getUser().getUsername()), "reason_code", "INVITE_APPROVED" @@ -311,7 +315,8 @@ public class AuthController { @PostMapping("/twitter") public ResponseEntity loginWithTwitter(@RequestBody TwitterLoginRequest req) { boolean viaInvite = req.getInviteToken() != null && !req.getInviteToken().isEmpty(); - if (viaInvite && !inviteService.validate(req.getInviteToken())) { + InviteService.InviteValidateResult inviteValidateResult = inviteService.validate(req.getInviteToken()); + if (viaInvite && !inviteValidateResult.isValidate()) { return ResponseEntity.badRequest().body(Map.of("error", "Invalid invite token")); } Optional resultOpt = twitterAuthService.authenticate( @@ -323,7 +328,7 @@ public class AuthController { if (resultOpt.isPresent()) { AuthResult result = resultOpt.get(); if (viaInvite && result.isNewUser()) { - inviteService.consume(req.getInviteToken()); + inviteService.consume(req.getInviteToken(), inviteValidateResult.getInviteToken().getInviter().getUsername()); return ResponseEntity.ok(Map.of( "token", jwtService.generateToken(result.getUser().getUsername()), "reason_code", "INVITE_APPROVED" diff --git a/backend/src/main/java/com/openisle/controller/CommentController.java b/backend/src/main/java/com/openisle/controller/CommentController.java index 09e998607..77b3b0b16 100644 --- a/backend/src/main/java/com/openisle/controller/CommentController.java +++ b/backend/src/main/java/com/openisle/controller/CommentController.java @@ -47,7 +47,7 @@ public class CommentController { Comment comment = commentService.addComment(auth.getName(), postId, req.getContent()); CommentDto dto = commentMapper.toDto(comment); dto.setReward(levelService.awardForComment(auth.getName())); - dto.setPointReward(pointService.awardForComment(auth.getName(),postId)); + dto.setPointReward(pointService.awardForComment(auth.getName(), postId, comment.getId())); log.debug("createComment succeeded for comment {}", comment.getId()); return ResponseEntity.ok(dto); } diff --git a/backend/src/main/java/com/openisle/controller/PointHistoryController.java b/backend/src/main/java/com/openisle/controller/PointHistoryController.java new file mode 100644 index 000000000..a547d309a --- /dev/null +++ b/backend/src/main/java/com/openisle/controller/PointHistoryController.java @@ -0,0 +1,28 @@ +package com.openisle.controller; + +import com.openisle.dto.PointHistoryDto; +import com.openisle.mapper.PointHistoryMapper; +import com.openisle.service.PointService; +import lombok.RequiredArgsConstructor; +import org.springframework.security.core.Authentication; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; +import java.util.stream.Collectors; + +@RestController +@RequestMapping("/api/point-histories") +@RequiredArgsConstructor +public class PointHistoryController { + private final PointService pointService; + private final PointHistoryMapper pointHistoryMapper; + + @GetMapping + public List list(Authentication auth) { + return pointService.listHistory(auth.getName()).stream() + .map(pointHistoryMapper::toDto) + .collect(Collectors.toList()); + } +} diff --git a/backend/src/main/java/com/openisle/controller/PostController.java b/backend/src/main/java/com/openisle/controller/PostController.java index 3dd4662e0..86d8fd9d1 100644 --- a/backend/src/main/java/com/openisle/controller/PostController.java +++ b/backend/src/main/java/com/openisle/controller/PostController.java @@ -45,7 +45,7 @@ public class PostController { draftService.deleteDraft(auth.getName()); PostDetailDto dto = postMapper.toDetailDto(post, auth.getName()); dto.setReward(levelService.awardForPost(auth.getName())); - dto.setPointReward(pointService.awardForPost(auth.getName())); + dto.setPointReward(pointService.awardForPost(auth.getName(), post.getId())); return ResponseEntity.ok(dto); } diff --git a/backend/src/main/java/com/openisle/dto/PointHistoryDto.java b/backend/src/main/java/com/openisle/dto/PointHistoryDto.java new file mode 100644 index 000000000..cae0b6f6b --- /dev/null +++ b/backend/src/main/java/com/openisle/dto/PointHistoryDto.java @@ -0,0 +1,23 @@ +package com.openisle.dto; + +import com.openisle.model.PointHistoryType; +import lombok.Getter; +import lombok.Setter; + +import java.time.LocalDateTime; + +@Getter +@Setter +public class PointHistoryDto { + private Long id; + private PointHistoryType type; + private int amount; + private int balance; + private Long postId; + private String postTitle; + private Long commentId; + private String commentContent; + private Long fromUserId; + private String fromUserName; + private LocalDateTime createdAt; +} diff --git a/backend/src/main/java/com/openisle/mapper/PointHistoryMapper.java b/backend/src/main/java/com/openisle/mapper/PointHistoryMapper.java new file mode 100644 index 000000000..9a3881d5a --- /dev/null +++ b/backend/src/main/java/com/openisle/mapper/PointHistoryMapper.java @@ -0,0 +1,34 @@ +package com.openisle.mapper; + +import com.openisle.dto.PointHistoryDto; +import com.openisle.model.PointHistory; +import org.springframework.stereotype.Component; + +@Component +public class PointHistoryMapper { + public PointHistoryDto toDto(PointHistory history) { + PointHistoryDto dto = new PointHistoryDto(); + dto.setId(history.getId()); + dto.setType(history.getType()); + dto.setAmount(history.getAmount()); + dto.setBalance(history.getBalance()); + dto.setCreatedAt(history.getCreatedAt()); + if (history.getPost() != null) { + dto.setPostId(history.getPost().getId()); + dto.setPostTitle(history.getPost().getTitle()); + } + if (history.getComment() != null) { + dto.setCommentId(history.getComment().getId()); + dto.setCommentContent(history.getComment().getContent()); + if (history.getComment().getPost() != null && dto.getPostId() == null) { + dto.setPostId(history.getComment().getPost().getId()); + dto.setPostTitle(history.getComment().getPost().getTitle()); + } + } + if (history.getFromUser() != null) { + dto.setFromUserId(history.getFromUser().getId()); + dto.setFromUserName(history.getFromUser().getUsername()); + } + return dto; + } +} diff --git a/backend/src/main/java/com/openisle/model/PointHistory.java b/backend/src/main/java/com/openisle/model/PointHistory.java new file mode 100644 index 000000000..347d4c75a --- /dev/null +++ b/backend/src/main/java/com/openisle/model/PointHistory.java @@ -0,0 +1,49 @@ +package com.openisle.model; + +import jakarta.persistence.*; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.time.LocalDateTime; + +/** Point change history for a user. */ +@Entity +@Getter +@Setter +@NoArgsConstructor +@Table(name = "point_histories") +public class PointHistory { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ManyToOne(fetch = FetchType.LAZY, optional = false) + @JoinColumn(name = "user_id") + private User user; + + @Enumerated(EnumType.STRING) + @Column(nullable = false) + private PointHistoryType type; + + @Column(nullable = false) + private int amount; + + @Column(nullable = false) + private int balance; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "post_id") + private Post post; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "comment_id") + private Comment comment; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "from_user_id") + private User fromUser; + + @Column(name = "created_at", nullable = false) + private LocalDateTime createdAt; +} diff --git a/backend/src/main/java/com/openisle/model/PointHistoryType.java b/backend/src/main/java/com/openisle/model/PointHistoryType.java new file mode 100644 index 000000000..ceda76185 --- /dev/null +++ b/backend/src/main/java/com/openisle/model/PointHistoryType.java @@ -0,0 +1,10 @@ +package com.openisle.model; + +public enum PointHistoryType { + POST, + COMMENT, + POST_LIKED, + COMMENT_LIKED, + INVITE, + SYSTEM_ONLINE +} diff --git a/backend/src/main/java/com/openisle/repository/PointHistoryRepository.java b/backend/src/main/java/com/openisle/repository/PointHistoryRepository.java new file mode 100644 index 000000000..ac1ee7096 --- /dev/null +++ b/backend/src/main/java/com/openisle/repository/PointHistoryRepository.java @@ -0,0 +1,12 @@ +package com.openisle.repository; + +import com.openisle.model.PointHistory; +import com.openisle.model.User; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.List; + +public interface PointHistoryRepository extends JpaRepository { + List findByUserOrderByIdDesc(User user); + long countByUser(User user); +} diff --git a/backend/src/main/java/com/openisle/service/InviteService.java b/backend/src/main/java/com/openisle/service/InviteService.java index cd0f895a3..23ca58bd1 100644 --- a/backend/src/main/java/com/openisle/service/InviteService.java +++ b/backend/src/main/java/com/openisle/service/InviteService.java @@ -5,6 +5,7 @@ import com.openisle.model.User; import com.openisle.repository.InviteTokenRepository; import com.openisle.repository.UserRepository; import lombok.RequiredArgsConstructor; +import lombok.Value; import org.springframework.stereotype.Service; import java.time.LocalDate; @@ -18,6 +19,12 @@ public class InviteService { private final JwtService jwtService; private final PointService pointService; + @Value + public class InviteValidateResult { + InviteToken inviteToken; + boolean validate; + } + public String generate(String username) { User inviter = userRepository.findByUsername(username).orElseThrow(); LocalDate today = LocalDate.now(); @@ -35,20 +42,23 @@ public class InviteService { return token; } - public boolean validate(String token) { + public InviteValidateResult validate(String token) { + if (token == null || token.isEmpty()) { + return new InviteValidateResult(null, false); + } try { jwtService.validateAndGetSubjectForInvite(token); } catch (Exception e) { - return false; + return new InviteValidateResult(null, false); } InviteToken invite = inviteTokenRepository.findById(token).orElse(null); - return invite != null && invite.getUsageCount() < 3; + return new InviteValidateResult(invite, invite != null && invite.getUsageCount() < 3); } - public void consume(String token) { + public void consume(String token, String newUserName) { InviteToken invite = inviteTokenRepository.findById(token).orElseThrow(); invite.setUsageCount(invite.getUsageCount() + 1); inviteTokenRepository.save(invite); - pointService.awardForInvite(invite.getInviter().getUsername()); + pointService.awardForInvite(invite.getInviter().getUsername(), newUserName); } } diff --git a/backend/src/main/java/com/openisle/service/PointService.java b/backend/src/main/java/com/openisle/service/PointService.java index be46b1fc6..69a543389 100644 --- a/backend/src/main/java/com/openisle/service/PointService.java +++ b/backend/src/main/java/com/openisle/service/PointService.java @@ -1,7 +1,6 @@ package com.openisle.service; -import com.openisle.model.PointLog; -import com.openisle.model.User; +import com.openisle.model.*; import com.openisle.repository.*; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -16,19 +15,22 @@ public class PointService { private final PointLogRepository pointLogRepository; private final PostRepository postRepository; private final CommentRepository commentRepository; + private final PointHistoryRepository pointHistoryRepository; - public int awardForPost(String userName) { + public int awardForPost(String userName, Long postId) { User user = userRepository.findByUsername(userName).orElseThrow(); PointLog log = getTodayLog(user); if (log.getPostCount() > 1) return 0; log.setPostCount(log.getPostCount() + 1); pointLogRepository.save(log); - return addPoint(user, 30); + Post post = postRepository.findById(postId).orElseThrow(); + return addPoint(user, 30, PointHistoryType.POST, post, null, null); } - public int awardForInvite(String userName) { + public int awardForInvite(String userName, String inviteeName) { User user = userRepository.findByUsername(userName).orElseThrow(); - return addPoint(user, 500); + User invitee = userRepository.findByUsername(inviteeName).orElseThrow(); + return addPoint(user, 500, PointHistoryType.INVITE, null, null, invitee); } private PointLog getTodayLog(User user) { @@ -45,20 +47,41 @@ public class PointService { }); } - private int addPoint(User user, int amount) { + private int addPoint(User user, int amount, PointHistoryType type, + Post post, Comment comment, User fromUser) { + if (pointHistoryRepository.countByUser(user) == 0) { + recordHistory(user, PointHistoryType.SYSTEM_ONLINE, 0, null, null, null); + } user.setPoint(user.getPoint() + amount); userRepository.save(user); + recordHistory(user, type, amount, post, comment, fromUser); return amount; } + private void recordHistory(User user, PointHistoryType type, int amount, + Post post, Comment comment, User fromUser) { + PointHistory history = new PointHistory(); + history.setUser(user); + history.setType(type); + history.setAmount(amount); + history.setBalance(user.getPoint()); + history.setPost(post); + history.setComment(comment); + history.setFromUser(fromUser); + history.setCreatedAt(java.time.LocalDateTime.now()); + pointHistoryRepository.save(history); + } + // 同时为评论者和发帖人增加积分,返回值为评论者增加的积分数 // 注意需要考虑发帖和回复是同一人的场景 - public int awardForComment(String commenterName, Long postId) { + public int awardForComment(String commenterName, Long postId, Long commentId) { // 标记评论者是否已达到积分奖励上限 boolean isTheRewardCapped = false; // 根据帖子id找到发帖人 - User poster = postRepository.findById(postId).orElseThrow().getAuthor(); + Post post = postRepository.findById(postId).orElseThrow(); + User poster = post.getAuthor(); + Comment comment = commentRepository.findById(commentId).orElseThrow(); // 获取评论者的加分日志 User commenter = userRepository.findByUsername(commenterName).orElseThrow(); @@ -74,15 +97,15 @@ public class PointService { } else { log.setCommentCount(log.getCommentCount() + 1); pointLogRepository.save(log); - return addPoint(commenter, 10); + return addPoint(commenter, 10, PointHistoryType.COMMENT, post, comment, null); } } else { - addPoint(poster, 10); + addPoint(poster, 10, PointHistoryType.COMMENT, post, comment, commenter); // 如果发帖人与评论者不是同一个,则根据是否达到积分上限来判断评论者加分情况 if (isTheRewardCapped) { return 0; } else { - return addPoint(commenter, 10); + return addPoint(commenter, 10, PointHistoryType.COMMENT, post, comment, null); } } } @@ -101,7 +124,8 @@ public class PointService { } // 如果不是同一个,则为发帖人加分 - return addPoint(poster, 10); + Post post = postRepository.findById(postId).orElseThrow(); + return addPoint(poster, 10, PointHistoryType.POST_LIKED, post, null, reactioner); } // 考虑点赞者和评论者是同一个的情况 @@ -118,7 +142,17 @@ public class PointService { } // 如果不是同一个,则为发帖人加分 - return addPoint(commenter, 10); + Comment comment = commentRepository.findById(commentId).orElseThrow(); + Post post = comment.getPost(); + return addPoint(commenter, 10, PointHistoryType.COMMENT_LIKED, post, comment, reactioner); + } + + public java.util.List listHistory(String userName) { + User user = userRepository.findByUsername(userName).orElseThrow(); + if (pointHistoryRepository.countByUser(user) == 0) { + recordHistory(user, PointHistoryType.SYSTEM_ONLINE, 0, null, null, null); + } + return pointHistoryRepository.findByUserOrderByIdDesc(user); } } diff --git a/frontend_nuxt/components/BaseSwitch.vue b/frontend_nuxt/components/BaseSwitch.vue new file mode 100644 index 000000000..f04197c92 --- /dev/null +++ b/frontend_nuxt/components/BaseSwitch.vue @@ -0,0 +1,65 @@ + + + + + diff --git a/frontend_nuxt/components/GlobalPopups.vue b/frontend_nuxt/components/GlobalPopups.vue index 3c31e8d9c..90a0f00c7 100644 --- a/frontend_nuxt/components/GlobalPopups.vue +++ b/frontend_nuxt/components/GlobalPopups.vue @@ -50,7 +50,7 @@ onMounted(async () => { }) const checkMilkTeaActivity = async () => { - if (!process.client) return + if (!import.meta.client) return if (localStorage.getItem('milkTeaActivityPopupShown')) return try { const res = await fetch(`${API_BASE_URL}/api/activities`) @@ -68,7 +68,7 @@ const checkMilkTeaActivity = async () => { } const checkInviteCodeActivity = async () => { - if (!process.client) return + if (!import.meta.client) return if (localStorage.getItem('inviteCodeActivityPopupShown')) return try { const res = await fetch(`${API_BASE_URL}/api/activities`) @@ -86,30 +86,30 @@ const checkInviteCodeActivity = async () => { } const closeInviteCodePopup = () => { - if (!process.client) return + if (!import.meta.client) return localStorage.setItem('inviteCodeActivityPopupShown', 'true') showInviteCodePopup.value = false } const closeMilkTeaPopup = () => { - if (!process.client) return + if (!import.meta.client) return localStorage.setItem('milkTeaActivityPopupShown', 'true') showMilkTeaPopup.value = false } const checkNotificationSetting = async () => { - if (!process.client) return + if (!import.meta.client) return if (!authState.loggedIn) return if (localStorage.getItem('notificationSettingPopupShown')) return showNotificationPopup.value = true } const closeNotificationPopup = () => { - if (!process.client) return + if (!import.meta.client) return localStorage.setItem('notificationSettingPopupShown', 'true') showNotificationPopup.value = false } const checkNewMedals = async () => { - if (!process.client) return + if (!import.meta.client) return if (!authState.loggedIn || !authState.userId) return try { const res = await fetch(`${API_BASE_URL}/api/medals?userId=${authState.userId}`) @@ -127,7 +127,7 @@ const checkNewMedals = async () => { } } const closeMedalPopup = () => { - if (!process.client) return + if (!import.meta.client) return const seen = new Set(JSON.parse(localStorage.getItem('seenMedals') || '[]')) newMedals.value.forEach((m) => seen.add(m.type)) localStorage.setItem('seenMedals', JSON.stringify([...seen])) diff --git a/frontend_nuxt/components/InfiniteLoadMore.vue b/frontend_nuxt/components/InfiniteLoadMore.vue index 441c0ecf4..c4e1a9e0a 100644 --- a/frontend_nuxt/components/InfiniteLoadMore.vue +++ b/frontend_nuxt/components/InfiniteLoadMore.vue @@ -40,7 +40,7 @@ const stopObserver = () => { } const startObserver = () => { - if (!process.client || props.pause || done.value) return + if (!import.meta.client || props.pause || done.value) return stopObserver() io = new IntersectionObserver( async (entries) => { diff --git a/frontend_nuxt/components/TagSelect.vue b/frontend_nuxt/components/TagSelect.vue index b80f506a2..14de024a7 100644 --- a/frontend_nuxt/components/TagSelect.vue +++ b/frontend_nuxt/components/TagSelect.vue @@ -63,7 +63,7 @@ const isImageIcon = (icon) => { } const buildTagsUrl = (kw = '') => { - const base = API_BASE_URL || (process.client ? window.location.origin : '') + const base = API_BASE_URL || (import.meta.client ? window.location.origin : '') const url = new URL('/api/tags', base) if (kw) url.searchParams.set('keyword', kw) diff --git a/frontend_nuxt/composables/useToast.js b/frontend_nuxt/composables/useToast.js index 900aa1f74..e8daff063 100644 --- a/frontend_nuxt/composables/useToast.js +++ b/frontend_nuxt/composables/useToast.js @@ -1,7 +1,7 @@ // 导出一个便捷的 toast 对象 export const toast = { success: async (message) => { - if (process.client) { + if (import.meta.client) { try { const { useToast } = await import('vue-toastification') const toastInstance = useToast() @@ -12,7 +12,7 @@ export const toast = { } }, error: async (message) => { - if (process.client) { + if (import.meta.client) { try { const { useToast } = await import('vue-toastification') const toastInstance = useToast() @@ -23,7 +23,7 @@ export const toast = { } }, warning: async (message) => { - if (process.client) { + if (import.meta.client) { try { const { useToast } = await import('vue-toastification') const toastInstance = useToast() @@ -34,7 +34,7 @@ export const toast = { } }, info: async (message) => { - if (process.client) { + if (import.meta.client) { try { const { useToast } = await import('vue-toastification') const toastInstance = useToast() @@ -48,7 +48,7 @@ export const toast = { // 导出 useToast composable export const useToast = () => { - if (process.client) { + if (import.meta.client) { return new Promise(async (resolve) => { try { const { useToast: useVueToast } = await import('vue-toastification') diff --git a/frontend_nuxt/pages/about/stats.vue b/frontend_nuxt/pages/about/stats.vue index 74d02898b..7b881d67d 100644 --- a/frontend_nuxt/pages/about/stats.vue +++ b/frontend_nuxt/pages/about/stats.vue @@ -1,5 +1,8 @@