前面章节我们构思了golang博客后台所需要的功能模块,其中最开始部分便是后台设置。

后台设置中,我们一共分成了四块。它们分别是:全局设置、功能设置、首页TDK设置、导航设置。

后台设置中的全局设置

全局设置中包含了网站名称、网站地址、网站logo、备案号码、版权信息、后台地址、网站状态、闭站提示等信息。

  • 网站名称 网站名称用于前端博客页面的调用,一般上,在SEO功能上,一般用于添加为标题后缀等地方。
  • 网站地址 指该网站的网址,如:https://www.kandaoni.com,用来生成全站的绝对地址。
  • 网站logo 在后台可以方便的更换logo,网站logo是一个网站的灵魂,当然这不是必须设置的不设置的话,前端header的地方就会用网站名称代替,设置了的话,则会显示logo。
  • 备案号码 最近国内对备案的检查越来越严格了,不备案的网站,不能在国内服务器上上线,备案了,也必须悬挂备案号,并且要连接到备案中心。因此这里添加一个备案号码填写的地方,可以方便的设置备案号。填写了备案号码后,前台调用的时候,会自动添加跳转到备案中心的连接,再也不怕备案被注销了。
  • 版权信息 这里可以自定义版权信息,用于显示在博客的页脚的地方,相当于SEO优化中的一句话导航,这里可以布局关键词,增加首页优化关键词效率。
  • 后台地址 往往一个网站是否容易被入侵,后台地址暴露占了很大的原因。这个功能的优势是,你可以自定义后台地址,再也不担心后台地址被暴露的风险了。
  • 网站状态 当你的博客暂时不想打开了,可以将网站状态设置为关闭,整个网站就不能打开了。当然,除了后台,后台依然是可以被访问的,要不就再也不能打开网站了。
  • 闭站提示 配合网站状态使用,在闭站的时候,想展示的内容。

admin-setting

我们先实现前端的页面。我们在 manage/src/views/ 文件夹下创建一个setting文件夹,并添加上system.html

<title>全局设置</title>

<div class="layui-card layadmin-header">
  <div class="layui-breadcrumb" lay-filter="breadcrumb">
    <a lay-href="">后台首页</a>
    <a><cite>后台设置</cite></a>
    <a><cite>全局设置</cite></a>
  </div>
</div>

<div class="layui-fluid">
  <div class="layui-row layui-col-space15">
    <div class="layui-col-md12">
      <div class="layui-card">
        <div class="layui-card-header">全局设置</div>
        <div class="layui-card-body" pad15>
          <script type="text/html" template lay-url="/setting/system" lay-done="layui.form.render();">
            <div class="layui-form" style="padding: 20px 0 0 0;">
          <div class="layui-form-item">
            <label class="layui-form-label">网站名称</label>
            <div class="layui-input-inline">
              <input type="text" name="site_name" value="{{ d.data.site_name || '' }}"  lay-verify="required" placeholder="请输入...(必填)" autocomplete="off" class="layui-input">
            </div>
            <div class="layui-form-mid layui-word-aux">该名称会以后缀形式显示在网站标题上</div>
          </div>
          <div class="layui-form-item">
            <label class="layui-form-label">网站地址</label>
            <div class="layui-input-inline">
              <input type="text" name="base_url" value="{{ d.data.base_url || '' }}"  lay-verify="required" placeholder="请输入...(必填)" autocomplete="off" class="layui-input">
            </div>
            <div class="layui-form-mid layui-aux-word">指该网站的网址,如:https://www.kandaoni.com,用来生成全站的绝对地址</div>
          </div>
          <div class="layui-form-item">
            <label class="layui-form-label">网站LOGO</label>
            <div class="layui-input-inline">
              <button type="button" class="layui-btn" id="site-logo">
                <i class="layui-icon">&#xe67c;</i>上传图片
              </button>
            </div>
            <div class="layui-form-mid">
              <input type="hidden" name="site_logo" value="{{ d.data.site_logo || '' }}" id="site-logo-input">
              <img src="{{ d.data.site_logo || '' }}" class="site-logo-img" id="site-logo-img"/>
            </div>
            <div class="layui-form-mid layui-word-aux">网站LOGO会显示在页头</div>
          </div>
          <div class="layui-form-item">
            <label class="layui-form-label">备案号码</label>
            <div class="layui-input-inline">
              <input type="text" name="site_icp" value="{{ d.data.site_icp || '' }}" autocomplete="off" class="layui-input">
            </div>
            <div class="layui-form-mid layui-word-aux">ICP备案查询地址:<a href="https://beian.miit.gov.cn/" target="_blank">beian.miit.gov.cn</a>,只需填主体备案号即可。没有则不填。</div>
          </div>
          <div class="layui-form-item">
            <label class="layui-form-label">版权信息</label>
            <div class="layui-input-inline">
              <textarea class="layui-textarea" name="site_copyright" rows="3">{{ d.data.site_copyright || '' }}</textarea>
            </div>
            <div class="layui-form-mid layui-word-aux">版权信息会显示在页尾</div>
          </div>
          <div class="layui-form-item">
            <label class="layui-form-label">后台路径</label>
            <div class="layui-input-inline">
              <input type="text" name="admin_uri" value="{{ d.data.admin_uri || '' }}" autocomplete="off" class="layui-input">
            </div>
            <div class="layui-form-mid layui-word-aux">可以自定义后台访问路径,减少被暴露概率。比如<cite>/manage</cite>。更改后需要重启才能生效</div>
          </div>
          <div class="layui-form-item">
            <label class="layui-form-label">网站状态</label>
            <div class="layui-input-inline">
              <input type="radio" name="site_close" value="0" title="正常" {{ d.data.site_close != 1 ? 'checked' : '' }}>
              <input type="radio" name="site_close" value="1" title="闭站" {{ d.data.site_close == 1 ? 'checked' : '' }}>
            </div>
          </div>
          <div class="layui-form-item">
            <label class="layui-form-label">闭站提示</label>
            <div class="layui-input-inline">
              <textarea class="layui-textarea" name="site_close_tips" rows="3">{{ d.data.site_close_tips || '' }}</textarea>
            </div>
            <div class="layui-form-mid layui-word-aux">站点关闭后,将会显示上面的提示。支持html标签</div>
          </div>
          
          <div class="layui-form-item">
            <label class="layui-form-label"></label>
            <div class="layui-input-inline">
              <input type="button" lay-submit lay-filter="system-submit" value="确认" class="layui-btn">
            </div>
          </div>
        </div>
        </script>
        </div>
      </div>
    </div>
  </div>
