Back to blocks

передать форму в приложение

4.0
ruПрограммированиеформаприложениепередача данных

Промпт

передать содержимое формы в другое приложение

Сгенерированный Результат

import React, { useState } from 'react';

const App = () => {
  const [formData, setFormData] = useState({
    name: '',
    email: '',
    message: ''
  });

  const handleChange = (e) => {
    const { name, value } = e.target;
    setFormData(prevState => ({
      ...prevState,
      [name]: value
    }));
  };

  const handleSubmit = (e) => {
    e.preventDefault();
    const jsonString = JSON.stringify(formData);
    console.log(jsonString);
    // Here you can send jsonString to another application
  };

  return (
    <div className="min-h-screen bg-gray-100 flex items-center justify-center">
      <form onSubmit={handleSubmit} className="bg-white p-6 rounded-lg shadow-lg w-full max-w-md">
        <h2 className="text-2xl font-bold mb-6 text-gray-800">Contact Us</h2>
        <div className="mb-4">
          <label className="block text-gray-700 text-sm font-bold mb-2" htmlFor="name">
            Name
          </label>
          <input
            className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
            id="name"
            type="text"
            name="name"
            value={formData.name}
            onChange={handleChange}
            placeholder="Your Name"
          />
        </div>
        <div className="mb-4">
          <label className="block text-gray-700 text-sm font-bold mb-2" htmlFor="email">
            Email
          </label>
          <input
            className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
            id="email"
            type="email"
            name="email"
            value={formData.email}
            onChange={handleChange}
            placeholder="Your Email"
          />
        </div>
        <div className="mb-6">
          <label className="block text-gray-700 text-sm font-bold mb-2" htmlFor="message">
            Message
          </label>
          <textarea
            className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
            id="message"
            name="message"
            value={formData.message}
            onChange={handleChange}
            placeholder="Your Message"
          />
        </div>
        <div className="flex items-center justify-between">
          <button
            className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline"
            type="submit"
          >
            Submit
          </button>
        </div>
      </form>
    </div>
  );
};

export default App;

Больше Рекомендаций

Посмотреть Все

Госзакупки: парсер PHP

Программированиеru
4.0

