feat(posts): add @mention highlighting and vote segment display
All checks were successful
Admin CI / build-and-push-web (push) Successful in 2m59s
All checks were successful
Admin CI / build-and-push-web (push) Successful in 2m59s
- Parse post content to highlight @mentions with blue styling - Render vote segments from post data with formatted options display - Add MessageSegment interface and segments/is_vote fields to Post type - Update API base URL and branding from Carrot BBS to WithYou
This commit is contained in:
@@ -1,2 +1,2 @@
|
|||||||
# 开发环境 API 地址
|
# 开发环境 API 地址
|
||||||
VITE_API_BASE_URL=https://bbs.littlelan.cn/api/v1
|
VITE_API_BASE_URL=https://withyou.littlelan.cn/api/v1
|
||||||
@@ -2,7 +2,7 @@ import axios from 'axios'
|
|||||||
import { useAuthStore } from '@/stores/authStore'
|
import { useAuthStore } from '@/stores/authStore'
|
||||||
|
|
||||||
const apiClient = axios.create({
|
const apiClient = axios.create({
|
||||||
baseURL: import.meta.env.VITE_API_BASE_URL || 'https://bbs.littlelan.cn/api/v1',
|
baseURL: import.meta.env.VITE_API_BASE_URL || 'https://withyou.littlelan.cn/api/v1',
|
||||||
timeout: 10000,
|
timeout: 10000,
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
|
|||||||
@@ -206,7 +206,7 @@ export default function Sidebar({ collapsed, onCollapse }: SidebarProps) {
|
|||||||
{/* Logo区域 */}
|
{/* Logo区域 */}
|
||||||
<div className="flex h-16 items-center justify-between border-b px-4">
|
<div className="flex h-16 items-center justify-between border-b px-4">
|
||||||
{!collapsed && (
|
{!collapsed && (
|
||||||
<span className="text-xl font-bold text-primary">Carrot BBS</span>
|
<span className="text-xl font-bold text-primary">WithYou</span>
|
||||||
)}
|
)}
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
@@ -230,7 +230,7 @@ export default function Sidebar({ collapsed, onCollapse }: SidebarProps) {
|
|||||||
{/* 底部版本信息 */}
|
{/* 底部版本信息 */}
|
||||||
{!collapsed && (
|
{!collapsed && (
|
||||||
<div className="border-t p-4 text-center text-xs text-muted-foreground">
|
<div className="border-t p-4 text-center text-xs text-muted-foreground">
|
||||||
Carrot BBS Admin v1.0.0
|
WithYou Admin v0.0.1
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</aside>
|
</aside>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useState, useEffect } from 'react'
|
import { useState, useEffect } from 'react'
|
||||||
import { Users, FileText, MessageSquare, Clock, TrendingUp, Calendar, CalendarDays, CalendarRange } from 'lucide-react'
|
import { Users, FileText, MessageSquare, Clock, TrendingUp, Calendar, CalendarDays, CalendarRange } from 'lucide-react'
|
||||||
import { StatsCard } from '@/components/StatsCard'
|
import { StatsCard } from '@/components/StatsCard'
|
||||||
import { ActivityChart } from '@/components/ActivityChart'
|
import { ActivityChart } from '@/components/ActivityChart'
|
||||||
@@ -78,7 +78,7 @@ export default function Dashboard() {
|
|||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
<div>
|
<div>
|
||||||
<h1 className="text-3xl font-bold">Dashboard</h1>
|
<h1 className="text-3xl font-bold">Dashboard</h1>
|
||||||
<p className="text-muted-foreground">欢迎来到 Carrot BBS 管理后台</p>
|
<p className="text-muted-foreground">欢迎来到 WithYou 管理后台</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 统计卡片 */}
|
{/* 统计卡片 */}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import { useNavigate, useLocation } from 'react-router-dom'
|
import { useNavigate, useLocation } from 'react-router-dom'
|
||||||
import { useAuthStore } from '@/stores/authStore'
|
import { useAuthStore } from '@/stores/authStore'
|
||||||
import { Button } from '@/components/ui/button'
|
import { Button } from '@/components/ui/button'
|
||||||
@@ -62,7 +62,7 @@ export default function Login() {
|
|||||||
<span className="text-2xl font-bold">C</span>
|
<span className="text-2xl font-bold">C</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<CardTitle className="text-2xl font-bold">Carrot BBS</CardTitle>
|
<CardTitle className="text-2xl font-bold">WithYou</CardTitle>
|
||||||
<CardDescription>管理后台登录</CardDescription>
|
<CardDescription>管理后台登录</CardDescription>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<form onSubmit={handleSubmit}>
|
<form onSubmit={handleSubmit}>
|
||||||
|
|||||||
@@ -636,8 +636,25 @@ export default function Posts() {
|
|||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<h3 className="text-2xl font-bold text-slate-900 leading-tight">{selectedPost.title}</h3>
|
<h3 className="text-2xl font-bold text-slate-900 leading-tight">{selectedPost.title}</h3>
|
||||||
<div className="prose prose-slate max-w-none">
|
<div className="prose prose-slate max-w-none">
|
||||||
<p className="text-slate-700 whitespace-pre-wrap leading-relaxed text-base">{selectedPost.content}</p>
|
<p className="text-slate-700 whitespace-pre-wrap leading-relaxed text-base">
|
||||||
|
{selectedPost.content?.split(/@(\S+)/g).map((part, i) =>
|
||||||
|
i % 2 === 1 ? <span key={i} className="text-blue-600 font-medium">@{part}</span> : part
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
{/* 投票信息(从 segments 解析) */}
|
||||||
|
{selectedPost.segments?.filter(s => s.type === 'vote').map((seg, i) => (
|
||||||
|
<div key={i} className="mt-3 p-4 rounded-lg border border-slate-200 bg-slate-50">
|
||||||
|
<div className="flex items-center gap-2 text-sm font-medium text-slate-700 mb-3">
|
||||||
|
<span>📊 投票</span>
|
||||||
|
</div>
|
||||||
|
{seg.data?.options?.map((opt: any, j: number) => (
|
||||||
|
<div key={j} className="py-1.5 text-slate-600">
|
||||||
|
{j + 1}. {opt.content}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 图片展示 */}
|
{/* 图片展示 */}
|
||||||
|
|||||||
@@ -58,17 +58,25 @@ export interface PostImage {
|
|||||||
height?: number
|
height?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 消息段类型
|
||||||
|
export interface MessageSegment {
|
||||||
|
type: string
|
||||||
|
data: Record<string, any>
|
||||||
|
}
|
||||||
|
|
||||||
// 帖子
|
// 帖子
|
||||||
export interface Post {
|
export interface Post {
|
||||||
id: string
|
id: string
|
||||||
title: string
|
title: string
|
||||||
content: string
|
content: string
|
||||||
|
segments?: MessageSegment[]
|
||||||
author: User
|
author: User
|
||||||
status: PostStatus
|
status: PostStatus
|
||||||
likes_count: number
|
likes_count: number
|
||||||
comments_count: number
|
comments_count: number
|
||||||
views_count: number
|
views_count: number
|
||||||
images?: PostImage[]
|
images?: PostImage[]
|
||||||
|
is_vote?: boolean
|
||||||
created_at: string
|
created_at: string
|
||||||
updated_at: string
|
updated_at: string
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user