mirror of
https://github.com/nagisa77/OpenIsle.git
synced 2026-03-01 17:41:03 +08:00
Compare commits
19 Commits
codex/add-
...
codex/add-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c58c14f9b7 | ||
|
|
990d7cfbf9 | ||
|
|
eb860a74af | ||
|
|
b3d050b42e | ||
|
|
db678a95c6 | ||
|
|
6d66cb48dc | ||
|
|
1fe2994743 | ||
|
|
126b10ce45 | ||
|
|
3b1843b6dd | ||
|
|
6a5d00f086 | ||
|
|
06368a6cf1 | ||
|
|
c38e4bc44c | ||
|
|
e9f25d3b1a | ||
|
|
fe167aa0b9 | ||
|
|
f3421265d2 | ||
|
|
f4817cd6d1 | ||
|
|
5ae0f9311c | ||
|
|
567452f570 | ||
|
|
bb4e866bd0 |
@@ -0,0 +1,36 @@
|
|||||||
|
package com.openisle.config;
|
||||||
|
|
||||||
|
import com.openisle.model.Role;
|
||||||
|
import com.openisle.model.User;
|
||||||
|
import com.openisle.repository.UserRepository;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.boot.CommandLineRunner;
|
||||||
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensure a dedicated "system" user exists for internal operations.
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class SystemUserInitializer implements CommandLineRunner {
|
||||||
|
private final UserRepository userRepository;
|
||||||
|
private final PasswordEncoder passwordEncoder;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run(String... args) {
|
||||||
|
userRepository.findByUsername("system").orElseGet(() -> {
|
||||||
|
User system = new User();
|
||||||
|
system.setUsername("system");
|
||||||
|
system.setEmail("system@openisle.local");
|
||||||
|
// todo(tim): raw password 采用环境变量
|
||||||
|
system.setPassword(passwordEncoder.encode("system"));
|
||||||
|
system.setRole(Role.USER);
|
||||||
|
system.setVerified(true);
|
||||||
|
system.setApproved(true);
|
||||||
|
system.setAvatar("https://openisle-1307107697.cos.ap-guangzhou.myqcloud.com/assert/image.png");
|
||||||
|
return userRepository.save(system);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -5,22 +5,24 @@ import lombok.Getter;
|
|||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
public class PostChangeLogDto {
|
public class PostChangeLogDto {
|
||||||
private Long id;
|
private Long id;
|
||||||
private String username;
|
private String username;
|
||||||
|
private String userAvatar;
|
||||||
private PostChangeType type;
|
private PostChangeType type;
|
||||||
private LocalDateTime time;
|
private LocalDateTime time;
|
||||||
private String oldTitle;
|
private String oldTitle;
|
||||||
private String newTitle;
|
private String newTitle;
|
||||||
private String oldContent;
|
private String oldContent;
|
||||||
private String newContent;
|
private String newContent;
|
||||||
private String oldCategory;
|
private CategoryDto oldCategory;
|
||||||
private String newCategory;
|
private CategoryDto newCategory;
|
||||||
private String oldTags;
|
private List<TagDto> oldTags;
|
||||||
private String newTags;
|
private List<TagDto> newTags;
|
||||||
private Boolean oldClosed;
|
private Boolean oldClosed;
|
||||||
private Boolean newClosed;
|
private Boolean newClosed;
|
||||||
private LocalDateTime oldPinnedAt;
|
private LocalDateTime oldPinnedAt;
|
||||||
|
|||||||
@@ -1,15 +1,35 @@
|
|||||||
package com.openisle.mapper;
|
package com.openisle.mapper;
|
||||||
|
|
||||||
|
import com.openisle.dto.CategoryDto;
|
||||||
import com.openisle.dto.PostChangeLogDto;
|
import com.openisle.dto.PostChangeLogDto;
|
||||||
|
import com.openisle.dto.TagDto;
|
||||||
import com.openisle.model.*;
|
import com.openisle.model.*;
|
||||||
|
import com.openisle.repository.CategoryRepository;
|
||||||
|
import com.openisle.repository.TagRepository;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
public class PostChangeLogMapper {
|
public class PostChangeLogMapper {
|
||||||
|
|
||||||
|
private final CategoryRepository categoryRepository;
|
||||||
|
private final TagRepository tagRepository;
|
||||||
|
private final CategoryMapper categoryMapper;
|
||||||
|
private final TagMapper tagMapper;
|
||||||
|
|
||||||
public PostChangeLogDto toDto(PostChangeLog log) {
|
public PostChangeLogDto toDto(PostChangeLog log) {
|
||||||
PostChangeLogDto dto = new PostChangeLogDto();
|
PostChangeLogDto dto = new PostChangeLogDto();
|
||||||
dto.setId(log.getId());
|
dto.setId(log.getId());
|
||||||
dto.setUsername(log.getUser().getUsername());
|
if (log.getUser() != null) {
|
||||||
|
dto.setUsername(log.getUser().getUsername());
|
||||||
|
dto.setUserAvatar(log.getUser().getAvatar());
|
||||||
|
}
|
||||||
dto.setType(log.getType());
|
dto.setType(log.getType());
|
||||||
dto.setTime(log.getCreatedAt());
|
dto.setTime(log.getCreatedAt());
|
||||||
if (log instanceof PostTitleChangeLog t) {
|
if (log instanceof PostTitleChangeLog t) {
|
||||||
@@ -19,11 +39,11 @@ public class PostChangeLogMapper {
|
|||||||
dto.setOldContent(c.getOldContent());
|
dto.setOldContent(c.getOldContent());
|
||||||
dto.setNewContent(c.getNewContent());
|
dto.setNewContent(c.getNewContent());
|
||||||
} else if (log instanceof PostCategoryChangeLog cat) {
|
} else if (log instanceof PostCategoryChangeLog cat) {
|
||||||
dto.setOldCategory(cat.getOldCategory());
|
dto.setOldCategory(mapCategory(cat.getOldCategory()));
|
||||||
dto.setNewCategory(cat.getNewCategory());
|
dto.setNewCategory(mapCategory(cat.getNewCategory()));
|
||||||
} else if (log instanceof PostTagChangeLog tag) {
|
} else if (log instanceof PostTagChangeLog tag) {
|
||||||
dto.setOldTags(tag.getOldTags());
|
dto.setOldTags(mapTags(tag.getOldTags()));
|
||||||
dto.setNewTags(tag.getNewTags());
|
dto.setNewTags(mapTags(tag.getNewTags()));
|
||||||
} else if (log instanceof PostClosedChangeLog cl) {
|
} else if (log instanceof PostClosedChangeLog cl) {
|
||||||
dto.setOldClosed(cl.isOldClosed());
|
dto.setOldClosed(cl.isOldClosed());
|
||||||
dto.setNewClosed(cl.isNewClosed());
|
dto.setNewClosed(cl.isNewClosed());
|
||||||
@@ -36,4 +56,37 @@ public class PostChangeLogMapper {
|
|||||||
}
|
}
|
||||||
return dto;
|
return dto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private CategoryDto mapCategory(String name) {
|
||||||
|
if (name == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return categoryRepository.findByName(name)
|
||||||
|
.map(categoryMapper::toDto)
|
||||||
|
.orElseGet(() -> {
|
||||||
|
CategoryDto dto = new CategoryDto();
|
||||||
|
dto.setName(name);
|
||||||
|
return dto;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<TagDto> mapTags(String tags) {
|
||||||
|
if (tags == null || tags.isBlank()) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
return Arrays.stream(tags.split(","))
|
||||||
|
.map(String::trim)
|
||||||
|
.map(this::mapTag)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
private TagDto mapTag(String name) {
|
||||||
|
return tagRepository.findByName(name)
|
||||||
|
.map(tagMapper::toDto)
|
||||||
|
.orElseGet(() -> {
|
||||||
|
TagDto dto = new TagDto();
|
||||||
|
dto.setName(name);
|
||||||
|
return dto;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ public abstract class PostChangeLog {
|
|||||||
@JoinColumn(name = "post_id")
|
@JoinColumn(name = "post_id")
|
||||||
private Post post;
|
private Post post;
|
||||||
|
|
||||||
@ManyToOne(fetch = FetchType.LAZY, optional = false)
|
@ManyToOne(fetch = FetchType.LAZY, optional = true)
|
||||||
@JoinColumn(name = "user_id")
|
@JoinColumn(name = "user_id")
|
||||||
private User user;
|
private User user;
|
||||||
|
|
||||||
|
|||||||
@@ -7,5 +7,7 @@ public enum PostChangeType {
|
|||||||
TAG,
|
TAG,
|
||||||
CLOSED,
|
CLOSED,
|
||||||
PINNED,
|
PINNED,
|
||||||
FEATURED
|
FEATURED,
|
||||||
|
VOTE_RESULT,
|
||||||
|
LOTTERY_RESULT
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package com.openisle.model;
|
||||||
|
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.Table;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@NoArgsConstructor
|
||||||
|
@Entity
|
||||||
|
@Table(name = "post_lottery_result_change_logs")
|
||||||
|
public class PostLotteryResultChangeLog extends PostChangeLog {
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package com.openisle.model;
|
||||||
|
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.Table;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@NoArgsConstructor
|
||||||
|
@Entity
|
||||||
|
@Table(name = "post_vote_result_change_logs")
|
||||||
|
public class PostVoteResultChangeLog extends PostChangeLog {
|
||||||
|
}
|
||||||
|
|
||||||
@@ -4,7 +4,10 @@ import com.openisle.model.Category;
|
|||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
public interface CategoryRepository extends JpaRepository<Category, Long> {
|
public interface CategoryRepository extends JpaRepository<Category, Long> {
|
||||||
List<Category> findByNameContainingIgnoreCase(String keyword);
|
List<Category> findByNameContainingIgnoreCase(String keyword);
|
||||||
|
|
||||||
|
Optional<Category> findByName(String name);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import org.springframework.data.jpa.repository.JpaRepository;
|
|||||||
import org.springframework.data.domain.Pageable;
|
import org.springframework.data.domain.Pageable;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
public interface TagRepository extends JpaRepository<Tag, Long> {
|
public interface TagRepository extends JpaRepository<Tag, Long> {
|
||||||
List<Tag> findByNameContainingIgnoreCase(String keyword);
|
List<Tag> findByNameContainingIgnoreCase(String keyword);
|
||||||
@@ -15,4 +16,6 @@ public interface TagRepository extends JpaRepository<Tag, Long> {
|
|||||||
|
|
||||||
List<Tag> findByCreatorOrderByCreatedAtDesc(User creator, Pageable pageable);
|
List<Tag> findByCreatorOrderByCreatedAtDesc(User creator, Pageable pageable);
|
||||||
List<Tag> findByCreator(User creator);
|
List<Tag> findByCreator(User creator);
|
||||||
|
|
||||||
|
Optional<Tag> findByName(String name);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package com.openisle.service;
|
|||||||
import com.openisle.model.*;
|
import com.openisle.model.*;
|
||||||
import com.openisle.repository.PostChangeLogRepository;
|
import com.openisle.repository.PostChangeLogRepository;
|
||||||
import com.openisle.repository.PostRepository;
|
import com.openisle.repository.PostRepository;
|
||||||
|
import com.openisle.repository.UserRepository;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
@@ -15,6 +16,12 @@ import java.util.stream.Collectors;
|
|||||||
public class PostChangeLogService {
|
public class PostChangeLogService {
|
||||||
private final PostChangeLogRepository logRepository;
|
private final PostChangeLogRepository logRepository;
|
||||||
private final PostRepository postRepository;
|
private final PostRepository postRepository;
|
||||||
|
private final UserRepository userRepository;
|
||||||
|
|
||||||
|
private User getSystemUser() {
|
||||||
|
return userRepository.findByUsername("system")
|
||||||
|
.orElseThrow(() -> new IllegalStateException("System user not found"));
|
||||||
|
}
|
||||||
|
|
||||||
public void recordContentChange(Post post, User user, String oldContent, String newContent) {
|
public void recordContentChange(Post post, User user, String oldContent, String newContent) {
|
||||||
PostContentChangeLog log = new PostContentChangeLog();
|
PostContentChangeLog log = new PostContentChangeLog();
|
||||||
@@ -86,6 +93,22 @@ public class PostChangeLogService {
|
|||||||
logRepository.save(log);
|
logRepository.save(log);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void recordVoteResult(Post post) {
|
||||||
|
PostVoteResultChangeLog log = new PostVoteResultChangeLog();
|
||||||
|
log.setPost(post);
|
||||||
|
log.setUser(getSystemUser());
|
||||||
|
log.setType(PostChangeType.VOTE_RESULT);
|
||||||
|
logRepository.save(log);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void recordLotteryResult(Post post) {
|
||||||
|
PostLotteryResultChangeLog log = new PostLotteryResultChangeLog();
|
||||||
|
log.setPost(post);
|
||||||
|
log.setUser(getSystemUser());
|
||||||
|
log.setType(PostChangeType.LOTTERY_RESULT);
|
||||||
|
logRepository.save(log);
|
||||||
|
}
|
||||||
|
|
||||||
public List<PostChangeLog> listLogs(Long postId) {
|
public List<PostChangeLog> listLogs(Long postId) {
|
||||||
Post post = postRepository.findById(postId)
|
Post post = postRepository.findById(postId)
|
||||||
.orElseThrow(() -> new com.openisle.exception.NotFoundException("Post not found"));
|
.orElseThrow(() -> new com.openisle.exception.NotFoundException("Post not found"));
|
||||||
|
|||||||
@@ -368,6 +368,7 @@ public class PostService {
|
|||||||
for (User participant : pp.getParticipants()) {
|
for (User participant : pp.getParticipants()) {
|
||||||
notificationService.createNotification(participant, NotificationType.POLL_RESULT_PARTICIPANT, pp, null, null, null, null, null);
|
notificationService.createNotification(participant, NotificationType.POLL_RESULT_PARTICIPANT, pp, null, null, null, null, null);
|
||||||
}
|
}
|
||||||
|
postChangeLogService.recordVoteResult(pp);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -402,6 +403,7 @@ public class PostService {
|
|||||||
notificationService.createNotification(lp.getAuthor(), NotificationType.LOTTERY_DRAW, lp, null, null, null, null, null);
|
notificationService.createNotification(lp.getAuthor(), NotificationType.LOTTERY_DRAW, lp, null, null, null, null, null);
|
||||||
notificationService.sendCustomPush(lp.getAuthor(), "抽奖已开奖", String.format("%s/posts/%d", websiteUrl, lp.getId()));
|
notificationService.sendCustomPush(lp.getAuthor(), "抽奖已开奖", String.format("%s/posts/%d", websiteUrl, lp.getId()));
|
||||||
}
|
}
|
||||||
|
postChangeLogService.recordLotteryResult(lp);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,28 +1,53 @@
|
|||||||
<template>
|
<template>
|
||||||
<div :id="`change-log-${log.id}`" class="change-log-container">
|
<div :id="`change-log-${log.id}`" class="change-log-container">
|
||||||
<div class="change-log-text">
|
<div class="change-log-text">
|
||||||
<span class="change-log-user">{{ log.username }}</span>
|
<BaseImage
|
||||||
<span v-if="log.type === 'CONTENT'">
|
v-if="log.userAvatar"
|
||||||
变更了文章内容
|
class="change-log-avatar"
|
||||||
<div class="content-diff" v-html="diffHtml"></div>
|
:src="log.userAvatar"
|
||||||
|
alt="avatar"
|
||||||
|
@click="() => navigateTo(`/users/${log.username}`)"
|
||||||
|
/>
|
||||||
|
<span v-if="log.username" class="change-log-user">{{ log.username }}</span>
|
||||||
|
<span v-if="log.type === 'CONTENT'" class="change-log-content">变更了文章内容</span>
|
||||||
|
<span v-else-if="log.type === 'TITLE'" class="change-log-content">变更了文章标题</span>
|
||||||
|
<span v-else-if="log.type === 'CATEGORY'" class="change-log-content change-log-category">
|
||||||
|
<div class="change-log-category-text">变更了文章分类, 从</div>
|
||||||
|
<ArticleCategory :category="log.oldCategory" />
|
||||||
|
<div class="change-log-category-text">修改为</div>
|
||||||
|
<ArticleCategory :category="log.newCategory" />
|
||||||
</span>
|
</span>
|
||||||
<span v-else-if="log.type === 'TITLE'">变更了文章标题</span>
|
<span v-else-if="log.type === 'TAG'" class="change-log-content change-log-category">
|
||||||
<span v-else-if="log.type === 'CATEGORY'">变更了文章分类</span>
|
<div class="change-log-category-text">变更了文章标签, 从</div>
|
||||||
<span v-else-if="log.type === 'TAG'">变更了文章标签</span>
|
<ArticleTags :tags="log.oldTags" />
|
||||||
<span v-else-if="log.type === 'CLOSED'">
|
<div class="change-log-category-text">修改为</div>
|
||||||
|
<ArticleTags :tags="log.newTags" />
|
||||||
|
</span>
|
||||||
|
<span v-else-if="log.type === 'CLOSED'" class="change-log-content">
|
||||||
<template v-if="log.newClosed">关闭了文章</template>
|
<template v-if="log.newClosed">关闭了文章</template>
|
||||||
<template v-else>重新打开了文章</template>
|
<template v-else>重新打开了文章</template>
|
||||||
</span>
|
</span>
|
||||||
<span v-else-if="log.type === 'PINNED'">
|
<span v-else-if="log.type === 'PINNED'" class="change-log-content">
|
||||||
<template v-if="log.newPinnedAt">置顶了文章</template>
|
<template v-if="log.newPinnedAt">置顶了文章</template>
|
||||||
<template v-else>取消置顶了文章</template>
|
<template v-else>取消置顶文章</template>
|
||||||
</span>
|
</span>
|
||||||
<span v-else-if="log.type === 'FEATURED'">
|
<span v-else-if="log.type === 'FEATURED'" class="change-log-content">
|
||||||
<template v-if="log.newFeatured">将文章设为精选</template>
|
<template v-if="log.newFeatured">将文章设为精选</template>
|
||||||
<template v-else>取消精选文章</template>
|
<template v-else>取消精选文章</template>
|
||||||
</span>
|
</span>
|
||||||
|
<span v-else-if="log.type === 'VOTE_RESULT'" class="change-log-content"
|
||||||
|
>系统已计算投票结果</span
|
||||||
|
>
|
||||||
|
<span v-else-if="log.type === 'LOTTERY_RESULT'" class="change-log-content"
|
||||||
|
>系统已「精密计算」抽奖结果 (=゚ω゚)ノ</span
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
<div class="change-log-time">{{ log.time }}</div>
|
<div class="change-log-time">{{ log.time }}</div>
|
||||||
|
<div
|
||||||
|
v-if="log.type === 'CONTENT' || log.type === 'TITLE'"
|
||||||
|
class="content-diff"
|
||||||
|
v-html="diffHtml"
|
||||||
|
></div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -30,14 +55,49 @@
|
|||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import { html } from 'diff2html'
|
import { html } from 'diff2html'
|
||||||
import { createTwoFilesPatch } from 'diff'
|
import { createTwoFilesPatch } from 'diff'
|
||||||
|
import { useIsMobile } from '~/utils/screen'
|
||||||
import 'diff2html/bundles/css/diff2html.min.css'
|
import 'diff2html/bundles/css/diff2html.min.css'
|
||||||
const props = defineProps({ log: Object })
|
import BaseImage from '~/components/BaseImage.vue'
|
||||||
|
import { navigateTo } from 'nuxt/app'
|
||||||
|
import { themeState } from '~/utils/theme'
|
||||||
|
import ArticleCategory from '~/components/ArticleCategory.vue'
|
||||||
|
import ArticleTags from '~/components/ArticleTags.vue'
|
||||||
|
const props = defineProps({
|
||||||
|
log: Object,
|
||||||
|
title: String,
|
||||||
|
})
|
||||||
|
|
||||||
const diffHtml = computed(() => {
|
const diffHtml = computed(() => {
|
||||||
|
const isMobile = useIsMobile()
|
||||||
|
// Track theme changes
|
||||||
|
const isDark = import.meta.client && document.documentElement.dataset.theme === 'dark'
|
||||||
|
themeState.mode
|
||||||
|
const colorScheme = isDark ? 'dark' : 'light'
|
||||||
|
|
||||||
if (props.log.type === 'CONTENT') {
|
if (props.log.type === 'CONTENT') {
|
||||||
const oldContent = props.log.oldContent ?? ''
|
const oldContent = props.log.oldContent ?? ''
|
||||||
const newContent = props.log.newContent ?? ''
|
const newContent = props.log.newContent ?? ''
|
||||||
const diff = createTwoFilesPatch('old', 'new', oldContent, newContent)
|
const diff = createTwoFilesPatch(props.title, props.title, oldContent, newContent)
|
||||||
return html(diff, { inputFormat: 'diff', showFiles: false, matching: 'lines' })
|
return html(diff, {
|
||||||
|
inputFormat: 'diff',
|
||||||
|
showFiles: false,
|
||||||
|
matching: 'lines',
|
||||||
|
drawFileList: false,
|
||||||
|
outputFormat: isMobile.value ? 'line-by-line' : 'side-by-side',
|
||||||
|
colorScheme,
|
||||||
|
})
|
||||||
|
} else if (props.log.type === 'TITLE') {
|
||||||
|
const oldTitle = props.log.oldTitle ?? ''
|
||||||
|
const newTitle = props.log.newTitle ?? ''
|
||||||
|
const diff = createTwoFilesPatch(oldTitle, newTitle, '', '')
|
||||||
|
return html(diff, {
|
||||||
|
inputFormat: 'diff',
|
||||||
|
showFiles: false,
|
||||||
|
matching: 'lines',
|
||||||
|
drawFileList: false,
|
||||||
|
outputFormat: isMobile.value ? 'line-by-line' : 'side-by-side',
|
||||||
|
colorScheme,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
return ''
|
return ''
|
||||||
})
|
})
|
||||||
@@ -47,13 +107,31 @@ const diffHtml = computed(() => {
|
|||||||
.change-log-container {
|
.change-log-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
padding-bottom: 30px;
|
/* padding-top: 5px; */
|
||||||
opacity: 0.7;
|
/* padding-bottom: 30px; */
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
.change-log-text {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
}
|
}
|
||||||
.change-log-user {
|
.change-log-user {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
margin-right: 4px;
|
margin-right: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.change-log-user,
|
||||||
|
.change-log-content {
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.change-log-avatar {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
border-radius: 50%;
|
||||||
|
margin-right: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
.change-log-time {
|
.change-log-time {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
opacity: 0.6;
|
opacity: 0.6;
|
||||||
@@ -62,4 +140,11 @@ const diffHtml = computed(() => {
|
|||||||
.content-diff {
|
.content-diff {
|
||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.change-log-category {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
gap: 4px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -134,7 +134,7 @@
|
|||||||
:post-closed="closed"
|
:post-closed="closed"
|
||||||
@deleted="onCommentDeleted"
|
@deleted="onCommentDeleted"
|
||||||
/>
|
/>
|
||||||
<PostChangeLogItem v-else :log="item" />
|
<PostChangeLogItem v-else :log="item" :title="title" />
|
||||||
</template>
|
</template>
|
||||||
</BaseTimeline>
|
</BaseTimeline>
|
||||||
</div>
|
</div>
|
||||||
@@ -363,6 +363,10 @@ const changeLogIcon = (l) => {
|
|||||||
} else {
|
} else {
|
||||||
return 'dislike'
|
return 'dislike'
|
||||||
}
|
}
|
||||||
|
} else if (l.type === 'VOTE_RESULT') {
|
||||||
|
return 'check-one'
|
||||||
|
} else if (l.type === 'LOTTERY_RESULT') {
|
||||||
|
return 'gift'
|
||||||
} else {
|
} else {
|
||||||
return 'info'
|
return 'info'
|
||||||
}
|
}
|
||||||
@@ -371,12 +375,21 @@ const changeLogIcon = (l) => {
|
|||||||
const mapChangeLog = (l) => ({
|
const mapChangeLog = (l) => ({
|
||||||
id: l.id,
|
id: l.id,
|
||||||
username: l.username,
|
username: l.username,
|
||||||
|
userAvatar: l.userAvatar,
|
||||||
type: l.type,
|
type: l.type,
|
||||||
createdAt: l.time,
|
createdAt: l.time,
|
||||||
time: TimeManager.format(l.time),
|
time: TimeManager.format(l.time),
|
||||||
newClosed: l.newClosed,
|
newClosed: l.newClosed,
|
||||||
newPinnedAt: l.newPinnedAt,
|
newPinnedAt: l.newPinnedAt,
|
||||||
newFeatured: l.newFeatured,
|
newFeatured: l.newFeatured,
|
||||||
|
oldContent: l.oldContent,
|
||||||
|
newContent: l.newContent,
|
||||||
|
oldTitle: l.oldTitle,
|
||||||
|
newTitle: l.newTitle,
|
||||||
|
oldCategory: l.oldCategory,
|
||||||
|
newCategory: l.newCategory,
|
||||||
|
oldTags: l.oldTags,
|
||||||
|
newTags: l.newTags,
|
||||||
icon: changeLogIcon(l),
|
icon: changeLogIcon(l),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -76,6 +76,7 @@ import {
|
|||||||
DoubleDown,
|
DoubleDown,
|
||||||
Open,
|
Open,
|
||||||
Dislike,
|
Dislike,
|
||||||
|
CheckOne,
|
||||||
} from '@icon-park/vue-next'
|
} from '@icon-park/vue-next'
|
||||||
|
|
||||||
export default defineNuxtPlugin((nuxtApp) => {
|
export default defineNuxtPlugin((nuxtApp) => {
|
||||||
@@ -155,4 +156,5 @@ export default defineNuxtPlugin((nuxtApp) => {
|
|||||||
nuxtApp.vueApp.component('DoubleDown', DoubleDown)
|
nuxtApp.vueApp.component('DoubleDown', DoubleDown)
|
||||||
nuxtApp.vueApp.component('OpenIcon', Open)
|
nuxtApp.vueApp.component('OpenIcon', Open)
|
||||||
nuxtApp.vueApp.component('Dislike', Dislike)
|
nuxtApp.vueApp.component('Dislike', Dislike)
|
||||||
|
nuxtApp.vueApp.component('CheckOne', CheckOne)
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user