Compare commits

...

17 Commits

Author SHA1 Message Date
Tim
d978bd428e fix: 积分icon优化 2025-08-26 11:21:43 +08:00
Tim
e5954cfb62 Merge pull request #730 from nagisa77/codex/add-point-system-for-lottery-participation
feat: integrate points with lottery participation
2025-08-26 11:14:36 +08:00
Tim
cb614b9739 feat: integrate points with lottery participation 2025-08-26 11:14:20 +08:00
Tim
88ce6b682d fix: 抽奖ui 优化 2025-08-26 10:59:54 +08:00
Tim
e02db635c4 fix: 调整收藏样式 2025-08-26 10:52:53 +08:00
Tim
231379181a Merge pull request #729 from nagisa77/codex/add-tab-for-favorite-articles
feat: add favorites tab to user profile
2025-08-26 10:49:01 +08:00
Tim
bd9ce67d4b feat: add favorites tab to user profile 2025-08-26 10:48:38 +08:00
Tim
6527b3790e fix: add link logo, 以及跳转新窗口 2025-08-26 10:47:02 +08:00
Tim
f01e8c942a Merge remote-tracking branch 'origin/main' into feature/daily_bugfix_0826 2025-08-26 10:34:21 +08:00
Tim
1e1ae29d32 fix: reactions面板,超过三种reaction才采用省略样式 而不是三个 #724 2025-08-26 10:33:45 +08:00
Tim
d31a8bfee4 Merge pull request #726 from WoJiaoFuXiaoYun/main
fix: 修复小窗口点击站内链接,会从小窗直接跳,预期主窗口跳转 #723
2025-08-26 10:33:21 +08:00
WangHe
29a96595f7 fix: 修复小窗口点击站内链接,会从小窗直接跳,预期主窗口跳转 #723 2025-08-26 10:14:28 +08:00
Tim
2b242367d7 fix: 站内信:从红点点进去又退出来,没有消退红点,新信息也没适配 #712 2025-08-26 10:12:16 +08:00
Tim
3f0cd2bf0f Merge pull request #720 from nagisa77/feature/daily_bugfix_0825_b
Feature/daily bugfix 0825 b
2025-08-25 20:38:28 +08:00
Tim
a98a631378 Revert "feat: add message float components"
This reverts commit b0eef220a6.
2025-08-25 20:38:10 +08:00
Tim
ac3fc6702a Merge pull request #721 from nagisa77/codex/add-floating-window-support-for-message-box
feat: add floating message window
2025-08-25 17:12:37 +08:00
Tim
b0eef220a6 feat: add message float components 2025-08-25 17:12:21 +08:00
24 changed files with 218 additions and 32 deletions

View File

@@ -41,7 +41,8 @@ public class PostController {
Post post = postService.createPost(auth.getName(), req.getCategoryId(),
req.getTitle(), req.getContent(), req.getTagIds(),
req.getType(), req.getPrizeDescription(), req.getPrizeIcon(),
req.getPrizeCount(), req.getStartTime(), req.getEndTime());
req.getPrizeCount(), req.getPointCost(),
req.getStartTime(), req.getEndTime());
draftService.deleteDraft(auth.getName());
PostDetailDto dto = postMapper.toDetailDto(post, auth.getName());
dto.setReward(levelService.awardForPost(auth.getName()));

View File

