125 lines
6.4 KiB
JavaScript
125 lines
6.4 KiB
JavaScript
// js/view.js
|
||
|
||
export default {
|
||
renderProductList(products, onEdit, onDelete) {
|
||
const list = document.getElementById('productsList');
|
||
list.innerHTML = '';
|
||
products.forEach(product => {
|
||
const col = document.createElement('div');
|
||
col.className = 'col';
|
||
// Если image не начинается с "data:" — выводим как есть (старые товары), иначе как base64
|
||
const imgSrc = product.image?.startsWith('data:') ? product.image : (product.image || 'images/no-image.png');
|
||
col.innerHTML = `
|
||
<div class="card h-100">
|
||
<img src="${imgSrc}" class="card-img-top" alt="${product.name}" style="width: 100%; height: 300px; object-fit: cover;">
|
||
<div class="card-body">
|
||
<h5 class="card-title">${product.name}</h5>
|
||
<p class="card-text">${product.price} руб</p>
|
||
<p class="card-text">
|
||
<small class="text-muted">Категория: ${product.category?.name || '-'}</small><br>
|
||
<small class="text-muted">Бренд: ${product.brand?.name || '-'}</small>
|
||
</p>
|
||
<button class="btn btn-primary btn-sm me-2 edit-btn"><i class="bi bi-pencil"></i> Редактировать</button>
|
||
<button class="btn btn-danger btn-sm delete-btn"><i class="bi bi-trash"></i> Удалить</button>
|
||
</div>
|
||
</div>
|
||
`;
|
||
// Навесить обработчики
|
||
col.querySelector('.edit-btn').onclick = () => onEdit(product.id);
|
||
col.querySelector('.delete-btn').onclick = () => onDelete(product.id);
|
||
list.appendChild(col);
|
||
});
|
||
},
|
||
|
||
showProductModal({ product = {}, categories = [], brands = [] }, onSubmit) {
|
||
const modalContent = document.getElementById('productModalContent');
|
||
modalContent.innerHTML = `
|
||
<div class="modal-header">
|
||
<h5 class="modal-title">${product.id ? 'Редактировать' : 'Добавить'} товар</h5>
|
||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||
</div>
|
||
<form id="productForm">
|
||
<div class="modal-body">
|
||
<div class="mb-3">
|
||
<label class="form-label">Название товара</label>
|
||
<input type="text" class="form-control" name="name" value="${product.name || ''}" required>
|
||
</div>
|
||
<div class="mb-3">
|
||
<label class="form-label">Цена (руб)</label>
|
||
<input type="number" class="form-control" name="price" value="${product.price || ''}" required>
|
||
</div>
|
||
<div class="mb-3">
|
||
<label class="form-label">Изображение</label>
|
||
<input type="file" class="form-control" name="image" accept="image/*" ${product.id ? "" : "required"}>
|
||
<img id="previewImage" src="${product.image || ''}" style="max-width:100%;margin-top:10px;${product.image ? '' : 'display:none;'}"/>
|
||
</div>
|
||
<div class="mb-3">
|
||
<label class="form-label">Категория</label>
|
||
<select class="form-select" name="categoryId" required>
|
||
<option value="">Выберите категорию</option>
|
||
${categories.map(cat => `
|
||
<option value="${cat.id}" ${product.categoryId == cat.id ? 'selected' : ''}>${cat.name}</option>
|
||
`).join('')}
|
||
</select>
|
||
</div>
|
||
<div class="mb-3">
|
||
<label class="form-label">Бренд</label>
|
||
<select class="form-select" name="brandId" required>
|
||
<option value="">Выберите бренд</option>
|
||
${brands.map(br => `
|
||
<option value="${br.id}" ${product.brandId == br.id ? 'selected' : ''}>${br.name}</option>
|
||
`).join('')}
|
||
</select>
|
||
</div>
|
||
</div>
|
||
<div class="modal-footer">
|
||
<button type="submit" class="btn btn-success">${product.id ? 'Сохранить' : 'Добавить'}</button>
|
||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Отмена</button>
|
||
</div>
|
||
</form>
|
||
`;
|
||
|
||
// Показываем модалку через Bootstrap JS
|
||
const modal = new bootstrap.Modal(document.getElementById('productModal'));
|
||
modal.show();
|
||
|
||
// Предпросмотр выбранной картинки
|
||
const fileInput = modalContent.querySelector('input[type="file"]');
|
||
const preview = modalContent.querySelector('#previewImage');
|
||
fileInput.onchange = () => {
|
||
const file = fileInput.files[0];
|
||
if (file) {
|
||
const reader = new FileReader();
|
||
reader.onload = e => {
|
||
preview.src = e.target.result;
|
||
preview.style.display = '';
|
||
};
|
||
reader.readAsDataURL(file);
|
||
}
|
||
};
|
||
|
||
// Обработка формы (с поддержкой base64-картинки)
|
||
document.getElementById('productForm').onsubmit = async (e) => {
|
||
e.preventDefault();
|
||
const formData = new FormData(e.target);
|
||
const obj = Object.fromEntries(formData.entries());
|
||
obj.price = +obj.price;
|
||
obj.categoryId = +obj.categoryId;
|
||
obj.brandId = +obj.brandId;
|
||
|
||
if (fileInput.files.length) {
|
||
const file = fileInput.files[0];
|
||
const reader = new FileReader();
|
||
reader.onload = function(event) {
|
||
obj.image = event.target.result; // base64
|
||
onSubmit(obj, modal);
|
||
};
|
||
reader.readAsDataURL(file);
|
||
} else {
|
||
obj.image = product.image || '';
|
||
onSubmit(obj, modal);
|
||
}
|
||
};
|
||
}
|
||
};
|