<?php error_reporting(E_ALL); ini_set('display_errors', 1); class ZakupkiParser { private $curl; private $cookies; private $cache = []; private $regionMapping = [ // Федеральные города '77' => 'Москва', '78' => 'Санкт-Петербург', '92' => 'Севастополь', // Области '29' => 'Архангельская область', '30' => 'Астраханская область', '31' => 'Белгородская область', '32' => 'Брянская область', '33' => 'Владимирская область', '34' => 'Волгоградская область', '35' => 'Вологодская область', '36' => 'Воронежская область', '37' => 'Ивановская область', '38' => 'Иркутская область', '39' => 'Калининградская область', '40' => 'Калужская область', '42' => 'Кемеровская область', '43' => 'Кировская область', '44' => 'Костромская область', '45' => 'Курганская область', '46' => 'Курская область', '47' => 'Ленинградская область', '48' => 'Липецкая область', '49' => 'Магаданская область', '50' => 'Московская область', '51' => 'Мурманская область', '52' => 'Нижегородская область', '53' => 'Новгородская область', '54' => 'Новосибирская область', '55' => 'Омская область', '56' => 'Оренбургская область', '57' => 'Орловская область', '58' => 'Пензенская область', '60' => 'Псковская область', '61' => 'Ростовская область', '62' => 'Рязанская область', '63' => 'Самарская область', '64' => 'Саратовская область', '65' => 'Сахалинская область', '66' => 'Свердловская область', '67' => 'Смоленская область', '68' => 'Тамбовская область', '69' => 'Тверская область', '70' => 'Томская область', '71' => 'Тульская область', '72' => 'Тюменская область', '73' => 'Ульяновская область', '74' => 'Челябинская область', '75' => 'Забайкальский край', '76' => 'Ярославская область', // Республики '01' => 'Республика Адыгея', '02' => 'Республика Башкортостан', '03' => 'Республика Бурятия', '04' => 'Республика Алтай', '05' => 'Республика Дагестан', '06' => 'Республика Ингушетия', '07' => 'Кабардино-Балкарская Республика', '08' => 'Республика Калмыкия', '09' => 'Карачаево-Черкесская Республика', '10' => 'Республика Карелия', '11' => 'Республика Коми', '12' => 'Республика Марий Эл', '13' => 'Республика Мордовия', '14' => 'Республика Саха (Якутия)', '15' => 'Республика Северная Осетия — Алания', '16' => 'Республика Татарстан', '17' => 'Республика Тыва', '18' => 'Удмуртская Республика', '19' => 'Республика Хакасия', '20' => 'Чеченская Республика', '21' => 'Чувашская Республика', '91' => 'Республика Крым', // Края '22' => 'Алтайский край', '23' => 'Краснодарский край', '24' => 'Красноярский край', '25' => 'Приморский край', '26' => 'Ставропольский край', '27' => 'Хабаровский край', '41' => 'Камчатский край', '59' => 'Пермский край', // Автономные округа '79' => 'Еврейская автономная область', '83' => 'Ненецкий автономный округ', '86' => 'Ханты-Мансийский автономный округ', '87' => 'Чукотский автономный округ', '89' => 'Ямало-Ненецкий автономный округ' ]; // Добавить массив городов для улучшенного поиска private $cityMapping = [ // Москва и МО 'москва' => '77', 'московская' => '50', 'подольск' => '50', 'химки' => '50', 'королев' => '50', 'мытищи' => '50', 'люберцы' => '50', 'красногорск' => '50', 'электросталь' => '50', 'коломна' => '50', 'одинцово' => '50', 'серпухов' => '50', // Санкт-Петербург и ЛО 'петербург' => '78', 'спб' => '78', 'ленинградская' => '47', 'гатчина' => '47', 'выборг' => '47', 'тосно' => '47', 'кингисепп' => '47', // Крупные города 'новосибирск' => '54', 'екатеринбург' => '66', 'казань' => '16', 'нижний новгород' => '52', 'челябинск' => '74', 'самара' => '63', 'омск' => '55', 'ростов-на-дону' => '61', 'ростов' => '61', 'уфа' => '02', 'красноярск' => '24', 'воронеж' => '36', 'пермь' => '59', 'волгоград' => '34', 'краснодар' => '23', 'саратов' => '64', 'тюмень' => '72', 'тольятти' => '63', 'ижевск' => '18', 'барнаул' => '22', 'ульяновск' => '73', 'иркутск' => '38', 'хабаровск' => '27', 'ярославль' => '76', 'владивосток' => '25', 'махачкала' => '05', 'томск' => '70', 'оренбург' => '56', 'кемерово' => '42', 'новокузнецк' => '42', 'рязань' => '62', 'астрахань' => '30', 'пенза' => '58', 'липецк' => '48', 'тула' => '71', 'киров' => '43', 'чебоксары' => '21', 'калининград' => '39', 'брянск' => '32', 'курск' => '46', 'иваново' => '37', 'магнитогорск' => '74', 'тверь' => '69', 'ставрополь' => '26', 'белгород' => '31', 'сочи' => '23', 'нижний тагил' => '66', 'архангельск' => '29', 'владимир' => '33', 'калуга' => '40', 'смоленск' => '67', 'волжский' => '34', 'курган' => '45', 'орел' => '57', 'череповец' => '35', 'вологда' => '35', 'мурманск' => '51', 'тамбов' => '68', 'стерлитамак' => '02', 'нижневартовск' => '86', 'кострома' => '44', 'новороссийск' => '23', 'йошкар-ола' => '12', 'таганрог' => '61', 'комсомольск-на-амуре' => '27', 'сыктывкар' => '11', 'нижнекамск' => '16', 'дзержинск' => '52', 'орск' => '56', 'ангарск' => '38', 'балаково' => '64', 'благовещенск' => '28', 'прокопьевск' => '42', 'псков' => '60', 'бийск' => '22', 'энгельс' => '64', 'рыбинск' => '76', 'балашиха' => '50', 'северодвинск' => '29', 'армавир' => '23', 'подольск' => '50', 'королев' => '50', 'петрозаводск' => '10', 'железнодорожный' => '50', 'видное' => '50', 'ковров' => '33', 'электросталь' => '50', 'миасс' => '74', 'первоуральск' => '66', 'копейск' => '74', 'коломна' => '50', 'химки' => '50', 'серпухов' => '50', 'новочеркасск' => '61', 'батайск' => '61', 'красногорск' => '50', 'мытищи' => '50', 'камышин' => '34', 'новошахтинск' => '61', 'октябрьский' => '02', 'ачинск' => '24', 'первомайский' => '27', 'елец' => '48', 'междуреченск' => '42', 'нефтекамск' => '02', 'димитровград' => '73', 'кисловодск' => '26', 'ессентуки' => '26', 'пятигорск' => '26', 'невинномысск' => '26', 'черкесск' => '09', 'нальчик' => '07', 'владикавказ' => '15', 'грозный' => '20', 'элиста' => '08', 'майкоп' => '01', 'горно-алтайск' => '04', 'абакан' => '19', 'кызыл' => '17', 'улан-удэ' => '03', 'чита' => '75', 'якутск' => '14', 'магадан' => '49', 'южно-сахалинск' => '65', 'петропавловск-камчатский' => '41', 'анадырь' => '87', 'салехард' => '89', 'ханты-мансийск' => '86', 'нарьян-мар' => '83', 'биробиджан' => '79', 'симферополь' => '91', 'севастополь' => '92' ]; public function __construct() { $this->cookies = tempnam(sys_get_temp_dir(), 'zakupki_'); $this->curl = curl_init(); curl_setopt_array($this->curl, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_FOLLOWLOCATION => true, CURLOPT_USERAGENT => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36', CURLOPT_TIMEOUT => 30, CURLOPT_SSL_VERIFYPEER => false, CURLOPT_ENCODING => 'gzip, deflate', CURLOPT_COOKIEJAR => $this->cookies, CURLOPT_COOKIEFILE => $this->cookies ]); } private function fetch($url, $post = false, $data = null) { curl_setopt($this->curl, CURLOPT_URL, $url); curl_setopt($this->curl, CURLOPT_POST, $post); if ($post && $data) { curl_setopt($this->curl, CURLOPT_POSTFIELDS, $data); } $response = curl_exec($this->curl); if (curl_errno($this->curl)) { throw new Exception('CURL error: ' . curl_error($this->curl)); } $httpCode = curl_getinfo($this->curl, CURLINFO_HTTP_CODE); if ($httpCode !== 200) { throw new Exception("HTTP error {$httpCode} for URL: {$url}"); } return $response; } // Извлечение ID тендера из различных источников private function extractTenderId($block, $xpath) { $tenderId = ''; // Попытка 1: из ссылки на номер тендера $linkNode = $xpath->query(".//div[contains(@class, 'registry-entry__header-mid__number')]/a/@href", $block); if ($linkNode->length) { $href = $linkNode->item(0)->nodeValue; if (preg_match('/regNumber=([^&]+)/', $href, $matches)) { $tenderId = $matches[1]; } } // Попытка 2: из атрибута onclick кнопки "Подробнее" if (empty($tenderId)) { $buttonNode = $xpath->query(".//button[contains(@onclick, 'showTenderDetails')]", $block); if ($buttonNode->length) { $onclick = $buttonNode->item(0)->getAttribute('onclick'); if (preg_match("/showTenderDetails$$'([^']+)'$$/", $onclick, $matches)) { $tenderId = $matches[1]; } } } // Попытка 3: из data-атрибутов if (empty($tenderId)) { $dataNode = $xpath->query(".//*[@data-tender-id]", $block); if ($dataNode->length) { $tenderId = $dataNode->item(0)->getAttribute('data-tender-id'); } } return $tenderId; } // Определение региона из адреса или других данных private function extractRegion($block, $xpath) { $region = ''; // Поиск региона в адресе заказчика $addressNode = $xpath->query(".//div[contains(@class, 'registry-entry__body-href')]/following-sibling::div", $block); if ($addressNode->length) { $address = mb_strtolower(trim($addressNode->item(0)->nodeValue), 'UTF-8'); // Сначала ищем по точным названиям регионов foreach ($this->regionMapping as $code => $name) { if (stripos($address, mb_strtolower($name, 'UTF-8')) !== false) { $region = $code; break; } } // Если не нашли, ищем по городам if (empty($region)) { foreach ($this->cityMapping as $city => $code) { if (stripos($address, $city) !== false) { $region = $code; break; } } } } // Поиск в других местах if (empty($region)) { $regionNode = $xpath->query(".//span[contains(@class, 'region-info')]", $block); if ($regionNode->length) { $regionText = mb_strtolower(trim($regionNode->item(0)->nodeValue), 'UTF-8'); foreach ($this->regionMapping as $code => $name) { if (stripos($regionText, mb_strtolower($name, 'UTF-8')) !== false) { $region = $code; break; } } } } return $region; } // Основной метод поиска с поддержкой регионов public function searchTenders($params = []) { $cacheKey = md5(serialize($params)); if (isset($this->cache[$cacheKey])) { return $this->cache[$cacheKey]; } // Параметры поиска $searchParams = [ 'morphology' => 'on', 'search-filter' => 'Дате+размещения', 'pageNumber' => $params['page'] ?? 1, 'sortDirection' => 'false', 'recordsPerPage' => '_' . ($params['size'] ?? 50), 'showLotsInfoHidden' => 'false', 'sortBy' => 'UPDATE_DATE', 'fz44' => 'on', 'fz223' => 'on', 'af' => 'on', 'ca' => 'on', 'pc' => 'on', 'pa' => 'on', 'currencyIdGeneral' => '-1' ]; // Добавляем параметры поиска if (!empty($params['search_text'])) { $searchParams['searchString'] = $params['search_text']; } if (!empty($params['inn'])) { $searchParams['customerId'] = $params['inn']; } if (!empty($params['price_min'])) { $searchParams['priceFrom'] = $params['price_min']; } if (!empty($params['price_max'])) { $searchParams['priceTo'] = $params['price_max']; } if (!empty($params['date_from'])) { $searchParams['publishDateFrom'] = $params['date_from']; } if (!empty($params['date_to'])) { $searchParams['publishDateTo'] = $params['date_to']; } // Добавляем фильтр по регионам if (!empty($params['regions']) && is_array($params['regions'])) { foreach ($params['regions'] as $region) { $searchParams["selectedSubjectsIdNameHidden"] = $region; } } // Добавляем поиск по городу/региону if (!empty($params['city_search'])) { $searchParams['searchString'] = $params['city_search']; } $url = 'https://zakupki.gov.ru/epz/order/extendedsearch/results.html?' . http_build_query($searchParams); $html = $this->fetch($url); $result = $this->parseTenderList($html, $params); // Кэшируем результат на 5 минут $this->cache[$cacheKey] = $result; return $result; } // Улучшенный парсинг списка тендеров private function parseTenderList($html, $params = []) { $dom = new DOMDocument(); @$dom->loadHTML(mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8')); $xpath = new DOMXPath($dom); $tenders = []; $blocks = $xpath->query("//div[contains(@class, 'registry-entry__form')]"); $selectedRegions = $params['regions'] ?? []; foreach ($blocks as $block) { $tender = []; // Улучшенное извлечение ID тендера $tender['tenderId'] = $this->extractTenderId($block, $xpath); $tender['region'] = $this->extractRegion($block, $xpath); // Фильтрация по регионам на стороне клиента (дополнительная проверка) if (!empty($selectedRegions) && !empty($tender['region']) && !in_array($tender['region'], $selectedRegions)) { continue; } // Номер тендера $numberNode = $xpath->query(".//div[contains(@class, 'registry-entry__header-mid__number')]/a", $block); $tender['purchaseNumber'] = $numberNode->length ? trim($numberNode->item(0)->nodeValue) : ''; // Название тендера $titleNode = $xpath->query(".//div[contains(@class, 'registry-entry__body-value')]", $block); $tender['purchaseObjectInfo'] = $titleNode->length ? trim($titleNode->item(0)->nodeValue) : ''; // Заказчик $customerNode = $xpath->query(".//div[contains(@class, 'registry-entry__body-href')]/a", $block); $tender['customer'] = ['fullName' => $customerNode->length ? trim($customerNode->item(0)->nodeValue) : '']; // Цена $priceNode = $xpath->query(".//div[contains(@class, 'price-block__value')]", $block); $price = $priceNode->length ? trim($priceNode->item(0)->nodeValue) : ''; $tender['lot'] = ['price' => preg_replace('/[^\d,.]/', '', $price)]; // Даты $dateNodes = $xpath->query(".//div[contains(@class, 'data-block__value')]", $block); $tender['publishDate'] = $dateNodes->length > 0 ? trim($dateNodes->item(0)->nodeValue) : ''; $tender['updateDate'] = $dateNodes->length > 1 ? trim($dateNodes->item(1)->nodeValue) : ''; $tender['submissionCloseDateTime'] = $dateNodes->length > 2 ? trim($dateNodes->item(2)->nodeValue) : ''; // Регистрационный номер (из ссылки) $linkNode = $xpath->query(".//div[contains(@class, 'registry-entry__header-mid__number')]/a/@href", $block); if ($linkNode->length) { $href = $linkNode->item(0)->nodeValue; if (preg_match('/regNumber=([^&]+)/', $href, $matches)) { $tender['regNumber'] = $matches[1]; } } // Используем tenderId как основной идентификатор if (empty($tender['regNumber']) && !empty($tender['tenderId'])) { $tender['regNumber'] = $tender['tenderId']; } $tenders[] = $tender; } // Пагинация $totalItems = 0; $paginationNode = $xpath->query("//div[contains(@class, 'paginator-block')]//span"); if ($paginationNode->length) { $text = $paginationNode->item(0)->textContent; if (preg_match('/из (\d+)/', $text, $matches)) { $totalItems = (int)$matches[1]; } } return [ 'content' => $tenders, 'totalElements' => $totalItems, 'regions' => $this->regionMapping ]; } // Получение деталей с кэшированием public function getTenderDetails($regNumber) { $cacheKey = "details_" . $regNumber; if (isset($this->cache[$cacheKey])) { return $this->cache[$cacheKey]; } $url = "https://zakupki.gov.ru/epz/order/notice/ea20/view/common-info.html?regNumber={$regNumber}"; $html = $this->fetch($url); $result = $this->parseTenderDetails($html, $regNumber); // Кэшируем детали на 10 минут $this->cache[$cacheKey] = $result; return $result; } // Полный парсинг всех деталей тендера private function parseTenderDetails($html, $regNumber) { $dom = new DOMDocument(); @$dom->loadHTML(mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8')); $xpath = new DOMXPath($dom); $details = [ 'regNumber' => $regNumber, 'generalInfo' => [], 'contactInfo' => [], 'procedureInfo' => [], 'priceInfo' => [], 'objectInfo' => [], 'requirements' => [], 'customerRequirements' => [], 'financialInfo' => [], 'applicationSecurity' => [], 'contractConditions' => [], 'contractSecurity' => [], 'bankingSupport' => [], 'documents' => [], 'lots' => [], 'region' => '', 'regionName' => '' ]; // 1. ОСНОВНАЯ ИНФОРМАЦИЯ ТЕНДЕРА $this->parseMainInfo($xpath, $details); // 2. ОБЩАЯ ИНФОРМАЦИЯ О ЗАКУПКЕ $this->parseGeneralInfo($xpath, $details); // 3. КОНТАКТНАЯ ИНФОРМАЦИЯ $this->parseContactInfo($xpath, $details); // 4. ИНФОРМАЦИЯ О ПРОЦЕДУРЕ ЗАКУПКИ $this->parseProcedureInfo($xpath, $details); // 5. НАЧАЛЬНАЯ ЦЕНА КОНТРАКТА $this->parsePriceInfo($xpath, $details); // 6. ИНФОРМАЦИЯ ОБ ОБЪЕКТЕ ЗАКУПКИ (КТРУ) $this->parseObjectInfo($xpath, $details); // 7. ПРЕИМУЩЕСТВА И ТРЕБОВАНИЯ $this->parseRequirements($xpath, $details); // 8. ТРЕБОВАНИЯ ЗАКАЗЧИКА $this->parseCustomerRequirements($xpath, $details); // 9. ФИНАНСОВАЯ ИНФОРМАЦИЯ $this->parseFinancialInfo($xpath, $details); // 10. ОБЕСПЕЧЕНИЕ ЗАЯВКИ $this->parseApplicationSecurity($xpath, $details); // 11. УСЛОВИЯ КОНТРАКТА $this->parseContractConditions($xpath, $details); // 12. ОБЕСПЕЧЕНИЕ ИСПОЛНЕНИЯ КОНТРАКТА $this->parseContractSecurity($xpath, $details); // 13. БАНКОВСКОЕ СОПРОВОЖДЕНИЕ $this->parseBankingSupport($xpath, $details); // 14. ДОКУМЕНТЫ $this->parseDocuments($xpath, $details); return $details; } // Парсинг основной информации тендера private function parseMainInfo($xpath, &$details) { // Извлекаем регион из блока time-zone $this->extractRegionFromTimeZone($xpath, $details); // Заголовок и номер $titleNode = $xpath->query("//div[contains(@class, 'cardMainInfo__title')]"); $details['generalInfo']['title'] = $titleNode->length ? trim($titleNode->item(0)->textContent) : ''; $numberNode = $xpath->query("//span[contains(@class, 'cardMainInfo__purchaseLink')]/a"); $details['generalInfo']['number'] = $numberNode->length ? trim($numberNode->item(0)->textContent) : ''; // Статус $statusNode = $xpath->query("//span[contains(@class, 'cardMainInfo__state')]"); $details['generalInfo']['status'] = $statusNode->length ? trim($statusNode->item(0)->textContent) : ''; // Объект закупки $objectNode = $xpath->query("//div[contains(@class, 'cardMainInfo__section')]/span[contains(text(), 'Объект закупки')]/following-sibling::span"); if (!$objectNode->length) { // Альтернативный поиск в заголовке $objectNode = $xpath->query("//h1[contains(@class, 'cardMainInfo__title')] | //div[contains(@class, 'cardMainInfo__title')]"); } $details['generalInfo']['object'] = $objectNode->length ? trim($objectNode->item(0)->textContent) : 'Не указан'; // Организация $orgNode = $xpath->query("//div[contains(@class, 'cardMainInfo__section')]/span[contains(text(), 'Организация') or contains(text(), 'размещение')]/following-sibling::span/a"); if (!$orgNode->length) { // Альтернативный поиск организации $orgNode = $xpath->query("//a[contains(@href, 'organization/view')]"); } $details['generalInfo']['organization'] = $orgNode->length ? trim($orgNode->item(0)->textContent) : 'Не указана'; // Цена $priceNode = $xpath->query("//span[contains(@class, 'cost')]"); $details['generalInfo']['price'] = $priceNode->length ? trim($priceNode->item(0)->textContent) : ''; // Даты из основной карточки $publishDateNode = $xpath->query("//div[contains(@class, 'cardMainInfo__section')]//span[contains(@class, 'cardMainInfo__title') and contains(text(), 'Размещено')]/following-sibling::span[contains(@class, 'cardMainInfo__content')]"); if ($publishDateNode->length) { $details['generalInfo']['publishDate'] = trim($publishDateNode->item(0)->textContent); } $updateDateNode = $xpath->query("//div[contains(@class, 'cardMainInfo__section')]//span[contains(@class, 'cardMainInfo__title') and contains(text(), 'Обновлено')]/following-sibling::span[contains(@class, 'cardMainInfo__content')]"); if ($updateDateNode->length) { $details['generalInfo']['updateDate'] = trim($updateDateNode->item(0)->textContent); } $endDateNode = $xpath->query("//div[contains(@class, 'cardMainInfo__section')]//span[contains(@class, 'cardMainInfo__title') and contains(text(), 'Окончание')]/following-sibling::span[contains(@class, 'cardMainInfo__content')]"); if ($endDateNode->length) { $details['generalInfo']['endDate'] = trim($endDateNode->item(0)->textContent); } // Если даты не найдены в основной карточке, ищем в других местах if (empty($details['generalInfo']['publishDate'])) { $altPublishNode = $xpath->query("//span[contains(text(), 'Размещено')]/following-sibling::span | //td[contains(text(), 'Размещено')]/following-sibling::td"); if ($altPublishNode->length) { $details['generalInfo']['publishDate'] = trim($altPublishNode->item(0)->textContent); } } if (empty($details['generalInfo']['updateDate'])) { $altUpdateNode = $xpath->query("//span[contains(text(), 'Обновлено')]/following-sibling::span | //td[contains(text(), 'Обновлено')]/following-sibling::td"); if ($altUpdateNode->length) { $details['generalInfo']['updateDate'] = trim($altUpdateNode->item(0)->textContent); } } if (empty($details['generalInfo']['endDate'])) { $altEndNode = $xpath->query("//span[contains(text(), 'Окончание')]/following-sibling::span | //td[contains(text(), 'Окончание')]/following-sibling::td"); if ($altEndNode->length) { $details['generalInfo']['endDate'] = trim($altEndNode->item(0)->textContent); } } // Добавляем информацию о часовом поясе $timeZoneNode = $xpath->query("//div[contains(@class, 'time-zone__value')]/span"); if ($timeZoneNode->length) { $details['generalInfo']['timeZone'] = trim($timeZoneNode->item(0)->textContent); } } // Парсинг общей информации о закупке private function parseGeneralInfo($xpath, &$details) { $sections = $xpath->query("//h2[contains(text(), 'Общая информация о закупке')]/following-sibling::section | //h2[contains(text(), 'Общая информация о закупке')]/parent::*/following-sibling::*/section"); foreach ($sections as $section) { $titleNode = $xpath->query(".//span[contains(@class, 'section__title')]", $section); $infoNode = $xpath->query(".//span[contains(@class, 'section__info')]", $section); if ($titleNode->length && $infoNode->length) { $title = trim($titleNode->item(0)->textContent); $info = trim($infoNode->item(0)->textContent); $details['generalInfo'][$this->normalizeKey($title)] = $info; } } } // Парсинг контактной информации private function parseContactInfo($xpath, &$details) { $sections = $xpath->query("//h2[contains(text(), 'Контактная информация')]/following-sibling::section | //h2[contains(text(), 'Контактная информация')]/parent::*/following-sibling::*/section"); foreach ($sections as $section) { $titleNode = $xpath->query(".//span[contains(@class, 'section__title')]", $section); $infoNode = $xpath->query(".//span[contains(@class, 'section__info')]", $section); if ($titleNode->length && $infoNode->length) { $title = trim($titleNode->item(0)->textContent); $info = trim($infoNode->item(0)->textContent); $details['contactInfo'][$this->normalizeKey($title)] = $info; // Определение региона из адреса, только если он еще не определен if (empty($details['region']) && (stripos($title, 'адрес') !== false || stripos($title, 'нахождение') !== false)) { foreach ($this->regionMapping as $code => $name) { if (stripos($info, $name) !== false) { $details['region'] = $code; $details['regionName'] = $name; break; } } } } } } // Парсинг информации о процедуре закупки private function parseProcedureInfo($xpath, &$details) { $sections = $xpath->query("//h2[contains(text(), 'Информация о процедуре закупки')]/following-sibling::section | //h2[contains(text(), 'Информация о процедуре закупки')]/parent::*/following-sibling::*/section"); foreach ($sections as $section) { $titleNode = $xpath->query(".//span[contains(@class, 'section__title')]", $section); $infoNode = $xpath->query(".//span[contains(@class, 'section__info')]", $section); if ($titleNode->length && $infoNode->length) { $title = trim($titleNode->item(0)->textContent); $info = trim($infoNode->item(0)->textContent); $details['procedureInfo'][$this->normalizeKey($title)] = $info; } } } // Парсинг информации о цене private function parsePriceInfo($xpath, &$details) { $sections = $xpath->query("//h2[contains(text(), 'Начальная') and contains(text(), 'цена')]/following-sibling::section | //h2[contains(text(), 'Начальная') and contains(text(), 'цена')]/parent::*/following-sibling::*/section"); foreach ($sections as $section) { $titleNode = $xpath->query(".//span[contains(@class, 'section__title')]", $section); $infoNode = $xpath->query(".//span[contains(@class, 'section__info')]", $section); if ($titleNode->length && $infoNode->length) { $title = trim($titleNode->item(0)->textContent); $info = trim($infoNode->item(0)->textContent); $details['priceInfo'][$this->normalizeKey($title)] = $info; } } } // Парсинг информации об объекте закупки (КТРУ) private function parseObjectInfo($xpath, &$details) { $table = $xpath->query("//table[contains(@class, 'tableBlock')]//tbody[contains(@class, 'tableBlock__body')]"); if ($table->length) { $rows = $xpath->query(".//tr[contains(@class, 'tableBlock__row')]", $table->item(0)); foreach ($rows as $row) { $cols = $xpath->query(".//td[contains(@class, 'tableBlock__col')]", $row); if ($cols->length >= 6) { $item = [ 'code' => trim($cols->item(1)->textContent), 'name' => trim($cols->item(2)->textContent), 'unit' => $cols->length > 3 ? trim($cols->item(3)->textContent) : '', 'quantity' => $cols->length > 4 ? trim($cols->item(4)->textContent) : '', 'price' => $cols->length > 5 ? trim($cols->item(5)->textContent) : '', 'sum' => $cols->length > 6 ? trim($cols->item(6)->textContent) : '' ]; // Извлечение характеристик $characteristics = []; $charRows = $xpath->query(".//tr[contains(@style, 'display: none')]//table//tr", $row->parentNode); foreach ($charRows as $charRow) { $charCols = $xpath->query(".//td", $charRow); if ($charCols->length >= 2) { $charName = trim($charCols->item(0)->textContent); $charValue = trim($charCols->item(1)->textContent); if (!empty($charName) && !empty($charValue)) { $characteristics[$charName] = $charValue; } } } $item['characteristics'] = $characteristics; $details['objectInfo'][] = $item; } } } // Итоговая сумма $totalNode = $xpath->query("//tfoot//span[contains(@class, 'cost')]"); if ($totalNode->length) { $details['objectInfo']['total'] = trim($totalNode->item(0)->textContent); } } // Парсинг требований и преимуществ private function parseRequirements($xpath, &$details) { $sections = $xpath->query("//h2[contains(text(), 'Преимущества') or contains(text(), 'требования')]/following-sibling::section | //h2[contains(text(), 'Преимущества') or contains(text(), 'требования')]/parent::*/following-sibling::*/section"); foreach ($sections as $section) { $titleNode = $xpath->query(".//span[contains(@class, 'section__title')]", $section); $infoNode = $xpath->query(".//span[contains(@class, 'section__info')]", $section); if ($titleNode->length && $infoNode->length) { $title = trim($titleNode->item(0)->textContent); $info = trim($infoNode->item(0)->textContent); $details['requirements'][$this->normalizeKey($title)] = $info; } } } // Парсинг требований заказчика private function parseCustomerRequirements($xpath, &$details) { $sections = $xpath->query("//span[contains(text(), 'Требования заказчика')]/ancestor::div[contains(@class, 'collapse__title')]/following-sibling::div//section"); foreach ($sections as $section) { $titleNode = $xpath->query(".//span[contains(@class, 'section__title')]", $section); $infoNode = $xpath->query(".//span[contains(@class, 'section__info')]", $section); if ($titleNode->length && $infoNode->length) { $title = trim($titleNode->item(0)->textContent); $info = trim($infoNode->item(0)->textContent); $details['customerRequirements'][$this->normalizeKey($title)] = $info; } } } // Парсинг финансовой информации private function parseFinancialInfo($xpath, &$details) { // Финансовое обеспечение $tables = $xpath->query("//h2[contains(text(), 'финансирования') or contains(text(), 'Финансовое обеспечение')]/following-sibling::table | //h2[contains(text(), 'финансирования') or contains(text(), 'Финансовое обеспечение')]/parent::*/following-sibling::*/table"); foreach ($tables as $table) { $rows = $xpath->query(".//tr", $table); $tableData = []; foreach ($rows as $row) { $cols = $xpath->query(".//td | .//th", $row); $rowData = []; foreach ($cols as $col) { $rowData[] = trim($col->textContent); } if (!empty($rowData)) { $tableData[] = $rowData; } } $details['financialInfo']['tables'][] = $tableData; } // Дополнительные секции $sections = $xpath->query("//h2[contains(text(), 'финансирования')]/following-sibling::section | //h2[contains(text(), 'финансирования')]/parent::*/following-sibling::*/section"); foreach ($sections as $section) { $titleNode = $xpath->query(".//span[contains(@class, 'section__title')]", $section); $infoNode = $xpath->query(".//span[contains(@class, 'section__info')]", $section); if ($titleNode->length && $infoNode->length) { $title = trim($titleNode->item(0)->textContent); $info = trim($infoNode->item(0)->textContent); $details['financialInfo'][$this->normalizeKey($title)] = $info; } } } // Парсинг обеспечения заявки private function parseApplicationSecurity($xpath, &$details) { $sections = $xpath->query("//h2[contains(text(), 'Обеспечение заявки')]/following-sibling::section | //h2[contains(text(), 'Обеспечение заявки')]/parent::*/following-sibling::*/section"); foreach ($sections as $section) { $titleNode = $xpath->query(".//span[contains(@class, 'section__title')]", $section); $infoNode = $xpath->query(".//span[contains(@class, 'section__info')]", $section); if ($titleNode->length && $infoNode->length) { $title = trim($titleNode->item(0)->textContent); $info = trim($infoNode->item(0)->textContent); $details['applicationSecurity'][$this->normalizeKey($title)] = $info; } } } // Парсинг условий контракта private function parseContractConditions($xpath, &$details) { $sections = $xpath->query("//h2[contains(text(), 'Условия контракта')]/following-sibling::section | //h2[contains(text(), 'Условия контракта')]/parent::*/following-sibling::*/section"); foreach ($sections as $section) { $titleNode = $xpath->query(".//span[contains(@class, 'section__title')]", $section); $infoNode = $xpath->query(".//span[contains(@class, 'section__info')]", $section); if ($titleNode->length && $infoNode->length) { $title = trim($titleNode->item(0)->textContent); $info = trim($infoNode->item(0)->textContent); $details['contractConditions'][$this->normalizeKey($title)] = $info; } } } // Парсинг обеспечения исполнения контракта private function parseContractSecurity($xpath, &$details) { $sections = $xpath->query("//h2[contains(text(), 'Обеспечение исполнения контракта')]/following-sibling::section | //h2[contains(text(), 'Обеспечение исполнения контракта')]/parent::*/following-sibling::*/section"); foreach ($sections as $section) { $titleNode = $xpath->query(".//span[contains(@class, 'section__title')]", $section); $infoNode = $xpath->query(".//span[contains(@class, 'section__info')]", $section); if ($titleNode->length && $infoNode->length) { $title = trim($titleNode->item(0)->textContent); $info = trim($infoNode->item(0)->textContent); $details['contractSecurity'][$this->normalizeKey($title)] = $info; } } } // Парсинг банковского сопровождения private function parseBankingSupport($xpath, &$details) { $sections = $xpath->query("//h2[contains(text(), 'банковском') or contains(text(), 'казначейском')]/following-sibling::section | //h2[contains(text(), 'банковском') or contains(text(), 'казначейском')]/parent::*/following-sibling::*/section"); foreach ($sections as $section) { $titleNode = $xpath->query(".//span[contains(@class, 'section__title')]", $section); $infoNode = $xpath->query(".//span[contains(@class, 'section__info')]", $section); if ($titleNode->length && $infoNode->length) { $title = trim($titleNode->item(0)->textContent); $info = trim($infoNode->item(0)->textContent); $details['bankingSupport'][$this->normalizeKey($title)] = $info; } } } // Парсинг документов private function parseDocuments($xpath, &$details) { $docNodes = $xpath->query("//a[contains(@class, 'section__doc-link')] | //a[contains(@href, '/document/')]"); foreach ($docNodes as $node) { $docHref = $node->getAttribute('href'); if (!preg_match('/^https?:\/\//', $docHref)) { $docHref = 'https://zakupki.gov.ru' . $docHref; } $details['documents'][] = [ 'fileName' => trim($node->textContent), 'url' => $docHref ]; } } // Извлечение региона из блока time-zone private function extractRegionFromTimeZone($xpath, &$details) { $timeZoneNode = $xpath->query("//div[contains(@class, 'time-zone__value')]/span"); if ($timeZoneNode->length) { $timeZoneText = trim($timeZoneNode->item(0)->textContent); // Извлекаем название региона из строки типа "Москва МСК (UTC+3)" if (preg_match('/^([^М\s]+)/', $timeZoneText, $matches)) { $regionName = mb_strtolower(trim($matches[1]), 'UTF-8'); // Ищем соответствие в нашем маппинге регионов foreach ($this->regionMapping as $code => $name) { if (stripos($regionName, mb_strtolower($name, 'UTF-8')) !== false || stripos(mb_strtolower($name, 'UTF-8'), $regionName) !== false) { $details['region'] = $code; $details['regionName'] = $name; return true; } } // Ищем в городах foreach ($this->cityMapping as $city => $code) { if (stripos($regionName, $city) !== false) { $details['region'] = $code; $details['regionName'] = $this->regionMapping[$code]; return true; } } // Если не нашли в маппинге, но регион есть, сохраняем его название if (!empty($regionName)) { $details['regionName'] = ucfirst($regionName); return true; } } } return false; } // Нормализация ключей для массивов private function normalizeKey($key) { $key = trim($key); $key = preg_replace('/[^\w\s-]/u', '', $key); $key = preg_replace('/\s+/', '_', $key); $key = mb_strtolower($key, 'UTF-8'); return $key; } // Получение списка доступных регионов public function getRegions() { return $this->regionMapping; } public function __destruct() { if ($this->curl) { curl_close($this->curl); } if (file_exists($this->cookies)) { @unlink($this->cookies); } } } // Обработка запросов if (isset($_GET['action'])) { header('Content-Type: application/json; charset=utf-8'); try { $parser = new ZakupkiParser(); if ($_GET['action'] === 'search') { $params = [ 'page' => $_GET['page'] ?? 1, 'size' => $_GET['per_page'] ?? 20, 'search_text' => $_GET['search_text'] ?? '', 'inn' => $_GET['inn'] ?? '', 'price_min' => $_GET['price_min'] ?? null, 'price_max' => $_GET['price_max'] ?? null, 'date_from' => $_GET['date_from'] ?? null, 'date_to' => $_GET['date_to'] ?? null, 'regions' => isset($_GET['regions']) ? explode(',', $_GET['regions']) : [], 'city_search' => $_GET['city_search'] ?? '' ]; $result = $parser->searchTenders($params); echo json_encode($result, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT); } elseif ($_GET['action'] === 'detail' && !empty($_GET['regNumber'])) { $details = $parser->getTenderDetails($_GET['regNumber']); echo json_encode($details, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT); } elseif ($_GET['action'] === 'regions') { $regions = $parser->getRegions(); echo json_encode($regions, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT); } } catch (Exception $e) { http_response_code(500); echo json_encode(['error' => $e->getMessage()], JSON_UNESCAPED_UNICODE); } exit; } ?> <!DOCTYPE html> <html lang="ru"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Полный парсер госзакупок</title> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> <link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet" /> <style> .card-detail { transition: all 0.3s; border-left: 4px solid transparent; } .card-detail:hover { transform: translateY(-5px); box-shadow: 0 10px 20px rgba(0,0,0,0.1); border-left-color: #0d6efd; } .section-title { border-left: 4px solid #0d6efd; padding-left: 15px; margin: 25px 0 15px; } .loader { width: 3rem; height: 3rem; } .document-badge { cursor: pointer; transition: all 0.2s; margin: 2px; } .document-badge:hover { background: #0d6efd!important; transform: scale(1.05); } .pagination .page-item.active .page-link { background: #0d6efd; border-color: #0d6efd; } .lot-badge { font-size: 0.85rem; } .region-badge { font-size: 0.75rem; margin-left: 5px; } .select2-container { width: 100% !important; } .tender-stats { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; border-radius: 10px; padding: 20px; margin-bottom: 20px; } .cache-info { position: fixed; top: 10px; right: 10px; z-index: 1000; } .detail-section { margin-bottom: 30px; border: 1px solid #e9ecef; border-radius: 8px; overflow: hidden; } .detail-section-header { background: #f8f9fa; padding: 15px 20px; border-bottom: 1px solid #e9ecef; font-weight: 600; color: #495057; } .detail-section-content { padding: 20px; } .info-table { width: 100%; margin-bottom: 0; } .info-table td { padding: 8px 12px; border-bottom: 1px solid #f1f3f4; } .info-table td:first-child { font-weight: 500; color: #6c757d; width: 30%; } .object-item { border: 1px solid #e9ecef; border-radius: 6px; margin-bottom: 15px; overflow: hidden; } .object-item-header { background: #f8f9fa; padding: 10px 15px; font-weight: 500; } .object-item-content { padding: 15px; } .characteristics-table { font-size: 0.9em; margin-top: 10px; } .financial-table { font-size: 0.9em; margin-bottom: 20px; } </style> </head> <body> <div class="container py-4"> <h1 class="text-center mb-4"> <i class="fas fa-file-contract me-2"></i>Полный парсер госзакупок </h1> <!-- Информация о кэше --> <div id="cacheInfo" class="cache-info d-none"> <div class="alert alert-info alert-dismissible fade show" role="alert"> <i class="fas fa-info-circle me-2"></i> <span id="cacheMessage">Данные загружены из кэша</span> <button type="button" class="btn-close" data-bs-dismiss="alert"></button> </div> </div> <!-- Статистика --> <div id="tenderStats" class="tender-stats d-none"> <div class="row text-center"> <div class="col-md-3"> <h3 id="totalTenders">0</h3> <p class="mb-0">Всего тендеров</p> </div> <div class="col-md-3"> <h3 id="totalValue">0 ₽</h3> <p class="mb-0">Общая стоимость</p> </div> <div class="col-md-3"> <h3 id="avgValue">0 ₽</h3> <p class="mb-0">Средняя стоимость</p> </div> <div class="col-md-3"> <h3 id="regionsCount">0</h3> <p class="mb-0">Регионов</p> </div> </div> </div> <!-- Форма поиска --> <div class="card mb-4"> <div class="card-body"> <form id="searchForm" class="row g-3"> <div class="col-md-6"> <label class="form-label">Ключевые слова</label> <input type="text" name="search_text" class="form-control" placeholder="Номер или название закупки..."> </div> <div class="col-md-6"> <label class="form-label">ИНН заказчика</label> <input type="text" name="inn" class="form-control" placeholder="ИНН организации..."> </div> <div class="col-md-6"> <label class="form-label">Регионы</label> <select name="regions" id="regionSelect" class="form-select" multiple> <!-- Опции будут загружены динамически --> </select> <div class="form-text">Выберите один или несколько регионов для фильтрации</div> </div> <div class="col-md-6"> <label class="form-label">Поиск по городу/региону</label> <input type="text" name="city_search" class="form-control" placeholder="Введите название города или региона..."> <div class="form-text">Например: Москва, Санкт-Петербург, Екатеринбург</div> </div> <div class="col-md-3"> <label class="form-label">Минимальная цена</label> <div class="input-group"> <input type="number" name="price_min" class="form-control" placeholder="₽"> <span class="input-group-text">₽</span> </div> </div> <div class="col-md-3"> <label class="form-label">Максимальная цена</label> <div class="input-group"> <input type="number" name="price_max" class="form-control" placeholder="₽"> <span class="input-group-text">₽</span> </div> </div> <div class="col-md-3"> <label class="form-label">Дата от</label> <input type="date" name="date_from" class="form-control"> </div> <div class="col-md-3"> <label class="form-label">Дата до</label> <input type="date" name="date_to" class="form-control"> </div> <div class="col-md-2"> <label class="form-label">На странице</label> <select name="per_page" class="form-select"> <option value="10">10</option> <option value="20" selected>20</option> <option value="50">50</option> <option value="100">100</option> </select> </div> <div class="col-md-8 d-flex align-items-end"> <button type="submit" class="btn btn-primary w-100"> <i class="fas fa-search me-2"></i>Найти тендеры </button> </div> <div class="col-md-2 d-flex align-items-end"> <button type="button" class="btn btn-outline-secondary w-100" onclick="app.clearCache()"> <i class="fas fa-trash me-2"></i>Очистить кэш </button> </div> </form> </div> </div> <!-- Результаты --> <div id="results" class="mb-4"> <div class="text-center py-5"> <div class="spinner-border text-primary loader" role="status"> <span class="visually-hidden">Загрузка...</span> </div> <p class="mt-3">Загружаем список тендеров...</p> </div> </div> <!-- Пагинация --> <nav id="pagination" class="d-none"> <ul class="pagination justify-content-center"> <!-- Номера страниц будут вставлены здесь --> </ul> </nav> </div> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script> <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script> <script> class ZakupkiApp { constructor() { this.currentPage = 1; this.totalPages = 1; this.perPage = 20; this.totalItems = 0; this.currentTender = null; this.cache = new Map(); this.regions = {}; this.initializeRegions(); this.searchTenders(); } async initializeRegions() { try { const response = await fetch('?action=regions'); if (response.ok) { this.regions = await response.json(); this.populateRegionSelect(); } } catch (error) { console.error('Ошибка загрузки регионов:', error); } } populateRegionSelect() { const select = document.getElementById('regionSelect'); select.innerHTML = ''; Object.entries(this.regions).forEach(([code, name]) => { const option = document.createElement('option'); option.value = code; option.textContent = name; select.appendChild(option); }); // Инициализация Select2 $('#regionSelect').select2({ placeholder: 'Выберите регионы...', allowClear: true, width: '100%' }); } async searchTenders(page = 1) { try { this.showLoader('Загружаем список тендеров...'); this.currentPage = page; const formData = new FormData(document.getElementById('searchForm')); const selectedRegions = $('#regionSelect').val() || []; const params = { action: 'search', page: page, per_page: formData.get('per_page') || 20, search_text: formData.get('search_text') || '', inn: formData.get('inn') || '', price_min: formData.get('price_min') || null, price_max: formData.get('price_max') || null, date_from: formData.get('date_from') || null, date_to: formData.get('date_to') || null, regions: selectedRegions.join(','), city_search: formData.get('city_search') || '' }; // Проверяем кэш const cacheKey = JSON.stringify(params); if (this.cache.has(cacheKey)) { const cachedResult = this.cache.get(cacheKey); this.renderTenders(cachedResult); this.showCacheInfo('Данные загружены из кэша'); return; } // Формируем параметры запроса const queryString = Object.keys(params) .filter(key => params[key] !== null && params[key] !== '') .map(key => `${key}=${encodeURIComponent(params[key])}`) .join('&'); const response = await fetch(`?${queryString}`); if (!response.ok) throw new Error('Ошибка сети'); const result = await response.json(); // Сохраняем в кэш this.cache.set(cacheKey, result); this.renderTenders(result); } catch (error) { this.showError(error.message); } } renderTenders(data) { const tenders = data.content || []; this.totalItems = data.totalElements || tenders.length; this.totalPages = Math.ceil(this.totalItems / this.perPage); // Обновляем статистику this.updateStats(tenders); let html = `<h2 class="mb-3">Найдено тендеров: ${this.totalItems.toLocaleString('ru-RU')}</h2>`; if (tenders.length === 0) { html += `<div class="alert alert-warning"> <i class="fas fa-exclamation-triangle me-2"></i> По вашему запросу тендеров не найдено </div>`; } else { html += '<div class="row g-4">'; tenders.forEach(tender => { const regNumber = tender.regNumber || tender.tenderId || ''; const number = tender.purchaseNumber || ''; const title = tender.purchaseObjectInfo || 'Без названия'; const customer = tender.customer?.fullName || 'Заказчик не указан'; // Цена let price = 'Цена не указана'; if (tender.lot?.price) { const numPrice = parseFloat(tender.lot.price.replace(/[^\d.]/g, '')); if (!isNaN(numPrice)) { price = numPrice.toLocaleString('ru-RU', { minimumFractionDigits: 2, maximumFractionDigits: 2 }) + ' ₽'; } } // Регион const regionBadge = tender.region && this.regions[tender.region] ? `<span class="badge bg-secondary region-badge">${this.regions[tender.region]}</span>` : ''; // Даты const formatDate = (dateString) => { if (!dateString) return 'Н/Д'; try { const date = new Date(dateString); return isNaN(date) ? dateString : date.toLocaleDateString('ru-RU'); } catch { return dateString; } }; const posted = formatDate(tender.publishDate); const updated = formatDate(tender.updateDate); const deadline = formatDate(tender.submissionCloseDateTime); html += ` <div class="col-md-6"> <div class="card card-detail h-100" data-tender-id="${regNumber}"> <div class="card-body"> <h5 class="card-title text-primary">${title}${regionBadge}</h5> <h6 class="card-subtitle mb-2 text-muted">${number}</h6> <div class="mb-2"> <span class="badge bg-info">${price}</span> </div> <p class="card-text"> <strong>Заказчик:</strong> ${customer}<br> <strong>Размещено:</strong> ${posted}<br> <strong>Обновлено:</strong> ${updated}<br> <strong>Окончание подачи:</strong> ${deadline} </p> <button class="btn btn-sm btn-outline-primary" onclick="app.showTenderDetails('${regNumber}')"> <i class="fas fa-info-circle me-1"></i> Подробнее </button> </div> </div> </div>`; }); html += '</div>'; } document.getElementById('results').innerHTML = html; this.renderPagination(); } updateStats(tenders) { const statsContainer = document.getElementById('tenderStats'); if (tenders.length === 0) { statsContainer.classList.add('d-none'); return; } let totalValue = 0; const regions = new Set(); tenders.forEach(tender => { if (tender.lot?.price) { const price = parseFloat(tender.lot.price.replace(/[^\d.]/g, '')); if (!isNaN(price)) { totalValue += price; } } // Учитываем регион, если он определен if (tender.region && tender.region !== '') { regions.add(tender.region); } // Также проверяем regionName для случаев, когда код региона не определен if (!tender.region && tender.regionName && tender.regionName !== 'Не определен') { regions.add(tender.regionName); } }); const avgValue = tenders.length > 0 ? totalValue / tenders.length : 0; document.getElementById('totalTenders').textContent = this.totalItems.toLocaleString('ru-RU'); document.getElementById('totalValue').textContent = totalValue.toLocaleString('ru-RU', { minimumFractionDigits: 0, maximumFractionDigits: 0 }) + ' ₽'; document.getElementById('avgValue').textContent = avgValue.toLocaleString('ru-RU', { minimumFractionDigits: 0, maximumFractionDigits: 0 }) + ' ₽'; document.getElementById('regionsCount').textContent = regions.size; statsContainer.classList.remove('d-none'); } showCacheInfo(message) { const cacheInfo = document.getElementById('cacheInfo'); const cacheMessage = document.getElementById('cacheMessage'); cacheMessage.textContent = message; cacheInfo.classList.remove('d-none'); setTimeout(() => { cacheInfo.classList.add('d-none'); }, 3000); } clearCache() { this.cache.clear(); this.showCacheInfo('Кэш очищен'); } renderPagination() { const paginationContainer = document.querySelector('#pagination ul'); paginationContainer.innerHTML = ''; if (this.totalPages <= 1) { document.getElementById('pagination').classList.add('d-none'); return; } document.getElementById('pagination').classList.remove('d-none'); // Кнопка "Назад" const prevBtn = document.createElement('li'); prevBtn.className = `page-item ${this.currentPage === 1 ? 'disabled' : ''}`; prevBtn.innerHTML = `<a class="page-link" href="#" onclick="app.goToPage(${this.currentPage - 1})">&laquo;</a>`; paginationContainer.appendChild(prevBtn); // Номера страниц const startPage = Math.max(1, this.currentPage - 2); const endPage = Math.min(this.totalPages, startPage + 4); for (let i = startPage; i <= endPage; i++) { const pageItem = document.createElement('li'); pageItem.className = `page-item ${i === this.currentPage ? 'active' : ''}`; pageItem.innerHTML = `<a class="page-link" href="#" onclick="app.goToPage(${i})">${i}</a>`; paginationContainer.appendChild(pageItem); } // Кнопка "Вперед" const nextBtn = document.createElement('li'); nextBtn.className = `page-item ${this.currentPage === this.totalPages ? 'disabled' : ''}`; nextBtn.innerHTML = `<a class="page-link" href="#" onclick="app.goToPage(${this.currentPage + 1})">&raquo;</a>`; paginationContainer.appendChild(nextBtn); } goToPage(page) { if (page < 1) page = 1; if (page > this.totalPages) page = this.totalPages; this.searchTenders(page); } async showTenderDetails(identifier) { try { this.showLoader('Загружаем детали тендера...'); this.currentTender = identifier; // Проверяем кэш const cacheKey = `details_${identifier}`; if (this.cache.has(cacheKey)) { const cachedDetails = this.cache.get(cacheKey); this.renderTenderDetails(cachedDetails); this.showCacheInfo('Детали загружены из кэша'); return; } const response = await fetch(`?action=detail&regNumber=${encodeURIComponent(identifier)}`); if (!response.ok) throw new Error('Ошибка загрузки деталей'); const details = await response.json(); // Сохраняем в кэш this.cache.set(cacheKey, details); this.renderTenderDetails(details); } catch (error) { this.showError(error.message); } } renderTenderDetails(details) { if (!details) { this.showError('Не удалось загрузить детали тендера'); return; } const regionInfo = details.region && this.regions[details.region] ? `<span class="badge bg-info">${this.regions[details.region]}</span>` : ''; let html = ` <div class="mb-4"> <button class="btn btn-secondary" onclick="app.backToList()"> <i class="fas fa-arrow-left me-2"></i>Назад к списку </button> </div> <!-- Основная информация --> <div class="detail-section"> <div class="detail-section-header"> <h4 class="mb-0"> <i class="fas fa-info-circle me-2"></i> ${details.generalInfo?.object || 'Детальная информация о тендере'} ${regionInfo} </h4> </div> <div class="detail-section-content"> <table class="info-table"> <tr><td>Регистрационный номер</td><td>${details.regNumber || 'Не указан'}</td></tr> <tr><td>Номер закупки</td><td>${details.generalInfo?.number || 'Не указан'}</td></tr> <tr><td>Статус</td><td><span class="badge bg-info">${details.generalInfo?.status || 'Неизвестен'}</span></td></tr> <tr><td>Организация</td><td>${details.generalInfo?.organization || 'Не указана'}</td></tr> <tr><td>Начальная цена</td><td>${details.generalInfo?.price || 'Не указана'}</td></tr> <tr><td>Дата размещения</td><td>${details.generalInfo?.publishDate || 'Не указана'}</td></tr> <tr><td>Дата обновления</td><td>${details.generalInfo?.updateDate || 'Не указана'}</td></tr> <tr><td>Окончание подачи заявок</td><td>${details.generalInfo?.endDate || 'Не указана'}</td></tr> <tr><td>Регион</td><td>${details.regionName || 'Не определен'}</td></tr> </table> </div> </div>`; // Общая информация о закупке if (details.generalInfo && Object.keys(details.generalInfo).length > 0) { html += ` <div class="detail-section"> <div class="detail-section-header"> <i class="fas fa-clipboard-list me-2"></i>Общая информация о закупке </div> <div class="detail-section-content"> <table class="info-table">`; Object.entries(details.generalInfo).forEach(([key, value]) => { if (value && !['title', 'number', 'status', 'object', 'organization', 'price', 'publishDate', 'updateDate', 'endDate'].includes(key)) { const label = this.formatLabel(key); html += `<tr><td>${label}</td><td>${value}</td></tr>`; } }); html += `</table></div></div>`; } // Контактная информация if (details.contactInfo && Object.keys(details.contactInfo).length > 0) { html += ` <div class="detail-section"> <div class="detail-section-header"> <i class="fas fa-address-book me-2"></i>Контактная информация </div> <div class="detail-section-content"> <table class="info-table">`; Object.entries(details.contactInfo).forEach(([key, value]) => { if (value) { const label = this.formatLabel(key); html += `<tr><td>${label}</td><td>${value}</td></tr>`; } }); html += `</table></div></div>`; } // Информация о процедуре закупки if (details.procedureInfo && Object.keys(details.procedureInfo).length > 0) { html += ` <div class="detail-section"> <div class="detail-section-header"> <i class="fas fa-calendar-alt me-2"></i>Информация о процедуре закупки </div> <div class="detail-section-content"> <table class="info-table">`; Object.entries(details.procedureInfo).forEach(([key, value]) => { if (value) { const label = this.formatLabel(key); html += `<tr><td>${label}</td><td>${value}</td></tr>`; } }); html += `</table></div></div>`; } // Информация о цене if (details.priceInfo && Object.keys(details.priceInfo).length > 0) { html += ` <div class="detail-section"> <div class="detail-section-header"> <i class="fas fa-ruble-sign me-2"></i>Информация о цене контракта </div> <div class="detail-section-content"> <table class="info-table">`; Object.entries(details.priceInfo).forEach(([key, value]) => { if (value) { const label = this.formatLabel(key); html += `<tr><td>${label}</td><td>${value}</td></tr>`; } }); html += `</table></div></div>`; } // Информация об объекте закупки (КТРУ) if (details.objectInfo && Array.isArray(details.objectInfo) && details.objectInfo.length > 0) { html += ` <div class="detail-section"> <div class="detail-section-header"> <i class="fas fa-boxes me-2"></i>Информация об объекте закупки (КТРУ) </div> <div class="detail-section-content">`; details.objectInfo.forEach((item, index) => { if (typeof item === 'object' && item.code) { html += ` <div class="object-item"> <div class="object-item-header"> <strong>Позиция ${index + 1}:</strong> ${item.code} - ${item.name} </div> <div class="object-item-content"> <div class="row"> <div class="col-md-6"> <table class="info-table"> <tr><td>Код КТРУ</td><td>${item.code}</td></tr> <tr><td>Наименование</td><td>${item.name}</td></tr> <tr><td>Единица измерения</td><td>${item.unit}</td></tr> </table> </div> <div class="col-md-6"> <table class="info-table"> <tr><td>Количество</td><td>${item.quantity}</td></tr> <tr><td>Цена за единицу</td><td>${item.price}</td></tr> <tr><td>Общая стоимость</td><td>${item.sum}</td></tr> </table> </div> </div>`; // Характеристики if (item.characteristics && Object.keys(item.characteristics).length > 0) { html += ` <h6 class="mt-3 mb-2">Характеристики:</h6> <table class="characteristics-table table table-sm"> <thead> <tr> <th>Характеристика</th> <th>Значение</th> </tr> </thead> <tbody>`; Object.entries(item.characteristics).forEach(([charName, charValue]) => { html += `<tr><td>${charName}</td><td>${charValue}</td></tr>`; }); html += `</tbody></table>`; } html += `</div></div>`; } }); if (details.objectInfo.total) { html += `<div class="alert alert-info mt-3"> <strong>Итого:</strong> ${details.objectInfo.total} </div>`; } html += `</div></div>`; } // Требования и преимущества if (details.requirements && Object.keys(details.requirements).length > 0) { html += ` <div class="detail-section"> <div class="detail-section-header"> <i class="fas fa-check-circle me-2"></i>Преимущества и требования к участникам </div> <div class="detail-section-content"> <table class="info-table">`; Object.entries(details.requirements).forEach(([key, value]) => { if (value) { const label = this.formatLabel(key); html += `<tr><td>${label}</td><td>${value}</td></tr>`; } }); html += `</table></div></div>`; } // Требования заказчика if (details.customerRequirements && Object.keys(details.customerRequirements).length > 0) { html += ` <div class="detail-section"> <div class="detail-section-header"> <i class="fas fa-user-tie me-2"></i>Требования заказчика </div> <div class="detail-section-content"> <table class="info-table">`; Object.entries(details.customerRequirements).forEach(([key, value]) => { if (value) { const label = this.formatLabel(key); html += `<tr><td>${label}</td><td>${value}</td></tr>`; } }); html += `</table></div></div>`; } // Финансовая информация if (details.financialInfo && Object.keys(details.financialInfo).length > 0) { html += ` <div class="detail-section"> <div class="detail-section-header"> <i class="fas fa-chart-line me-2"></i>Финансовая информация </div> <div class="detail-section-content">`; // Таблицы финансирования if (details.financialInfo.tables && Array.isArray(details.financialInfo.tables)) { details.financialInfo.tables.forEach((table, index) => { if (Array.isArray(table) && table.length > 0) { html += `<table class="financial-table table table-striped">`; table.forEach((row, rowIndex) => { if (Array.isArray(row)) { html += `<tr>`; row.forEach(cell => { const tag = rowIndex === 0 ? 'th' : 'td'; html += `<${tag}>${cell}</${tag}>`; }); html += `</tr>`; } }); html += `</table>`; } }); } // Дополнительная финансовая информация html += `<table class="info-table">`; Object.entries(details.financialInfo).forEach(([key, value]) => { if (value && key !== 'tables') { const label = this.formatLabel(key); html += `<tr><td>${label}</td><td>${value}</td></tr>`; } }); html += `</table></div></div>`; } // Обеспечение заявки if (details.applicationSecurity && Object.keys(details.applicationSecurity).length > 0) { html += ` <div class="detail-section"> <div class="detail-section-header"> <i class="fas fa-shield-alt me-2"></i>Обеспечение заявки </div> <div class="detail-section-content"> <table class="info-table">`; Object.entries(details.applicationSecurity).forEach(([key, value]) => { if (value) { const label = this.formatLabel(key); html += `<tr><td>${label}</td><td>${value}</td></tr>`; } }); html += `</table></div></div>`; } // Условия контракта if (details.contractConditions && Object.keys(details.contractConditions).length > 0) { html += ` <div class="detail-section"> <div class="detail-section-header"> <i class="fas fa-file-contract me-2"></i>Условия контракта </div> <div class="detail-section-content"> <table class="info-table">`; Object.entries(details.contractConditions).forEach(([key, value]) => { if (value) { const label = this.formatLabel(key); html += `<tr><td>${label}</td><td>${value}</td></tr>`; } }); html += `</table></div></div>`; } // Обеспечение исполнения контракта if (details.contractSecurity && Object.keys(details.contractSecurity).length > 0) { html += ` <div class="detail-section"> <div class="detail-section-header"> <i class="fas fa-lock me-2"></i>Обеспечение исполнения контракта </div> <div class="detail-section-content"> <table class="info-table">`; Object.entries(details.contractSecurity).forEach(([key, value]) => { if (value) { const label = this.formatLabel(key); html += `<tr><td>${label}</td><td>${value}</td></tr>`; } }); html += `</table></div></div>`; } // Банковское сопровождение if (details.bankingSupport && Object.keys(details.bankingSupport).length > 0) { html += ` <div class="detail-section"> <div class="detail-section-header"> <i class="fas fa-university me-2"></i>Банковское и казначейское сопровождение </div> <div class="detail-section-content"> <table class="info-table">`; Object.entries(details.bankingSupport).forEach(([key, value]) => { if (value) { const label = this.formatLabel(key); html += `<tr><td>${label}</td><td>${value}</td></tr>`; } }); html += `</table></div></div>`; } // Документы if (details.documents && details.documents.length > 0) { html += ` <div class="detail-section"> <div class="detail-section-header"> <i class="fas fa-file-pdf me-2"></i>Документы </div> <div class="detail-section-content"> <div class="d-flex flex-wrap gap-2">`; details.documents.forEach(doc => { html += `<a href="${doc.url}" target="_blank" class="document-badge badge bg-success text-decoration-none"> <i class="fas fa-file-pdf me-1"></i> ${doc.fileName || 'Документ'} </a>`; }); html += `</div></div></div>`; } document.getElementById('results').innerHTML = html; document.getElementById('pagination').classList.add('d-none'); } formatLabel(key) { return key.replace(/_/g, ' ') .replace(/\b\w/g, l => l.toUpperCase()) .replace(/([a-z])([A-Z])/g, '$1 $2'); } backToList() { this.searchTenders(this.currentPage); } showLoader(message = 'Загрузка данных...') { document.getElementById('results').innerHTML = ` <div class="text-center py-5"> <div class="spinner-border text-primary loader" role="status"> <span class="visually-hidden">Загрузка...</span> </div> <p class="mt-3">${message}</p> </div>`; document.getElementById('pagination').classList.add('d-none'); } showError(message) { document.getElementById('results').innerHTML = ` <div class="alert alert-danger"> <h4><i class="fas fa-exclamation-circle me-2"></i> Ошибка</h4> <p>${message}</p> <button class="btn btn-secondary" onclick="app.searchTenders()"> <i class="fas fa-sync me-1"></i> Попробовать снова </button> </div>`; document.getElementById('pagination').classList.add('d-none'); } } // Инициализация приложения const app = new ZakupkiApp(); // Обработка формы document.getElementById('searchForm').addEventListener('submit', function(e) { e.preventDefault(); app.searchTenders(); }); </script> </body> </html>

госзакупкиPHPпарсер12.06.2025

Создание хакерского приложения

Программированиеru
4.0

Сделай "хакерское" приложение с успокаивающей музыкой на фоне и терминалом с зеленым текстом, к терминалу будет подключена нейросеть deepseek через api, сделай что бы приложение можно было вызвать комбинацией клавиш windows+shift+f, а так же сделай что бы в терминале (именно в самом терминале) можно было воспроизводить видео из ютуба, сделай что бы в приложении можно было вводить текст не только текстом но и голосом. Сделай что бы по приложению была инструкция на английском и русском языках (в самом приложении) а еще что бы было 2 страницы - главная (с терминалом) и дополнительная с кнопками которые вызывают команды из обычного терминала но только по нажатию. Добавь в терминал команды для управления системой, всего команд должно быть 200+, еще на 2 страница справа сделай анимацию ноутбука на экране которого бесконечно взламывается система

хакерское приложениенейросетьтерминал29.03.2025