@@ -105,6 +105,17 @@ public class UserController {
.collect(java.util.stream.Collectors.toList());
}
@GetMapping("/{identifier}/subscribed-posts")
public java.util.List<PostMetaDto> subscribedPosts(@PathVariable("identifier") String identifier,
@RequestParam(value = "limit", required = false) Integer limit) {
int l = limit != null ? limit : defaultPostsLimit;
User user = userService.findByIdentifier(identifier).orElseThrow();
return subscriptionService.getSubscribedPosts(user.getUsername()).stream()
.limit(l)
.map(userMapper::toMetaDto)
.collect(java.util.stream.Collectors.toList());
}
@GetMapping("/{identifier}/replies")
public java.util.List<CommentInfoDto> userReplies(@PathVariable("identifier") String identifier,
@RequestParam(value = "limit", required = false) Integer limit) {

View File

@@ -10,6 +10,7 @@ public class LotteryDto {
private String prizeDescription;
private String prizeIcon;
private int prizeCount;
private int pointCost;
private LocalDateTime startTime;
private LocalDateTime endTime;
private List<AuthorDto> participants;

View File

@@ -23,6 +23,7 @@ public class PostRequest {
private String prizeDescription;
private String prizeIcon;
private Integer prizeCount;
private Integer pointCost;
private LocalDateTime startTime;
private LocalDateTime endTime;
}

View File

@@ -86,6 +86,7 @@ public class PostMapper {
l.setPrizeDescription(lp.getPrizeDescription());
l.setPrizeIcon(lp.getPrizeIcon());
l.setPrizeCount(lp.getPrizeCount());
l.setPointCost(lp.getPointCost());
l.setStartTime(lp.getStartTime());
l.setEndTime(lp.getEndTime());
l.setParticipants(lp.getParticipants().stream().map(userMapper::toAuthorDto).collect(Collectors.toList()));

View File

@@ -26,6 +26,9 @@ public class LotteryPost extends Post {
@Column(nullable = false)
private int prizeCount;
@Column(nullable = false)
private int pointCost;
@Column
private LocalDateTime startTime;

View File

@@ -8,5 +8,7 @@ public enum PointHistoryType {
INVITE,
FEATURE,
SYSTEM_ONLINE,
REDEEM
REDEEM,
LOTTERY_JOIN,
LOTTERY_REWARD
}

View File

@@ -2,6 +2,7 @@ package com.openisle.service;
import com.openisle.model.*;
import com.openisle.repository.*;
import com.openisle.exception.FieldException;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
@@ -39,6 +40,17 @@ public class PointService {
return addPoint(user, 500, PointHistoryType.FEATURE, post, null, null);
}
public void processLotteryJoin(User participant, LotteryPost post) {
int cost = post.getPointCost();
if (cost > 0) {
if (participant.getPoint() < cost) {
throw new FieldException("point", "积分不足");
}
addPoint(participant, -cost, PointHistoryType.LOTTERY_JOIN, post, null, post.getAuthor());
addPoint(post.getAuthor(), cost, PointHistoryType.LOTTERY_REWARD, post, null, participant);
}
}
private PointLog getTodayLog(User user) {
LocalDate today = LocalDate.now();
return pointLogRepository.findByUserAndLogDate(user, today)

View File

@@ -164,6 +164,7 @@ public class PostService {
String prizeDescription,
String prizeIcon,
Integer prizeCount,
Integer pointCost,
LocalDateTime startTime,
LocalDateTime endTime) {
long recent = postRepository.countByAuthorAfter(username,
@@ -188,10 +189,14 @@ public class PostService {
PostType actualType = type != null ? type : PostType.NORMAL;
Post post;
if (actualType == PostType.LOTTERY) {
if (pointCost != null && (pointCost < 0 || pointCost > 100)) {
throw new IllegalArgumentException("pointCost must be between 0 and 100");
}
LotteryPost lp = new LotteryPost();
lp.setPrizeDescription(prizeDescription);
lp.setPrizeIcon(prizeIcon);
lp.setPrizeCount(prizeCount != null ? prizeCount : 0);
lp.setPointCost(pointCost != null ? pointCost : 0);
lp.setStartTime(startTime);
lp.setEndTime(endTime);
post = lp;
@@ -250,8 +255,10 @@ public class PostService {
.orElseThrow(() -> new com.openisle.exception.NotFoundException("Post not found"));
User user = userRepository.findByUsername(username)
.orElseThrow(() -> new com.openisle.exception.NotFoundException("User not found"));
post.getParticipants().add(user);
lotteryPostRepository.save(post);
if (post.getParticipants().add(user)) {
pointService.processLotteryJoin(user, post);
lotteryPostRepository.save(post);
}
}
@Transactional

View File

@@ -107,6 +107,11 @@ public class SubscriptionService {
return commentSubRepo.findByComment(c).stream().map(CommentSubscription::getUser).toList();
}
public List<Post> getSubscribedPosts(String username) {
User user = userRepo.findByUsername(username).orElseThrow();
return postSubRepo.findByUser(user).stream().map(PostSubscription::getPost).toList();
}
public long countSubscribers(String username) {
User user = userRepo.findByUsername(username).orElseThrow();

View File

@@ -0,0 +1 @@
ALTER TABLE lottery_posts ADD COLUMN point_cost INT NOT NULL DEFAULT 0;

View File

@@ -76,7 +76,7 @@ class PostControllerTest {
post.setTags(Set.of(tag));
when(postService.createPost(eq("alice"), eq(1L), eq("t"), eq("c"), eq(List.of(1L)),
isNull(), isNull(), isNull(), isNull(), isNull(), isNull())).thenReturn(post);
isNull(), isNull(), isNull(), isNull(), isNull(), isNull(), isNull())).thenReturn(post);
when(postService.viewPost(eq(1L), any())).thenReturn(post);
when(commentService.getCommentsForPost(eq(1L), any())).thenReturn(List.of());
when(commentService.getParticipants(anyLong(), anyInt())).thenReturn(List.of());
@@ -187,7 +187,7 @@ class PostControllerTest {
.andExpect(status().isBadRequest());
verify(postService, never()).createPost(any(), any(), any(), any(), any(),
any(), any(), any(), any(), any(), any());
any(), any(), any(), any(), any(), any(), any());
}
@Test

View File

@@ -136,6 +136,30 @@ class UserControllerTest {
.andExpect(jsonPath("$[0].title").value("hello"));
}
@Test
void listSubscribedPosts() throws Exception {
User user = new User();
user.setUsername("bob");
com.openisle.model.Category cat = new com.openisle.model.Category();
cat.setName("tech");
com.openisle.model.Post post = new com.openisle.model.Post();
post.setId(6L);
post.setTitle("fav");
post.setCreatedAt(java.time.LocalDateTime.now());
post.setCategory(cat);
post.setAuthor(user);
Mockito.when(userService.findByIdentifier("bob")).thenReturn(Optional.of(user));
Mockito.when(subscriptionService.getSubscribedPosts("bob")).thenReturn(java.util.List.of(post));
PostMetaDto meta = new PostMetaDto();
meta.setId(6L);
meta.setTitle("fav");
Mockito.when(userMapper.toMetaDto(post)).thenReturn(meta);
mockMvc.perform(get("/api/users/bob/subscribed-posts"))
.andExpect(status().isOk())
.andExpect(jsonPath("$[0].title").value("fav"));
}
@Test
void listUserReplies() throws Exception {
User user = new User();

View File

@@ -146,7 +146,7 @@ class PostServiceTest {
assertThrows(RateLimitException.class,
() -> service.createPost("alice", 1L, "t", "c", List.of(1L),
null, null, null, null, null, null));
null, null, null, null, null, null, null));
}
@Test

View File

@@ -1,6 +1,6 @@
<template>
<div v-if="floatRoute" class="message-float-window" :style="{ height: floatHeight }">
<iframe :src="iframeSrc" frameborder="0"></iframe>
<iframe :src="iframeSrc" frameborder="0" ref="iframeRef" @load="injectBaseTag"></iframe>
<div class="float-actions">
<i
@@ -9,14 +9,12 @@
title="收起至 100px"
@click="collapseToMini"
></i>
<!-- 回弹60vh -->
<i
class="fas fa-chevron-up"
v-if="floatHeight !== DEFAULT_HEIGHT"
title="回弹至 60vh"
@click="reboundToDefault"
></i>
<!-- 全屏打开原有逻辑 -->
<i class="fas fa-expand" title="在页面中打开" @click="expand"></i>
</div>
</div>
@@ -29,6 +27,7 @@ const DEFAULT_HEIGHT = '60vh'
const MINI_HEIGHT = '45px'
const floatHeight = ref(DEFAULT_HEIGHT)
const iframeRef = ref(null)
const iframeSrc = computed(() => {
if (!floatRoute.value) return ''
return floatRoute.value + (floatRoute.value.includes('?') ? '&' : '?') + 'float=1'
@@ -49,7 +48,17 @@ function expand() {
navigateTo(target)
}
// 当浮窗重新出现时,恢复默认高度
function injectBaseTag() {
if (!iframeRef.value) return
const iframeDoc = iframeRef.value.contentDocument || iframeRef.value.contentWindow.document
if (iframeDoc && !iframeDoc.querySelector('base')) {
const base = iframeDoc.createElement('base')
base.target = '_top'
iframeDoc.head.appendChild(base)
}
}
watch(
() => floatRoute.value,
(v) => {
@@ -64,7 +73,6 @@ watch(
bottom: 0;
right: 0;
width: 400px;
/* 高度由内联样式绑定控制60vh / 100px */
max-height: 90vh;
background-color: var(--background-color);
border: 1px solid var(--normal-border-color);
@@ -72,7 +80,8 @@ watch(
z-index: 2000;
display: flex;
flex-direction: column;
transition: height 0.25s ease; /* 平滑过渡 */
transition: height 0.25s ease;
/* 平滑过渡 */
}
.message-float-window iframe {

View File

@@ -6,7 +6,7 @@
@mouseenter="cancelHide"
@mouseleave="scheduleHide"
>
<template v-if="reactions.length < 4">
<template v-if="counts.length < 4">
<div
v-for="r in displayedReactions"
:key="r.type"

View File

@@ -8,7 +8,7 @@
<i class="fas fa-compress" @click="minimize" title="最小化"></i>
</div>
<div class="tabs">
<div :class="['tab', { active: activeTab === 'messages' }]" @click="activeTab = 'messages'">
<div :class="['tab', { active: activeTab === 'messages' }]" @click="switchToMessage">
站内信
</div>
<div :class="['tab', { active: activeTab === 'channels' }]" @click="switchToChannels">
@@ -147,7 +147,7 @@ const { fetchChannelUnread: refreshChannelUnread, setFromList: setChannelUnreadF
useChannelsUnreadCount()
let subscription = null
const activeTab = ref('messages')
const activeTab = ref('channels')
const channels = ref([])
const loadingChannels = ref(false)
const isFloatMode = computed(() => route.query.float === '1')
@@ -159,6 +159,7 @@ async function fetchConversations() {
toast.error('请先登录')
return
}
loading.value = true
try {
const response = await fetch(`${API_BASE_URL}/api/messages/conversations`, {
method: 'GET',
@@ -215,11 +216,14 @@ async function fetchChannels() {
}
}
function switchToMessage() {
activeTab.value = 'messages'
fetchConversations()
}
function switchToChannels() {
activeTab.value = 'channels'
if (channels.value.length === 0) {
fetchChannels()
}
fetchChannels()
}
async function goToChannel(id) {
@@ -244,12 +248,15 @@ async function goToChannel(id) {
}
onActivated(async () => {
loading.value = true
currentUser.value = await fetchCurrentUser()
if (currentUser.value) {
await fetchConversations()
refreshGlobalUnreadCount() // Refresh global count when entering the list
if (activeTab.value === 'messages') {
await fetchConversations()
} else {
await fetchChannels()
}
refreshGlobalUnreadCount()
refreshChannelUnread()
const token = getToken()
if (token && !isConnected.value) {

View File

@@ -66,6 +66,18 @@
/>
</div>
</div>
<div class="prize-point-row">
<span class="prize-row-title">参与所需积分</span>
<div class="prize-count-input">
<input
class="prize-count-input-field"
type="number"
v-model.number="pointCost"
min="0"
max="100"
/>
</div>
</div>
<div class="prize-time-row">
<span class="prize-row-title">抽奖结束时间</span>
<client-only>
@@ -105,6 +117,7 @@ const showPrizeCropper = ref(false)
const prizeName = ref('')
const prizeCount = ref(1)
const prizeDescription = ref('')
const pointCost = ref(0)
const endTime = ref(null)
const startTime = ref(null)
const dateConfig = { enableTime: true, time_24hr: true, dateFormat: 'Y-m-d H:i' }
@@ -133,6 +146,11 @@ watch(prizeCount, (val) => {
if (!val || val < 1) prizeCount.value = 1
})
watch(pointCost, (val) => {
if (val === undefined || val === null || val < 0) pointCost.value = 0
if (val > 100) pointCost.value = 100
})
const loadDraft = async () => {
const token = getToken()
if (!token) return
@@ -168,6 +186,7 @@ const clearPost = async () => {
showPrizeCropper.value = false
prizeDescription.value = ''
prizeCount.value = 1
pointCost.value = 0
endTime.value = null
startTime.value = null
@@ -315,6 +334,10 @@ const submitPost = async () => {
toast.error('请选择抽奖结束时间')
return
}
if (pointCost.value < 0 || pointCost.value > 100) {
toast.error('参与积分需在0到100之间')
return
}
}
try {
const token = getToken()
@@ -354,6 +377,7 @@ const submitPost = async () => {
prizeDescription: postType.value === 'LOTTERY' ? prizeDescription.value : undefined,
startTime:
postType.value === 'LOTTERY' ? new Date(startTime.value).toISOString() : undefined,
pointCost: postType.value === 'LOTTERY' ? pointCost.value : undefined,
// 将时间转换为 UTC+8.5 时区 todo: 需要优化
endTime:
postType.value === 'LOTTERY'
@@ -498,6 +522,8 @@ const submitPost = async () => {
display: flex;
flex-direction: column;
gap: 20px;
margin-bottom: 200px;
}
.prize-row-title {

View File

@@ -146,6 +146,24 @@
<template v-else-if="item.type === 'REDEEM'">
兑换商品消耗 {{ -item.amount }} 积分
</template>
<template v-else-if="item.type === 'LOTTERY_JOIN'">
参与抽奖帖
<NuxtLink :to="`/posts/${item.postId}`" class="timeline-link">{{
item.postTitle
}}</NuxtLink>
消耗 {{ -item.amount }} 积分
</template>
<template v-else-if="item.type === 'LOTTERY_REWARD'">
你的抽奖帖
<NuxtLink :to="`/posts/${item.postId}`" class="timeline-link">{{
item.postTitle
}}</NuxtLink>
<NuxtLink :to="`/users/${item.fromUserId}`" class="timeline-link">{{
item.fromUserName
}}</NuxtLink>
参与获得 {{ item.amount }} 积分
</template>
<template v-else-if="item.type === 'SYSTEM_ONLINE'"> 积分历史系统上线 </template>
<i class="fas fa-coins"></i> 你目前的积分是 {{ item.balance }}
</div>
@@ -201,6 +219,8 @@ const iconMap = {
SYSTEM_ONLINE: 'fas fa-clock',
REDEEM: 'fas fa-gift',
FEATURE: 'fas fa-star',
LOTTERY_JOIN: 'fas fa-ticket-alt',
LOTTERY_REWARD: 'fas fa-ticket-alt',
}
onMounted(async () => {

View File

@@ -119,7 +119,9 @@
class="join-prize-button"
@click="joinLottery"
>
<div class="join-prize-button-text">参与抽奖</div>
<div class="join-prize-button-text">
参与抽奖 <i class="fas fa-coins"></i> {{ lottery.pointCost }}
</div>
</div>
<div v-else-if="hasJoined" class="join-prize-button-disabled">
<div class="join-prize-button-text">已参与</div>
@@ -134,7 +136,9 @@
class="join-prize-button"
@click="joinLottery"
>
<div class="join-prize-button-text">参与抽奖</div>
<div class="join-prize-button-text">
参与抽奖 <i class="fas fa-coins"></i> {{ lottery.pointCost }}
</div>
</div>
<div v-else-if="hasJoined" class="join-prize-button-disabled">
<div class="join-prize-button-text">已参与</div>
@@ -810,11 +814,12 @@ const joinLottery = async () => {
method: 'POST',
headers: { Authorization: `Bearer ${token}` },
})
const data = await res.json().catch(() => ({}))
if (res.ok) {
toast.success('已参与抽奖')
await refreshPost()
} else {
toast.error('操作失败')
toast.error(data.error || '操作失败')
}
}
@@ -894,7 +899,7 @@ onMounted(async () => {
})
</script>
<style>
<style scoped>
.post-page-container {
background-color: var(--background-color);
display: flex;

View File

@@ -94,6 +94,13 @@
<i class="fas fa-user-plus"></i>
<div class="profile-tabs-item-label">关注</div>
</div>
<div
:class="['profile-tabs-item', { selected: selectedTab === 'favorites' }]"
@click="selectedTab = 'favorites'"
>
<i class="fas fa-bookmark"></i>
<div class="profile-tabs-item-label">收藏</div>
</div>
<div
:class="['profile-tabs-item', { selected: selectedTab === 'achievements' }]"
@click="selectedTab = 'achievements'"
@@ -318,6 +325,23 @@
</div>
</div>
<div v-else-if="selectedTab === 'favorites'" class="favorites-container">
<div v-if="favoritePosts.length > 0">
<BaseTimeline :items="favoritePosts">
<template #item="{ item }">
<NuxtLink :to="`/posts/${item.post.id}`" class="timeline-link">
{{ item.post.title }}
</NuxtLink>
<div class="timeline-snippet">
{{ stripMarkdown(item.post.snippet) }}
</div>
<div class="timeline-date">{{ formatDate(item.post.createdAt) }}</div>
</template>
</BaseTimeline>
</div>
<div v-else class="summary-empty">暂无收藏文章</div>
</div>
<div v-else-if="selectedTab === 'achievements'" class="achievements-container">
<AchievementList :medals="medals" :can-select="isMine" />
</div>
@@ -352,6 +376,7 @@ const user = ref({})
const hotPosts = ref([])
const hotReplies = ref([])
const hotTags = ref([])
const favoritePosts = ref([])
const timelineItems = ref([])
const timelineFilter = ref('all')
const filteredTimelineItems = computed(() => {
@@ -369,7 +394,7 @@ const subscribed = ref(false)
const isLoading = ref(true)
const tabLoading = ref(false)
const selectedTab = ref(
['summary', 'timeline', 'following', 'achievements'].includes(route.query.tab)
['summary', 'timeline', 'following', 'favorites', 'achievements'].includes(route.query.tab)
? route.query.tab
: 'summary',
)
@@ -472,6 +497,16 @@ const fetchFollowUsers = async () => {
followings.value = followingRes.ok ? await followingRes.json() : []
}
const fetchFavorites = async () => {
const res = await fetch(`${API_BASE_URL}/api/users/${username}/subscribed-posts`)
if (res.ok) {
const data = await res.json()
favoritePosts.value = data.map((p) => ({ icon: 'fas fa-bookmark', post: p }))
} else {
favoritePosts.value = []
}
}
const loadSummary = async () => {
tabLoading.value = true
await fetchSummary()
@@ -490,6 +525,12 @@ const loadFollow = async () => {
tabLoading.value = false
}
const loadFavorites = async () => {
tabLoading.value = true
await fetchFavorites()
tabLoading.value = false
}
const fetchAchievements = async () => {
const res = await fetch(`${API_BASE_URL}/api/medals?userId=${user.value.id}`)
if (res.ok) {
@@ -578,6 +619,8 @@ const init = async () => {
await loadTimeline()
} else if (selectedTab.value === 'following') {
await loadFollow()
} else if (selectedTab.value === 'favorites') {
await loadFavorites()
} else if (selectedTab.value === 'achievements') {
await loadAchievements()
}
@@ -596,6 +639,8 @@ watch(selectedTab, async (val) => {
await loadTimeline()
} else if (val === 'following' && followers.value.length === 0 && followings.value.length === 0) {
await loadFollow()
} else if (val === 'favorites' && favoritePosts.value.length === 0) {
await loadFavorites()
} else if (val === 'achievements' && medals.value.length === 0) {
await loadAchievements()
}
@@ -900,6 +945,10 @@ watch(selectedTab, async (val) => {
.follow-container {
}
.favorites-container {
padding: 10px;
}
.follow-tabs {
display: flex;
flex-direction: row;

View File

@@ -106,7 +106,7 @@ const md = new MarkdownIt({
md.use(mentionPlugin)
md.use(tiebaEmojiPlugin)
md.use(linkPlugin) // 添加链接插件
md.use(linkPlugin)
export function renderMarkdown(text) {
return md.render(text || '')

View File

@@ -16,6 +16,7 @@ export function createVditor(editorId, options = {}) {
const { placeholder = '', preview = {}, input, after } = options
const config = useRuntimeConfig()
const API_BASE_URL = config.public.apiBaseUrl
const WEBSITE_BASE_URL = config.public.websiteBaseUrl
const fetchMentions = async (value) => {
if (!value) {
@@ -80,7 +81,7 @@ export function createVditor(editorId, options = {}) {
}))
},
},
vditorPostCitation(API_BASE_URL),
vditorPostCitation(API_BASE_URL, WEBSITE_BASE_URL),
],
},
cdn: 'https://openisle-1307107697.cos.ap-guangzhou.myqcloud.com/assert/vditor',

View File

@@ -10,7 +10,7 @@ async function searchPost(apiBaseUrl, keyword) {
})
}
export default (apiBaseUrl) => {
export default (apiBaseUrl, websiteBaseUrl) => {
return {
key: '#',
hint: async (keyword) => {
@@ -22,8 +22,8 @@ export default (apiBaseUrl) => {
let value = ''
return (
body.map((item) => ({
value: `[${item.title}](/posts/${item.id})`,
html: `<div>${item.title}</div>`,
value: `[🔗${item.title}](${websiteBaseUrl}/posts/${item.id})`,
html: `<div><i class="fas fa-link"></i> ${item.title}</div>`,
})) ?? []
)
} else {