Forráskód Böngészése

Fixed Articles Logo issues

BrettonYe 1 éve
szülő
commit
bebc385ec0

+ 1 - 0
.gitignore

@@ -1,6 +1,7 @@
 /.phpunit.cache
 /node_modules
 /public/vendor
+/public/upload
 /storage/*.key
 /vendor
 .env

+ 30 - 26
app/Http/Controllers/Admin/ArticleController.php

@@ -17,31 +17,34 @@ use Str;
 class ArticleController extends Controller
 {
     public function index(Request $request)
-    { // 文章列表
+    {
+        // 文章列表
         $categories = Article::whereNotNull('category')->distinct()->get('category');
         $articles = Article::query();
+
         foreach (['id', 'category', 'language', 'type'] as $field) {
             $request->whenFilled($field, function ($value) use ($articles, $field) {
                 $articles->where($field, $value);
             });
         }
+
         $articles = $articles->latest()->orderByDesc('sort')->paginate()->appends($request->except('page'));
 
         return view('admin.article.index', compact('articles', 'categories'));
     }
 
-    public function store(ArticleRequest $request)
-    { // 添加文章
+    public function store(ArticleRequest $request): RedirectResponse
+    {
+        // 添加文章
         $data = $request->validated();
-        // LOGO
+
         try {
             if ($data['type'] !== '4' && $request->hasFile('logo')) {
                 $path = $this->fileUpload($request->file('logo'));
-                if (is_string($path)) {
-                    $data['logo'] = $path;
-                } else {
-                    return $path;
+                if ($path === false) {
+                    return redirect()->back()->withInput()->withErrors('Logo存储失败');
                 }
+                $data['logo'] = $path;
             }
 
             if ($article = Article::create($data)) {
@@ -56,50 +59,50 @@ class ArticleController extends Controller
         return redirect()->back()->withInput()->withErrors('添加失败');
     }
 
-    public function fileUpload(UploadedFile $file)
-    { // 图片上传
+    public function fileUpload(UploadedFile $file): string|bool
+    {
         $fileName = Str::random(8).time().'.'.$file->getClientOriginalExtension();
 
-        if (! $file->storeAs('public', $fileName)) {
-            return redirect()->back()->withInput()->withErrors('Logo存储失败');
-        }
-
-        return 'upload/'.$fileName;
+        return $file->storeAs('public', $fileName) ? 'upload/'.$fileName : false;
     }
 
     public function create()
-    { // 添加文章页面
+    {
+        // 添加文章页面
         $categories = Article::whereNotNull('category')->distinct()->get('category');
 
         return view('admin.article.info', compact('categories'));
     }
 
     public function show(Article $article)
-    { // 文章页面
+    {
+        // 文章页面
         $article->content = (new ArticleService($article))->getContent();
 
         return view('admin.article.show', compact('article'));
     }
 
     public function edit(Article $article)
-    { // 编辑文章页面
+    {
+        // 编辑文章页面
         $categories = Article::whereNotNull('category')->distinct()->get('category');
 
         return view('admin.article.info', compact('article', 'categories'));
     }
 
     public function update(ArticleRequest $request, Article $article): RedirectResponse
-    { // 编辑文章
+    {
+        // 编辑文章
         $data = $request->validated();
-        $data['logo'] = $data['logo'] ?? null;
-        // LOGO
+
         if ($data['type'] !== '4' && $request->hasFile('logo')) {
             $path = $this->fileUpload($request->file('logo'));
-            if (is_string($path)) {
-                $data['logo'] = $path;
-            } else {
-                return $path;
+            if ($path === false) {
+                return redirect()->back()->withInput()->withErrors('Logo存储失败');
             }
+            $data['logo'] = $path;
+        } elseif (! $request->has('logo')) {
+            $data['logo'] = $article->logo;
         }
 
         if ($article->update($data)) {
@@ -110,7 +113,8 @@ class ArticleController extends Controller
     }
 
     public function destroy(Article $article): JsonResponse
-    { // 删除文章
+    {
+        // 删除文章
         try {
             $article->delete();
         } catch (Exception $e) {

+ 1 - 1
app/Http/Requests/Admin/ArticleRequest.php

@@ -14,7 +14,7 @@ class ArticleRequest extends FormRequest
             'language' => 'required|string',
             'category' => 'nullable|string',
             'sort' => 'nullable|numeric',
-            'logo' => 'nullable|exclude_if:type,4|image',
+            'logo' => 'nullable|'.($this->hasFile('logo') ? 'image' : 'url'),
             'content' => 'required',
         ];
     }

+ 1 - 1
config/filesystems.php

@@ -70,7 +70,7 @@ return [
     */
 
     'links' => [
-        public_path('storage') => storage_path('app/public'),
+        public_path('upload') => storage_path('app/public'),
     ],
 
 ];

