/data/*
/runtime/*
/api2/runtime/*
-/api2/json/*
\ No newline at end of file
+/api2/json/*
+/widgets/app.log
\ No newline at end of file
use Exception;
use Yii;
use yii\helpers\ArrayHelper;
+use yii\helpers\FileHelper;
+use yii\helpers\Url;
use yii\web\Controller;
use yii\web\NotFoundHttpException;
use yii\web\UploadedFile;
use yii_app\records\BouquetCompositionProducts;
use yii_app\records\BouquetForecast;
use yii_app\records\CityStore;
+use yii_app\records\Files;
use yii_app\records\Products1c;
use yii_app\records\Products1cNomenclature;
use yii_app\records\StoreType;
+use yii_app\services\FileService;
/**
* Контроллер для управления букетами и их составами.
public function actionView($id)
{
+ $model = BouquetComposition::findOne($id);
+
+ if (Yii::$app->request->isPost) {
+ $data = Yii::$app->request->post();
+ $month = $data['month'];
+ $year = $data['year'];
+
+ $model->photo_bouquet = UploadedFile::getInstances($model, 'photo_bouquet');
+ if ($model->photo_bouquet) {
+ Files::deleteAll(['file_type' => 'image', 'entity_id' => $id, 'entity' => BouquetComposition::PHOTO_BOUQUET]);
+ foreach ($model->photo_bouquet as $photo) {
+ FileService::saveUploadedFile($photo, BouquetComposition::PHOTO_BOUQUET, $model->id);
+ }
+ }
+
+ $model->video_presentation = UploadedFile::getInstances($model, 'video_presentation');
+ if ($model->video_presentation) {
+ Files::deleteAll(['file_type' => 'video', 'entity_id' => $id, 'entity' => BouquetComposition::VIDEO_PRESENTATION]);
+ FileService::saveUploadedFile($model->video_presentation, BouquetComposition::VIDEO_PRESENTATION, $model->id);
+ }
+
+ $model->video_build_process = UploadedFile::getInstances($model, 'video_build_process');
+ if ($model->video_build_process) {
+ Files::deleteAll(['file_type' => 'video', 'entity_id' => $id, 'entity' => BouquetComposition::VIDEO_BUILD_PROCESS]);
+ FileService::saveUploadedFile($model->video_build_process, BouquetComposition::VIDEO_BUILD_PROCESS, $model->id);
+ }
+
+
+ if (!empty($data['BouquetForecast']['type_sales_value'])) {
+ $salesData = $data['BouquetForecast']['type_sales_value'];
+
+ if (!empty($salesData['offline'])) {
+ BouquetForecast::processSalesData($id, $year, $month, $salesData['offline'], BouquetForecast::OFFLINE_STORES);
+ }
+
+ if (!empty($salesData['online'])) {
+ BouquetForecast::processSalesData($id, $year, $month, $salesData['online'], BouquetForecast::ONLINE_STORES);
+ }
+
+ if (!empty($salesData['marketplace'])) {
+ BouquetForecast::processSalesData($id, $year, $month, $salesData['marketplace'], BouquetForecast::MARKETPLACE);
+ }
+ }
+
+ }
+
+
+ $photoFiles = Files::find()->where(['entity_id' => $id, 'file_type' => 'image', 'entity' => BouquetComposition::PHOTO_BOUQUET])->all();
+ $videoFiles = Files::find()->where(['entity_id' => $id, 'file_type' => 'image', 'entity' => BouquetComposition::VIDEO_PRESENTATION])->all();
+ $processFiles = Files::find()->where(['entity_id' => $id, 'file_type' => 'image', 'entity' => BouquetComposition::VIDEO_BUILD_PROCESS])->all();
+
+ $photoUrls = array_map(fn($file) => Url::to([$file->url]), $photoFiles);
+ $videoUrls = array_map(fn($file) => Url::to([$file->url]), $videoFiles);
+ $processUrls = array_map(fn($file) => Url::to([$file->url]), $processFiles);
+
+ $bouquetCompositionProducts = BouquetCompositionProducts::find()->andWhere(['bouquet_id' => $id])->all();
+ $storesTypeList = BouquetForecast::getStoresList($id, BouquetForecast::OFFLINE_STORES, StoreType::class, []);
+ $marketplaceList = BouquetForecast::getStoresList($id, BouquetForecast::MARKETPLACE, CityStore::class, ['visible' => CityStore::IS_VISIBLE]);
+ $onlineStoresList = BouquetForecast::getStoresList($id, BouquetForecast::ONLINE_STORES, CityStore::class, ['visible' => CityStore::IS_VISIBLE]);
- BouquetForecast::findAll(['bouquet_id' => $id]);
- $storesTypeList = ArrayHelper::map(BouquetForecast::findAll(['bouquet_id' => $id]) ?? StoreType::find()->orderBy('sequence_number')->all(), 'id', 'name');
- $storesList = ArrayHelper::map(BouquetForecast::findAll(['bouquet_id' => $id]) ?? CityStore::findAll(['visible' => CityStore::IS_VISIBLE]), 'id', 'name');
return $this->render('view', [
- 'storesList' => $storesList,
+ 'model' => $model,
+ 'onlineStoresList' => $onlineStoresList,
+ 'bouquetCompositionProducts' => $bouquetCompositionProducts,
+ 'marketplaceList' => $marketplaceList,
'storesTypeList' => $storesTypeList,
+ 'photoUrls' => $photoUrls,
+ 'photoFiles' => $photoFiles,
+ 'videoUrls' => $videoUrls,
+ 'processUrls' => $processUrls,
]);
}
+
+ /**
+ * Сохранение записи в таблицу files
+ */
+ private function saveFileRecord($bouquetId, $filePath, $type)
+ {
+ $file = new Files([
+ 'url' => $filePath,
+ 'file_type' => $type,
+ 'created_at' => time(),
+ 'entity_id' => $bouquetId,
+ 'entity' => 'bouquet/photo',
+ ]);
+ $file->save();
+ }
+
public function actionUpdate($id)
{
$model = BouquetComposition::findOne($id);
'availableItems' => $availableItems,
]);
}
+
public function actionUpload()
{
$model = new BouquetComposition();
*/
public function safeUp()
{
- $this->createTable('{{%bouquet_forecast}}', [
+ $this->createTable('{{%erp24.bouquet_forecast}}', [
'id' => $this->primaryKey(),
'bouquet_id' => $this->integer()->comment('ИД букета'),
'year' => $this->integer()->comment('Год'),
'month' => $this->integer()->comment('Месяц'),
'type_sales' => $this->integer()->comment('Тип продаж'),
- 'store_sales_id' => $this->integer()->comment('ИД сущности типа продаж'),
- 'value' => $this->float('Значение'),
+ 'type_sales_id' => $this->integer()->comment('ИД сущности типа продаж'),
+ 'type_sales_value' => $this->float('Значение'),
'created_at' => $this->dateTime()->comment('Дата создания'),
'updated_at' => $this->dateTime()->comment('Дата обновления'),
'created_by' => $this->integer()->comment('ID создателя записи'),
*/
class BouquetComposition extends ActiveRecord
{
+ public $photo_bouquet;
+ public $video_presentation;
+ public $video_build_process;
+
+ public const PHOTO_BOUQUET = 'bouquet/photo_bouquet';
+ public const VIDEO_PRESENTATION = 'bouquet/video_presentation';
+ public const VIDEO_BUILD_PROCESS = 'bouquet/video_build_process';
+
public static function tableName()
{
return 'erp24.bouquet_composition';
[['name'], 'required'],
[['matrix_type_id', 'video_id', 'created_by', 'updated_by'], 'integer'],
[['created_at', 'updated_at'], 'safe'],
-
- [['photo_id'], 'file', 'extensions' => 'jpg, jpeg, png, gif'],
-
-
[['guid', 'name'], 'string', 'max' => 255],
+ [['photo_bouquet'], 'file', 'extensions' => 'jpg, jpeg, png, gif', 'maxFiles' => 10],
+ [['video_presentation', 'video_build_process'], 'file', 'extensions' => 'mkv, mov, avi, mp4'],
];
}
}
public function getProduct() {
- return $this->hasOne(Products1c::class, ['id' => 'product_guid']); // Исправил связь
+ return $this->hasOne(Products1c::class, ['id' => 'product_guid']);
}
}
namespace yii_app\records;
use Yii;
+use yii\behaviors\BlameableBehavior;
+use yii\behaviors\TimestampBehavior;
use yii\db\ActiveRecord;
+use yii\db\Expression;
/**
* This is the model class for table "bouquet_forecast".
* @property int|null $year Год
* @property int|null $month Месяц
* @property int|null $type_sales Тип продаж
- * @property int|null $store_sales_id ИД сущности типа продаж
- * @property float|null $value Значение
+ * @property int|null $type_sales_id ИД сущности типа продаж
+ * @property float|null $type_sales_value Значение
* @property string|null $created_at Дата создания
* @property string|null $updated_at Дата обновления
* @property int|null $created_by ID создателя записи
*/
public static function tableName()
{
- return '{{%bouquet_forecast}}';
+ return '{{%erp24.bouquet_forecast}}';
+ }
+
+ public function behaviors()
+ {
+ return [
+ [
+ 'class' => TimestampBehavior::class,
+ 'createdAtAttribute' => 'created_at',
+ 'updatedAtAttribute' => 'updated_at',
+ 'value' => new Expression('NOW()'),
+ ],
+ [
+ 'class' => BlameableBehavior::class,
+ 'createdByAttribute' => 'created_by',
+ 'updatedByAttribute' => 'updated_by',
+ ],
+ ];
}
/**
public function rules()
{
return [
- [['bouquet_id', 'year', 'month', 'type_sales', 'store_sales_id', 'created_by', 'updated_by'], 'integer'],
- [['value'], 'number'],
+ [['bouquet_id', 'year', 'month', 'type_sales', 'type_sales_id', 'created_by', 'updated_by'], 'integer'],
+ [['type_sales_value'], 'number'],
[['created_at', 'updated_at'], 'safe'],
];
}
'year' => 'Год',
'month' => 'Месяц',
'type_sales' => 'Тип продаж',
- 'store_sales_id' => 'ИД сущности типа продаж',
- 'value' => 'Значение',
+ 'type_sales_id' => 'ИД сущности типа продаж',
+ 'type_sales_value' => 'Значение',
'created_at' => 'Дата создания',
'updated_at' => 'Дата обновления',
'created_by' => 'ID создателя записи',
'updated_by' => 'ID обновителя записи',
];
}
-}
+
+ public static function getStoresList($id, $typeSales, $defaultModel, $defaultCondition)
+ {
+ $joinTable = $defaultModel === CityStore::class ? 'city_store' : 'store_type';
+
+ $list = BouquetForecast::find()
+ ->andWhere(['bouquet_id' => $id, 'type_sales' => $typeSales])
+ ->leftJoin("$joinTable AS df", "df.id = bouquet_forecast.type_sales_id")
+ ->select(["df.name AS name", 'type_sales_value AS value', 'type_sales_id AS id'])
+ ->orderBy('type_sales_id')
+ ->asArray()
+ ->all();
+
+ if (empty($list)) {
+ $list = $defaultModel::find()
+ ->andWhere($defaultCondition)
+ ->select([
+ 'id',
+ 'name',
+ new Expression('NULL as value')
+ ])
+ ->asArray()
+ ->all();
+ }
+
+ return $list;
+ }
+
+ public static function processSalesData($id, $year, $month, $salesData, $salesType)
+ {
+ foreach ($salesData as $key => $value) {
+ $model = BouquetForecast::findOne([
+ 'bouquet_id' => $id,
+ 'year' => $year,
+ 'month' => $month,
+ 'type_sales' => $salesType,
+ 'type_sales_id' => $key
+ ]);
+
+ if (empty($model)) {
+ $model = new BouquetForecast([
+ 'bouquet_id' => $id,
+ 'year' => $year,
+ 'month' => $month,
+ 'type_sales' => $salesType,
+ 'type_sales_id' => $key,
+ 'type_sales_value' => $value
+ ]);
+ $model->save();
+ } elseif ($model->type_sales_value != $value) {
+ $model->updateAttributes(['type_sales_value' => $value]);
+ }
+ }
+ }
+}
\ No newline at end of file
/** @var array|null $availableItems */
/** @var array|null $selectedItems */
-$this->title = 'Три гладиолуса';
+$this->title = $model->name;
$this->params['breadcrumbs'][] = ['label' => 'Букеты', 'url' => ['index']];
$this->params['breadcrumbs'][] = $this->title;
?>
<div class="bouquet-update p-4">
-<!-- --><?php //= Html::label('Редактирование букета', null, ['class' => 'h4']) ?>
<h3 class="ms-3 d-inline"><?= Html::encode("Редактирование букета: ") ?></h3>
<h2 class="d-inline"><strong><?= Html::encode($this->title) ?></strong></h2>
<div class="row">
<?php
-use app\widgets\FileUploadWidget;
-use app\widgets\MediaUploader;
-use dosamigos\fileupload\FileUploadUI;
use kartik\file\FileInput;
use yii\helpers\ArrayHelper;
use yii\helpers\Html;
use yii\helpers\Url;
use yii\widgets\ActiveForm;
-use yii\widgets\DetailView;
use yii_app\records\BouquetComposition;
use yii_app\records\MatrixType;
/** @var yii\web\View $this */
-/** @var yii_app\records\BouquetComposition $model
- */
+/** @var yii_app\records\BouquetComposition $model */
+/** @var array $bouquetCompositionProducts */
+/** @var array $storesTypeList */
+/** @var array $marketplaceList */
+/** @var array $onlineStoresList */
+/** @var array $photoFiles */
+/** @var array $videoFiles */
+/** @var array $processFiles */
-//$this->title = $model->name;
-$this->title = 'Три гладиолуса';
-//$this->title = $model->name;
+$this->title = $model->name;
$this->params['breadcrumbs'][] = ['label' => 'Букеты', 'url' => ['index']];
$this->params['breadcrumbs'][] = $this->title;
?>
<?php
+
+$photoConfig = array_map(fn($file) => [
+ 'caption' => $file->url,
+ 'key' => $file->id,
+ 'url' => Url::to(['file/delete', 'id' => $file->id]),
+], $photoFiles);
+?>
+<?php
$this->registerCss("
.file-caption {
max-width: 62% !important;
?>
<div class="bouquet-view border-bottom-4 p-4">
<?php $form = ActiveForm::begin([
- 'options' => ['enctype' => 'multipart/form-data'], // Это необходимо для загрузки файлов
+ 'options' => ['enctype' => 'multipart/form-data'],
]);
?>
<div class="row d-flex border-bottom justify-content-between align-items-center py-4">
</div>
<div class="row bg-white border rounded shadow-sm">
+ <?php foreach ($bouquetCompositionProducts as $product) { ?>
<div class="row ms-1 py-2">
- <div class="col-md-4 text-center">Гладиолусы краш</div>
- <div class="col-md-2 text-center">3.0</div>
+ <div class="col-md-4 text-center"><?= $product->product->name ?></div>
+ <div class="col-md-2 text-center"><?= $product->count ?></div>
<div class="col-md-1 text-center">10%</div>
<div class="col-md-2 text-center">30%</div>
<div class="col-md-1 text-center">10%</div>
<div class="col-md-2 text-center">3.2%</div>
</div>
+ <?php } ?>
</div>
<div class="row pt-3">
<div class="col-md-8"></div>
<div class="col-md-4">
- <?= Html::submitButton('Редактировать', ['class' => 'btn btn-warning w-100']) ?>
+ <?= Html::a('Редактировать', Url::to("/bouquet/update?id=$model->id"), ['class' => 'btn btn-warning w-100']) ?>
</div>
</div>
</div>
<div class="row w-100 w-md-75 w-lg-50">
<?php $model = new BouquetComposition(); ?>
<div class="row mb-2"><?= Html::label("Фото", null, ['class' => 'text-center font-weight-bold pt-5 h5']) ?></div>
- <?= $form->field($model, 'photo_id[]')->widget(FileInput::class, [
+ <?= $form->field($model, 'photo_bouquet[]')->widget(FileInput::class, [
'options' => [
'id' => 'bouquet-file-upload',
'multiple' => true, // Поддержка выбора нескольких файлов
+ 'accept' => 'image/png, image/jpeg, image/jpg',
],
'language' => 'ru',
'pluginOptions' => [
+ 'initialPreview' => $photoUrls,
+ 'initialPreviewConfig' => $photoConfig,
'showPreview' => true,
'showUpload' => false,
'showCancel' => false,
'mainClass' => 'input-group-lg',
- 'initialPreview' => [], // Задайте начальный список для предварительного просмотра (если есть)
+// 'initialPreview' => [], // Задайте начальный список для предварительного просмотра (если есть)
'maxFileSize' => 2800, // Максимальный размер файла (в килобайтах)
- 'dropZoneTitle' => 'Выберите файл', // Текст на зоне для перетаскивания
+ 'dropZoneTitle' => 'Выберите изображение', // Текст на зоне для перетаскивания
'browseOnZoneClick' => true, // Разрешить клик по зоне перетаскивания
'fileActionSettings' => [
'showZoom' => false, // Убираем иконку для увеличения
<div class="row">
<div class="col-md-6 w-100 w-md-75 w-lg-50">
<div class="row mb-2"><?= Html::label("Презентация", null, ['class' => 'text-center font-weight-bold pt-5 h5']) ?></div>
- <?= $form->field($model, 'photo_id[]')->widget(FileInput::class, [
+ <?= $form->field($model, 'video_presentation')->widget(FileInput::class, [
'options' => [
'id' => 'video-file-upload',
- 'multiple' => false, // Поддержка выбора нескольких файлов
+ 'multiple' => false,
+ 'accept' => 'video/mp4, video/avi, video/mov, video/mkv',
],
'language' => 'ru',
'pluginOptions' => [
'showUpload' => false,
'showCancel' => false,
'mainClass' => 'input-group-lg',
- 'initialPreview' => [], // Задайте начальный список для предварительного просмотра (если есть)
- 'maxFileSize' => 2800, // Максимальный размер файла (в килобайтах)
- 'dropZoneTitle' => 'Выберите файл', // Текст на зоне для перетаскивания
+ 'initialPreview' => $videoUrls,
+ 'maxFileSize' => 100000, // Максимальный размер файла (в килобайтах)
+ 'dropZoneTitle' => 'Выберите видеофайл', // Текст на зоне для перетаскивания
'browseOnZoneClick' => true, // Разрешить клик по зоне перетаскивания
'fileActionSettings' => [
- 'showZoom' => false, // Убираем иконку для увеличения
+ 'showZoom' => false,
],
],
])->label(false) ?>
</div>
<div class="col-md-6 w-100 w-md-75 w-lg-50">
<div class="row mb-2"><?= Html::label("Процесс сборки", null, ['class' => 'text-center font-weight-bold pt-5 h5']) ?></div>
- <?= $form->field($model, 'photo_id[]')->widget(FileInput::class, [
+ <?= $form->field($model, 'video_build_process')->widget(FileInput::class, [
'options' => [
'id' => 'presentation-file-upload',
- 'multiple' => false, // Поддержка выбора нескольких файлов
+ 'multiple' => false,
+ 'accept' => 'video/mp4, video/avi, video/mov, video/mkv',
],
'language' => 'ru',
'pluginOptions' => [
'showUpload' => false,
'showCancel' => false,
'mainClass' => 'input-group-lg',
- 'initialPreview' => [], // Задайте начальный список для предварительного просмотра (если есть)
- 'maxFileSize' => 2800, // Максимальный размер файла (в килобайтах)
- 'dropZoneTitle' => 'Выберите файл', // Текст на зоне для перетаскивания
+ 'initialPreview' => $processUrls,
+ 'maxFileSize' => 100000, // Максимальный размер файла (в килобайтах)
+ 'dropZoneTitle' => 'Выберите видеофайл',
'browseOnZoneClick' => true, // Разрешить клик по зоне перетаскивания
'fileActionSettings' => [
'showZoom' => false, // Убираем иконку для увеличения
</div>
<div class="row">
<div class="container border p-3" style="max-height: 185px; background-color: white; overflow: auto;">
- <div class="row align-items-center mb-2">
- <?php foreach ($storesTypeList as $type) {
- echo '<div class="col-md-8">
- <label class="col-form-label">'.$type.'</label>
- </div>
- <div class="col-md-4">
- <input type="number" class="form-control h-25" placeholder="Введите значение">
- </div>';
- } ?>
- </div>
+ <?php foreach ($storesTypeList as $item) { ?>
+ <div class="row align-items-center mb-2">
+ <div class="col-md-8">
+ <?= Html::label($item['name'], null, ['class' => "col-form-label"]); ?>
+ </div>
+ <div class="col-md-4">
+ <?= Html::input('number', "BouquetForecast[type_sales_value][offline][{$item['id']}]", $item['value'], ['class' => 'form-control']) ?>
+ </div>
+ </div>
+ <?php } ?>
</div>
</div>
+
<div class="row">
<?= Html::label("Интернет магазины", null, ['class' => 'text-center font-weight-bold pt-3 h6']) ?>
</div>
<div class="row">
<div class="container border p-3" style="max-height: 185px;background-color: white; overflow: auto;">
- <div class="row align-items-center mb-2">
- <?php foreach ($storesList as $store) {
- echo '<div class="col-md-8">
- <label class="col-form-label">'.$store.'</label>
- </div>
- <div class="col-md-4">
- <input type="number" class="form-control h-25" placeholder="Введите значение">
- </div>';
- } ?>
- </div>
+ <?php foreach ($onlineStoresList as $item) { ?>
+ <div class="row align-items-center mb-2">
+ <div class="col-md-8">
+ <?= Html::label($item['name'], null, ['class' => "col-form-label"]); ?>
+ </div>
+ <div class="col-md-4">
+ <?= Html::input('number', "BouquetForecast[type_sales_value][online][{$item['id']}]", $item['value'], ['class' => 'form-control']) ?>
+ </div>
+ </div>
+ <?php } ?>
</div>
</div>
+
<div class="row">
<?= Html::label("Маркетплейсы", null, ['class' => 'text-center font-weight-bold pt-3 h6']) ?>
</div>
<div class="row">
<div class="container border p-3" style="max-height: 185px; background-color: white; overflow: auto;">
- <div class="row align-items-center mb-2">
- <?php foreach ($storesList as $store) {
- echo '<div class="col-md-8">
- <label class="col-form-label">'.$store.'</label>
- </div>
- <div class="col-md-4">
- <input type="number" class="form-control" placeholder="Введите значение">
- </div>';
- } ?>
- </div>
+ <?php foreach ($marketplaceList as $item) { ?>
+ <div class="row align-items-center mb-2">
+ <div class="col-md-8">
+ <?= Html::label($item['name'], null, ['class' => "col-form-label"]); ?>
+ </div>
+ <div class="col-md-4">
+ <?= Html::input('number', "BouquetForecast[type_sales_value][marketplace][{$item['id']}]", $item['value'], ['class' => 'form-control']) ?>
+ </div>
+ </div>
+ <?php } ?>
</div>
</div>
</div>
+++ /dev/null
-<?php
-
-namespace app\widgets;
-
-use yii\base\Widget;
-use yii\helpers\Html;
-
-class FileUploadWidget extends Widget
-{
- public $containerId = 'file-upload-widget'; // Идентификатор контейнера
- public $inputId = 'file-upload'; // Идентификатор input для файлов
- public $maxFiles = 10; // Максимальное количество файлов (по желанию)
-
- public function init()
- {
- parent::init();
- }
-
- public function run()
- {
- $this->registerAssets();
-
- return $this->renderContent();
- }
-
- // Рендеринг HTML-контента для виджета
- protected function renderContent()
- {
- return Html::tag('div',
- Html::tag('label',
- Html::tag('input', '', ['type' => 'file', 'id' => $this->inputId, 'multiple' => true]) .
- Html::tag('span', '+', ['class' => 'plus-icon']),
- ['for' => $this->inputId, 'class' => 'upload-label']
- ),
- ['id' => $this->containerId, 'class' => 'file-container']
- );
- }
-
- // Встраивание CSS и JS непосредственно в HTML
- protected function registerAssets()
- {
- $view = $this->getView();
-
- $css = <<<CSS
- .file-container {
- display: flex;
- flex-wrap: wrap;
- gap: 10px;
- }
-
- .upload-label {
- position: relative;
- width: 100px;
- height: 100px;
- background-color: #f0f0f0;
- border: 2px dashed #ccc;
- display: flex;
- justify-content: center;
- align-items: center;
- cursor: pointer;
- }
-
- .upload-label input[type="file"] {
- display: none;
- }
-
- .plus-icon {
- font-size: 2rem;
- color: #555;
- }
-
- .uploaded-image {
- width: 100px;
- height: 100px;
- object-fit: cover;
- border: 2px solid #ccc;
- }
-
- .uploaded-container {
- position: relative;
- }
-
- .remove-btn {
- position: absolute;
- top: 5px;
- right: 5px;
- background-color: red;
- color: white;
- border: none;
- border-radius: 50%;
- width: 20px;
- height: 20px;
- font-size: 14px;
- cursor: pointer;
- }
- CSS;
-
- $js = <<<JS
- (function(\$) {
- \$(document).on('change', '#$this->inputId', function(event) {
- const files = event.target.files;
-
- for (const file of files) {
- const reader = new FileReader();
- reader.onload = function(e) {
- const fileContainer = \$(<div class="uploaded-container"></div>);
-
- const img = \$(<img src="\${e.target.result}" class="uploaded-image">);
- const removeButton = \$(<button class="remove-btn">X</button>);
-
- removeButton.on('click', function() {
- fileContainer.remove();
- });
-
- fileContainer.append(img).append(removeButton);
- \$('#$this->containerId').prepend(fileContainer);
- };
-
- reader.readAsDataURL(file);
- }
- });
- })(jQuery);
- JS;
-
- // Вставляем CSS и JS в страницу
- $view->registerCss($css);
- $view->registerJs($js);
- }
-}