From 7ca19da82649abb52aa728aad6780866faa0eae0 Mon Sep 17 00:00:00 2001 From: Arsen Mirzaev Tatyano-Muradovich Date: Sun, 4 Apr 2021 23:54:34 +1000 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D1=80=D0=B0=D0=B1=D0=BE=D1=82?= =?UTF-8?q?=D0=BA=D0=B0=20=D1=81=D1=82=D1=80=D0=B0=D0=BD=D0=B8=D1=86=D1=8B?= =?UTF-8?q?=20=D1=82=D0=BE=D0=B2=D0=B0=D1=80=D0=BE=D0=B2=20=D0=B8=20=D0=B8?= =?UTF-8?q?=D1=85=20=D0=B0=D0=B4=D0=BC=D0=B8=D0=BD=D0=B8=D1=81=D1=82=D1=80?= =?UTF-8?q?=D0=B8=D1=80=D0=BE=D0=B2=D0=B0=D0=BD=D0=B8=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- composer.lock | 88 +++--- .../skillparts/system/config/web.php.example | 3 +- .../system/controllers/ProductController.php | 195 +++++++++++- .../system/controllers/SearchController.php | 18 +- mirzaev/skillparts/system/models/Document.php | 33 +- .../skillparts/system/models/Notification.php | 5 + mirzaev/skillparts/system/models/Product.php | 128 +++----- mirzaev/skillparts/system/models/Supply.php | 98 +++++- mirzaev/skillparts/system/views/error.php | 13 +- .../skillparts/system/views/product/index.php | 127 ++++++-- .../skillparts/system/views/search/index.php | 298 +++++++++--------- .../system/views/search/loading.php | 2 +- mirzaev/skillparts/system/web/css/main.css | 13 + .../system/web/css/pages/product.css | 51 ++- .../system/web/css/pages/search.css | 5 + .../skillparts/system/web/js/notification.js | 2 +- mirzaev/skillparts/system/web/js/product.js | 23 ++ .../skillparts/system/web/js/product_panel.js | 125 +++++++- 18 files changed, 900 insertions(+), 327 deletions(-) diff --git a/composer.lock b/composer.lock index 65b4703..000ad20 100644 --- a/composer.lock +++ b/composer.lock @@ -351,16 +351,16 @@ }, { "name": "egulias/email-validator", - "version": "3.1.0", + "version": "3.1.1", "source": { "type": "git", "url": "https://github.com/egulias/EmailValidator.git", - "reference": "62c3b73c581c834885acf6e120b412b76acc495a" + "reference": "c81f18a3efb941d8c4d2e025f6183b5c6d697307" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/62c3b73c581c834885acf6e120b412b76acc495a", - "reference": "62c3b73c581c834885acf6e120b412b76acc495a", + "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/c81f18a3efb941d8c4d2e025f6183b5c6d697307", + "reference": "c81f18a3efb941d8c4d2e025f6183b5c6d697307", "shasum": "" }, "require": { @@ -407,7 +407,7 @@ ], "support": { "issues": "https://github.com/egulias/EmailValidator/issues", - "source": "https://github.com/egulias/EmailValidator/tree/3.1.0" + "source": "https://github.com/egulias/EmailValidator/tree/3.1.1" }, "funding": [ { @@ -415,7 +415,7 @@ "type": "github" } ], - "time": "2021-03-07T14:33:28+00:00" + "time": "2021-04-01T18:37:14+00:00" }, { "name": "ezyang/htmlpurifier", @@ -783,7 +783,7 @@ "source": { "type": "git", "url": "https://git.hood.su/mirzaev/yii2/arangodb", - "reference": "a7abed56d6a3d8d03b1c99e620a9c0ef3887bfcb" + "reference": "7a6e6fbadafc65ecb516d5a2dc87bd83f7c19274" }, "require": { "php": "^8.0.0", @@ -825,7 +825,7 @@ "ArangoDb", "yii2" ], - "time": "2021-03-21T19:01:43+00:00" + "time": "2021-03-29T00:20:46+00:00" }, { "name": "mirzaev/yii2-arangodb-sessions", @@ -833,12 +833,11 @@ "source": { "type": "git", "url": "https://git.hood.su/mirzaev/yii2/arangodb/sessions/", - "reference": "80c16734945a35a0d49170b9a1a854efb322fefb" + "reference": "e709d3d9145ccdec0dc2c50390d3b73b73fb1109" }, "require": { "mirzaev/yii2-arangodb": "~2.1.x-dev", "php": "^8.0.0", - "triagens/arangodb": "~3.2", "yiisoft/yii2": "2.*" }, "require-dev": { @@ -863,14 +862,14 @@ "role": "Developer" } ], - "description": "Yii\\web\\DbSession implementation for use with ArangoDB", + "description": "yii\\web\\DbSession implementation for use with ArangoDB", "homepage": "https://git.hood.su/mirzaev/yii2/arangodb/sessions", "keywords": [ "ArangoDb", "DbSession", "yii2" ], - "time": "2021-03-21T18:39:52+00:00" + "time": "2021-03-29T00:19:31+00:00" }, { "name": "moonlandsoft/yii2-phpexcel", @@ -2384,16 +2383,16 @@ }, { "name": "codeception/codeception", - "version": "4.1.19", + "version": "4.1.20", "source": { "type": "git", "url": "https://github.com/Codeception/Codeception.git", - "reference": "138dc9345a81ec994dcd6b9680c501a752a37b00" + "reference": "d8b16e13e1781dbc3a7ae8292117d520c89a9c5a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Codeception/Codeception/zipball/138dc9345a81ec994dcd6b9680c501a752a37b00", - "reference": "138dc9345a81ec994dcd6b9680c501a752a37b00", + "url": "https://api.github.com/repos/Codeception/Codeception/zipball/d8b16e13e1781dbc3a7ae8292117d520c89a9c5a", + "reference": "d8b16e13e1781dbc3a7ae8292117d520c89a9c5a", "shasum": "" }, "require": { @@ -2467,7 +2466,7 @@ ], "support": { "issues": "https://github.com/Codeception/Codeception/issues", - "source": "https://github.com/Codeception/Codeception/tree/4.1.19" + "source": "https://github.com/Codeception/Codeception/tree/4.1.20" }, "funding": [ { @@ -2475,7 +2474,7 @@ "type": "open_collective" } ], - "time": "2021-03-28T13:26:08+00:00" + "time": "2021-04-02T16:41:51+00:00" }, { "name": "codeception/lib-asserts", @@ -3057,20 +3056,22 @@ }, { "name": "fakerphp/faker", - "version": "v1.13.0", + "version": "v1.14.1", "source": { "type": "git", "url": "https://github.com/FakerPHP/Faker.git", - "reference": "ab3f5364d01f2c2c16113442fb987d26e4004913" + "reference": "ed22aee8d17c7b396f74a58b1e7fefa4f90d5ef1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/FakerPHP/Faker/zipball/ab3f5364d01f2c2c16113442fb987d26e4004913", - "reference": "ab3f5364d01f2c2c16113442fb987d26e4004913", + "url": "https://api.github.com/repos/FakerPHP/Faker/zipball/ed22aee8d17c7b396f74a58b1e7fefa4f90d5ef1", + "reference": "ed22aee8d17c7b396f74a58b1e7fefa4f90d5ef1", "shasum": "" }, "require": { - "php": "^7.1 || ^8.0" + "php": "^7.1 || ^8.0", + "psr/container": "^1.0", + "symfony/deprecation-contracts": "^2.2" }, "conflict": { "fzaninotto/faker": "*" @@ -3078,9 +3079,20 @@ "require-dev": { "bamarni/composer-bin-plugin": "^1.4.1", "ext-intl": "*", - "phpunit/phpunit": "^7.5.20 || ^8.5.8 || ^9.4.2" + "symfony/phpunit-bridge": "^4.4 || ^5.2" + }, + "suggest": { + "ext-curl": "Required by Faker\\Provider\\Image to download images.", + "ext-dom": "Required by Faker\\Provider\\HtmlLorem for generating random HTML.", + "ext-iconv": "Required by Faker\\Provider\\ru_RU\\Text::realText() for generating real Russian text.", + "ext-mbstring": "Required for multibyte Unicode string functionality." }, "type": "library", + "extra": { + "branch-alias": { + "dev-main": "v1.15-dev" + } + }, "autoload": { "psr-4": { "Faker\\": "src/Faker/" @@ -3103,9 +3115,9 @@ ], "support": { "issues": "https://github.com/FakerPHP/Faker/issues", - "source": "https://github.com/FakerPHP/Faker/tree/v1.13.0" + "source": "https://github.com/FakerPHP/Faker/tree/v.1.14.1" }, - "time": "2020-12-18T16:50:48+00:00" + "time": "2021-03-30T06:27:33+00:00" }, { "name": "guzzlehttp/psr7", @@ -5409,16 +5421,16 @@ }, { "name": "symfony/console", - "version": "v5.2.5", + "version": "v5.2.6", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "938ebbadae1b0a9c9d1ec313f87f9708609f1b79" + "reference": "35f039df40a3b335ebf310f244cb242b3a83ac8d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/938ebbadae1b0a9c9d1ec313f87f9708609f1b79", - "reference": "938ebbadae1b0a9c9d1ec313f87f9708609f1b79", + "url": "https://api.github.com/repos/symfony/console/zipball/35f039df40a3b335ebf310f244cb242b3a83ac8d", + "reference": "35f039df40a3b335ebf310f244cb242b3a83ac8d", "shasum": "" }, "require": { @@ -5486,7 +5498,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v5.2.5" + "source": "https://github.com/symfony/console/tree/v5.2.6" }, "funding": [ { @@ -5502,7 +5514,7 @@ "type": "tidelift" } ], - "time": "2021-03-06T13:42:15+00:00" + "time": "2021-03-28T09:42:18+00:00" }, { "name": "symfony/css-selector", @@ -6400,16 +6412,16 @@ }, { "name": "symfony/string", - "version": "v5.2.4", + "version": "v5.2.6", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "4e78d7d47061fa183639927ec40d607973699609" + "reference": "ad0bd91bce2054103f5eaa18ebeba8d3bc2a0572" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/4e78d7d47061fa183639927ec40d607973699609", - "reference": "4e78d7d47061fa183639927ec40d607973699609", + "url": "https://api.github.com/repos/symfony/string/zipball/ad0bd91bce2054103f5eaa18ebeba8d3bc2a0572", + "reference": "ad0bd91bce2054103f5eaa18ebeba8d3bc2a0572", "shasum": "" }, "require": { @@ -6463,7 +6475,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v5.2.4" + "source": "https://github.com/symfony/string/tree/v5.2.6" }, "funding": [ { @@ -6479,7 +6491,7 @@ "type": "tidelift" } ], - "time": "2021-02-16T10:20:28+00:00" + "time": "2021-03-17T17:12:15+00:00" }, { "name": "symfony/yaml", diff --git a/mirzaev/skillparts/system/config/web.php.example b/mirzaev/skillparts/system/config/web.php.example index e649c10..82d1c6b 100644 --- a/mirzaev/skillparts/system/config/web.php.example +++ b/mirzaev/skillparts/system/config/web.php.example @@ -4,6 +4,7 @@ $config = [ 'id' => 'skillparts', 'basePath' => dirname(__DIR__), 'bootstrap' => ['log'], + 'defaultRoute' => 'main', 'aliases' => [ '@vendor' => dirname(__DIR__) . '/../../../vendor', '@bower' => '@vendor/bower-asset', @@ -76,7 +77,7 @@ $config = [ 'controller' => 'main' ], 'product/' => 'product/index', - 'product///' => 'product/-', + 'product///' => 'product/-', 'orders' => 'order/index' ], ], diff --git a/mirzaev/skillparts/system/controllers/ProductController.php b/mirzaev/skillparts/system/controllers/ProductController.php index 83d0c0e..29eb3bd 100644 --- a/mirzaev/skillparts/system/controllers/ProductController.php +++ b/mirzaev/skillparts/system/controllers/ProductController.php @@ -106,8 +106,8 @@ class ProductController extends Controller if ($model = Product::searchByCatn($catn)) { // Товар найден - // Инициализация - $text = yii::$app->request->get('text') ?? yii::$app->request->post('text') ?? 'Без названия'; + // Инициализация + $text = yii::$app->request->get('text') ?? yii::$app->request->post('text') ?? 'Без названия'; $model->catn = $text; @@ -138,7 +138,7 @@ class ProductController extends Controller } } - public function actionEditDesc(string $catn): array|string|null + public function actionEditDscr(string $catn): array|string|null { // Инициализация $return = [ @@ -159,7 +159,7 @@ class ProductController extends Controller // Инициализация $text = yii::$app->request->get('text') ?? yii::$app->request->post('text') ?? 'Без названия'; - $product->desc = $text; + $product->dscr = $text; if ($product->save()) { // Товар обновлён @@ -188,6 +188,63 @@ class ProductController extends Controller } } + public function actionEditDmns(string $catn): array|string|null + { + // Инициализация + $return = [ + '_csrf' => yii::$app->request->getCsrfToken() + ]; + + if (is_null($catn)) { + // Не получен артикул + + yii::$app->response->statusCode = 500; + + goto end; + } + + if ($product = Product::searchByCatn($catn)) { + // Товар найден + + // Инициализация + $text = yii::$app->request->post('text') ?? yii::$app->request->get('text') ?? '0'; + $text or $text = '0'; + $dimension = yii::$app->request->post('dimension') ?? yii::$app->request->get('dimension') ?? 'x'; + + $product->dmns = array_merge( + $product->dmns ?? [], + [ + $dimension => $text + ] + ); + + if ($product->save()) { + // Товар обновлён + + $return['dimension'] = $text; + } + } + + /** + * Конец алгоритма + */ + end: + + if (yii::$app->request->isPost) { + // POST-запрос + + yii::$app->response->format = Response::FORMAT_JSON; + + return $return; + } + + if ($model = Product::searchByCatn($catn)) { + return $this->render('index', compact('model')); + } else { + return $this->redirect('/'); + } + } + public function actionWriteImage(string $catn): array|string|null { // Инициализация @@ -203,17 +260,137 @@ class ProductController extends Controller goto end; } - if ($model = Product::searchByCatn($catn)) { + if ($product = Product::searchByCatn($catn)) { // Товар найден // Инициализация - $model->file_image = UploadedFile::getInstancesByName('images'); - $model->scenario = $model::SCENARIO_IMPORT_IMAGE; + $product->file_image = UploadedFile::getInstancesByName('images'); + $product->scenario = $product::SCENARIO_IMPORT_IMAGE; - if ($model->importImages() > 0) { + if ($product->importImages() > 0) { // Товар обновлён - $return['main'] = $this->renderPartial('index', compact('model')); + $return['main'] = $this->renderPartial('index', ['model' => $product]); + } + } + + /** + * Конец алгоритма + */ + end: + + if (yii::$app->request->isPost) { + // POST-запрос + + yii::$app->response->format = Response::FORMAT_JSON; + + return $return; + } + + if ($model = Product::searchByCatn($catn)) { + return $this->render('index', compact('model')); + } else { + return $this->redirect('/'); + } + } + + public function actionDeleteImage(string $catn): array|string|null + { + // Инициализация + $return = [ + '_csrf' => yii::$app->request->getCsrfToken() + ]; + $index = yii::$app->request->post('index') ?? yii::$app->request->get('index'); + + if (is_null($catn) || is_null($index)) { + // Не получены обязательные параметры + + yii::$app->response->statusCode = 500; + + goto end; + } + + if ($product = Product::searchByCatn($catn)) { + // Товар найден + + // Инициализация (буфер нужен из-за кривых сеттеров) + $buffer = $product->imgs; + + // Удаление + unset($buffer[$index]); + + // Запись + $product->imgs = $buffer; + + if ($product->save()) { + // Товар обновлён + + $return['main'] = $this->renderPartial('index', ['model' => $product]); + } + } + + /** + * Конец алгоритма + */ + end: + + if (yii::$app->request->isPost) { + // POST-запрос + + yii::$app->response->format = Response::FORMAT_JSON; + + return $return; + } + + if ($model = Product::searchByCatn($catn)) { + return $this->render('index', compact('model')); + } else { + return $this->redirect('/'); + } + } + + public function actionWriteCover(string $catn): array|string|null + { + // Инициализация + $return = [ + '_csrf' => yii::$app->request->getCsrfToken() + ]; + $index = yii::$app->request->post('index') ?? yii::$app->request->get('index'); + + if (is_null($catn) || is_null($index)) { + // Не получены обязательные параметры + + yii::$app->response->statusCode = 500; + + goto end; + } + + if ($product = Product::searchByCatn($catn)) { + // Товар найден + + // Инициализация (буфер нужен из-за кривых сеттеров) + $buffer = $product->imgs; + + foreach($buffer as $image_index => &$image) { + // Перебор изображений + + if ($image_index === (int) $index) { + // Найдено запрашиваемое изображение + + // Установка обложки + $image['covr'] = true; + } else { + $image['covr'] = false; + } + } + + // Запись + $product->imgs = $buffer; + + if ($product->save()) { + // Товар обновлён + + $return['main'] = $this->renderPartial('index', ['model' => $product]); } } diff --git a/mirzaev/skillparts/system/controllers/SearchController.php b/mirzaev/skillparts/system/controllers/SearchController.php index 6796777..3a000ea 100644 --- a/mirzaev/skillparts/system/controllers/SearchController.php +++ b/mirzaev/skillparts/system/controllers/SearchController.php @@ -59,7 +59,7 @@ class SearchController extends Controller $timer = 0; // Период пропуска запросов (в секундах) - $period = 5; + $period = 3; $keep_connect = false; $sanction = false; $sanction_condition = ($session['last_request'] + $period - time()) < $period; @@ -128,7 +128,17 @@ class SearchController extends Controller $limit = yii::$app->request->isPost ? 10 : 20; - if ($response = Product::searchByPartialCatn($query, $limit, ['catn' => 'catn', '_key' => '_key'])) { + if ($response = Product::searchByPartialCatn($query, $limit, [ + '_key' => '_key', + 'catn' => 'catn', + 'name' => 'name', + // Баг с названием DESC + 'dscr' => 'dscr', + 'catg' => 'catg', + 'imgs' => 'imgs', + 'prod' => 'prod', + 'dmns' => 'dmns', + ])) { // Данные найдены по поиску в полях Каталожного номера foreach ($response as &$row) { @@ -205,7 +215,9 @@ class SearchController extends Controller goto keep_connect_wait; } - return $this->render('/search/index', compact('response', 'timer')); + $advanced = true; + + return $this->render('/search/index', compact('response', 'timer', 'advanced')); } } diff --git a/mirzaev/skillparts/system/models/Document.php b/mirzaev/skillparts/system/models/Document.php index fc46b3f..2f75c2e 100644 --- a/mirzaev/skillparts/system/models/Document.php +++ b/mirzaev/skillparts/system/models/Document.php @@ -157,12 +157,39 @@ abstract class Document extends ActiveRecord /** * Проверка на то, что в свойство передан массив */ - public function arrayValidator(string $attribute, array $params = null): bool + public function arrayValidator(string $attribute, array $params = null): void { if (is_array($this->$attribute)) { - return true; + return; + } else { + $this->addError($attribute, 'Передан не массив'); } - return false; + $this->addError($attribute, 'Неизвестная ошибка'); + } + + /** + * Проверка на то, что в свойство передан массив и он хранит циферные значения + */ + public function arrayWithNumbersValidator(string $attribute, array $params = null): void + { + try { + if (is_array($this->$attribute)) { + + foreach ($this->$attribute as $value) { + if (!(bool) preg_match('/^[0-9]*$/m', $value)) { + $this->addError($attribute, 'В массиве найдены запрещённые символы'); + } + } + + return; + } else { + $this->addError($attribute, 'Передан не массив'); + } + } catch (Exception $e) { + $this->addError($attribute, $e->getMessage()); + } + + $this->addError($attribute, 'Неизвестная ошибка'); } } diff --git a/mirzaev/skillparts/system/models/Notification.php b/mirzaev/skillparts/system/models/Notification.php index bd22f71..b4f5383 100644 --- a/mirzaev/skillparts/system/models/Notification.php +++ b/mirzaev/skillparts/system/models/Notification.php @@ -38,6 +38,11 @@ class Notification extends Document */ const TYPE_WARNING = 'warning'; + /** + * Тип уведомления: ошибка + */ + const TYPE_ERROR = 'error'; + /** * Цель для отправки уведомления * diff --git a/mirzaev/skillparts/system/models/Product.php b/mirzaev/skillparts/system/models/Product.php index 9b64290..cf0fd02 100644 --- a/mirzaev/skillparts/system/models/Product.php +++ b/mirzaev/skillparts/system/models/Product.php @@ -9,7 +9,7 @@ use yii\web\UploadedFile; use yii\imagine\Image; use app\models\traits\SearchByEdge; - +use Exception; use moonland\phpexcel\Excel; /** @@ -75,12 +75,12 @@ class Product extends Document [ 'catn', 'name', - 'desc', - 'ocid', + // В библеотеке есть баг на название DESC (неизвестно в моей или нет) + 'dscr', + 'prod', + 'dmns', 'imgs', - 'time', - 'oemn', - 'cost' + 'time' ] ); } @@ -95,12 +95,11 @@ class Product extends Document [ 'catn' => 'Каталожный номер (catn)', 'name' => 'Название (name)', - 'desc' => 'Описание (desc)', - 'ocid' => 'Идентификатор 1C (ocid)', + 'dscr' => 'Описание (dscr)', + 'prod' => 'Производитель (prod)', + 'dmns' => 'Габариты (dmns)', 'imgs' => 'Изображения (imgs)', 'time' => 'Срок доставки (time)', - 'oemn' => 'OEM номера (oemn)', - 'cost' => 'Стоимость (cost)', 'file_excel' => 'Документ (file_excel)', 'file_image' => 'Изображение (file_image)', 'group' => 'Группа (group)' @@ -129,12 +128,14 @@ class Product extends Document 'message' => '{attribute} должен быть строкой' ], [ - [ - 'oemn', - 'imgs' - ], + 'imgs', 'arrayValidator', - 'message' => '{attribute} должен быть массивом.' + 'message' => '{attribute} должен быть массивом' + ], + [ + 'dmns', + 'arrayWithNumbersValidator', + 'message' => '{attribute} должен быть массивом и хранить циферные значения' ], [ 'file_excel', @@ -176,52 +177,6 @@ class Product extends Document ); } - /** - * Инициализация продукта - * - * @param string $catn Артикул, каталожный номер - */ - public static function initEmpty(string $catn): self|array - { - $oemn = self::searchOemn($catn); - - if (count($oemn) === 1) { - // Передан только один артикул - - if ($model = self::searchByCatn($catn)) { - // Продукт уже существует - - return $model; - } - - // Запись пустого продукта - return self::writeEmpty($catn); - } - - // Инициализация - $models = []; - - foreach ($oemn as $catn) { - // Перебор всех найденных артикулов - - if ($model = self::searchByCatn($catn)) { - // Продукт уже существует - - continue; - } - - // Запись - if ($model = self::writeEmpty($catn)) { - // Записано - - // Запись в массив сохранённых моделей - $models[] = $model; - } - } - - return $models; - } - /** * Запись пустого продукта */ @@ -237,28 +192,6 @@ class Product extends Document return $model->save() ? $model : null; } - /** - * Поиск OEM номеров - * - * @param string $oemn Необработанная строка с OEM-номерами - * @param string $delimiters Разделители - * - * @todo НЕ ЗАБЫТЬ СДЕЛАТЬ НАСТРОЙКУ РАЗДЕЛИТЕЛЕЙ - * - * @return array OEM-номера - */ - public static function searchOemn(string $oemn, string $delimiters = '\s\+\/,'): array - { - // Инициализация - $catn = []; - - // Конвертация - preg_match_all("/[^$delimiters]+/", $oemn, $catn); - - return $catn[0]; - } - - /** * Импорт изображений * @@ -266,12 +199,12 @@ class Product extends Document */ public function importImages(): int { + // Инициализация + $amount = 0; + if ($this->validate()) { // Проверка пройдена - // Инициализация - $amount = 0; - foreach ($this->file_image as $file) { // Перебор обрабатываемых изображений @@ -307,16 +240,19 @@ class Product extends Document Image::resize(YII_PATH_PUBLIC . $catalog . '/' . $file->baseName . '.' . $file->extension, 150, 150) ->save(YII_PATH_PUBLIC . $catalog_h150 . '/' . $file->baseName . '.' . $file->extension, ['quality' => 80]); + // Инициализация + $this->imgs ?? $this->imgs = []; + // Запись в базу данных $this->imgs = array_merge( - $this->imgs ?? [], + $this->imgs, [[ + 'covr' => count($this->imgs) === 0 ? true : false, 'orig' => $catalog . '/' . $file->baseName . '.' . $file->extension, 'h150' => $catalog_h150 . '/' . $file->baseName . '.' . $file->extension ]] ); - $this->scenario = self::SCENARIO_WRITE; if ($this->save()) { @@ -328,6 +264,22 @@ class Product extends Document } } + if ($this->hasErrors()) { + // Получены ошибки + + foreach ($this->getErrors() as $attribute => $errors) { + // Перебор атрибутов + + foreach ($errors as $error) { + // Перебор ошибок атрибутов + + $label = $this->getAttributeLabel($attribute); + + Notification::_write("$label: $error", type: Notification::TYPE_ERROR); + } + } + } + return $amount; } diff --git a/mirzaev/skillparts/system/models/Supply.php b/mirzaev/skillparts/system/models/Supply.php index 4e74276..f581c16 100644 --- a/mirzaev/skillparts/system/models/Supply.php +++ b/mirzaev/skillparts/system/models/Supply.php @@ -52,7 +52,9 @@ class Supply extends Product implements ProductInterface return array_merge( parent::attributes(), [ - 'onec' + 'cost', + 'onec', + 'ocid' ] ); } @@ -65,7 +67,28 @@ class Supply extends Product implements ProductInterface return array_merge( parent::attributeLabels(), [ - 'onec' => 'Данные 1С' + 'cost' => 'Стоимость (cost)', + 'onec' => 'Данные 1С', + 'ocid' => 'Идентификатор 1C (ocid)' + ] + ); + } + + /** + * Правила + */ + public function rules(): array + { + return array_merge( + parent::rules(), + [ + // [ + // [ + // 'oemn' + // ], + // 'arrayValidator', + // 'message' => '{attribute} должен быть массивом.' + // ] ] ); } @@ -281,7 +304,7 @@ class Supply extends Product implements ProductInterface // Создание продукта (временно заблокировано) // // Инициализация п̸̨͇͑͋͠р̷̬̂́̀̊о̸̜̯̹̅͒͘͝д̴̨̨̨̟̈́̆у̴̨̭̮̠́͋̈́к̴̭͊̋̎т̵̛̣͈̔̐͆а̵̨͖͑ - // $product = Product::initEmpty($this->catn); + // $product = self::initEmpty($this->catn); // if (!is_array($product)) { // // Создался только один товар и вернулся в виде модели @@ -296,7 +319,7 @@ class Supply extends Product implements ProductInterface // // Перебор артикулов из массива ОЕМ-номеров // // Инициализация и запись - // $product[] = Product::initEmpty($oem); + // $product[] = self::initEmpty($oem); // } // } @@ -380,6 +403,73 @@ class Supply extends Product implements ProductInterface return null; } + /** + * Инициализация продукта + * + * @param string $catn Артикул, каталожный номер + */ + public static function initEmpty(string $catn): self|array + { + $oemn = self::searchOemn($catn); + + if (count($oemn) === 1) { + // Передан только один артикул + + if ($model = Product::searchByCatn($catn)) { + // Продукт уже существует + + return $model; + } + + // Запись пустого продукта + return Product::writeEmpty($catn); + } + + // Инициализация + $models = []; + + foreach ($oemn as $catn) { + // Перебор всех найденных артикулов + + if ($model = Product::searchByCatn($catn)) { + // Продукт уже существует + + continue; + } + + // Запись + if ($model = Product::writeEmpty($catn)) { + // Записано + + // Запись в массив сохранённых моделей + $models[] = $model; + } + } + + return $models; + } + + /** + * Поиск OEM номеров + * + * @param string $oemn Необработанная строка с OEM-номерами + * @param string $delimiters Разделители + * + * @todo НЕ ЗАБЫТЬ СДЕЛАТЬ НАСТРОЙКУ РАЗДЕЛИТЕЛЕЙ + * + * @return array OEM-номера + */ + public static function searchOemn(string $oemn, string $delimiters = '\s\+\/,'): array + { + // Инициализация + $catn = []; + + // Конвертация + preg_match_all("/[^$delimiters]+/", $oemn, $catn); + + return $catn[0]; + } + /** * Запись цены из 1С */ diff --git a/mirzaev/skillparts/system/views/error.php b/mirzaev/skillparts/system/views/error.php index aeb7bbb..19cce35 100644 --- a/mirzaev/skillparts/system/views/error.php +++ b/mirzaev/skillparts/system/views/error.php @@ -6,11 +6,12 @@ $this->title = $name; ?> -
-

title) ?>

+
+
+

title) ?>