+ 1 - 0
public/assets/custom/articles.min.css

@@ -0,0 +1 @@
+ol>li{margin-bottom:8px}.btn-group:hover>.dropdown-menu{display:block;margin-top:0}.panel .dropdown-item:hover{position:relative;padding-left:16px}.panel .dropdown-item:focus{outline-color:#fff}.panel .dropdown-item:hover::before{content:'';border-left:6px solid #77d9ed;border-top:5px solid transparent;border-bottom:5px solid transparent;position:absolute;left:6px;top:50%;transform:translateY(-50%)}.panel .dropdown-divider{overflow:revert;position:relative;z-index:-2;text-align:center}.panel .dropdown-divider::before{content:attr(data-content);position:absolute;transform:translate(-50%,-50%);background-color:#fff;padding:0 2px;font-size:12px;font-weight:300;z-index:-1;white-space:nowrap}.apple-account-list{list-style-type:none;padding-top:10px}.apple-account-item{display:flex;justify-content:space-between;margin-bottom:6px}.account-password{margin-left:20px}.account-notes li{margin-bottom:5px}

+ 1 - 0
public/assets/custom/tinymce.min.css

@@ -0,0 +1 @@
+body{padding-top:10px}

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 0 - 0
public/assets/global/vendor/dropify/dropify.min.js


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 0 - 1
public/assets/global/vendor/media-match/media.match.min.js


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 0 - 5
public/assets/global/vendor/respond/respond.min.js


+ 107 - 64
resources/views/admin/article/info.blade.php

@@ -11,15 +11,15 @@
                     {{ isset($article) ? trans('admin.action.edit_item', ['attribute' => trans('model.article.attribute')]) : trans('admin.action.add_item', ['attribute' => trans('model.article.attribute')]) }}
                 </h2>
             </div>
-            @if($errors->any())
-                <x-alert type="danger" :message="$errors->all()"/>
+            @if ($errors->any())
+                <x-alert type="danger" :message="$errors->all()" />
             @endif
             @if (Session::has('successMsg'))
-                <x-alert type="success" :message="Session::pull('successMsg')"/>
+                <x-alert type="success" :message="Session::pull('successMsg')" />
             @endif
             <div class="panel-body">
-                <form action="{{ isset($article) ? route('admin.article.update', $article) : route('admin.article.store')}}" class="form-horizontal" enctype="multipart/form-data"
-                      method="post">@csrf
+                <form class="form-horizontal" action="{{ isset($article) ? route('admin.article.update', $article) : route('admin.article.store') }}"
+                      enctype="multipart/form-data" method="post">@csrf
                     @isset($article)
                         @method('PUT')
                     @endisset
@@ -27,11 +27,11 @@
                         <label class="col-form-label col-md-2" for="type"> {{ trans('model.common.type') }} </label>
                         <div class="col-md-10 d-flex align-items-center">
                             <div class="radio-custom radio-primary radio-inline">
-                                <input type="radio" name="type" value="1" checked/>
+                                <input name="type" type="radio" value="1" checked />
                                 <label for="type">{{ trans('admin.article.type.knowledge') }}</label>
                             </div>
                             <div class="radio-custom radio-primary radio-inline">
-                                <input type="radio" name="type" value="2"/>
+                                <input name="type" type="radio" value="2" />
                                 <label for="type">{{ trans('admin.article.type.announcement') }}</label>
                             </div>
                         </div>
@@ -39,21 +39,21 @@
                     <div class="form-group row">
                         <label class="col-form-label col-md-2" for="title"> {{ trans('validation.attributes.title') }} </label>
                         <div class="col-md-4">
-                            <input type="text" class="form-control" name="title" id="title" autofocus required/>
+                            <input class="form-control" id="title" name="title" type="text" autofocus required />
                         </div>
                     </div>
                     <div class="form-group row article">
                         <label class="col-form-label col-md-2" for="category"> {{ trans('model.article.category') }} </label>
                         <div class="col-md-4">
-                            @if(isset($categories))
-                                <input type="text" class="form-control" list="category_list" id="category" name="category"/>
+                            @if (isset($categories))
+                                <input class="form-control" id="category" name="category" type="text" list="category_list" />
                                 <datalist id="category_list">
-                                    @foreach($categories as $category)
-                                        <option value="{{$category->category}}">{{$category->category}}</option>
+                                    @foreach ($categories as $category)
+                                        <option value="{{ $category->category }}">{{ $category->category }}</option>
                                     @endforeach
                                 </datalist>
                             @else
-                                <input type="text" class="form-control" id="category" name="category"/>
+                                <input class="form-control" id="category" name="category" type="text" />
                             @endif
                             <span class="text-help"> {{ trans('admin.article.category_hint') }} </span>
                         </div>
@@ -61,11 +61,11 @@
                     <div class="form-group row">
                         <label class="col-form-label col-md-2" for="language"> {{ trans('model.article.language') }} </label>
                         <div class="col-md-4">
-                            <select class="form-control" data-plugin="selectpicker" id="language" name="language" data-style="btn-outline btn-primary">
+                            <select class="form-control" id="language" name="language" data-plugin="selectpicker" data-style="btn-outline btn-primary">
                                 @foreach (config('common.language') as $key => $value)
-                                    <option value="{{$key}}">
-                                        <i class="fi fi-{{$value[1]}}" aria-hidden="true"></i>
-                                        <span style="padding: inherit;">{{$value[0]}}</span>
+                                    <option value="{{ $key }}">
+                                        <i class="fi fi-{{ $value[1] }}" aria-hidden="true"></i>
+                                        <span style="padding: inherit;">{{ $value[0] }}</span>
                                     </option>
                                 @endforeach
                             </select>
@@ -74,15 +74,17 @@
                     <div class="form-group row article">
                         <label class="col-form-label col-md-2" for="sort"> {{ trans('model.common.sort') }} </label>
                         <div class="col-md-2">
-                            <input type="number" class="form-control" name="sort" id="sort" value="10" min="0" max="255" required/>
+                            <input class="form-control" id="sort" name="sort" type="number" value="10" min="0" max="255" required />
                             <span class="text-help"> {{ trans('admin.sort_asc') }} </span>
                         </div>
                     </div>
+
                     <div class="form-group row">
                         <label class="col-form-label col-md-2" for="logo"> {{ trans('model.article.logo') }} </label>
-                        <div class="col-md-4" id="logoUpload">
-                            <input type="file" id="logo" name="logo" data-plugin="dropify" data-default-file="{{asset($article->logo ?? '/assets/images/default.png')}}"/>
-                            <span class="text-help"> {{ trans('admin.article.logo_hint') }} </span>
+                        <div class="col-md-4">
+                            <input id="logo" name="logo" data-plugin="dropify"
+                                   data-default-file="{{ asset($article->logo ?? '/assets/images/default.png') }}" type="file" />
+                            <input class="form-control" id="logoUrl" type="text" placeholder="{{ trans('admin.article.logo_placeholder') }}">
                         </div>
                     </div>
                     <div class="form-group row">
@@ -97,8 +99,8 @@
                     </div>
                     <div class="form-actions text-right">
                         <div class="btn-group">
-                            <a href="{{route('admin.article.index')}}" class="btn btn-danger">{{ trans('common.back') }}</a>
-                            <button type="submit" class="btn btn-success">{{ trans('common.submit') }}</button>
+                            <a class="btn btn-danger" href="{{ route('admin.article.index') }}">{{ trans('common.back') }}</a>
+                            <button class="btn btn-success" type="submit">{{ trans('common.submit') }}</button>
                         </div>
                     </div>
                 </form>
@@ -113,50 +115,91 @@
     <script src="/assets/global/vendor/bootstrap-select/bootstrap-select.min.js"></script>
     <script src="/assets/global/js/Plugin/bootstrap-select.js"></script>
     <script>
-        @isset($article)
         $(document).ready(function() {
-          $("input[name='type'][value='{{$article->type}}']").click();
-          $('#title').val(@json($article->title));
-          $('#category').val(@json($article->category));
-          $('#language').selectpicker('val', '{{$article->language}}');
-          $('#sort').val('{{$article->sort}}');
-        });
-        @endisset
+            const $type = $('input:radio[name="type"]')
+            const $logo = $('#logo')
+            const $logoUrl = $('#logoUrl')
+            let logoCleared = false
+
+            // 初始化 TinyMCE
+            tinymce.init({
+                selector: 'textarea', // change this value according to your HTML
+                license_key: 'gpl',
+                plugins: 'advlist autolink autoresize autosave code emoticons help image importcss link lists media ' +
+                    'preview quickbars searchreplace table visualblocks visualchars wordcount',
+                toolbar: 'restoredraft undo redo | styles | bold italic forecolor backcolor emoticons| alignleft aligncenter alignright alignjustify' +
+                    ' | bullist numlist outdent indent | link image media',
+                menubar: 'view edit insert format table tools help',
+                link_default_target: '_blank',
+                quickbars_insert_toolbar: 'quicktable image media',
+                quickbars_selection_toolbar: 'bold italic underline | blocks | bullist numlist | blockquote quicklink',
+                extended_valid_elements: 'button[onclick|class],i[class|aria-hidden]', // Allow more attributes for <a>
+                language: '{{ app()->getLocale() }}',
+                content_css: [
+                    '/assets/bundle/app.min.css',
+                    '/assets/global/fonts/font-awesome/css/all.min.css',
+                    '/assets/global/fonts/material-design/material-design.min.css',
+                    '/assets/global/fonts/web-icons/web-icons.min.css',
+                    'https://fonts.loli.net/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap',
+                    '/assets/custom/articles.min.css',
+                    '/assets/custom/tinymce.min.css'
+                ],
+                min_height: 500,
+                max_height: 800,
+            })
+
+            // 初始化已有文章数据
+            @isset($article)
+                $type.filter(`[value="${@json($article->type)}"]`).click()
+                $('#title').val(@json($article->title))
+                $('#category').val(@json($article->category))
+                $('#language').selectpicker('val', @json($article->language))
+                $('#sort').val(@json($article->sort))
+            @endisset
+
+            // 初始化 Dropify
+            const dropify = $logo.dropify().data('dropify')
+
+            // 类型切换处理
+            $type.on('change', function() {
+                $('.article').toggle($(this).val() === '1')
+            })
 
-        tinymce.init({
-          selector: 'textarea',  // change this value according to your HTML
-          plugins: 'advlist autolink autoresize autosave code emoticons help image importcss link lists media ' +
-              'preview quickbars searchreplace table visualblocks visualchars wordcount',
-          toolbar: 'restoredraft undo redo | styles | bold italic forecolor backcolor emoticons| alignleft aligncenter alignright alignjustify' +
-              ' | bullist numlist outdent indent | link image media',
-          menubar: 'view edit insert format table tools help',
-          link_default_target: '_blank',
-          quickbars_insert_toolbar: 'quicktable image media',
-          quickbars_selection_toolbar: 'bold italic underline | blocks | bullist numlist | blockquote quicklink',
-          extended_valid_elements: 'button[onclick|class],i[class|aria-hidden]', // Allow more attributes for <a>
-          language: '{{app()->getLocale()}}',
-          content_css: '/assets/bundle/app.min.css',
-          min_height: 500,
-          max_height: 800,
-          setup: function (editor) {
-            editor.on('PreProcess', function (e) {
-              // Allow href attribute to contain 'javascript:' protocol
-              tinymce.each(e.node.getElementsByTagName('a'), function (a) {
-                if (a.href && a.href.startsWith('javascript:')) {
-                  a.setAttribute('href', a.href);
+            // 添加 Dropify clear 事件处理
+            $logo.on('dropify.afterClear', function() {
+                logoCleared = true
+            })
+
+            // Logo URL 输入处理
+            $logoUrl.on('input', handleLogoUrlInput)
+
+            // 表单提交处理
+            $('form').on('submit', handleFormSubmit)
+
+            function handleLogoUrlInput() {
+                const imageUrl = $logoUrl.val()
+                if (imageUrl) {
+                    updateDropifyPreview(imageUrl)
+                } else {
+                    dropify.resetPreview()
+                    dropify.clearElement()
                 }
-              });
-            });
-          }
-        });
+            }
 
-        $('input:radio[name=\'type\']').on('change', function() {
-          const article = $('.article');
-          if ($(this).val() === '1') {
-            article.show();
-          } else {
-            article.hide();
-          }
-        });
+            function updateDropifyPreview(imageUrl) {
+                dropify.settings.defaultFile = imageUrl
+                dropify.destroy()
+                $logo.dropify({
+                    defaultFile: imageUrl
+                }).data('dropify').init()
+            }
+
+            function handleFormSubmit(event) {
+                const logoUrl = $logoUrl.val()
+                if (logoUrl || (logoCleared && !$logo.val())) {
+                    $logo.attr('type', 'text').val(logoUrl || null)
+                }
+            }
+        })
     </script>
 @endsection

