polish(frontend): 侧栏与徽章可读性,智能悬停提示
降权全站字重;优化右栏公告/数据块;类型状态徽章用固定浅色区分;TipHost 仅在文案不一致、非文字或省略时显示。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -107,7 +107,7 @@ function PendingBadge({ count }: { count: number }) {
|
||||
if (count <= 0) return null;
|
||||
return (
|
||||
<span
|
||||
className="ml-auto shrink-0 min-w-[20px] h-5 px-1.5 rounded-full inline-flex items-center justify-center text-[10.5px] font-extrabold tabular-nums"
|
||||
className="ml-auto shrink-0 min-w-[20px] h-5 px-1.5 rounded-full inline-flex items-center justify-center text-[10.5px] font-medium tabular-nums"
|
||||
style={{ background: "var(--gold-soft)", color: "var(--gold)" }}
|
||||
aria-label={`${count} 条待审核`}
|
||||
>
|
||||
@@ -140,7 +140,7 @@ export function AdminSidebar({ user }: { user: User }) {
|
||||
<span className="block text-[14.5px] font-extrabold tracking-tight truncate" style={{ color: "var(--ink)" }}>
|
||||
管理面板
|
||||
</span>
|
||||
<span className="block text-[10.5px] font-semibold mt-0.5 tracking-wide" style={{ color: "var(--ink-3)" }}>
|
||||
<span className="block text-[10.5px] mt-0.5 tracking-wide" style={{ color: "var(--ink-3)" }}>
|
||||
J13 ADMIN
|
||||
</span>
|
||||
</span>
|
||||
@@ -159,7 +159,7 @@ export function AdminSidebar({ user }: { user: User }) {
|
||||
key={item.href}
|
||||
href={item.href}
|
||||
aria-current={active ? "page" : undefined}
|
||||
className="flex items-center gap-2.5 px-3 py-2.5 rounded-xl text-[13.5px] font-semibold transition-colors"
|
||||
className="flex items-center gap-2.5 px-3 py-2.5 rounded-xl text-[13.5px] transition-colors"
|
||||
style={
|
||||
active
|
||||
? { background: "var(--nav-active-bg)", color: "var(--nav-active-ink)" }
|
||||
@@ -178,7 +178,7 @@ export function AdminSidebar({ user }: { user: User }) {
|
||||
<div className="pt-2" style={{ borderTop: "1px solid var(--line)" }}>
|
||||
<Link
|
||||
href="/"
|
||||
className="flex items-center gap-2.5 px-3 py-2.5 rounded-xl text-[13.5px] font-semibold transition-colors hover:bg-[var(--panel-2)]"
|
||||
className="flex items-center gap-2.5 px-3 py-2.5 rounded-xl text-[13.5px] transition-colors hover:bg-[var(--panel-2)]"
|
||||
style={{ color: "var(--ink-2)" }}
|
||||
>
|
||||
<ArrowLeft size={16} className="shrink-0" style={{ color: "var(--ink-3)" }} />
|
||||
@@ -190,10 +190,10 @@ export function AdminSidebar({ user }: { user: User }) {
|
||||
<div className="flex items-center gap-2.5 mt-2 px-2.5 py-2.5 rounded-xl" style={{ background: "var(--panel-2)" }}>
|
||||
<Avatar name={user.nickname || user.username} src={user.avatar || undefined} size={32} />
|
||||
<div className="min-w-0 leading-tight">
|
||||
<p className="text-[13px] font-bold truncate" style={{ color: "var(--ink)" }}>
|
||||
<p className="text-[13px] truncate" style={{ color: "var(--ink)" }}>
|
||||
{user.nickname}
|
||||
</p>
|
||||
<p className="text-[11px] font-semibold truncate" style={{ color: "var(--ink-3)" }}>
|
||||
<p className="text-[11px] truncate" style={{ color: "var(--ink-3)" }}>
|
||||
@{user.username}
|
||||
</p>
|
||||
</div>
|
||||
@@ -234,7 +234,7 @@ export function AdminNavMobile({ user }: { user: User }) {
|
||||
key={item.href}
|
||||
href={item.href}
|
||||
aria-current={active ? "page" : undefined}
|
||||
className="shrink-0 inline-flex items-center gap-1.5 px-3.5 py-2 rounded-full text-[13px] font-semibold transition-colors"
|
||||
className="shrink-0 inline-flex items-center gap-1.5 px-3.5 py-2 rounded-full text-[13px] transition-colors"
|
||||
style={
|
||||
active
|
||||
? { background: "var(--nav-active-bg)", color: "var(--nav-active-ink)" }
|
||||
@@ -245,7 +245,7 @@ export function AdminNavMobile({ user }: { user: User }) {
|
||||
{item.label}
|
||||
{item.pendingBadge && pendingTotal > 0 && (
|
||||
<span
|
||||
className="min-w-[18px] h-[18px] px-1 rounded-full inline-flex items-center justify-center text-[10px] font-extrabold tabular-nums"
|
||||
className="min-w-[18px] h-[18px] px-1 rounded-full inline-flex items-center justify-center text-[10px] font-medium tabular-nums"
|
||||
style={{ background: "var(--gold-soft)", color: "var(--gold)" }}
|
||||
>
|
||||
{pendingTotal > 99 ? "99+" : pendingTotal}
|
||||
@@ -256,7 +256,7 @@ export function AdminNavMobile({ user }: { user: User }) {
|
||||
})}
|
||||
<Link
|
||||
href="/"
|
||||
className="shrink-0 inline-flex items-center gap-1.5 px-3.5 py-2 rounded-full text-[13px] font-semibold transition-colors"
|
||||
className="shrink-0 inline-flex items-center gap-1.5 px-3.5 py-2 rounded-full text-[13px] transition-colors"
|
||||
style={{ color: "var(--ink-2)", background: "var(--panel)", border: "1px solid var(--line)" }}
|
||||
>
|
||||
<ArrowLeft size={14} />
|
||||
|
||||
@@ -154,7 +154,7 @@ export default function AnnouncementAdmin() {
|
||||
{/* 编辑/新建表单 */}
|
||||
{form ? (
|
||||
<div className="panel p-5 lg:sticky lg:top-24">
|
||||
<p className="font-extrabold text-[15px] mb-4" style={{ color: "var(--ink)" }}>
|
||||
<p className="font-semibold text-[15px] mb-4" style={{ color: "var(--ink)" }}>
|
||||
{form.id == null ? "新建公告" : "编辑公告"}
|
||||
</p>
|
||||
|
||||
@@ -265,7 +265,7 @@ export default function AnnouncementAdmin() {
|
||||
>
|
||||
<FileText size={22} />
|
||||
</span>
|
||||
<p className="font-bold text-[14.5px]" style={{ color: "var(--ink)" }}>
|
||||
<p className="text-[14.5px]" style={{ color: "var(--ink)" }}>
|
||||
公告编辑器
|
||||
</p>
|
||||
<p className="meta mt-1.5 text-[12.5px] leading-relaxed">
|
||||
@@ -285,7 +285,7 @@ export default function AnnouncementAdmin() {
|
||||
className="px-4 sm:px-5 py-3.5 flex items-center justify-between"
|
||||
style={{ borderBottom: "1px solid var(--line)" }}
|
||||
>
|
||||
<span className="font-bold text-[15px]" style={{ color: "var(--ink)" }}>
|
||||
<span className="font-medium text-[15px]" style={{ color: "var(--ink)" }}>
|
||||
全部公告
|
||||
</span>
|
||||
<span className="meta text-[12px] tabular-nums">共 {list.length} 条</span>
|
||||
@@ -311,7 +311,7 @@ export default function AnnouncementAdmin() {
|
||||
<span className={`j13-anno-tag is-${a.tag_color}`}>{a.tag}</span>
|
||||
{!a.published && (
|
||||
<span
|
||||
className="inline-flex items-center rounded-full px-2 py-px text-[10.5px] font-bold leading-[1.55]"
|
||||
className="inline-flex items-center rounded-full px-2 py-px text-[10.5px] leading-[1.55]"
|
||||
style={{ background: "var(--gold-soft)", color: "var(--gold)" }}
|
||||
>
|
||||
草稿
|
||||
@@ -320,7 +320,7 @@ export default function AnnouncementAdmin() {
|
||||
<span className="meta text-[11px] tabular-nums">{formatDate(a.created_at)}</span>
|
||||
</div>
|
||||
<p
|
||||
className="mt-1 truncate text-[14px] font-semibold"
|
||||
className="mt-1 truncate text-[14px]"
|
||||
style={{ color: "var(--ink)" }}
|
||||
data-tip={a.title}
|
||||
>
|
||||
|
||||
@@ -101,7 +101,7 @@ export default function AppearanceAdmin({ initialAccent }: { initialAccent: stri
|
||||
|
||||
{/* 预设色板 */}
|
||||
<section className="j13-module p-5 sm:p-6 mb-5">
|
||||
<h2 className="text-[15px] font-extrabold mb-1" style={{ color: "var(--ink)" }}>
|
||||
<h2 className="text-[15px] font-semibold mb-1" style={{ color: "var(--ink)" }}>
|
||||
预设主题色
|
||||
</h2>
|
||||
<p className="meta text-[12.5px] mb-4">浅/暗两套色板均已调校,保证文字对比度达标</p>
|
||||
@@ -139,7 +139,7 @@ export default function AppearanceAdmin({ initialAccent }: { initialAccent: stri
|
||||
)}
|
||||
</span>
|
||||
<span
|
||||
className="text-[12px] font-bold leading-none"
|
||||
className="text-[12px] leading-none"
|
||||
style={{ color: active ? "var(--accent)" : "var(--ink-2)" }}
|
||||
>
|
||||
{p.name}
|
||||
@@ -152,7 +152,7 @@ export default function AppearanceAdmin({ initialAccent }: { initialAccent: stri
|
||||
|
||||
{/* 自定义颜色 */}
|
||||
<section className="j13-module p-5 sm:p-6 mb-5">
|
||||
<h2 className="text-[15px] font-extrabold mb-1 flex items-center gap-1.5" style={{ color: "var(--ink)" }}>
|
||||
<h2 className="text-[15px] font-semibold mb-1 flex items-center gap-1.5" style={{ color: "var(--ink)" }}>
|
||||
<Pipette size={15} /> 自定义颜色
|
||||
</h2>
|
||||
<p className="meta text-[12.5px] mb-4">
|
||||
@@ -220,14 +220,14 @@ export default function AppearanceAdmin({ initialAccent }: { initialAccent: stri
|
||||
|
||||
{/* 实时预览(整页已实时换肤,此处展示典型组件) */}
|
||||
<section className="j13-module p-5 sm:p-6 mb-6">
|
||||
<h2 className="text-[15px] font-extrabold mb-4" style={{ color: "var(--ink)" }}>实时预览</h2>
|
||||
<h2 className="text-[15px] font-semibold mb-4" style={{ color: "var(--ink)" }}>实时预览</h2>
|
||||
<div className="rounded-[var(--r-sm)] border p-4 space-y-4" style={{ borderColor: "var(--line)", background: "var(--bg)" }}>
|
||||
<div className="flex flex-wrap items-center gap-2.5">
|
||||
<button type="button" className="btn btn-primary btn-sm">
|
||||
<Save size={14} /> 主按钮
|
||||
</button>
|
||||
<button type="button" className="btn btn-line btn-sm">次要按钮</button>
|
||||
<a className="text-[13px] font-bold" style={{ color: "var(--accent)" }} href="#preview" onClick={(e) => e.preventDefault()}>
|
||||
<a className="text-[13px]" style={{ color: "var(--accent)" }} href="#preview" onClick={(e) => e.preventDefault()}>
|
||||
文字链接示例
|
||||
</a>
|
||||
</div>
|
||||
@@ -235,7 +235,7 @@ export default function AppearanceAdmin({ initialAccent }: { initialAccent: stri
|
||||
<span className="j13-tab" aria-selected="false">未选中</span>
|
||||
<span className="j13-tab" aria-selected="true">选中标签</span>
|
||||
<span
|
||||
className="inline-flex items-center rounded-full px-3.5 py-2 text-[13.5px] font-semibold"
|
||||
className="inline-flex items-center rounded-full px-3.5 py-2 text-[13.5px]"
|
||||
style={{ background: "var(--nav-active-bg)", color: "var(--nav-active-ink)" }}
|
||||
>
|
||||
导航选中态
|
||||
@@ -246,7 +246,7 @@ export default function AppearanceAdmin({ initialAccent }: { initialAccent: stri
|
||||
<span className="j13-bcard-ico" style={{ background: "var(--accent-soft)", color: "var(--accent)" }}>
|
||||
<Palette size={16} />
|
||||
</span>
|
||||
<span className="j13-bcard-name text-[13px] font-bold flex-1">选中的板块卡片</span>
|
||||
<span className="j13-bcard-name text-[13px] flex-1">选中的板块卡片</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -102,7 +102,7 @@ export default async function AdminEconomyPage() {
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-5">
|
||||
<section className="panel overflow-hidden">
|
||||
<div
|
||||
className="px-5 py-3.5 font-bold text-[15px]"
|
||||
className="px-5 py-3.5 font-medium text-[15px]"
|
||||
style={{ borderBottom: "1px solid var(--line)", color: "var(--ink)" }}
|
||||
>
|
||||
帖子类型分布
|
||||
@@ -117,7 +117,7 @@ export default async function AdminEconomyPage() {
|
||||
className="px-5 py-3 flex items-center justify-between"
|
||||
style={{ borderTop: "1px solid var(--line)" }}
|
||||
>
|
||||
<span className="font-semibold text-[14px]" style={{ color: "var(--ink)" }}>
|
||||
<span className="text-[14px]" style={{ color: "var(--ink)" }}>
|
||||
{postTypeLabel(t)}
|
||||
</span>
|
||||
<span className="tabular-nums meta">{fmt(n)}</span>
|
||||
@@ -129,7 +129,7 @@ export default async function AdminEconomyPage() {
|
||||
|
||||
<section className="panel overflow-hidden">
|
||||
<div
|
||||
className="px-5 py-3.5 font-bold text-[15px]"
|
||||
className="px-5 py-3.5 font-medium text-[15px]"
|
||||
style={{ borderBottom: "1px solid var(--line)", color: "var(--ink)" }}
|
||||
>
|
||||
最近流水
|
||||
@@ -145,14 +145,14 @@ export default async function AdminEconomyPage() {
|
||||
style={{ borderTop: "1px solid var(--line)" }}
|
||||
>
|
||||
<div className="min-w-0">
|
||||
<p className="font-semibold text-[13.5px] truncate" style={{ color: "var(--ink)" }}>
|
||||
<p className="text-[13.5px] truncate" style={{ color: "var(--ink)" }}>
|
||||
{pointReasonLabel(it.reason)}
|
||||
{it.note ? ` · ${it.note}` : ""}
|
||||
</p>
|
||||
<p className="meta text-[12px] mt-0.5">{formatRelative(it.created_at)}</p>
|
||||
</div>
|
||||
<span
|
||||
className="tabular-nums font-bold shrink-0"
|
||||
className="tabular-nums font-medium shrink-0"
|
||||
style={{ color: it.delta >= 0 ? "var(--accent)" : "var(--danger)" }}
|
||||
>
|
||||
{it.delta >= 0 ? `+${it.delta}` : it.delta}
|
||||
|
||||
@@ -231,7 +231,7 @@ export default function ModerationAdmin({
|
||||
role="tab"
|
||||
aria-selected={active}
|
||||
onClick={() => switchTab(t.key)}
|
||||
className="inline-flex items-center gap-1.5 px-3.5 py-2 rounded-full text-[13px] font-semibold transition-colors"
|
||||
className="inline-flex items-center gap-1.5 px-3.5 py-2 rounded-full text-[13px] transition-colors"
|
||||
style={
|
||||
active
|
||||
? { background: "var(--nav-active-bg)", color: "var(--nav-active-ink)" }
|
||||
@@ -281,7 +281,7 @@ export default function ModerationAdmin({
|
||||
<Link
|
||||
href={`/post/${p.id}`}
|
||||
target="_blank"
|
||||
className="block text-[15.5px] font-extrabold tracking-tight leading-snug hover:text-[var(--accent)] transition-colors"
|
||||
className="block text-[15.5px] font-semibold tracking-tight leading-snug hover:text-[var(--accent)] transition-colors"
|
||||
style={{ color: "var(--ink)" }}
|
||||
>
|
||||
{p.title}
|
||||
@@ -298,7 +298,7 @@ export default function ModerationAdmin({
|
||||
>
|
||||
<div className="flex items-center gap-2 min-w-0">
|
||||
<Avatar name={p.user.nickname || p.user.username} src={p.user.avatar || undefined} size={26} />
|
||||
<span className="text-[12.5px] font-semibold truncate" style={{ color: "var(--ink-2)" }}>
|
||||
<span className="text-[12.5px] truncate" style={{ color: "var(--ink-2)" }}>
|
||||
{p.user.nickname}
|
||||
</span>
|
||||
<span className="meta text-[11.5px] truncate">@{p.user.username}</span>
|
||||
@@ -350,7 +350,7 @@ export default function ModerationAdmin({
|
||||
<Avatar name={c.user.nickname || c.user.username} src={c.user.avatar || undefined} size={34} />
|
||||
<div className="min-w-0 flex-1">
|
||||
<div className="flex items-center gap-2 flex-wrap">
|
||||
<span className="text-[13.5px] font-bold truncate" style={{ color: "var(--ink)" }}>
|
||||
<span className="text-[13.5px] truncate" style={{ color: "var(--ink)" }}>
|
||||
{c.user.nickname}
|
||||
</span>
|
||||
<Link
|
||||
@@ -448,7 +448,7 @@ function EmptyQueue({ icon, text }: { icon: React.ReactNode; text: string }) {
|
||||
>
|
||||
{icon}
|
||||
</span>
|
||||
<p className="font-bold text-[14.5px]" style={{ color: "var(--ink)" }}>
|
||||
<p className="text-[14.5px]" style={{ color: "var(--ink)" }}>
|
||||
{text}
|
||||
</p>
|
||||
<p className="meta mt-1.5 text-[12.5px]">新提交的内容会按时间顺序出现在这里</p>
|
||||
|
||||
@@ -41,7 +41,7 @@ function PanelHead({ title, extra }: { title: string; extra?: string }) {
|
||||
className="px-4 sm:px-5 py-3.5 flex items-center justify-between gap-2"
|
||||
style={{ borderBottom: "1px solid var(--line)" }}
|
||||
>
|
||||
<span className="font-bold text-[15px]" style={{ color: "var(--ink)" }}>
|
||||
<span className="font-medium text-[15px]" style={{ color: "var(--ink)" }}>
|
||||
{title}
|
||||
</span>
|
||||
{extra && (
|
||||
@@ -76,19 +76,19 @@ function PendingWorkPanel({ counts }: { counts: PendingCounts }) {
|
||||
</span>
|
||||
<div className="min-w-0 flex-1">
|
||||
<div className="flex items-center gap-2 flex-wrap">
|
||||
<p className="text-[15px] font-extrabold" style={{ color: "var(--ink)" }}>
|
||||
<p className="text-[15px] font-semibold" style={{ color: "var(--ink)" }}>
|
||||
内容审核
|
||||
</p>
|
||||
{idle ? (
|
||||
<span
|
||||
className="text-[11.5px] font-semibold px-2 py-0.5 rounded-full"
|
||||
className="text-[11.5px] px-2 py-0.5 rounded-full"
|
||||
style={{ background: "var(--tint)", color: "var(--ink-3)" }}
|
||||
>
|
||||
暂无待审
|
||||
</span>
|
||||
) : (
|
||||
<span
|
||||
className="text-[11.5px] font-semibold px-2 py-0.5 rounded-full tabular-nums"
|
||||
className="text-[11.5px] px-2 py-0.5 rounded-full tabular-nums"
|
||||
style={{ background: "var(--ok-soft)", color: "var(--ok)" }}
|
||||
>
|
||||
{fmt(total)} 项待处理
|
||||
@@ -170,7 +170,7 @@ export default async function AdminDashboardPage() {
|
||||
className="panel mb-4 px-4 sm:px-5 py-4 flex items-center justify-between gap-3 transition-colors hover:bg-[var(--panel-2)]"
|
||||
>
|
||||
<div>
|
||||
<p className="font-bold text-[15px]" style={{ color: "var(--ink)" }}>
|
||||
<p className="font-medium text-[15px]" style={{ color: "var(--ink)" }}>
|
||||
积分与类型帖看板
|
||||
</p>
|
||||
<p className="meta text-[12.5px] mt-0.5">余额合计、悬赏托管、类型分布与最近流水</p>
|
||||
@@ -202,7 +202,7 @@ export default async function AdminDashboardPage() {
|
||||
/>
|
||||
<span className="min-w-0 flex-1 leading-tight">
|
||||
<span
|
||||
className="block text-[13.5px] font-bold truncate"
|
||||
className="block text-[13.5px] truncate"
|
||||
style={{ color: "var(--ink)" }}
|
||||
>
|
||||
{u.nickname}
|
||||
@@ -215,7 +215,7 @@ export default async function AdminDashboardPage() {
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
className="shrink-0 inline-flex items-center gap-1 text-[11.5px] font-semibold"
|
||||
className="shrink-0 inline-flex items-center gap-1 text-[11.5px]"
|
||||
style={{ color: "var(--ink-3)" }}
|
||||
>
|
||||
<UserPlus size={12} aria-hidden="true" />
|
||||
@@ -227,7 +227,7 @@ export default async function AdminDashboardPage() {
|
||||
{canUsers && (
|
||||
<Link
|
||||
href="/admin/users"
|
||||
className="flex items-center justify-center gap-1.5 px-4 py-3 text-[13px] font-semibold transition-colors hover:bg-[var(--panel-2)]"
|
||||
className="flex items-center justify-center gap-1.5 px-4 py-3 text-[13px] transition-colors hover:bg-[var(--panel-2)]"
|
||||
style={{
|
||||
borderTop: "1px solid var(--line)",
|
||||
color: "var(--accent)",
|
||||
@@ -262,13 +262,13 @@ export default async function AdminDashboardPage() {
|
||||
<span className="min-w-0 flex-1">
|
||||
<span className="flex items-center justify-between gap-2">
|
||||
<span
|
||||
className="text-[13.5px] font-bold truncate"
|
||||
className="text-[13.5px] truncate"
|
||||
style={{ color: "var(--ink)" }}
|
||||
>
|
||||
{b.name}
|
||||
</span>
|
||||
<span
|
||||
className="text-[12px] font-bold tabular-nums shrink-0"
|
||||
className="text-[12px] tabular-nums shrink-0"
|
||||
style={{ color: "var(--ink-2)" }}
|
||||
>
|
||||
{fmt(b.post_count)}
|
||||
|
||||
@@ -90,13 +90,13 @@ function MiniStat({
|
||||
{icon}
|
||||
</span>
|
||||
<div className="min-w-0 leading-tight">
|
||||
<p className="text-[11.5px] font-semibold truncate" style={{ color: "var(--ink-3)" }}>
|
||||
<p className="text-[11.5px] truncate" style={{ color: "var(--ink-3)" }}>
|
||||
{label}
|
||||
</p>
|
||||
<p className="text-[19px] font-extrabold tabular-nums" style={{ color: "var(--ink)" }}>
|
||||
{value}
|
||||
{sub && (
|
||||
<span className="ml-1.5 text-[11px] font-semibold" style={{ color: "var(--ink-3)" }}>
|
||||
<span className="ml-1.5 text-[11px]" style={{ color: "var(--ink-3)" }}>
|
||||
{sub}
|
||||
</span>
|
||||
)}
|
||||
@@ -279,7 +279,7 @@ export default function UsersAdmin({
|
||||
if (u.banned) {
|
||||
return (
|
||||
<span
|
||||
className="inline-flex items-center gap-1.5 text-[12.5px] font-bold"
|
||||
className="inline-flex items-center gap-1.5 text-[12.5px]"
|
||||
style={{ color: "var(--danger)" }}
|
||||
data-tip="账号已封禁"
|
||||
>
|
||||
@@ -291,7 +291,7 @@ export default function UsersAdmin({
|
||||
if (u.online) {
|
||||
return (
|
||||
<span
|
||||
className="inline-flex items-center gap-1.5 text-[12.5px] font-bold"
|
||||
className="inline-flex items-center gap-1.5 text-[12.5px]"
|
||||
style={{ color: "var(--ok)" }}
|
||||
data-tip={u.last_seen_at ? `最近活跃 ${formatRelative(u.last_seen_at)}` : "在线"}
|
||||
>
|
||||
@@ -302,7 +302,7 @@ export default function UsersAdmin({
|
||||
}
|
||||
return (
|
||||
<span
|
||||
className="inline-flex items-center gap-1.5 text-[12.5px] font-semibold"
|
||||
className="inline-flex items-center gap-1.5 text-[12.5px]"
|
||||
style={{ color: "var(--ink-3)" }}
|
||||
data-tip={u.last_seen_at ? `最近活跃 ${formatRelative(u.last_seen_at)}` : "从未活跃"}
|
||||
>
|
||||
@@ -325,7 +325,7 @@ export default function UsersAdmin({
|
||||
aria-label={`查看 ${u.username} 的登录历史`}
|
||||
>
|
||||
<span
|
||||
className="block text-[12px] font-semibold tabular-nums truncate group-hover:text-[var(--accent)]"
|
||||
className="block text-[12px] tabular-nums truncate group-hover:text-[var(--accent)]"
|
||||
style={{ color: u.last_login_ip ? "var(--ink-2)" : "var(--ink-3)" }}
|
||||
>
|
||||
{u.last_login_ip || "暂无记录"}
|
||||
@@ -371,7 +371,7 @@ export default function UsersAdmin({
|
||||
<div className="flex items-center gap-1 justify-end">
|
||||
{auditBtn}
|
||||
<span
|
||||
className="inline-flex items-center rounded-full px-2.5 py-1 text-[11px] font-bold whitespace-nowrap"
|
||||
className="inline-flex items-center rounded-full px-2.5 py-1 text-[11px] whitespace-nowrap"
|
||||
style={{ background: "var(--panel-2)", color: "var(--ink-3)" }}
|
||||
>
|
||||
当前账号
|
||||
@@ -540,7 +540,7 @@ export default function UsersAdmin({
|
||||
type="button"
|
||||
aria-pressed={active}
|
||||
onClick={() => changeFilter(t.key)}
|
||||
className="inline-flex items-center gap-1.5 px-3.5 py-2 rounded-full text-[13px] font-semibold transition-colors"
|
||||
className="inline-flex items-center gap-1.5 px-3.5 py-2 rounded-full text-[13px] transition-colors"
|
||||
style={
|
||||
active
|
||||
? { background: "var(--nav-active-bg)", color: "var(--nav-active-ink)" }
|
||||
@@ -570,7 +570,7 @@ export default function UsersAdmin({
|
||||
className="px-4 sm:px-5 py-3.5 flex items-center justify-between gap-2 flex-wrap"
|
||||
style={{ borderBottom: "1px solid var(--line)" }}
|
||||
>
|
||||
<span className="font-bold text-[15px]" style={{ color: "var(--ink)" }}>
|
||||
<span className="font-medium text-[15px]" style={{ color: "var(--ink)" }}>
|
||||
{appliedQ ? (
|
||||
<>
|
||||
搜索“<span style={{ color: "var(--accent)" }}>{appliedQ}</span>”
|
||||
@@ -591,7 +591,7 @@ export default function UsersAdmin({
|
||||
{/* 桌面表头 */}
|
||||
{users.length > 0 && (
|
||||
<div
|
||||
className={`${rowGrid} text-[11.5px] font-semibold uppercase tracking-wide`}
|
||||
className={`${rowGrid} text-[11.5px] uppercase tracking-wide`}
|
||||
style={{ color: "var(--ink-3)", borderBottom: "1px solid var(--line)", background: "var(--panel-2)" }}
|
||||
>
|
||||
<span>成员</span>
|
||||
@@ -618,7 +618,7 @@ export default function UsersAdmin({
|
||||
>
|
||||
<UsersIcon size={22} />
|
||||
</span>
|
||||
<p className="font-bold text-[14.5px]" style={{ color: "var(--ink)" }}>
|
||||
<p className="text-[14.5px]" style={{ color: "var(--ink)" }}>
|
||||
{hasFilter ? "没有匹配的成员" : "暂无成员"}
|
||||
</p>
|
||||
<p className="meta mt-1.5 text-[12.5px]">
|
||||
@@ -635,7 +635,7 @@ export default function UsersAdmin({
|
||||
<div className="flex items-center gap-2.5 min-w-0">
|
||||
<Avatar name={u.nickname || u.username} src={u.avatar || undefined} size={34} />
|
||||
<div className="min-w-0 leading-tight">
|
||||
<p className="text-[13.5px] font-bold truncate" style={{ color: "var(--ink)" }}>
|
||||
<p className="text-[13.5px] truncate" style={{ color: "var(--ink)" }}>
|
||||
{u.nickname}
|
||||
</p>
|
||||
<p className="text-[11.5px] truncate" style={{ color: "var(--ink-3)" }}>
|
||||
@@ -651,7 +651,7 @@ export default function UsersAdmin({
|
||||
<div className="min-w-0">{renderLastLogin(u)}</div>
|
||||
<div className="hidden xl:block">
|
||||
<p
|
||||
className="text-[12.5px] font-semibold tabular-nums"
|
||||
className="text-[12.5px] tabular-nums"
|
||||
style={{ color: "var(--ink-2)" }}
|
||||
data-tip="仅统计已发布内容;驳回/删除见内容档案"
|
||||
>
|
||||
@@ -670,7 +670,7 @@ export default function UsersAdmin({
|
||||
<Avatar name={u.nickname || u.username} src={u.avatar || undefined} size={38} />
|
||||
<div className="min-w-0 flex-1 leading-tight">
|
||||
<div className="flex items-center gap-2 flex-wrap">
|
||||
<span className="text-[14px] font-bold truncate" style={{ color: "var(--ink)" }}>
|
||||
<span className="text-[14px] truncate" style={{ color: "var(--ink)" }}>
|
||||
{u.nickname}
|
||||
</span>
|
||||
{u.role !== ROLES.USER && (
|
||||
@@ -678,7 +678,7 @@ export default function UsersAdmin({
|
||||
)}
|
||||
{isSelf && (
|
||||
<span
|
||||
className="inline-flex items-center rounded-full px-2 py-px text-[10.5px] font-bold leading-[1.55]"
|
||||
className="inline-flex items-center rounded-full px-2 py-px text-[10.5px] leading-[1.55]"
|
||||
style={{ background: "var(--panel-2)", color: "var(--ink-3)" }}
|
||||
>
|
||||
当前账号
|
||||
@@ -931,7 +931,7 @@ function RoleAssignDialog({
|
||||
</>
|
||||
}
|
||||
>
|
||||
<p className="mb-2 text-[12.5px] font-bold" style={{ color: "var(--ink)" }}>
|
||||
<p className="mb-2 text-[12.5px]" style={{ color: "var(--ink)" }}>
|
||||
管理角色
|
||||
</p>
|
||||
<div className="flex flex-col gap-2" role="radiogroup" aria-label="管理角色">
|
||||
@@ -964,7 +964,7 @@ function RoleAssignDialog({
|
||||
{o.value === ROLES.USER ? <UsersIcon size={18} /> : <ShieldCheck size={18} />}
|
||||
</span>
|
||||
<span className="min-w-0 flex-1">
|
||||
<span className="block text-[13.5px] font-bold" style={{ color: "var(--ink)" }}>
|
||||
<span className="block text-[13.5px] font-medium" style={{ color: "var(--ink)" }}>
|
||||
{o.title}
|
||||
</span>
|
||||
<span className="block mt-1 text-[12px] leading-relaxed" style={{ color: "var(--ink-3)" }}>
|
||||
@@ -985,7 +985,7 @@ function RoleAssignDialog({
|
||||
|
||||
{role === ROLES.BOARD_ADMIN && (
|
||||
<div className="mt-4">
|
||||
<p className="text-[12.5px] font-bold mb-2" style={{ color: "var(--ink)" }}>
|
||||
<p className="text-[12.5px] mb-2" style={{ color: "var(--ink)" }}>
|
||||
授权板块 <span style={{ color: "var(--danger)" }}>*</span>
|
||||
</p>
|
||||
{boards.length === 0 ? (
|
||||
@@ -1001,7 +1001,7 @@ function RoleAssignDialog({
|
||||
role="checkbox"
|
||||
aria-checked={checked}
|
||||
onClick={() => toggleBoard(b.id)}
|
||||
className="flex items-center gap-2 rounded-lg px-3 py-2 text-[12.5px] font-semibold transition-colors"
|
||||
className="flex items-center gap-2 rounded-lg px-3 py-2 text-[12.5px] transition-colors"
|
||||
style={{
|
||||
border: `1.5px solid ${checked ? "var(--accent)" : "var(--line)"}`,
|
||||
background: checked ? "var(--accent-soft)" : "var(--panel-2)",
|
||||
@@ -1026,7 +1026,7 @@ function RoleAssignDialog({
|
||||
</div>
|
||||
)}
|
||||
{err && (
|
||||
<p className="mt-2 text-[12px] font-semibold" style={{ color: "var(--danger)" }}>
|
||||
<p className="mt-2 text-[12px]" style={{ color: "var(--danger)" }}>
|
||||
{err}
|
||||
</p>
|
||||
)}
|
||||
@@ -1034,7 +1034,7 @@ function RoleAssignDialog({
|
||||
)}
|
||||
|
||||
<div className="mt-5 pt-4" style={{ borderTop: "1px solid var(--line)" }}>
|
||||
<p className="text-[12.5px] font-bold mb-2" style={{ color: "var(--ink)" }}>
|
||||
<p className="text-[12.5px] mb-2" style={{ color: "var(--ink)" }}>
|
||||
账号状态
|
||||
</p>
|
||||
<div className="grid grid-cols-2 gap-2" role="radiogroup" aria-label="账号状态">
|
||||
@@ -1050,7 +1050,7 @@ function RoleAssignDialog({
|
||||
}}
|
||||
>
|
||||
<span
|
||||
className="flex items-center gap-1.5 text-[13px] font-bold"
|
||||
className="flex items-center gap-1.5 text-[13px] font-medium"
|
||||
style={{ color: !banned ? "var(--ok)" : "var(--ink)" }}
|
||||
>
|
||||
<RotateCcw size={14} /> 正常
|
||||
@@ -1071,7 +1071,7 @@ function RoleAssignDialog({
|
||||
}}
|
||||
>
|
||||
<span
|
||||
className="flex items-center gap-1.5 text-[13px] font-bold"
|
||||
className="flex items-center gap-1.5 text-[13px] font-medium"
|
||||
style={{ color: banned ? "var(--danger)" : "var(--ink)" }}
|
||||
>
|
||||
<Ban size={14} /> 封禁
|
||||
@@ -1199,10 +1199,10 @@ function LoginLogsDialog({ target, onClose }: { target: AdminUser | null; onClos
|
||||
aria-hidden
|
||||
/>
|
||||
<div className="min-w-0 flex-1">
|
||||
<p className="text-[12.5px] font-semibold tabular-nums truncate" style={{ color: "var(--ink)" }}>
|
||||
<p className="text-[12.5px] tabular-nums truncate" style={{ color: "var(--ink)" }}>
|
||||
{log.ip || "未知 IP"}
|
||||
<span
|
||||
className="ml-2 inline-flex items-center rounded-full px-1.5 py-px text-[10.5px] font-bold"
|
||||
className="ml-2 inline-flex items-center rounded-full px-1.5 py-px text-[10.5px]"
|
||||
style={
|
||||
log.success
|
||||
? { background: "var(--ok-soft)", color: "var(--ok)" }
|
||||
|
||||
@@ -60,7 +60,7 @@ function StatusChip({
|
||||
{chips.map((c) => (
|
||||
<span
|
||||
key={c.label}
|
||||
className="inline-flex items-center rounded-full px-2 py-0.5 text-[11px] font-bold"
|
||||
className="inline-flex items-center rounded-full px-2 py-0.5 text-[11px]"
|
||||
style={{ background: c.soft, color: c.color }}
|
||||
>
|
||||
{c.label}
|
||||
@@ -175,7 +175,7 @@ export default function UserAuditClient({
|
||||
<div>
|
||||
<Link
|
||||
href="/admin/users"
|
||||
className="inline-flex items-center gap-1.5 text-[13px] font-semibold mb-4 hover:opacity-80"
|
||||
className="inline-flex items-center gap-1.5 text-[13px] mb-4 hover:opacity-80"
|
||||
style={{ color: "var(--accent)" }}
|
||||
>
|
||||
<ArrowLeft size={14} />
|
||||
@@ -196,7 +196,7 @@ export default function UserAuditClient({
|
||||
<RoleBadge role={u.role} />
|
||||
{u.banned && (
|
||||
<span
|
||||
className="text-[11px] font-bold px-2 py-0.5 rounded-full"
|
||||
className="text-[11px] px-2 py-0.5 rounded-full"
|
||||
style={{ background: "var(--danger-soft)", color: "var(--danger)" }}
|
||||
>
|
||||
已封禁
|
||||
@@ -251,7 +251,7 @@ export default function UserAuditClient({
|
||||
key={t.key}
|
||||
type="button"
|
||||
onClick={() => switchTab(t.key)}
|
||||
className="inline-flex items-center gap-1.5 px-3 py-2.5 text-[13px] font-bold whitespace-nowrap -mb-px"
|
||||
className="inline-flex items-center gap-1.5 px-3 py-2.5 text-[13px] whitespace-nowrap -mb-px"
|
||||
style={{
|
||||
color: active ? "var(--accent)" : "var(--ink-3)",
|
||||
borderBottom: active ? "2px solid var(--accent)" : "2px solid transparent",
|
||||
@@ -259,7 +259,7 @@ export default function UserAuditClient({
|
||||
>
|
||||
{t.icon}
|
||||
{t.label}
|
||||
<span className="tabular-nums font-semibold opacity-70">
|
||||
<span className="tabular-nums opacity-70">
|
||||
{tabCount(t.key).toLocaleString("zh-CN")}
|
||||
</span>
|
||||
</button>
|
||||
@@ -298,13 +298,13 @@ export default function UserAuditClient({
|
||||
)}
|
||||
</div>
|
||||
{p.deleted ? (
|
||||
<p className="text-[14px] font-bold" style={{ color: "var(--ink-3)" }}>
|
||||
<p className="text-[14px] font-medium" style={{ color: "var(--ink-3)" }}>
|
||||
{p.title || "(无标题)"}
|
||||
</p>
|
||||
) : (
|
||||
<Link
|
||||
href={`/post/${p.id}`}
|
||||
className="text-[14px] font-bold hover:underline"
|
||||
className="text-[14px] font-medium hover:underline"
|
||||
style={{ color: "var(--ink)" }}
|
||||
>
|
||||
{p.title || "(无标题)"}
|
||||
@@ -339,7 +339,7 @@ export default function UserAuditClient({
|
||||
) : (
|
||||
<Link
|
||||
href={`/post/${c.post_id}`}
|
||||
className="text-[11.5px] font-semibold hover:underline"
|
||||
className="text-[11.5px] hover:underline"
|
||||
style={{ color: "var(--accent)" }}
|
||||
>
|
||||
帖子 #{c.post_id}
|
||||
|
||||
@@ -381,7 +381,7 @@ export default function ChatShell({
|
||||
{query.trim() && (
|
||||
<div className="px-2 pb-2">
|
||||
<p
|
||||
className="px-2 py-1.5 text-[11px] font-bold uppercase tracking-wider"
|
||||
className="px-2 py-1.5 text-[11px] uppercase tracking-wider"
|
||||
style={{ color: "var(--ink-3)" }}
|
||||
>
|
||||
用户 {searching ? "…" : ""}
|
||||
@@ -397,7 +397,7 @@ export default function ChatShell({
|
||||
>
|
||||
<Avatar name={u.nickname || u.username} src={u.avatar || undefined} size={36} />
|
||||
<div className="min-w-0">
|
||||
<p className="text-[13px] font-semibold truncate" style={{ color: "var(--ink)" }}>
|
||||
<p className="text-[13px] truncate" style={{ color: "var(--ink)" }}>
|
||||
{u.nickname || u.username}
|
||||
</p>
|
||||
<p className="text-[11px] meta truncate">@{u.username}</p>
|
||||
@@ -406,7 +406,7 @@ export default function ChatShell({
|
||||
))
|
||||
)}
|
||||
<p
|
||||
className="px-2 py-1.5 mt-1 text-[11px] font-bold uppercase tracking-wider"
|
||||
className="px-2 py-1.5 mt-1 text-[11px] uppercase tracking-wider"
|
||||
style={{ color: "var(--ink-3)" }}
|
||||
>
|
||||
会话
|
||||
@@ -468,14 +468,14 @@ export default function ChatShell({
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="flex items-center gap-1.5">
|
||||
<span
|
||||
className="font-semibold text-[13.5px] truncate"
|
||||
className="text-[13.5px] truncate"
|
||||
style={{ color: "var(--ink)" }}
|
||||
>
|
||||
{roomTitle(room)}
|
||||
</span>
|
||||
{pinned && (
|
||||
<span
|
||||
className="inline-flex items-center gap-0.5 text-[10px] font-bold shrink-0 px-1.5 py-0.5 rounded"
|
||||
className="inline-flex items-center gap-0.5 text-[10px] shrink-0 px-1.5 py-0.5 rounded"
|
||||
style={{
|
||||
background: "var(--accent-soft)",
|
||||
color: "var(--accent)",
|
||||
@@ -486,12 +486,12 @@ export default function ChatShell({
|
||||
</span>
|
||||
)}
|
||||
{room.is_default && (
|
||||
<span className="text-[10px] font-bold shrink-0" style={{ color: "var(--gold)" }}>
|
||||
<span className="text-[10px] shrink-0" style={{ color: "var(--gold)" }}>
|
||||
大厅
|
||||
</span>
|
||||
)}
|
||||
{room.is_private && room.room_type === "group" && (
|
||||
<span className="text-[10px] font-bold shrink-0 meta">私密</span>
|
||||
<span className="text-[10px] shrink-0 meta">私密</span>
|
||||
)}
|
||||
<span className="ml-auto meta text-[10.5px] shrink-0">
|
||||
{formatTime(room.last_message?.created_at || room.updated_at)}
|
||||
@@ -501,7 +501,7 @@ export default function ChatShell({
|
||||
<p className="text-[12px] truncate meta flex-1">{roomPreview(room)}</p>
|
||||
{room.unread_count > 0 && (
|
||||
<span
|
||||
className="shrink-0 min-w-[18px] h-[18px] px-1 rounded-full text-[10px] font-bold flex items-center justify-center"
|
||||
className="shrink-0 min-w-[18px] h-[18px] px-1 rounded-full text-[10px] font-medium flex items-center justify-center"
|
||||
style={{ background: "var(--clay)", color: "#fff" }}
|
||||
>
|
||||
{room.unread_count > 99 ? "99+" : room.unread_count}
|
||||
@@ -530,7 +530,7 @@ export default function ChatShell({
|
||||
>
|
||||
<MessageSquare size={28} />
|
||||
</span>
|
||||
<p className="font-bold text-[16px]" style={{ color: "var(--ink)" }}>
|
||||
<p className="text-[16px]" style={{ color: "var(--ink)" }}>
|
||||
选择一个会话开始聊天
|
||||
</p>
|
||||
<p className="meta mt-1.5 text-[13px] max-w-xs">
|
||||
@@ -637,7 +637,7 @@ function CreateGroupModal({
|
||||
>
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<label className="block text-[12px] font-bold mb-1.5" style={{ color: "var(--ink-2)" }}>
|
||||
<label className="block text-[12px] mb-1.5" style={{ color: "var(--ink-2)" }}>
|
||||
群名称 <span style={{ color: "var(--danger)" }}>*</span>
|
||||
</label>
|
||||
<input
|
||||
@@ -652,7 +652,7 @@ function CreateGroupModal({
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-[12px] font-bold mb-1.5" style={{ color: "var(--ink-2)" }}>
|
||||
<label className="block text-[12px] mb-1.5" style={{ color: "var(--ink-2)" }}>
|
||||
群简介(选填)
|
||||
</label>
|
||||
<textarea
|
||||
@@ -665,14 +665,14 @@ function CreateGroupModal({
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-[12px] font-bold mb-2" style={{ color: "var(--ink-2)" }}>
|
||||
<label className="block text-[12px] mb-2" style={{ color: "var(--ink-2)" }}>
|
||||
可见性
|
||||
</label>
|
||||
<div className="flex gap-2">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setIsPrivate(false)}
|
||||
className="flex-1 h-9 rounded-lg text-[13px] font-semibold"
|
||||
className="flex-1 h-9 rounded-lg text-[13px]"
|
||||
style={{
|
||||
background: !isPrivate ? "var(--accent-soft)" : "var(--bg)",
|
||||
color: !isPrivate ? "var(--accent)" : "var(--ink-2)",
|
||||
@@ -684,7 +684,7 @@ function CreateGroupModal({
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setIsPrivate(true)}
|
||||
className="flex-1 h-9 rounded-lg text-[13px] font-semibold"
|
||||
className="flex-1 h-9 rounded-lg text-[13px]"
|
||||
style={{
|
||||
background: isPrivate ? "var(--accent-soft)" : "var(--bg)",
|
||||
color: isPrivate ? "var(--accent)" : "var(--ink-2)",
|
||||
@@ -697,7 +697,7 @@ function CreateGroupModal({
|
||||
</div>
|
||||
{isPrivate && (
|
||||
<div>
|
||||
<label className="block text-[12px] font-bold mb-1.5" style={{ color: "var(--ink-2)" }}>
|
||||
<label className="block text-[12px] mb-1.5" style={{ color: "var(--ink-2)" }}>
|
||||
初始成员(选填)
|
||||
</label>
|
||||
<input
|
||||
|
||||
@@ -130,14 +130,14 @@ function RecallNotice({
|
||||
uid > 0 ? (
|
||||
<Link
|
||||
href={`/u/${uid}`}
|
||||
className="font-semibold transition-opacity hover:opacity-80"
|
||||
className="transition-opacity hover:opacity-80"
|
||||
style={nameStyle}
|
||||
data-tip={`查看 ${name} 的主页`}
|
||||
>
|
||||
{name}
|
||||
</Link>
|
||||
) : (
|
||||
<span className="font-semibold" style={nameStyle}>
|
||||
<span style={nameStyle}>
|
||||
{name}
|
||||
</span>
|
||||
);
|
||||
@@ -966,7 +966,7 @@ export default function ChatRoomClient({
|
||||
</h1>
|
||||
{isDefault && (
|
||||
<span
|
||||
className="text-[10.5px] font-bold px-2 py-0.5 rounded-full"
|
||||
className="text-[10.5px] px-2 py-0.5 rounded-full"
|
||||
style={{ background: "var(--gold-soft)", color: "var(--gold)" }}
|
||||
>
|
||||
大厅
|
||||
@@ -974,7 +974,7 @@ export default function ChatRoomClient({
|
||||
)}
|
||||
{isPrivate && !isDirect && (
|
||||
<span
|
||||
className="text-[10.5px] font-bold px-2 py-0.5 rounded-full"
|
||||
className="text-[10.5px] px-2 py-0.5 rounded-full"
|
||||
style={{ background: "var(--panel-2)", color: "var(--ink-2)" }}
|
||||
>
|
||||
私密
|
||||
@@ -1056,7 +1056,7 @@ export default function ChatRoomClient({
|
||||
>
|
||||
{!canView ? (
|
||||
<div className="text-center py-12 px-4">
|
||||
<p className="font-semibold" style={{ color: "var(--ink)" }}>
|
||||
<p style={{ color: "var(--ink)" }}>
|
||||
{isPrivate ? "此群为私密群" : "预览群信息"}
|
||||
</p>
|
||||
<p className="meta mt-1.5">
|
||||
@@ -1123,7 +1123,7 @@ export default function ChatRoomClient({
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleReEdit(m.id)}
|
||||
className="font-semibold transition-opacity hover:opacity-80"
|
||||
className="transition-opacity hover:opacity-80"
|
||||
style={{
|
||||
color:
|
||||
"color-mix(in srgb, var(--accent) 72%, var(--ink))",
|
||||
@@ -1166,7 +1166,7 @@ export default function ChatRoomClient({
|
||||
<Crown size={11} style={{ color: "var(--gold)" }} />
|
||||
)}
|
||||
<span
|
||||
className="text-[12px] font-semibold truncate"
|
||||
className="text-[12px] truncate"
|
||||
style={{ color: "var(--ink-2)" }}
|
||||
>
|
||||
{m.sender.nickname}
|
||||
@@ -1188,7 +1188,7 @@ export default function ChatRoomClient({
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="inline-flex items-center gap-1 mb-1.5 text-[11px] font-semibold"
|
||||
className="inline-flex items-center gap-1 mb-1.5 text-[11px]"
|
||||
style={{ color: "var(--ink-3)" }}
|
||||
>
|
||||
<Undo2 size={11} strokeWidth={2.4} />
|
||||
@@ -1232,7 +1232,7 @@ export default function ChatRoomClient({
|
||||
<Crown size={11} style={{ color: "var(--gold)" }} />
|
||||
)}
|
||||
<span
|
||||
className="text-[12px] font-semibold truncate"
|
||||
className="text-[12px] truncate"
|
||||
style={{ color: "var(--ink-2)" }}
|
||||
>
|
||||
{m.sender.nickname}
|
||||
@@ -1319,7 +1319,7 @@ export default function ChatRoomClient({
|
||||
style={{ background: "var(--accent)" }}
|
||||
/>
|
||||
<div className="flex-1 min-w-0">
|
||||
<p className="text-[11px] font-bold" style={{ color: "var(--accent)" }}>
|
||||
<p className="text-[11px]" style={{ color: "var(--accent)" }}>
|
||||
回复 {replyTo.sender.nickname || replyTo.sender.username}
|
||||
</p>
|
||||
<p className="text-[12px] truncate meta mt-0.5">
|
||||
@@ -1365,7 +1365,7 @@ export default function ChatRoomClient({
|
||||
size={28}
|
||||
/>
|
||||
<div className="min-w-0">
|
||||
<p className="text-[13px] font-semibold truncate" style={{ color: "var(--ink)" }}>
|
||||
<p className="text-[13px] truncate" style={{ color: "var(--ink)" }}>
|
||||
{m.user.nickname || m.user.username}
|
||||
</p>
|
||||
<p className="text-[11px] meta truncate">@{m.user.username}</p>
|
||||
@@ -1486,7 +1486,7 @@ export default function ChatRoomClient({
|
||||
className="flex items-center justify-between px-4 py-3 shrink-0"
|
||||
style={{ borderBottom: "1px solid var(--line)" }}
|
||||
>
|
||||
<p className="text-[13px] font-bold" style={{ color: "var(--ink)" }}>
|
||||
<p className="text-[13px] font-medium" style={{ color: "var(--ink)" }}>
|
||||
{rightPanel === "members"
|
||||
? `成员 · ${memberCount}`
|
||||
: rightPanel === "invite"
|
||||
@@ -1630,7 +1630,7 @@ function MembersPanel({
|
||||
{showTitle && (
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<p
|
||||
className="text-[12px] font-bold tracking-[0.08em] uppercase"
|
||||
className="text-[12px] tracking-[0.08em] uppercase"
|
||||
style={{ color: "var(--ink-3)" }}
|
||||
>
|
||||
成员 · {members.length}
|
||||
@@ -1666,7 +1666,7 @@ function MembersPanel({
|
||||
/>
|
||||
<Link
|
||||
href={`/u/${m.user.id}`}
|
||||
className="flex-1 min-w-0 truncate text-[13px] font-semibold hover:text-[var(--accent)] transition-colors"
|
||||
className="flex-1 min-w-0 truncate text-[13px] hover:text-[var(--accent)] transition-colors"
|
||||
style={{ color: "var(--ink)" }}
|
||||
>
|
||||
{m.user.nickname}
|
||||
@@ -1795,7 +1795,7 @@ function SettingsPanel({
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<label
|
||||
className="block text-[12px] font-bold mb-1.5"
|
||||
className="block text-[12px] mb-1.5"
|
||||
style={{ color: "var(--ink-2)" }}
|
||||
>
|
||||
群名称
|
||||
@@ -1815,7 +1815,7 @@ function SettingsPanel({
|
||||
</div>
|
||||
<div>
|
||||
<label
|
||||
className="block text-[12px] font-bold mb-1.5"
|
||||
className="block text-[12px] mb-1.5"
|
||||
style={{ color: "var(--ink-2)" }}
|
||||
>
|
||||
群简介
|
||||
|
||||
@@ -121,23 +121,22 @@ body {
|
||||
padding: 5px 10px;
|
||||
border-radius: 8px;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
line-height: 1.35;
|
||||
font-weight: 400;
|
||||
line-height: 1.4;
|
||||
letter-spacing: 0.01em;
|
||||
pointer-events: none;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
color: var(--panel);
|
||||
background: color-mix(in srgb, var(--ink) 92%, transparent);
|
||||
border: 1px solid color-mix(in srgb, var(--ink) 88%, transparent);
|
||||
box-shadow: var(--shadow);
|
||||
white-space: normal;
|
||||
overflow-wrap: anywhere;
|
||||
color: var(--accent);
|
||||
background: color-mix(in srgb, var(--accent) 10%, var(--panel));
|
||||
border: 1px solid color-mix(in srgb, var(--accent) 30%, var(--line));
|
||||
box-shadow: var(--shadow-sm);
|
||||
animation: j13-tip-in 80ms ease-out;
|
||||
}
|
||||
.dark .j13-tip-bubble {
|
||||
color: var(--ink);
|
||||
background: color-mix(in srgb, var(--panel-2) 94%, var(--ink));
|
||||
border-color: var(--line-2);
|
||||
color: var(--accent);
|
||||
background: color-mix(in srgb, var(--accent) 16%, var(--panel));
|
||||
border-color: color-mix(in srgb, var(--accent) 38%, var(--line));
|
||||
}
|
||||
@keyframes j13-tip-in {
|
||||
from {
|
||||
@@ -248,7 +247,7 @@ body {
|
||||
background: var(--panel);
|
||||
color: var(--ink-2);
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
font-weight: 500;
|
||||
transition: background 0.12s ease, color 0.12s ease, border-color 0.12s ease;
|
||||
}
|
||||
.j13-compose-typechip:hover:not(:disabled) {
|
||||
@@ -326,7 +325,7 @@ body {
|
||||
}
|
||||
.j13-compose-title::placeholder {
|
||||
color: var(--ink-3);
|
||||
font-weight: 700;
|
||||
font-weight: 500;
|
||||
}
|
||||
.j13-compose-title:focus {
|
||||
outline: none;
|
||||
@@ -427,7 +426,7 @@ body {
|
||||
border-radius: 10px;
|
||||
color: var(--ink);
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
font-weight: 500;
|
||||
text-align: left;
|
||||
cursor: pointer;
|
||||
transition: background 0.12s ease;
|
||||
@@ -450,7 +449,7 @@ body {
|
||||
border-radius: 12px;
|
||||
color: var(--ink-2);
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* 正文画布占满剩余高度 */
|
||||
@@ -522,7 +521,7 @@ body {
|
||||
padding: 0 12px;
|
||||
border-radius: 999px;
|
||||
font-size: 12.5px;
|
||||
font-weight: 700;
|
||||
font-weight: 500;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
@@ -717,7 +716,7 @@ h1, h2, h3, h4 {
|
||||
/* 主题强调色加深 + 对比色字,胶囊形 */
|
||||
background: color-mix(in srgb, var(--accent) 82%, #000);
|
||||
color: #fff !important;
|
||||
font-weight: 600;
|
||||
font-weight: 500;
|
||||
text-decoration: none !important;
|
||||
cursor: pointer;
|
||||
transition: filter 0.12s ease, opacity 0.12s ease;
|
||||
@@ -754,7 +753,7 @@ a.j13-mention-token:hover {
|
||||
.kicker {
|
||||
font-family: var(--font-sans);
|
||||
font-size: 12.5px;
|
||||
font-weight: 600;
|
||||
font-weight: 500;
|
||||
letter-spacing: 0.02em;
|
||||
color: var(--ink-3);
|
||||
}
|
||||
@@ -797,7 +796,7 @@ a.j13-mention-token:hover {
|
||||
padding: 10px 20px;
|
||||
border-radius: 999px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
font-weight: 500;
|
||||
line-height: 1;
|
||||
border: 1px solid transparent;
|
||||
cursor: pointer;
|
||||
@@ -918,7 +917,7 @@ a.j13-mention-token:hover {
|
||||
.field-label {
|
||||
display: block;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
font-weight: 500;
|
||||
color: var(--ink-2);
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
@@ -971,7 +970,7 @@ a.j13-mention-token:hover {
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
font-weight: 500;
|
||||
padding: 4px 11px;
|
||||
background: var(--clay-soft);
|
||||
color: var(--clay);
|
||||
@@ -983,7 +982,7 @@ a.j13-mention-token:hover {
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
font-weight: 500;
|
||||
padding: 4px 11px;
|
||||
background: var(--gold-soft);
|
||||
color: var(--gold);
|
||||
@@ -995,7 +994,7 @@ a.j13-mention-token:hover {
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
font-weight: 500;
|
||||
padding: 4px 11px;
|
||||
background: var(--accent-soft);
|
||||
color: var(--accent);
|
||||
@@ -1006,18 +1005,23 @@ a.j13-mention-token:hover {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
font-weight: 500;
|
||||
padding: 4px 11px;
|
||||
background: color-mix(in srgb, var(--accent) 12%, var(--tint));
|
||||
color: var(--accent);
|
||||
/* 固定靛紫,不跟主题 accent,避免与状态徽章撞色 */
|
||||
background: color-mix(in srgb, #5b5bd6 12%, var(--panel));
|
||||
color: #4f46b8;
|
||||
border-radius: 999px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.dark .badge-type {
|
||||
background: rgba(165, 180, 252, 0.16);
|
||||
color: #a5b4fc;
|
||||
}
|
||||
.badge-access {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
font-weight: 500;
|
||||
padding: 4px 11px;
|
||||
background: var(--tint);
|
||||
color: var(--ink-2);
|
||||
@@ -1025,14 +1029,132 @@ a.j13-mention-token:hover {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* 帖子列表中的板块小标签:全站统一浅灰配色,避免每行多色眩目 */
|
||||
/* 类型帖状态徽章:浅色区分,避免同一灰;状态色固定,不跟主题 */
|
||||
.badge-status {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
padding: 4px 11px;
|
||||
border-radius: 999px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.badge-status-unsolved {
|
||||
/* 淡红:待解决,与推荐金 / 已解决绿区分 */
|
||||
background: color-mix(in srgb, #c8281e 11%, var(--panel));
|
||||
color: #b91c1c;
|
||||
}
|
||||
.badge-status-solved {
|
||||
background: color-mix(in srgb, var(--ok) 16%, var(--panel));
|
||||
color: var(--ok);
|
||||
}
|
||||
.badge-status-open {
|
||||
/* 进行中:天空蓝,不跟主题 accent,亦区别于类型徽章 */
|
||||
background: color-mix(in srgb, #0284c7 12%, var(--panel));
|
||||
color: #0369a1;
|
||||
}
|
||||
.badge-status-closed {
|
||||
background: color-mix(in srgb, #5b6b8c 12%, var(--panel));
|
||||
color: #5b6b8c;
|
||||
}
|
||||
.badge-status-expired {
|
||||
background: color-mix(in srgb, var(--danger) 12%, var(--panel));
|
||||
color: var(--danger);
|
||||
}
|
||||
.badge-status-drawn {
|
||||
background: color-mix(in srgb, #7c5cbf 14%, var(--panel));
|
||||
color: #6a4aad;
|
||||
}
|
||||
.dark .badge-status-unsolved {
|
||||
background: rgba(255, 107, 96, 0.14);
|
||||
color: #fca5a5;
|
||||
}
|
||||
.dark .badge-status-solved {
|
||||
background: color-mix(in srgb, var(--ok) 22%, transparent);
|
||||
color: var(--ok);
|
||||
}
|
||||
.dark .badge-status-open {
|
||||
background: rgba(56, 189, 248, 0.14);
|
||||
color: #7dd3fc;
|
||||
}
|
||||
.dark .badge-status-closed {
|
||||
background: rgba(170, 182, 207, 0.14);
|
||||
color: #aab6cf;
|
||||
}
|
||||
.dark .badge-status-expired {
|
||||
background: color-mix(in srgb, var(--danger) 20%, transparent);
|
||||
color: var(--danger);
|
||||
}
|
||||
.dark .badge-status-drawn {
|
||||
background: rgba(192, 168, 245, 0.16);
|
||||
color: #c0a8f5;
|
||||
}
|
||||
|
||||
/* 站点公告列表 */
|
||||
.j13-anno-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
.j13-anno-item {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 0;
|
||||
padding: 11px 12px 11px 0;
|
||||
border-radius: 12px;
|
||||
border: 1px solid var(--line);
|
||||
background: transparent;
|
||||
transition: background 0.15s ease, border-color 0.15s ease;
|
||||
overflow: hidden;
|
||||
}
|
||||
.j13-anno-item::before {
|
||||
content: "";
|
||||
flex-shrink: 0;
|
||||
width: 3px;
|
||||
align-self: stretch;
|
||||
margin-right: 11px;
|
||||
border-radius: 0 2px 2px 0;
|
||||
background: color-mix(in srgb, var(--accent) 55%, transparent);
|
||||
}
|
||||
.j13-anno-item:hover,
|
||||
.j13-anno-item:focus-visible {
|
||||
background: var(--accent-soft);
|
||||
border-color: color-mix(in srgb, var(--accent) 28%, var(--line));
|
||||
}
|
||||
.j13-anno-item:hover::before,
|
||||
.j13-anno-item:focus-visible::before {
|
||||
background: var(--accent);
|
||||
}
|
||||
.j13-anno-item-body {
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
padding-right: 2px;
|
||||
}
|
||||
.j13-anno-item-title {
|
||||
display: block;
|
||||
font-size: 13px;
|
||||
font-weight: 400;
|
||||
line-height: 1.45;
|
||||
color: var(--ink);
|
||||
}
|
||||
.j13-anno-item:hover .j13-anno-item-title,
|
||||
.j13-anno-item:focus-visible .j13-anno-item-title {
|
||||
color: var(--accent);
|
||||
}
|
||||
.j13-anno-item-meta {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-top: 6px;
|
||||
}
|
||||
.j13-board-tag {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 0 7px;
|
||||
border-radius: 6px;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
font-weight: 500;
|
||||
line-height: 1.65;
|
||||
background: var(--tint);
|
||||
color: var(--ink-2);
|
||||
@@ -1052,7 +1174,7 @@ a.j13-mention-token:hover {
|
||||
padding: 2px 10px;
|
||||
border-radius: 999px;
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
font-weight: 500;
|
||||
line-height: 1.55;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
@@ -1116,7 +1238,7 @@ a.j13-mention-token:hover {
|
||||
background: var(--nav-active-bg);
|
||||
border-color: var(--nav-active-line);
|
||||
color: var(--nav-active-ink);
|
||||
font-weight: 700;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* ----- 辅助 ----- */
|
||||
@@ -1161,7 +1283,7 @@ a.j13-mention-token:hover {
|
||||
padding: 4.5px 12px;
|
||||
font-family: var(--font-sans);
|
||||
font-size: 12.5px;
|
||||
font-weight: 600;
|
||||
font-weight: 500;
|
||||
line-height: 1.35;
|
||||
color: var(--ink-3);
|
||||
cursor: pointer;
|
||||
@@ -1177,7 +1299,7 @@ a.j13-mention-token:hover {
|
||||
background: var(--accent-soft);
|
||||
border-color: color-mix(in srgb, var(--accent) 38%, var(--line));
|
||||
color: var(--accent);
|
||||
font-weight: 700;
|
||||
font-weight: 600;
|
||||
}
|
||||
.j13-tab:active {
|
||||
transform: scale(0.97);
|
||||
@@ -1218,7 +1340,7 @@ a.j13-mention-token:hover {
|
||||
background: var(--nav-active-bg);
|
||||
border-color: var(--nav-active-line);
|
||||
color: var(--nav-active-ink);
|
||||
font-weight: 700;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* 首页左栏「全部讨论 / 板块目录」卡片:与面包屑的纯文字位置指引区分,
|
||||
@@ -1251,6 +1373,7 @@ a.j13-mention-token:hover {
|
||||
}
|
||||
.j13-bcard-name {
|
||||
color: var(--ink);
|
||||
font-size: 14px;
|
||||
}
|
||||
.j13-bcard[aria-current="page"] .j13-bcard-name {
|
||||
color: var(--nav-active-ink);
|
||||
@@ -1285,7 +1408,7 @@ a.j13-mention-token:hover {
|
||||
align-items: center;
|
||||
gap: 7px;
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
font-weight: 500;
|
||||
letter-spacing: 0.02em;
|
||||
color: var(--ink);
|
||||
}
|
||||
@@ -1318,31 +1441,42 @@ a.j13-mention-token:hover {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* 站点数据 bento 统计瓦片 */
|
||||
/* 站点数据:无灰底瓦片,细线分隔 */
|
||||
.j13-pulse {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 8px;
|
||||
gap: 0;
|
||||
margin-top: 10px;
|
||||
border: 1px solid var(--line);
|
||||
border-radius: var(--r-sm);
|
||||
overflow: hidden;
|
||||
}
|
||||
.j13-pulse-cell {
|
||||
min-width: 0;
|
||||
padding: 11px 12px 10px;
|
||||
background: var(--panel-2);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: var(--r-sm);
|
||||
padding: 12px 14px 11px;
|
||||
background: transparent;
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
}
|
||||
.j13-pulse-cell:nth-child(odd) {
|
||||
border-right: 1px solid var(--line);
|
||||
}
|
||||
.j13-pulse-cell:nth-child(-n + 2) {
|
||||
border-bottom: 1px solid var(--line);
|
||||
}
|
||||
.j13-pulse-lab {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
font-size: 11.5px;
|
||||
font-weight: 400;
|
||||
color: var(--ink-3);
|
||||
white-space: nowrap;
|
||||
}
|
||||
.j13-pulse-num {
|
||||
margin-top: 6px;
|
||||
font-size: 21px;
|
||||
font-weight: 800;
|
||||
font-size: 22px;
|
||||
font-weight: 500;
|
||||
line-height: 1.05;
|
||||
letter-spacing: -0.015em;
|
||||
color: var(--ink);
|
||||
@@ -1354,6 +1488,7 @@ a.j13-mention-token:hover {
|
||||
gap: 3px;
|
||||
margin-top: 6px;
|
||||
font-size: 10.5px;
|
||||
font-weight: 400;
|
||||
line-height: 1;
|
||||
color: var(--ink-3);
|
||||
font-variant-numeric: tabular-nums;
|
||||
@@ -1361,7 +1496,7 @@ a.j13-mention-token:hover {
|
||||
}
|
||||
.j13-pulse-foot.is-up {
|
||||
color: var(--ok);
|
||||
font-weight: 600;
|
||||
font-weight: 400;
|
||||
}
|
||||
.j13-pulse-dot {
|
||||
position: relative;
|
||||
@@ -1409,7 +1544,7 @@ a.j13-mention-token:hover {
|
||||
padding: 6px 11px;
|
||||
border-radius: 9999px;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
font-weight: 500;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
@@ -1433,7 +1568,7 @@ a.j13-mention-token:hover {
|
||||
padding-top: 2px;
|
||||
text-align: center;
|
||||
font-size: 13px;
|
||||
font-weight: 800;
|
||||
font-weight: 500;
|
||||
font-variant-numeric: tabular-nums;
|
||||
color: var(--ink-3);
|
||||
flex-shrink: 0;
|
||||
@@ -1481,7 +1616,7 @@ a.j13-mention-token:hover {
|
||||
background: var(--panel);
|
||||
border-radius: 999px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
font-weight: 500;
|
||||
color: var(--ink-2);
|
||||
white-space: nowrap;
|
||||
transition: background 0.16s ease, color 0.16s ease, border-color 0.16s ease;
|
||||
@@ -1608,14 +1743,14 @@ a.j13-mention-token:hover {
|
||||
/* 子回复昵称 */
|
||||
.cmt-reply-name {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
font-weight: 500;
|
||||
color: var(--ink-2);
|
||||
}
|
||||
|
||||
/* 楼层号标签(主评论昵称旁) */
|
||||
.cmt-floor-label {
|
||||
font-size: 11.5px;
|
||||
font-weight: 600;
|
||||
font-weight: 500;
|
||||
color: var(--ink-3);
|
||||
text-decoration: none;
|
||||
padding: 1px 6px;
|
||||
@@ -1650,7 +1785,7 @@ a.j13-mention-token:hover {
|
||||
/* 被回复者名称(可点击跳转) */
|
||||
.cmt-reply-target {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
font-weight: 500;
|
||||
color: var(--ink-2);
|
||||
text-decoration: none;
|
||||
white-space: nowrap;
|
||||
@@ -1680,7 +1815,7 @@ a.j13-mention-token:hover {
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
font-size: 12.5px;
|
||||
font-weight: 600;
|
||||
font-weight: 500;
|
||||
color: var(--accent);
|
||||
padding: 4px 10px;
|
||||
border-radius: 99px;
|
||||
@@ -1765,7 +1900,7 @@ a.j13-mention-token:hover {
|
||||
z-index: 10001;
|
||||
padding: 10px 18px;
|
||||
font-size: 13.5px;
|
||||
font-weight: 600;
|
||||
font-weight: 500;
|
||||
background: var(--accent);
|
||||
color: var(--accent-on);
|
||||
border-radius: 999px;
|
||||
|
||||
@@ -87,7 +87,7 @@ export default function PointsClient() {
|
||||
style={{ borderTop: "1px solid var(--line)" }}
|
||||
>
|
||||
<div className="min-w-0">
|
||||
<p className="font-bold text-[14px]" style={{ color: "var(--ink)" }}>
|
||||
<p className="text-[14px]" style={{ color: "var(--ink)" }}>
|
||||
{pointReasonLabel(it.reason)}
|
||||
</p>
|
||||
{it.note && (
|
||||
|
||||
@@ -38,7 +38,7 @@ import PostAttachments from "@/components/PostAttachments";
|
||||
import PostInteractPanels from "@/components/PostInteractPanels";
|
||||
import { canModerateBoard } from "@/lib/roles";
|
||||
import { IconComment, IconEye } from "@/components/Icons";
|
||||
import { normalizePostType, postTypeLabel, contentAccessLabel, typeStatusLabel } from "@/lib/postMeta";
|
||||
import { normalizePostType, postTypeLabel, contentAccessLabel, typeStatusLabel, typeStatusClass } from "@/lib/postMeta";
|
||||
|
||||
interface PageProps {
|
||||
params: Promise<{ id: string }>;
|
||||
@@ -128,7 +128,7 @@ function BoardCard({ board }: { board: Board }) {
|
||||
{board.name.slice(0, 1)}
|
||||
</span>
|
||||
<span
|
||||
className="font-bold text-[14.5px] truncate group-hover:text-[var(--accent)] transition-colors"
|
||||
className="font-semibold text-[14.5px] truncate group-hover:text-[var(--accent)] transition-colors"
|
||||
style={{ color: "var(--ink)" }}
|
||||
>
|
||||
{board.name}
|
||||
@@ -163,7 +163,7 @@ function AuthorRailCard({ profile }: { profile: UserProfile }) {
|
||||
<div className="min-w-0">
|
||||
<Link
|
||||
href={`/u/${u.id}`}
|
||||
className="block font-bold text-[14.5px] truncate hover:text-[var(--accent)] transition-colors"
|
||||
className="block font-semibold text-[14.5px] truncate hover:text-[var(--accent)] transition-colors"
|
||||
style={{ color: "var(--ink)" }}
|
||||
>
|
||||
{u.nickname}
|
||||
@@ -205,7 +205,7 @@ function AuthorPosts({ posts }: { posts: PostListItem[] }) {
|
||||
className="block rounded-[10px] px-2 py-2 transition-colors hover:bg-[var(--accent-soft)] focus-visible:bg-[var(--accent-soft)]"
|
||||
>
|
||||
<span
|
||||
className="block text-[13px] font-semibold leading-snug line-clamp-2"
|
||||
className="block text-[13px] leading-snug line-clamp-2"
|
||||
style={{ color: "var(--ink)" }}
|
||||
>
|
||||
{p.title}
|
||||
@@ -247,7 +247,7 @@ function RelatedPosts({
|
||||
<span className="j13-rank-no">{i + 1}</span>
|
||||
<span className="min-w-0 flex-1">
|
||||
<span
|
||||
className="block text-[13px] font-semibold leading-snug line-clamp-2"
|
||||
className="block text-[13px] leading-snug line-clamp-2"
|
||||
style={{ color: "var(--ink)" }}
|
||||
>
|
||||
{p.title}
|
||||
@@ -267,7 +267,7 @@ function RelatedPosts({
|
||||
</ol>
|
||||
<Link
|
||||
href={`/board/${boardId}`}
|
||||
className="j13-raillink justify-center mt-1.5 text-[12.5px] font-semibold"
|
||||
className="j13-raillink justify-center mt-1.5 text-[12.5px]"
|
||||
style={{ color: "var(--accent)" }}
|
||||
>
|
||||
查看全部 <ChevronRight size={13} />
|
||||
@@ -281,7 +281,7 @@ function AuthorCardStacked({ profile }: { profile: UserProfile }) {
|
||||
return (
|
||||
<div className="panel p-5 sm:p-6">
|
||||
<p
|
||||
className="text-[11.5px] font-bold tracking-[0.08em] uppercase mb-3.5"
|
||||
className="text-[11.5px] font-medium tracking-[0.08em] uppercase mb-3.5"
|
||||
style={{ color: "var(--ink-3)" }}
|
||||
>
|
||||
关于作者
|
||||
@@ -294,7 +294,7 @@ function AuthorCardStacked({ profile }: { profile: UserProfile }) {
|
||||
<div className="flex items-center gap-2 flex-wrap">
|
||||
<Link
|
||||
href={`/u/${profile.user.id}`}
|
||||
className="font-bold text-[15px] hover:text-[var(--accent)] transition-colors"
|
||||
className="font-semibold text-[15px] hover:text-[var(--accent)] transition-colors"
|
||||
style={{ color: "var(--ink)" }}
|
||||
>
|
||||
{profile.user.nickname}
|
||||
@@ -352,6 +352,7 @@ export default async function PostDetailPage({ params, searchParams }: PageProps
|
||||
const isPublished = post.status === "published";
|
||||
const postType = normalizePostType(post.post_type);
|
||||
const typeStatus = typeStatusLabel(postType, post.type_status);
|
||||
const typeStatusCls = typeStatus ? typeStatusClass(postType, post.type_status) : "";
|
||||
const canManageTyped =
|
||||
!!me.user && (me.user.id === post.user.id || canModerateBoard(me.user, post.board_id));
|
||||
const canSeeStatus = canManageTyped;
|
||||
@@ -449,8 +450,8 @@ export default async function PostDetailPage({ params, searchParams }: PageProps
|
||||
{postType !== "discussion" && postType !== "question" && (
|
||||
<span className="badge-type shrink-0 mt-2 sm:mt-3">{postTypeLabel(postType)}</span>
|
||||
)}
|
||||
{typeStatus && (
|
||||
<span className="badge-access shrink-0 mt-2 sm:mt-3">{typeStatus}</span>
|
||||
{typeStatus && typeStatusCls && (
|
||||
<span className={`${typeStatusCls} shrink-0 mt-2 sm:mt-3`}>{typeStatus}</span>
|
||||
)}
|
||||
{post.content_access && post.content_access !== "public" && (
|
||||
<span className="badge-access shrink-0 mt-2 sm:mt-3">
|
||||
@@ -459,7 +460,7 @@ export default async function PostDetailPage({ params, searchParams }: PageProps
|
||||
)}
|
||||
{!isPublished && canSeeStatus && post.status === "pending" && (
|
||||
<span
|
||||
className="inline-flex items-center gap-1 rounded-full px-2.5 py-0.5 text-[11.5px] font-bold shrink-0 mt-2 sm:mt-3"
|
||||
className="inline-flex items-center gap-1 rounded-full px-2.5 py-0.5 text-[11.5px] font-medium shrink-0 mt-2 sm:mt-3"
|
||||
style={{ background: "var(--gold-soft)", color: "var(--gold)" }}
|
||||
>
|
||||
等待审核
|
||||
@@ -467,7 +468,7 @@ export default async function PostDetailPage({ params, searchParams }: PageProps
|
||||
)}
|
||||
{!isPublished && canSeeStatus && post.status === "rejected" && (
|
||||
<span
|
||||
className="inline-flex items-center gap-1 rounded-full px-2.5 py-0.5 text-[11.5px] font-bold shrink-0 mt-2 sm:mt-3"
|
||||
className="inline-flex items-center gap-1 rounded-full px-2.5 py-0.5 text-[11.5px] font-medium shrink-0 mt-2 sm:mt-3"
|
||||
style={{ background: "var(--danger-soft)", color: "var(--danger)" }}
|
||||
>
|
||||
未通过审核
|
||||
@@ -486,7 +487,7 @@ export default async function PostDetailPage({ params, searchParams }: PageProps
|
||||
</Link>
|
||||
<Link
|
||||
href={`/u/${post.user.id}`}
|
||||
className="min-w-0 truncate text-[15px] font-bold hover:text-[var(--accent)] transition-colors"
|
||||
className="min-w-0 truncate text-[15px] font-semibold hover:text-[var(--accent)] transition-colors"
|
||||
style={{ color: "var(--ink)" }}
|
||||
>
|
||||
{post.user.nickname}
|
||||
@@ -607,7 +608,7 @@ export default async function PostDetailPage({ params, searchParams }: PageProps
|
||||
/>
|
||||
) : (
|
||||
<section className="panel p-6 sm:p-8 mt-6 text-center" aria-label="评论区">
|
||||
<p className="text-[14px] font-bold" style={{ color: "var(--ink-2)" }}>
|
||||
<p className="text-[14px]" style={{ color: "var(--ink-2)" }}>
|
||||
{post.status === "pending" ? "帖子审核通过后开放评论" : "帖子未通过审核,评论已关闭"}
|
||||
</p>
|
||||
<p className="meta mt-1.5 text-[12.5px]">
|
||||
|
||||
@@ -143,6 +143,7 @@ export default async function UserPage({ params, searchParams }: UserPageProps)
|
||||
<Link
|
||||
href={`/u/${id}?tab=security`}
|
||||
aria-label="安全设置"
|
||||
data-tip="安全设置"
|
||||
className="inline-flex w-8 h-8 items-center justify-center rounded-full border transition-colors hover:border-[var(--accent)] hover:text-[var(--accent)]"
|
||||
style={{ background: "var(--panel)", borderColor: "var(--line-2)", color: "var(--ink-2)" }}
|
||||
>
|
||||
|
||||
Reference in New Issue
Block a user