Skip to content

Commit

Permalink
feat: 初步完成 Message 页面编写
Browse files Browse the repository at this point in the history
  • Loading branch information
nonhana committed Nov 25, 2024
1 parent 191c591 commit 00f6f8e
Show file tree
Hide file tree
Showing 14 changed files with 165 additions and 58 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<script setup lang="ts">
import type { TransitionProps } from 'vue'
import type { DialogOptions } from './useDialog'
import type { DialogOptions } from '~/composables/useDialog'
const props = withDefaults(defineProps<DialogOptions>(), {
title: '默认标题',
Expand Down
2 changes: 1 addition & 1 deletion components/hana/Message/Container.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script setup lang="ts">
import type { MessageOptions } from './useMessage'
import type { MessageOptions } from '~/composables/useMessage'
import Message from './Item.vue'
interface MessageItem extends MessageOptions {
Expand Down
2 changes: 1 addition & 1 deletion components/hana/Message/Item.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script setup lang="ts">
import type { MessageOptions } from './useMessage'
import type { MessageOptions } from '~/composables/useMessage'
const props = withDefaults(defineProps<MessageOptions>(), {
message: '默认消息',
Expand Down
2 changes: 0 additions & 2 deletions components/main/Header/User.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
<script setup lang="ts">
import useDialog from '~/components/hana/Dialog/useDialog'
import useMessage from '~/components/hana/Message/useMessage'
import { useStore } from '~/store'
const { t } = useI18n()
Expand Down
52 changes: 33 additions & 19 deletions components/thoughts/Input.vue
Original file line number Diff line number Diff line change
@@ -1,31 +1,45 @@
<script setup lang="ts">
import dayjs from 'dayjs'
import { useStore } from '~/store'
import type { MessageItem } from '~/types/message'
const emits = defineEmits<{
(e: 'published'): void
}>()
const { messagesStore } = useStore()
const { newMessages } = messagesStore
const { callHanaMessage } = useMessage()
const value = ref('')
function handlePublish() {
if (value.value === '')
async function handlePublish() {
if (value.value === '') {
callHanaMessage({
type: 'error',
message: '内容不能为空',
})
return
const messageItem: MessageItem = {
id: newMessages.length + 1,
}
const objData = {
content: value.value,
author: {
id: 1,
name: 'Hana',
site: '/about',
avatar: '/images/avatar.webp',
}
const { data } = await useAsyncData('post-message', () => $fetch('/api/messages/post', {
headers: {
Authorization: `Bearer ${localStorage.getItem('token')}`,
},
publishedAt: dayjs().format('YYYY-MM-DD HH:mm:ss'),
editedAt: '',
isMe: true,
method: 'POST',
body: JSON.stringify(objData),
}))
if (data.value?.success) {
value.value = ''
callHanaMessage({
type: 'success',
message: '发布成功',
})
emits('published')
}
else {
const errorList = data.value?.payload?.map(item => item.message).join(', ')
callHanaMessage({
type: 'error',
message: errorList || data.value?.statusMessage || '发布失败',
})
}
messagesStore.addMessage(messageItem)
value.value = ''
}
function handleKeyDown(event: KeyboardEvent) {
Expand Down
22 changes: 15 additions & 7 deletions components/thoughts/Messages/Item.vue
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<script setup lang="ts">
import { useStore } from '~/store'
import type { MessageItem } from '~/types/message'
const props = withDefaults(defineProps<{
Expand All @@ -8,6 +9,9 @@ const props = withDefaults(defineProps<{
index: 0,
})
const { userStore } = useStore()
const isMe = computed(() => props.message.author!.id === userStore.userInfo?.id)
const opacity = ref(0)
const top = ref('10px')
Expand All @@ -30,25 +34,29 @@ watch(() => props.index, () => resetAnimation)
<template>
<div
class="relative flex items-start gap-5"
:class="{ 'flex-row-reverse self-end': message.isMe }"
:class="{ 'flex-row-reverse self-end': isMe }"
:style="{ transition: `all 0.2s ${index * 0.1}s`, opacity, top }"
>
<div class="flex flex-col items-center gap-2">
<NuxtImg :src="message.author.avatar" class="rounded-full" width="40" height="40" />
<NuxtLink :to="message.author.site" class="text-text hover:text-hana-blue">
<span>{{ message.author.name }}</span>
<NuxtImg v-if="message.author!.avatar" :src="message.author!.avatar" width="40" height="40" class="cursor-pointer rounded-full" />
<div v-else class="flex size-10 cursor-pointer items-center justify-center rounded-full bg-hana-blue text-xl text-white">
<span>{{ message.author!.username[0] }}</span>
</div>
<NuxtLink v-if="message.author!.site" :to="message.author!.site" class="text-text hover:text-hana-blue">
<span>{{ message.author!.username }}</span>
</NuxtLink>
<span v-else>{{ message.author!.username }}</span>
</div>
<div class="flex flex-col gap-1">
<div v-if="message.replyTo" class="flex self-end text-text">
<div v-if="message.parent" class="flex self-end text-text">
<div class="line-clamp-1 max-w-40">
<span>{{ message.replyTo.content }}</span>
<span>{{ message.parent.content }}</span>
</div>
<Icon name="material-symbols:reply" />
</div>
<div
class="w-fit rounded-lg p-3"
:class="[message.isMe ? 'self-end bg-hana-blue-400 text-white' : 'bg-white text-text']"
:class="[isMe ? 'self-end bg-hana-blue-400 text-white' : 'bg-white text-text']"
>
<span class="whitespace-pre-wrap">{{ message.content }}</span>
</div>
Expand Down
35 changes: 29 additions & 6 deletions components/thoughts/Messages/index.vue
Original file line number Diff line number Diff line change
@@ -1,17 +1,40 @@
<script setup lang="ts">
import { useStore } from '~/store'
import type { MessageItem } from '~/types/message'
const { messagesStore } = useStore()
const { messages, newMessages, updateMessages } = messagesStore
const props = defineProps<{
needRefresh: boolean
}>()
onUnmounted(() => {
updateMessages()
const emits = defineEmits<{
(e: 'refreshed'): void
}>()
const { needRefresh } = toRefs(props)
const messages = ref<MessageItem[]>([])
async function fetchMessages() {
const data = await $fetch('/api/messages/list')
if (data.success) {
messages.value = data.payload ?? []
}
}
watch(needRefresh, async (newV) => {
if (newV) {
await fetchMessages()
emits('refreshed')
}
})
onMounted(async () => {
await fetchMessages()
})
</script>

<template>
<div class="relative mb-5 flex w-full flex-col gap-5">
<ThoughtsMessagesItem v-for="(message, index) in messages" :key="message.id" :index="index" :message="message" />
<ThoughtsMessagesItem v-for="message in newMessages" :key="message.id" :message="message" />
</div>
</template>
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { createVNode, render } from 'vue'
import Dialog from './index.vue'
import Dialog from '~/components/hana/Dialog.vue'

// 声明式调用 Dialog 的选项
// 如 <HanaDialog ... />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { ComponentPublicInstance } from 'vue'
import { createVNode, render } from 'vue'
import Container from './Container.vue'
import Container from '~/components/hana/Message/Container.vue'

export interface MessageOptions {
message?: string
Expand Down
12 changes: 10 additions & 2 deletions pages/thoughts.vue
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
<script setup lang="ts">
const needRefresh = ref(false)
function handlePublished() {
needRefresh.value = true
}
function handleRefreshed() {
needRefresh.value = false
}
</script>

<template>
<div class="w-full">
<ThoughtsMessages />
<ThoughtsInput />
<ThoughtsMessages :need-refresh="needRefresh" @refreshed="handleRefreshed" />
<ThoughtsInput @published="handlePublished" />
</div>
</template>
33 changes: 32 additions & 1 deletion server/api/messages/list.get.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,36 @@
import dayjs from 'dayjs'
import prisma from '~/lib/prisma'
import { formattedEventHandler } from '~/server/utils/formattedEventHandler'

export default formattedEventHandler(async () => {
return { payload: [] }
const messages = await prisma.message.findMany({
select: {
id: true,
parent: {
select: {
id: true,
content: true,
},
},
author: {
select: {
id: true,
username: true,
site: true,
avatar: true,
},
},
content: true,
publishedAt: true,
editedAt: true,
},
})

const result = messages.map(message => ({
...message,
publishedAt: dayjs(message.publishedAt).format('YYYY-MM-DD HH:mm:ss'),
editedAt: dayjs(message.editedAt).format('YYYY-MM-DD HH:mm:ss'),
}))

return { payload: result }
})
30 changes: 28 additions & 2 deletions server/api/messages/post.post.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,31 @@
import { z } from 'zod'
import prisma from '~/lib/prisma'
import { useZodVerify } from '~/server/composables/useZodVerify'
import { formattedEventHandler } from '~/server/utils/formattedEventHandler'

export default formattedEventHandler(async () => {
return { payload: [] }
const verifySchema = z.object({
content: z.string().min(1, { message: 'Content must not be empty' }).max(1024, { message: 'Content must not exceed 1024 characters' }),
})

export default formattedEventHandler(async (event) => {
const id = event.context.jwtPayload.id
const body = await readBody(event)
const { success, errorList, result } = useZodVerify(verifySchema, body)
if (!success) {
return {
statusCode: 400,
statusMessage: 'Invalid request body',
payload: errorList,
success: false,
}
}

const { content } = result

await prisma.message.create({
data: {
content,
authorId: id,
},
})
})
4 changes: 2 additions & 2 deletions server/middleware/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export default eventHandler((event) => {
if (!token) {
return {
statusCode: 401,
statusMessage: 'Unauthorized',
statusMessage: 'Unauthorized, please login',
success: false,
}
}
Expand All @@ -23,7 +23,7 @@ export default eventHandler((event) => {
catch {
return {
statusCode: 401,
statusMessage: 'Invalid token',
statusMessage: 'Invalid token, please login',
success: false,
}
}
Expand Down
23 changes: 11 additions & 12 deletions types/message.d.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
export interface MessageItem {
id: number
content: string
author: {
id: number
publishedAt: string
editedAt: string
parent: {
content: string
id: number
name: string
site: string
avatar: string
}
publishedAt: string // YYYY-MM-DD HH:mm
editedAt: string // YYYY-MM-DD HH:mm
isMe: boolean
replyTo?: {
} | null
author: {
id: number
content: string
}
username: string
site: string | null
avatar: string | null
} | null
}

0 comments on commit 00f6f8e

Please sign in to comment.