+ 71 - 67
resources/views/admin/article/show.blade.php

@@ -2,11 +2,7 @@
 @section('css')
     <link href="/assets/global/fonts/font-awesome/css/all.min.css" rel="stylesheet">
     <link href="/assets/global/fonts/material-design/material-design.min.css" rel="stylesheet">
-    <style>
-        ol > li {
-            margin-bottom: 8px;
-        }
-    </style>
+    <link href="/assets/custom/articles.min.css" rel="stylesheet">
 @endsection
 @section('content')
     <div class="page-content container-fluid">
@@ -14,13 +10,16 @@
             <div class="col-md-10 offset-md-1">
                 <div class="panel">
                     <div class="panel-heading">
-                        <h3 class="panel-title">{{$article->title}} {!! $article->category ?'<sub class="ml-20">'.$article->category.'</sub>':'' !!}</h3>
-                        <div class="panel-actions"><code>{{$article->created_at}}</code></div>
+                        <h3 class="panel-title">
+                            <img class="mr-10" src="{{ asset($article->logo) }}" alt="logo" style="height: 32px" />{{ $article->title }}
+                            {!! $article->category ? '<sub class="ml-20">' . $article->category . '</sub>' : '' !!}
+                        </h3>
+                        <div class="panel-actions"><code>{{ $article->created_at }}</code></div>
                     </div>
                     <div class="panel-body pt-0 pb-60">
                         <div style="padding: 10px;">{!! $article->content !!}</div>
                         <div class="panel-footer text-right">
