🏠

完善网站功能

给博客添加发布新文章功能

讲师:乌鸦哥

让我们的博客变得更完整!

我们要实现什么功能?

把博客变成真正可用的发布平台 📝

目前的问题

我们的博客还不够"智能" 🤔

  • 文章都是写死在代码里的
  • 想发新文章还要改代码
  • 普通人根本用不了
  • 这哪里像个真正的博客?

我们要实现的目标

像微信公众号一样方便 ✨

  • 管理员后台 - 只有管理员能发文章
  • 在线编辑 - 网页上直接写文章
  • 一键发布 - 填表单就能发布
  • 即时预览 - 发布后立即可见

第一步:创建管理员页面

搭建我们的"编辑部" 🏢

为什么需要管理员页面?

就像报社的编辑部一样

  • 不是任何人都能发文章
  • 需要有个专门的工作区域
  • 要与普通读者页面区分开
  • 保证内容质量和安全性

创建 admin 页面


# 在 pages 目录下创建 admin 文件夹
mkdir pages/admin

# 创建管理员首页
touch pages/admin/index.vue

# 创建写文章页面
touch pages/admin/new-post.vue
            

就像在办公楼里开辟一个编辑部

设置登录验证

门口要有保安 🔐


<script setup>
// 检查是否已登录
const { data: user } = await useFetch('/api/user/me')

// 如果没有登录,跳转到登录页
if (!user.value) {
  await navigateTo('/login')
}

// 如果不是管理员,拒绝访问
if (user.value.role !== 'admin') {
  throw createError({
    statusCode: 403,
    statusMessage: '访问被拒绝:需要管理员权限'
  })
}
</script>

<template>
  <div>
    <h1>管理员控制台</h1>
    <p>欢迎回来,{{ user.name }}!</p>
  </div>
</template>
            

第二步:创建文章发布表单

设计我们的"投稿箱" 📋

表单需要哪些字段?

就像填写投稿表一样

  • 文章标题 - 吸引读者的第一要素
  • 文章内容 - 主要内容区域
  • 文章标签 - 分类和搜索用
  • 封面图片 - 提高颜值
  • 发布状态 - 草稿还是发布

创建发布表单


<template>
  <div class="max-w-4xl mx-auto p-6">
    <h1 class="text-3xl font-bold mb-8">发布新文章</h1>
    
    <form @submit.prevent="publishPost">
      <!-- 文章标题 -->
      <div class="mb-6">
        <label class="block text-sm font-medium mb-2">文章标题</label>
        <input 
          v-model="post.title"
          type="text" 
          class="w-full p-3 border rounded-lg"
          placeholder="请输入吸引人的标题..."
          required
        >
      </div>

      <!-- 文章内容 -->
      <div class="mb-6">
        <label class="block text-sm font-medium mb-2">文章内容</label>
        <textarea 
          v-model="post.content"
          rows="15"
          class="w-full p-3 border rounded-lg"
          placeholder="在这里写下你的精彩内容..."
          required
        ></textarea>
      </div>

      <!-- 提交按钮 -->
      <button 
        type="submit"
        class="bg-blue-500 text-white px-6 py-3 rounded-lg hover:bg-blue-600"
      >
        发布文章
      </button>
    </form>
  </div>
</template>
            

添加表单验证

质量控制很重要 ✅


<script setup>
const post = ref({
  title: '',
  content: '',
  tags: [],
  status: 'draft'
})

// 表单验证
const validatePost = () => {
  if (!post.value.title.trim()) {
    alert('请输入文章标题')
    return false
  }
  
  if (post.value.title.length > 100) {
    alert('标题太长了,请控制在100字以内')
    return false
  }
  
  if (!post.value.content.trim()) {
    alert('文章内容不能为空')
    return false
  }
  
  if (post.value.content.length < 50) {
    alert('文章内容太短了,至少要50字哦')
    return false
  }
  
  return true
}
</script>
            

第三步:创建后端接口

搭建数据处理中心 🔧

我们需要哪些接口?

像邮局的不同服务窗口

  • POST /api/posts - 发布新文章
  • GET /api/posts - 获取文章列表
  • GET /api/posts/[id] - 获取单篇文章
  • PUT /api/posts/[id] - 修改文章
  • DELETE /api/posts/[id] - 删除文章

创建发布文章接口


// server/api/posts/index.post.js
import { PrismaClient } from '@prisma/client'

const prisma = new PrismaClient()