-
- +
+ +
- -
+
\ No newline at end of file diff --git a/mirzaev/skillparts/system/views/product/index.php b/mirzaev/skillparts/system/views/product/index.php index ab76d25..a4ca323 100644 --- a/mirzaev/skillparts/system/views/product/index.php +++ b/mirzaev/skillparts/system/views/product/index.php @@ -16,16 +16,34 @@ use app\models\Product;
$image) { - // Перебор изображений + // Инициализация + $covr_not_found = true; + + // Проверка на то, что обложка установлена + foreach (!empty($model['imgs']) && is_array($model['imgs']) ? $model['imgs'] : [] as $image) { + if ($image['covr'] ?? false) { + $covr_not_found = false; + } + } + + foreach (!empty($model['imgs']) && is_array($model['imgs']) ? $model['imgs'] : [] as $key => $image) { + // Перебор изображений для генерации обложек // Инициализация $h150 = $image['h150'] ?? '/img/covers/h150/product.png'; - // Генерация предпросмотра изображения + if (($image['covr'] ?? false) || ($covr_not_found && $key === 0)) { + echo << + HTML; + } else { + echo << + HTML; + } + echo << - + HTML; } @@ -37,39 +55,57 @@ use app\models\Product; || yii::$app->user->identity->type === 'moderator') ) : ?> -
$image) { - // Перебор изображений + foreach (!empty($model['imgs']) && is_array($model['imgs']) ? $model['imgs'] : [] as $key => $image) { + // Перебор изображений для генерации полных версий // Инициализация $name = $image['name'] ?? 'Без названия'; $orig = $image['orig'] ?? '/img/covers/product.png'; - $covr = $image['covr'] ?? false; - if ($covr || count($imgs) < 2) { - // Если это изображение является обложкой + if (($image['covr'] ?? false) || ($covr_not_found && $key === 0)) { + // Если это изображение является обложкой или обложка не найдена // Реинициализация $checked = 'checked'; } // Генерация изображения - echo << -
- -
- HTML; + if ( + !yii::$app->user->isGuest + && (yii::$app->user->identity->type === 'administrator' + || yii::$app->user->identity->type === 'moderator') + ) { + $catn = $model['catn']; + + echo << +
+ +
+ + +
+
+ HTML; + } else { + echo << +
+ +
+ HTML; + } // Деинициализация $checked = ''; @@ -87,14 +123,16 @@ use app\models\Product;

+
+
user->isGuest && (yii::$app->user->identity->type === 'administrator' || yii::$app->user->identity->type === 'moderator') ) : ?> -
- +
+ -
+ +
+ +
-
+
-
+ +
+ +
- + + + + user->isGuest + && (yii::$app->user->identity->type === 'administrator' + || yii::$app->user->identity->type === 'moderator') + ) : ?> +
+

