form.blade.php 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. {{--
  2. Вьюха: Форма создания и редактирования автомобиля (единая для create и edit)
  3. Создана: 2026-05-06
  4. Контроллер: Admin\CarController::create() / edit() / store() / update()
  5. Переменные: $car (Car|null — null при создании), $dictSections (коллекция, индексирована по code)
  6. Partials (в admin/cars/partials/): _section_main, _section_tech, _section_price,
  7. _section_desc_opts, _section_photos
  8. JS (в @push('js')): модели грузятся динамически из $makeModels JSON, select Модель
  9. заблокирован до выбора Марки; обновление label у custom-file-input
  10. Плагин: Select2 для полей "Марка" и "Модель"
  11. --}}
  12. @extends('admin.layout')
  13. @section('title', isset($car) ? 'Редактировать автомобиль' : 'Добавить автомобиль')
  14. @section('content_header')
  15. <div class="d-flex justify-content-between align-items-center">
  16. <h1 class="m-0">{{ isset($car) ? 'Редактировать автомобиль #' . $car->id : 'Добавить автомобиль' }}</h1>
  17. <a href="{{ route('admin.cars.index') }}" class="btn btn-default btn-sm">
  18. <i class="fas fa-arrow-left"></i> Назад
  19. </a>
  20. </div>
  21. @stop
  22. @section('breadcrumb')
  23. <li class="breadcrumb-item"><a href="{{ route('admin.dashboard') }}">Главная</a></li>
  24. <li class="breadcrumb-item"><a href="{{ route('admin.cars.index') }}">Автомобили</a></li>
  25. <li class="breadcrumb-item active">{{ isset($car) ? 'Редактировать #' . $car->id : 'Добавить' }}</li>
  26. @stop
  27. @section('content')
  28. @php
  29. $car = $car ?? null;
  30. $v = fn($field, $default = null) => old($field, $car?->$field ?? $default);
  31. $makesList = $dictSections->get('makes');
  32. $bodyTypes = $dictSections->get('body_types')?->values ?? collect();
  33. $countries = $dictSections->get('countries')?->values ?? collect();
  34. $cities = $dictSections->get('cities')?->values ?? collect();
  35. $colorsExt = $dictSections->get('colors_ext')?->values ?? collect();
  36. $colorsInt = $dictSections->get('colors_int')?->values ?? collect();
  37. $engineVols = $dictSections->get('engine_volumes')?->values ?? collect();
  38. $optionsList = $dictSections->get('options')?->values ?? collect();
  39. $makes = $makesList?->allValues()->whereNull('parent_id')->with('children')->get() ?? collect();
  40. // Данные для JS: { "Toyota": ["Camry","RAV4",...], "BMW": [...], ... }
  41. // Передаём в @push('js') чтобы не гонять модели через data-атрибуты тысячи option-тегов
  42. $makeModels = $makes->mapWithKeys(fn($m) => [
  43. $m->value => $m->children->pluck('value')->all(),
  44. ])->all();
  45. $platforms = $dictSections->get('platforms')?->values ?? collect();
  46. $carOptions = is_array($car?->options) ? $car->options : (json_decode($car?->options ?? '[]', true) ?? []);
  47. @endphp
  48. <form action="{{ $car ? route('admin.cars.update', $car) : route('admin.cars.store') }}"
  49. method="POST" enctype="multipart/form-data">
  50. @csrf
  51. @if($car) @method('PUT') @endif
  52. @if($errors->any())
  53. <div class="alert alert-danger">
  54. <ul class="mb-0">
  55. @foreach($errors->all() as $e) <li>{{ $e }}</li> @endforeach
  56. </ul>
  57. </div>
  58. @endif
  59. @include('admin.cars.partials._section_main')
  60. @include('admin.cars.partials._section_tech')
  61. @include('admin.cars.partials._section_price')
  62. @include('admin.cars.partials._section_desc_opts')
  63. @include('admin.cars.partials._section_photos')
  64. <div class="mb-4">
  65. <button type="submit" class="btn btn-primary">
  66. <i class="fas fa-save"></i> {{ $car ? 'Сохранить изменения' : 'Добавить автомобиль' }}
  67. </button>
  68. <a href="{{ route('admin.cars.index') }}" class="btn btn-default ml-2">Отмена</a>
  69. </div>
  70. </form>
  71. @stop
  72. @section('plugins.Select2', true)
  73. @push('js')
  74. <script>
  75. (function () {
  76. // Все модели сгруппированы по марке: { "Toyota": ["Camry","RAV4",...], ... }
  77. var MODELS_BY_MAKE = @json($makeModels);
  78. // Значения для восстановления при редактировании (old() или $car->*)
  79. var initialMake = @json($v('make', ''));
  80. var initialModel = @json($v('model', ''));
  81. var $selMake = $('#sel-make');
  82. var $selModel = $('#sel-model');
  83. /**
  84. * Перестраивает #sel-model под выбранную марку.
  85. * make — строка, пустая = марка не выбрана
  86. * selectModel — какую модель выбрать после перестройки (для edit-формы)
  87. */
  88. function populateModels(make, selectModel) {
  89. // Уничтожаем Select2 если уже инициализирован
  90. if ($selModel.data('select2')) {
  91. $selModel.select2('destroy');
  92. }
  93. var sel = $selModel[0];
  94. if (!make) {
  95. // Марка не выбрана — блокируем и показываем подсказку
  96. sel.innerHTML = '<option value="">— сначала выберите марку —</option>';
  97. sel.disabled = true;
  98. } else {
  99. // Марка выбрана — строим список моделей только для неё
  100. sel.disabled = false;
  101. sel.innerHTML = '<option value="">— выберите модель —</option>';
  102. var models = MODELS_BY_MAKE[make] || [];
  103. models.forEach(function (model) {
  104. var opt = document.createElement('option');
  105. opt.value = model;
  106. opt.textContent = model;
  107. if (model === selectModel) opt.selected = true;
  108. sel.appendChild(opt);
  109. });
  110. }
  111. // Инициализируем Select2 обратно
  112. $selModel.select2({ theme: 'bootstrap4', width: '100%' });
  113. }
  114. // При смене марки — сбрасываем выбор модели и перестраиваем список
  115. $selMake.on('change', function () {
  116. populateModels(this.value, '');
  117. });
  118. // Инициализация при загрузке страницы:
  119. // для create — марка пуста, select заблокирован;
  120. // для edit — восстанавливаем марку и модель
  121. $(function () {
  122. populateModels(initialMake, initialModel);
  123. // Обновление подписи file input при выборе файла
  124. document.querySelectorAll('.custom-file-input').forEach(function (input) {
  125. input.addEventListener('change', function () {
  126. var label = this.nextElementSibling;
  127. label.textContent = this.files.length > 1
  128. ? this.files.length + ' файлов выбрано'
  129. : (this.files[0] ? this.files[0].name : 'Выберите файл...');
  130. });
  131. });
  132. });
  133. })();
  134. </script>
  135. @endpush