# 主題
<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` 設為新的設置頁面的路徑