# Blog主题教程

# Blog主题模块开发步骤

# 第一步、准备工作

# 1.1) 准备静态HTML模板

准备需要开发的静态资源模板文件,结构可参考如下

└─ template/                  → 模板根目录
     ├─ static/               → 静态资源
     │    ├─ css/
     │    │    ├─ style.css
     │    │    └─ ......
     │    ├─ js/
     │    │    ├─ script.js
     │    │    └─ ......
     │    └─ image/
     │         ├─ image.jpg
     │         └─ ......
     ├─ show.html             → 详情
     ├─ list.html             → 列表
     ├─ tags.html             → 标签
     ├─ about.html            → 关于
     ├─ message.html          → 留言
     └─ index.html            → 首页
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 1.2) 安装模块开发助手

模块开发助手可以极大效率的提高开发效率,在 系统管理 → 模块管理 安装 模块开发助手 (opens new window)

# 第二步、模块开发

# 2.1) 创建模块

访问 系统管理 → 模块开发助手,通过常用工具创建模块

image-20220628151833042

image-20220628151925106

image-20220628152106465

# 2.2) 安装启用主题模块

访问 系统管理 → 模块管理 → 本地模块,安装并启用模块。

# 2.3) 主题模块开发完成

以下教程以主题模块 BlogThemeMyTest 为例进行。

2.3.1) 复制静态资源

复制静态HTML模板中的所有静态资源文件 template/static/ 到主题模块静态资源 module/BlogThemeMyTest/Asset 中。

如,静态HTML模板结构为

└─ template/                        → 模板根目录
    └─ static/
        ├─ css
        │   └─ style.css
        ├─ js
        │   └─ script.js
        └─ image
            └─ image.jpg
1
2
3
4
5
6
7
8

复制到模块静态资源结构为

└─ module/BlogThemeMyTest/Asset/    → 模块静态资源结构
    └─ static/
        ├─ css
        │   └─ style.css
        ├─ js
        │   └─ script.js
        └─ image
            └─ image.jpg
1
2
3
4
5
6
7
8

2.3.2) 做静态资源软连

开发阶段为了模块开发方便,访问的静态资源使用软连链接到模块静态资源文件夹。先删除 public/vendor/BlogThemeMyTest/ 目录,然后执行以下命令完成软连创建。

php artisan modstart:module-link-asset BlogThemeMyTest

这样,访问路径 http://example.com/vendor/BlogThemeMyTest/css/style.css 路径会定向到文件 module/BlogThemeMyTest/Asset/css/style.css

2.3.3) 完成模板开发

复制以下页面到模块,并完成页面功能开发。

  • 列表list.htmlmodule/BlogThemeMyTest/View/pc/blog/list.blade.php
  • 详情show.htmlmodule/BlogThemeMyTest/View/pc/blog/show.blade.php
  • 表填tags.htmlmodule/BlogThemeMyTest/View/pc/blog/tags.blade.php
  • 关于about.htmlmodule/BlogThemeMyTest/View/pc/blog/about.blade.php
  • 留言message.htmlmodule/BlogThemeMyTest/View/pc/blog/message.blade.php
  • 首页index.htmlmodule/BlogThemeMyTest/View/pc/blog/index.blade.php

要面开发主要需要以下两个步骤:

① 完成页面所有静态资源的路径替换

将所有路径 path/to/static 替换为 @asset('vendor/BlogThemeMyTest/path/to/static')

script
<script src="static/js/script.js"></script>
替换为
<script src="@asset('vendor/BlogThemeMyTest/static/js/script.js')"></script>

css
<link rel="stylesheet" href="static/css/style.css">
替换为
<link rel="stylesheet" href="@asset('vendor/BlogThemeMyTest/static/css/style.css')">

图片
<img src="static/image/image.jpg" />
替换为
<img src="@asset('vendor/BlogThemeMyTest/static/image/image.jpg')" />
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

② 完成页面变量替换和内容渲染

该部分每个页面需要参考个页面可用变量完成开发和内容替换。

# 第三步、模块发布分享

# 3.1) 账户信息认证

  1. 注册ModStart账号:访问 https://modstart.com (opens new window) 完成账号注册
  2. 实名认证:在用户中心完成用户实名认证

# 3.2) 在平台创建信息模块

个人中心 → 开发者 → 开发者中心 创建模块,填写基础模块信息完成创建。

创建模块

# 3.3) 模块打包上传到模块市场

进入 系统管理 → 模块开发助手 ,使用相同的 ModStart 账号登录,对于拥有的模块操作栏会出现发布按钮,点击发布一键发布到模块市场。

模块打包上传

发布成功后,需要后台审核模块,完成审核后模块即可显示在模块市场。

# 模块文件介绍

# 目录结构

