从零到精通:用go+vue语言打造高效多语言博客系统的完整指南

后端部分 (Go)

  1. 首先创建文章相关的数据模型
    package model

import (
“gorm.io/gorm”
)

// Article 文章主表
type Article struct {
gorm.Model
Status uint8 json:"status" gorm:"default:1" // 状态:0-禁用 1-启用
Sort int json:"sort" gorm:"default:0" // 排序
AuthorId uint json:"authorId" // 作者ID
CategoryId uint json:"categoryId" // 分类ID
Thumbnail string json:"thumbnail" // 缩略图

// 关联
Translations []ArticleTranslation `json:"translations"`
Category     ArticleCategory      `json:"category"`

}

// ArticleTranslation 文章翻译表
type ArticleTranslation struct {
gorm.Model
ArticleId uint json:"articleId"
Lang string json:"lang" gorm:"size:5" // 语言代码 如:zh-CN, en-US
Title string json:"title" // 标题
Description string json:"description" // 描述
Content string json:"content" gorm:"type:text" // 内容
Keywords string json:"keywords" // SEO关键词
Slug string json:"slug" gorm:"uniqueIndex" // URL友好的标题
}

// ArticleCategory 文章分类
type ArticleCategory struct {
gorm.Model
ParentId uint json:"parentId"
Status uint8 json:"status" gorm:"default:1"
Sort int json:"sort" gorm:"default:0"
Translations []ArticleCategoryTranslation json:"translations"
}

// ArticleCategoryTranslation 分类翻译表
type ArticleCategoryTranslation struct {
gorm.Model
CategoryId uint json:"categoryId"
Lang string json:"lang" gorm:"size:5"
Name string json:"name"
Description string json:"description"
Slug string json:"slug" gorm:"uniqueIndex"
}
2. 创建服务层
package service

import (
“dagisku-server/app/model”
“dagisku-server/global”
)

type ArticleService struct{}

type ArticleListRequest struct {
Page int json:"page" form:"page"
PageSize int json:"pageSize" form:"pageSize"
Lang string json:"lang" form:"lang"
Status *uint8 json:"status" form:"status"
CategoryId *uint json:"categoryId" form:"categoryId"
}

func (s *ArticleService) GetList(req ArticleListRequest) (list []model.Article, total int64, err error) {
limit := req.PageSize
offset := req.PageSize * (req.Page - 1)

db := global.DB.Model(&model.Article{})

// 构建查询条件
if req.Status != nil {
    db = db.Where("status = ?", *req.Status)
}
if req.CategoryId != nil {
    db = db.Where("category_id = ?", *req.CategoryId)
}

// 预加载翻译数据
db = db.Preload("Translations", "lang = ?", req.Lang)
db = db.Preload("Category").Preload("Category.Translations", "lang = ?", req.Lang)

err = db.Count(&total).Error
if err != nil {
    return
}

err = db.Order("sort desc, id desc").Limit(limit).Offset(offset).Find(&list).Error
return

}

func (s *ArticleService) Create(article *model.Article) error {
return global.DB.Create(article).Error
}

func (s *ArticleService) Update(article *model.Article) error {
return global.DB.Save(article).Error
}

func (s *ArticleService) Delete(id uint) error {
return global.DB.Delete(&model.Article{}, id).Error
}
3. 创建控制器
package article

import (
“dagisku-server/app/model”
“dagisku-server/app/service”
“dagisku-server/utils/response”
“github.com/gin-gonic/gin”
)

type ArticleApi struct{}

// List 获取文章列表
func (api *ArticleApi) List(c *gin.Context) {
var req service.ArticleListRequest
if err := c.ShouldBindQuery(&req); err != nil {
response.FailWithMessage(err.Error(), c)
return
}

if req.Page == 0 {
    req.Page = 1
}
if req.PageSize == 0 {
    req.PageSize = 10
}

service := service.ArticleService{}
list, total, err := service.GetList(req)
if err != nil {
    response.FailWithMessage(err.Error(), c)
    return
}

response.OkWithData(gin.H{
    "list": list,
    "total": total,
}, c)

}

// Create 创建文章
func (api *ArticleApi) Create(c *gin.Context) {
var article model.Article
if err := c.ShouldBindJSON(&article); err != nil {
response.FailWithMessage(err.Error(), c)
return
}

service := service.ArticleService{}
if err := service.Create(&article); err != nil {
    response.FailWithMessage(err.Error(), c)
    return
}

response.Ok(c)

}

// Update 更新文章
func (api *ArticleApi) Update(c *gin.Context) {
var article model.Article
if err := c.ShouldBindJSON(&article); err != nil {
response.FailWithMessage(err.Error(), c)
return
}

service := service.ArticleService{}
if err := service.Update(&article); err != nil {
    response.FailWithMessage(err.Error(), c)
    return
}

response.Ok(c)

}

