# 主题
<p class="uk-article-lead">主题改变网站的外观。主题最简单的形式就是在生成一系列的 HTML 标签来包裹由各种扩展输出的内容。</p>
**Note** 本教程中的例子来源于 _Hello_ 主题,可以在商店中安装它。安装后,_Hello_ 主题位于 `/packages/pagekit/theme-hello`.
[toc=2]
## 包的定义
主题其实是 Pagekit 的 `pagekit-theme` 类型的[包](224130)。每个包都需要一个描述让 Pagekit 识别它。描述是位于 `composer.json` 文件中,看起来是下面代码的样子。 更多详情,查阅[包](224130) 这一章。
```json
{
"name": "pagekit/theme-hello",
"type": "pagekit-theme",
"version": "0.9.0",
"title": "Hello"
}
```
## 模块的定义
主题本身只是一个[模块](224131)。所以你可能想要先去阅读模块。它为主题能做的事展开了许多可能性。
定义主题中的控件位置和菜单,加载额外的脚本等等。这里有一个简短的 `index.php` 例子可以让你快速起步。主题具体的属性解释都在下文中。
```php
return [
'name' => 'theme-hello',
/**
* Define menu positions.
*/
'menus' => [
'main' => 'Main',
],
/**
* Define widget positions.
*/
'positions' => [
'sidebar' => 'Sidebar',
]
];
```
主题定义渲染菜单和小工具的位置。如下面的说明,实际的渲染发生在 `template.php` 中。然而,你的主题需要先注册这些位置,需要用到 `menus` 和 `positions` 属性。这些包含位置名称和标签的数组,会显示在管理面板中。
### 菜单
在主题中,可以在从 Pagekit 系统中渲染随意多个菜单到各个位置上。要让 Pagekit 知晓这些位置,需要使用 `menu` 属性注册它们。
每个菜单位置都是由识别符(比如 `main`)和标签定义,并显示给用户(比如 _Main_)。
```php
'menus' => [
'main' => 'Main',
'offcanvas' => 'Offcanvas'
],
```
### 位置
小工具位置允许用户将小工具发布到主题标签中的各个位置上。它们显示在 Pagekit 管理面板的 _Widgets_ 这块,可以设置小工具的时候进行选择。
每个小工具位置都是由识别符(比如 `sidebar`)和标签定义,并显示给用户(比如 _Sidebar_)。
```php
'positions' => [
'sidebar' => 'Sidebar',
],
```
## 布局文件
除了强制性的模块文件,主题带有它自己的 `views/template.php` 文件。它是主题标签的主要文件,含有以下用于渲染的对象。
|对象 | 描述 |
|--------- | ------------------------------ |
|`$view` | 视图渲染器实例|
|`$params` | 主题参数|
|`$app` | Application 容器实例|
**Note** 对于 PHP 模板,短码(short notation) `<?= $var ?>` 输出变量 `$var` 的值。
```html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Always render head section from the system -->
<?= $view->render('head') ?>
<!-- Include theme css -->
<?php $view->style('theme', 'theme:css/theme.css') ?>
<!-- Include theme JS, require jQuery which will also be included -->
<?php $view->script('theme', 'theme:js/theme.js', 'jquery') ?>
</head>
<body>
<!-- Render logo with site URL -->
<?php if ($logo = $params['logo']) : ?>
<a href="<?= $view->url()->get() ?>">
<img src="<?= $this->escape($logo) ?>" alt="">
</a>
<?php endif ?>
<!-- Render menu position -->
<?php if ($view->menu()->exists('main')) : ?>
<?= $view->menu('main') ?>
<?php endif ?>
<!-- Render widget position -->
<?php if ($view->position()->exists('sidebar')) : ?>
<?= $view->position('sidebar') ?>
<?php endif; ?>
<!-- Render system messages -->
<?= $view->render('messages') ?>
<!-- Render content -->
<?= $view->render('content') ?>
<!-- Insert code before the closing body tag -->
<?= $view->render('footer') ?>
</body>
</html>
```
## 菜单和位置渲染器
你可能想要使用自定义菜单或位置渲染器。下面有两个例子想你展示如何使用它们。
```php
<?= $view->menu('main', 'menu-navbar.php') ?>
```
此例中,`main` 菜单将被 `menu-navbar.php` 布局文件渲染。
```php
<ul class="uk-navbar-nav">
<?php foreach ($root->getChildren() as $node) : ?>
<li>
<!-- ... more markup ... -->
</li>
<?php endforeach ?>
</ul>
```
同样可用于小工具位置。
```php
<?= $view->position('hero', 'position-grid.php') ?>
```
这里,小工具位置 `hero` 将被 `position-grid.php` l 布局文件渲染。
```php
<?php foreach ($widgets as $widget) : ?>
<div class="uk-width-1-<?= count($widgets) ?>">
<div>
<h3><?= $widget->title ?></h3>
<?= $widget->get('result') ?>
</div>
</div>
<?php endforeach ?>
```
## 默认的 Pagekit 标签
Pagekit 的管理面板使用 UIkit 前端框架构建。这就是为何 Pagekit 的核心扩展,比如静态页面和博客,都会输出带有 UIkit 的 CSS class 的标签。所以,你完全可以使用 UIkit 来创建自己的主题。
要为 Pagekit 系统输出的信息添加样式,你可以只为少量 class 添加 CSS,而不是引入整个 UIkit CSS。Hello 主题的 `theme.css` 文件已经带有必要的 class了。
如果想要彻底修改 Pagekit 自己生成的标签,可以覆写系统的视图文件。
## 覆写系统视图
要覆写系统的视图文件,只需要按下表所述,在主题中创建相应的文件夹来模拟原有的文件结构,并将主题文件放在此处。
|文件 | 原有视图文件 | 描述|
|---------------------------- | ---------------------------------------- | ------------------------|
|`views/system/site/page.php` | `/app/system/site/views/page.php` | 默认的静态页面视图|
|`views/blog/post.php` | `/packages/pagekit/blog/views/post.php` | 博客文章单页视图|
|`views/blog/posts.php` | `/packages/pagekit/blog/views/posts.php` | 博客文章列表视图|
要了解在这些视图中有哪些可用变量,可以在原有视图文件中找到。
## 为网站界面添加主题选项
这是通过 javascript 实现的,如果使用 Vue 组件,会非常顺利。
在站点树界面已激活时,加载你的 JS。在 `index.php` 中,你可以监听响应的事件来进行此操作。
```
'events' => [
'view.system/site/admin/settings' => function ($event, $view) use ($app) {
$view->script('site-theme', 'theme:js/site-theme.js', 'site-settings');
$view->data('$theme', $this);
},
// ...
],
```
`js/site-theme.js` 包含渲染界面和负责存储主题设置的 Vue 组件。
**Note** 尽管可以在单个 JS 文件中处理这些东西,并以字符串表示标签,但事实上最佳实践是使用 Vue 组件创建 `*.vue` 文件。在默认的 _One_ 主题中的`app/components` 文件夹可以找到例子。
```js
window.Site.components['site-theme'] = {
section: {
label: 'Theme',
icon: 'pk-icon-large-brush',
priority: 15
},
template: '<div>Your form markup here</div>',
data: function () {
return window.$theme;
},
events: {
save: function() {
var config = _.omit(this.config, ['positions', 'menus', 'widget']);
this.$http.post('admin/system/settings/config', {name: this.name, config: config}).error(function (data) {
this.$notify(data, 'danger');
});
}
}
};
```
## 为站点树中的节点配置添加主题选项卡
要为站点树中特定的节点(Node)添加主题选项。例如,想要允许用户为每个页面选取不同的主图。可以为站点界面添加一个 _Theme_ 选项卡。
```php
'events' => [
// ...
'view.system/site/admin/edit' => function ($event, $view) {
$view->script('node-theme', 'theme:js/node-theme.js', 'site-edit');
},
// ...
];
```
针对 `js/node-theme.js` 的例子:
```js
window.Site.components['node-theme'] = {
section: {
label: 'Theme',
priority: 90
},
props: ['node'],
template: '<div>Your form markup here</div>'
};
```
**Note** 与默认的 _One_ 主题中 `app/components` 文件夹中完整的 Vue 组件进行比较。
## 为小工具界面添加主题选项
注册要在_Widget_ 编辑视图中加载的脚本。
```
'view.system/widget/edit' => function ($event, $view) {
$view->script('widget-theme', 'theme:app/bundle/widget-theme.js', 'widget-edit');
},
```
Example for `widget-theme.js`:
```js
window.Widgets.components['widget-theme'] = {
section: {
label: 'Theme',
priority: 90
},
props: ['widget', 'config'],
template: '<div>Your form markup here</div>'
};
```
**Note** 与默认的 _One_ 主题中 `app/components` 文件夹中完整的 Vue 组件进行比较。
## 手动添加设置页面
如果预设的添加设置页面的方法不符合你的需要,你还可以手动创建全新的界面。使用 `index.php` 中的模块定义,你能掌控一切然后可以像下面这样来处理:
1. 为设置页面创建视图文件
2. 新建一个包含渲染此视图文件的操作的控制器
3. 在 `index.php` 中注册控制器和路由
4. 可选,将 `settings` 设为新的设置页面的路径