Yii 2 框架本身与 Bootstrap 紧密集成,许多内置小部件(Widgets)都是基于 Bootstrap 5 设计的。
接下来我们介绍如何使用 Layui 代替掉 Bootstrap。
Yii 2 的前端资源(如 CSS、JavaScript、图片、字体等)由 Asset Manager 组件管理,禁用 Bootstrap 和导入 Layui 都需要通过 Asset Manager 进行操作。
禁用 Bootstrap
找到 /config/web.php
,在 components
中添加 assetManager
组件配置
php
'components' => [
'assetManager' => [
'bundles' => [
'yii\bootstrap5\BootstrapAsset' => false,
'yii\bootstrap5\BootstrapPluginAsset' => false,
]
],
// 其它组件配置项
]
导入 Layui
使用 npm 安装包时默认会将新包安装到根目录的 node_modules
文件夹中,安装前需要修改 /config/web.php
文件的配置中 aliases
下的 @npm
将其指向 node_modules
。
php
'aliases' => [
...
// 使用于 LayuiAsset -> sourcePath
'@npm' => '@app/node_modules',
],
安装 Layui
shell
npm i layui
新建 LayuiAsset 类
php
// /assets/LayuiAsset.php
namespace app\assets;
use yii\web\AssetBundle;
use yii\web\View;
class LayuiAsset extends AssetBundle
{
public $sourcePath = '@npm/layui/dist';
public $css = [
'css/layui.css',
];
public $js = [
'layui.js',
];
public $jsOptions = [
'position' => View::POS_HEAD,
// 将 layer 注册为全局对象
'onload' => 'layui.use(["layer"], function(){ window.layer = layui.layer;});',
];
}
修改依赖
php
// /assets/AppAsset.php
class AppAsset extends AssetBundle
{
...
public $depends = [
'yii\web\YiiAsset',
// 'yii\bootstrap5\BootstrapAsset'
'app\assets\LayuiAsset',
];
}
至此已可以使用 Layui 所有组件进行页面开发。
下面尝试使用 Layui 风格封装一个 Navbar 小组件
Layui 风格的导航栏
Yii 2 的所有小组件继承自 yii\base\Widget
,以下为抄袭 yii\bootstrap5\Nav
小组件封装的一个 Layui 风格的导航栏
php
namespace app\widgets\layui;
use app\assets\LayuiAsset;
use yii\base\InvalidConfigException;
use yii\base\Widget;
use yii\helpers\ArrayHelper;
use yii\helpers\Html;
use Yii;
class Navbar extends Widget
{
public array $items = [];
public string $theme = '';
public bool $encodeLabels = true;
public bool $activateItems = true;
public bool $activateParents = true;
public array $options = ['class' => 'layui-nav'];
public $route = null;
public $params = null;
public function init()
{
parent::init();
if ($this->route === null && Yii::$app->controller !== null) {
$this->route = Yii::$app->controller->getRoute();
}
if ($this->params === null) {
$this->params = Yii::$app->request->getQueryParams();
}
}
public function run(): string
{
LayuiAsset::register($this->getView());
return $this->renderItems();
}
public function renderItems(): string
{
$items = [];
foreach ($this->items as $item) {
if (isset($item['visible']) && !$item['visible']) {
continue;
}
// 一级菜单
$items[] = $this->renderItem($item);
}
// 主题设置
if ($this->theme) {
$themeClass = $this->getThemeClass($this->theme);
Html::addCssClass($this->options, ['class' => $themeClass]);
}
return Html::tag('ul', implode("\n", $items), $this->options);
}
public function renderItem($item)
{
if (is_string($item)) {
return $item;
}
if (!isset($item['label'])) {
throw new InvalidConfigException("The 'label' option is required.");
}
$encodeLabel = $item['encode'] ?? $this->encodeLabels;
$label = $encodeLabel ? Html::encode($item['label']) : $item['label'];
$items = ArrayHelper::getValue($item, 'items', ''); // 下拉选项
$linkOptions = ArrayHelper::getValue($item, 'linkOptions', []);
$url = ArrayHelper::getValue($item, 'url', '#');
$active = $this->isItemActive($item);
$options = ['class' => 'layui-nav-item'];
if (is_array($items)) {
$url = 'javascript:void(0)';
$items = $this->isChildActive($items, $active);
$items = $this->renderDropdown($items, $item);
}
// 开启了菜单高亮则添加高亮类
if ($this->activateItems && $active) {
Html::addCssClass($options, ['class' => 'layui-this']);
}
return Html::tag('li', Html::a($label, $url, $linkOptions) . $items, $options);
}
protected function renderDropdown($items, $item): string
{
if (is_string($item)) {
return $item;
}
$options = ['class' => 'layui-nav-child'];
$content = '';
foreach ($items as $item) {
$itemOptions = $this->isItemActive($item) ? ['class' => 'layui-this'] : [];
if (is_string($item)) {
$content .= Html::tag('dd', $item);
} else {
$encodeLabel = $item['encode'] ?? $this->encodeLabels;
$label = $encodeLabel ? Html::encode($item['label']) : $item['label'];
$url = ArrayHelper::getValue($item, 'url', '#');
$content .= Html::tag('dd', Html::a($label, $url), $itemOptions);
}
}
return Html::tag('dl', $content, $options);
}
protected function isItemActive($item): bool
{
if (!$this->activateItems) {
return false;
}
if (isset($item['active'])) {
return (bool)ArrayHelper::getValue($item, 'active', false);
}
if (isset($item['url'][0]) && is_array($item['url'])) {
$route = $item['url'][0];
if ($route[0] !== '/' && Yii::$app->controller) {
$route = Yii::$app->controller->module->getUniqueId() . '/' . $route;
}
if (ltrim($route, '/') !== $this->route) {
return false;
}
unset($item['url']['#']);
if (count($item['url']) > 1) {
$params = $item['url'];
unset($params[0]);
foreach ($params as $name => $value) {
if ($value !== null && (!isset($this->params[$name]) || $this->params[$name] != $value)) {
return false;
}
}
}
return true;
}
return false;
}
protected function isChildActive(array $items, bool &$active): array
{
foreach ($items as $i => $child) {
if (is_array($child) && !ArrayHelper::getValue($child, 'visible', true)) {
continue;
}
if (is_array($child) && $this->isItemActive($child)) {
ArrayHelper::setValue($items[$i], 'active', true);
if ($this->activateParents) {
$active = true;
}
}
$childItems = ArrayHelper::getValue($child, 'items');
if (is_array($childItems)) {
$activeParent = false;
$items[$i]['items'] = $this->isChildActive($childItems, $activeParent);
if ($activeParent) {
Html::addCssClass($items[$i]['options'], ['class' => 'layui-this']);
$active = true;
}
}
}
return $items;
}
private function getThemeClass($theme)
{
if (in_array($theme, ['gray','cyan','green','blue'])) {
return 'layui-bg-' . $theme;
}
return '';
}
}
使用方法
php
<?= \app\widgets\layui\Navbar::widget([
'items' => [
['label' => 'Home', 'url' => ['/site/index']],
['label' => 'About', 'url' => ['/site/about']],
['label' => 'Contact', 'url' => ['/site/contact']],
Yii::$app->user->isGuest
? ['label' => 'Login', 'url' => ['/site/login']]
: ['label' => 'Logout (' . Yii::$app->user->identity->username . ')', 'url' => ['/site/logout'], 'linkOptions' => ['data-method' => 'post']],
['label' => 'Setting', 'items' => [
['label' => 'System1', 'url' => ['/site/system1']],
['label' => 'System2', 'url' => ['/site/system2']],
['label' => 'System3', 'url' => ['/site/system3']],
]]
]
]) ?>

效果不错呢,嘻嘻。