// Delete 删除文章
func (api *ArticleApi) Delete(c *gin.Context) {
id := c.Param(“id”)
service := service.ArticleService{}
if err := service.Delete(uint(id)); err != nil {
response.FailWithMessage(err.Error(), c)
return
}

response.Ok(c)

}
4. 注册路由
// … existing code …

type ApiGroup struct {
LoginApi
system.DBApi
system.JwtApi
// … other existing APIs …
article.ArticleApi
article.ArticleCategoryApi
}

// … existing code …
func InitAdminRouter(Router *gin.RouterGroup) {
adminRouter := Router.Group(“admin”)

// ... existing routes ...

// 文章相关路由
articleRouter := adminRouter.Group("article")
{
    articleRouter.GET("list", articleApi.List)
    articleRouter.POST("create", articleApi.Create)
    articleRouter.PUT("update", articleApi.Update)
    articleRouter.DELETE("delete/:id", articleApi.Delete)
}

}
前端部分 (Nuxt 3)

  1. 创建API请求
    import { useFetch } from ‘#app’

export const useArticleApi = () => {
const config = useRuntimeConfig()
const baseURL = config.public.apiBase

const getList = async (params: {
page: number
pageSize: number
lang: string
status?: number
categoryId?: number
}) => {
return await useFetch(‘/admin/article/list’, {
baseURL,
method: ‘GET’,
params
})
}

const create = async (data: any) => {
return await useFetch(‘/admin/article/create’, {
baseURL,
method: ‘POST’,
body: data
})
}

const update = async (data: any) => {
return await useFetch(‘/admin/article/update’, {
baseURL,
method: ‘PUT’,
body: data
})
}

const remove = async (id: number) => {
return await useFetch(/admin/article/delete/${id}, {
baseURL,
method: ‘DELETE’
})
}

return {
getList,
create,
update,
remove
}
}
2. 创建文章列表页面

  1. 创建文章表单组件

    <el-form
    ref=“formRef”
    :model=“form”
    :rules=“rules”
    label-width=“100px”
<el-tabs v-model="activeLang">
  <el-tab-pane
    v-for="lang in languages"
    :key="lang.value"
    :label="lang.label"
    :name="lang.value"
  >
    <el-form-item
      :prop="`translations.${getTransIndex(lang.value)}.title`"
      label="标题"
    >
      <el-input
        v-model="getTranslation(lang.value).title"
        placeholder="请输入标题"
      />
    </el-form-item>
    
    <el-form-item
      :prop="`translations.${getTransIndex(lang.value)}.description`"
      label="描述"
    >
      <el-input
        type="textarea"
        v-model="getTranslation(lang.value).description"
        placeholder="请输入描述"
      />
    </el-form-item>
    
    <el-form-item
      :prop="`translations.${getTransIndex(lang.value)}.content`"
      label="内容"
    >
      <editor
        v-model="getTranslation(lang.value).content"
        :height="400"
      />
    </el-form-item>
  </el-tab-pane>
</el-tabs>

<el-form-item label="分类" prop="categoryId">
  <el-select v-model="form.categoryId">
    <el-option
      v-for="item in categories"
      :key="item.id"
      :label="item.translations[0].name"
      :value="item.id"
    />
  </el-select>
</el-form-item>

<el-form-item label="缩略图" prop="thumbnail">
  <upload v-model="form.thumbnail" />
</el-form-item>

<el-form-item label="状态" prop="status">
  <el-switch
    v-model="form.status"
    :active-value="1"
    :inactive-value="0"
  />
</el-form-item>

<el-form-item>
  <el-button type="primary" @click="handleSubmit">保存</el-button>
  <el-button @click="$emit('cancel')">取消</el-button>
</el-form-item>
  1. 多语言配置
    import { createI18n } from ‘vue-i18n’

export default defineNuxtPlugin(({ vueApp }) => {
const i18n = createI18n({
legacy: false,
globalInjection: true,
locale: ‘zh-CN’,
messages: {
‘zh-CN’: {
article: {
title: ‘文章管理’,
list: ‘文章列表’,
add: ‘新增文章’,
edit: ‘编辑文章’,
// … 其他翻译
}
},
‘en-US’: {
article: {
title: ‘Article Management’,
list: ‘Article List’,
add: ‘New Article’,
edit: ‘Edit Article’,
// … 其他翻译
}
}
}
})

vueApp.use(i18n)
})
5. 前端路由配置
export default defineNuxtRouteMiddleware((to) => {
const token = useCookie(‘token’)

if (!token.value && to.path.startsWith(‘/admin’)) {
return navigateTo(‘/login’)
}
})
这个实现包含了:

  1. 后端完整的CRUD接口
  2. 多语言支持(中英文)
  3. 富文本编辑器支持
  4. 图片上传功能
  5. 分类管理
  6. 权限控制
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值