Help
RSS
API
Feed
Maltego
Contact
Domain > carthing.ru
×
More information on this domain is in
AlienVault OTX
Is this malicious?
Yes
No
DNS Resolutions
Date
IP Address
2025-08-27
185.50.25.7
(
ClassC
)
2026-02-27
77.222.40.47
(
ClassC
)
Port 80
HTTP/1.1 200 OKServer: nginx/1.27.4Date: Fri, 27 Feb 2026 23:57:30 GMTContent-Type: text/html; charsetUTF-8Content-Length: 67095Connection: keep-aliveKeep-Alive: timeout10Vary: Accept-EncodingX-Powered-By: PHP/7.1.33 !DOCTYPE html>html langru>head> meta charsetUTF-8> meta nameviewport contentwidthdevice-width, initial-scale1.0> title>Калькулятор ТО Toyota/title> style> * { box-sizing: border-box; margin: 0; padding: 0; font-family: Arial, sans-serif; } :root { --primary-blue: #1e3a8a; --secondary-blue: #3b82f6; --light-blue: #e6f7ff; --white: #ffffff; --light-gray: #f5f5f5; --dark-gray: #333333; --red: #e30613; --green: #4CAF50; --orange: #FF9800; } body { background-color: var(--light-gray); padding: 20px; color: var(--dark-gray); } .container { max-width: 1300px; margin: 0 auto; background: white; padding: 30px; border-radius: 10px; box-shadow: 0 0 15px rgba(0, 0, 0, 0.1); } h1 { text-align: center; color: var(--primary-blue); margin-bottom: 30px; } .subtitle { text-align: center; color: #666; margin-bottom: 30px; } .calculator { display: flex; flex-direction: column; gap: 20px; } .selectors { display: flex; justify-content: space-between; gap: 15px; margin-bottom: 20px; } .selector { flex: 1; } .selector label { display: block; margin-bottom: 8px; font-weight: bold; color: #333; } .selector select { width: 100%; padding: 12px; border: 1px solid #ddd; border-radius: 5px; font-size: 16px; background-color: #f9f9f9; transition: border-color 0.3s; } .selector select:focus { border-color: var(--primary-blue); outline: none; } .result { margin-top: 30px; padding: 20px; background-color: #f9f9f9; border-radius: 5px; border-left: 4px solid var(--primary-blue); } .result h2 { color: #333; margin-bottom: 15px; } table { width: 100%; border-collapse: collapse; margin-top: 15px; background-color: var(--white); box-shadow: 0 2px 15px rgba(0,0,0,0.05); } table th, table td { padding: 12px; text-align: left; border-bottom: 1px solid #ddd; } table th { background-color: var(--primary-blue); color: var(--white); font-weight: bold; } tr:nth-child(even) { background-color: var(--light-blue); } tr:hover { background-color: #f0f8ff; } .score-column { background-color: var(--light-blue); font-weight: bold; } .quantity-input { width: 60px; padding: 5px; text-align: center; border: 1px solid #ddd; border-radius: 3px; } .quantity-input:disabled { background-color: #f5f5f5; color: #999; } .service-checkbox { transform: scale(1.2); margin-right: 5px; } .catalog-number { font-family: monospace; background-color: #f0f0f0; padding: 4px 8px; border-radius: 3px; cursor: pointer; transition: background-color 0.2s; color: var(--secondary-blue); font-weight: bold; position: relative; } .catalog-number:hover { background-color: #e0e0e0; text-decoration: underline; } .copy-notification { position: absolute; background: #4CAF50; color: white; padding: 2px 6px; border-radius: 3px; font-size: 12px; top: -25px; left: 50%; transform: translateX(-50%); opacity: 0; transition: opacity 0.3s; } .copy-notification.show { opacity: 1; } .stock-low { color: var(--red); font-weight: bold; } .total { font-weight: bold; font-size: 1.2em; margin-top: 20px; text-align: right; color: var(--primary-blue); } .hidden { display: none; } .disabled-service { color: #999; } .service-group { background-color: #f0f0f0; font-weight: bold; } .alternative-service { padding-left: 30px !important; background-color: #f9f9f9; } .notification { position: fixed; top: 20px; right: 20px; padding: 15px 20px; background-color: #4CAF50; color: white; border-radius: 5px; box-shadow: 0 3px 10px rgba(0,0,0,0.2); opacity: 0; transition: opacity 0.3s; z-index: 1000; } .notification.show { opacity: 1; } .error { color: var(--red); padding: 15px; background-color: #ffeeee; border-radius: 4px; margin-bottom: 20px; } .search-form { background-color: var(--white); padding: 25px; border-radius: 8px; box-shadow: 0 2px 15px rgba(0,0,0,0.05); margin-bottom: 30px; margin-top: 30px; } .search-form inputtypetext { width: 70%; padding: 12px 15px; border: 1px solid #ddd; border-radius: 4px; font-size: 16px; margin-right: 10px; } .search-form button { background-color: var(--secondary-blue); color: white; border: none; padding: 12px 25px; border-radius: 4px; cursor: pointer; font-size: 16px; transition: background-color 0.3s; } .search-form button:hover { background-color: var(--primary-blue); } .edit-controls { display: flex; justify-content: space-between; align-items: center; margin-bottom: 15px; padding-bottom: 10px; border-bottom: 2px solid #eee; } .edit-controls-left { display: flex; gap: 10px; } .edit-controls-right { display: flex; gap: 10px; align-items: center; } .edit-btn { background-color: var(--orange); color: white; border: none; padding: 8px 15px; border-radius: 4px; cursor: pointer; font-size: 14px; transition: background-color 0.3s; } .edit-btn:hover { background-color: #e68900; } .save-btn { background-color: var(--green); color: white; border: none; padding: 8px 15px; border-radius: 4px; cursor: pointer; font-size: 14px; transition: background-color 0.3s; } .save-btn:hover { background-color: #45a049; } .delete-btn { background-color: var(--red); color: white; border: none; padding: 8px 15px; border-radius: 4px; cursor: pointer; font-size: 14px; transition: background-color 0.3s; } .delete-btn:hover { background-color: #c10505; } .copy-template-btn { background-color: #9C27B0; color: white; border: none; padding: 8px 15px; border-radius: 4px; cursor: pointer; font-size: 14px; transition: background-color 0.3s; } .copy-template-btn:hover { background-color: #7B1FA2; } .edit-mode .catalog-input { display: inline-block; width: 120px; padding: 4px 8px; border: 1px solid #ddd; border-radius: 3px; } .edit-mode .name-input { display: inline-block; width: 200px; padding: 4px 8px; border: 1px solid #ddd; border-radius: 3px; } .edit-mode .price-input { display: inline-block; width: 100px; padding: 4px 8px; border: 1px solid #ddd; border-radius: 3px; } .edit-checkbox { transform: scale(1.2); cursor: pointer; } .editing-row { background-color: #fff8e1 !important; } .new-row { background-color: #e8f5e9 !important; } .save-changes-btn { background-color: var(--primary-blue); color: white; border: none; padding: 10px 20px; border-radius: 4px; cursor: pointer; font-size: 16px; margin-top: 20px; transition: background-color 0.3s; } .save-changes-btn:hover { background-color: #15296d; } .loading { opacity: 0.6; pointer-events: none; } .add-to-db-btn { background-color: #9C27B0; color: white; border: none; padding: 6px 12px; border-radius: 4px; cursor: pointer; font-size: 12px; margin-left: 5px; transition: background-color 0.3s; } .add-to-db-btn:hover { background-color: #7B1FA2; } .move-btn { background-color: #607D8B; color: white; border: none; padding: 6px 12px; border-radius: 4px; cursor: pointer; font-size: 12px; margin-left: 5px; transition: background-color 0.3s; } .move-btn:hover { background-color: #455A64; } .move-controls { display: flex; flex-direction: column; gap: 5px; margin-right: 5px; } .current-config { background-color: #e8f4fd; padding: 10px 15px; border-radius: 5px; margin-bottom: 15px; border-left: 4px solid var(--secondary-blue); } .current-config strong { color: var(--primary-blue); } .service-count { background-color: var(--secondary-blue); color: white; padding: 4px 8px; border-radius: 3px; font-size: 12px; } .modal-overlay { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background-color: rgba(0, 0, 0, 0.5); display: flex; justify-content: center; align-items: center; z-index: 2000; opacity: 0; visibility: hidden; transition: all 0.3s; } .modal-overlay.show { opacity: 1; visibility: visible; } .modal { background-color: white; padding: 30px; border-radius: 10px; max-width: 500px; width: 90%; max-height: 90vh; overflow-y: auto; } .modal h3 { margin-top: 0; color: var(--primary-blue); margin-bottom: 20px; } .modal label { display: block; margin-bottom: 5px; font-weight: bold; } .modal input, .modal select { width: 100%; padding: 10px; margin-bottom: 15px; border: 1px solid #ddd; border-radius: 4px; font-size: 16px; } .modal-buttons { display: flex; justify-content: flex-end; gap: 10px; margin-top: 20px; } .cancel-btn { background-color: #999; color: white; border: none; padding: 10px 20px; border-radius: 4px; cursor: pointer; } .confirm-btn { background-color: var(--green); color: white; border: none; padding: 10px 20px; border-radius: 4px; cursor: pointer; } .group-select { padding: 4px 8px; border: 1px solid #ddd; border-radius: 3px; background-color: white; } @media (max-width: 768px) { .selectors { flex-direction: column; } table { font-size: 14px; } .quantity-input { width: 50px; } .alternative-service { padding-left: 15px !important; } .search-form inputtypetext { width: 100%; margin-bottom: 10px; } .search-form button { width: 100%; } .edit-mode .catalog-input { width: 80px; } .edit-mode .name-input { width: 150px; } .edit-mode .price-input { width: 80px; } .move-controls { flex-direction: row; } .edit-controls { flex-direction: column; gap: 10px; align-items: flex-start; } .edit-controls-left, .edit-controls-right { width: 100%; justify-content: space-between; } } /style>/head>body> div classcontainer> h1>Рассчитайте стоимость технического обслуживания TOYOTA/h1> p classsubtitle>Всего за 3 клика: выберите модель, модификацию и пробег/p> div classcalculator> div classselectors> div classselector> label formodel>Модель автомобиля/label> select idmodel onchangeupdateModifications()> option value>-- Выберите модель --/option> option valuecamry>Camry/option> option valuecorolla>Corolla/option> option valuerav4>RAV4/option> option valueland_cruiser_prado>Land Cruiser Prado/option> option valueland_cruiser>Land Cruiser/option> option valuehighlander>Highlander/option> option valuehilux>Hilux/option> option valuefortuner>Fortuner/option> option valuealphard>Alphard/option> option valuevellfire>Vellfire/option> option valueyaris>Yaris/option> option valuechr>C-HR/option> option valueavensis>Avensis/option> option valueavanza>Avanza/option> option valuerush>Rush/option> option valueinnova>Innova/option> option valueprius>Prius/option> option valueaygo>Aygo/option> option valuegt86>GT86/option> option valuesupra>Supra/option> option valuetacoma>Tacoma/option> option valuetundra>Tundra/option> option valuesequoia>Sequoia/option> option valuesienna>Sienna/option> option valuevenza>Venza/option> option valueverso>Verso/option> option valueiq>iQ/option> /select> /div> div classselector> label formodification>Модификация/label> select idmodification onchangeupdateMileage() disabled> option value>-- Сначала выберите модель --/option> /select> /div> div classselector> label formileage>Пробег, км/label> select idmileage onchangeloadServiceTemplate() disabled> option value>-- Сначала выберите модификацию --/option> /select> /div> /div> div idresult classresult hidden> div classcurrent-config idcurrentConfig> !-- Здесь будет отображаться текущая конфигурация --> /div> div classedit-controls> div classedit-controls-left> button classedit-btn onclicktoggleEditMode()>Режим редактирования/button> button classcopy-template-btn onclickshowCopyTemplateModal()>Копировать в другой пробег/button> /div> div classedit-controls-right> span idserviceCount classservice-count>0 услуг/span> button classsave-btn onclickaddNewRow() styledisplay:none;>Добавить строку/button> /div> /div> table idservices-table> thead> tr> th width5%>/th> th>Наименование/th> th>Каталожный номер/th> th>Количество/th> th>Стоимость за ед./th> th>Стоимость/th> th width15%>/th> /tr> /thead> tbody idservices-list> !-- Здесь будут отображаться услуги --> /tbody> /table> div idtotal classtotal>/div> button classsave-changes-btn onclicksaveChanges() styledisplay:none;>Сохранить изменения/button> /div> /div> div classsearch-form> h2>Поиск каталожных номеров/h2> form methodget> input typetext namecatalog_number value placeholderВведите каталожный номер required> button typesubmit>Найти/button> /form> /div> /div> !-- Модальное окно для добавления в базу --> div idaddToDbModal classmodal-overlay> div classmodal> h3>Добавить новую позицию в базу данных/h3> div idmodalContent> div idmodalMessage styledisplay:none; margin-bottom: 15px; padding: 10px; border-radius: 4px;>/div> label formodalCatalogNumber>Каталожный номер:/label> input typetext idmodalCatalogNumber readonly> label formodalItemName>Наименование:/label> input typetext idmodalItemName placeholderВведите наименование товара> label formodalItemPrice>Стоимость за единицу (руб.):/label> input typenumber idmodalItemPrice placeholder0.00 step0.01 min0> div classmodal-buttons> button classcancel-btn onclickcloseModal()>Отмена/button> button classconfirm-btn onclickconfirmAddToDatabase()>Добавить в базу/button> /div> /div> /div> /div> !-- Модальное окно для копирования шаблона --> div idcopyTemplateModal classmodal-overlay> div classmodal> h3>Копировать шаблон в другой пробег/h3> div idcopyModalContent> div idcopyModalMessage styledisplay:none; margin-bottom: 15px; padding: 10px; border-radius: 4px;>/div> p>Текущий шаблон: strong idcurrentTemplateInfo>/strong>/p> label fortargetMileage>Выберите пробег для копирования:/label> select idtargetMileage> option value10000>10 000 км/option> option value20000>20 000 км/option> option value30000>30 000 км/option> option value40000>40 000 км/option> option value50000>50 000 км/option> option value60000>60 000 км/option> option value70000>70 000 км/option> option value80000>80 000 км/option> option value90000>90 000 км/option> option value100000>100 000 км/option> /select> div classmodal-buttons> button classcancel-btn onclickcloseCopyModal()>Отмена/button> button classconfirm-btn onclickconfirmCopyTemplate()>Копировать/button> /div> /div> /div> /div> div idnotification classnotification>Номер скопирован в буфер обмена/div> script> // База данных услуг для разных моделей, модификаций и пробегов const serviceDatabase { camry: { 2.0 л (150 л.с.) бензин: { 10000: { name: Замена масла в двигателе, quantity: 1, price: 800, enabled: true, group: Техническое обслуживание, catalogNumber: SER-001 }, { name: Масло Toyota 5W30, quantity: 4.5, price: 1500, enabled: true, group: Техническое обслуживание, alternative: true, catalogNumber: OIL-5W30 }, { name: Масло TOYOTA 0W20 (ilsac gf-5/6), quantity: 4.5, price: 1700, enabled: false, group: Техническое обслуживание, alternative: true, catalogNumber: OIL-0W20 }, { name: Масляный фильтр Toyota, quantity: 1, price: 1400, enabled: true, group: Техническое обслуживание, catalogNumber: FIL-001 }, { name: Прокладка сливной пробки ДВС Toyota, quantity: 1, price: 190, enabled: true, group: Техническое обслуживание, catalogNumber: GASK-001 } } }, corolla: { 1.6 л (132 л.с.) бензин: { 10000: { name: Замена масла в двигателе, quantity: 1, price: 800, enabled: true, group: Техническое обслуживание, catalogNumber: SER-001 }, { name: Масло Toyota 5W30, quantity: 4.2, price: 1500, enabled: true, group: Техническое обслуживание, alternative: true, catalogNumber: OIL-5W30 }, { name: Масляный фильтр Toyota, quantity: 1, price: 1400, enabled: true, group: Техническое обслуживание, catalogNumber: FIL-001B }, { name: Прокладка сливной пробки ДВС Toyota, quantity: 1, price: 190, enabled: true, group: Техническое обслуживание, catalogNumber: GASK-001 } } }, rav4: { 2.0 л (150 л.с.) бензин: { 10000: { name: Замена масла в двигателе, quantity: 1, price: 800, enabled: true, group: Техническое обслуживание, catalogNumber: SER-001 }, { name: Масло Toyota 5W30, quantity: 4.7, price: 1500, enabled: true, group: Техническое обслуживание, alternative: true, catalogNumber: OIL-5W30 }, { name: Масляный фильтр Toyota, quantity: 1, price: 1400, enabled: true, group: Техническое обслуживание, catalogNumber: FIL-001C }, { name: Прокладка сливной пробки ДВС Toyota, quantity: 1, price: 190, enabled: true, group: Техническое обслуживание, catalogNumber: GASK-001 } } } }; // Данные по модификациям const modifications { camry: 2.0 л (150 л.с.) бензин, 2.5 л (181 л.с.) бензин, 2.5 л (181 л.с.) гибрид, 3.5 л (249 л.с.) бензин, corolla: 1.6 л (132 л.с.) бензин, 1.8 л (140 л.с.) гибрид, 2.0 л (150 л.с.) бензин, rav4: 2.0 л (150 л.с.) бензин, 2.5 л (180 л.с.) бензин, 2.5 л (197 л.с.) гибрид, land_cruiser_prado: 2.7 л (163 л.с.) бензин, 2.8 л (177 л.с.) дизель, 4.0 л (249 л.с.) бензин, land_cruiser: 4.0 л (249 л.с.) бензин, 4.6 л (309 л.с.) бензин, 4.5 л (249 л.с.) дизель, highlander: 2.4 л (188 л.с.) бензин, 3.5 л (249 л.с.) бензин, 2.5 л (245 л.с.) гибрид, hilux: 2.4 л (160 л.с.) дизель, 2.7 л (166 л.с.) бензин, 2.8 л (177 л.с.) дизель, fortuner: 2.7 л (166 л.с.) бензин, 2.8 л (177 л.с.) дизель, alphard: 2.5 л (182 л.с.) гибрид, 3.5 л (280 л.с.) бензин, vellfire: 2.5 л (182 л.с.) гибрид, 3.5 л (280 л.с.) бензин, yaris: 1.0 л (69 л.с.) бензин, 1.3 л (99 л.с.) бензин, 1.5 л (110 л.с.) гибрид, chr: 1.2 л (116 л.с.) бензин, 1.8 л (122 л.с.) гибрид, 2.0 л (150 л.с.) гибрид, avensis: 1.6 л (132 л.с.) бензин, 1.8 л (147 л.с.) бензин, 2.0 л (152 л.с.) дизель, avanza: 1.3 л (95 л.с.) бензин, 1.5 л (109 л.с.) бензин, rush: 1.5 л (109 л.с.) бензин, innova: 2.0 л (139 л.с.) бензин, 2.7 л (166 л.с.) бензин, 2.4 л (150 л.с.) дизель, prius: 1.8 л (122 л.с.) гибрид, aygo: 1.0 л (72 л.с.) бензин, gt86: 2.0 л (200 л.с.) бензин, supra: 2.0 л (258 л.с.) бензин, 3.0 л (340 л.с.) бензин, tacoma: 2.7 л (159 л.с.) бензин, 3.5 л (278 л.с.) бензин, tundra: 4.6 л (310 л.с.) бензин, 5.7 л (386 л.с.) бензин, sequoia: 5.7 л (386 л.с.) бензин, sienna: 2.7 л (187 л.с.) бензин, 3.5 л (266 л.с.) бензин, venza: 2.7 л (187 л.с.) бензин, 3.5 л (266 л.с.) бензин, verso: 1.6 л (132 л.с.) бензин, 1.8 л (147 л.с.) бензин, iq: 1.0 л (68 л.с.) бензин }; // Доступные группы const availableGroups Техническое обслуживание, Салон, Тормозная система, Ходовая часть, Электрика, Кузовные работы, Дополнительные услуги ; // Текущие выбранные услуги let currentServices ; let currentGroups new Set(); let isEditMode false; let currentModel ; let currentModification ; let currentMileage ; let modalServiceIndex null; let hasUnsavedChanges false; // Функция для получения данных из базы данных async function getCatalogData(catalogNumber) { try { const response await fetch(get_catalog_data.php, { method: POST, headers: { Content-Type: application/x-www-form-urlencoded, }, body: `catalog_number${encodeURIComponent(catalogNumber)}` }); if (response.ok) { const data await response.json(); return data; } return { name: Не найдено, price: 0, exists: false }; } catch (error) { console.error(Ошибка при получении данных:, error); return { name: Ошибка загрузки, price: 0, exists: false }; } } // Функция для добавления товара в базу данных async function addItemToDatabase(catalogNumber, name, price) { try { const response await fetch(add_to_catalog.php, { method: POST, headers: { Content-Type: application/json, }, body: JSON.stringify({ catalog_number: catalogNumber, name: name, price: price }) }); if (response.ok) { const result await response.json(); return result; } return { success: false, error: Ошибка сервера }; } catch (error) { console.error(Ошибка при добавлении:, error); return { success: false, error: error.message }; } } // Функция для сохранения изменений async function saveChangesToServer() { try { // Подготавливаем данные для сохранения const servicesToSave currentServices.map(service > ({ name: service.name, catalogNumber: service.catalogNumber, quantity: service.quantity, price: service.price, enabled: service.enabled, group: service.group || Техническое обслуживание, alternative: service.alternative || false })); const response await fetch(save_services.php, { method: POST, headers: { Content-Type: application/json, }, body: JSON.stringify({ model: currentModel, modification: currentModification, mileage: currentMileage, services: servicesToSave }) }); if (response.ok) { const result await response.json(); return result; } return { success: false, error: Ошибка сервера }; } catch (error) { console.error(Ошибка при сохранении:, error); return { success: false, error: error.message }; } } // Функция для загрузки сохраненных услуг async function loadSavedServices() { try { const response await fetch(load_services.php, { method: POST, headers: { Content-Type: application/x-www-form-urlencoded, }, body: `model${encodeURIComponent(currentModel)}&modification${encodeURIComponent(currentModification)}&mileage${encodeURIComponent(currentMileage)}` }); if (response.ok) { const data await response.json(); return data; } return ; } catch (error) { console.error(Ошибка при загрузке:, error); return ; } } // Функция для копирования шаблона async function copyServiceTemplate(targetMileage) { try { const response await fetch(copy_service_template.php, { method: POST, headers: { Content-Type: application/json, }, body: JSON.stringify({ sourceModel: currentModel, sourceModification: currentModification, sourceMileage: currentMileage, targetMileage: targetMileage }) }); if (response.ok) { const result await response.json(); return result; } return { success: false, error: Ошибка сервера }; } catch (error) { console.error(Ошибка при копировании:, error); return { success: false, error: error.message }; } } // Показать модальное окно для добавления в базу function showAddToDbModal(index, catalogNumber, currentName , currentPrice 0) { modalServiceIndex index; document.getElementById(modalCatalogNumber).value catalogNumber; document.getElementById(modalItemName).value currentName || ; document.getElementById(modalItemPrice).value currentPrice || ; // Очищаем сообщение const modalMessage document.getElementById(modalMessage); modalMessage.style.display none; modalMessage.textContent ; modalMessage.className ; document.getElementById(addToDbModal).classList.add(show); } // Показать модальное окно для копирования шаблона function showCopyTemplateModal() { document.getElementById(currentTemplateInfo).textContent `${getModelName(currentModel)} - ${currentModification} - ${formatMileage(currentMileage)}`; // Очищаем сообщение const copyModalMessage document.getElementById(copyModalMessage); copyModalMessage.style.display none; copyModalMessage.textContent ; copyModalMessage.className ; document.getElementById(copyTemplateModal).classList.add(show); } // Закрыть модальное окно function closeModal() { document.getElementById(addToDbModal).classList.remove(show); modalServiceIndex null; } // Закрыть модальное окно копирования function closeCopyModal() { document.getElementById(copyTemplateModal).classList.remove(show); } // Подтвердить добавление в базу async function confirmAddToDatabase() { const catalogNumber document.getElementById(modalCatalogNumber).value; const name document.getElementById(modalItemName).value; const price document.getElementById(modalItemPrice).value; const modalMessage document.getElementById(modalMessage); if (!name.trim()) { modalMessage.textContent Пожалуйста, введите наименование товара; modalMessage.className error; modalMessage.style.display block; return; } if (!price || parseFloat(price) 0) { modalMessage.textContent Пожалуйста, введите корректную стоимость; modalMessage.className error; modalMessage.style.display block; return; } const result await addItemToDatabase(catalogNumber, name, price); if (result.success) { modalMessage.textContent `Товар успешно ${result.action created ? добавлен : обновлен} в базе данных!`; modalMessage.className ; modalMessage.style.backgroundColor #4CAF50; modalMessage.style.color white; modalMessage.style.display block; // Обновляем данные в текущей строке if (modalServiceIndex ! null) { currentServicesmodalServiceIndex.name name; currentServicesmodalServiceIndex.price parseFloat(price); currentServicesmodalServiceIndex.catalogNumber catalogNumber; // Закрываем модальное окно через 2 секунды setTimeout(() > { closeModal(); updateServicesTable(); showNotification(`Товар ${catalogNumber} добавлен в базу данных`, success); }, 2000); } } else { modalMessage.textContent Ошибка при добавлении в базу: + (result.error || Неизвестная ошибка); modalMessage.className error; modalMessage.style.display block; } } // Подтвердить копирование шаблона async function confirmCopyTemplate() { const targetMileage document.getElementById(targetMileage).value; const copyModalMessage document.getElementById(copyModalMessage); if (targetMileage currentMileage) { copyModalMessage.textContent Нельзя скопировать шаблон в тот же самый пробег; copyModalMessage.className error; copyModalMessage.style.display block; return; } const result await copyServiceTemplate(targetMileage); if (result.success) { copyModalMessage.textContent `Шаблон успешно скопирован в ${formatMileage(targetMileage)}!`; copyModalMessage.className ; copyModalMessage.style.backgroundColor #4CAF50; copyModalMessage.style.color white; copyModalMessage.style.display block; setTimeout(() > { closeCopyModal(); showNotification(`Шаблон скопирован в ${formatMileage(targetMileage)}`, success); }, 2000); } else { copyModalMessage.textContent Ошибка при копировании: + (result.error || Неизвестная ошибка); copyModalMessage.className error; copyModalMessage.style.display block; } } // Показать уведомление function showNotification(message, type info) { const notification document.getElementById(notification); notification.textContent message; notification.style.backgroundColor type error ? #e30613 : type success ? #4CAF50 : #2196F3; notification.classList.add(show); setTimeout(() > { notification.classList.remove(show); }, 3000); } // Получить название модели по значению function getModelName(modelValue) { const modelNames { camry: Camry, corolla: Corolla, rav4: RAV4, land_cruiser_prado: Land Cruiser Prado, land_cruiser: Land Cruiser, highlander: Highlander, hilux: Hilux, fortuner: Fortuner, alphard: Alphard, vellfire: Vellfire, yaris: Yaris, chr: C-HR, avensis: Avensis, avanza: Avanza, rush: Rush, innova: Innova, prius: Prius, aygo: Aygo, gt86: GT86, supra: Supra, tacoma: Tacoma, tundra: Tundra, sequoia: Sequoia, sienna: Sienna, venza: Venza, verso: Verso, iq: iQ }; return modelNamesmodelValue || modelValue; } // Форматировать пробег function formatMileage(mileage) { return mileage ? mileage.replace(/\B(?(\d{3})+(?!\d))/g, ) + км : ; } // Обновление списка модификаций function updateModifications() { const modelSelect document.getElementById(model); const modificationSelect document.getElementById(modification); const mileageSelect document.getElementById(mileage); const resultDiv document.getElementById(result); currentModel modelSelect.value; if (currentModel) { modificationSelect.disabled false; modificationSelect.innerHTML option value>-- Выберите модификацию --/option>; modificationscurrentModel.forEach(mod > { modificationSelect.innerHTML + `option value${mod}>${mod}/option>`; }); mileageSelect.innerHTML option value>-- Сначала выберите модификацию --/option>; mileageSelect.disabled true; resultDiv.classList.add(hidden); } else { modificationSelect.innerHTML option value>-- Сначала выберите модель --/option>; modificationSelect.disabled true; mileageSelect.innerHTML option value>-- Сначала выберите модификацию --/option>; mileageSelect.disabled true; resultDiv.classList.add(hidden); } } // Обновление списка пробега function updateMileage() { const modificationSelect document.getElementById(modification); const mileageSelect document.getElementById(mileage); const resultDiv document.getElementById(result); currentModification modificationSelect.value; if (currentModification) { mileageSelect.disabled false; mileageSelect.innerHTML option value>-- Выберите пробег --/option>; // Все возможные пробеги const allMileages 10000, 20000, 30000, 40000, 50000, 60000, 70000, 80000, 90000, 100000; allMileages.forEach(mileage > { mileageSelect.innerHTML + `option value${mileage}>${formatMileage(mileage)}/option>`; }); resultDiv.classList.add(hidden); } else { mileageSelect.innerHTML option value>-- Сначала выберите модификацию --/option>; mileageSelect.disabled true; resultDiv.classList.add(hidden); } } // Загрузка шаблона обслуживания async function loadServiceTemplate() { const modelSelect document.getElementById(model); const modificationSelect document.getElementById(modification); const mileageSelect document.getElementById(mileage); const resultDiv document.getElementById(result); const saveButton document.querySelector(.save-changes-btn); const configDiv document.getElementById(currentConfig); currentModel modelSelect.value; currentModification modificationSelect.value; currentMileage mileageSelect.value; if (currentModel && currentModification && currentMileage) { resultDiv.classList.add(loading); // Обновляем информацию о текущей конфигурации configDiv.innerHTML ` strong>Текущее ТО:/strong> ${getModelName(currentModel)} - ${currentModification} - ${formatMileage(currentMileage)} span stylefloat: right; color: #666;>Каждая комбинация имеет свою отдельную таблицу/span> `; // Загружаем сохраненные данные из базы const savedServices await loadSavedServices(); if (savedServices.length > 0) { // Используем сохраненные данные currentServices savedServices; showNotification(Загружен сохраненный шаблон ТО, success); } else { // Если сохраненных данных нет, проверяем стандартные const defaultServicesForConfig serviceDatabasecurrentModel?.currentModification?.currentMileage; if (defaultServicesForConfig) { currentServices JSON.parse(JSON.stringify(defaultServicesForConfig)); showNotification(Использован стандартный шаблон ТО, info); } else { currentServices ; showNotification(Создан новый пустой шаблон ТО, info); } } // Обновляем группы услуг updateServiceGroups(); resultDiv.classList.remove(hidden); resultDiv.classList.remove(loading); saveButton.style.display block; updateServicesTable(); } else { resultDiv.classList.add(hidden); saveButton.style.display none; } } // Обновление групп услуг function updateServiceGroups() { currentGroups new Set(); currentServices.forEach(service > { if (service.group) currentGroups.add(service.group); }); } // Обновление таблицы услуг function updateServicesTable() { const servicesList document.getElementById(services-list); const totalDiv document.getElementById(total); const serviceCount document.getElementById(serviceCount); servicesList.innerHTML ; let totalCost 0; let enabledCount 0; // Сначала группируем услуги по группам const groupedServices {}; currentServices.forEach((service, index) > { const group service.group || Без группы; if (!groupedServicesgroup) { groupedServicesgroup ; } groupedServicesgroup.push({...service, originalIndex: index}); if (service.enabled) { enabledCount++; } }); // Отображаем группы и услуги Object.keys(groupedServices).forEach(group > { servicesList.innerHTML + ` tr classservice-group> td colspan7>${group}/td> /tr> `; groupedServicesgroup.forEach((service, groupIndex) > { const index service.originalIndex; const serviceTotal service.enabled ? service.quantity * service.price : 0; totalCost + serviceTotal; const alternativeClass service.alternative ? alternative-service : ; const editingClass service.editing ? editing-row : ; const newRowClass service.isNew ? new-row : ; servicesList.innerHTML + ` tr class${service.enabled ? : disabled-service} ${alternativeClass} ${editingClass} ${newRowClass} data-index${index}> td> input typecheckbox classservice-checkbox ${service.enabled ? checked : } onchangetoggleService(${index}, this.checked)> /td> td> ${service.editing ? `input typetext classname-input value${service.name} onchangeupdateServiceField(${index}, name, this.value)>` : service.name} /td> td> ${service.editing ? `input typetext classcatalog-input value${service.catalogNumber} onchangeupdateCatalogNumber(${index}, this.value)>` : `span classcatalog-number onclickcopyCatalogNumber(${service.catalogNumber}, this)> ${service.catalogNumber} span classcopy-notification>Скопировано!/span> /span>`} /td> td> input typenumber min0 step0.5 value${service.quantity} classquantity-input onchangeupdateQuantity(${index}, this.value) oninputupdateQuantity(${index}, this.value) ${service.enabled ? : disabled}> /td> td> ${service.editing ? `input typenumber classprice-input value${service.price} step0.01 min0 onchangeupdateServiceField(${index}, price, this.value)>` : `${service.price.toLocaleString(ru-RU)} руб.`} /td> td>${serviceTotal.toLocaleString(ru-RU)} руб./td> td> ${isEditMode ? `div classmove-controls> button classmove-btn onclickmoveRowUp(${index}) titleПереместить вверх>↑/button> button classmove-btn onclickmoveRowDown(${index}) titleПереместить вниз>↓/button> /div> input typecheckbox classedit-checkbox ${service.editing ? checked : } onchangetoggleEditRow(${index}, this.checked)> ${service.editing ? `button classadd-to-db-btn onclickaddToDatabase(${index}) titleДобавить в базу данных>📥/button> button classdelete-btn onclickdeleteRow(${index})>×/button>` : }` : } /td> /tr> `; }); }); // Добавляем селектор выбора группы для новой строки if (isEditMode) { servicesList.innerHTML + ` tr idadd-row-placeholder> td colspan7 styletext-align: center; padding: 20px;> button classsave-btn onclickaddNewRow()>Добавить новую строку/button> select idnew-row-group classgroup-select stylemargin-left: 10px;> ${availableGroups.map(group > `option value${group}>${group}/option>`).join()} /select> /td> /tr> `; } totalDiv.textContent `Итого: ${totalCost.toLocaleString(ru-RU)} руб.`; serviceCount.textContent `${enabledCount}/${currentServices.length} услуг`; const addButton document.querySelector(.save-btn); if (addButton && addButton.parentElement ! document.getElementById(add-row-placeholder)) { addButton.style.display isEditMode ? inline-block : none; } } // Добавить позицию в базу данных async function addToDatabase(index) { const service currentServicesindex; // Проверяем, есть ли уже такой номер в базе const catalogData await getCatalogData(service.catalogNumber); showAddToDbModal(index, service.catalogNumber, service.name, service.price); } // Переместить строку вверх function moveRowUp(index) { if (index > 0) { currentServicesindex, currentServicesindex - 1 currentServicesindex - 1, currentServicesindex; updateServicesTable(); } } // Переместить строку вниз function moveRowDown(index) { if (index currentServices.length - 1) { currentServicesindex, currentServicesindex + 1 currentServicesindex + 1, currentServicesindex; updateServicesTable(); } } // Переключение режима редактирования function toggleEditMode() { isEditMode !isEditMode; const editButton document.querySelector(.edit-btn); const resultDiv document.getElementById(result); if (isEditMode) { editButton.textContent Завершить редактирование; resultDiv.classList.add(edit-mode); hasUnsavedChanges true; } else { editButton.textContent Режим редактирования; resultDiv.classList.remove(edit-mode); // Сбрасываем все чекбоксы редактирования currentServices.forEach(service > { service.editing false; }); } updateServicesTable(); } // Переключение редактирования строки function toggleEditRow(index, isEditing) { currentServicesindex.editing isEditing; updateServicesTable(); } // Добавление новой строки в выбранную группу function addNewRow() { const groupSelect document.getElementById(new-row-group); const selectedGroup groupSelect ? groupSelect.value : Техническое обслуживание; const newService { name: Новая услуга, catalogNumber: NEW- + Date.now().toString().slice(-6), quantity: 1, price: 0, enabled: true, group: selectedGroup, editing: true, isNew: true }; currentServices.push(newService); if (!currentGroups.has(selectedGroup)) { currentGroups.add(selectedGroup); } updateServicesTable(); } // Удаление строки function deleteRow(index) { if (confirm(Вы уверены, что хотите удалить эту строку?)) { currentServices.splice(index, 1); updateServicesTable(); } } // Обновление каталожного номера с проверкой в базе async function updateCatalogNumber(index, newCatalogNumber) { const rowElement document.querySelector(`trdata-index${index}`); rowElement.classList.add(loading); currentServicesindex.catalogNumber newCatalogNumber; // Проверяем в базе данных const catalogData await getCatalogData(newCatalogNumber); if (catalogData.exists) { // Если товар найден в базе, обновляем данные currentServicesindex.name catalogData.name; currentServicesindex.price catalogData.price; showNotification(Данные загружены из базы, success); } else { // Если товара нет в базе, предлагаем добавить currentServicesindex.name Товар не найден в базе; currentServicesindex.price 0; showNotification(Товар не найден в базе. Вы можете добавить его., warning); } rowElement.classList.remove(loading); updateServicesTable(); } // Обновление поля услуги function updateServiceField(index, field, value) { if (field price) { currentServicesindexfield parseFloat(value) || 0; } else { currentServicesindexfield value; } updateServicesTable(); } // Обновление количества function updateQuantity(index, value) { const quantity parseFloat(value); if (!isNaN(quantity) && quantity > 0) { currentServicesindex.quantity quantity; updateServicesTable(); } } // Включение/выключение услуги function toggleService(index, isEnabled) { currentServicesindex.enabled isEnabled; if (isEnabled && currentServicesindex.alternative) { const group currentServicesindex.group; currentServices.forEach((service, i) > { if (i ! index && service.group group && service.alternative) { service.enabled false; } }); } if (!isEnabled) { currentServicesindex.quantity 0; } updateServicesTable(); } // Сохранение изменений async function saveChanges() { const saveButton document.querySelector(.save-changes-btn); saveButton.classList.add(loading); saveButton.disabled true; try { const result await saveChangesToServer(); if (result.success) { showNotification(Изменения успешно сохранены!, success); // Сбрасываем флаги новых строк currentServices.forEach(service > { if (service.isNew) { delete service.isNew; } if (service.editing) { service.editing false; } }); hasUnsavedChanges false; // Обновляем таблицу updateServicesTable(); } else { throw new Error(result.error || Ошибка сохранения); } } catch (error) { console.error(Ошибка при сохранении:, error); showNotification(Ошибка при сохранении изменений: + error.message, error); } finally { saveButton.classList.remove(loading); saveButton.disabled false; } } // Копирование каталожного номера function copyCatalogNumber(catalogNumber, element) { const tempTextArea document.createElement(textarea); tempTextArea.value catalogNumber; document.body.appendChild(tempTextArea); tempTextArea.select(); try { const successful document.execCommand(copy); if (successful) { const notification element.querySelector(.copy-notification); if (notification) { notification.classList.add(show); setTimeout(() > { notification.classList.remove(show); }, 2000); } showNotification(`Номер ${catalogNumber} скопирован в буфер обмена`, success); } } catch (err) { navigator.clipboard.writeText(catalogNumber).then(() > { showNotification(`Номер ${catalogNumber} скопирован в буфер обмена`, success); }).catch(err > { alert(Не удалось скопировать номер. Скопируйте его вручную: + catalogNumber); }); } finally { document.body.removeChild(tempTextArea); } } /script>/body>/html>
View on OTX
|
View on ThreatMiner
Please enable JavaScript to view the
comments powered by Disqus.
Data with thanks to
AlienVault OTX
,
VirusTotal
,
Malwr
and
others
. [
Sitemap
]