-                            <a href="{{route('admin.article.index')}}" class="btn btn-primary">{{ trans('common.back') }}</a>
+                            <a class="btn btn-primary" href="{{ route('admin.article.index') }}">{{ trans('common.back') }}</a>
                         </div>
                     </div>
                 </div>
@@ -31,70 +30,75 @@
 @section('javascript')
     <script src="/assets/custom/clipboardjs/clipboard.min.js"></script>
     <script>
-      const clipboard = new ClipboardJS('.mt-clipboard');
+        const clipboard = new ClipboardJS(".mt-clipboard");
 
-      function fetch(id) {
-        if (!document.getElementById('article_B' + id).innerHTML) {
-          $.ajax({
-            method: 'GET',
-            url: '{{route('article', '')}}/' + id,
-            beforeSend: function() {
-              $('#loading_article').show();
-            },
-            success: function(ret) {
-              document.getElementById('article_B' + id).innerHTML = ret.content;
-            },
-            complete: function() {
-              $('#loading_article').hide();
-            },
-          });
-        }
+        function fetch(id) {
+            if (!document.getElementById("article_B" + id).innerHTML) {
+                $.ajax({
+                    method: "GET",
+                    url: '{{ route('article', '') }}/' + id,
+                    beforeSend: function() {
+                        $("#loading_article").show();
+                    },
+                    success: function(ret) {
+                        document.getElementById("article_B" + id).innerHTML = ret.content;
+                    },
+                    complete: function() {
+                        $("#loading_article").hide();
+                    }
+                });
+            }
 
-        return false;
-      }
+            return false;
+        }
 