module/BlogThemeXxx
├─ Admin/                             → 后台管理
│    ├─ Controller/
│    │    └─ ConfigController.php     → 后台配置开发Controller
│    └─ routes.php                    → 路由
├─ Asset/                             → 模块资源文件,安装后会复制到 public/vendor/BlogThemeXxx
│    └─ ......
├─ Core/
│    ├─ ModuleServiceProvider.php     → 模块核心注册器
│    └─ ThemeSiteTemplateProvider.php → 主题注册器,用于注册当前模块为博客主题
├─ Docs/                              → 模块文档
│    ├─ module/
│    │    ├─ content.md               → 模块介绍,发布时自动更新到模块介绍
│    │    ├─ demo.md                  → 模块演示,发布时自动更新到模块演示
│    │    ├─ mobilePreview.md         → 模块手机预览图,每行一个支持多个,发布时自动更新
│    │    └─ preview.md               → 模块电脑端预览图,每行一个支持多个,发布时自动更新
│    └─ release.md                    → 模块发布日志,发布时自动更新到模块发布日志
├─ Lang/                              → 多语言
│    ├─ en.php                        → 英文语言包
│    └─ zh.php                        → 中文语言包
├─ View/                              → 模块视图主目录
│    └─ pc/                           → 电脑端视图
│         ├─ blog/                    → 博客视图
│         │    ├─ about.blade.php     → 关于
│         │    ├─ index.blade.php     → 首页
│         │    ├─ list.blade.php      → 列表
│         │    ├─ message.blade.php   → 留言
│         │    ├─ show.blade.php      → 详情
│         │    └─ tags.blade.php      → 标签
│         ├─ footer.blade.php         → 公共视图底部
│         ├─ frame.blade.php          → 公共视图框架
│         ├─ frameTheme.blade.php     → 主题视图框架
│         └─ header.blade.php         → 公共视图顶部
└─ config.json                        → 模块配置文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

# 博客链接

  • /blogs:博客列表
  • /blogs?keywords=xxx:博客关键词搜索
  • /blogs?keywords=xxx:博客标签搜索
  • /blog/{id}:博客详情
  • /blog/message:留言
  • /blog/archive:博客归档
  • /blog/about:关于

# 博客首页 blog.index

# 博客列表 $records


@foreach($records as $record)
    ID{{ $record['id'] }}
    标题:{{ $record['title'] }}
    时间:{{ $record['postTime'] }}
    摘要:{{ $record['summary'] }}
    图片:{{ join(',',$record['images']) }}
    标签:{{ join(',',$record['tag']) }}
    分类ID{{ $record['categoryId'] }}
    分类标题:{{ $record['_category']['title'] }}
    分类封面:{{ $record['_category']['cover'] }}
    点击数:{{ $record['clickCount'] }}
    评论数:{{ $record['commentCount'] }}
    喜欢数:{{ $record['likeCount'] }}
    收藏数:{{ $record['favCount'] }}
    置顶:{{ $record['isTop'] }}
    热门:{{ $record['isHot'] }}
    推荐:{{ $record['isRecommend'] }}
    日期:{{ $record['_date'] }}
    封面:{{ $record['_cover'] }}
    链接:{{ $record['_url'] }}
@endforeach
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
变量 说明
$records 博客记录数组
├ $record['id'] 博客ID
├ $record['title'] 博客标题
├ $record['postTime'] 博客创建时间
├ $record['summary'] 博客摘要
├ $record['images'] 博客图片数组
├ $record['tag'] 博客标签数组
├ $record['categoryId'] 博客分类ID
├ $record['_category']['title'] 博客分类标题
├ $record['_category']['cover'] 博客分类封面
├ $record['clickCount'] 博客点击数
├ $record['commentCount'] 博客评论数
├ $record['likeCount'] 博客喜欢数
├ $record['favCount'] 博客收藏数
├ $record['isTop'] 博客置顶
├ $record['isHot'] 博客热门
├ $record['isRecommend'] 博客推荐
├ $record['_date'] 日期
├ $record['_cover'] 博客封面(单张),会自动从内容中抽取
└ $record['_url'] 博客链接

# 博客分页 $page


# 当前页数 $page

{{$page}}
1

# 分页大小 $pageSize

{{$pageSize}}
1

# 分页HTML

$pageHtml

{!! $pageHtml !!}
1

当前列表分页HTML数据


<div class="pages">
    <a href="?page=1">1</a>
    <span>2</span>
    <a href="?page=1">3</a>
</div>
1
2
3
4
5
6

如果使用系统完整分页条,则以上样式名称在输出的HTML代码中都带,可以直接使用对应名称在自己的css中定义具体样式即可。

# 自定义分页

{!! \ModStart\Core\Util\PageHtmlUtil::render($total, $pageSize, $page,
        '?' . \ModStart\Core\Input\Request::mergeQueries(['page' => ['{page}']]), [
    'warp' => '<div>%s%</div>',
    'more' => '<span>...</span>',
    'prev' => '<a href="%s%">上一页</a>',
    'prevDisabled' => '<span>上一页</span>',
    'next' => '<a href="%s%">下一页</a>',
    'nextDisabled' => '<span>下一页</span>',
    'first' => '<a href="%s%">首页</a>',
    'last' => '<a href="%s%">尾页</a>',
    'current' => '<span>%p%</span>',
    'item' => '<a href="%s%">%p%</a>',
]) !!}
1
2
3
4
5
6
7
8
9
10
11
12
13