</div>

<script>
  layui.use('setting', layui.factory('setting'));
</script>

html部分代码比较简单,照抄就可以,里面的script包裹的html,会自动渲染。

接着在 manage/src/controller/ 文件夹下创建一个setting.js文件:

/**

 @Name:layuiAdmin 设置
 @Author:贤心
 @Site:http://www.layui.com/admin/
 @License: LPPL
    
 */
 
layui.define(['form', 'upload', 'table'], function(exports){
  var $ = layui.$
  ,layer = layui.layer
  ,laytpl = layui.laytpl
  ,setter = layui.setter
  ,view = layui.view
  ,admin = layui.admin
  ,form = layui.form
  ,table = layui.table
  ,upload = layui.upload;

  //logo上传
  var uploadInst = upload.render({
    elem: '#site-logo' //绑定元素
    ,url: '/attachment/upload' //上传接口
    ,done: function(res){
      //上传完毕回调
      $('#site-logo-input').val(res.data.src);
      $('#site-logo-img').prop('src', res.data.src);
    }
    ,error: function(){
      //请求异常回调
      layer.msg("上传出错");
    }
  });

  //网站设置
  form.on('submit(system-submit)', function(obj){
    delete obj.field.file;
    obj.field.site_close = Number(obj.field.site_close);
    admin.req({
      url: '/setting/system'
      ,data: obj.field
      ,type: 'post'
      ,done: function(res){
        layer.msg(res.msg, {
          offset: '15px'
          ,icon: 1
        });
      }
      ,fail: function(res){
        layer.msg(res.msg, {
          offset: '15px'
          ,icon: 2
        });
      }
    });
  });

  //对外暴露的接口
  exports('setting', {});
});

全局设置中,我们需要表单提交,和博客网站的logo上传,因此在js中,我们实现了两个部分的功能,一个是上传图片,另一个是提交表单。又因为我们的后台是用golang写的,golang是一个强类型语言,因此我们在提交表单的时候,需要将数字字符串转换成数字类型:

obj.field.site_close = Number(obj.field.site_close);

前端页面这样写就完成了,我们接着实现golang部分的代码:

后台全局设置功能实现

接着在controller/manageController文件夹下创建一个 setting.go文件:

package manageController

import (
	"github.com/kataras/iris/v12"
	"irisweb/config"
	"irisweb/model"
	"irisweb/provider"
	"irisweb/request"
	"strings"
)