-      // 更换订阅地址
-      function exchangeSubscribe() {
-        swal.fire({
-          title: '{{ trans('common.warning') }}',
-          text: '{{ trans('user.subscribe.exchange_warning') }}',
-          icon: 'warning',
-          showCancelButton: true,
-          cancelButtonText: '{{ trans('common.close') }}',
-          confirmButtonText: '{{ trans('common.confirm') }}',
-        }).then((result) => {
-          if (result.value) {
-            $.post('{{route('changeSub')}}', {_token: '{{csrf_token()}}'}, function(ret) {
-              if (ret.status === 'success') {
-                swal.fire({
-                  title: ret.message,
-                  icon: 'success',
-                  timer: 1000,
-                  showConfirmButton: false,
-                }).then(() => window.location.reload());
-              } else {
-                swal.fire({title: ret.message, icon: 'error'}).then(() => window.location.reload());
-              }
+        // 更换订阅地址
+        function exchangeSubscribe() {
+            swal.fire({
+                title: '{{ trans('common.warning') }}',
+                text: '{{ trans('user.subscribe.exchange_warning') }}',
+                icon: "warning",
+                showCancelButton: true,
+                cancelButtonText: '{{ trans('common.close') }}',
+                confirmButtonText: '{{ trans('common.confirm') }}'
+            }).then((result) => {
+                if (result.value) {
+                    $.post('{{ route('changeSub') }}', {
+                        _token: '{{ csrf_token() }}'
+                    }, function(ret) {
+                        if (ret.status === "success") {
+                            swal.fire({
+                                title: ret.message,
+                                icon: "success",
+                                timer: 1000,
+                                showConfirmButton: false
+                            }).then(() => window.location.reload());
+                        } else {
+                            swal.fire({
+                                title: ret.message,
+                                icon: "error"
+                            }).then(() => window.location.reload());
+                        }
+                    });
+                }
             });
-          }
-        });
-      }
+        }
 
-      clipboard.on('success', function() {
-        swal.fire({
-          title: '{{ trans('common.copy.success') }}',
-          icon: 'success',
-          timer: 1300,
-          showConfirmButton: false,
+        clipboard.on("success", function() {
+            swal.fire({
+                title: '{{ trans('common.copy.success') }}',
+                icon: "success",
+                timer: 1300,
+                showConfirmButton: false
+            });
         });
-      });
-      clipboard.on('error', function() {
-        swal.fire({
-          title: '{{ trans('common.copy.failed') }}',
-          icon: 'error',
-          timer: 1500,
-          showConfirmButton: false,
+        clipboard.on("error", function() {
+            swal.fire({
+                title: '{{ trans('common.copy.failed') }}',
+                icon: "error",
+                timer: 1500,
+                showConfirmButton: false
+            });
         });
-      });
     </script>
-@endsection
+@endsection

+ 119 - 118
resources/views/user/knowledge.blade.php

@@ -2,15 +2,7 @@
 @section('css')
     <link href="/assets/global/fonts/font-awesome/css/all.min.css" rel="stylesheet">
     <link href="/assets/global/fonts/material-design/material-design.min.css" rel="stylesheet">
-    <style>
-        ol > li {
-            margin-bottom: 8px;
-        }
-
-        .panel-group .panel-title {
-            font-size: 20px;
-        }
-    </style>
+    <link href="/assets/custom/articles.min.css" rel="stylesheet">
 @endsection
 @section('content')
     <div class="page-header">
@@ -19,69 +11,68 @@
     <div class="page-content container-fluid">
         @if ($knowledges->isNotEmpty())
             <div class="row">
-                <div class="col-xxl-2 col-lg-4 col-md-12">
+                <div class="offset-xxl-1 col-xxl-2 col-xl-3 offset-lg-0 col-lg-4 offset-sm-2 col-sm-8">
                     <div class="panel">
-                        <div class="panel-body">
-                            <div class="list-group" role="tablist">
-                                @foreach($knowledges as $category => $articles)
-                                    @php $str = string_urlsafe($category) @endphp
-                                    <a class="list-group-item list-group-item-action @if($loop->first) active @endif" data-toggle="tab"
-                                       href="#{{$str}}" aria-controls="{{$str}}" role="tab">{{$category}}</a>
-                                @endforeach
-                            </div>
+                        <div class="list-group" role="tablist">
+                            @foreach ($knowledges as $category => $articles)
+                                @php $str = string_urlsafe($category) @endphp
+                                <a class="list-group-item @if ($loop->first) list-group-item-action active @endif" data-toggle="tab"
+                                   href="#{{ $str }}" role="tab" aria-controls="{{ $str }}">{{ $category }}</a>
+                            @endforeach
                         </div>
                     </div>
                 </div>
-                <div class="col-xxl-8 col-lg-8 col-md-12">
+                <div class="col-xxl-8 col-xl-9 col-lg-8 col-md-12">
                     <div class="panel">
                         <div class="panel-heading progress" id="loading_article" style="display: none;">
-                            <div class="progress-bar progress-bar-striped active" aria-valuenow="100" aria-valuemin="0" aria-valuemax="100" style="width: 100%" role="progressbar">
+                            <div class="progress-bar progress-bar-striped active" role="progressbar" aria-valuenow="100" aria-valuemin="0" aria-valuemax="100"
+                                 style="width: 100%">
                                 <span class="sr-only">100% Complete</span>
                             </div>
                         </div>
                         <div class="panel-body pt-30">
                             <div class="tab-content">
-                                @foreach($knowledges as $category => $articles)
-                                    <div class="tab-pane animation-fade @if($loop->first) active @endif" id="{{string_urlsafe($category)}}" role="tabpanel">
-                                        <div class="panel-group panel-group-simple panel-group-continuous" id="category_{{$loop->iteration}}" aria-multiselectable="true"
-                                             role="tablist">
+                                @foreach ($knowledges as $category => $articles)
+                                    <div class="tab-pane animation-fade @if ($loop->first) active @endif" id="{{ string_urlsafe($category) }}"
+                                         role="tabpanel">
+                                        <div class="panel-group panel-group-simple panel-group-continuous" role="tablist" aria-multiselectable="true">
                                             @if ($loop->first)
                                                 <div class="panel">
                                                     <div class="panel-heading" id="question_1" role="tab">
-                                                        <a class="panel-title cyan-600" aria-controls="answer_1" aria-expanded="true" data-toggle="collapse" href="#answer_1"
-                                                           data-parent="#category_{{$loop->iteration}}">
-                                                            <i class="icon wb-link" aria-hidden="true"></i>{{trans('user.subscribe.link')}}
+                                                        <a class="panel-title cyan-600" data-toggle="collapse" href="#answer_1" aria-controls="answer_1"
+                                                           aria-expanded="true">
+                                                            <i class="icon wb-link" aria-hidden="true"></i>{{ trans('user.subscribe.link') }}
                                                         </a>
                                                     </div>
-                                                    <div class="panel-collapse collapse show" id="answer_1" aria-labelledby="question_1" role="tabpanel">
+                                                    <div class="panel-collapse collapse show" id="answer_1" role="tabpanel" aria-labelledby="question_1">
                                                         <div class="panel-body">
-                                                            @if($subStatus)
-                                                                <x-alert type="warning" :message="trans('user.subscribe.tips')"/>
+                                                            @if ($subStatus)
+                                                                <x-alert type="warning" :message="trans('user.subscribe.tips')" />
                                                                 <div class="input-group">
-                                                                    <input type="text" class="form-control" id="sub_link" value="{{$subUrl}}"/>
+                                                                    <input class="form-control" id="sub_link" type="text" value="{{ $subUrl }}" />
                                                                     <div class="input-group-btn btn-group" role="group">
-                                                                        @if(count($subType) > 1)
+                                                                        @if (count($subType) > 1)
                                                                             <div class="btn-group" role="group">
-                                                                                <button type="button" class="btn btn-primary dropdown-toggle" id="sublink" data-toggle="dropdown"
-                                                                                        aria-expanded="false">
+                                                                                <button class="btn btn-primary dropdown-toggle" id="sublink"
+                                                                                        data-toggle="dropdown" type="button" aria-expanded="false">
                                                                                     {{ __('user.subscribe.custom') }}
                                                                                 </button>
-                                                                                <div class="dropdown-menu" aria-labelledby="sublink" role="menu">
-                                                                                    @if(in_array('ss', $subType, true))
-                                                                                        <a class="dropdown-item" onclick="linkManager('0')"
-                                                                                           role="menuitem">{{ __('user.subscribe.ss_only') }}</a>
+                                                                                <div class="dropdown-menu" role="menu" aria-labelledby="sublink">
+                                                                                    @if (in_array('ss', $subType, true))
+                                                                                        <a class="dropdown-item" role="menuitem"
+                                                                                           onclick="linkManager('0')">{{ __('user.subscribe.ss_only') }}</a>
                                                                                     @endif
-                                                                                    @if(in_array('ssr', $subType, true))
-                                                                                        <a class="dropdown-item" onclick="linkManager('1')"
-                                                                                           role="menuitem">{{ __('user.subscribe.ssr_only') }}</a>
+                                                                                    @if (in_array('ssr', $subType, true))
+                                                                                        <a class="dropdown-item" role="menuitem"
+                                                                                           onclick="linkManager('1')">{{ __('user.subscribe.ssr_only') }}</a>
                                                                                     @endif
-                                                                                    @if(in_array('v2', $subType, true))
-                                                                                        <a class="dropdown-item" onclick="linkManager('2')"
-                                                                                           role="menuitem">{{ __('user.subscribe.v2ray_only') }}</a>
+                                                                                    @if (in_array('v2', $subType, true))
+                                                                                        <a class="dropdown-item" role="menuitem"
+                                                                                           onclick="linkManager('2')">{{ __('user.subscribe.v2ray_only') }}</a>
                                                                                     @endif
-                                                                                    @if(in_array('trojan', $subType, true))
-                                                                                        <a class="dropdown-item" onclick="linkManager('3')"
-                                                                                           role="menuitem">{{ __('user.subscribe.trojan_only') }}</a>
+                                                                                    @if (in_array('trojan', $subType, true))
+                                                                                        <a class="dropdown-item" role="menuitem"
+                                                                                           onclick="linkManager('3')">{{ __('user.subscribe.trojan_only') }}</a>
                                                                                     @endif
                                                                                 </div>
                                                                             </div>
@@ -92,11 +83,11 @@
                                                                         <button class="btn btn-outline-info mt-clipboard" data-clipboard-action="copy"
                                                                                 data-clipboard-target="#sub_link">
                                                                             <i class="icon wb-copy" aria-hidden="true"></i>
-                                                                            {{trans('common.copy.attribute')}}</button>
+                                                                            {{ trans('common.copy.attribute') }}</button>
                                                                     </div>
                                                                 </div>
                                                             @else
-                                                                <x-alert type="danger" :message="__($subMsg)"/>
+                                                                <x-alert type="danger" :message="__($subMsg)" />
                                                             @endif
                                                         </div>
                                                     </div>
@@ -104,14 +95,19 @@
                                             @endif
                                             @foreach ($articles as $article)
                                                 <div class="panel">
-                                                    <div class="panel-heading" id="article_Q{{$article->id}}" role="tab">
-                                                        <a class="panel-title" onclick="fetch('{{$article->id}}')" aria-controls="article_A{{$article->id}}"
-                                                           aria-expanded="false"
-                                                           data-toggle="collapse" href="#article_A{{$article->id}}" data-parent="#category_{{$loop->parent->iteration}}">
-                                                            {{$article->title}}
+                                                    <div class="panel-heading"id="article_Q{{ $article->id }}">
+                                                        <a class="panel-title collapsed" data-toggle="collapse" href="#article_A{{ $article->id }}"
+                                                           role="tab" aria-controls="article_A{{ $article->id }}" aria-expanded="false"
+                                                           style="display: flex;" onclick="getArticle('{{ $article->id }}')">
+                                                            @isset($article->logo)
+                                                                <img class="mr-5" src="{{ asset($article->logo) }}" alt=""
+                                                                     style="height: 36px; align-self: center" loading="lazy" />
+                                                            @endisset
+                                                            <h4 style="margin-top: 11px">{{ $article->title }}</h4>
                                                         </a>
-                                                        <div class="panel-collapse" id="article_A{{$article->id}}" aria-labelledby="article_Q{{$article->id}}" role="tabpanel">
-                                                            <div class="panel-body" id="article_B{{$article->id}}"></div>
+                                                        <div class="panel-collapse collapse" id="article_A{{ $article->id }}" role="tabpanel"
+                                                             aria-labelledby="article_Q{{ $article->id }}">
+                                                            <div class="panel-body" id="load_article_{{ $article->id }}"></div>
                                                         </div>
                                                     </div>
                                                 </div>
@@ -135,75 +131,80 @@
     <script src="/assets/custom/jump-tab.js"></script>
     <script src="/assets/global/js/Plugin/asprogress.js"></script>
     <script>
-      const clipboard = new ClipboardJS('.mt-clipboard');
+        const clipboard = new ClipboardJS(".mt-clipboard");
 
-      function fetch(id) {
-        if (!document.getElementById('article_B' + id).innerHTML) {
-          $.ajax({
-            method: 'GET',
-            url: '{{route('article', '')}}/' + id,
-            beforeSend: function() {
-              $('#loading_article').show();
-            },
-            success: function(ret) {
-              document.getElementById('article_B' + id).innerHTML = ret.content;
-            },
-            complete: function() {
-              $('#loading_article').hide();
-            },
-          });
-        }
+        function getArticle(id) {
+            if (!document.getElementById("load_article_" + id).innerHTML) {
+                $.ajax({
+                    method: "GET",
+                    url: '{{ route('article', '') }}/' + id,
+                    beforeSend: function() {
+                        $("#loading_article").show();
+                    },
+                    success: function(ret) {
+                        document.getElementById("load_article_" + id).innerHTML = ret.content;
+                    },
+                    complete: function() {
+                        $("#loading_article").hide();
+                    }
+                });
+            }
 
-        return false;
-      }
+            return false;
+        }
 
-      function linkManager($type) {
-        $('#sub_link').val('{{$subUrl}}?type=' + $type);
-        return false;
-      }
+        function linkManager($type) {
+            $("#sub_link").val('{{ $subUrl }}?type=' + $type);
+            return false;
+        }
 
-      // 更换订阅地址
-      function exchangeSubscribe() {
-        swal.fire({
-          title: '{{trans('common.warning')}}',
-          text: '{{trans('user.subscribe.exchange_warning')}}',
-          icon: 'warning',
-          showCancelButton: true,
-          cancelButtonText: '{{trans('common.close')}}',
-          confirmButtonText: '{{trans('common.confirm')}}',
-        }).then((result) => {
-          if (result.value) {
-            $.post('{{route('changeSub')}}', {_token: '{{csrf_token()}}'}, function(ret) {
-              if (ret.status === 'success') {
-                swal.fire({
-                  title: ret.message,
-                  icon: 'success',
-                  timer: 1000,
-                  showConfirmButton: false,
-                }).then(() => window.location.reload());
-              } else {
-                swal.fire({title: ret.message, icon: 'error'}).then(() => window.location.reload());
-              }
+        // 更换订阅地址
+        function exchangeSubscribe() {
+            swal.fire({
+                title: '{{ trans('common.warning') }}',
+                text: '{{ trans('user.subscribe.exchange_warning') }}',
+                icon: "warning",
+                showCancelButton: true,
+                cancelButtonText: '{{ trans('common.close') }}',
+                confirmButtonText: '{{ trans('common.confirm') }}'
+            }).then((result) => {
+                if (result.value) {
+                    $.post('{{ route('changeSub') }}', {
+                        _token: '{{ csrf_token() }}'
+                    }, function(ret) {
+                        if (ret.status === "success") {
+                            swal.fire({
+                                title: ret.message,
+                                icon: "success",
+                                timer: 1000,
+                                showConfirmButton: false
+                            }).then(() => window.location.reload());
+                        } else {
+                            swal.fire({
+                                title: ret.message,
+                                icon: "error"
+                            }).then(() => window.location.reload());
+                        }
+                    });
+                }
             });
-          }
-        });
-      }
+        }
 
-      clipboard.on('success', function() {
-        swal.fire({
-          title: '{{trans('common.copy.success')}}',
-          icon: 'success',
-          timer: 1300,
-          showConfirmButton: false,
+        clipboard.on("success", function() {
+            swal.fire({
+                title: '{{ trans('common.copy.success') }}',
+                icon: "success",
+                timer: 1300,
+                showConfirmButton: false
+            });
         });
-      });
-      clipboard.on('error', function() {
-        swal.fire({
-          title: '{{trans('common.copy.failed')}}',
-          icon: 'error',
-          timer: 1500,
-          showConfirmButton: false,
+        clipboard.on("error", function() {
+            swal.fire({
+                title: '{{ trans('common.copy.failed') }}',
+                icon: "error",
+                timer: 1500,
+                showConfirmButton: false
+            });
         });
-      });
     </script>
 @endsection

Nem az összes módosított fájl került megjelenítésre, mert túl sok fájl változott