# 博客列表 blog.list

# 博客列表 $records


@foreach($records as $record)
    ID{{ $record['id'] }}
    标题:{{ $record['title'] }}
    时间:{{ $record['postTime'] }}
    摘要:{{ $record['summary'] }}
    图片:{{ join(',',$record['images']) }}
    标签:{{ join(',',$record['tag']) }}
    分类ID{{ $record['categoryId'] }}
    分类标题:{{ $record['_category']['title'] }}
    分类封面:{{ $record['_category']['cover'] }}
    点击数:{{ $record['clickCount'] }}
    评论数:{{ $record['commentCount'] }}
    喜欢数:{{ $record['likeCount'] }}
    收藏数:{{ $record['favCount'] }}
    置顶:{{ $record['isTop'] }}
    热门:{{ $record['isHot'] }}
    推荐:{{ $record['isRecommend'] }}
    日期:{{ $record['_date'] }}
    封面:{{ $record['_cover'] }}
    链接:{{ $record['_url'] }}
@endforeach
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
变量 说明
$records 博客记录数组
├ $record['id'] 博客ID
├ $record['title'] 博客标题
├ $record['postTime'] 博客创建时间
├ $record['summary'] 博客摘要
├ $record['images'] 博客图片数组
├ $record['tag'] 博客标签数组
├ $record['categoryId'] 博客分类ID
├ $record['_category']['title'] 博客分类标题
├ $record['_category']['cover'] 博客分类封面
├ $record['clickCount'] 博客点击数
├ $record['commentCount'] 博客评论数
├ $record['likeCount'] 博客喜欢数
├ $record['favCount'] 博客收藏数
├ $record['isTop'] 博客置顶
├ $record['isHot'] 博客热门
├ $record['isRecommend'] 博客推荐
├ $record['_date'] 日期
├ $record['_cover'] 博客封面(单张),会自动从内容中抽取
└ $record['_url'] 博客链接

# 博客分页


# 当前页数 $page

{{$page}}
1

# 分页大小 $pageSize

{{$pageSize}}
1

# 分页HTML

$pageHtml

{!! $pageHtml !!}
1

当前列表分页HTML数据


<div class="pages">
    <a href="?page=1">1</a>
    <span>2</span>
    <a href="?page=1">3</a>
</div>
1
2
3
4
5
6

如果使用系统完整分页条,则以上样式名称在输出的HTML代码中都带,可以直接使用对应名称在自己的css中定义具体样式即可。

# 自定义分页

{!! \ModStart\Core\Util\PageHtmlUtil::render($total, $pageSize, $page,
        '?' . \ModStart\Core\Input\Request::mergeQueries(['page' => ['{page}']]), [
    'warp' => '<div>%s%</div>',
    'more' => '<span>...</span>',
    'prev' => '<a href="%s%">上一页</a>',
    'prevDisabled' => '<span>上一页</span>',
    'next' => '<a href="%s%">下一页</a>',
    'nextDisabled' => '<span>下一页</span>',
    'first' => '<a href="%s%">首页</a>',
    'last' => '<a href="%s%">尾页</a>',
    'current' => '<span>%p%</span>',
    'item' => '<a href="%s%">%p%</a>',
]) !!}
1
2
3
4
5
6
7
8
9
10
11
12
13

# 博客详情 blog.show

# 博客 $record


ID{{ $record['id'] }}
标题:{{ $record['title'] }}
时间:{{ $record['postTime'] }}
摘要:{{ $record['summary'] }}
图片:{{ join(',',$record['images']) }}
标签:{{ join(',',$record['tag']) }}
分类ID{{ $record['categoryId'] }}
分类标题:{{ $record['_category']['title'] }}
分类封面:{{ $record['_category']['cover'] }}
点击数:{{ $record['clickCount'] }}
评论数:{{ $record['commentCount'] }}
喜欢数:{{ $record['likeCount'] }}
收藏数:{{ $record['favCount'] }}
置顶:{{ $record['isTop'] }}
热门:{{ $record['isHot'] }}
推荐:{{ $record['isRecommend'] }}
日期:{{ $record['_date'] }}
封面:{{ $record['_cover'] }}
链接:{{ $record['_url'] }}
内容:{{ $record['content'] }}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
变量 说明
$record['id'] 博客ID
$record['title'] 博客标题
$record['postTime'] 博客创建时间
$record['summary'] 博客摘要
$record['images'] 博客图片数组
$record['tag'] 博客标签数组
$record['categoryId'] 博客分类ID
$record['_category']['title'] 博客分类标题
$record['_category']['cover'] 博客分类封面
$record['clickCount'] 博客点击数
$record['commentCount'] 博客评论数
$record['likeCount'] 博客喜欢数
$record['favCount'] 博客收藏数
$record['isTop'] 博客置顶
$record['isHot'] 博客热门
$record['isRecommend'] 博客推荐
$record['_date'] 日期
$record['_cover'] 博客封面(单张),会自动从内容中抽取
$record['_url'] 博客链接
$record['content'] 博客内容

