From a309f741b7e18c8afecf15ca470f957589bde1d2 Mon Sep 17 00:00:00 2001 From: fomichev Date: Fri, 17 Apr 2026 10:26:57 +0300 Subject: [PATCH] =?utf8?q?=D0=A4=D0=B8=D0=BA=D1=81=20=D0=BF=D0=BE=20=D0=BC?= =?utf8?q?=D0=B0=D0=BF=D0=BF=D0=B8=D0=BD=D0=B3=D1=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit --- erp24/views/product-mapping/index.php | 260 ++------------------------ erp24/web/js/product-mapping/index.js | 243 ++++++++++++++++++++++++ 2 files changed, 258 insertions(+), 245 deletions(-) create mode 100644 erp24/web/js/product-mapping/index.js diff --git a/erp24/views/product-mapping/index.php b/erp24/views/product-mapping/index.php index f346b785..70b27b09 100644 --- a/erp24/views/product-mapping/index.php +++ b/erp24/views/product-mapping/index.php @@ -109,249 +109,19 @@ use yii\widgets\LinkPager; '; - - function collectFilters() { - var params = {}; - $('.pm-filter').each(function() { - var \$el = $(this); - var name = \$el.attr('name'); - if (!name) return; - if (\$el.is(':checkbox')) { - if (\$el.is(':checked')) { - params[name] = '1'; - } - } else { - var v = \$el.val(); - if (v !== '' && v !== null) { - params[name] = v; - } - } - }); - params['per-page'] = $('#pm-per-page').val(); - return params; - } - - function reloadList(extraParams) { - var params = collectFilters(); - if (extraParams) { - \$.extend(params, extraParams); - } - var \$tab = $('#tab-mappings'); - \$tab.html('
Загрузка...
'); - \$.get('{$indexUrl}', params, function(html) { - \$tab.html(html); - }); - } - - function reloadAnalytics() { - var params = collectFilters(); - delete params['per-page']; - \$.get('{$analyticsUrl}', params, function(data) { - \$.each(data, function(field, value) { - $('[data-analytics-field="' + field + '"]').text(value); - }); - }, 'json'); - } - - // ========= ФИЛЬТРЫ ========= - - // Смена фильтров — reload списка - $(document).on('change', '.pm-filter', function() { - var name = $(this).attr('name'); - - // Каскадное обновление категорий - if (name === 'category') { - var category = $(this).val(); - $('#pm-filter-subcategory').html('').prop('disabled', !category); - $('#pm-filter-species').html('').prop('disabled', true); - if (category) { - \$.get('{$cascadeUrl}', {category: category}, function(data) { - var html = ''; - \$.each(data.subcategories, function(_, sub) { - html += ''; - }); - $('#pm-filter-subcategory').html(html).prop('disabled', false); - }, 'json'); - } - } else if (name === 'subcategory') { - var cat = $('#pm-filter-category').val(); - var sub = $(this).val(); - $('#pm-filter-species').html('').prop('disabled', !sub); - if (cat && sub) { - \$.get('{$cascadeUrl}', {category: cat, subcategory: sub}, function(data) { - var html = ''; - \$.each(data.species, function(_, sp) { - html += ''; - }); - $('#pm-filter-species').html(html).prop('disabled', false); - }, 'json'); - } - } - - // Для поиска по названию — debounce - if (name === 'search') return; - - reloadList({page: 1}); - }); - - // Поиск — с debounce 400мс - $(document).on('input', 'input[name="search"].pm-filter', function() { - clearTimeout(searchTimer); - searchTimer = setTimeout(function() { - reloadList({page: 1}); - }, 400); - }); - - // Сброс фильтров - $(document).on('click', '#pm-filter-reset', function() { - $('.pm-filter').each(function() { - var \$el = $(this); - if (\$el.is(':checkbox')) { - \$el.prop('checked', false); - } else if (\$el.is('select')) { - \$el.val(''); - } else { - \$el.val(''); - } - }); - reloadList({page: 1}); - }); - - // Смена per-page - $('#pm-per-page').on('change', function() { - reloadList({page: 1}); - }); - - // Экспорт в .xlsx — редирект с текущими фильтрами - $(document).on('click', '#pm-export-btn', function() { - var params = collectFilters(); - delete params['per-page']; - var qs = $.param(params); - window.location.href = '{$exportUrl}' + (qs ? '?' + qs : ''); - }); - - // Пагинация — перехват ссылок - $(document).on('click', '.pm-pager a', function(e) { - e.preventDefault(); - var href = $(this).attr('href'); - var pageMatch = href.match(/[?&]page=(\d+)/); - var page = pageMatch ? pageMatch[1] : 1; - reloadList({page: page}); - }); - - // ========= МОДАЛКА МАППИНГА ========= - - $(document).on('click', '.btn-pm-add', function() { - var \$btn = $(this); - if (\$btn.prop('disabled')) return; - \$btn.prop('disabled', true); - editingId = null; - var guid = \$btn.data('product-guid'); - $('#pm-modal-title').text('Добавить поставщика'); - $('#pm-modal-body').html(loaderHtml); - pmModal.show(); - \$.get('{$createFormUrl}', {product_guid: guid}, function(html) { - $('#pm-modal-body').html(html); - }).always(function() { - \$btn.prop('disabled', false); - }); - }); - - $(document).on('click', '.btn-pm-edit', function(e) { - e.preventDefault(); - var \$btn = $(this); - if (\$btn.prop('disabled')) return; - \$btn.prop('disabled', true); - editingId = \$btn.data('id'); - $('#pm-modal-title').text('Редактировать маппинг'); - $('#pm-modal-body').html(loaderHtml); - pmModal.show(); - \$.get('{$updateFormUrl}', {id: editingId}, function(html) { - $('#pm-modal-body').html(html); - }).always(function() { - \$btn.prop('disabled', false); - }); - }); - - $(document).on('click', '#btn-mapping-save', function() { - var \$form = $('#mapping-form'); - var url = editingId ? '{$updateUrl}?id=' + editingId : '{$createUrl}'; - - \$form.find('.is-invalid').removeClass('is-invalid'); - \$form.find('.invalid-feedback').remove(); - - \$.ajax({ - url: url, - type: 'POST', - data: \$form.serialize(), - dataType: 'json', - success: function(resp) { - if (resp.success) { - pmModal.hide(); - reloadList(); - } else if (resp.errors) { - \$.each(resp.errors, function(field, messages) { - var \$input = \$form.find('[name="ProductMapping[' + field + ']"]'); - if (!\$input.length) { - \$input = \$form.find('[name="ProductMapping[' + field + '][]"]'); - } - \$input.addClass('is-invalid'); - \$input.after('
' + messages[0] + '
'); - }); - } - }, - error: function() { alert('Ошибка сервера'); } - }); - }); - - $(document).on('click', '.btn-pm-delete', function(e) { - e.preventDefault(); - var id = $(this).data('id'); - var name = $(this).data('name'); - - if (!confirm('Удалить маппинг на поставщика "' + name + '"?')) { - return; - } - - \$.ajax({ - url: '{$deleteUrl}?id=' + id, - type: 'POST', - data: {_csrf: yii.getCsrfToken()}, - dataType: 'json', - success: function(resp) { - if (resp.success) { - reloadList(); - } else { - alert(resp.message || 'Ошибка удаления'); - } - }, - error: function() { alert('Ошибка сервера'); } - }); - }); - - // Очистка ошибок при вводе - $(document).on('input change', '#mapping-form input, #mapping-form select', function() { - $(this).removeClass('is-invalid'); - $(this).next('.invalid-feedback').remove(); - }); -})(); -JS; - -$this->registerJs($js); +$this->registerJs('window.pmCfg = ' . \yii\helpers\Json::encode([ + 'urls' => [ + 'createForm' => Url::to(['/product-mapping/create-form']), + 'updateForm' => Url::to(['/product-mapping/update-form']), + 'create' => Url::to(['/product-mapping/create']), + 'update' => Url::to(['/product-mapping/update']), + 'delete' => Url::to(['/product-mapping/delete']), + 'index' => Url::to(['/product-mapping/index']), + 'analytics' => Url::to(['/product-mapping/analytics']), + 'cascade' => Url::to(['/product-mapping/cascade-filters']), + 'export' => Url::to(['/product-mapping/export']), + ], +]) . ';'); + +$this->registerJsFile('/js/product-mapping/index.js', ['position' => \yii\web\View::POS_END]); ?> diff --git a/erp24/web/js/product-mapping/index.js b/erp24/web/js/product-mapping/index.js new file mode 100644 index 00000000..09955826 --- /dev/null +++ b/erp24/web/js/product-mapping/index.js @@ -0,0 +1,243 @@ +/* global bootstrap, yii */ +(function () { + var cfg = window.pmCfg; + if (!cfg) return; + + // Снимаем все предыдущие обработчики этого модуля с document, + // чтобы предотвратить накопление при каждом reloadList(). + var NS = '.pmapping'; + $(document).off(NS); + + var pmModal = new bootstrap.Modal(document.getElementById('pm-modal')); + var editingId = null; + var searchTimer = null; + var loaderHtml = '
'; + + function collectFilters() { + var params = {}; + $('.pm-filter').each(function () { + var $el = $(this); + var name = $el.attr('name'); + if (!name) return; + if ($el.is(':checkbox')) { + if ($el.is(':checked')) { + params[name] = '1'; + } + } else { + var v = $el.val(); + if (v !== '' && v !== null) { + params[name] = v; + } + } + }); + params['per-page'] = $('#pm-per-page').val(); + return params; + } + + function reloadList(extraParams) { + var params = collectFilters(); + if (extraParams) { + $.extend(params, extraParams); + } + var $tab = $('#tab-mappings'); + $tab.html('
Загрузка...
'); + $.get(cfg.urls.index, params, function (html) { + $tab.html(html); + }); + } + + function reloadAnalytics() { + var params = collectFilters(); + delete params['per-page']; + $.get(cfg.urls.analytics, params, function (data) { + $.each(data, function (field, value) { + $('[data-analytics-field="' + field + '"]').text(value); + }); + }, 'json'); + } + + // ========= ФИЛЬТРЫ ========= + + $(document).on('change' + NS, '.pm-filter', function () { + var name = $(this).attr('name'); + + if (name === 'category') { + var category = $(this).val(); + $('#pm-filter-subcategory').html('').prop('disabled', !category); + $('#pm-filter-species').html('').prop('disabled', true); + if (category) { + $.get(cfg.urls.cascade, {category: category}, function (data) { + var html = ''; + $.each(data.subcategories, function (_, sub) { + html += ''; + }); + $('#pm-filter-subcategory').html(html).prop('disabled', false); + }, 'json'); + } + reloadList({page: 1}); + return; + } + + if (name === 'subcategory') { + var cat = $('#pm-filter-category').val(); + var sub = $(this).val(); + $('#pm-filter-species').html('').prop('disabled', !sub); + if (cat && sub) { + $.get(cfg.urls.cascade, {category: cat, subcategory: sub}, function (data) { + var html = ''; + $.each(data.species, function (_, sp) { + html += ''; + }); + $('#pm-filter-species').html(html).prop('disabled', false); + }, 'json'); + } + reloadList({page: 1}); + return; + } + + if (name === 'search') return; + + reloadList({page: 1}); + }); + + // Поиск — с debounce 400мс + $(document).on('input' + NS, 'input[name="search"].pm-filter', function () { + clearTimeout(searchTimer); + searchTimer = setTimeout(function () { + reloadList({page: 1}); + }, 400); + }); + + // Сброс фильтров + $(document).on('click' + NS, '#pm-filter-reset', function () { + $('.pm-filter').each(function () { + var $el = $(this); + if ($el.is(':checkbox')) { + $el.prop('checked', false); + } else if ($el.is('select')) { + $el.val(''); + } else { + $el.val(''); + } + }); + reloadList({page: 1}); + }); + + // Смена per-page + $('#pm-per-page').on('change', function () { + reloadList({page: 1}); + }); + + // Экспорт в .xlsx — редирект с текущими фильтрами + $(document).on('click' + NS, '#pm-export-btn', function () { + var params = collectFilters(); + delete params['per-page']; + var qs = $.param(params); + window.location.href = cfg.urls.export + (qs ? '?' + qs : ''); + }); + + // Пагинация — перехват ссылок + $(document).on('click' + NS, '.pm-pager a', function (e) { + e.preventDefault(); + var href = $(this).attr('href'); + var pageMatch = href.match(/[?&]page=(\d+)/); + var page = pageMatch ? pageMatch[1] : 1; + reloadList({page: page}); + }); + + // ========= МОДАЛКА МАППИНГА ========= + + $(document).on('click' + NS, '.btn-pm-add', function () { + var $btn = $(this); + if ($btn.prop('disabled')) return; + $btn.prop('disabled', true); + editingId = null; + var guid = $btn.data('product-guid'); + $('#pm-modal-title').text('Добавить поставщика'); + $('#pm-modal-body').html(loaderHtml); + pmModal.show(); + $.get(cfg.urls.createForm, {product_guid: guid}, function (html) { + $('#pm-modal-body').html(html); + }).always(function () { + $btn.prop('disabled', false); + }); + }); + + $(document).on('click' + NS, '.btn-pm-edit', function (e) { + e.preventDefault(); + var $btn = $(this); + if ($btn.prop('disabled')) return; + $btn.prop('disabled', true); + editingId = $btn.data('id'); + $('#pm-modal-title').text('Редактировать маппинг'); + $('#pm-modal-body').html(loaderHtml); + pmModal.show(); + $.get(cfg.urls.updateForm, {id: editingId}, function (html) { + $('#pm-modal-body').html(html); + }).always(function () { + $btn.prop('disabled', false); + }); + }); + + $(document).on('click' + NS, '#btn-mapping-save', function () { + var $form = $('#mapping-form'); + var url = editingId ? cfg.urls.update + '?id=' + editingId : cfg.urls.create; + + $form.find('.is-invalid').removeClass('is-invalid'); + $form.find('.invalid-feedback').remove(); + + $.ajax({ + url: url, + type: 'POST', + data: $form.serialize(), + dataType: 'json', + success: function (resp) { + if (resp.success) { + pmModal.hide(); + reloadList(); + } else if (resp.errors) { + $.each(resp.errors, function (field, messages) { + var $input = $form.find('[name="ProductMapping[' + field + ']"]'); + if (!$input.length) { + $input = $form.find('[name="ProductMapping[' + field + '][]"]'); + } + $input.addClass('is-invalid'); + $input.after('
' + messages[0] + '
'); + }); + } + }, + error: function () { alert('Ошибка сервера'); } + }); + }); + + $(document).on('click' + NS, '.btn-pm-delete', function (e) { + e.preventDefault(); + var id = $(this).data('id'); + var name = $(this).data('name'); + + if (!confirm('Удалить маппинг на поставщика "' + name + '"?')) { + return; + } + + $.ajax({ + url: cfg.urls.delete + '?id=' + id, + type: 'POST', + data: {_csrf: yii.getCsrfToken()}, + dataType: 'json', + success: function (resp) { + if (resp.success) { + reloadList(); + } else { + alert(resp.message || 'Ошибка удаления'); + } + }, + error: function () { alert('Ошибка сервера'); } + }); + }); + + // Очистка ошибок при вводе + $(document).on('input change' + NS, '#mapping-form input, #mapping-form select', function () { + $(this).removeClass('is-invalid'); + $(this).next('.invalid-feedback').remove(); + }); +}()); -- 2.39.5