💄 Optimize post detail view

This commit is contained in:
2025-11-06 01:11:23 +08:00
parent f851bcd646
commit 6da1a4d21c
3 changed files with 76 additions and 153 deletions

View File

@@ -1,15 +1,14 @@
<template> <template>
<v-chip-group class="d-flex flex-wrap gap-2"> <div class="d-flex flex-wrap gap-2">
<!-- Add Reaction Button --> <!-- Add Reaction Button -->
<v-chip <v-chip
v-if="canReact" v-if="canReact"
color="primary"
rounded rounded
:disabled="submitting" :disabled="submitting"
prepend-icon="mdi-plus"
@click="showReactionDialog" @click="showReactionDialog"
> >
<v-icon start size="16">mdi-plus</v-icon> React
<span class="text-xs">React</span>
</v-chip> </v-chip>
<!-- Existing Reactions --> <!-- Existing Reactions -->
@@ -27,7 +26,7 @@
{{ count }} {{ count }}
</v-chip> </v-chip>
</v-chip> </v-chip>
</v-chip-group> </div>
<!-- Reaction Selection Dialog --> <!-- Reaction Selection Dialog -->
<v-dialog v-model="reactionDialog" max-width="500" height="600"> <v-dialog v-model="reactionDialog" max-width="500" height="600">
@@ -258,15 +257,6 @@ function getReactionCount(symbol: string): number {
</script> </script>
<style scoped> <style scoped>
.post-reaction-list {
min-height: 32px;
}
.reaction-chip {
height: 28px !important;
border-radius: 14px;
}
.reaction-emoji { .reaction-emoji {
font-size: 16px; font-size: 16px;
margin-right: 4px; margin-right: 4px;
@@ -278,24 +268,10 @@ function getReactionCount(symbol: string): number {
} }
.reaction-count { .reaction-count {
height: 16px !important;
font-size: 10px; font-size: 10px;
padding: 0 4px; padding: 0 4px;
} }
/* Dialog Styles */
.reaction-dialog {
height: 600px;
display: flex;
flex-direction: column;
}
.dialog-content {
flex: 1;
overflow-y: auto;
max-height: calc(600px - 80px);
}
.reaction-section { .reaction-section {
margin-bottom: 16px; margin-bottom: 16px;
} }

View File

@@ -4,18 +4,19 @@
<v-progress-circular indeterminate size="64" color="primary" /> <v-progress-circular indeterminate size="64" color="primary" />
<p class="mt-4">Loading post...</p> <p class="mt-4">Loading post...</p>
</div> </div>
<div v-else-if="error" class="text-center py-12"> <div v-else-if="error" class="text-center py-12">
<v-alert type="error" class="mb-4" prominent> <v-alert type="error" class="mb-4" prominent>
<v-alert-title>Error Loading Post</v-alert-title> <v-alert-title>Error Loading Post</v-alert-title>
{{ error?.statusMessage || "Failed to load post" }} {{ error?.statusMessage || "Failed to load post" }}
</v-alert> </v-alert>
</div> </div>
<div v-else-if="post" class="max-w-4xl mx-auto">
<!-- Article Type: Split Header and Content --> <div v-else-if="post" class="max-w-7xl mx-auto">
<template v-if="post.type === 1"> <div class="grid grid-cols-1 lg:grid-cols-12 gap-4">
<!-- Post Header Section (Article) --> <!-- Main Content Column -->
<v-card class="mb-4 elevation-2" rounded="lg"> <div class="lg:col-span-8 flex flex-col gap-4">
<v-card-text class="pa-6"> <v-card class="pa-6">
<post-header :item="post" class="mb-4" /> <post-header :item="post" class="mb-4" />
<!-- Post Title and Description --> <!-- Post Title and Description -->
@@ -47,25 +48,17 @@
<v-icon size="16">mdi-pencil</v-icon> <v-icon size="16">mdi-pencil</v-icon>
<span>Updated {{ formatDate(post.updatedAt) }}</span> <span>Updated {{ formatDate(post.updatedAt) }}</span>
</div> </div>
<div <div class="flex items-center gap-1">
v-if="(post as any).viewCount || (post as any).view_count"
class="flex items-center gap-1"
>
<v-icon size="16">mdi-eye</v-icon> <v-icon size="16">mdi-eye</v-icon>
<span <span>
>{{ {{ post.viewsTotal }} / {{ post.viewsUnique }}
(post as any).viewCount || (post as any).view_count || 0 views
}} </span>
views</span
>
</div> </div>
</div> </div>
</v-card-text> </v-card>
</v-card>
<!-- Merged Content and Attachments Section (Article) --> <v-card class="pa-6">
<v-card class="mb-4 elevation-1" rounded="lg">
<v-card-text class="pa-8">
<article <article
v-if="htmlContent" v-if="htmlContent"
class="prose prose-xl dark:prose-invert prose-slate max-w-none mb-8" class="prose prose-xl dark:prose-invert prose-slate max-w-none mb-8"
@@ -75,112 +68,58 @@
<!-- Attachments within Content Section --> <!-- Attachments within Content Section -->
<attachment-list :attachments="post.attachments || []" /> <attachment-list :attachments="post.attachments || []" />
</v-card-text> </v-card>
</v-card> </div>
</template>
<!-- Other Types: Merged Header, Content, and Attachments --> <!-- Sidebar Column -->
<template v-else> <div class="lg:col-span-4 flex flex-col gap-4">
<!-- Merged Header, Content, and Attachments Section --> <!-- Tags Section -->
<v-card class="px-4 py-3 mb-4 elevation-1" rounded="lg"> <v-card
<v-card-text class="pa-6"> v-if="post.tags && post.tags.length > 0"
<post-header :item="post" class="mb-4" /> rounded="lg"
prepend-icon="mdi-tag-multiple"
<!-- Post Title and Description --> title="Tags & Categories"
<div v-if="post.title || post.description" class="mb-4"> >
<h1 <v-card-text>
v-if="post.title" <div class="flex flex-wrap gap-2">
class="text-3xl font-bold mb-3 leading-tight" <v-chip
> v-for="category in post.categories"
{{ post.title }} :key="category.id"
</h1> prepend-icon="mdi-tshape"
<p rounded
v-if="post.description"
class="text-lg text-medium-emphasis leading-relaxed"
>
{{ post.description }}
</p>
</div>
<!-- Post Metadata -->
<div
class="flex items-center gap-4 text-sm text-medium-emphasis mb-4"
>
<div class="flex items-center gap-1">
<v-icon size="16">mdi-calendar</v-icon>
<span>{{ formatDate(post.createdAt) }}</span>
</div>
<div
v-if="post.updatedAt && post.updatedAt !== post.createdAt"
class="flex items-center gap-1"
>
<v-icon size="16">mdi-pencil</v-icon>
<span>Updated {{ formatDate(post.updatedAt) }}</span>
</div>
<div
v-if="(post as any).viewCount || (post as any).view_count"
class="flex items-center gap-1"
>
<v-icon size="16">mdi-eye</v-icon>
<span
>{{
(post as any).viewCount || (post as any).view_count || 0
}}
views</span
> >
{{ category.slug }}
</v-chip>
<v-chip
v-for="tag in post.tags"
:key="tag.id"
prepend-icon="mdi-tag"
rounded
>
{{ tag.slug }}
</v-chip>
</div> </div>
</div> </v-card-text>
</v-card>
<!-- Main Content --> <!-- Post Reactions -->
<article <v-card
v-if="htmlContent" class="elevation-1"
class="prose prose-xl dark:prose-invert prose-slate max-w-none mb-8" rounded="lg"
> title="Reactions"
<div v-html="htmlContent" /> prepend-icon="mdi-thumb-up"
</article> >
<v-card-text>
<!-- Attachments within Merged Section --> <post-reaction-list
<attachment-list :attachments="post.attachments || []" /> can-react
</v-card-text> :parent-id="id"
</v-card> :reactions="(post as any).reactions || {}"
</template> :reactions-made="(post as any).reactionsMade || {}"
@react="handleReaction"
<!-- Tags Section --> />
<v-card </v-card-text>
v-if="post.tags && post.tags.length > 0" </v-card>
class="mb-4 elevation-1" </div>
rounded="lg"
>
<v-card-title class="text-h6">
<v-icon class="mr-2">mdi-tag-multiple</v-icon>
Tags
</v-card-title>
<v-card-text>
<div class="flex flex-wrap gap-2">
<v-chip
v-for="tag in post.tags"
:key="tag"
size="small"
variant="outlined"
color="primary"
class="cursor-pointer hover:bg-primary hover:text-primary-foreground transition-colors"
>
<v-icon start size="16">mdi-tag</v-icon>
{{ tag }}
</v-chip>
</div>
</v-card-text>
</v-card>
<!-- Post Reactions -->
<div>
<post-reaction-list
can-react
:parent-id="id"
:reactions="(post as any).reactions || {}"
:reactions-made="(post as any).reactionsMade || {}"
@react="handleReaction"
/>
</div> </div>
</div> </div>
</v-container> </v-container>

View File

@@ -73,11 +73,19 @@ export interface SnPost {
publisherId: string; publisherId: string;
publisher: SnPublisher; publisher: SnPublisher;
awards: unknown | null; awards: unknown | null;
tags: string[]; tags: SnPostCategory[];
categories: string[]; categories: SnPostCategory[];
isTruncated: boolean; isTruncated: boolean;
resourceIdentifier: string; resourceIdentifier: string;
createdAt: string; createdAt: string;
updatedAt: string; updatedAt: string;
deletedAt: string | null; deletedAt: string | null;
} }
export interface SnPostCategory {
id: string;
slug: string;
name?: string;
posts?: SnPost[];
usage?: number;
}