mirror of
https://github.com/nagisa77/OpenIsle.git
synced 2026-02-21 06:20:59 +08:00
Compare commits
6 Commits
codex/add-
...
codex/upda
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fe167aa0b9 | ||
|
|
f3421265d2 | ||
|
|
f4817cd6d1 | ||
|
|
5ae0f9311c | ||
|
|
567452f570 | ||
|
|
bb4e866bd0 |
@@ -5,22 +5,24 @@ import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public class PostChangeLogDto {
|
||||
private Long id;
|
||||
private String username;
|
||||
private String userAvatar;
|
||||
private PostChangeType type;
|
||||
private LocalDateTime time;
|
||||
private String oldTitle;
|
||||
private String newTitle;
|
||||
private String oldContent;
|
||||
private String newContent;
|
||||
private String oldCategory;
|
||||
private String newCategory;
|
||||
private String oldTags;
|
||||
private String newTags;
|
||||
private CategoryDto oldCategory;
|
||||
private CategoryDto newCategory;
|
||||
private List<TagDto> oldTags;
|
||||
private List<TagDto> newTags;
|
||||
private Boolean oldClosed;
|
||||
private Boolean newClosed;
|
||||
private LocalDateTime oldPinnedAt;
|
||||
|
||||
@@ -1,15 +1,24 @@
|
||||
package com.openisle.mapper;
|
||||
|
||||
import com.openisle.dto.CategoryDto;
|
||||
import com.openisle.dto.PostChangeLogDto;
|
||||
import com.openisle.dto.TagDto;
|
||||
import com.openisle.model.*;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Component
|
||||
public class PostChangeLogMapper {
|
||||
public PostChangeLogDto toDto(PostChangeLog log) {
|
||||
PostChangeLogDto dto = new PostChangeLogDto();
|
||||
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.setTime(log.getCreatedAt());
|
||||
if (log instanceof PostTitleChangeLog t) {
|
||||
@@ -19,11 +28,41 @@ public class PostChangeLogMapper {
|
||||
dto.setOldContent(c.getOldContent());
|
||||
dto.setNewContent(c.getNewContent());
|
||||
} else if (log instanceof PostCategoryChangeLog cat) {
|
||||
dto.setOldCategory(cat.getOldCategory());
|
||||
dto.setNewCategory(cat.getNewCategory());
|
||||
if (cat.getOldCategory() != null) {
|
||||
CategoryDto oldCat = new CategoryDto();
|
||||
oldCat.setName(cat.getOldCategory());
|
||||
dto.setOldCategory(oldCat);
|
||||
}
|
||||
if (cat.getNewCategory() != null) {
|
||||
CategoryDto newCat = new CategoryDto();
|
||||
newCat.setName(cat.getNewCategory());
|
||||
dto.setNewCategory(newCat);
|
||||
}
|
||||
} else if (log instanceof PostTagChangeLog tag) {
|
||||
dto.setOldTags(tag.getOldTags());
|
||||
dto.setNewTags(tag.getNewTags());
|
||||
if (tag.getOldTags() != null && !tag.getOldTags().isBlank()) {
|
||||
List<TagDto> oldTags = Arrays.stream(tag.getOldTags().split(","))
|
||||
.map(String::trim)
|
||||
.filter(s -> !s.isEmpty())
|
||||
.map(name -> {
|
||||
TagDto t = new TagDto();
|
||||
t.setName(name);
|
||||
return t;
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
dto.setOldTags(oldTags);
|
||||
}
|
||||
if (tag.getNewTags() != null && !tag.getNewTags().isBlank()) {
|
||||
List<TagDto> newTags = Arrays.stream(tag.getNewTags().split(","))
|
||||
.map(String::trim)
|
||||
.filter(s -> !s.isEmpty())
|
||||
.map(name -> {
|
||||
TagDto t = new TagDto();
|
||||
t.setName(name);
|
||||
return t;
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
dto.setNewTags(newTags);
|
||||
}
|
||||
} else if (log instanceof PostClosedChangeLog cl) {
|
||||
dto.setOldClosed(cl.isOldClosed());
|
||||
dto.setNewClosed(cl.isNewClosed());
|
||||
|
||||
@@ -23,7 +23,7 @@ public abstract class PostChangeLog {
|
||||
@JoinColumn(name = "post_id")
|
||||
private Post post;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY, optional = false)
|
||||
@ManyToOne(fetch = FetchType.LAZY, optional = true)
|
||||
@JoinColumn(name = "user_id")
|
||||
private User user;
|
||||
|
||||
|
||||
@@ -7,5 +7,7 @@ public enum PostChangeType {
|
||||
TAG,
|
||||
CLOSED,
|
||||
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 {
|
||||
}
|
||||
|
||||
@@ -86,6 +86,20 @@ public class PostChangeLogService {
|
||||
logRepository.save(log);
|
||||
}
|
||||
|
||||
public void recordVoteResult(Post post) {
|
||||
PostVoteResultChangeLog log = new PostVoteResultChangeLog();
|
||||
log.setPost(post);
|
||||
log.setType(PostChangeType.VOTE_RESULT);
|
||||
logRepository.save(log);
|
||||
}
|
||||
|
||||
public void recordLotteryResult(Post post) {
|
||||
PostLotteryResultChangeLog log = new PostLotteryResultChangeLog();
|
||||
log.setPost(post);
|
||||
log.setType(PostChangeType.LOTTERY_RESULT);
|
||||
logRepository.save(log);
|
||||
}
|
||||
|
||||
public List<PostChangeLog> listLogs(Long postId) {
|
||||
Post post = postRepository.findById(postId)
|
||||
.orElseThrow(() -> new com.openisle.exception.NotFoundException("Post not found"));
|
||||
|
||||
@@ -1,28 +1,39 @@
|
||||
<template>
|
||||
<div :id="`change-log-${log.id}`" class="change-log-container">
|
||||
<div class="change-log-text">
|
||||
<span class="change-log-user">{{ log.username }}</span>
|
||||
<span v-if="log.type === 'CONTENT'">
|
||||
变更了文章内容
|
||||
<div class="content-diff" v-html="diffHtml"></div>
|
||||
</span>
|
||||
<span v-else-if="log.type === 'TITLE'">变更了文章标题</span>
|
||||
<span v-else-if="log.type === 'CATEGORY'">变更了文章分类</span>
|
||||
<span v-else-if="log.type === 'TAG'">变更了文章标签</span>
|
||||
<span v-else-if="log.type === 'CLOSED'">
|
||||
<BaseImage
|
||||
v-if="log.userAvatar"
|
||||
class="change-log-avatar"
|
||||
: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">变更了文章分类</span>
|
||||
<span v-else-if="log.type === 'TAG'" class="change-log-content">变更了文章标签</span>
|
||||
<span v-else-if="log.type === 'CLOSED'" class="change-log-content">
|
||||
<template v-if="log.newClosed">关闭了文章</template>
|
||||
<template v-else>重新打开了文章</template>
|
||||
</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-else>取消置顶了文章</template>
|
||||
<template v-else>取消置顶文章</template>
|
||||
</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-else>取消精选文章</template>
|
||||
</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 class="change-log-time">{{ log.time }}</div>
|
||||
<div
|
||||
v-if="log.type === 'CONTENT' || log.type === 'TITLE'"
|
||||
class="content-diff"
|
||||
v-html="diffHtml"
|
||||
></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -30,14 +41,39 @@
|
||||
import { computed } from 'vue'
|
||||
import { html } from 'diff2html'
|
||||
import { createTwoFilesPatch } from 'diff'
|
||||
import { useIsMobile } from '~/utils/screen'
|
||||
import 'diff2html/bundles/css/diff2html.min.css'
|
||||
const props = defineProps({ log: Object })
|
||||
import BaseImage from '~/components/BaseImage.vue'
|
||||
import { navigateTo } from 'nuxt/app'
|
||||
const props = defineProps({
|
||||
log: Object,
|
||||
title: String,
|
||||
})
|
||||
|
||||
const diffHtml = computed(() => {
|
||||
const isMobile = useIsMobile()
|
||||
if (props.log.type === 'CONTENT') {
|
||||
const oldContent = props.log.oldContent ?? ''
|
||||
const newContent = props.log.newContent ?? ''
|
||||
const diff = createTwoFilesPatch('old', 'new', oldContent, newContent)
|
||||
return html(diff, { inputFormat: 'diff', showFiles: false, matching: 'lines' })
|
||||
const diff = createTwoFilesPatch(props.title, props.title, oldContent, newContent)
|
||||
return html(diff, {
|
||||
inputFormat: 'diff',
|
||||
showFiles: false,
|
||||
matching: 'lines',
|
||||
drawFileList: false,
|
||||
outputFormat: isMobile.value ? 'line-by-line' : 'side-by-side',
|
||||
})
|
||||
} 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',
|
||||
})
|
||||
}
|
||||
return ''
|
||||
})
|
||||
@@ -47,13 +83,31 @@ const diffHtml = computed(() => {
|
||||
.change-log-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding-bottom: 30px;
|
||||
opacity: 0.7;
|
||||
/* padding-top: 5px; */
|
||||
/* padding-bottom: 30px; */
|
||||
font-size: 14px;
|
||||
}
|
||||
.change-log-text {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.change-log-user {
|
||||
font-weight: bold;
|
||||
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 {
|
||||
font-size: 12px;
|
||||
opacity: 0.6;
|
||||
|
||||
@@ -134,7 +134,7 @@
|
||||
:post-closed="closed"
|
||||
@deleted="onCommentDeleted"
|
||||
/>
|
||||
<PostChangeLogItem v-else :log="item" />
|
||||
<PostChangeLogItem v-else :log="item" :title="title" />
|
||||
</template>
|
||||
</BaseTimeline>
|
||||
</div>
|
||||
@@ -363,6 +363,10 @@ const changeLogIcon = (l) => {
|
||||
} else {
|
||||
return 'dislike'
|
||||
}
|
||||
} else if (l.type === 'VOTE_RESULT') {
|
||||
return 'check-one'
|
||||
} else if (l.type === 'LOTTERY_RESULT') {
|
||||
return 'gift'
|
||||
} else {
|
||||
return 'info'
|
||||
}
|
||||
@@ -371,12 +375,21 @@ const changeLogIcon = (l) => {
|
||||
const mapChangeLog = (l) => ({
|
||||
id: l.id,
|
||||
username: l.username,
|
||||
userAvatar: l.userAvatar,
|
||||
type: l.type,
|
||||
createdAt: l.time,
|
||||
time: TimeManager.format(l.time),
|
||||
newClosed: l.newClosed,
|
||||
newPinnedAt: l.newPinnedAt,
|
||||
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),
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user