],
'queue' => [
'class' => Queue::class,
- // 'dsn' => 'amqp://admin:3qqHK2MRgGgxUdVT61@rabbitmq-yii_erp24:5672',
- 'dsn' => 'amqp://admin:3qqHK2MRgGgxUdVT61@localhost:5672',
+ 'dsn' => 'amqp://admin:3qqHK2MRgGgxUdVT61@rabbitmq-yii_erp24:5672',
+ // 'dsn' => 'amqp://admin:3qqHK2MRgGgxUdVT61@localhost:5672',
'queueName' => 'telegram-queue',
'as log' => \yii\queue\LogBehavior::class,
'ttr' => 300, // Время для выполнения задания
'enablePrettyUrl' => true,
'showScriptName' => false,
'rules' => [
+ 'wiki/<parent_cat_slug>/<article_slug>' => 'wiki/view',
],
],
],
*/
public function actionView($parent_cat_slug, $article_slug)
{
+ $categories = WikiCategory::find()->all();
+ $tree = $this->buildTree($categories);
$category = WikiCategory::findOne(['slug' => $parent_cat_slug]);
if (!$category) {
throw new NotFoundHttpException('Category not found.');
throw new NotFoundHttpException('Article not found.');
}
- return $this->render('view', ['model' => $model]);
+ return $this->render('view', ['model' => $model, 'tree' => $tree]);
}
/**
{
return $this->hasMany(WikiCategory::class, ['parent_id' => 'id']);
}
+ public function getParentCategories()
+ {
+ $categories = [];
+ $category = $this;
+ while ($category !== null) {
+ array_unshift($categories, $category);
+ $category = $this->parent;
+ }
+ return $categories;
+ }
+
}
<?= $form->field($model, 'content')->widget(CKEditor::class, [
'options' => ['rows' => 6],
- 'uploadUrl' => '/uploads',
+ 'uploadUrl' => Yii::getAlias('@uploads')
]) ?>
$this->title = 'Документация';
$this->params['breadcrumbs'][] = $this->title;
$user = Yii::$app->user->identity;
-function renderTree($tree, $level = 0)
-{
- if (empty($tree)) {
- return '';
- }
-
- $html = '<ul class="tree-level-' . $level . '">';
- foreach ($tree as $node) {
- $category = $node['category'];
- $articles = $node['articles'];
- $children = $node['children'];
-
- // Выводим категорию
- $html .= '<li>';
- $html .= Html::tag('div', Html::encode($category->title), ['class' => 'category-title']);
-
- // Выводим описание категории (если есть)
- if (!empty($category->description)) {
- $html .= Html::tag('div', Html::encode($category->description), ['class' => 'category-description']);
- }
-
-
- if (!empty($articles)) {
- $html .= '<ul class="articles-list">';
- foreach ($articles as $article) {
- $articleUrl = Url::to(['view', 'parent_cat_slug' => $category->slug, 'article_slug' => $article->slug], true);
- $html .= '<li>';
- $html .= Html::a(Html::encode($article->title), $articleUrl, ['class' => 'article-link']);
-
- $html .= '</li>';
- }
- $html .= '</ul>';
- }
-
- if (!empty($children)) {
- $html .= '<ul class="subcategories visible">';
- $html .= renderTree($children, $level + 1);
- $html .= '</ul>';
- }
-
- $html .= '</li>';
- }
- $html .= '</ul>';
-
- return $html;
-}
?>
<!-- Вывод дерева -->
<div class="row">
<div class="col-3 tree-container">
- <?= renderTree($tree); ?>
+ <?= $this->render('tree', ['tree' => $tree]) ?>
</div>
<div class="col-9">
- <?= GridView::widget([
+ <?= GridView::widget([
'dataProvider' => $dataProvider,
'filterModel' => $searchModel,
'columns' => [
],
['class' => 'yii\grid\ActionColumn'],
],
- ]); ?>
+ ]) ?>
</div>
</div>
-</div>
-
-<!-- Стили -->
-<style>
- .tree-container {
- font-family: Roboto, "Helvetica Neue", Helvetica, Arial, sans-serif;
- line-height: 1.6;
- }
-
- .tree-level-0 {
- margin-left: 0;
- }
-
- .tree-level-1 {
- margin-left: 20px;
- }
-
- .tree-level-2 {
- margin-left: 40px;
- }
-
- .category-title {
- font-weight: bold;
- cursor: pointer;
- padding: 8px 12px;
- background-color: #f4f4f4;
- border-radius: 4px;
- transition: background-color 0.3s ease;
- }
-
- .category-title:hover {
- background-color: #e0e0e0;
- }
-
- .category-description {
- color: #666;
- font-size: 0.9em;
- margin-top: 4px;
- }
-
- .article-link {
- display: block;
- padding: 6px 12px;
- text-decoration: none;
- color: #337ab7;
- border-radius: 4px;
- transition: background-color 0.3s ease, color 0.3s ease;
- }
-
- .article-link:hover {
- background-color: #e0e0e0;
- color: #23527c;
- }
- .article-description {
- color: #666;
- font-size: 0.9em;
- margin-top: 4px;
- }
-
- .subcategories {
- list-style-type: none;
- padding-left: 20px;
- transition: max-height 0.3s ease;
- overflow: hidden;
- }
-
- .subcategories.hidden {
- max-height: 0;
- }
-
- .subcategories.visible {
- margin-bottom: 20px;
- }
+ <style>
+ .tree-container {
+ font-family: Roboto, "Helvetica Neue", Helvetica, Arial, sans-serif;
+ line-height: 1.6;
+ }
+ .tree-level-0 {
+ margin-left: 0;
+ }
+ .tree-level-1 {
+ margin-left: 20px;
+ }
+ .tree-level-2 {
+ margin-left: 40px;
+ }
+ /* Категории */
+ .category-title {
+ font-weight: bold;
+ cursor: pointer;
+ padding: 8px 12px;
+ background-color: #f4f4f4;
+ border-radius: 4px;
+ transition: background-color 0.3s ease;
+ }
+ .category-title:hover {
+ background-color: #e0e0e0;
+ }
-</style>
+ /* Стрелочка */
+ .category-title.toggleable svg {
+ margin-left: 8px;
+ transition: transform 0.3s ease;
+ transform: rotate(0deg);
+ }
+ .category-title.toggleable.open svg {
+ transform: rotate(90deg);
+ }
-<!-- JavaScript для раскрывающегося дерева -->
-<script>
- document.addEventListener('DOMContentLoaded', function () {
- const categoryTitles = document.querySelectorAll('.category-title');
- categoryTitles.forEach(title => {
- title.addEventListener('click', function () {
- const parentLi = this.closest('li');
- const subcategories = parentLi.querySelector('.subcategories');
+ /* Скрытые подкатегории и статьи */
+ .subcategories.hidden {
+ display: none;
+ }
+ .subcategories.visible {
+ display: block;
+ }
- if (subcategories) {
- subcategories.classList.toggle('visible');
- subcategories.classList.toggle('hidden');
- }
+ /* Статьи */
+ .articles-list {
+ list-style-type: none;
+ padding-left: 20px;
+ }
+ .article-link {
+ display: block;
+ padding: 6px 12px;
+ text-decoration: none;
+ color: #337ab7;
+ border-radius: 4px;
+ transition: background-color 0.3s ease, color 0.3s ease;
+ }
+ .article-link:hover {
+ background-color: #e0e0e0;
+ color: #23527c;
+ }
+ .article-description {
+ color: #666;
+ font-size: 0.9em;
+ margin-top: 4px;
+ }
+ </style>
+ <script>
+ function copyToClipboard(text) {
+ navigator.clipboard.writeText(text).then(function() {
+ alert('Ссылка скопирована: ' + text);
+ }, function(err) {
+ console.error('Ошибка копирования: ', err);
+ });
+ }
+ document.addEventListener('DOMContentLoaded', function () {
+ const categoryTitles = document.querySelectorAll('.category-title.toggleable');
+ categoryTitles.forEach(title => {
+ title.addEventListener('click', function () {
+ const parentLi = this.closest('li');
+ const subcategories = parentLi.querySelector('.subcategories');
+
+ if (subcategories) {
+ subcategories.classList.toggle('visible');
+ subcategories.classList.toggle('hidden');
+ this.classList.toggle('open');
+ }
+ });
});
});
- });
-</script>
-
+ </script>
--- /dev/null
+<?php
+use yii\helpers\Html;
+use yii\helpers\Url;
+
+/** @var array $tree */
+/** @var int $level */
+
+if (!isset($level)) {
+ $level = 0;
+}
+if (empty($tree)) {
+ return '';
+}
+?>
+
+<ul class="tree-level-<?= $level ?>">
+ <?php foreach ($tree as $node): ?>
+ <?php
+ $category = $node['category'];
+ $articles = $node['articles'];
+ $children = $node['children'];
+ $hasContent = !empty($articles) || !empty($children);
+ ?>
+ <li>
+ <!-- Категория с кнопкой раскрытия/сворачивания -->
+ <div
+ class="category-title open <?= $hasContent ? 'toggleable' : '' ?>"
+ data-has-content="<?= $hasContent ? 'true' : 'false' ?>"
+ >
+ <?= Html::encode($category->title) ?>
+ <?php if ($hasContent): ?>
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-chevron-right" viewBox="0 0 16 16">
+ <path fill-rule="evenodd" d="M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708"/>
+ </svg>
+ <?php endif; ?>
+ </div>
+
+
+ <?php if (!empty($category->description)): ?>
+ <div class="category-description"><?= Html::encode($category->description) ?></div>
+ <?php endif; ?>
+
+ <?php if ($hasContent): ?>
+ <ul class="subcategories visible">
+ <!-- Статьи -->
+ <?php if (!empty($articles)): ?>
+ <ul class="articles-list">
+ <?php foreach ($articles as $article): ?>
+ <li>
+ <?= Html::a(Html::encode($article->title), Url::to([
+ 'view',
+ 'parent_cat_slug' => $category->slug,
+ 'article_slug' => $article->slug
+ ], true), ['class' => 'article-link']) ?>
+ </li>
+ <?php endforeach; ?>
+ </ul>
+ <?php endif; ?>
+
+ <!-- Подкатегории -->
+ <?php if (!empty($children)): ?>
+ <?= $this->render('tree', ['tree' => $children, 'level' => $level + 1]) ?>
+ <?php endif; ?>
+ </ul>
+ <?php endif; ?>
+ </li>
+ <?php endforeach; ?>
+</ul>
use yii\helpers\Html;
use yii\helpers\Url;
use yii\web\YiiAsset;
-use yii\widgets\DetailView;
+
use yii\web\View;
use yii\bootstrap5\Button;
use yii\bootstrap5\Alert;
/** @var View $this */
/** @var yii_app\records\WikiArticle $model */
+/** @var array $tree */
+
$this->title = $model->title;
$this->params['breadcrumbs'][] = ['label' => 'Wiki Articles', 'url' => ['index']];
YiiAsset::register($this);
?>
-<div class="wiki-article-view p-4 bg-white rounded shadow-sm">
- <div class="d-flex justify-content-between align-items-center mb-3">
- <h1 class="mb-0">
- <?= Html::encode($this->title) ?>
- <button class="btn btn-outline-secondary btn-sm" onclick="copyToClipboard('<?= $articleUrl ?>')">
- <svg id="Layer_1" xmlns="http://www.w3.org/2000/svg"
- xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
- viewBox="0 0 115.77 122.88" style="enable-background:new 0 0 115.77 122.88" xml:space="preserve">
+<div class="wiki-article-view p-4 ">
+ <?= Html::a('Назад', ['index'], ['class' => 'btn btn-secondary mb-4']) ?>
+ <div class="row">
+ <div class="col-3 tree-container">
+ <?= $this->render('tree', ['tree' => $tree]) ?>
+ </div>
+ <div class="col-9">
+ <div class="d-flex justify-content-between align-items-center mb-3">
+ <h1 class="mb-0">
+ <?= Html::encode($this->title) ?>
+ <button class="btn btn-outline-secondary btn-sm" onclick="copyToClipboard('<?= $articleUrl ?>')">
+ <svg id="Layer_1" xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 115.77 122.88" style="enable-background:new 0 0 115.77 122.88" xml:space="preserve">
<style type="text/css">.st0{fill-rule:evenodd;clip-rule:evenodd;}</style>
- <g><path class="st0" d="M89.62,13.96v7.73h12.19h0.01v0.02c3.85,0.01,7.34,1.57,9.86,
+ <g><path class="st0" d="M89.62,13.96v7.73h12.19h0.01v0.02c3.85,0.01,7.34,1.57,9.86,
4.1c2.5,2.51,4.06,5.98,4.07,9.82h0.02v0.02 v73.27v0.01h-0.02c-0.01,3.84-1.57,
7.33-4.1,9.86c-2.51,2.5-5.98,4.06-9.82,4.07v0.02h-0.02h-61.7H40.1v-0.02
c-3.84-0.01-7.34-1.57-9.86-4.1c-2.5-2.51-4.06-5.98-4.07-9.82h-0.02v-0.02V92.51H13.96h-0.01v-0.02c-3.84-0.01-7.34-1.57-9.86-4.1 c-2.5-2.51-4.06-5.98-4.07-9.82H0v-0.02V13.96v-0.01h0.02c0.01-3.85,1.58-7.34,4.1-9.86c2.51-2.5,5.98-4.06,9.82-4.07V0h0.02h61.7 h0.01v0.02c3.85,0.01,7.34,1.57,9.86,4.1c2.5,2.51,4.06,5.98,4.07,9.82h0.02V13.96L89.62,13.96z M79.04,21.69v-7.73v-0.02h0.02 c0-0.91-0.39-1.75-1.01-2.37c-0.61-0.61-1.46-1-2.37-1v0.02h-0.01h-61.7h-0.02v-0.02c-0.91,0-1.75,0.39-2.37,1.01 c-0.61,0.61-1,1.46-1,2.37h0.02v0.01v64.59v0.02h-0.02c0,0.91,0.39,1.75,1.01,2.37c0.61,0.61,1.46,1,2.37,1v-0.02h0.01h12.19V35.65 v-0.01h0.02c0.01-3.85,1.58-7.34,4.1-9.86c2.51-2.5,5.98-4.06,9.82-4.07v-0.02h0.02H79.04L79.04,21.69z M105.18,108.92V35.65v-0.02 h0.02c0-0.91-0.39-1.75-1.01-2.37c-0.61-0.61-1.46-1-2.37-1v0.02h-0.01h-61.7h-0.02v-0.02c-0.91,0-1.75,0.39-2.37,1.01 c-0.61,0.61-1,1.46-1,2.37h0.02v0.01v73.27v0.02h-0.02c0,0.91,0.39,1.75,1.01,2.37c0.61,0.61,1.46,1,2.37,1v-0.02h0.01h61.7h0.02 v0.02c0.91,0,1.75-0.39,2.37-1.01c0.61-0.61,1-1.46,1-2.37h-0.02V108.92L105.18,108.92z"/>
- </g></svg>
- </button>
- </h1>
- <?= Html::a('Назад', ['index'], ['class' => 'btn btn-secondary']) ?>
- </div>
+ </g></svg>
+ </button>
+ </h1>
- <div class="text-muted mb-3">
- <strong>Автор:</strong> <?= Html::encode($model->created_by) ?> |
- <strong>Дата создания:</strong> <?= date('d.m.Y H:i', strtotime($model->created_at)) ?>
- </div>
+ </div>
+
+ <div class="text-muted mb-3">
+ <strong>Автор:</strong> <?= Html::encode($model->created_by) ?> |
+ <strong>Дата создания:</strong> <?= date('d.m.Y H:i', strtotime($model->created_at)) ?>
+ </div>
+
+ <div class="article-content border p-3 rounded bg-light">
+ <?= nl2br($model->content) ?>
+ </div>
+ <?php if ($user->group_id == 81) : ?>
+ <div class="mt-4">
+ <?= Html::a('Редактировать', ['update', 'id' => $model->id], ['class' => 'btn btn-primary']) ?>
+ <?= Html::a('Удалить', ['delete', 'id' => $model->id], [
+ 'class' => 'btn btn-danger',
+ 'data' => [
+ 'confirm' => 'Вы уверены, что хотите удалить статью?',
+ 'method' => 'post',
+ ],
+ ]) ?>
+ </div>
+ <?php endif; ?>
+
+ </div>
- <div class="article-content border p-3 rounded bg-light">
- <?= nl2br($model->content) ?>
- </div>
- <?php if ($user->group_id == 81) : ?>
- <div class="mt-4">
- <?= Html::a('Редактировать', ['update', 'id' => $model->id], ['class' => 'btn btn-primary']) ?>
- <?= Html::a('Удалить', ['delete', 'id' => $model->id], [
- 'class' => 'btn btn-danger',
- 'data' => [
- 'confirm' => 'Вы уверены, что хотите удалить статью?',
- 'method' => 'post',
- ],
- ]) ?>
</div>
- <?php endif; ?>
+
+
</div>
+<style>
+ .tree-container {
+ font-family: Roboto, "Helvetica Neue", Helvetica, Arial, sans-serif;
+ line-height: 1.6;
+ }
+ .tree-level-0 {
+ margin-left: 0;
+ }
+ .tree-level-1 {
+ margin-left: 20px;
+ }
+ .tree-level-2 {
+ margin-left: 40px;
+ }
+ /* Категории */
+ .category-title {
+ font-weight: bold;
+ cursor: pointer;
+ padding: 8px 12px;
+ background-color: #f4f4f4;
+ border-radius: 4px;
+ transition: background-color 0.3s ease;
+ }
+ .category-title:hover {
+ background-color: #e0e0e0;
+ }
+
+ /* Стрелочка */
+ .category-title.toggleable svg {
+ margin-left: 8px;
+ transition: transform 0.3s ease;
+ transform: rotate(0deg);
+ }
+ .category-title.toggleable.open svg {
+ transform: rotate(90deg);
+ }
+
+ /* Скрытые подкатегории и статьи */
+ .subcategories.hidden {
+ display: none;
+ }
+ .subcategories.visible {
+ display: block;
+ }
+
+ /* Статьи */
+ .articles-list {
+ list-style-type: none;
+ padding-left: 20px;
+ }
+ .article-link {
+ display: block;
+ padding: 6px 12px;
+ text-decoration: none;
+ color: #337ab7;
+ border-radius: 4px;
+ transition: background-color 0.3s ease, color 0.3s ease;
+ }
+ .article-link:hover {
+ background-color: #e0e0e0;
+ color: #23527c;
+ }
+ .article-description {
+ color: #666;
+ font-size: 0.9em;
+ margin-top: 4px;
+ }
+</style>
<script>
function copyToClipboard(text) {
navigator.clipboard.writeText(text).then(function() {
console.error('Ошибка копирования: ', err);
});
}
+ document.addEventListener('DOMContentLoaded', function () {
+ const categoryTitles = document.querySelectorAll('.category-title.toggleable');
+ categoryTitles.forEach(title => {
+ title.addEventListener('click', function () {
+ const parentLi = this.closest('li');
+ const subcategories = parentLi.querySelector('.subcategories');
+
+ if (subcategories) {
+ subcategories.classList.toggle('visible');
+ subcategories.classList.toggle('hidden');
+ this.classList.toggle('open');
+ }
+ });
+ });
+ });
</script>