diff --git a/README.MD b/README.MD index 0a322f9..e6e01a4 100644 --- a/README.MD +++ b/README.MD @@ -107,3 +107,6 @@ gitee地址:[https://gitee.com/tiger1103/gfast-ui](https://gitee.com/tiger1103 > > 快来加入群聊【Gfast框架交流群】(群号865697297),发现精彩内容。 + +### 若框架对您有帮助,您可以捐赠表达一下心意: +> \ No newline at end of file diff --git a/app/service/cache_service/cache_values.go b/app/service/cache_service/cache_values.go index ce0efca..3663262 100644 --- a/app/service/cache_service/cache_values.go +++ b/app/service/cache_service/cache_values.go @@ -6,7 +6,7 @@ const ( AdminAuthRole AdminCmsMenu AdminConfigDict - GovProject + AdminBlogClassification ) //缓存TAG标签 @@ -15,6 +15,5 @@ const ( AdminCmsTag AdminSysConfigTag AdminModelTag - AdminDeptUserTag - GovProjectCateTag + AdminBlogTag ) diff --git a/config/config.toml b/config/config.toml index f5b02bc..9901c9e 100644 --- a/config/config.toml +++ b/config/config.toml @@ -58,7 +58,7 @@ #后台相关配置 [adminInfo] - notCheckAuthAdminIds = [1,31] #无需验证后台权限的用户id + notCheckAuthAdminIds = [1,2,31] #无需验证后台权限的用户id pageNum = 10 #后台列表分页长度 dataDir = "./data" #数据目录 diff --git a/library/response/plugin/blog/response.go b/library/response/plugin/blog/response.go new file mode 100644 index 0000000..31cc486 --- /dev/null +++ b/library/response/plugin/blog/response.go @@ -0,0 +1,14 @@ +package blog + +import ( + "gfast/library/response" + "github.com/gogf/gf/frame/g" + "github.com/gogf/gf/net/ghttp" + "github.com/gogf/gf/os/gview" +) + +func Response(r *ghttp.Request, tpl string, params ...gview.Params) error { + view := g.View() + view.SetPath("template/plugin/blog") + return response.WriteTpl(r, tpl, view, params...) +} diff --git a/plugin/blog/controller/home/index.go b/plugin/blog/controller/home/index.go new file mode 100644 index 0000000..8d1d009 --- /dev/null +++ b/plugin/blog/controller/home/index.go @@ -0,0 +1,323 @@ +package home + +import ( + "gfast/app/model/admin/plug_link" + "gfast/app/service/admin/plug_link_service" + "gfast/app/service/admin/plug_service" + "gfast/library/response" + "gfast/library/response/plugin/blog" + "gfast/plugin/blog/model/blog_classification" + "gfast/plugin/blog/model/blog_comment" + "gfast/plugin/blog/model/blog_log" + "gfast/plugin/blog/service/blog_service" + "github.com/gogf/gf/frame/g" + "github.com/gogf/gf/net/ghttp" + "github.com/gogf/gf/util/gvalid" +) + +type Index struct{} + +type Classification struct { + *blog_classification.Entity + Children []*Classification +} + +// 首页 +func (c *Index) Index(r *ghttp.Request) { + // 查询文章列表 + var req = new(blog_log.SelectPageReq) + req.PageSize = 8 + req.Status = 1 + _, _, logList, err := blog_service.SelectLogListByPage(req) + if err != nil { + g.Log().Error(err) + } + // 查询3篇置顶文章列表 + topList, err := blog_service.FindSizeArticleBySign(3, 1, 1, 0) + if err != nil { + g.Log().Error(err) + } + // 查询3篇幻灯 + slideList, err := blog_service.FindSizeArticleBySign(3, 1, 2, 0) + if err != nil { + g.Log().Error(err) + } + // 查询10篇根据点击数排序的文章用于排行榜 + hitsList, err := blog_service.FindArticleByHits(10, 1) + if err != nil { + g.Log().Error(err) + } + // 查询友情链接 + linkList, err := plug_link_service.ListByTypeId(13, 6, 1) + if err != nil { + g.Log().Error(err) + } + // 查询赞助分类 + coffeeArticle, err := plug_service.GetSizeAd(2, 1, 21) + if err != nil { + g.Log().Error(err) + } + // 查询推荐文章 + recommendList, err := blog_service.FindSizeArticleBySign(4, 1, 3, 0) + if err != nil { + g.Log().Error(err) + } + // 查询最新文章 + newList, err := blog_service.FindSizeArticleBySign(4, 1, 0, 0) + if err != nil { + g.Log().Error(err) + } + // 查询所有状态为正常的分类 + classificationList, err := blog_service.FindAllList() + if err != nil { + g.Log().Error(err) + } + classifications := getClassificationgList(classificationList, 0) + blog.Response(r, "homePage.html", g.Map{ + "logList": logList, + "topList": topList, + "slideList": slideList, + "hitsList": hitsList, + "linkList": linkList, + "coffeeArticle": coffeeArticle, + "recommendList": recommendList, + "newList": newList, + "classificationList": classifications, + }) +} + +func getClassificationgList(classificationList []*blog_classification.Entity, pid uint) (cList []*Classification) { + cList = make([]*Classification, 0, len(classificationList)) + for _, c1 := range classificationList { + var cl *Classification + if c1.ClassificationPid == pid { + cl = &Classification{Entity: c1} + cl.Children = getClassificationgList(classificationList, c1.ClassificationId) + cList = append(cList, cl) + } + } + return +} + +// 分类、搜索 +func (c *Index) BlogList(r *ghttp.Request) { + // 根据条件查询当前分类下的文章 + var req = new(blog_log.SelectPageReq) + req.PageNum = r.GetInt64("page") + pageSize := 10 + req.PageSize = int64(pageSize) + req.LogTitle = r.GetString("keyboard") + req.Status = 1 + cateId := r.GetInt("cateId") + req.CateTypeId = cateId + total, _, list, err := blog_service.SelectLogListByPage(req) + if err != nil { + g.Log().Error(err) + } + // 根据分类id查询分类(分类名用于导航) + classification, err := blog_service.GetClassificationByID(int64(cateId)) + if err != nil { + g.Log().Error(err) + } + // 以下为右边侧栏所需数据 + // 查询10篇根据点击数排序的文章用于排行榜 + hitsList, err := blog_service.FindArticleByHits(10, 1) + if err != nil { + g.Log().Error(err) + } + // 查询友情链接 + linkList, err := plug_link_service.ListByTypeId(13, 6, 1) + if err != nil { + g.Log().Error(err) + } + // 查询赞助分类 + coffeeArticle, err := plug_service.GetSizeAd(2, 1, 21) + if err != nil { + g.Log().Error(err) + } + // 查询推荐文章 + recommendList, err := blog_service.FindSizeArticleBySign(4, 1, 3, 0) + if err != nil { + g.Log().Error(err) + } + // 查询最新文章 + newList, err := blog_service.FindSizeArticleBySign(4, 1, 0, 0) + if err != nil { + g.Log().Error(err) + } + // 查询所有状态为正常的分类 + classificationList, err := blog_service.FindAllList() + if err != nil { + g.Log().Error(err) + } + classifications := getClassificationgList(classificationList, 0) + tplData := g.Map{ + "list": list, + "pageStyle": r.GetPage(total, pageSize).GetContent(3), + "classificationList": classifications, + "hitsList": hitsList, + "linkList": linkList, + "coffeeArticle": coffeeArticle, + "recommendList": recommendList, + "newList": newList, + "classification": classification, + } + if classification != nil && classification.ClassificationType == 4 { + //单页 + blog.Response(r, "content.html", tplData) + } else { + blog.Response(r, "blogList.html", tplData) + } +} + +// 内容页面(关于博主) +func (c *Index) Content(r *ghttp.Request) { + logId := r.GetInt("logId") + // 根据id查询对应博客日志 + log, err := blog_service.GetLogByID(int64(logId)) + if err != nil { + g.Log().Error(err) + } + // 更新点击数 + log.LogHits += 1 + blog_log.Model.Save(log) + if log.LogUrl != "" { + //跳转连接 + r.Response.RedirectTo(log.LogUrl) + r.Exit() + } + // 查询4篇相关文章(同一分类下最新的4篇) + relevantList, err := blog_service.FindSizeArticle(4, 1, log.LogType) + if err != nil { + g.Log().Error(err) + } + // 查询10篇根据点击数排序的文章用于排行榜 + hitsList, err := blog_service.FindArticleByHits(10, 1) + if err != nil { + g.Log().Error(err) + } + // 查询友情链接 + linkList, err := plug_link_service.ListByTypeId(13, 6, 1) + if err != nil { + g.Log().Error(err) + } + // 查询赞助分类 + coffeeArticle, err := plug_service.GetSizeAd(2, 1, 21) + if err != nil { + g.Log().Error(err) + } + // 查询推荐文章 + recommendList, err := blog_service.FindSizeArticleBySign(4, 1, 3, 0) + if err != nil { + g.Log().Error(err) + } + // 查询最新文章 + newList, err := blog_service.FindSizeArticleBySign(4, 1, 0, 0) + if err != nil { + g.Log().Error(err) + } + // 查询所有状态为正常的分类 + classificationList, err := blog_service.FindAllList() + if err != nil { + g.Log().Error(err) + } + classifications := getClassificationgList(classificationList, 0) + // 查询当前日志的未停用评论 + req := new(blog_comment.SelectPageReq) + req.Status = "1" + req.PageSize = 5 + req.Flag = 1 + req.PageNum = r.GetInt64("page") + req.CommentLogIds = append(req.CommentLogIds, logId) + total, _, commentList, err := blog_service.SelectCommentListByPage(req) // 分页查询一级评论 + commentList, err = blog_service.GetChildren(commentList) // 查出二级评论(回复) + blog.Response(r, "content.html", g.Map{ + "classificationList": classifications, + "log": log, + "hitsList": hitsList, + "linkList": linkList, + "coffeeArticle": coffeeArticle, + "recommendList": recommendList, + "newList": newList, + "pageStyle": r.GetPage(total, int(req.PageSize)).GetContent(3), + "commentList": commentList, + "relevantList": relevantList, + }) +} + +// 新增评论 +func (c *Index) AddComment(r *ghttp.Request) { + if r.Method == "POST" { + var req *blog_comment.AddReq + err := r.Parse(&req) + if err != nil { + response.FailJson(true, r, err.(*gvalid.Error).FirstString()) + } + err = blog_service.AddCommentSave(req) + if err != nil { + response.FailJson(true, r, err.Error()) + } + response.SusJson(true, r, "添加成功") + } +} + +// 友情链接申请 +func (c *Index) Link(r *ghttp.Request) { + // 查询10篇根据点击数排序的文章用于排行榜 + hitsList, err := blog_service.FindArticleByHits(10, 1) + if err != nil { + g.Log().Error(err) + } + // 查询友情链接 + linkList, err := plug_link_service.ListByTypeId(13, 6, 1) + if err != nil { + g.Log().Error(err) + } + // 查询赞助分类 + coffeeArticle, err := plug_service.GetSizeAd(2, 1, 21) + if err != nil { + g.Log().Error(err) + } + // 查询推荐文章 + recommendList, err := blog_service.FindSizeArticleBySign(4, 1, 3, 0) + if err != nil { + g.Log().Error(err) + } + // 查询最新文章 + newList, err := blog_service.FindSizeArticleBySign(4, 1, 0, 0) + if err != nil { + g.Log().Error(err) + } + // 查询所有状态为正常的分类 + classificationList, err := blog_service.FindAllList() + if err != nil { + g.Log().Error(err) + } + classifications := getClassificationgList(classificationList, 0) + blog.Response(r, "link.html", g.Map{ + "classificationList": classifications, + "hitsList": hitsList, + "linkList": linkList, + "coffeeArticle": coffeeArticle, + "recommendList": recommendList, + "newList": newList, + }) +} + +// 添加申请友情链接 +func (c *Index) AddLink(r *ghttp.Request) { + if r.Method == "POST" { + var req = new(plug_link.AddReq) + err := r.Parse(req) + req.LinkOpen = 0 + req.LinkTarget = "_blank" + if err != nil { + response.FailJson(true, r, err.(*gvalid.Error).FirstString()) + } + err = plug_link_service.AddSavePlugLink(req) + if err != nil { + response.FailJson(true, r, err.Error()) + } + response.SusJson(true, r, "提交成功") + } +} diff --git a/plugin/blog/controller/system/blog_classification.go b/plugin/blog/controller/system/blog_classification.go new file mode 100644 index 0000000..3889849 --- /dev/null +++ b/plugin/blog/controller/system/blog_classification.go @@ -0,0 +1,139 @@ +package system + +import ( + "gfast/app/service/cache_service" + "gfast/library/response" + "gfast/plugin/blog/model/blog_classification" + "gfast/plugin/blog/service/blog_service" + "github.com/gogf/gf/errors/gerror" + "github.com/gogf/gf/frame/g" + "github.com/gogf/gf/net/ghttp" + "github.com/gogf/gf/util/gconv" + "github.com/gogf/gf/util/gvalid" +) + +// 简单博客管理-分类管理 +type BlogClassification struct{} + +func (c *BlogClassification) Add(r *ghttp.Request) { + if r.Method == "POST" { + var req *blog_classification.AddReq + // 通过Parse方法解析获取参数 + err := r.Parse(&req) + if err != nil { + response.FailJson(true, r, err.(*gvalid.Error).FirstString()) + } + // 调用service中的添加函数添加广告位 + err = blog_service.AddClassificationSave(req) + if err != nil { + response.FailJson(true, r, err.Error()) + } + cache_service.New().RemoveByTag(cache_service.AdminBlogTag) + response.SusJson(true, r, "添加成功!") + } + //获取上级分类(频道) + menus, err := blog_service.GetMenuListChannel() + if err != nil { + response.FailJson(true, r, err.Error()) + } + res := g.Map{ + "parentList": menus, + } + response.SusJson(true, r, "添加栏目", res) +} + +func (c *BlogClassification) Delete(r *ghttp.Request) { + // 从页面获取要删除记录的 ID int切片 + ids := r.GetInts("classificationId") + if len(ids) == 0 { + response.FailJson(true, r, "ID获取失败,删除失败") + } + err := blog_service.DeleteClassificationByIds(ids) + if err != nil { + response.FailJson(true, r, err.Error()) + } + cache_service.New().RemoveByTag(cache_service.AdminBlogTag) + response.SusJson(true, r, "删除成功") +} + +func (c *BlogClassification) Edit(r *ghttp.Request) { + // 如果是post提交的请求就执行修改操作 + if r.Method == "POST" { + var editReq *blog_classification.EditReq + // 通过Parse方法解析获取参数 + err := r.Parse(&editReq) + if err != nil { + response.FailJson(true, r, err.(*gvalid.Error).FirstString()) + } + err = blog_service.EditClassificationSave(editReq) + if err != nil { + response.FailJson(true, r, err.Error()) + } + cache_service.New().RemoveByTag(cache_service.AdminBlogTag) + response.SusJson(true, r, "修改参数成功") + } + // 不是post提交的请求就到修改页面后查询出要修改的记录 + id := r.GetInt("classificationId") + params, err := blog_service.GetClassificationByID(int64(id)) + if err != nil { + response.FailJson(true, r, err.Error()) + } + //获取上级分类(频道) + menus, err := blog_service.GetMenuListChannel() + if err != nil { + response.FailJson(true, r, err.Error()) + } + res := g.Map{ + "params": params, + "parentList": menus, + } + response.SusJson(true, r, "ok", res) +} + +func (c *BlogClassification) List(r *ghttp.Request) { + // 定义一个结构体存储请求参数 + var req *blog_classification.SelectPageReq + // 获取参数 + err := r.Parse(&req) + if err != nil { + response.FailJson(true, r, err.Error()) + } + total, page, list, err := blog_service.SelectClassificationListByPage(req) + if err != nil { + response.FailJson(true, r, err.Error()) + } + result := g.Map{ + "currentPage": page, + "total": total, + "list": list, + } + response.SusJson(true, r, "日志分类列表", result) +} + +func (c *BlogClassification) Sort(r *ghttp.Request) { + sorts := r.Get("sorts") + s := gconv.Map(sorts) + if s == nil { + response.FailJson(true, r, "获取记录id、序号失败") + } + var err error + for k, v := range s { + _, err = blog_classification.Model.Where("classification_id=?", k).Data("classification_sort", v).Update() + if err != nil { + response.FailJson(true, r, err.Error()) + } + } + cache_service.New().RemoveByTag(cache_service.AdminBlogTag) + response.SusJson(true, r, "排序成功") +} + +// 查询所有分类名称和对应id +func (c *BlogClassification) Type(r *ghttp.Request) { + res, err := blog_classification.Model.FindAll() + if err != nil { + g.Log().Error(err) + err = gerror.New("查询所有分类名称失败!") + response.FailJson(true, r, err.Error()) + } + response.SusJson(true, r, "分类名称列表", res) +} diff --git a/plugin/blog/controller/system/blog_comment.go b/plugin/blog/controller/system/blog_comment.go new file mode 100644 index 0000000..686bb5f --- /dev/null +++ b/plugin/blog/controller/system/blog_comment.go @@ -0,0 +1,69 @@ +package system + +import ( + "gfast/library/response" + "gfast/plugin/blog/model/blog_comment" + "gfast/plugin/blog/service/blog_service" + "github.com/gogf/gf/frame/g" + "github.com/gogf/gf/net/ghttp" + "github.com/gogf/gf/util/gvalid" +) + +// 简单博客管理-评论管理 +type BlogComment struct{} + +func (c *BlogComment) Delete(r *ghttp.Request) { + ids := r.GetInts("commentId") + if len(ids) == 0 { + response.FailJson(true, r, "ID获取失败,删除失败") + } + err := blog_service.DeleteCommentByIDs(ids) + if err != nil { + response.FailJson(true, r, err.Error()) + } + response.SusJson(true, r, "删除成功") +} + +func (c *BlogComment) Edit(r *ghttp.Request) { + // 如果是post提交的请求就执行修改操作 + if r.Method == "POST" { + var editReq *blog_comment.EditReq + // 通过Parse方法解析获取参数 + err := r.Parse(&editReq) + if err != nil { + response.FailJson(true, r, err.(*gvalid.Error).FirstString()) + } + err = blog_service.EditCommentSave(editReq) + if err != nil { + response.FailJson(true, r, err.Error()) + } + response.SusJson(true, r, "修改参数成功") + } + // 不是post提交的请求就到修改页面后查询出要修改的记录 + id := r.GetInt("commentId") + params, err := blog_service.GetCommentByID(int64(id)) + if err != nil { + response.FailJson(true, r, err.Error()) + } + response.SusJson(true, r, "ok", params) +} + +func (c *BlogComment) List(r *ghttp.Request) { + // 定义一个结构体存储请求参数 + var req *blog_comment.SelectPageReq + // 获取参数 + err := r.Parse(&req) + if err != nil { + response.FailJson(true, r, err.Error()) + } + total, page, list, err := blog_service.SelectCommentListByPage(req) + if err != nil { + response.FailJson(true, r, err.Error()) + } + result := g.Map{ + "currentPage": page, + "total": total, + "list": list, + } + response.SusJson(true, r, "评论列表", result) +} diff --git a/plugin/blog/controller/system/blog_log.go b/plugin/blog/controller/system/blog_log.go new file mode 100644 index 0000000..57b3b9c --- /dev/null +++ b/plugin/blog/controller/system/blog_log.go @@ -0,0 +1,103 @@ +package system + +import ( + "gfast/library/response" + "gfast/plugin/blog/model/blog_log" + "gfast/plugin/blog/service/blog_service" + "github.com/gogf/gf/frame/g" + "github.com/gogf/gf/net/ghttp" + "github.com/gogf/gf/util/gconv" + "github.com/gogf/gf/util/gvalid" +) + +// 简单博客管理-日志管理 +type BlogLog struct{} + +func (c *BlogLog) Add(r *ghttp.Request) { + if r.Method == "POST" { + var req *blog_log.AddReq + // 通过Parse方法解析获取参数 + err := r.Parse(&req) + if err != nil { + response.FailJson(true, r, err.(*gvalid.Error).FirstString()) + } + // 调用service中的添加函数添加广告 + err = blog_service.AddLogSave(req) + if err != nil { + response.FailJson(true, r, err.Error()) + } + response.SusJson(true, r, "添加成功!") + } +} + +func (c *BlogLog) Delete(r *ghttp.Request) { + ids := r.GetInts("logID") + if len(ids) == 0 { + response.FailJson(true, r, "ID获取失败,删除失败") + } + err := blog_service.DeleteLogByIDs(ids) + if err != nil { + response.FailJson(true, r, err.Error()) + } + response.SusJson(true, r, "删除成功") +} + +func (c *BlogLog) Edit(r *ghttp.Request) { + // 如果是post提交的请求就执行修改操作 + if r.Method == "POST" { + var editReq *blog_log.EditReq + // 通过Parse方法解析获取参数 + err := r.Parse(&editReq) + if err != nil { + response.FailJson(true, r, err.(*gvalid.Error).FirstString()) + } + err = blog_service.EditLogSave(editReq) + if err != nil { + response.FailJson(true, r, err.Error()) + } + response.SusJson(true, r, "修改参数成功") + } + // 不是post提交的请求就到修改页面后查询出要修改的记录 + id := r.GetInt("logID") + params, err := blog_service.GetLogByID(int64(id)) + if err != nil { + response.FailJson(true, r, err.Error()) + } + response.SusJson(true, r, "ok", params) +} + +func (c *BlogLog) List(r *ghttp.Request) { + // 定义一个结构体存储请求参数 + var req *blog_log.SelectPageReq + // 获取参数 + err := r.Parse(&req) + if err != nil { + response.FailJson(true, r, err.Error()) + } + total, page, list, err := blog_service.SelectLogListByPage(req) + if err != nil { + response.FailJson(true, r, err.Error()) + } + result := g.Map{ + "currentPage": page, + "total": total, + "list": list, + } + response.SusJson(true, r, "日志列表", result) +} + +func (c *BlogLog) Sort(r *ghttp.Request) { + sorts := r.Get("sorts") + s := gconv.Map(sorts) + if s == nil { + response.FailJson(true, r, "获取记录id、序号失败") + } + var err error + for k, v := range s { + _, err = blog_log.Model.Where("log_id=?", k).Data("log_sort", v).Update() + if err != nil { + response.FailJson(true, r, err.Error()) + } + } + response.SusJson(true, r, "排序成功") +} diff --git a/plugin/blog/model/blog_classification/blog_classification.go b/plugin/blog/model/blog_classification/blog_classification.go new file mode 100644 index 0000000..a3571fe --- /dev/null +++ b/plugin/blog/model/blog_classification/blog_classification.go @@ -0,0 +1,181 @@ +package blog_classification + +import ( + "gfast/app/service/cache_service" + "github.com/gogf/gf/errors/gerror" + "github.com/gogf/gf/frame/g" +) + +// AddReq 用于存储新增记录请求参数 +type AddReq struct { + ClassificationName string `p:"classificationName" v:"required#名称不能为空"` // 名称 + ClassificationSort uint `p:"classificationSort" v:"required#排序不能为空"` // 排序 + ClassificationDescribe string `p:"classificationDescribe"` // 分类描述 + ClassificationPid uint `p:"classificationPid"` // 父id + ClassificationType uint `p:"classificationType"` // 分类类型1.频道页/2.发布栏目/3.跳转栏目/4.单页栏目 + ClassificationStatus uint `p:"classificationStatus" v:"required#分类状态不能为空"` // 状态 + ClassificationAddress string `p:"classificationAddress"` + ClassificationContent string `p:"classificationContent"` +} + +// EditReq 用于存储修改广告位请求参数 +type EditReq struct { + ClassificationId int64 `p:"classificationId" v:"required|min:1#主键ID不能为空|主键ID值错误"` + AddReq +} + +// SelectPageReq 用于存储分页查询的请求参数 +type SelectPageReq struct { + ClassificationName string `p:"classificationName"` // 名称 + PageNum int64 `p:"pageNum"` // 当前页 + PageSize int64 `p:"pageSize"` // 每页显示记录数 +} + +// GetClassificationByID 根据ID查询记录 +func GetClassificationByID(id int64) (*Entity, error) { + entity, err := Model.FindOne(id) + if err != nil { + g.Log().Error(err) + return nil, gerror.New("根据ID查询记录出错") + } + if entity == nil { + return nil, gerror.New("根据ID未能查询到记录") + } + return entity, nil +} + +// 根据名称和ID来判断是否已存在相同名称的分类 +func CheakClassificationNameUnique(classificationName string, classificationId int64) error { + var ( + entity *Entity + err error + ) + if classificationId == 0 { + entity, err = Model.FindOne(Columns.ClassificationName, classificationName) + } else { + entity, err = Model.Where(Columns.ClassificationName, classificationName).And(Columns.ClassificationId+"!=?", classificationId).FindOne() + } + if err != nil { + g.Log().Error(err) + return gerror.New("校验名称唯一性失败") + } + if entity != nil { + return gerror.New("名称已经存在!") + } + return nil +} + +// AddSave 添加 +func AddSave(req *AddReq) error { + var entity Entity + entity.ClassificationName = req.ClassificationName + entity.ClassificationSort = req.ClassificationSort + entity.ClassificationDescribe = req.ClassificationDescribe + entity.ClassificationPid = req.ClassificationPid + entity.ClassificationType = req.ClassificationType + entity.ClassificationStatus = req.ClassificationStatus + entity.ClassificationAddress = req.ClassificationAddress + entity.ClassificationContent = req.ClassificationContent + _, err := Model.Save(entity) + if err != nil { + g.Log().Error(err) + return gerror.New("添加失败") + } + return nil +} + +// 批量删除 +func DeleteClassificationByIds(ids []int) error { + _, err := Model.Where("classification_id in(?)", ids).Delete() + if err != nil { + g.Log().Error(err) + return gerror.New("删除失败") + } + return nil +} + +// 根据ID修改信息 +func EditSave(editReq *EditReq) error { + // 先根据ID来查询要修改的记录 + entity, err := GetClassificationByID(editReq.ClassificationId) + if err != nil { + return err + } + // 修改实体 + entity.ClassificationName = editReq.ClassificationName + entity.ClassificationSort = editReq.ClassificationSort + entity.ClassificationStatus = editReq.ClassificationStatus + entity.ClassificationType = editReq.ClassificationType + entity.ClassificationPid = editReq.ClassificationPid + entity.ClassificationDescribe = editReq.ClassificationDescribe + entity.ClassificationAddress = editReq.ClassificationAddress + entity.ClassificationContent = editReq.ClassificationContent + _, err = Model.Save(entity) + if err != nil { + g.Log().Error(err) + return gerror.New("修改失败") + } + return nil +} + +// 分页查询,返回值total总记录数,page当前页 +func SelectListByPage(req *SelectPageReq) (total int, page int64, list []*Entity, err error) { + model := Model + if req != nil { + if req.ClassificationName != "" { + model = model.Where("classification_name like ?", "%"+req.ClassificationName+"%") + } + } + // 查询总记录数(总行数) + total, err = model.Count() + if err != nil { + g.Log().Error(err) + err = gerror.New("获取总记录数失败") + return 0, 0, nil, err + } + if req.PageNum == 0 { + req.PageNum = 1 + } + page = req.PageNum + if req.PageSize == 0 { + req.PageSize = 10 + } + // 分页排序查询 + list, err = model.Page(int(page), int(req.PageSize)).Order("classification_sort asc,classification_id asc").All() + if err != nil { + g.Log().Error(err) + err = gerror.New("分页查询失败") + return 0, 0, nil, err + } + return total, page, list, nil +} + +//获取所有菜单列表 +func GetList() (list []*Entity, err error) { + cache := cache_service.New() + //从缓存获取数据 + iList := cache.Get(cache_service.AdminBlogClassification) + if iList != nil { + list = iList.([]*Entity) + return + } + list, err = Model.Order("classification_sort ASC,classification_id ASC").All() + if err != nil { + g.Log().Error() + err = gerror.New("获取菜单数据失败") + return + } + //缓存数据 + cache.Set(cache_service.AdminBlogClassification, list, 0, cache_service.AdminBlogTag) + return +} + +// 查询所有状态为正常的分类 +func FindAllList() (list []*Entity, err error) { + list, err = Model.Where("classification_status = ?", 1).Order("classification_sort asc,classification_id asc").All() + if err != nil { + g.Log().Error(err) + return nil, gerror.New("查询博客分类列表出错") + } + return list, nil +} diff --git a/plugin/blog/model/blog_classification/blog_classification_entity.go b/plugin/blog/model/blog_classification/blog_classification_entity.go new file mode 100644 index 0000000..c2ecc99 --- /dev/null +++ b/plugin/blog/model/blog_classification/blog_classification_entity.go @@ -0,0 +1,65 @@ +// ========================================================================== +// This is auto-generated by gf cli tool. You may not really want to edit it. +// ========================================================================== + +package blog_classification + +import ( + "database/sql" + "github.com/gogf/gf/database/gdb" +) + +// Entity is the golang structure for table blog_classification. +type Entity struct { + ClassificationId uint `orm:"classification_id,primary" json:"classification_id"` // + ClassificationName string `orm:"classification_name" json:"classification_name"` // 分类名称 + ClassificationPid uint `orm:"classification_pid" json:"classification_pid"` // 分类父id + ClassificationSort uint `orm:"classification_sort" json:"classification_sort"` // 排序 + ClassificationType uint `orm:"classification_type" json:"classification_type"` // 分类类型1.频道页/2.发布栏目/3.跳转栏目/4.单页栏目 + ClassificationDescribe string `orm:"classification_describe" json:"classification_describe"` // 分类描述 + ClassificationStatus uint `orm:"classification_status" json:"classification_status"` // 分类状态,0停用,1正常 + ClassificationAddress string `orm:"classification_address" json:"classification_address"` // 跳转栏目的跳转地址 + ClassificationContent string `orm:"classification_content" json:"classification_content"` // 单页栏目的内容 +} + +// OmitEmpty sets OPTION_OMITEMPTY option for the model, which automatically filers +// the data and where attributes for empty values. +func (r *Entity) OmitEmpty() *arModel { + return Model.Data(r).OmitEmpty() +} + +// Inserts does "INSERT...INTO..." statement for inserting current object into table. +func (r *Entity) Insert() (result sql.Result, err error) { + return Model.Data(r).Insert() +} + +// InsertIgnore does "INSERT IGNORE INTO ..." statement for inserting current object into table. +func (r *Entity) InsertIgnore() (result sql.Result, err error) { + return Model.Data(r).InsertIgnore() +} + +// Replace does "REPLACE...INTO..." statement for inserting current object into table. +// If there's already another same record in the table (it checks using primary key or unique index), +// it deletes it and insert this one. +func (r *Entity) Replace() (result sql.Result, err error) { + return Model.Data(r).Replace() +} + +// Save does "INSERT...INTO..." statement for inserting/updating current object into table. +// It updates the record if there's already another same record in the table +// (it checks using primary key or unique index). +func (r *Entity) Save() (result sql.Result, err error) { + return Model.Data(r).Save() +} + +// Update does "UPDATE...WHERE..." statement for updating current object from table. +// It updates the record if there's already another same record in the table +// (it checks using primary key or unique index). +func (r *Entity) Update() (result sql.Result, err error) { + return Model.Data(r).Where(gdb.GetWhereConditionOfStruct(r)).Update() +} + +// Delete does "DELETE FROM...WHERE..." statement for deleting current object from table. +func (r *Entity) Delete() (result sql.Result, err error) { + return Model.Where(gdb.GetWhereConditionOfStruct(r)).Delete() +} diff --git a/plugin/blog/model/blog_classification/blog_classification_model.go b/plugin/blog/model/blog_classification/blog_classification_model.go new file mode 100644 index 0000000..fa7331c --- /dev/null +++ b/plugin/blog/model/blog_classification/blog_classification_model.go @@ -0,0 +1,359 @@ +// ========================================================================== +// This is auto-generated by gf cli tool. You may not really want to edit it. +// ========================================================================== + +package blog_classification + +import ( + "database/sql" + "github.com/gogf/gf/database/gdb" + "github.com/gogf/gf/frame/g" + "github.com/gogf/gf/frame/gmvc" + "time" +) + +// arModel is a active record design model for table blog_classification operations. +type arModel struct { + gmvc.M +} + +var ( + // Table is the table name of blog_classification. + Table = "blog_classification" + // Model is the model object of blog_classification. + Model = &arModel{g.DB("default").Table(Table).Safe()} + // Columns defines and stores column names for table blog_classification. + Columns = struct { + ClassificationId string // + ClassificationName string // 分类名称 + ClassificationPid string // 分类父id + ClassificationSort string // 排序 + ClassificationType string // 分类标志(菜单类型)1.频道页/2.发布栏目/3.跳转栏目/4.单页栏目 + ClassificationDescribe string // 分类描述 + ClassificationStatus string // 分类状态,0停用,1正常 + ClassificationAddress string // 跳转栏目的跳转地址 + ClassificationContent string // 单页栏目的内容 + }{ + ClassificationId: "classification_id", + ClassificationName: "classification_name", + ClassificationPid: "classification_pid", + ClassificationSort: "classification_sort", + ClassificationType: "classification_type", + ClassificationDescribe: "classification_describe", + ClassificationStatus: "classification_status", + ClassificationAddress: "classification_address", + ClassificationContent: "classification_content", + } +) + +// FindOne is a convenience method for Model.FindOne. +// See Model.FindOne. +func FindOne(where ...interface{}) (*Entity, error) { + return Model.FindOne(where...) +} + +// FindAll is a convenience method for Model.FindAll. +// See Model.FindAll. +func FindAll(where ...interface{}) ([]*Entity, error) { + return Model.FindAll(where...) +} + +// FindValue is a convenience method for Model.FindValue. +// See Model.FindValue. +func FindValue(fieldsAndWhere ...interface{}) (gdb.Value, error) { + return Model.FindValue(fieldsAndWhere...) +} + +// FindArray is a convenience method for Model.FindArray. +// See Model.FindArray. +func FindArray(fieldsAndWhere ...interface{}) ([]gdb.Value, error) { + return Model.FindArray(fieldsAndWhere...) +} + +// FindCount is a convenience method for Model.FindCount. +// See Model.FindCount. +func FindCount(where ...interface{}) (int, error) { + return Model.FindCount(where...) +} + +// Insert is a convenience method for Model.Insert. +func Insert(data ...interface{}) (result sql.Result, err error) { + return Model.Insert(data...) +} + +// InsertIgnore is a convenience method for Model.InsertIgnore. +func InsertIgnore(data ...interface{}) (result sql.Result, err error) { + return Model.InsertIgnore(data...) +} + +// Replace is a convenience method for Model.Replace. +func Replace(data ...interface{}) (result sql.Result, err error) { + return Model.Replace(data...) +} + +// Save is a convenience method for Model.Save. +func Save(data ...interface{}) (result sql.Result, err error) { + return Model.Save(data...) +} + +// Update is a convenience method for Model.Update. +func Update(dataAndWhere ...interface{}) (result sql.Result, err error) { + return Model.Update(dataAndWhere...) +} + +// Delete is a convenience method for Model.Delete. +func Delete(where ...interface{}) (result sql.Result, err error) { + return Model.Delete(where...) +} + +// As sets an alias name for current table. +func (m *arModel) As(as string) *arModel { + return &arModel{m.M.As(as)} +} + +// TX sets the transaction for current operation. +func (m *arModel) TX(tx *gdb.TX) *arModel { + return &arModel{m.M.TX(tx)} +} + +// Master marks the following operation on master node. +func (m *arModel) Master() *arModel { + return &arModel{m.M.Master()} +} + +// Slave marks the following operation on slave node. +// Note that it makes sense only if there's any slave node configured. +func (m *arModel) Slave() *arModel { + return &arModel{m.M.Slave()} +} + +// LeftJoin does "LEFT JOIN ... ON ..." statement on the model. +// The parameter can be joined table and its joined condition, +// and also with its alias name, like: +// Table("user").LeftJoin("user_detail", "user_detail.uid=user.uid") +// Table("user", "u").LeftJoin("user_detail", "ud", "ud.uid=u.uid") +func (m *arModel) LeftJoin(table ...string) *arModel { + return &arModel{m.M.LeftJoin(table...)} +} + +// RightJoin does "RIGHT JOIN ... ON ..." statement on the model. +// The parameter
can be joined table and its joined condition, +// and also with its alias name, like: +// Table("user").RightJoin("user_detail", "user_detail.uid=user.uid") +// Table("user", "u").RightJoin("user_detail", "ud", "ud.uid=u.uid") +func (m *arModel) RightJoin(table ...string) *arModel { + return &arModel{m.M.RightJoin(table...)} +} + +// InnerJoin does "INNER JOIN ... ON ..." statement on the model. +// The parameter
can be joined table and its joined condition, +// and also with its alias name, like: +// Table("user").InnerJoin("user_detail", "user_detail.uid=user.uid") +// Table("user", "u").InnerJoin("user_detail", "ud", "ud.uid=u.uid") +func (m *arModel) InnerJoin(table ...string) *arModel { + return &arModel{m.M.InnerJoin(table...)} +} + +// Fields sets the operation fields of the model, multiple fields joined using char ','. +func (m *arModel) Fields(fields string) *arModel { + return &arModel{m.M.Fields(fields)} +} + +// FieldsEx sets the excluded operation fields of the model, multiple fields joined using char ','. +func (m *arModel) FieldsEx(fields string) *arModel { + return &arModel{m.M.FieldsEx(fields)} +} + +// Option sets the extra operation option for the model. +func (m *arModel) Option(option int) *arModel { + return &arModel{m.M.Option(option)} +} + +// OmitEmpty sets OPTION_OMITEMPTY option for the model, which automatically filers +// the data and where attributes for empty values. +func (m *arModel) OmitEmpty() *arModel { + return &arModel{m.M.OmitEmpty()} +} + +// Filter marks filtering the fields which does not exist in the fields of the operated table. +func (m *arModel) Filter() *arModel { + return &arModel{m.M.Filter()} +} + +// Where sets the condition statement for the model. The parameter can be type of +// string/map/gmap/slice/struct/*struct, etc. Note that, if it's called more than one times, +// multiple conditions will be joined into where statement using "AND". +// Eg: +// Where("uid=10000") +// Where("uid", 10000) +// Where("money>? AND name like ?", 99999, "vip_%") +// Where("uid", 1).Where("name", "john") +// Where("status IN (?)", g.Slice{1,2,3}) +// Where("age IN(?,?)", 18, 50) +// Where(User{ Id : 1, UserName : "john"}) +func (m *arModel) Where(where interface{}, args ...interface{}) *arModel { + return &arModel{m.M.Where(where, args...)} +} + +// And adds "AND" condition to the where statement. +func (m *arModel) And(where interface{}, args ...interface{}) *arModel { + return &arModel{m.M.And(where, args...)} +} + +// Or adds "OR" condition to the where statement. +func (m *arModel) Or(where interface{}, args ...interface{}) *arModel { + return &arModel{m.M.Or(where, args...)} +} + +// Group sets the "GROUP BY" statement for the model. +func (m *arModel) Group(groupBy string) *arModel { + return &arModel{m.M.Group(groupBy)} +} + +// Order sets the "ORDER BY" statement for the model. +func (m *arModel) Order(orderBy ...string) *arModel { + return &arModel{m.M.Order(orderBy...)} +} + +// Limit sets the "LIMIT" statement for the model. +// The parameter can be either one or two number, if passed two number is passed, +// it then sets "LIMIT limit[0],limit[1]" statement for the model, or else it sets "LIMIT limit[0]" +// statement. +func (m *arModel) Limit(limit ...int) *arModel { + return &arModel{m.M.Limit(limit...)} +} + +// Offset sets the "OFFSET" statement for the model. +// It only makes sense for some databases like SQLServer, PostgreSQL, etc. +func (m *arModel) Offset(offset int) *arModel { + return &arModel{m.M.Offset(offset)} +} + +// Page sets the paging number for the model. +// The parameter is started from 1 for paging. +// Note that, it differs that the Limit function start from 0 for "LIMIT" statement. +func (m *arModel) Page(page, limit int) *arModel { + return &arModel{m.M.Page(page, limit)} +} + +// Batch sets the batch operation number for the model. +func (m *arModel) Batch(batch int) *arModel { + return &arModel{m.M.Batch(batch)} +} + +// Cache sets the cache feature for the model. It caches the result of the sql, which means +// if there's another same sql request, it just reads and returns the result from cache, it +// but not committed and executed into the database. +// +// If the parameter < 0, which means it clear the cache with given . +// If the parameter = 0, which means it never expires. +// If the parameter > 0, which means it expires after . +// +// The optional parameter is used to bind a name to the cache, which means you can later +// control the cache like changing the or clearing the cache with specified . +// +// Note that, the cache feature is disabled if the model is operating on a transaction. +func (m *arModel) Cache(duration time.Duration, name ...string) *arModel { + return &arModel{m.M.Cache(duration, name...)} +} + +// Data sets the operation data for the model. +// The parameter can be type of string/map/gmap/slice/struct/*struct, etc. +// Eg: +// Data("uid=10000") +// Data("uid", 10000) +// Data(g.Map{"uid": 10000, "name":"john"}) +// Data(g.Slice{g.Map{"uid": 10000, "name":"john"}, g.Map{"uid": 20000, "name":"smith"}) +func (m *arModel) Data(data ...interface{}) *arModel { + return &arModel{m.M.Data(data...)} +} + +// All does "SELECT FROM ..." statement for the model. +// It retrieves the records from table and returns the result as []*Entity. +// It returns nil if there's no record retrieved with the given conditions from table. +// +// The optional parameter is the same as the parameter of Model.Where function, +// see Model.Where. +func (m *arModel) All(where ...interface{}) ([]*Entity, error) { + all, err := m.M.All(where...) + if err != nil { + return nil, err + } + var entities []*Entity + if err = all.Structs(&entities); err != nil && err != sql.ErrNoRows { + return nil, err + } + return entities, nil +} + +// One retrieves one record from table and returns the result as *Entity. +// It returns nil if there's no record retrieved with the given conditions from table. +// +// The optional parameter is the same as the parameter of Model.Where function, +// see Model.Where. +func (m *arModel) One(where ...interface{}) (*Entity, error) { + one, err := m.M.One(where...) + if err != nil { + return nil, err + } + var entity *Entity + if err = one.Struct(&entity); err != nil && err != sql.ErrNoRows { + return nil, err + } + return entity, nil +} + +// FindOne retrieves and returns a single Record by Model.WherePri and Model.One. +// Also see Model.WherePri and Model.One. +func (m *arModel) FindOne(where ...interface{}) (*Entity, error) { + one, err := m.M.FindOne(where...) + if err != nil { + return nil, err + } + var entity *Entity + if err = one.Struct(&entity); err != nil && err != sql.ErrNoRows { + return nil, err + } + return entity, nil +} + +// FindAll retrieves and returns Result by by Model.WherePri and Model.All. +// Also see Model.WherePri and Model.All. +func (m *arModel) FindAll(where ...interface{}) ([]*Entity, error) { + all, err := m.M.FindAll(where...) + if err != nil { + return nil, err + } + var entities []*Entity + if err = all.Structs(&entities); err != nil && err != sql.ErrNoRows { + return nil, err + } + return entities, nil +} + +// Chunk iterates the table with given size and callback function. +func (m *arModel) Chunk(limit int, callback func(entities []*Entity, err error) bool) { + m.M.Chunk(limit, func(result gdb.Result, err error) bool { + var entities []*Entity + err = result.Structs(&entities) + if err == sql.ErrNoRows { + return false + } + return callback(entities, err) + }) +} + +// LockUpdate sets the lock for update for current operation. +func (m *arModel) LockUpdate() *arModel { + return &arModel{m.M.LockUpdate()} +} + +// LockShared sets the lock in share mode for current operation. +func (m *arModel) LockShared() *arModel { + return &arModel{m.M.LockShared()} +} + +// Unscoped enables/disables the soft deleting feature. +func (m *arModel) Unscoped() *arModel { + return &arModel{m.M.Unscoped()} +} diff --git a/plugin/blog/model/blog_comment/blog_comment.go b/plugin/blog/model/blog_comment/blog_comment.go new file mode 100644 index 0000000..81ca84d --- /dev/null +++ b/plugin/blog/model/blog_comment/blog_comment.go @@ -0,0 +1,214 @@ +package blog_comment + +import ( + "gfast/app/model/admin/user" + "gfast/plugin/blog/model/blog_log" + "github.com/gogf/gf/database/gdb" + "github.com/gogf/gf/errors/gerror" + "github.com/gogf/gf/frame/g" + "github.com/gogf/gf/os/gtime" +) + +// AddReq 用于存储新增请求的请求参数 +type AddReq struct { + CommentLogId uint `p:"commentLogId" v:"required|min:1#评论所属日志ID不能为空|评论所属日志ID错误"` // 当前评论所属日志id + CommentNickname string `p:"commentNickname" v:"required#评论用户昵称不能为空"` // 评论用户的用户昵称 + CommentContent string `p:"commentContent" v:"required#评论内容不能为空"` // 评论内容 + CommentPid uint `p:"commentPid"` // 父评论id + ReplyName string `p:"replyName"` // 当前回复对象的昵称 + ReplyId uint `p:"replyId"` // 当前回复对象的id +} + +// EditReq 用于存储修改请求参数 +type EditReq struct { + CommentId int64 `p:"commentId" v:"required|min:1#评论ID不能为空|评论ID错误"` + CommentStatus uint `p:"commentStatus"` // 此评论的状态,0隐藏,1发布 + CommentContent string `p:"commentContent" v:"required#评论内容不能为空"` // 评论内容 +} + +// SelectPageReq 用于存储分页查询的请求参数 +type SelectPageReq struct { + CommentNickname string `p:"commentNickname"` // 评论用户昵称 + LogTitle string `p:"logTitle"` // 日志标题 + CommentLogIds []int // 存储根据日志标题查出来的日志id + PageNum int64 `p:"pageNum"` // 当前页 + PageSize int64 `p:"pageSize"` // 每页显示记录数 + Status string // 状态:用于控制查询相应状态的评论,0停用,1正常 + // 评论在博客页面回复中分两级一级是没有父评论的,二级是有父评论的,一条一级评论下的二级评论的父id值相同,均为该一级评论的id + Flag int // 用于判断查询模式,0:查询不区分评论等级(查询所有),1:只查询一级评论(父id为0的评论) +} + +// 用于存储分页查询的数据 +type ListEntity struct { + Entity + LogTitle string `orm:"log_title" json:"log_title" ` // 所属日志标题 + UserName string `orm:"user_name" json:"user_name" ` // 所属用户的用户名 + Children []*Entity +} + +// GetChildren 将分页查出的实体切片中每一个元素的子评论查出来并添加进去 +func GetChildren(listEntity []*ListEntity) ([]*ListEntity, error) { + for i, v := range listEntity { + children, err := GetChildrenByCommentId(v.CommentId) + if err != nil { + return nil, err + } + listEntity[i].Children = children + } + return listEntity, nil +} + +// GetChildrenByCommentId 根据评论id查询该评论所有已审核的回复 +func GetChildrenByCommentId(id uint) ([]*Entity, error) { + list, err := Model.Where("comment_pid = ?", id).And("comment_status = ?", 1).All() + if err != nil { + g.Log().Error(err) + return nil, gerror.New("根据评论id查询回复评论出错") + } + return list, nil +} + +// GetCommentByID 根据ID查询记录 +func GetCommentByID(id int64) (*Entity, error) { + entity, err := Model.FindOne("comment_id", id) + if err != nil { + g.Log().Error(err) + return nil, gerror.New("根据ID查询记录出错!") + } + if entity == nil { + return nil, gerror.New("根据ID未能查询到记录") + } + return entity, nil +} + +// 查询并更新回复数 +func FindSonCommentCount(id int) error { + i, err := Model.Where("reply_id = ?", id).Count() + if err != nil { + g.Log().Debug(err) + return gerror.New("查询回复数出错") + } + entity, err := GetCommentByID(int64(id)) + if err != nil { + return err + } + entity.CommentNum = uint(i) + Model.Save(entity) + return nil +} + +// AddSave 添加的方法 +func AddSave(req *AddReq) error { + var entity Entity + entity.CommentLogId = req.CommentLogId // 所属日志 + entity.CommentNickname = req.CommentNickname // 评论用户昵称 + entity.CommentContent = req.CommentContent // 评论内容 + entity.CommentPid = req.CommentPid // 父评论id,没有父则为0 + entity.ReplyName = req.ReplyName + entity.ReplyId = req.ReplyId + entity.CreateTime = uint(gtime.Timestamp()) // 评论时间 + // 保存实体 + _, err := entity.Insert() + if err != nil { + g.Log().Error(err) + return gerror.New("添加记录入库失败!") + } + return nil +} + +// 批量删除记录 +func DeleteByIDs(ids []int) error { + _, err := Model.Delete("comment_id in(?)", ids) + if err != nil { + g.Log().Error(err) + return gerror.New("删除记录失败!") + } + return nil +} + +// 根据ID修改记录 +func EditSave(req *EditReq) error { + // 先根据ID来查询要修改的记录 + entity, err := GetCommentByID(req.CommentId) + if err != nil { + return err + } + // 修改实体 + entity.CommentContent = req.CommentContent + entity.CommentStatus = req.CommentStatus + _, err = Model.Save(entity) + if err != nil { + g.Log().Error(err) + return gerror.New("修改记录失败!") + } + return nil +} + +// 根据日志标题查询对应的日志id切片(因为评论中只存有日志id没有日志标题) +func FindUserAndLogIds(req *SelectPageReq) (*SelectPageReq, error) { + if req.LogTitle != "" { + logList, err := blog_log.Model.Where("log_title like ?", "%"+req.LogTitle+"%").Fields("log_id").All() + if err != nil { + g.Log().Error(err) + return req, gerror.New("根据日志标题查询日志id时出错") + } + for _, v := range logList { + req.CommentLogIds = append(req.CommentLogIds, v.LogId) + } + } + return req, nil +} + +// 分页查询,返回值total总记录数,page当前页 +func SelectListByPage(req *SelectPageReq) (total int, page int64, list []*ListEntity, err error) { + model := g.DB().Table(Table + " comment") + if req != nil { + if req.Flag == 1 { + model.Where("comment_pid = ?", 0) + } + if req.Status == "0" { + model.Where("comment.comment_status = ?", 0) + } + if req.Status == "1" { + model.Where("comment.comment_status = ?", 1) + } + if req.CommentNickname != "" { + model.Where("comment.comment_nickname like ?", "%"+req.CommentNickname+"%") + } + if req.CommentLogIds != nil { + model.Where("comment.comment_log_id in(?)", req.CommentLogIds) + } + } + model = model.LeftJoin(blog_log.Table+" log", "log.log_id=comment.comment_log_id") + model = model.LeftJoin(user.Table+" u", "u.id=comment.comment_user_id") + // 查询广告位总记录数(总行数) + total, err = model.Count() + if err != nil { + g.Log().Error(err) + err = gerror.New("获取总记录数失败") + return 0, 0, nil, err + } + if req.PageNum == 0 { + req.PageNum = 1 + } + page = req.PageNum + if req.PageSize == 0 { + req.PageSize = 10 + } + // 分页排序查询 + var res gdb.Result + res, err = model.Fields("comment.*,log.log_title,u.user_name").Page(int(page), int(req.PageSize)).Order("comment.create_time desc,comment.comment_id asc").All() + if err != nil { + g.Log().Error(err) + err = gerror.New("分页查询失败") + return 0, 0, nil, err + } + + err = res.Structs(&list) + if err != nil { + g.Log().Error(err) + err = gerror.New("分页查询失败") + return 0, 0, nil, err + } + return total, page, list, nil +} diff --git a/plugin/blog/model/blog_comment/blog_comment_entity.go b/plugin/blog/model/blog_comment/blog_comment_entity.go new file mode 100644 index 0000000..808eb90 --- /dev/null +++ b/plugin/blog/model/blog_comment/blog_comment_entity.go @@ -0,0 +1,67 @@ +// ========================================================================== +// This is auto-generated by gf cli tool. You may not really want to edit it. +// ========================================================================== + +package blog_comment + +import ( + "database/sql" + "github.com/gogf/gf/database/gdb" +) + +// Entity is the golang structure for table blog_comment. +type Entity struct { + CommentId uint `orm:"comment_id,primary" json:"comment_id"` // + CommentUserId uint `orm:"comment_user_id" json:"comment_user_id"` // 评论用户的用户id + CommentContent string `orm:"comment_content" json:"comment_content"` // 评论内容 + CommentPid uint `orm:"comment_pid" json:"comment_pid"` // 当前评论所回复的父评论的id + CommentNum uint `orm:"comment_num" json:"comment_num"` // 当前评论的回复数(下一级子评论数) + CommentStatus uint `orm:"comment_status" json:"comment_status"` // 此评论的状态,0隐藏,1发布 + CommentLogId uint `orm:"comment_log_id" json:"comment_log_id"` // 当前评论所属日志id + CreateTime uint `orm:"create_time" json:"create_time"` // 评论创建时间 + CommentNickname string `orm:"comment_nickname" json:"comment_nickname"` // 评论用户昵称 + ReplyName string `orm:"reply_name" json:"reply_name"` // 当前所回复的对象昵称 + ReplyId uint `orm:"reply_id" json:"reply_id"` // 当前回复对象的id +} + +// OmitEmpty sets OPTION_OMITEMPTY option for the model, which automatically filers +// the data and where attributes for empty values. +func (r *Entity) OmitEmpty() *arModel { + return Model.Data(r).OmitEmpty() +} + +// Inserts does "INSERT...INTO..." statement for inserting current object into table. +func (r *Entity) Insert() (result sql.Result, err error) { + return Model.Data(r).Insert() +} + +// InsertIgnore does "INSERT IGNORE INTO ..." statement for inserting current object into table. +func (r *Entity) InsertIgnore() (result sql.Result, err error) { + return Model.Data(r).InsertIgnore() +} + +// Replace does "REPLACE...INTO..." statement for inserting current object into table. +// If there's already another same record in the table (it checks using primary key or unique index), +// it deletes it and insert this one. +func (r *Entity) Replace() (result sql.Result, err error) { + return Model.Data(r).Replace() +} + +// Save does "INSERT...INTO..." statement for inserting/updating current object into table. +// It updates the record if there's already another same record in the table +// (it checks using primary key or unique index). +func (r *Entity) Save() (result sql.Result, err error) { + return Model.Data(r).Save() +} + +// Update does "UPDATE...WHERE..." statement for updating current object from table. +// It updates the record if there's already another same record in the table +// (it checks using primary key or unique index). +func (r *Entity) Update() (result sql.Result, err error) { + return Model.Data(r).Where(gdb.GetWhereConditionOfStruct(r)).Update() +} + +// Delete does "DELETE FROM...WHERE..." statement for deleting current object from table. +func (r *Entity) Delete() (result sql.Result, err error) { + return Model.Where(gdb.GetWhereConditionOfStruct(r)).Delete() +} diff --git a/plugin/blog/model/blog_comment/blog_comment_model.go b/plugin/blog/model/blog_comment/blog_comment_model.go new file mode 100644 index 0000000..623b473 --- /dev/null +++ b/plugin/blog/model/blog_comment/blog_comment_model.go @@ -0,0 +1,363 @@ +// ========================================================================== +// This is auto-generated by gf cli tool. You may not really want to edit it. +// ========================================================================== + +package blog_comment + +import ( + "database/sql" + "github.com/gogf/gf/database/gdb" + "github.com/gogf/gf/frame/g" + "github.com/gogf/gf/frame/gmvc" + "time" +) + +// arModel is a active record design model for table blog_comment operations. +type arModel struct { + gmvc.M +} + +var ( + // Table is the table name of blog_comment. + Table = "blog_comment" + // Model is the model object of blog_comment. + Model = &arModel{g.DB("default").Table(Table).Safe()} + // Columns defines and stores column names for table blog_comment. + Columns = struct { + CommentId string // + CommentUserId string // 评论用户的用户id + CommentContent string // 评论内容 + CommentPid string // 当前评论所回复的父评论的id + CommentNum string // 当前评论的回复数(下一级子评论数) + CommentStatus string // 此评论的状态,0隐藏,1发布 + CommentLogId string // 当前评论所属日志id + CreateTime string // 评论创建时间 + CommentNickname string // 评论用户昵称 + ReplyName string // 当前所回复的对象昵称 + ReplyId string // 当前回复对象的id + }{ + CommentId: "comment_id", + CommentUserId: "comment_user_id", + CommentContent: "comment_content", + CommentPid: "comment_pid", + CommentNum: "comment_num", + CommentStatus: "comment_status", + CommentLogId: "comment_log_id", + CreateTime: "create_time", + CommentNickname: "comment_nickname", + ReplyName: "reply_name", + ReplyId: "reply_id", + } +) + +// FindOne is a convenience method for Model.FindOne. +// See Model.FindOne. +func FindOne(where ...interface{}) (*Entity, error) { + return Model.FindOne(where...) +} + +// FindAll is a convenience method for Model.FindAll. +// See Model.FindAll. +func FindAll(where ...interface{}) ([]*Entity, error) { + return Model.FindAll(where...) +} + +// FindValue is a convenience method for Model.FindValue. +// See Model.FindValue. +func FindValue(fieldsAndWhere ...interface{}) (gdb.Value, error) { + return Model.FindValue(fieldsAndWhere...) +} + +// FindArray is a convenience method for Model.FindArray. +// See Model.FindArray. +func FindArray(fieldsAndWhere ...interface{}) ([]gdb.Value, error) { + return Model.FindArray(fieldsAndWhere...) +} + +// FindCount is a convenience method for Model.FindCount. +// See Model.FindCount. +func FindCount(where ...interface{}) (int, error) { + return Model.FindCount(where...) +} + +// Insert is a convenience method for Model.Insert. +func Insert(data ...interface{}) (result sql.Result, err error) { + return Model.Insert(data...) +} + +// InsertIgnore is a convenience method for Model.InsertIgnore. +func InsertIgnore(data ...interface{}) (result sql.Result, err error) { + return Model.InsertIgnore(data...) +} + +// Replace is a convenience method for Model.Replace. +func Replace(data ...interface{}) (result sql.Result, err error) { + return Model.Replace(data...) +} + +// Save is a convenience method for Model.Save. +func Save(data ...interface{}) (result sql.Result, err error) { + return Model.Save(data...) +} + +// Update is a convenience method for Model.Update. +func Update(dataAndWhere ...interface{}) (result sql.Result, err error) { + return Model.Update(dataAndWhere...) +} + +// Delete is a convenience method for Model.Delete. +func Delete(where ...interface{}) (result sql.Result, err error) { + return Model.Delete(where...) +} + +// As sets an alias name for current table. +func (m *arModel) As(as string) *arModel { + return &arModel{m.M.As(as)} +} + +// TX sets the transaction for current operation. +func (m *arModel) TX(tx *gdb.TX) *arModel { + return &arModel{m.M.TX(tx)} +} + +// Master marks the following operation on master node. +func (m *arModel) Master() *arModel { + return &arModel{m.M.Master()} +} + +// Slave marks the following operation on slave node. +// Note that it makes sense only if there's any slave node configured. +func (m *arModel) Slave() *arModel { + return &arModel{m.M.Slave()} +} + +// LeftJoin does "LEFT JOIN ... ON ..." statement on the model. +// The parameter
can be joined table and its joined condition, +// and also with its alias name, like: +// Table("user").LeftJoin("user_detail", "user_detail.uid=user.uid") +// Table("user", "u").LeftJoin("user_detail", "ud", "ud.uid=u.uid") +func (m *arModel) LeftJoin(table ...string) *arModel { + return &arModel{m.M.LeftJoin(table...)} +} + +// RightJoin does "RIGHT JOIN ... ON ..." statement on the model. +// The parameter
can be joined table and its joined condition, +// and also with its alias name, like: +// Table("user").RightJoin("user_detail", "user_detail.uid=user.uid") +// Table("user", "u").RightJoin("user_detail", "ud", "ud.uid=u.uid") +func (m *arModel) RightJoin(table ...string) *arModel { + return &arModel{m.M.RightJoin(table...)} +} + +// InnerJoin does "INNER JOIN ... ON ..." statement on the model. +// The parameter
can be joined table and its joined condition, +// and also with its alias name, like: +// Table("user").InnerJoin("user_detail", "user_detail.uid=user.uid") +// Table("user", "u").InnerJoin("user_detail", "ud", "ud.uid=u.uid") +func (m *arModel) InnerJoin(table ...string) *arModel { + return &arModel{m.M.InnerJoin(table...)} +} + +// Fields sets the operation fields of the model, multiple fields joined using char ','. +func (m *arModel) Fields(fields string) *arModel { + return &arModel{m.M.Fields(fields)} +} + +// FieldsEx sets the excluded operation fields of the model, multiple fields joined using char ','. +func (m *arModel) FieldsEx(fields string) *arModel { + return &arModel{m.M.FieldsEx(fields)} +} + +// Option sets the extra operation option for the model. +func (m *arModel) Option(option int) *arModel { + return &arModel{m.M.Option(option)} +} + +// OmitEmpty sets OPTION_OMITEMPTY option for the model, which automatically filers +// the data and where attributes for empty values. +func (m *arModel) OmitEmpty() *arModel { + return &arModel{m.M.OmitEmpty()} +} + +// Filter marks filtering the fields which does not exist in the fields of the operated table. +func (m *arModel) Filter() *arModel { + return &arModel{m.M.Filter()} +} + +// Where sets the condition statement for the model. The parameter can be type of +// string/map/gmap/slice/struct/*struct, etc. Note that, if it's called more than one times, +// multiple conditions will be joined into where statement using "AND". +// Eg: +// Where("uid=10000") +// Where("uid", 10000) +// Where("money>? AND name like ?", 99999, "vip_%") +// Where("uid", 1).Where("name", "john") +// Where("status IN (?)", g.Slice{1,2,3}) +// Where("age IN(?,?)", 18, 50) +// Where(User{ Id : 1, UserName : "john"}) +func (m *arModel) Where(where interface{}, args ...interface{}) *arModel { + return &arModel{m.M.Where(where, args...)} +} + +// And adds "AND" condition to the where statement. +func (m *arModel) And(where interface{}, args ...interface{}) *arModel { + return &arModel{m.M.And(where, args...)} +} + +// Or adds "OR" condition to the where statement. +func (m *arModel) Or(where interface{}, args ...interface{}) *arModel { + return &arModel{m.M.Or(where, args...)} +} + +// Group sets the "GROUP BY" statement for the model. +func (m *arModel) Group(groupBy string) *arModel { + return &arModel{m.M.Group(groupBy)} +} + +// Order sets the "ORDER BY" statement for the model. +func (m *arModel) Order(orderBy ...string) *arModel { + return &arModel{m.M.Order(orderBy...)} +} + +// Limit sets the "LIMIT" statement for the model. +// The parameter can be either one or two number, if passed two number is passed, +// it then sets "LIMIT limit[0],limit[1]" statement for the model, or else it sets "LIMIT limit[0]" +// statement. +func (m *arModel) Limit(limit ...int) *arModel { + return &arModel{m.M.Limit(limit...)} +} + +// Offset sets the "OFFSET" statement for the model. +// It only makes sense for some databases like SQLServer, PostgreSQL, etc. +func (m *arModel) Offset(offset int) *arModel { + return &arModel{m.M.Offset(offset)} +} + +// Page sets the paging number for the model. +// The parameter is started from 1 for paging. +// Note that, it differs that the Limit function start from 0 for "LIMIT" statement. +func (m *arModel) Page(page, limit int) *arModel { + return &arModel{m.M.Page(page, limit)} +} + +// Batch sets the batch operation number for the model. +func (m *arModel) Batch(batch int) *arModel { + return &arModel{m.M.Batch(batch)} +} + +// Cache sets the cache feature for the model. It caches the result of the sql, which means +// if there's another same sql request, it just reads and returns the result from cache, it +// but not committed and executed into the database. +// +// If the parameter < 0, which means it clear the cache with given . +// If the parameter = 0, which means it never expires. +// If the parameter > 0, which means it expires after . +// +// The optional parameter is used to bind a name to the cache, which means you can later +// control the cache like changing the or clearing the cache with specified . +// +// Note that, the cache feature is disabled if the model is operating on a transaction. +func (m *arModel) Cache(duration time.Duration, name ...string) *arModel { + return &arModel{m.M.Cache(duration, name...)} +} + +// Data sets the operation data for the model. +// The parameter can be type of string/map/gmap/slice/struct/*struct, etc. +// Eg: +// Data("uid=10000") +// Data("uid", 10000) +// Data(g.Map{"uid": 10000, "name":"john"}) +// Data(g.Slice{g.Map{"uid": 10000, "name":"john"}, g.Map{"uid": 20000, "name":"smith"}) +func (m *arModel) Data(data ...interface{}) *arModel { + return &arModel{m.M.Data(data...)} +} + +// All does "SELECT FROM ..." statement for the model. +// It retrieves the records from table and returns the result as []*Entity. +// It returns nil if there's no record retrieved with the given conditions from table. +// +// The optional parameter is the same as the parameter of Model.Where function, +// see Model.Where. +func (m *arModel) All(where ...interface{}) ([]*Entity, error) { + all, err := m.M.All(where...) + if err != nil { + return nil, err + } + var entities []*Entity + if err = all.Structs(&entities); err != nil && err != sql.ErrNoRows { + return nil, err + } + return entities, nil +} + +// One retrieves one record from table and returns the result as *Entity. +// It returns nil if there's no record retrieved with the given conditions from table. +// +// The optional parameter is the same as the parameter of Model.Where function, +// see Model.Where. +func (m *arModel) One(where ...interface{}) (*Entity, error) { + one, err := m.M.One(where...) + if err != nil { + return nil, err + } + var entity *Entity + if err = one.Struct(&entity); err != nil && err != sql.ErrNoRows { + return nil, err + } + return entity, nil +} + +// FindOne retrieves and returns a single Record by Model.WherePri and Model.One. +// Also see Model.WherePri and Model.One. +func (m *arModel) FindOne(where ...interface{}) (*Entity, error) { + one, err := m.M.FindOne(where...) + if err != nil { + return nil, err + } + var entity *Entity + if err = one.Struct(&entity); err != nil && err != sql.ErrNoRows { + return nil, err + } + return entity, nil +} + +// FindAll retrieves and returns Result by by Model.WherePri and Model.All. +// Also see Model.WherePri and Model.All. +func (m *arModel) FindAll(where ...interface{}) ([]*Entity, error) { + all, err := m.M.FindAll(where...) + if err != nil { + return nil, err + } + var entities []*Entity + if err = all.Structs(&entities); err != nil && err != sql.ErrNoRows { + return nil, err + } + return entities, nil +} + +// Chunk iterates the table with given size and callback function. +func (m *arModel) Chunk(limit int, callback func(entities []*Entity, err error) bool) { + m.M.Chunk(limit, func(result gdb.Result, err error) bool { + var entities []*Entity + err = result.Structs(&entities) + if err == sql.ErrNoRows { + return false + } + return callback(entities, err) + }) +} + +// LockUpdate sets the lock for update for current operation. +func (m *arModel) LockUpdate() *arModel { + return &arModel{m.M.LockUpdate()} +} + +// LockShared sets the lock in share mode for current operation. +func (m *arModel) LockShared() *arModel { + return &arModel{m.M.LockShared()} +} + +// Unscoped enables/disables the soft deleting feature. +func (m *arModel) Unscoped() *arModel { + return &arModel{m.M.Unscoped()} +} diff --git a/plugin/blog/model/blog_log/blog_log.go b/plugin/blog/model/blog_log/blog_log.go new file mode 100644 index 0000000..d1fe53e --- /dev/null +++ b/plugin/blog/model/blog_log/blog_log.go @@ -0,0 +1,253 @@ +package blog_log + +import ( + "gfast/plugin/blog/model/blog_classification" + "github.com/gogf/gf/database/gdb" + "github.com/gogf/gf/errors/gerror" + "github.com/gogf/gf/frame/g" + "github.com/gogf/gf/os/gtime" +) + +// AddReq 用于存储新增请求的请求参数 +type AddReq struct { + LogType int `p:"logType" v:"required#名称不能为空"` // 所属分类 + LogSign int `p:"logSign"` // 0.一般 1.置顶,2.幻灯,3.推荐 + LogTitle string `p:"logTitle" v:"required#标题不能为空"` // 日志标题 + LogAuthor string `p:"logAuthor"` // 作者名 + LogUrl string `p:"logUrl"` // 跳转地址 + LogThumbnail string `p:"logThumbnail"` // 缩略图 + LogStatus uint `p:"logStatus"` // 状态:1发布,0未发布 + LogSort int `P:"logSort"` // 排序 + LogContent string `p:"logContent" v:"required#内容不能为空"` // 内容 +} + +// EditReq 用于存储修改请求参数 +type EditReq struct { + LogId int64 `p:"logId" v:"required|min:1#日志ID不能为空|日志ID错误"` + AddReq +} + +// SelectPageReq 用于存储分页查询的请求参数 +type SelectPageReq struct { + LogTitle string `p:"logTitle"` // 日志标题 + PageNum int64 `p:"pageNum"` // 当前页 + PageSize int64 `p:"pageSize"` // 每页显示记录数 + CateTypeId int `p:"cateId"` // 分类类型id + Status int // 状态 +} + +// 用于存储联合查询的数据 +type ListEntity struct { + Entity + ClassificationName string `orm:"classification_name" json:"classification_name" ` // 所属分类名 +} + +// GetLogByID 根据ID查询记录 +func GetLogByID(id int64) (entity *ListEntity, err error) { + entity = new(ListEntity) + model := g.DB().Table(Table + " log") + model = model.Where("log_id = ?", id) + model = model.LeftJoin(blog_classification.Table+" cf", "cf.classification_id = log.log_type") + var res gdb.Record + res, err = model.Fields("log.*,cf.classification_name").FindOne() + if err != nil { + g.Log().Error(err) + return nil, gerror.New("根据ID查询记录出错!") + } + err = res.Struct(entity) + if err != nil { + g.Log().Error(err) + return nil, gerror.New("根据ID查询转换时出错") + } + if entity == nil { + return nil, gerror.New("根据ID未能查询到记录") + } + return entity, nil +} + +// AddSave 添加的方法 +func AddSave(req *AddReq) error { + var entity Entity + entity.LogType = req.LogType + entity.LogSign = req.LogSign + entity.LogTitle = req.LogTitle + entity.LogAuthor = req.LogAuthor + entity.LogUrl = req.LogUrl + entity.LogThumbnail = req.LogThumbnail + entity.CreatTime = uint(gtime.Timestamp()) + entity.LogStatus = req.LogStatus + entity.LogSort = req.LogSort + entity.LogContent = req.LogContent + // 保存实体 + _, err := entity.Insert() + if err != nil { + g.Log().Error(err) + return gerror.New("添加记录入库失败!") + } + return nil +} + +// 批量删除记录 +func DeleteByIDs(ids []int) error { + _, err := Model.Delete("log_id in(?)", ids) + if err != nil { + g.Log().Error(err) + return gerror.New("删除记录失败!") + } + return nil +} + +// 根据ID修改记录 +func EditSave(req *EditReq) error { + // 先根据ID来查询要修改的记录 + entity, err := GetLogByID(req.LogId) + if err != nil { + return err + } + // 修改实体 + entity.LogType = req.LogType + entity.LogSign = req.LogSign + entity.LogTitle = req.LogTitle + entity.LogAuthor = req.LogAuthor + entity.LogUrl = req.LogUrl + entity.LogThumbnail = req.LogThumbnail + entity.LogStatus = req.LogStatus + entity.LogSort = req.LogSort + entity.LogContent = req.LogContent + _, err = Model.Filter().Save(entity) + if err != nil { + g.Log().Error(err) + return gerror.New("修改记录失败!") + } + return nil +} + +// 分页查询,返回值total总记录数,page当前页 +func SelectListByPage(req *SelectPageReq) (total int, page int64, list []*ListEntity, err error) { + model := g.DB().Table(Table + " log") + if req != nil { + if req.LogTitle != "" { + model.Where("log.log_title like ?", "%"+req.LogTitle+"%") + } + if req.Status == 1 { + model.Where("log.log_status = 1") + } + if req.CateTypeId != 0 { + model.Where("log_type = ?", req.CateTypeId) + } + } + model = model.LeftJoin(blog_classification.Table+" cf", "cf.classification_id=log.log_type") + // 查询广告位总记录数(总行数) + total, err = model.Count() + if err != nil { + g.Log().Error(err) + err = gerror.New("获取总记录数失败") + return 0, 0, nil, err + } + if req.PageNum == 0 { + req.PageNum = 1 + } + page = req.PageNum + if req.PageSize == 0 { + req.PageSize = 10 + } + // 分页排序查询 + var res gdb.Result + res, err = model.Fields("log.*,cf.classification_name"). + Page(int(page), int(req.PageSize)).Order("log.log_sort asc,log.log_id desc").All() + if err != nil { + g.Log().Error(err) + err = gerror.New("分页查询失败") + return 0, 0, nil, err + } + err = res.Structs(&list) + if err != nil { + g.Log().Error(err) + err = gerror.New("分页查询失败") + return 0, 0, nil, err + } + return total, page, list, nil +} + +// 按时间倒序查询size篇标志为sign分类id为typeId,状态为status的文章,标志值0.一般,1.置顶,2.幻灯,3.推荐,typeId等于0时不区分分类 +func FindSizeArticleBySign(size int, status int, sign int, typeId int) (list []*ListEntity, err error) { + model := g.DB().Table(Table + " log") + if status == 1 { + model = model.Where("log.log_status = ?", 1) + } + if status == 0 { + model = model.Where("log.log_status = ?", 0) + } + if sign == 0 { + model = model.Where("log.log_sign = 0") + } + if sign == 1 { + model = model.Where("log.log_sign = 1") + } + if sign == 2 { + model = model.Where("log.log_sign = 2") + } + if sign == 3 { + model = model.Where("log.log_sign = 3") + } + if typeId != 0 { + model = model.Where("log_type = ?", typeId) + } + model = model.LeftJoin(blog_classification.Table+" cf", "cf.classification_id=log.log_type") + // 分页排序查询 + var res gdb.Result + res, err = model.Fields("log.*,cf.classification_name"). + Order("log.log_sort asc,log.creat_time desc").Limit(size).All() + if err != nil { + g.Log().Error(err) + return nil, gerror.New("根据标志查询出错") + } + err = res.Structs(&list) + if err != nil { + g.Log().Error(err) + return nil, gerror.New("根据标志查询出错") + } + return +} + +// 按时间倒序查询size篇分类id为typeId,状态为status的文章,typeId等于0时不区分分类 +func FindSizeArticle(size int, status int, typeId int) (list []*ListEntity, err error) { + model := g.DB().Table(Table + " log") + if status == 1 { + model = model.Where("log.log_status = ?", 1) + } + if status == 0 { + model = model.Where("log.log_status = ?", 0) + } + if typeId != 0 { + model = model.Where("log_type = ?", typeId) + } + model = model.LeftJoin(blog_classification.Table+" cf", "cf.classification_id=log.log_type") + // 分页排序查询 + var res gdb.Result + res, err = model.Fields("log.*,cf.classification_name").Order("log.log_sort asc,log.creat_time desc").Limit(size).All() + if err != nil { + g.Log().Error(err) + return nil, gerror.New("根据标志查询出错") + } + err = res.Structs(&list) + if err != nil { + g.Log().Error(err) + return nil, gerror.New("根据标志查询出错") + } + return +} + +// 查询size篇文章并根据点击数排序 +func FindArticleByHits(size int, status int) (list []*Entity, err error) { + model := Model + if status == 1 { + model.Where("log_status = 1") + } + list, err = model.Order("log_hits desc").FindAll() + if err != nil { + g.Log().Error(err) + return nil, gerror.New("根据点击数排序查询失败") + } + return +} diff --git a/plugin/blog/model/blog_log/blog_log_entity.go b/plugin/blog/model/blog_log/blog_log_entity.go new file mode 100644 index 0000000..17f0e56 --- /dev/null +++ b/plugin/blog/model/blog_log/blog_log_entity.go @@ -0,0 +1,69 @@ +// ========================================================================== +// This is auto-generated by gf cli tool. You may not really want to edit it. +// ========================================================================== + +package blog_log + +import ( + "database/sql" + "github.com/gogf/gf/database/gdb" +) + +// Entity is the golang structure for table blog_log. +type Entity struct { + LogId int `orm:"log_id,primary" json:"log_id"` + LogType int `orm:"log_type" json:"log_type"` // 所属分类 + LogSign int `orm:"log_sign" json:"log_sign"` // 0.一般 1.置顶,2.幻灯,3.推荐 + LogTitle string `orm:"log_title" json:"log_title"` // 日志标题 + LogAuthor string `orm:"log_author" json:"log_author"` // 用户名 + LogUrl string `orm:"log_url" json:"log_url"` // 跳转地址 + LogThumbnail string `orm:"log_thumbnail" json:"log_thumbnail"` // 缩略图 + LogHits uint64 `orm:"log_hits" json:"log_hits"` // 查看数点击数 + LogComments int64 `orm:"log_comments" json:"log_comments"` // 评论数 + CreatTime uint `orm:"creat_time" json:"creat_time"` // 创建时间 + LogStatus uint `orm:"log_status" json:"log_status"` // 状态:1发布,0未发布 + LogSort int `orm:"log_sort" json:"log_sort"` // 排序 + LogContent string `orm:"log_content" json:"log_content"` // 内容 +} + +// OmitEmpty sets OPTION_OMITEMPTY option for the model, which automatically filers +// the data and where attributes for empty values. +func (r *Entity) OmitEmpty() *arModel { + return Model.Data(r).OmitEmpty() +} + +// Inserts does "INSERT...INTO..." statement for inserting current object into table. +func (r *Entity) Insert() (result sql.Result, err error) { + return Model.Data(r).Insert() +} + +// InsertIgnore does "INSERT IGNORE INTO ..." statement for inserting current object into table. +func (r *Entity) InsertIgnore() (result sql.Result, err error) { + return Model.Data(r).InsertIgnore() +} + +// Replace does "REPLACE...INTO..." statement for inserting current object into table. +// If there's already another same record in the table (it checks using primary key or unique index), +// it deletes it and insert this one. +func (r *Entity) Replace() (result sql.Result, err error) { + return Model.Data(r).Replace() +} + +// Save does "INSERT...INTO..." statement for inserting/updating current object into table. +// It updates the record if there's already another same record in the table +// (it checks using primary key or unique index). +func (r *Entity) Save() (result sql.Result, err error) { + return Model.Data(r).Save() +} + +// Update does "UPDATE...WHERE..." statement for updating current object from table. +// It updates the record if there's already another same record in the table +// (it checks using primary key or unique index). +func (r *Entity) Update() (result sql.Result, err error) { + return Model.Data(r).Where(gdb.GetWhereConditionOfStruct(r)).Update() +} + +// Delete does "DELETE FROM...WHERE..." statement for deleting current object from table. +func (r *Entity) Delete() (result sql.Result, err error) { + return Model.Where(gdb.GetWhereConditionOfStruct(r)).Delete() +} diff --git a/plugin/blog/model/blog_log/blog_log_model.go b/plugin/blog/model/blog_log/blog_log_model.go new file mode 100644 index 0000000..b04f968 --- /dev/null +++ b/plugin/blog/model/blog_log/blog_log_model.go @@ -0,0 +1,367 @@ +// ========================================================================== +// This is auto-generated by gf cli tool. You may not really want to edit it. +// ========================================================================== + +package blog_log + +import ( + "database/sql" + "github.com/gogf/gf/database/gdb" + "github.com/gogf/gf/frame/g" + "github.com/gogf/gf/frame/gmvc" + "time" +) + +// arModel is a active record design model for table blog_log operations. +type arModel struct { + gmvc.M +} + +var ( + // Table is the table name of blog_log. + Table = "blog_log" + // Model is the model object of blog_log. + Model = &arModel{g.DB("default").Table(Table).Safe()} + // Columns defines and stores column names for table blog_log. + Columns = struct { + LogId string // + LogType string // 分类, 1va + LogSign string // 0.一般 1.置顶,2.幻灯,3.推荐 + LogTitle string // 日志标题 + LogAuthor string // 作者名 + LogUrl string // 跳转地址 + LogThumbnail string // 缩略图 + LogHits string // 查看数点击数 + LogComments string // 评论数 + CreatTime string // 创建时间 + LogStatus string // 状态:1发布,0未发布 + LogSort string // 排序 + LogContent string // 内容 + }{ + LogId: "log_id", + LogType: "log_type", + LogSign: "log_sign", + LogTitle: "log_title", + LogAuthor: "log_author", + LogUrl: "log_url", + LogThumbnail: "log_thumbnail", + LogHits: "log_hits", + LogComments: "log_comments", + CreatTime: "creat_time", + LogStatus: "log_status", + LogSort: "log_sort", + LogContent: "log_content", + } +) + +// FindOne is a convenience method for Model.FindOne. +// See Model.FindOne. +func FindOne(where ...interface{}) (*Entity, error) { + return Model.FindOne(where...) +} + +// FindAll is a convenience method for Model.FindAll. +// See Model.FindAll. +func FindAll(where ...interface{}) ([]*Entity, error) { + return Model.FindAll(where...) +} + +// FindValue is a convenience method for Model.FindValue. +// See Model.FindValue. +func FindValue(fieldsAndWhere ...interface{}) (gdb.Value, error) { + return Model.FindValue(fieldsAndWhere...) +} + +// FindArray is a convenience method for Model.FindArray. +// See Model.FindArray. +func FindArray(fieldsAndWhere ...interface{}) ([]gdb.Value, error) { + return Model.FindArray(fieldsAndWhere...) +} + +// FindCount is a convenience method for Model.FindCount. +// See Model.FindCount. +func FindCount(where ...interface{}) (int, error) { + return Model.FindCount(where...) +} + +// Insert is a convenience method for Model.Insert. +func Insert(data ...interface{}) (result sql.Result, err error) { + return Model.Insert(data...) +} + +// InsertIgnore is a convenience method for Model.InsertIgnore. +func InsertIgnore(data ...interface{}) (result sql.Result, err error) { + return Model.InsertIgnore(data...) +} + +// Replace is a convenience method for Model.Replace. +func Replace(data ...interface{}) (result sql.Result, err error) { + return Model.Replace(data...) +} + +// Save is a convenience method for Model.Save. +func Save(data ...interface{}) (result sql.Result, err error) { + return Model.Save(data...) +} + +// Update is a convenience method for Model.Update. +func Update(dataAndWhere ...interface{}) (result sql.Result, err error) { + return Model.Update(dataAndWhere...) +} + +// Delete is a convenience method for Model.Delete. +func Delete(where ...interface{}) (result sql.Result, err error) { + return Model.Delete(where...) +} + +// As sets an alias name for current table. +func (m *arModel) As(as string) *arModel { + return &arModel{m.M.As(as)} +} + +// TX sets the transaction for current operation. +func (m *arModel) TX(tx *gdb.TX) *arModel { + return &arModel{m.M.TX(tx)} +} + +// Master marks the following operation on master node. +func (m *arModel) Master() *arModel { + return &arModel{m.M.Master()} +} + +// Slave marks the following operation on slave node. +// Note that it makes sense only if there's any slave node configured. +func (m *arModel) Slave() *arModel { + return &arModel{m.M.Slave()} +} + +// LeftJoin does "LEFT JOIN ... ON ..." statement on the model. +// The parameter
can be joined table and its joined condition, +// and also with its alias name, like: +// Table("user").LeftJoin("user_detail", "user_detail.uid=user.uid") +// Table("user", "u").LeftJoin("user_detail", "ud", "ud.uid=u.uid") +func (m *arModel) LeftJoin(table ...string) *arModel { + return &arModel{m.M.LeftJoin(table...)} +} + +// RightJoin does "RIGHT JOIN ... ON ..." statement on the model. +// The parameter
can be joined table and its joined condition, +// and also with its alias name, like: +// Table("user").RightJoin("user_detail", "user_detail.uid=user.uid") +// Table("user", "u").RightJoin("user_detail", "ud", "ud.uid=u.uid") +func (m *arModel) RightJoin(table ...string) *arModel { + return &arModel{m.M.RightJoin(table...)} +} + +// InnerJoin does "INNER JOIN ... ON ..." statement on the model. +// The parameter
can be joined table and its joined condition, +// and also with its alias name, like: +// Table("user").InnerJoin("user_detail", "user_detail.uid=user.uid") +// Table("user", "u").InnerJoin("user_detail", "ud", "ud.uid=u.uid") +func (m *arModel) InnerJoin(table ...string) *arModel { + return &arModel{m.M.InnerJoin(table...)} +} + +// Fields sets the operation fields of the model, multiple fields joined using char ','. +func (m *arModel) Fields(fields string) *arModel { + return &arModel{m.M.Fields(fields)} +} + +// FieldsEx sets the excluded operation fields of the model, multiple fields joined using char ','. +func (m *arModel) FieldsEx(fields string) *arModel { + return &arModel{m.M.FieldsEx(fields)} +} + +// Option sets the extra operation option for the model. +func (m *arModel) Option(option int) *arModel { + return &arModel{m.M.Option(option)} +} + +// OmitEmpty sets OPTION_OMITEMPTY option for the model, which automatically filers +// the data and where attributes for empty values. +func (m *arModel) OmitEmpty() *arModel { + return &arModel{m.M.OmitEmpty()} +} + +// Filter marks filtering the fields which does not exist in the fields of the operated table. +func (m *arModel) Filter() *arModel { + return &arModel{m.M.Filter()} +} + +// Where sets the condition statement for the model. The parameter can be type of +// string/map/gmap/slice/struct/*struct, etc. Note that, if it's called more than one times, +// multiple conditions will be joined into where statement using "AND". +// Eg: +// Where("uid=10000") +// Where("uid", 10000) +// Where("money>? AND name like ?", 99999, "vip_%") +// Where("uid", 1).Where("name", "john") +// Where("status IN (?)", g.Slice{1,2,3}) +// Where("age IN(?,?)", 18, 50) +// Where(User{ Id : 1, UserName : "john"}) +func (m *arModel) Where(where interface{}, args ...interface{}) *arModel { + return &arModel{m.M.Where(where, args...)} +} + +// And adds "AND" condition to the where statement. +func (m *arModel) And(where interface{}, args ...interface{}) *arModel { + return &arModel{m.M.And(where, args...)} +} + +// Or adds "OR" condition to the where statement. +func (m *arModel) Or(where interface{}, args ...interface{}) *arModel { + return &arModel{m.M.Or(where, args...)} +} + +// Group sets the "GROUP BY" statement for the model. +func (m *arModel) Group(groupBy string) *arModel { + return &arModel{m.M.Group(groupBy)} +} + +// Order sets the "ORDER BY" statement for the model. +func (m *arModel) Order(orderBy ...string) *arModel { + return &arModel{m.M.Order(orderBy...)} +} + +// Limit sets the "LIMIT" statement for the model. +// The parameter can be either one or two number, if passed two number is passed, +// it then sets "LIMIT limit[0],limit[1]" statement for the model, or else it sets "LIMIT limit[0]" +// statement. +func (m *arModel) Limit(limit ...int) *arModel { + return &arModel{m.M.Limit(limit...)} +} + +// Offset sets the "OFFSET" statement for the model. +// It only makes sense for some databases like SQLServer, PostgreSQL, etc. +func (m *arModel) Offset(offset int) *arModel { + return &arModel{m.M.Offset(offset)} +} + +// Page sets the paging number for the model. +// The parameter is started from 1 for paging. +// Note that, it differs that the Limit function start from 0 for "LIMIT" statement. +func (m *arModel) Page(page, limit int) *arModel { + return &arModel{m.M.Page(page, limit)} +} + +// Batch sets the batch operation number for the model. +func (m *arModel) Batch(batch int) *arModel { + return &arModel{m.M.Batch(batch)} +} + +// Cache sets the cache feature for the model. It caches the result of the sql, which means +// if there's another same sql request, it just reads and returns the result from cache, it +// but not committed and executed into the database. +// +// If the parameter < 0, which means it clear the cache with given . +// If the parameter = 0, which means it never expires. +// If the parameter > 0, which means it expires after . +// +// The optional parameter is used to bind a name to the cache, which means you can later +// control the cache like changing the or clearing the cache with specified . +// +// Note that, the cache feature is disabled if the model is operating on a transaction. +func (m *arModel) Cache(duration time.Duration, name ...string) *arModel { + return &arModel{m.M.Cache(duration, name...)} +} + +// Data sets the operation data for the model. +// The parameter can be type of string/map/gmap/slice/struct/*struct, etc. +// Eg: +// Data("uid=10000") +// Data("uid", 10000) +// Data(g.Map{"uid": 10000, "name":"john"}) +// Data(g.Slice{g.Map{"uid": 10000, "name":"john"}, g.Map{"uid": 20000, "name":"smith"}) +func (m *arModel) Data(data ...interface{}) *arModel { + return &arModel{m.M.Data(data...)} +} + +// All does "SELECT FROM ..." statement for the model. +// It retrieves the records from table and returns the result as []*Entity. +// It returns nil if there's no record retrieved with the given conditions from table. +// +// The optional parameter is the same as the parameter of Model.Where function, +// see Model.Where. +func (m *arModel) All(where ...interface{}) ([]*Entity, error) { + all, err := m.M.All(where...) + if err != nil { + return nil, err + } + var entities []*Entity + if err = all.Structs(&entities); err != nil && err != sql.ErrNoRows { + return nil, err + } + return entities, nil +} + +// One retrieves one record from table and returns the result as *Entity. +// It returns nil if there's no record retrieved with the given conditions from table. +// +// The optional parameter is the same as the parameter of Model.Where function, +// see Model.Where. +func (m *arModel) One(where ...interface{}) (*Entity, error) { + one, err := m.M.One(where...) + if err != nil { + return nil, err + } + var entity *Entity + if err = one.Struct(&entity); err != nil && err != sql.ErrNoRows { + return nil, err + } + return entity, nil +} + +// FindOne retrieves and returns a single Record by Model.WherePri and Model.One. +// Also see Model.WherePri and Model.One. +func (m *arModel) FindOne(where ...interface{}) (*Entity, error) { + one, err := m.M.FindOne(where...) + if err != nil { + return nil, err + } + var entity *Entity + if err = one.Struct(&entity); err != nil && err != sql.ErrNoRows { + return nil, err + } + return entity, nil +} + +// FindAll retrieves and returns Result by by Model.WherePri and Model.All. +// Also see Model.WherePri and Model.All. +func (m *arModel) FindAll(where ...interface{}) ([]*Entity, error) { + all, err := m.M.FindAll(where...) + if err != nil { + return nil, err + } + var entities []*Entity + if err = all.Structs(&entities); err != nil && err != sql.ErrNoRows { + return nil, err + } + return entities, nil +} + +// Chunk iterates the table with given size and callback function. +func (m *arModel) Chunk(limit int, callback func(entities []*Entity, err error) bool) { + m.M.Chunk(limit, func(result gdb.Result, err error) bool { + var entities []*Entity + err = result.Structs(&entities) + if err == sql.ErrNoRows { + return false + } + return callback(entities, err) + }) +} + +// LockUpdate sets the lock for update for current operation. +func (m *arModel) LockUpdate() *arModel { + return &arModel{m.M.LockUpdate()} +} + +// LockShared sets the lock in share mode for current operation. +func (m *arModel) LockShared() *arModel { + return &arModel{m.M.LockShared()} +} + +// Unscoped enables/disables the soft deleting feature. +func (m *arModel) Unscoped() *arModel { + return &arModel{m.M.Unscoped()} +} diff --git a/plugin/blog/service/blog_service/classification.go b/plugin/blog/service/blog_service/classification.go new file mode 100644 index 0000000..c4bed5f --- /dev/null +++ b/plugin/blog/service/blog_service/classification.go @@ -0,0 +1,73 @@ +package blog_service + +import ( + "gfast/plugin/blog/model/blog_classification" +) + +//获取频道列表 +func GetMenuListChannel() (list []*blog_classification.Entity, err error) { + //获取频道列表 + listAll, err := GetMenuList() + if err != nil { + return + } + list = make([]*blog_classification.Entity, 0, len(listAll)) + for _, v := range listAll { + list = append(list, v) + } + return +} + +//获取所有菜单列表 +func GetMenuList() (list []*blog_classification.Entity, err error) { + return blog_classification.GetList() +} + +// 添加 +func AddClassificationSave(req *blog_classification.AddReq) (err error) { + // 判断名称是否已存在 + err = blog_classification.CheakClassificationNameUnique(req.ClassificationName, 0) + if err != nil { + return err + } + // 不存在则调用AddSave()函数添加 + err = blog_classification.AddSave(req) + if err != nil { + return err + } + return nil +} + +// 批量删除 +func DeleteClassificationByIds(ids []int) error { + return blog_classification.DeleteClassificationByIds(ids) +} + +// 修改 +func EditClassificationSave(editReq *blog_classification.EditReq) error { + // 判断修改后的名称的唯一性 + err := blog_classification.CheakClassificationNameUnique(editReq.ClassificationName, editReq.ClassificationId) + if err != nil { + return err + } + err = blog_classification.EditSave(editReq) + if err != nil { + return err + } + return nil +} + +// 根据ID查询 +func GetClassificationByID(id int64) (*blog_classification.Entity, error) { + return blog_classification.GetClassificationByID(id) +} + +// 分页查询 +func SelectClassificationListByPage(req *blog_classification.SelectPageReq) (total int, page int64, list []*blog_classification.Entity, err error) { + return blog_classification.SelectListByPage(req) +} + +// 查询所有状态为正常的分类 +func FindAllList() (list []*blog_classification.Entity, err error) { + return blog_classification.FindAllList() +} diff --git a/plugin/blog/service/blog_service/comment.go b/plugin/blog/service/blog_service/comment.go new file mode 100644 index 0000000..cfa728c --- /dev/null +++ b/plugin/blog/service/blog_service/comment.go @@ -0,0 +1,46 @@ +package blog_service + +import ( + "gfast/plugin/blog/model/blog_comment" +) + +// 添加 +func AddCommentSave(req *blog_comment.AddReq) error { + err := blog_comment.AddSave(req) + if err != nil { + return err + } + if req.CommentPid != 0 { + err = blog_comment.FindSonCommentCount(int(req.ReplyId)) // 每次添加评论都要更新所回复评论的回复数 + if err != nil { + return err + } + } + return nil +} + +// 删除 +func DeleteCommentByIDs(Ids []int) error { + return blog_comment.DeleteByIDs(Ids) +} + +//修改 +func EditCommentSave(editReq *blog_comment.EditReq) error { + return blog_comment.EditSave(editReq) +} + +// 根据ID查询 +func GetCommentByID(id int64) (*blog_comment.Entity, error) { + return blog_comment.GetCommentByID(id) +} + +// 分页查询 +func SelectCommentListByPage(req *blog_comment.SelectPageReq) (total int, page int64, list []*blog_comment.ListEntity, err error) { + blog_comment.FindUserAndLogIds(req) + return blog_comment.SelectListByPage(req) +} + +// GetChildren 将分页查出的实体切片中每一个元素的子评论查出来并添加进去 +func GetChildren(listEntity []*blog_comment.ListEntity) ([]*blog_comment.ListEntity, error) { + return blog_comment.GetChildren(listEntity) +} diff --git a/plugin/blog/service/blog_service/log.go b/plugin/blog/service/blog_service/log.go new file mode 100644 index 0000000..08a8b4c --- /dev/null +++ b/plugin/blog/service/blog_service/log.go @@ -0,0 +1,45 @@ +package blog_service + +import ( + "gfast/plugin/blog/model/blog_log" +) + +// 添加 +func AddLogSave(req *blog_log.AddReq) error { + return blog_log.AddSave(req) +} + +// 删除 +func DeleteLogByIDs(Ids []int) error { + return blog_log.DeleteByIDs(Ids) +} + +//修改 +func EditLogSave(editReq *blog_log.EditReq) error { + return blog_log.EditSave(editReq) +} + +// 根据ID查询 +func GetLogByID(id int64) (*blog_log.ListEntity, error) { + return blog_log.GetLogByID(id) +} + +// 分页查询日志 +func SelectLogListByPage(req *blog_log.SelectPageReq) (total int, page int64, list []*blog_log.ListEntity, err error) { + return blog_log.SelectListByPage(req) +} + +// 按时间倒序查询size篇标志为sign分类id为typeId,状态为status的文章,标志值0.一般,1.置顶,2.幻灯,3.推荐,typeId等于0时不区分分类 +func FindSizeArticleBySign(size int, status int, sign int, typeId int) ([]*blog_log.ListEntity, error) { + return blog_log.FindSizeArticleBySign(size, status, sign, typeId) +} + +// 按时间倒序查询size篇分类id为typeId,状态为status的文章,typeId等于0时不区分分类 +func FindSizeArticle(size int, status int, typeId int) (list []*blog_log.ListEntity, err error) { + return blog_log.FindSizeArticle(size, status, typeId) +} + +// 查询size篇文章并根据点击数排序 +func FindArticleByHits(size int, status int) ([]*blog_log.Entity, error) { + return blog_log.FindArticleByHits(size, status) +} diff --git a/public/resource/plugin/blog/css/base.css b/public/resource/plugin/blog/css/base.css new file mode 100644 index 0000000..f92e9e3 --- /dev/null +++ b/public/resource/plugin/blog/css/base.css @@ -0,0 +1,1320 @@ +@charset "utf-8"; +/* CSS Document */ +* { + margin: 0; + padding: 0 +} + +img { + border: 0; + display: block +} +/*去除图片默认的1px边框;将图片标签变成块级元素,可清除默认的3px距离*/ + +ul li { + list-style: none; +} +/*去除ul li标签前面小点*/ + +a { + color: #222; + text-decoration: none; +} +/* 所有a 标签的 颜色为#222; 去除默认的下划线*/ + +a:hover { + color: #e01109; +} + +h1 { + font-size: 28px +} + +h2 { + font-size: 18px +} + +h3, h4, h5, h6 { + font-size: 16px +} + +body { + font: 15px "Microsoft YaHei", Arial, Helvetica, sans-serif; + color: #222; + background: #f5f5f5 +} + +/*设置 文字大小、字体、颜色、背景*/ +header, main { + width: 1200px; + margin: auto +} + +article { + width: 860px; + float: left; +} + +aside { + width: 320px; + float: right; +} + +footer { + width: 100%; + padding-bottom: 20px; + text-align: center; + clear: both +} + +.nav { + /*float: left;*/ + display: table; + width: 100%; + height: 105px; + margin: 20px 0; + box-shadow: rgba(0, 0, 0, .1) 3px 5px 5px; +} + +.nav a img { + width: 90px; + height: 90px; + float: left; + /*background: #fff;*/ + /*display: block;*/ + /*margin-right: 20px;*/ + /*border-radius: 100%;*/ +} + +.nav-list1 { + float: right; +} + +.submenu { + float: left; + margin: 0px 20px 0px 0px; + font-size: 20px; + width: 86px; + height: 37px; + text-align: center; +} + +.logo-name { + margin-top: 20px; +} + +.describe { + margin-top: 10px; + float: left; +} + +.submenu:hover .nav-submenu { + display: block; +} + +.nav-submenu { + width: 125px; + display: none; + border: 1px solid #c7c3c3; + text-align: left; + margin: 10px 0 0 0; + background: #f2f2f4; + font-size: 17px; + position: relative; + z-index: 10; + box-shadow: rgba(0, 0, 0, 0.2) 3px 5px 5px; +} + +.nav-submenu div { + background: #f2f2f4; + border-left: 1px solid #c7c3c3; + border-top: 1px solid #c7c3c3; + left: 38px; + transform: rotate(45deg); + top: -6px; + width: 8px; + height: 8px; + position: absolute; +} + +.nav-submenu li { + margin: 7px 0px; + padding-left: 15px; +} + +.nav-submenu li:hover { + background: #dedfe1; +} + +.nav-submenu li a:hover { + color: black; +} + +/*blogs*/ +.blogs { +} + +.blogs li { + overflow: hidden; + margin-bottom: 20px; + background: #fff; + padding: 20px; + position: relative; +} + +.top_blog .blogs li::after { + position: absolute; + content: ""; + right: 10px; + top: 40px; + border-style: solid; + border-width: 0 13px 13px 13px; +} + +.top_blog .blogs li::after { + border-color: #e01109 #e01109 transparent #e01109; +} + +.top_blog .blogs li::before { + background: #e01109; + position: absolute; + content: ""; + right: 10px; + top: 0; + width: 26px; + height: 30px; + text-align: center; + font-size: 16px; + font-weight: bold; + color: #fff; + padding: 10px 0 0 0; +} + +.top_blog .blogs li:nth-child(1)::before { + content: "1" +} + +.top_blog .blogs li:nth-child(2)::before { + content: "2" +} + +.top_blog .blogs li:nth-child(3)::before { + content: "3" +} + +.blogs li i { + display: block; + width: 160px; + height: 110px; + overflow: hidden; + float: left; + margin-right: 20px; +} + +.blogs li i img { + width: 100%; + min-height: 100%; + transition: .5s; +} + +.blogs h2 { + margin: 0 0 14px 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.blog_smalltext { + overflow: hidden; + -webkit-box-orient: vertical; + display: -webkit-box; + -webkit-line-clamp: 2; + font-size: 14px; + color: #666; + margin-bottom: 14px; +} + +.blog_info { + color: #888; + font-size: 13px; +} + +.blog_info span { + margin-right: 15px; + position: relative; + padding-left: 20px; + line-height: 20px; +} + +.blog_info span a { + color: #518f97 +} + +.blog_info span a:hover { + color: #e01109; +} + +.blog_info span:nth-child(n):before { + position: absolute; + content: ""; + width: 20px; + height: 20px; + left: 0; + top: 0; +} + +.blog_info span:nth-child(1) { + background: url(../image/icon.png) no-repeat 0 0; + background-size: 18px +} + +.blog_info span:nth-child(2) { + background: url(../image/icon.png) no-repeat 0 -18px; + background-size: 18px +} + +.blog_info span:nth-child(3) { + background: url(../image/icon.png) no-repeat 0 -36px; + background-size: 18px +} + +.blog_info span:nth-child(4) { + background: url(../image/icon.png) no-repeat 0 -54px; + background-size: 18px +} + +.blog_info span:nth-child(5) { + background: url(../image/icon.png) no-repeat 0 -72px; + background-size: 18px +} + +.blogs li:hover { + box-shadow: rgba(0, 0, 0, .1) 3px 5px 5px; +} + +.blogs li:hover h2 { + color: #F00 +} + +.blogs li:hover img { + transform: scale(1.1) +} + +/*aside*/ +aside div { + background: #fff; + margin-bottom: 20px +} + +.search { + background: #ed4040; + position: relative; + border: #ed4040 2px solid; + border-radius: 5px; + overflow: hidden; +} + +.search input.input_submit { + border: 0; + color: #fff; + outline: none; + position: absolute; + top: 0; + right: 0; + width: 25%; + display: block; + font-size: 15px; + height: 36px; + line-height: 36px; + text-indent: 1em; + cursor: pointer; + background: url(../image/search.png) no-repeat left 10px center; + background-size: 21px; +} + +.search input.input_text { + border: 0; + line-height: 36px; + height: 36px; + font-size: 14px; + width: 75%; + outline: none; + text-indent: 1em; +} + +h2.aside_title { + padding: 20px; +} + +.paihang { +} + +.paihang ol { + padding: 0 20px 20px 40px; +} + +.paihang ol li { + margin-bottom: 20px; + padding-left: 10px; +} + +.coffee { + padding: 0px 20px 20px 20px; + width: 280px; +} + +.recommend { + padding: 0px 20px 20px 20px; + height: 150px; + width: 280px; +} + +.links span { + float: right; + font-size: 16px; + font-weight: normal +} + +.links span a { + color: #666; +} + +.links span a:hover { + color: #f00 +} + +.links ul { + padding: 0 20px 20px 20px; + overflow: hidden +} + +.links li { + width: 50%; + float: left; + display: inline-block; + line-height: 30px; + text-align: center +} + +.links li a { + margin: 5px; + border: #666 1px solid; + display: block; + border-radius: 3px; + color: #666 +} + +.links li a:hover { + border: #222 1px solid; + color: #000 +} + +/*footer*/ +footer img { + display: inline-block; + vertical-align: middle; + margin-right: 5px; +} + +footer p { + line-height: 30px; + font-size: 14px; + color: #666 +} + +footer p span { + margin: 0 10px +} + +footer a { + color: #666 +} + +/*weizhi*/ +.weizhi { + margin: 0 0 20px 0; + font-size: 14px; + color: #949494; + background: url(../image/weizhi.png) no-repeat left center; + background-size: 20px; + padding-left: 30px +} + +.weizhi a { + color: #949494; +} + +.weizhi a:hover { + color: #000 +} + +/*suiyan*/ +.suiyan_list { +} + +.suiyan_list ul { + overflow: hidden; +} + +.suiyan_list ul li { + width: 25%; + float: left; +} + +.suiyan_list ul li a { + display: block; + padding: 20px; + margin: 30px auto; + background: #fff; + box-shadow: rgba(0, 0, 0, .1) 3px 5px 5px; + width: 200px; + height: 200px; + transform: rotate(-10deg); + position: relative; + color: #333 +} + +.suiyan_list ul li:nth-child(even) a { + -webkit-transform: rotate(10deg); + top: 10px; + background: rgb(255, 255, 205);; +} + +.suiyan_list ul li:nth-child(3n) a { + -webkit-transform: rotate(-5deg); + top: -10px; + background: rgb(254, 201, 227); +} + +.suiyan_list ul li:nth-child(5n) a { + -webkit-transform: rotate(8deg); + top: -10px; + background: rgb(210, 251, 253); +} + +.suiyan_list ul li a:hover { + transform: scale(1.1); + transition: .2s; + box-shadow: rgba(0, 0, 0, .5) 3px 5px 5px; +} + +.suiyan_time { + font-size: 14px; + color: #999; + margin-bottom: 10px; +} + +.suiyan_text { + line-height: 30px; +} + +.suiyan_list span { + position: absolute; + width: 50px; + height: 50px; + bottom: 5px; + right: 5px; +} + +/*tuijian*/ +.tuijian { +} + +.tuijian ul { + padding: 0 20px 20px 20px; +} + +.tuijian ul li { + overflow: hidden; + margin-bottom: 20px; +} + +.tuijian ul li a { +} + +.tuijian i { + display: block; + width: 90px; + height: 60px; + overflow: hidden; + margin-right: 20px; + float: left; +} + +.tuijian p { + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; + overflow: hidden; + margin-top: 8px +} + +.tuijian img { + width: 100%; + min-height: 100%; +} + +/*love*/ +.love { +} + +.love ul { + padding: 0 20px 20px 20px; + overflow: hidden +} + +.love ul li { + overflow: hidden; + width: 50%; + float: left; +} + +.love ul li a { + margin: 0 3px 10px; + display: block; + overflow: hidden; +} + +.love i { + display: block; + width: 100%; + height: 90px; + overflow: hidden; +} + +.love p { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + font-size: 14px; +} + +.love img { + width: 100%; + min-height: 100%; +} + +/*tagsclous*/ +.tagsclous h2 { + padding-bottom: 0 !important +} + +.tagsclous ul { + padding: 20px; + overflow: hidden; + position: relative; +} + +.tagsclous ul:before { + animation: linescroll 5s; + animation-iteration-count: infinite; + content: ""; + width: 20px; + height: 20px; + background: red; + position: absolute; + z-index: 9 +} + +.tagsclous a { + display: inline-block; + background: #ffffff; + width: 33.3333%; + font-size: 14px; + float: left; + line-height: 40px; + text-align: center; + position: relative; +} + +.tagsclous a:nth-child(3n-1):before { + width: 1px; + height: 100%; + content: ""; + position: absolute; + background: #eae5e5; + left: 0; + top: 0; +} + +.tagsclous a:nth-child(3n-1):after { + width: 1px; + height: 100%; + content: ""; + position: absolute; + background: #eae5e5; + right: 0; + top: 0; +} + +.tagsclous a:nth-child(6n+1) { + background: #f5f5f5; +} + +.tagsclous a:nth-child(6n+2) { + background: #f5f5f5; +} + +.tagsclous a:nth-child(6n+3) { + background: #f5f5f5; +} + +.tagsclous a:hover { + background: #e01109; + color: #fff +} + +@keyframes linescroll { + 0% { + left: 0px; + top: 0px; + } + 25% { + left: 300px; + top: 0px; + } + 50% { + left: 300px; + top: 220px; + } + 75% { + left: 0px; + top: 220px; + } + 100% { + left: 0px; + top: 0px; + } +} + +/*xiangce*/ +.xiangce_list ul { + width: 25%; + float: left; + overflow: hidden; +} + +.xiangce_list li { + margin: 10px +} + +.xiangce_list li a { + margin: 0 0 30px 0; + padding: 12px; + background: white; + border-radius: 3px; + box-shadow: 0px 1px 2px 0px rgba(0, 0, 0, 0.05); + display: block; +} + +.xiangce_list li img { + width: 100%; + margin: 0 auto 10px +} + +.xiangce_list li p { + color: #666; + text-align: center +} + +.xiangce_list li a:hover { + box-shadow: 0px 2px 3px 1px rgba(0, 0, 0, 0.1); + transform: translateY(-5px); + transition: all .2s; +} + +.time_list { + background: #fff; + padding: 20px; +} + +.time_list li { + line-height: 36px; +} + +.time_list li a { + display: block; + position: relative; +} + +.time_list li span { + margin: 0 40px 0 0; + float: left; + color: #999; +} + +.time_list ul { + position: relative; +} + +.time_list ul:before { + position: absolute; + content: ""; + width: 2px; + height: 100%; + background: #ccc; + left: 102px; + top: 0; +} + +.time_list li a:before { + position: absolute; + content: ""; + width: 12px; + height: 12px; + background: #fff; + border: #ccc 2px solid; + border-radius: 100%; + left: 95px; + top: 10px; +} + +.time_list li a:hover:before { + background: #f00; +} + +.about { + background: #fff; + padding: 20px; + font-size: 16px; + line-height: 24px; +} + +.about p { + margin: 10px 0; +} + +.about h2 { + line-height: 40px; + background: #f2f2f2; + border-left: #000 4px solid; + padding-left: 10px; + margin: 10px 0; +} + +.about img { + width: 80% !important; + height: auto !important; + margin: auto; +} + +.aside_right { + padding: 20px; +} + +.aside_right h2 { + margin: 0 0 10px 0; +} + +.aside_right ul { + overflow: hidden; + padding: 0 0 20px 0; +} + +.aside_right ul li { + line-height: 30px; + background: #f2f2f2; + margin-bottom: 10px; + text-indent: 1em; + border-radius: 10px; +} + +.aside_right ol { + line-height: 30px; + margin-left: 30px; +} + +.container { + background: #fff; + padding: 20px; +} + +.container h1 { + font-size: 24px; + margin-bottom: 20px; +} + +.content { + margin-top: 15px; + font-size: 16px; + line-height: 24px; +} + +.content p { + margin: 20px 0; +} + +.content img { + margin: auto; + max-width: 100% !important; + height: auto !important; +} + +.otherlink { + margin: 20px 0; + line-height: 26px; +} + +.otherlink h2, .pinglun_box h2 { + margin-bottom: 10px; + position: relative; +} + +.otherlink h2:before, .pinglun_box h2:before { + position: absolute; + content: ""; + width: 4px; + height: 100%; + background: #f00; + left: -20px; +} + + +.relevant_article { + width: 820px; + height: 145px; +} + +.relevant_article div { + float: left; + margin-right: 5px; + width: 200px; + height: 135px; +} + +.otherlink img { + width: 200px; + height: 135px; +} + +@media only screen and (max-width: 1200px) { + header, main { + width: 90%; + } + + article { + width: 68%; + } + + aside { + width: 30%; + } +} + +@media only screen and (max-width: 768px) { + article { + width: 100%; + } + + .touxiang { + width: 60%; + } + + aside { + display: none; + } + + nav ul li a { + padding: 0 15px; + } + + img.weixin { + z-index: 999; + } + + .suiyan_list ul { + padding: 30px; + } + + .suiyan_list ul li { + width: 33.33333%; + } + + .suiyan_list ul li a { + padding: 10px; + width: 180px; + } + + .suiyan_text { + font-size: 14px; + } + + .suiyan_list ul li a:hover { + z-index: 9; + } + + .xiangce_list li p { + font-size: 14px; + } + + .xiangce_list li a { + padding: 5px; + } + + .xiangce_list li { + margin: 5px; + } +} + +@media only screen and (max-width: 480px) { + header, main { + width: 96%; + } + + .touxiang { + width: 100%; + margin: 5px 0; + } + + .touxiang i { + width: 60px; + height: 60px; + margin-right: 10px; + } + + .touxiang i img { + width: 100%; + } + + .touxiang h2 { + margin: 5px 0; + } + + .guanzhu { + position: fixed; + z-index: 9; + bottom: 0; + top: inherit; + width: 100%; + } + + .guanzhu:hover .weixin { + bottom: 30px; + left: 33%; + box-shadow: #b8b1b1 2px 2px 10px; + } + + footer { + padding-bottom: 50px; + margin-top: 20px + } + + nav { + overflow-x: scroll; + height: 40px; + line-height: 40px; + } + + nav ul { + width: max-content; + overflow-x: scroll; + } + + .blogs li { + padding: 10px; + } + + .blogs h2 { + font-size: 16px; + margin: 0 0 5px 0; + } + + .blogs li i { + width: 90px; + height: 60px; + margin-right: 10px; + } + + .top_blog .blogs li::after { + display: none; + } + + .top_blog .blogs li::before { + display: none; + } + + .blog_info span:last-child { + display: none; + } + + .suiyan_list ul li { + width: 50%; + } + + .xiangce_list ul { + width: 100% + } + + .xiangce_list li a { + padding: 12px; + } + + .time_list li span { + width: 100%; + } + + .time_list ul:before { + left: 0; + } + + .time_list li a:before { + left: -8px; + } + + .time_list li a { + padding-left: 20px; + } + + .time_list li span { + padding-left: 20px; + } + + .about img { + width: 100% !important; + } + + .container h1 { + font-size: 20px; + } + + .info-pre-next p, .otherlink li { + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + } + + footer p span:last-child { + display: block; + } + + main { + overflow: hidden; + } +} + +.swiper-container { + width: 860px; + height: 350px; + margin-bottom: 20px; + float: left; +} + +.swiper-wrapper img { + width: 860px; + height: 350px; +} + +.swiper-container { + --swiper-theme-color: #e5e4e4; /* 设置Swiper风格 */ + --swiper-navigation-color: #8B8EAD; /* 单独设置按钮颜色 */ + --swiper-navigation-size: 30px; /* 设置按钮大小 */ +} + +/*留言*/ +.guest_tit { + height: 70px; + line-height: 50px; +} + +.guest_tit input { + border-radius: 5px; + height: 40px; + border: 1px solid #dddddd; + font-size: 17px; + font-family: 微软雅黑; + width: 200px; + float: left; + padding: 0 10px; +} + +.guest_tit label { + width: 100px; + line-height: 40px; + display: block; + float: left; + font-size: 18px; +} + +.guest_txt { + height: 187px; +} + +.guest_txt textarea { + border-radius: 5px; + width: 80%; + font-size: 17px; + font-family: 微软雅黑; + height: 150px; + padding: 5px 10px; + border: 1px solid #dddddd; +} + +.guest_s { + line-height: 50px; + width: 450px; +} + +.btn { + width: 20%; + border-radius: 5px; + background-color: #e01109; + display: inline-block; + color: #fff; + border: none; + line-height: 47px; + font-size: 20px; + cursor: pointer; + padding: 0 10px; + margin-right: 15px; +} + +/* 分页样式 */ +.paging { + margin: 15px; + text-align: center; + font-size: 15px; +} + +.paging a, .paging span { + margin: 0 1px; + border: solid 1px #ccc; + padding: 5px; +} + +.GPageSpan { + color: red; +} + +/* 没有评论的样式 */ +.no_comment { + height: 100px; + color: #7d8293; + padding-top: 6px; +} + +/* 评论样式 */ +.comment { + margin-top: 10px; + border-bottom: 1px solid #dee2e6; +} + +.comment_nickname { + float: left; + font-size: 15px; + font-weight: 700; + line-height: 18px; + padding-bottom: 4px; +} + +.comment_create_time { + color: #99a2aa; + line-height: 18px; + font-size: 13px; +} + +.comment_content { + padding-left: 5px; + margin-top: 10px; + color: #666; +} + +.comment_reply { + color: #99a2aa; + font-size: 14px; + margin: 7px 0px; +} + +.comment_reply span { + padding: 5px; + border-radius: 4px; + display: inline-block; +} + +.comment_reply span:hover { + color: #508af5; + background: #dcdee3; + border-radius: 4px; + cursor: pointer; + display: inline-block; +} + +/* 友情链接 */ +.link_content { + width: 860px; + float: left; +} + +.link_content h2 { + margin: 0 auto; + width: 100px; +} + +.link_content div { + margin: 30px auto; + width: 470px; +} + +.link_content div input { + height: 35px; + width: 438px; + padding: 2px 15px; + font-size: 16px; + border-radius: 4px; + border: 1px solid #c8d0d0; +} + +.link_content div textarea { + width: 438px; + padding: 10px 15px; + font-size: 16px; + font-family: 微软雅黑; + border-radius: 4px; + border: 1px solid #c8d0d0; +} + +::-webkit-input-placeholder { + color: #d1d3da; +} + +.comment_button { + border: none; + background: #e01109; + color: #fff; + border-radius: 5px; + width: 100%; + cursor: pointer; + line-height: 50px; + font-size: 20px; +} + +.reply_box { + margin-left: 25px; +} + +.reply_id { + display: none; +} + +.reply_item { + display: none; +} + +.view_more { + color: #99a2aa; + font-size: 14px; + margin-left: 29px; +} + +.view_more span { + padding: 5px; + border-radius: 4px; + display: inline-block; +} + +.view_more span:hover { + color: #508af5; + background: #dcdee3; + border-radius: 4px; + cursor: pointer; + display: inline-block; +} \ No newline at end of file diff --git a/public/resource/plugin/blog/image/001.jpg b/public/resource/plugin/blog/image/001.jpg new file mode 100644 index 0000000..d1ac5a2 Binary files /dev/null and b/public/resource/plugin/blog/image/001.jpg differ diff --git a/public/resource/plugin/blog/image/002.png b/public/resource/plugin/blog/image/002.png new file mode 100644 index 0000000..2b7e8bb Binary files /dev/null and b/public/resource/plugin/blog/image/002.png differ diff --git a/public/resource/plugin/blog/image/003.jpg b/public/resource/plugin/blog/image/003.jpg new file mode 100644 index 0000000..03d26e0 Binary files /dev/null and b/public/resource/plugin/blog/image/003.jpg differ diff --git a/public/resource/plugin/blog/image/004.jpg b/public/resource/plugin/blog/image/004.jpg new file mode 100644 index 0000000..c96f9ba Binary files /dev/null and b/public/resource/plugin/blog/image/004.jpg differ diff --git a/public/resource/plugin/blog/image/005.jpg b/public/resource/plugin/blog/image/005.jpg new file mode 100644 index 0000000..6f317b9 Binary files /dev/null and b/public/resource/plugin/blog/image/005.jpg differ diff --git a/public/resource/plugin/blog/image/006.jpg b/public/resource/plugin/blog/image/006.jpg new file mode 100644 index 0000000..9c7d513 Binary files /dev/null and b/public/resource/plugin/blog/image/006.jpg differ diff --git a/public/resource/plugin/blog/image/bq1.png b/public/resource/plugin/blog/image/bq1.png new file mode 100644 index 0000000..66ee2c1 Binary files /dev/null and b/public/resource/plugin/blog/image/bq1.png differ diff --git a/public/resource/plugin/blog/image/bq2.png b/public/resource/plugin/blog/image/bq2.png new file mode 100644 index 0000000..de4c2bb Binary files /dev/null and b/public/resource/plugin/blog/image/bq2.png differ diff --git a/public/resource/plugin/blog/image/bq3.png b/public/resource/plugin/blog/image/bq3.png new file mode 100644 index 0000000..20e7527 Binary files /dev/null and b/public/resource/plugin/blog/image/bq3.png differ diff --git a/public/resource/plugin/blog/image/bq4.png b/public/resource/plugin/blog/image/bq4.png new file mode 100644 index 0000000..db35e2d Binary files /dev/null and b/public/resource/plugin/blog/image/bq4.png differ diff --git a/public/resource/plugin/blog/image/bq5.png b/public/resource/plugin/blog/image/bq5.png new file mode 100644 index 0000000..2994698 Binary files /dev/null and b/public/resource/plugin/blog/image/bq5.png differ diff --git a/public/resource/plugin/blog/image/ga.png b/public/resource/plugin/blog/image/ga.png new file mode 100644 index 0000000..41870d1 Binary files /dev/null and b/public/resource/plugin/blog/image/ga.png differ diff --git a/public/resource/plugin/blog/image/gfastLogo.png b/public/resource/plugin/blog/image/gfastLogo.png new file mode 100644 index 0000000..2c10f1f Binary files /dev/null and b/public/resource/plugin/blog/image/gfastLogo.png differ diff --git a/public/resource/plugin/blog/image/icon.png b/public/resource/plugin/blog/image/icon.png new file mode 100644 index 0000000..49bb327 Binary files /dev/null and b/public/resource/plugin/blog/image/icon.png differ diff --git a/public/resource/plugin/blog/image/search.png b/public/resource/plugin/blog/image/search.png new file mode 100644 index 0000000..6b16a86 Binary files /dev/null and b/public/resource/plugin/blog/image/search.png differ diff --git a/public/resource/plugin/blog/image/weizhi.png b/public/resource/plugin/blog/image/weizhi.png new file mode 100644 index 0000000..5dd6306 Binary files /dev/null and b/public/resource/plugin/blog/image/weizhi.png differ diff --git a/public/resource/plugin/blog/image/wx.png b/public/resource/plugin/blog/image/wx.png new file mode 100644 index 0000000..6b0a522 Binary files /dev/null and b/public/resource/plugin/blog/image/wx.png differ diff --git a/public/resource/plugin/blog/image/yangqq.png b/public/resource/plugin/blog/image/yangqq.png new file mode 100644 index 0000000..1a03daa Binary files /dev/null and b/public/resource/plugin/blog/image/yangqq.png differ diff --git a/public/resource/plugin/blog/js/nav.js b/public/resource/plugin/blog/js/nav.js new file mode 100644 index 0000000..a58e1ab --- /dev/null +++ b/public/resource/plugin/blog/js/nav.js @@ -0,0 +1,11 @@ +// JavaScript Document + +$(document).ready(function(){ + var myNav = $("nav a"),i; + for(i=0;i + + +
+

点击排行

+
    + ${range $index, $article := .hitsList} +
  1. ${$article.LogTitle}
  2. + ${end} +
+
+
+

推荐文章

+ ${range $index,$article := .recommendList} + 推荐文章 + ${end} +
+
+

最新文章

+
    + ${range $index, $article := .newList} +
  1. ${$article.LogTitle}
  2. + ${end} +
+
+ +
+

请喝咖啡

+ ${range $index,$article := .coffeeArticle} + 请喝咖啡 + ${end} +
+ +${end} \ No newline at end of file diff --git a/template/plugin/blog/blogList.html b/template/plugin/blog/blogList.html new file mode 100644 index 0000000..b0d8a68 --- /dev/null +++ b/template/plugin/blog/blogList.html @@ -0,0 +1,52 @@ + + + + + gfast博客 + + + + + + +${template "header" .} + +
+ ${if ne .classification nil} +

您现在的位置是:网站首页 > ${.classification.ClassificationName}

+ ${else} +

您现在的位置是:网站首页 > 搜索

+ ${end} +
+ +
+
    + ${range $index,$article := .list} +
  • + + 标题图片 +

    ${$article.LogTitle}

    +
    +
    ${subStr $article.LogContent 23}
    +

    + ${timeFormatYear $article.CreatTime} + ${$article.LogAuthor} + ${$article.ClassificationName} + ${$article.LogHits} +

    +
  • + ${end} +
+
+ + +
+ + + ${template "aside" .} + +
+${template "footer" .} + + + diff --git a/template/plugin/blog/content.html b/template/plugin/blog/content.html new file mode 100644 index 0000000..50a31c3 --- /dev/null +++ b/template/plugin/blog/content.html @@ -0,0 +1,193 @@ + + + + + gfast博客 + + + + + + + +${template "header" .} +
+ ${if and (ne .classification nil) (eq .classification.ClassificationType 4)} +

您现在的位置是:网站首页 > ${.classification.ClassificationName}

+
+
+

${.classification.ClassificationName}

+
+ ${.classification.ClassificationContent} +
+
+
+ ${else} +

您现在的位置是:网站首页 > ${.log.ClassificationName} > ${.log.LogTitle}

+
+
+

${.log.LogTitle}

+

+ ${timeFormatYear .log.CreatTime} + ${.log.LogAuthor} + ${.log.ClassificationName} + ${.log.LogHits} +

+
+ ${.log.LogContent} +
+ +
+
+

文章评论

+ ${range $index,$comment := .commentList} +
+
${$comment.CommentNickname}
+
+ (${timeFormatYear $comment.CreateTime}) +
+
+ ${$comment.CommentContent} +
+
${$comment.CommentId}
+
回复
+ +
+ ${range $index2, $item := $comment.Children} + + ${if lt $index2 3} +
+
${$item.CommentNickname}
+
+ (${timeFormatYear $item.CreateTime}) +
+
+ @${$item.ReplyName}:${$item.CommentContent} +
+
${$item.CommentId}
+
回复
+
+ ${else} +
+
${$item.CommentNickname}
+
+ (${timeFormatYear $item.CreateTime}) +
+
+ @${$item.ReplyName}:${$item.CommentContent} +
+
${$item.CommentId}
+
回复
+
+ ${end} + ${end} +
+ ${if gt $comment.CommentNum 3} +
+ 共${$comment.CommentNum}条回复,点击查看 +
+ ${else} +
+ 共${$comment.CommentNum}条回复,点击查看 +
+ ${end} +
+ ${end} + ${if ne .commentList nil} +
+ ${$.pageStyle} +
+ ${else} +
还没有人发表评论
+ ${end} +
+
+

发表评论

+
+ + + + +
+ +
+
+ +
+
+ + +
+ +
+
+
+
+ ${end} + + + ${template "aside" .} + +
+${template "footer" .} + + diff --git a/template/plugin/blog/footer.html b/template/plugin/blog/footer.html new file mode 100644 index 0000000..8bb82f3 --- /dev/null +++ b/template/plugin/blog/footer.html @@ -0,0 +1,11 @@ +${define "footer"} + + +${end} \ No newline at end of file diff --git a/template/plugin/blog/header.html b/template/plugin/blog/header.html new file mode 100644 index 0000000..d3ecc43 --- /dev/null +++ b/template/plugin/blog/header.html @@ -0,0 +1,52 @@ +${define "header"} +
+ +
+${end} \ No newline at end of file diff --git a/template/plugin/blog/homePage.html b/template/plugin/blog/homePage.html new file mode 100644 index 0000000..d9c6635 --- /dev/null +++ b/template/plugin/blog/homePage.html @@ -0,0 +1,103 @@ + + + + + gfast博客 + + + + + + + + +${template "header" .} + +
+
+ +
+
+ ${range $index,$article := .slideList} +
+ ${end} +
+ +
+ +
+
+
+ +
+
    + ${range $index,$article := .topList} +
  • + + 标题图片 +

    ${$article.LogTitle}

    +
    +
    ${subStr $article.LogContent 23}
    +

    + ${timeFormatYear $article.CreatTime} + ${$article.LogAuthor} + ${$article.ClassificationName} + ${$article.LogHits} +

    +
  • + ${end} +
+
+ + + +
+
    + ${range $index,$article := .logList} + ${if ne $article.LogSign 1} +
  • + + 标题图片 +

    ${$article.LogTitle}

    +
    +
    ${subStr $article.LogContent 23}
    +

    + ${timeFormatYear $article.CreatTime} + ${$article.LogAuthor} + ${$article.ClassificationName} + ${$article.LogHits} +

    +
  • + ${end} + ${end} +
+
+ + +
+ + + ${template "aside" .} + +
+${template "footer" .} + + + + diff --git a/template/plugin/blog/link.html b/template/plugin/blog/link.html new file mode 100644 index 0000000..c89bfe9 --- /dev/null +++ b/template/plugin/blog/link.html @@ -0,0 +1,59 @@ + + + + + gfast博客 + + + + + + + + + +${template "header" .} + +
+ + ${template "aside" .} + +
+${template "footer" .} + +