From 735534c6e681fe8335b272c486353cba47c11775 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E6=9D=8E=E5=BF=97=E5=BC=BA?= <357099073@qq.com>
Date: Thu, 20 Aug 2026 16:49:06 +0800
Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=A0=87=E7=AD=BE?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
go/pkg/tagengine/meta.go | 36 +++++++++
go/pkg/tagengine/provider.go | 143 +++++++++++++++++++++++++++++++++++
2 files changed, 179 insertions(+)
diff --git a/go/pkg/tagengine/meta.go b/go/pkg/tagengine/meta.go
index 1e000fd..340a684 100644
--- a/go/pkg/tagengine/meta.go
+++ b/go/pkg/tagengine/meta.go
@@ -115,6 +115,42 @@ func TagDocs() []TagMeta {
Fields: []string{"id 文章ID", "title 标题(受 titlelen 影响)", "titlefull 完整标题", "desc 摘要", "image 缩略图", "arcurl 详情页链接(/news/ID)", "pubdate 发布日期", "views 阅读量", "author 作者"},
Example: "{yz:newscenter row=\"8\" titlelen=\"28\"}\n
\n{/yz:newscenter}",
},
+ {
+ Name: "productlist 产品列表",
+ Category: "loop",
+ Syntax: "{yz:productlist row=\"8\" titlelen=\"20\"}...{/yz:productlist}",
+ Desc: "循环输出当前租户启用中的产品(backend 端 - 内容管理 - 产品管理),按 sort 升序。数据来自 yz_cms_product 表。",
+ Params: []string{"row 输出条数,默认 10,最大 50", "titlelen 标题截断字数,不填为完整标题"},
+ Fields: []string{"id 产品ID", "title 标题(受 titlelen 影响)", "titlefull 完整标题", "desc 产品简介", "thumb 产品图片地址", "content 富文本详情(HTML,用 div 承接)", "url 跳转地址(产品管理中填写的链接,未填为空)"},
+ Example: "{yz:productlist row=\"8\" titlelen=\"16\"}\n\n{/yz:productlist}",
+ },
+ {
+ Name: "producttype 产品分类",
+ Category: "loop",
+ Syntax: "{yz:producttype row=\"10\"}...{/yz:producttype}",
+ Desc: "循环输出当前租户启用中的产品分类(backend 端 - 内容管理 - 产品分类),按 sort 升序。数据来自 yz_cms_product_category 表,常用于产品页的分类 Tab/筛选栏。",
+ Params: []string{"row 输出条数,默认 20,最大 50"},
+ Fields: []string{"id 分类ID", "name 分类名称", "desc 分类描述", "pid 父分类ID(0 为顶级)", "url 锚点链接(#product-type-ID,可自定义)"},
+ Example: "{yz:producttype}\n[field:name/]\n{/yz:producttype}",
+ },
+ {
+ Name: "solutionlist 解决方案列表",
+ Category: "loop",
+ Syntax: "{yz:solutionlist row=\"6\" titlelen=\"20\"}...{/yz:solutionlist}",
+ Desc: "循环输出当前租户启用中的解决方案(backend 端 - 内容管理 - 解决方案管理),按 sort 升序。数据来自 yz_cms_solution 表。",
+ Params: []string{"row 输出条数,默认 10,最大 50", "titlelen 标题截断字数,不填为完整标题"},
+ Fields: []string{"id 方案ID", "title 标题(受 titlelen 影响)", "titlefull 完整标题", "desc 方案简介", "thumb 图片地址", "url 跳转地址(解决方案管理中填写的链接,未填为空)"},
+ Example: "{yz:solutionlist row=\"6\"}\n\n{/yz:solutionlist}",
+ },
+ {
+ Name: "solutiontype 解决方案分类",
+ Category: "loop",
+ Syntax: "{yz:solutiontype row=\"10\"}...{/yz:solutiontype}",
+ Desc: "循环输出当前租户启用中的解决方案分类(backend 端 - 内容管理 - 解决方案分类),按 sort 升序。数据来自 yz_cms_solution_category 表。",
+ Params: []string{"row 输出条数,默认 20,最大 50"},
+ Fields: []string{"id 分类ID", "name 分类名称", "desc 分类描述", "pid 父分类ID(0 为顶级)", "url 锚点链接(#solution-type-ID,可自定义)"},
+ Example: "{yz:solutiontype}\n[field:name/]\n{/yz:solutiontype}",
+ },
{
Name: "arcview 文章详情",
Category: "single",
diff --git a/go/pkg/tagengine/provider.go b/go/pkg/tagengine/provider.go
index 745df72..6d31229 100644
--- a/go/pkg/tagengine/provider.go
+++ b/go/pkg/tagengine/provider.go
@@ -24,6 +24,10 @@ func init() {
RegisterBlock("arclist", arclistProvider)
RegisterBlock("arcview", arcviewProvider)
RegisterBlock("friendlink", friendlinkProvider)
+ RegisterBlock("productlist", productlistProvider)
+ RegisterBlock("producttype", producttypeProvider)
+ RegisterBlock("solutionlist", solutionlistProvider)
+ RegisterBlock("solutiontype", solutiontypeProvider)
RegisterBodyBlock("newscenter", newscenterHandler)
RegisterSelfClose("onepage", onepageHandler)
RegisterSelfClose("pagelist", pagelistHandler)
@@ -246,6 +250,145 @@ func friendlinkProvider(tid uint64, params map[string]string, ctx *RenderCtx) ([
return out, nil
}
+// productlistProvider 产品列表循环,支持 row/titlelen 参数
+func productlistProvider(tid uint64, params map[string]string, ctx *RenderCtx) ([]map[string]string, error) {
+ _ = models.EnsureCmsProductTables()
+ row := atoiDefault(params["row"], 10)
+ if row <= 0 || row > 50 {
+ row = 10
+ }
+ titleLen := atoiDefault(params["titlelen"], 0)
+
+ var rows []models.CmsProduct
+ _, err := models.Orm.QueryTable(new(models.CmsProduct)).
+ Filter("tid", tid).
+ Filter("status", 1).
+ Filter("delete_time__isnull", true).
+ OrderBy("sort", "id").
+ Limit(row).
+ All(&rows)
+ if err != nil && err != orm.ErrNoRows {
+ return nil, err
+ }
+ out := make([]map[string]string, 0, len(rows))
+ for _, p := range rows {
+ title := p.Title
+ if titleLen > 0 {
+ title = truncateTitle(title, titleLen)
+ }
+ out = append(out, map[string]string{
+ "id": strconv.FormatUint(p.ID, 10),
+ "title": title,
+ "titlefull": p.Title,
+ "desc": p.Desc,
+ "thumb": p.Thumb,
+ "content": p.Content,
+ "url": p.URL,
+ })
+ }
+ return out, nil
+}
+
+// producttypeProvider 产品分类循环,支持 row 参数
+func producttypeProvider(tid uint64, params map[string]string, ctx *RenderCtx) ([]map[string]string, error) {
+ _ = models.EnsureCmsProductTables()
+ row := atoiDefault(params["row"], 20)
+ if row <= 0 || row > 50 {
+ row = 20
+ }
+ var rows []models.CmsProductCategory
+ _, err := models.Orm.QueryTable(new(models.CmsProductCategory)).
+ Filter("tid", tid).
+ Filter("status", 1).
+ Filter("delete_time__isnull", true).
+ OrderBy("sort", "id").
+ Limit(row).
+ All(&rows)
+ if err != nil && err != orm.ErrNoRows {
+ return nil, err
+ }
+ out := make([]map[string]string, 0, len(rows))
+ for _, c := range rows {
+ out = append(out, map[string]string{
+ "id": strconv.FormatUint(c.ID, 10),
+ "name": c.Title,
+ "desc": c.Desc,
+ "pid": strconv.FormatUint(c.Pid, 10),
+ "url": "#product-type-" + strconv.FormatUint(c.ID, 10),
+ })
+ }
+ return out, nil
+}
+
+// solutionlistProvider 解决方案列表循环,支持 row/titlelen 参数
+func solutionlistProvider(tid uint64, params map[string]string, ctx *RenderCtx) ([]map[string]string, error) {
+ _ = models.EnsureCmsSolutionTables()
+ row := atoiDefault(params["row"], 10)
+ if row <= 0 || row > 50 {
+ row = 10
+ }
+ titleLen := atoiDefault(params["titlelen"], 0)
+
+ var rows []models.CmsSolution
+ _, err := models.Orm.QueryTable(new(models.CmsSolution)).
+ Filter("tid", tid).
+ Filter("status", 1).
+ Filter("delete_time__isnull", true).
+ OrderBy("sort", "id").
+ Limit(row).
+ All(&rows)
+ if err != nil && err != orm.ErrNoRows {
+ return nil, err
+ }
+ out := make([]map[string]string, 0, len(rows))
+ for _, s := range rows {
+ title := s.Title
+ if titleLen > 0 {
+ title = truncateTitle(title, titleLen)
+ }
+ out = append(out, map[string]string{
+ "id": strconv.FormatUint(s.ID, 10),
+ "title": title,
+ "titlefull": s.Title,
+ "desc": s.Desc,
+ "thumb": s.Thumb,
+ "url": s.URL,
+ })
+ }
+ return out, nil
+}
+
+// solutiontypeProvider 解决方案分类循环,支持 row 参数
+func solutiontypeProvider(tid uint64, params map[string]string, ctx *RenderCtx) ([]map[string]string, error) {
+ _ = models.EnsureCmsSolutionTables()
+ row := atoiDefault(params["row"], 20)
+ if row <= 0 || row > 50 {
+ row = 20
+ }
+ var rows []models.CmsSolutionCategory
+ _, err := models.Orm.QueryTable(new(models.CmsSolutionCategory)).
+ Filter("tid", tid).
+ Filter("status", 1).
+ Filter("delete_time__isnull", true).
+ OrderBy("sort", "id").
+ Limit(row).
+ All(&rows)
+ if err != nil && err != orm.ErrNoRows {
+ return nil, err
+ }
+ out := make([]map[string]string, 0, len(rows))
+ for _, c := range rows {
+ out = append(out, map[string]string{
+ "id": strconv.FormatUint(c.ID, 10),
+ "name": c.Title,
+ "desc": c.Desc,
+ "pid": strconv.FormatUint(c.Pid, 10),
+ "url": "#solution-type-" + strconv.FormatUint(c.ID, 10),
+ })
+ }
+ return out, nil
+}
+
// arclistProvider 文章列表循环,支持 row/cateid/titlelen/flag 参数
func arclistProvider(tid uint64, params map[string]string, ctx *RenderCtx) ([]map[string]string, error) {
row := atoiDefault(params["row"], 10)