func SettingSystem(ctx iris.Context) {
	system := config.JsonData.System

	ctx.JSON(iris.Map{
		"code": config.StatusOK,
		"msg":  "",
		"data": system,
	})
}

func SettingSystemForm(ctx iris.Context) {
	var req request.SystemConfig
	if err := ctx.ReadJSON(&req); err != nil {
		ctx.JSON(iris.Map{
			"code": config.StatusFailed,
			"msg":  err.Error(),
		})
		return
	}

	if !strings.HasPrefix(req.AdminUri, "/") || len(req.AdminUri) < 2 {
		ctx.JSON(iris.Map{
			"code": config.StatusFailed,
			"msg":  "后台路径需要以/开头",
		})
		return
	}

	config.JsonData.System.SiteName = req.SiteName
	config.JsonData.System.SiteLogo = req.SiteLogo
	config.JsonData.System.SiteIcp = req.SiteIcp
	config.JsonData.System.SiteCopyright = req.SiteCopyright
	config.JsonData.System.AdminUri = req.AdminUri
	config.JsonData.System.SiteClose = req.SiteClose
	config.JsonData.System.SiteCloseTips = req.SiteCloseTips

	err := config.WriteConfig()
	if err != nil {
		ctx.JSON(iris.Map{
			"code": config.StatusFailed,
			"msg":  err.Error(),
		})
		return
	}

	ctx.JSON(iris.Map{
		"code": config.StatusOK,
		"msg":  "配置已更新",
	})
}

这里我们需要实现两个函数,一个的读取全局配置的函数SettingSystem(),一个是保存全局配置的函数SettingSystemForm()

因为我们将全局配置等配置信息,存放在 config.json 中,因此,我们还需在config文件夹中增加对应的配置:

config文件夹下增加setting.go:

package config

type systemConfig struct {
	SiteName      string `json:"site_name"`
	SiteLogo      string `json:"site_logo"`
	SiteIcp       string `json:"site_icp"`
	SiteCopyright string `json:"site_copyright"`
	BaseUrl       string `json:"base_url"`
	AdminUri      string `json:"admin_uri"`
	SiteClose     int    `json:"site_close"`
	SiteCloseTips string `json:"site_close_tips"`
}

接着编辑 config/config.go ,在configData的struct中,增加systemConfig 的引用:

type configData struct {
	DB     mysqlConfig  `json:"mysql"`
	Server serverConfig `json:"server"`
	//setting
  System  systemConfig  `json:"system"`
}

由于我们的配置中,允许后台设置自定义的后台地址,但是默认这里是空的,我们还需要先给一个预设的后台地址,修改initJSON()函数,增加对应代码:

	//给后台添加默认值
	if JsonData.System.AdminUri == "" {
		JsonData.System.AdminUri = "/manage"
	}

config部分配置完毕,我们再增加后端路由即可实现后台第一个配置项全局设置了。

为了区分,我们在route文件夹下,增加manage.go文件,用来统一存放和管理后台路由:

package route

import (
	"fmt"
	"github.com/kataras/iris/v12"
	"irisweb/config"
	"irisweb/controller"
	"irisweb/controller/manageController"
	"irisweb/middleware"
)

func manageRoute(app *iris.Application) {
	manage := app.Party(config.JsonData.System.AdminUri, controller.Inspect)
	{
		manage.HandleDir("/", fmt.Sprintf("%smanage", config.ExecPath))
		manage.Post("/user/login", manageController.UserLogin)

		user := manage.Party("/user", middleware.ManageAuth)
		{
			user.Get("/detail", manageController.UserDetail)
			user.Post("/detail", manageController.UserDetailForm)
			user.Post("/logout", manageController.UserLogout)
		}

		setting := manage.Party("/setting", middleware.ManageAuth)
		{
			setting.Get("/system", manageController.SettingSystem)

			setting.Post("/system", manageController.SettingSystemForm)
		}
	}
}

路由上,我们使用app.Party(config.JsonData.System.AdminUri, controller.Inspect),来动态绑定后台设置的后台访问路径。

至此我们实现的后台的第一步。完成之后,我们启动golang项目来验证下结果,如果不出意外,就可以修改全局设置信息了。

教程用例源码

完整的项目示例代码托管在GitHub上,访问github.com/fesiong/goblog 可以查看完整的教程项目源代码,建议在查看教程的同时,认真对照源码,可以有效提高码代码速度和加深对博客项目的认识。建议直接fork一份来在上面做修改。欢迎点Star。