export default defineEventHandler(async (event) => {
  try {
    // 获取请求数据
    const body = await readBody(event)
    
    // 验证用户权限
    const user = await getCurrentUser(event)
    if (!user || user.role !== 'admin') {
      throw createError({
        statusCode: 403,
        statusMessage: '没有权限发布文章'
      })
    }
    
    // 创建新文章
    const newPost = await prisma.post.create({
      data: {
        title: body.title,
        content: body.content,
        authorId: user.id,
        status: body.status || 'published',
        createdAt: new Date()
      }
    })
    
    return {
      success: true,
      message: '文章发布成功!',
      post: newPost
    }
    
  } catch (error) {
    throw createError({
      statusCode: 500,
      statusMessage: '发布文章失败:' + error.message
    })
  }
})
            

创建获取文章列表接口


// server/api/posts/index.get.js
export default defineEventHandler(async (event) => {
  try {
    const query = getQuery(event)
    const page = parseInt(query.page) || 1
    const limit = parseInt(query.limit) || 10
    
    // 获取文章列表(只返回已发布的)
    const posts = await prisma.post.findMany({
      where: {
        status: 'published'
      },
      orderBy: {
        createdAt: 'desc'
      },
      skip: (page - 1) * limit,
      take: limit,
      include: {
        author: {
          select: {
            name: true,
            avatar: true
          }
        }
      }
    })
    
    // 获取总数
    const total = await prisma.post.count({
      where: { status: 'published' }
    })
    
    return {
      posts,
      pagination: {
        page,
        limit,
        total,
        pages: Math.ceil(total / limit)
      }
    }
    
  } catch (error) {
    throw createError({
      statusCode: 500,
      statusMessage: '获取文章列表失败'
    })
  }
})
            

第四步:完成整个流程

把所有部件组装起来 🔗

前端提交表单


// 在 pages/admin/new-post.vue 中
const publishPost = async () => {
  // 验证表单
  if (!validatePost()) return
  
  try {
    // 显示加载状态
    const loading = ref(true)
    
    // 发送请求到后端
    const { data } = await $fetch('/api/posts', {
      method: 'POST',
      body: {
        title: post.value.title,
        content: post.value.content,
        tags: post.value.tags,
        status: post.value.status
      }
    })
    
    // 成功提示
    alert('文章发布成功!')
    
    // 跳转到文章详情页
    await navigateTo(`/posts/${data.post.id}`)
    
  } catch (error) {
    // 错误处理
    alert('发布失败:' + error.message)
  } finally {
    loading.value = false
  }
}
            

更新首页显示


<!-- pages/index.vue -->
<script setup>
// 获取最新文章列表
const { data: postsData } = await useFetch('/api/posts', {
  query: {
    limit: 6
  }
})

const posts = computed(() => postsData.value?.posts || [])
</script>

<template>
  <div>
    <h1>最新文章</h1>
    
    <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
      <article 
        v-for="post in posts" 
        :key="post.id"
        class="bg-white rounded-lg shadow-md p-6"
      >
        <h2 class="text-xl font-bold mb-2">
          <NuxtLink :to="`/posts/${post.id}`">
            {{ post.title }}
          </NuxtLink>
        </h2>
        
        <p class="text-gray-600 mb-4">
          {{ post.content.substring(0, 150) }}...
        </p>
        
        <div class="flex justify-between text-sm text-gray-500">
          <span>作者:{{ post.author.name }}</span>
          <span>{{ formatDate(post.createdAt) }}</span>
        </div>
      </article>
    </div>
  </div>
</template>
            

完整的工作流程

从想法到发布的完整路径 🛤️

  1. 管理员登录 → 访问后台管理页面
  2. 填写表单 → 输入标题、内容等信息
  3. 提交发布 → 前端验证后发送到后端
  4. 后端处理 → 验证权限,保存到数据库
  5. 更新展示 → 首页自动显示新文章
  6. 用户浏览 → 普通用户可以看到新内容

优化和扩展功能

让博客更加专业 ⭐

用户体验优化

  • 自动保存草稿 - 避免内容丢失
  • 实时预览 - 边写边看效果
  • 图片上传 - 支持拖拽上传
  • Markdown支持 - 更方便的格式化

功能扩展

  • 评论系统 - 读者互动
  • 点赞功能 - 内容反馈
  • 搜索功能 - 快速找文章
  • RSS订阅 - 内容分发
  • 访问统计 - 了解读者行为

总结回顾

  • 管理员后台 - 安全的内容管理区域
  • 发布表单 - 友好的内容创作界面
  • 后端接口 - 强大的数据处理能力
  • 完整流程 - 从创作到发布的闭环
  • 功能扩展 - 无限的可能性

恭喜!你的博客现在已经是一个真正可用的发布平台了! 🎉

接下来我们可以继续添加更多酷炫的功能