# 博客上/下篇

# 上篇博客 $recordPrev

变量 说明
$recordPrev['id'] 博客ID
$recordPrev['title'] 博客标题
$recordPrev['_url'] 博客地址

# 下篇博客 $recordNext

变量 说明
$recordNext['id'] 博客ID
$recordNext['title'] 博客标题
$recordNext['_url'] 博客地址

# 评论列表 $comments


@foreach($comments as $comment)
    留言ID{{ $comment['id'] }}
    留言用户名:{{ $comment['username'] }}
    留言邮箱:{{ $comment['email'] }}
    留言网址:{{ $comment['url'] }}
    留言时间:{{ $comment['created_at'] }}
    留言头像:{{ $comment['_avatar'] }}
    留言内容:{{ $comment['content'] }}
@endforeach
1
2
3
4
5
6
7
8
9
变量 说明
$comments 评论记录数组
├ $comment['id'] ID
├ $comment['username'] 称呼
├ $comment['email'] 邮箱
├ $comment['url'] 网址
├ $comment['content'] 内容(富文本)
├ $comment['created_at'] 创建时间
└ $comment['_avatar'] 头像

# 评论分页

可用变量 $commentPage $commentPageSize $commentTotal

使用方法同博客分页。

# 评论表单

评论表单提交到接口 blog/comment/add

<form method="post" data-ajax-form action="{{modstart_api_url('blog/comment/add')}}">
    <input type="hidden" name="blogId" value="{{$record['id']}}"/>
    <input type="text" name="username" placeholder="访客" />
    <input type="text" name="email" placeholder="邮箱" />
    <input type="text" name="url" placeholder="主页" />
    <textarea name="content" placeholder="内容"></textarea>
    <div>
        验证:
        {!! \Module\Vendor\Provider\Captcha\CaptchaProvider::get(modstart_config('Blog_BlogCaptchaProvider','default'))->render() !!}
    </div>
    <button type="submit">发表评论</button>
</form>
需要注意,表单提交需要引入这两个JS来处理 data-ajax-form 的表单提交
<script src="@asset('asset/vendor/jquery.js')"></script>
<script src="@asset('asset/common/base.js')"></script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 留言页面 blog.message

# 留言 $records


@foreach($records as $record)
    留言ID{{ $record['id'] }}
    留言内容:{{ $record['content'] }}