+ Габариты: + + см + x + + см + x + + см +

+
+ +
+

+ Габариты: + + x + + x + +

+
+ +
user->isGuest && (yii::$app->user->identity->type === 'administrator' || yii::$app->user->identity->type === 'moderator') ) : ?> -

+

diff --git a/mirzaev/skillparts/system/views/search/index.php b/mirzaev/skillparts/system/views/search/index.php index 39a3371..7948e43 100644 --- a/mirzaev/skillparts/system/views/search/index.php +++ b/mirzaev/skillparts/system/views/search/index.php @@ -1,150 +1,166 @@ - - // Инициализация - extract($row); - isset($name) ?: $name = 'Без названия'; - isset($catg) ?: $catg = 'Категория'; - isset($covr) ?: $covr = '/img/covers/h150/product.png'; - $supplies_html = ''; + - - foreach ($supplies as $supply) { - // Конкатенация HTML кода - - // Инициализация артикула - $catn = $supply['catn'] ?? $supply['onec']['Артикул']; - - // Инициализация цены - $price_raw = $supply['prce'] ?? $supply['onec']['Цены']['Цена']['ЦенаЗаЕдиницу']; - $price = $price_raw . ' ' . $supply['onec']['Цены']['Цена']['Валюта']; - - // Инициализация количества - $amount_raw = $amount = $supply['amnt'] ?? $supply['onec']['Количество']; - - if (empty($amount_raw) || $amount_raw < 1) { - $amount = 'Под заказ'; - } else { - $amount .= ' шт'; - } - - - if ($amount_raw < 1 || $price_raw < 1) { - // Нет в наличии или цена 0 рублей - - $button_cart = << - -
- HTML; - } else { - $button_cart = << - - - HTML; - } - - - $supplies_html .= << - - $amount - - - Доставка - - - $price - - $button_cart -
- HTML; - } - - echo << -
- - - -
- $supplies_html -
-
-
- HTML; - - // echo << - //
- //
- // - //
- //
- //
$catn
- //
- //
- // {$amnt}шт - //

