欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

golang+vue打造高效多語言博客系統(tǒng)的完整指南

 更新時間:2025年03月24日 10:41:50   作者:程序員愛釣魚  
這篇文章主要為大家詳細介紹了如何使用golang和vue打造一個高效多語言博客系統(tǒng),本文為大家附上了完整版指南,有需要的小伙伴可以參考一下

后端部分 (Go)

1. 首先創(chuàng)建文章相關的數(shù)據(jù)模型

package model

import (
	"gorm.io/gorm"
)

// Article 文章主表
type Article struct {
	gorm.Model
	Status     uint8  `json:"status" gorm:"default:1"` // 狀態(tài):0-禁用 1-啟用
	Sort       int    `json:"sort" gorm:"default:0"`   // 排序
	AuthorId   uint   `json:"authorId"`                // 作者ID
	CategoryId uint   `json:"categoryId"`              // 分類ID
	Thumbnail  string `json:"thumbnail"`               // 縮略圖
	
	// 關聯(lián)
	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. 創(chuàng)建服務層

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)
	}
	
	// 預加載翻譯數(shù)據(jù)
	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. 創(chuàng)建控制器

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 創(chuàng)建文章
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. 創(chuàng)建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. 創(chuàng)建文章列表頁面

<template>
  <div>
    <el-card>
      <!-- 搜索欄 -->
      <el-form :inline="true" :model="searchForm">
        <el-form-item>
          <el-select v-model="searchForm.lang" placeholder="選擇語言">
            <el-option label="中文" value="zh-CN" />
            <el-option label="English" value="en-US" />
          </el-select>
        </el-form-item>
        <el-form-item>
          <el-button type="primary" @click="loadData">搜索</el-button>
          <el-button @click="handleAdd">新增</el-button>
        </el-form-item>
      </el-form>
      
      <!-- 數(shù)據(jù)表格 -->
      <el-table :data="tableData" v-loading="loading">
        <el-table-column prop="id" label="ID" width="80" />
        <el-table-column label="標題">
          <template #default="{ row }">
            {{ row.translations?.[0]?.title }}
          </template>
        </el-table-column>
        <el-table-column label="分類">
          <template #default="{ row }">
            {{ row.category?.translations?.[0]?.name }}
          </template>
        </el-table-column>
        <el-table-column prop="status" label="狀態(tài)">
          <template #default="{ row }">
            <el-tag :type="row.status === 1 ? 'success' : 'info'">
              {{ row.status === 1 ? '啟用' : '禁用' }}
            </el-tag>
          </template>
        </el-table-column>
        <el-table-column label="操作" width="200">
          <template #default="{ row }">
            <el-button type="primary" link @click="handleEdit(row)">編輯</el-button>
            <el-button type="danger" link @click="handleDelete(row)">刪除</el-button>
          </template>
        </el-table-column>
      </el-table>
      
      <!-- 分頁 -->
      <div class="pagination-container">
        <el-pagination
          v-model:current-page="page"
          v-model:page-size="pageSize"
          :total="total"
          @current-change="loadData"
        />
      </div>
    </el-card>
    
    <!-- 編輯彈窗 -->
    <el-dialog
      :title="dialogTitle"
      v-model="dialogVisible"
      width="800px"
    >
      <article-form
        v-if="dialogVisible"
        :form-data="formData"
        @submit="handleSubmit"
        @cancel="dialogVisible = false"
      />
    </el-dialog>
  </div>
</template>

<script setup lang="ts">
const { getList, remove } = useArticleApi()

// 狀態(tài)定義
const searchForm = ref({
  lang: 'zh-CN'
})
const loading = ref(false)
const tableData = ref([])
const page = ref(1)
const pageSize = ref(10)
const total = ref(0)
const dialogVisible = ref(false)
const dialogTitle = ref('')
const formData = ref<any>({})

// 加載數(shù)據(jù)
const loadData = async () => {
  loading.value = true
  try {
    const { data } = await getList({
      page: page.value,
      pageSize: pageSize.value,
      lang: searchForm.value.lang
    })
    tableData.value = data.value.list
    total.value = data.value.total
  } finally {
    loading.value = false
  }
}

// 處理新增
const handleAdd = () => {
  formData.value = {
    status: 1,
    translations: [{ lang: searchForm.value.lang }]
  }
  dialogTitle.value = '新增文章'
  dialogVisible.value = true
}

// 處理編輯
const handleEdit = (row: any) => {
  formData.value = { ...row }
  dialogTitle.value = '編輯文章'
  dialogVisible.value = true
}

