后端部分 (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. 创建文章列表页面
3. 创建文章表单组件
- <template>
- <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>
- </el-form>
- </template>
- <script setup lang="ts">
- const props = defineProps<{
- formData: any
- }>()
- const emit = defineEmits(['submit', 'cancel'])
- // 表单数据
- const form = ref({
- ...props.formData,
- translations: props.formData.translations || []
- })
- // 语言配置
- const languages = [
- { label: '中文', value: 'zh-CN' },
- { label: 'English', value: 'en-US' }
- ]
- const activeLang = ref(languages[0].value)
- // 获取翻译索引
- const getTransIndex = (lang: string) => {
- return form.value.translations.findIndex((t: any) => t.lang === lang)
- }
- // 获取或创建翻译
- const getTranslation = (lang: string) => {
- let trans = form.value.translations.find((t: any) => t.lang === lang)
- if (!trans) {
- trans = { lang }
- form.value.translations.push(trans)
- }
- return trans
- }
- // 表单验证规则
- const rules = {
- categoryId: [{ required: true, message: '请选择分类' }],
- translations: {
- title: [{ required: true, message: '请输入标题' }],
- content: [{ required: true, message: '请输入内容' }]
- }
- }
- // 提交表单
- const formRef = ref()
- const handleSubmit = async () => {
- if (!formRef.value) return
- await formRef.value.validate()
- emit('submit', form.value)
- }
- </script>
复制代码 4. 多语言配置
- 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')
- }
- })
复制代码 这个实现包含了:
- 后端完整的CRUD接口
- 多语言支持(中英文)
- 富文本编辑器支持
- 图片上传功能
- 分类管理
- 权限控制
以上就是golang+vue打造高效多语言博客系统的完整指南的详细内容,更多关于go vue多语言博客的资料请关注脚本之家其它相关文章!
来源:互联网
免责声明:如果侵犯了您的权益,请联系站长(1277306191@qq.com),我们会及时删除侵权内容,谢谢合作! |
|