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實現(xiàn)對docker容器心跳監(jiān)控功能
這篇文章主要介紹了golang實現(xiàn)對docker容器心跳監(jiān)控功能,本文給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下2019-09-09使用Go語言創(chuàng)建靜態(tài)文件服務器問題
這篇文章主要介紹了使用Go語言創(chuàng)建靜態(tài)文件服務器,本文通過試了代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-03-03