// 處理刪除
const handleDelete = async (row: any) => {
  try {
    await ElMessageBox.confirm('確認刪除該文章嗎?')
    await remove(row.id)
    ElMessage.success('刪除成功')
    loadData()
  } catch (err) {
    // 取消刪除時不顯示錯誤
    if (err !== 'cancel') {
      ElMessage.error('刪除失敗')
    }
  }
}

// 處理表單提交
const handleSubmit = async (data: any) => {
  try {
    if (data.id) {
      await update(data)
    } else {
      await create(data)
    }
    ElMessage.success('保存成功')
    dialogVisible.value = false
    loadData()
  } catch (err) {
    ElMessage.error('保存失敗')
  }
}

// 初始加載
onMounted(() => {
  loadData()
})
</script>

<style scoped>
.pagination-container {
  margin-top: 20px;
  text-align: right;
}
</style>

3. 創(chuàng)建文章表單組件

<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="狀態(tài)" 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'])

// 表單數(shù)據(jù)
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)
}

// 獲取或創(chuàng)建翻譯
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
}

// 表單驗證規(guī)則
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')
  }
})

這個實現(xiàn)包含了:

  • 后端完整的CRUD接口
  • 多語言支持(中英文)
  • 富文本編輯器支持
  • 圖片上傳功能
  • 分類管理
  • 權限控制

以上就是golang+vue打造高效多語言博客系統(tǒng)的完整指南的詳細內容,更多關于go vue多語言博客的資料請關注腳本之家其它相關文章!

相關文章

  • Golang中rune和byte的使用與區(qū)別

    Golang中rune和byte的使用與區(qū)別

    rune和byte都是Go語言中表示單個字符的類型,本文就來介紹一下Golang中rune和byte的使用與區(qū)別,具有一定的參考價值,感興趣的可以了解一下
    2025-02-02
  • golang配置管理神器Viper使用教程

    golang配置管理神器Viper使用教程

    這篇文章主要為大家介紹了golang配置管理神器Viper使用教程,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步早日升職加薪
    2022-04-04
  • Go指針的具體使用

    Go指針的具體使用

    本文主要介紹了Go指針的具體使用,包括使用指針、空指針、指針數(shù)組、指向指針的指針等,具有一定的參考價值,感興趣的可以了解一下
    2023-11-11
  • golang實現(xiàn)對docker容器心跳監(jiān)控功能

    golang實現(xiàn)對docker容器心跳監(jiān)控功能

    這篇文章主要介紹了golang實現(xiàn)對docker容器心跳監(jiān)控功能,本文給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下
    2019-09-09
  • Go語言中的流程控制結構和函數(shù)詳解

    Go語言中的流程控制結構和函數(shù)詳解

    這篇文章主要介紹了Go語言中的流程控制結構和函數(shù)詳解,本文詳細講解了if、goto、for、switch等控制語句,同時對函數(shù)相關知識做了講解,需要的朋友可以參考下
    2014-10-10
  • GoLand安裝與環(huán)境配置的完整步驟

    GoLand安裝與環(huán)境配置的完整步驟

    作為一個go語言程序員,覺得自己有義務為go新手開一條更簡單便捷的上手之路,下面這篇文章主要給大家介紹了關于GoLand安裝與環(huán)境配置的完整步驟,文中通過圖文介紹的非常詳細,需要的朋友可以參考下
    2022-12-12
  • 使用Go語言創(chuàng)建靜態(tài)文件服務器問題

    使用Go語言創(chuàng)建靜態(tài)文件服務器問題

    這篇文章主要介紹了使用Go語言創(chuàng)建靜態(tài)文件服務器,本文通過試了代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-03-03
  • golang 比較浮點數(shù)的大小方式

    golang 比較浮點數(shù)的大小方式

    這篇文章主要介紹了golang 比較浮點數(shù)的大小方式,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-05-05
  • 解決go echo后端處理跨域的兩種操作方式

    解決go echo后端處理跨域的兩種操作方式

    這篇文章主要介紹了解決go echo后端處理跨域的兩種操作方式,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-12-12
  • go簡介及國內鏡像源配置全過程

    go簡介及國內鏡像源配置全過程

    本文介紹了Go語言的基本概念和環(huán)境配置,包括GOROOT、GOPATH和GOMODULE的設置,還展示了如何在IDEA中配置Go語言的開發(fā)環(huán)境,并通過一個簡單的“HelloWorld”項目來熟悉Go語言的基本語法和開發(fā)流程
    2025-01-01

最新評論