@endforeach
1
2
3
4
变量 说明
$records 留言记录数组
└ $record['id'] ID
└ $record['username'] 称呼
└ $record['email'] 邮箱
└ $record['url'] 网址
└ $record['content'] 内容
└ $record['created_at] 时间
└ $record['_avatar'] 头像

# 留言分页


# 当前页数 $page

{{$page}}
1

# 分页大小 $pageSize

{{$pageSize}}
1

# 分页HTML

$pageHtml

{!! $pageHtml !!}
1

当前列表分页HTML数据


<div class="pages">
    <a href="?page=1">1</a>
    <span>2</span>
    <a href="?page=1">3</a>
</div>
1
2
3
4
5
6

如果使用系统完整分页条,则以上样式名称在输出的HTML代码中都带,可以直接使用对应名称在自己的css中定义具体样式即可。

# 自定义分页

{!! \ModStart\Core\Util\PageHtmlUtil::render($total, $pageSize, $page,
        '?' . \ModStart\Core\Input\Request::mergeQueries(['page' => ['{page}']]), [
    'warp' => '<div>%s%</div>',
    'more' => '<span>...</span>',
    'prev' => '<a href="%s%">上一页</a>',
    'prevDisabled' => '<span>上一页</span>',
    'next' => '<a href="%s%">下一页</a>',
    'nextDisabled' => '<span>下一页</span>',
    'first' => '<a href="%s%">首页</a>',
    'last' => '<a href="%s%">尾页</a>',
    'current' => '<span>%p%</span>',
    'item' => '<a href="%s%">%p%</a>',
]) !!}
1
2
3
4
5
6
7
8
9
10
11
12
13

# 留言表单

留言表单提交到 blog/message/add

<form method="post" data-ajax-form action="{{modstart_api_url('blog/message/add')}}">
    <input type="text" name="username" placeholder="名称" />
    <input type="text" name="email" placeholder="输入邮箱" />
    <input type="text" name="url" placeholder="输入网站" />
    <textarea name="content" placeholder="内容"></textarea>
    <div>
        验证
        {!! \Module\Vendor\Provider\Captcha\CaptchaProvider::get(modstart_config('Blog_MessageCaptchaProvider','default'))->render() !!}
    </div>
    <button type="submit">
        提交
    </button>
</form>
需要注意,表单提交需要引入这两个JS来处理 data-ajax-form 的表单提交
<script src="@asset('asset/vendor/jquery.js')"></script>
<script src="@asset('asset/common/base.js')"></script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 通用页面

# 公共变量

  • $pageTitle 页面标题
  • $pageKeywords 页面关键词
  • $pageDescription 页面描述

# 配置


// 网站名称
{{ modstart_config('siteName') }}
// 网站Logo
{{ modstart_config_asset_url('siteLogo') }}
// 网站副标题
{{ modstart_config('siteSlogan') }}
// 网站域名
{{ modstart_config('siteDomain') }}
// 网站关键词
{{ modstart_config('siteKeywords') }}
// 网站描述
{{ modstart_config('siteDescription') }}
// 备案编号
{{ modstart_config('siteBeian') }}
// 网站ICO
{{ modstart_config_asset_url('siteFavIco') }}
// 网站主色调
{{ modstart_config('sitePrimaryColor') }}
// 网站主题
{{ modstart_config('siteTemplate') }}
// 联系方式-邮箱
{{ modstart_config('Site_ContactEmail') }}
// 联系方式-电话
{{ modstart_config('Site_ContactPhone') }}
// 联系方式-地址
{{ modstart_config('Site_ContactAddress') }}
// 联系方式-二维码
{{ modstart_config_asset_url('Site_ContactQrcode') }}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

// 博客名称
{{ modstart_config('Blog_Name') }}
// 博客标语
{{ modstart_config('Blog_Slogan') }}
// 博客头像
{{ modstart_config_asset_url('Blog_Avatar') }}
// 联系方式-QQ
{{ modstart_config('Blog_ContactQQ') }}
// 联系方式-微博
{{ modstart_config('Blog_ContactWeibo') }}
// 联系方式-微信
{{ modstart_config('Blog_ContactWechat') }}
1
2
3
4
5
6
7
8
9
10
11
12

# 导航


# 循环显示导航

@foreach(MNav::all('head') as $nav)
    <a href="{{ $nav['link'] }}">{{ $nav['name'] }}</a>
@endforeach
1
2
3
变量 说明
$nav['name'] 导航名称
$nav['link'] 导航链接
$nav['_child'] 子导航(空表示只有一级导航,不为空表示为二级导航)
$nav['openType'] 打开方式(2=新窗口 其他值=当前窗口)

# 不同的位置

MNav::all('head'); // 头部导航
MNav::all('foot'); // 底部导航
1
2

# 多级嵌套(最多支持三级菜单)

@foreach(MNav::all('head') as $nav)
    @if(empty($nav['_child']))
        <!-- 无二级导航 -->
        <a href="{{ $nav['link'] }}">{{ $nav['name'] }}</a>
    @else
        <!-- 有二级导航 -->
        <div class="title">
            <a href="{{$nav['link']}}">{{$nav['name']}}</a>
        </div>
        <div class="child">
            @foreach($nav['_child'] as $nav2)
                @if(empty($nav2['_child']))
                    <!-- 无三级导航 -->
                    <a href="{{$nav2['link']}}">{{$nav2['name']}}</a>
                @else
                    <!-- 有三级导航 -->
                    <div class="title">
                        <a href="{{$nav2['link']}}">{{$nav2['name']}}</a>
                    </div>
                    <div class="child">
                        @foreach($nav2['_child'] as $nav3)
                            <a href="{{$nav3['link']}}">{{$nav3['name']}}</a>
                        @endforeach
                    </div>
                @endif
            @endforeach
        </div>
    @endif
@endforeach
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

# 高亮菜单

当前URL匹配链接时输出 CSS 类 active

@foreach(MNav::all('head') as $nav)
    <a href="{{ $nav['link'] }}"
    class="{{modstart_baseurl_active($nav['link'],'active')}}">
        {{ $nav['name'] }}
    </a>
@endforeach
1
2
3
4
5
6

更多参考 Nav (opens new window) 模块

# 轮播


# 循环显示轮播

// 循环特定位置轮播
@foreach(MBanner::all('Xxx') as $banner)
    @if($banner['type']===\Module\Banner\Type\BannerType::IMAGE)
        图片
        <a href="{{ $banner['link'] }}">{{ $banner['image'] }}</a>
    @elseif($banner['type']===\Module\Banner\Type\BannerType::IMAGE_TITLE_SLOGAN_LINK)
        图片+标题+描述+链接
        <a href="{{ $banner['link'] }}">{{ $banner['image'] }}</a>
        <span>{{ $banner['title'] }}</span>
        <span>{{ $banner['slogan'] }}</span>
        <a href="{{ $banner['link'] }}">{{ $banner['linkText'] }}</span>
    @elseif($banner['type']===\Module\Banner\Type\BannerType::VIDEO)
        视频
        <a href="{{ $banner['link'] }}">{{ $banner['video'] }}</a>
    @endif
@endforeach

// 循环特定位置轮播(仅包含图片)
@foreach(MBanner::allImage('Xxx') as $banner)
  <a href="{{ $banner['link'] }}">{{ $banner['image'] }}</a>
@endforeach
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 支持多个位置

// 位置 Blog
\MBanner::all('Blog')
// 位置 Cms
\MBanner::all('Cms')
1
2
3
4

更多使用可参考 Banner (opens new window) 模块

# 友情链接


# 循环显示友情链接

// 循环特定位置友情链接
@foreach(\MPartner::all('Xxx') as $partner)
  <a href="{{ $partner['link'] }}">{{ $partner['title'] }}</a>
@endforeach
1
2
3
4

# 不同位置友情链接

// 位置 Blog
\MPartner::all('Blog')
// 位置 Cms
\MPartner::all('Cms')
1
2
3
4

更多使用可参考 Partner (opens new window) 模块

# 站点地图


启用网站需要安装 SiteMapManager (opens new window) 模块获得支持,设置后访问路径如下:

  • `http://www.example.com/sitemap.xml`

系统使用动态站点地图,不需要手动生成,访问地址即为实时地图,输出格式为xml格式。具体使用方式可参考 SiteMapManager (opens new window) 模块的使用方法。

image-20220621084218094

# 用户


需要安装模块获得支持 Member (opens new window)

@if(\Module\Member\Auth\MemberUser::isLogin())
    用户ID{{$_memberUser['id']}}
    用户名:{{$_memberUser['username']}}
    昵称:{{$_memberUser['nickname']}}
@else
    请登录
@endif
1
2
3
4
5
6
7
变量 说明
$_memberUserId 用户ID,可以判断该变量是否为空来判断用户是否登录
$_memberUser['id'] 用户ID
$_memberUser['username'] 用户名
$_memberUser['avatar'] 头像
$_memberUser['nickname'] 昵称

更多参考 Member (opens new window)

# 视图开发


# 网站主色调

可以通过 CSS 的变量 var(--color-primary) 获取,该变量会根据用户后台设置的主色调来动态改变。

# 页面框架与继承

通常情况下一个主题结构如图所示

├── 视图根目录                                    → 模块视图主目录
│     └── pc                                    → 自适应默认为PC
│         ├── xxx                               → 模块视图子目录
│         │     ├── list.blade.php              → 详情页
│         │     ├── show.blade.php              → 列表页
│         │     ├── index.blade.php             → 首页
│         ├── footer.blade.php
│         ├── frame.blade.php                   → 框架视图
│         └── header.blade.php
1
2
3
4
5
6
7
8
9

# 框架视图 frame.blade.php

通过会在模板文件中可以通过 @extends($_viewFrame) 来确定当前模板的框架视图,该文件通常包含公共的头部、尾部、侧边栏等内容。

$_viewFrame 会根据当前模板查找最先匹配到的文件,通常情况下,该变量会按照如下顺序查找:

  1. resources/views/theme/<主题>/pc/frame.blade.php
  2. module/<主题模块>/View/pc/frame.blade.php
  3. resources/views/theme/default/pc/frame.blade.php

请保证 frame.blade.php 文件为以下结构,完整结构可参考 vendor/modstart/modstart/views/layout/frame.blade.php

<!doctype html>
<html>
<head>
    ...
    <title>@section('pageTitle')@yield('pageTitleMain','') | {{modstart_config('siteName')}}@endsection</title>
    <meta name="keywords" content="@yield('pageKeywords','')">
    <meta name="description" content="@yield('pageDescription','')">
    @section('headAppend')@show
</head>
<body>
    @section('body')
        @section('bodyContent')@show
    @show
    @section('bodyAppend')@show
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 页面视图

@extends($_viewFrame)

@section('pageTitleMain')我的视图标题@endsection

@section('bodyContent')
    <div class="ub-container">
        我的视图文件
    </div>
@endsection
1
2
3
4
5
6
7
8
9

其中:

  • $_viewFrame 变量表示当前系统使用的框架视图
  • @section('pageTitleMain') 为系统标题
  • @section('bodyContent') 为系统正文内容部分

# 自适应的设备视图

根据访问设备的不同,会启用不同的视图文件,具体逻辑可参照 \ModStart\Core\View\ResponsiveViewTrait 中的逻辑。

  • PC端使用 pc/ 中的视图
  • 手机端使用 m/ 中的视图
  • 当手机端视图不存在时,会自动降级使用 pc/ 中的视图

# 系统路径

// 系统路径
{{ modstart_web_url('foo/bar') }}
// 系统路径,带域名
{{ modstart_web_full_url('foo/bar') }}
1
2
3
4

# 页面 section 修改

<!--为页面设置标题 -->
@section('pageTitle')页面标题@endsection

<!--设置页面主标题(会自动补全网站名称)-->
@section('pageTitleMain')页面主标题@endsection

<!--设置页面关键词-->
@section('pageKeywords')页面关键词@endsection

<!--设置页面描述-->
@section('pageDescription')页面描述@endsection

<!--在页面head上追加内容-->
@section('headAppend')
    @parent
    <script src="head.js"></script>
@endsection

<!--在页面body上追加内容-->
@section('bodyAppend')
    @parent
    <script src="body.js"></script>
@endsection
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# 使用 @asset 引入路径

使用 @asset 指令可以引入静态资源,同时会自动添加版本号,以便于浏览器缓存。

<script src="@asset('foo/bar.js')"></script>
<link rel="stylesheet" href="@asset('vendor/Xxx/style/test.css')" />
<img src="@asset('vendor/Xxx/image/test.jpg')" />
1
2
3

最终显示在浏览器端的HTML代码为

<script src="/foo/bar.js?4015423772"></script>
<link rel="stylesheet" href="/vendor/Xxx/style/test.css?4015427772" />
<img src="/vendor/Xxx/image/test.jpg?4015423772" />
1
2
3

其中文件后加的 Hash 值在文件内容变动时会变化,避免了浏览器缓存。

# 页面 JS 和 CSS

页面 JavaScript 会自动放在页面尾部(</body> 之前)

// 引入行内 js
\ModStart\ModStart::script('console.log("Hello ModStart");');
// 引入一个 JavaScript 文件内容到页面
\ModStart\ModStart::js('文件路径.js');
// 引入一个 js 文件路径到页面
\ModStart\ModStart::js('文件路径.js');
1
2
3
4
5
6

页面 CSS 会自动放在头部(</head> 之前)

// 引入行内 css
\ModStart\ModStart::style('.test{ color:red; }');
// 引入一个 CSS 文件内容到页面
\ModStart\ModStart::styleFile('文件路径.css');
// 引入一个 CSS 文件路径到页面
\ModStart\ModStart::css('文件路径.css');
1
2
3
4
5
6

# 时间格式化

// $t = '2021-01-01 00:00:00'
date('Y-m-d', strtotime($t) )
1
2

时间格式化标签和PHP时间格式化语法一致,分别用不同字母代替,中间可以穿插任意字符,如:Y-m-dY/m/dY年m月d日

注意:Y代表完整年份,y代表简化年份,m代表月份,d代表日,H代表小时,i代表分,s代表秒,更多请参考PHP时间格式化语法 ,参考链接 :http://php.net/manual/zh/function.date.php (opens new window)

# HTML去除标签

// 去除HTML并限制输出 100 个字符
\ModStart\Core\Util\HtmlUtil::text($html,100)
1
2

一般在首页等特殊位置需要调取正文一段纯文本时使用,这时一般会配合下面的内容截取标签一起使用

# 内容截取

// HTML
\ModStart\Core\Util\HtmlUtil::text($html,100)
// 非HTML
\ModStart\Core\Util\StrUtil::limit($text,100,'...')
1
2
3
4

限制输出 100 个字符

# 其它格式

// 限定浮点格式,更多参考 https://www.php.net/manual/en/function.sprintf.php
sprintf('%0.2f', 3.14159)
1
2

# 当前网址

以访问 http://example.com/test/path?a=b 为例

// /test/path
\ModStart\Core\Input\Request::path()
// http://example.com/test/path?a=b
\ModStart\Core\Input\Request::currentPageUrl()
// http://example.com/test/path
\ModStart\Core\Input\Request::currentPageUrlWithOutQueries()
1
2
3
4
5
6

# 当前页面URL

获取当前访问页面完整地址,通常用于生成二维码等

\ModStart\Core\Input\Request::currentPageUrl()
1

# 二维码生成

生成二维码数据

// 返回base64图片字符串
\ModStart\Core\Util\QrcodeUtil::pngBase64String('ModStart')
1
2

可直接用于显示,如

<img src="{!! \ModStart\Core\Util\QrcodeUtil::pngBase64String('ModStart') !!}" />
1

# 获取系统配置

网站配置全部位于 config 数据表中,查找 key 值,通过如下如下函数可以直接获取值,该函数已内置添加了缓存可在视图中多次调用。

modstart_config('xxx','默认值')
1

image-20220620171930360

# 模板语法


# 显示数据

你可以使用「中括号」包住变量以显示传递至 Blade 视图的数据。就如以下的路由设置一样:

return view('welcome', ['name' => 'Samantha']);
1

你可以像这样显示 name 变量的内容:

Hello, {{ $name }}.
1

显示数据或默认值

此写法兼容Laravel5和Laravel9+

{{ empty($name) ? '默认值' : $name }}
1

当然也不是说一定只能显示传递至视图的变量内容。你也可以显示 PHP 函数的结果。实际上,你可以放置任何你想要的 PHP 代码到 Blade 显示的语法里面:

目前的 UNIX 时间戳为 {{ time() }}
1

注意:Blade 的 语法会自动调用 PHP htmlentites 函数来防御 XSS 攻击。

# 条件判断 if

你可以使用 @if、@elseif、@else 及 @endif 命令建构 if 表达式。这些命令的功能等同于在 PHP 中的语法:

@if (count($records) === 1)
    我有一条记录!
@elseif (count($records) > 1)
    我有多条记录!
@else
    我没有任何记录!
@endif
1
2
3
4
5
6
7

# 循环 for foreach

除了条件表达式外,Blade 也支持 PHP 的循环结构:

@for ($i = 0; $i < 10; $i++)
    目前的值为 {{ $i }}
@endfor

@foreach ($users as $user)
    <p>此用户为 {{ $user->id }}</p>
@endforeach

@forelse ($users as $user)
    <li>{{ $user->name }}</li>
@empty
    <p>没有用户</p>
@endforelse

@while (true)
    <p>我永远都在跑循环。</p>
@endwhile
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 定义页面布局

使用 Blade 模板的两个主要优点为 模板继承 与 区块。

让我们先通过一个简单的例子来上手。首先,我们需要确认一下「主要的」页面布局。大多数的网页应用程序在不同页面都保持着相同的布局方式,这种布局在这单个 Blade 视图中可以很方便的定义:

<html>
    <head>
        <title>应用程序名称 - @yield('title')</title>
    </head>
    <body>
        @section('sidebar')
            这是主要的侧边栏。
        @show

        <div class="container">
            @yield('content')
        </div>
    </body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14

如你所见,这个文件包含了传统的 HTML 语法。不过,请注意 @section 与 @yield 命令。正如其名,@section 命令定义一个内容区块,而 @yield 命令被用来 “显示指定区块” 的内容。

现在,我们已经定义好了这个应用程序的布局,让我们接着来定义一个继承此布局的子页面。

# 继承页面布局

当正在定义子页面时,你可以使用 Blade 的 @extends 命令指定子页面应该「继承」哪一个布局。当视图 @extends Blade 的布局之后,即可使用 @section 命令将内容注入于布局的区块中。切记,如上述例子所见,这些区块的内容都会使用 @yield 显示在布局中:

@extends('theme.pc.default.frame')

@section('title', '页面标题')

@section('sidebar')
    @parent
    <p>这边会附加在主要的侧边栏。</p>
@endsection

@section('content')
    <p>这是我的主要内容。</p>
@endsection
1
2
3
4
5
6
7
8
9
10
11
12

在这个例子中,sidebar 区块利用了 @parent 命令增加(而不是覆盖)内容至布局的侧边栏。@parent 命令会在视图输出时被置换成布局的内容。

# 大括号不转义输出

由于许多 JavaScript 框架也使用「大括号」在浏览器中显示指定的表达式,因此可以使用 @ 符号来告知 Blade 渲染引擎该表达式应该维持原样。举个例子:

Hello, @{{ name }}.
1

在这个例子中,@ 符号会被 Blade 移除。而且,Blade 引擎会保留 表达式,如此一来便可跟其它 JavaScript 框架一起应用。

# 显示未转义过的数据

在默认情况下,Blade 模板中的 表达式将会自动调用 PHP 的 htmlentities 函数,以避免 XSS 攻击。如果你不希望你的数据被转义,可以使用下列的语法:

Hello, {!! $name !!}.
1

注意:要非常小心处理用户提供的字符串,请总是使用双大括号语法来转义内容中的 HTML 元素,以避免 XSS 攻击。

# 引入子视图

Blade 的 @include 命令用来引入已存在的视图,所有在父视图的可用变量在被引入的视图中都是可用的。

@include('share.header')
1

尽管被引入的视图会继承父视图中的所有数据,你也可以通过传递额外的数组数据至被引入的页面:

@include('view.name', ['some' => 'data'])
1

使用说明:

@include('module::Xxx.View.pc.a.b') 表示包含路径 module/Xxx/View/pc/a/b.blade.php 视图文件

@include('theme.default.pc.a.b') 表示包含路径 resources/views/theme/default/pc/a/b.blade.php 视图文件

如果视图文件以 module:: 开头,表示是目录 module/ 中的视图,否则为 resources/views/theme/ 中的视图

# 当数据存在时输出

有时候你想要输出一个变量,但你并不确定这个变量是否已被设置。我们可以用像这样的冗长 PHP 代码表达:

{{ isset($name) ? $name : '默认值' }}
1

在这个例子中,如果 $name 变量存在,它的值将会被显示出来。但是,如果这个变量不存在,便会显示 默认值

# 注释

Blade 也允许在页面中定义注释,然而,跟 HTML 的注释不同的是,Blade 注释不会被包含在应用程序返回的 HTML 内:

{{-- 此注释将不会出现在渲染后的 HTML --}}
1

# API接口

新窗口查看API接口 (opens new window)

# 模块方法

新窗口查看Blog操作方法 (opens new window)

Last Updated: a year ago