1000р

- //
- //
- // - //
- //
- // - // HTML; - - // if (++$i % 4 === 0 && $amount - $i !== 0) { - // echo << - // HTML; - // } - } - } else { - echo << -

Ничего не найдено

- - HTML; - } - } - ?> - - @@ -152,7 +168,7 @@ user->isGuest && (yii::$app->user->identity->type === 'administrator' - || yii::$app->user->identity->type === 'moderator') + || yii::$app->user->identity->type === 'moderator') ) : ?> \ No newline at end of file diff --git a/mirzaev/skillparts/system/views/search/loading.php b/mirzaev/skillparts/system/views/search/loading.php index 21111c1..3495ebe 100644 --- a/mirzaev/skillparts/system/views/search/loading.php +++ b/mirzaev/skillparts/system/views/search/loading.php @@ -1,4 +1,4 @@ -


Поиск

+

Поиск

diff --git a/mirzaev/skillparts/system/web/css/main.css b/mirzaev/skillparts/system/web/css/main.css index b904d1f..9043043 100644 --- a/mirzaev/skillparts/system/web/css/main.css +++ b/mirzaev/skillparts/system/web/css/main.css @@ -40,6 +40,15 @@ main { background-color: #f0eefb; } +.unselectable { + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + .inactive, .inactive:hover, .inactive:focus, @@ -153,6 +162,10 @@ main { transition : 0s; } +.alert_white { + background: #fbf9ff; +} + .d-inline-block { display: inline-block; } diff --git a/mirzaev/skillparts/system/web/css/pages/product.css b/mirzaev/skillparts/system/web/css/pages/product.css index 66645d2..db182fe 100644 --- a/mirzaev/skillparts/system/web/css/pages/product.css +++ b/mirzaev/skillparts/system/web/css/pages/product.css @@ -13,25 +13,64 @@ } #page_product #product_slider>.product_slider_image { - max-width: 25vw; + width: 25vw; } #page_product #product_slider>.product_slider_preview>label { + position: relative; border : none; overflow: hidden; + transition: .1s; } -#page_product #product_slider>.product_slider_preview>label:hover>img { +#page_product #product_slider>.product_slider_preview>label>div { + display: none; + position: absolute; +} + +#page_product #product_slider>.product_slider_preview>label:hover>div { + display: block; +} + +#page_product #product_slider>.product_slider_preview>label>img { cursor: pointer; - filter: brightness(0.9) contrast(1.1); + filter: brightness(.85); } +#page_product #product_slider>.product_slider_preview>label:hover>img, #page_product #product_slider>.product_slider_preview>label:active>img, -#page_product #product_slider>.product_slider_preview>label:focus>img { - cursor: pointer; - filter: brightness(0.8) contrast(1.3); +#page_product #product_slider>.product_slider_preview>label:focus>img, +#page_product #product_slider>.product_slider_preview>label.active>img, +#page_product #product_slider>.product_slider_preview>label.button_write>img { + filter: none; +} + +#page_product #product_slider>.product_slider_preview>label.active { + left: -5px; + /* border: 3px solid #123eab; */ +} + +#page_product #product_slider>.product_slider_preview>label.button_write:hover>img { + filter: brightness(0.85) contrast(1.2); +} + +#page_product #product_slider>.product_slider_preview>label.button_write:active>img, +#page_product #product_slider>.product_slider_preview>label.button_write:focus>img { + filter: brightness(0.8) contrast(1.25); } #page_product article .product_description { display: contents; +} + +#page_product article .product_options_edit { + width: 100px; + height: 1rem; + border: none; +} + +#page_product article .product_options_edit_small { + width: 60px; + height: 1rem; + border: none; } \ No newline at end of file diff --git a/mirzaev/skillparts/system/web/css/pages/search.css b/mirzaev/skillparts/system/web/css/pages/search.css index 03db582..dc378ba 100644 --- a/mirzaev/skillparts/system/web/css/pages/search.css +++ b/mirzaev/skillparts/system/web/css/pages/search.css @@ -6,6 +6,11 @@ transition : 100ms ease-in; } +#page_search section>div>div>img { + object-fit: cover; + width : calc(65px - 1rem); +} + /* #page_search nav > div, #page_search section > div:hover > div { height: 68px; border-right: 6px solid #e1e1ea; diff --git a/mirzaev/skillparts/system/web/js/notification.js b/mirzaev/skillparts/system/web/js/notification.js index 713caa2..621f633 100644 --- a/mirzaev/skillparts/system/web/js/notification.js +++ b/mirzaev/skillparts/system/web/js/notification.js @@ -15,7 +15,7 @@ function notification_last() { }; // Проверка уведомлений -setInterval(notification_last, 5000); +setInterval(notification_last, 10000); function notification_popup_create(html, id) { // Инициализация diff --git a/mirzaev/skillparts/system/web/js/product.js b/mirzaev/skillparts/system/web/js/product.js index 17385a8..d10cbab 100644 --- a/mirzaev/skillparts/system/web/js/product.js +++ b/mirzaev/skillparts/system/web/js/product.js @@ -1,3 +1,26 @@ + +function product_image_choose(key, wrap) { + if (key !== undefined && key !== null && wrap !== undefined && wrap !== null) { + // Инициализация + let labels = wrap.getElementsByTagName('label'); + + for (let i = 0; i <= labels.length; i++) { + // Перебор всех элементов label (обложки изображений) + + // Инициализация + let label = labels[i]; + + if (label.getAttribute('for') === 'product_slider_image_' + key) { + label.classList.add('active'); + label.checked = 'true'; + } else { + label.classList.remove('active'); + label.checked = 'false'; + } + } + } +} + function product_response_success(data, status) { product_response(data, status); } diff --git a/mirzaev/skillparts/system/web/js/product_panel.js b/mirzaev/skillparts/system/web/js/product_panel.js index f6870bd..26530a3 100644 --- a/mirzaev/skillparts/system/web/js/product_panel.js +++ b/mirzaev/skillparts/system/web/js/product_panel.js @@ -101,6 +101,76 @@ function product_panel_catn_save(catn, element, redirect = false) { return true; } +function product_panel_dimensions_edit(catn, element, dimension) { + if (catn !== null && catn !== undefined && element !== null && element !== undefined) { + + let input = document.createElement('input'); + + input.setAttribute('id', element.id); + input.setAttribute('class', 'ml-1 text-center product_options_edit_small'); + input.setAttribute('type', 'number'); + input.setAttribute('value', element.innerText); + input.setAttribute('aria-invalid', 'false'); + + element.replaceWith(input); + + product_panel_handler_save(product_panel_dimensions_save, catn, input, dimension); + + return false; + } + + return true; +} + +function product_panel_dimensions_save(catn, element, dimension) { + if (catn !== null && catn !== undefined && element !== null && element !== undefined) { + // Инициализация + let text = element.value; + let span = document.createElement('span'); + + span.setAttribute('id', element.id); + span.setAttribute('class', 'ml-1 pointer-event'); + span.setAttribute('role', 'button'); + span.setAttribute('onclick', 'return product_panel_dimensions_edit(\'' + catn + '\', this, \'' + dimension + '\');'); + + if (text.length === 0) { + text = '0'; + } + + span.innerText = text; + + element.replaceWith(span); + + $.ajax({ + url: '/product/' + catn + '/edit/dmns', + type: 'post', + dataType: 'json', + data: { + '_csrf': yii.getCsrfToken(), + 'text': text, + 'dimension': dimension + }, + success: function (data, status) { + // Заголовок + if (data.dimension !== undefined && span !== null && span !== undefined) { + // Обновление заголовка + span.innerText = data.dimension; + + // Запись аттрибута + span.setAttribute('onclick', 'return product_panel_dimensions_edit(\'' + catn + '\', this, \'' + dimension + '\');'); + }; + + product_response_success(data, status); + }, + error: product_response_error + }); + + return false; + } + + return true; +} + function product_panel_description_edit(catn, element) { if (catn !== null && catn !== undefined && element !== null && element !== undefined) { element.innerHTML = ''; @@ -125,7 +195,7 @@ function product_panel_description_save(catn, element) { element.setAttribute('onclick', 'return product_panel_description_edit(\'' + catn + '\', this);'); $.ajax({ - url: '/product/' + catn + '/edit/desc', + url: '/product/' + catn + '/edit/dscr', type: 'post', dataType: 'json', data: { @@ -178,4 +248,57 @@ function product_panel_images_write(catn, element) { } return true; +} + +function product_panel_images_delete(catn, index) { + if (catn !== null && catn !== undefined && index !== null && index !== undefined) { + $.ajax({ + url: '/product/' + catn + '/delete/image', + type: 'post', + dataType: 'json', + data: { + '_csrf': yii.getCsrfToken(), + 'index': index + }, + success: product_response_success, + error: product_response_error + }); + + return false; + } + + return true; +} + +function product_panel_images_cover_write(catn, index) { + if (catn !== null && catn !== undefined && index !== null && index !== undefined) { + $.ajax({ + url: '/product/' + catn + '/write/cover', + type: 'post', + dataType: 'json', + data: { + '_csrf': yii.getCsrfToken(), + 'index': index + }, + success: product_response_success, + error: product_response_error + }); + + return false; + } + + return true; +} + +function product_panel_handler_save(save, catn, element, ...vars) { + if (save !== undefined && save !== null) { + let product_panel_handler_save_function = function (event) { + if (event.target.id !== element.id) { + save(catn, element, ...vars); + document.body.removeEventListener('click', product_panel_handler_save_function, true); + } + } + + document.body.addEventListener('click', product_panel_handler_save_function, true); + } } \ No newline at end of file