При отправке какой-нибудь чехни на работе мы писали заявку водителю. Особого формата эти заявки не имели, потому возникали странные вопросы и т.д. У начальника был doc-файл, в котором формат заявки был более менее цивильный(но не обязательный). Проблема в том, что в основном отправка была на склад и торговые объекты, которых 60 штук. Делать для каждого свой документ или изменять каждый раз ну очень нудное занятие.
Пример из жизни: пришли с заправки картриджи, из них штук 10 надо отправить по торговым объектам. Ну это ведь очень нудное дело написать 10 этих «заявок». А нужно было это довольно часто. В силу того что делать в ворде это было весьма сомнительной идеей, для разработки был выбран веб. В коем на тот момент я имел некоторые познания, но довольно посредственные. Когда-то давно делал статичные странички или даже недо-лэндинг. Но никакого там javascript, html5 и уж тем более бэкэнда.
Писалось это все на одном энтузиазме и лени. Пожалуй это, в принципе, лучший способ работы. Никто не вгоняет тебя в конкретные рамки.
Итак, у нас есть MySQL с таблицами, в которых есть данные по торговым объектам. Тут оказалось ничего сложного, погуглил, изучил три основных запроса в базе данных(SELECT, INSERT, UPDATE). Забавно оказалось, что есть разные варианты представления, которые в одних версиях базы работают — в других через пень колоду. Например = или like. Запомнил что не нужно делать limit выборки, если ключ не уникален и не сделана сортировка(order by) ну и т.д. В общем через несколько часов я уже мог запросом вытянуть нужные мне данные. Вот только дальше самой консоли или MySQL Front(пока я пользуюсь этим решением, хотя его глюки в новых версиях меня уже подбешивают). Ах да, перед этим мне пришлось узнать как в консоли создать себе юзера с полными правами, т.к. рут привязан к localhost-у.
В общем-то из всей этой кучи инфы для осуществления моей задумки мне нужен был всего-то:
SELECT * FROM `search.apt` WHERE LOGIN = '$apteka' LIMIT 1
при этом логин уникален, лишних данных не много и можно вытягивать все.
Далее вопрос встал с тем что необходимо как-то эти данные засунуть в веб-страницу. У нас есть корпоративный портал, который мне и послужил примером. Большое спасибо его автору, что он использовал именно библиотеку mysqli, а не mysql(примерно через год, когда я решил что пора переходить из морально устаревшего php 5.4 в 7.. оказалось что там mysql больше не поддерживается. И если без какого-нибудь split при переходе сайт и работает, но с косяками, то с отвалившейся базой точно нет).
Бэкэнд корпоративного портала был написан на php, еще в школе я очень не плохо писал на паскале(на этом собственно все мое творчество в программировании и закончилось) и он(php) мне показался достаточно адекватным языком(пару лет назад купил платный аккаунт на javarush, но дальше 10 урока так и не уехал. Не потому что сложно, потому что на java абсолютно не тянет писать + смена работы + кризис. В общем было совсем не до этого. Но вот php в сравнении с java мне больше лег в душу). Огромнейшим плюсом оказалось то что под него есть куча мануалов, в т.ч. и на русском + куча готовых решений по форумам, да и его синхронный код значительно проще, какого-нибудь асинхронного. В общем начал я с php, о чем ничуть не жалею.
Итак, нам нужно было вытянуть данные из MySQL и запихать их в страницу. Библиотека mysqli мне понравилась в силу того, что я до сих пор пока не столкнулся с тем чтобы она не могла выполнить какую-нибудь нужную мне команду. При этом она отправляет запрос в sql как есть.
Итак основная часть была:
$transferform = new mysqli($dbaddr, $dbuser, $dbpass, $db); if ($transferform->connect_errno) { echo "Не удалось подключиться к MySQL:".$transferform->connect_error; exit; } $transferform->query("SET NAMES utf8"); $result = $transferform->query("SELECT * FROM `search.apt` WHERE LOGIN = '$apteka' limit 1"); $row = $result->fetch_array(); ... $transferform->close;
Собственно она не делает ничего, кроме как вытягивает данные в массив $row. Переменную $apteka я получаю из $_GET, а документ возвращаю в виде html, сгенерированный через echo. Чтобы не заморачиваться с переносом проекта, он должен содержать все настройки в отдельном файле, что я и сделал. Просто создал файл с переменными и вставил его в нужный скрипт при помощи include.
Был уже готовый прототип, правда без красивой морды. Начальник его оценил и решил что было бы круто еще вставить туда карту, чем я и занялся. Вставка гугл-карт вообще ничего сложного из себя не представляет, после того как поймешь какой api пользоваться. я выбрал google maps embed. Получил токен и все что мне нужно было сделать это вставить фрейм с ссылкой, которая включала в себя токен и адрес. Т.к. html генерировался бэкэндом, что вставить из php нужную строку в фрейм было проще простого.
Примерно так выглядел скрипт в первой редакции:
<?php //НАСТРОЙКИ ---------------------------> include 'settings.php'; //---------------------------------> $apteka = $_GET['apteka']; $author = $_GET['author']; $mobile = $_GET['mobile']; $text = nl2br($_GET['text']); $ch = $_GET['ch']; if ($apteka == 'farmin.office' || $apteka == 'farmin.sklad' || $apteka == 'farmin.address') { if ($apteka == 'farmin.address') { $row['FIO'] = $_GET['to']; $row['MNUM'] = $_GET['tomob']; $row['OBJ'] = $_GET['toorg']; $row['ADR'] = $_GET['address1']; $row['REAL_ADR'] = $row['ADR']; $row['ADR'] = $row['ADR'].$_GET['address2']; $row['TIME'] = $_GET['rtime']; } if ($apteka == 'farmin.sklad') { $row['FIO'] = 'Моб. +***********'; $row['MNUM'] = 'Гор. +375173859068'; $row['OBJ'] = 'Склад ОДО Фармин'; $row['ADR'] = 'г.Минск, ул Казинца 121А, территория НПО Интеграл(въезд с ул. Корженевского)'; $row['REAL_ADR'] = 'г.Минск, ул Казинца 121А'; $row['TIME'] = 'Пн.-Пт. 8:30 - 17:30'; } if ($apteka == 'farmin.office') { $row['FIO'] = 'Моб. +**************'; $row['MNUM'] = 'Гор. +375172698822'; $row['OBJ'] = 'ОДО Фармин'; $row['ADR'] = 'г.Минск, пр-кт Независимости 177, 3 подъезд, 6 этаж'; $row['REAL_ADR'] = 'г.Минск, пр-кт Независимости 177'; $row['TIME'] = 'Пн.-Пт. 8:30 - 17:30'; } } else { $transferform = new mysqli($dbaddr, $dbuser, $dbpass, $db); if ($transferform->connect_errno) { echo "Не удалось подключиться к MySQL:".$transferform->connect_error; exit; } $transferform->query("SET NAMES utf8"); $result = $transferform->query("SELECT * FROM `search.apt` WHERE LOGIN = '$apteka' limit 1"); $row = $result->fetch_array(); $row['MNUM'] = $row['TEL']; } echo "<HTML>"; echo "\n"."<HEAD>"; echo "\n"."<TITLE>ЗАЯВКА ДЛЯ ВОДИТЕЛЕЙ</TITLE>"; echo "<link rel=\"stylesheet\" href=\"transferform.css\" type=\"text/css\">"; echo "\n"."</HEAD>"; echo "\n"."<table class=\"form\">"; echo "\n"."<tr>"; echo "\n"."<th colspan=\"2\"><p class=\"P2\">ЗАЯВКА ДЛЯ ВОДИТЕЛЕЙ</th>"; echo "\n"."</tr>"; echo "\n"."<tr>"; echo "\n"."<td class=\"textform\">Автор заявки</td>"; echo "\n"."<td class=\"textform1\">".$author."<br> ".$mobile."</td>"; echo "\n"."</tr>"; echo "\n"."<tr>"; echo "\n"."<td class=\"textform\">Адрес доставки/получения :</p></td>"; echo "\n"."<td class=\"textform1\">".$row['OBJ']."<br>".$row['ADR']."</td>"; echo "\n"."</tr>"; echo "\n"."<tr>"; echo "\n"."<td class=\"textform\">Контактное лицо:</td>"; echo "\n"."<td class=\"textform1\">".$row['FIO']."<br>".$row['MNUM']."</td>"; echo "\n"."</tr>"; echo "\n"."<tr>"; echo "\n"."<td class=\"textform\">Рабочее время:</td>"; echo "\n"."<td class=\"textform1\">".$row['TIME']."</td>"; echo "\n"."</tr>"; echo "\n"."<tr>"; echo "\n"."<td class=\"textform\">Описание:</td>"; echo "\n"."<td class=\"textform1\">".$text."</td>"; echo "\n"."</tr>"; if ($ch == 0) { echo "\n"."<tr>"; echo "\n"."<td colspan=\"2\"><iframe id=\"maps\" src=\"https://www.google.com/maps/embed/v1/place?key=************************&q=".$row['REAL_ADR']."\" frameborder=\"0\" style=\"border:0\" allowfullscreen></iframe></td>"; echo "\n"."</tr>"; } echo "\n"."</table>"; echo "\n"."</HTML>"; include 'statistic.php'; $transferform->close; //пример запроса /transfer.php?apteka=kompovid.apt01&author=Сергей+Дудко+Тест&mobile=%2B375ХХУУУУУУУ&text=Отправить+картридж ?>
При написании фронтэнда для этого было решено использовать выпадающие списки(у нас несколько юрлиц и у каждого свои торговые объекты). Я подыскал в интернете пример с синхронизированными списками на js. Тут все просто, при выборе определенного юрлица следующий список формируется только из его торговых объектов. Учитывая, что все это хранится прямо в js коде и при изменении состава юрлиц надо лезть и переписывать его(а еще браузеры любят хранить js в кэше и при его изменении не сразу обновлять), то это очень опрометчивое решение. Впрочем это была одна из причин переписать все к чертям несколько позже. В придачу к этому я добавил chekbox для наличия или отсутствия карты и две кнопки. По одной генерировался документ. Точнее просто вставлялся ответ от бэкэнда в тело документа, выглядело это так:
var request = 'apteka=' + encodeURIComponent(apteka) + '&' + 'author=' + encodeURIComponent(author) + '&' + 'mobile=' + encodeURIComponent(mobile) + '&' + 'text=' + encodeURIComponent(text) + '&' + 'ch=' + encodeURIComponent(ch); $(".framephp").html('<iframe id="iframe" src="transferecho.php?' + request + '" frameborder="0" scrolling="no">');
По второй кнопке производился фокус на данный класс и вызывалась функция печати:
var iframe = $('#iframe')[0]; iframe.contentWindow.focus(); iframe.contentWindow.print();
В итоге мой проект имел вид:
Веб-морда:
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <!-- Состав проекта: transfer.css; transferform.css; transfer.js; transferecho.js; linkedselect.js; massive.js; jquery-latest.min.js; author.js transferform.html; transfer.php; transferecho.php; dropdown.png --> <script type="text/javascript" src="jquery-latest.min.js"></script> <link rel="stylesheet" href="transfer.css" type="text/css"> <script type="text/javascript" src="transferecho.js"></script> <!--- Для динамической генерации страницы(в браузере): <script type="text/javascript" src="transferecho.js"></script>, Для генерации страницы на сервере(необходимы права записи): <script type="text/javascript" src="transfer.js"></script> Менять строчкой выше.---> <script type="text/javascript" src="linkedselect.js"></script> </head> <body> <table> <tr> <td> <div class="delivery_apt"> <div class="apttype"> <select size="1" id="List1"> <option value="apt"> Аптека </option> <option value="mag"> Магазин </option> <option value="farmin.office"> Офис Фармин </option> <option value="farmin.sklad"> Склад Фармин </option> <option value="farmin.address"> Ввести вручную </option> </select> </div> <div class="aptid"> <select size="1" id="List2" class="apt_id"></select> </div> <div class="aptnum"> <select size="1" id="List3" class="apt_num"></select> </div> <div class="src"> <script type="text/javascript" src="massive.js"></script> </div> <div class="author"> <INPUT id="author" VALUE ="Сергей Дудко"> </div> <div class="mobile"> <INPUT id="mobile" class="mobile" VALUE ="+375290000000"> </div> <script type="text/javascript" src="author.js"></script> <div class="text"> <textarea name="text" class="text" id="text">Отправить картридж после заправки</textarea> </div> <div class="checkbox"> <input type="checkbox" id="checkbox" onchange="ch1()">Удалить карту</input> </div> <div class="bottomsp1"> <button type="button" id="button" class="button button1" onclick="generate_form()" value="generate_form"/>Заполнить заявку</button> </div> <div class="bottomsp2"> <button type="button" id="button2" class="button2 button3" onclick="delete_form()" value="delete_form"/>Печать заявки</button> </div> <div class="bottomsp3"></div> </div> </td> <td><div class="framephp"><iframe id="iframe" src="transferform.html" frameborder='0' scrolling='no'></iframe></div> </td> </tr> </table> </body> </html>
Файл стилей:
/* Выпадающие списки */ select#List1, select#List2, select#List3 { -webkit-appearance: button; -webkit-border-radius: 2px; -webkit-box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.1); -webkit-padding-end: 20px; -webkit-padding-start: 2px; -webkit-user-select: none; background-image: url(dropdown.png), -webkit-linear-gradient(#FAFAFA, #F4F4F4 40%, #E5E5E5); background-position: 96% center; background-repeat: no-repeat; border: 1px solid #AAA; color: #555; font-size: inherit; margin: 10px; overflow: hidden; padding: 5px 10px; text-overflow: ellipsis; white-space: nowrap; width: 270px; } /* Автор, Номер, Текст */ #author, #mobile, #text { cursor: pointer; border-radius: 4px; color: #555; border: 1px solid #AAA; font-size: inherit; width: 270px; padding: 5px 10px; text-overflow: ellipsis; white-space: nowrap; margin: 10px; } #text { width: 270px; height: 100px; } /*Адрес(вручную) */ #to, #tomob, #toorg, #address1, #address2, #address3, #address4, #rtime, #addresstext{ cursor: pointer; border-radius: 4px; color: #555; border: 1px solid #AAA; font-size: inherit; width: 230px; padding: 5px 10px; text-overflow: ellipsis; white-space: nowrap; margin: 2px; } #addresstext { width: 250px; margin: 10px; } /*Удалить карту(флаг) */ .checkbox{ cursor: pointer; border-radius: 4px; color: #555; border: 1px solid #AAA; font-size: inherit; text-overflow: ellipsis; white-space: nowrap; width: 270px; margin: 10px; } /* Кнопки */ .button, .button2 { background-color: #00FF00; border: none; color: white; padding: 16px 32px; text-align: center; text-decoration: none; display: inline-block; font-size: inherit; margin: 4px 2px; -webkit-transition-duration: 0.4s; /* Safari */ transition-duration: 0.4s; cursor: pointer; margin: 10px; width: 270px; } .button1 { background-color: white; color: #555; border: 2px solid #555; } .button1:hover { background-color: #00FF00; color: white; } /* Удалить форму */ .button3 { background-color: white; color: #555; border: 2px solid #555; } .button3:hover { background-color: #c10b14; color: white; } #iframe { margin: 20px; width: 910px; height: 1310px; } .bottomsp3 { height: 400px; }
Картинка со стрелочкой для выпадающего списка, библиотека jquery-latest.min.js, скрипт синхронизированных списков:
/* wwww.tigir.com (дата последней модификации - 30.11.2007) Библиотека linkedselect.js из статьи "Javascript SELECT - динамические списки" - http://www.tigir.com/linked_select.htm syncList - "класс" связанных списков */ function syncList(){} //Определяем функцию конструктор //Определяем методы //Метод sync() - принимает список из значений атрибутов id элементов SELECT, образующих связанный список и запускает их синхронизацию syncList.prototype.sync = function() { //Перебираем аргументы (id элементов SELECT) и назначаем событиям onChange селектов, с соответствующими id, функцию-обработчик. //В качестве обработчика выступает второй метод объекта syncList - _sync (напрямую его вызывать не нужно) //Обработчик назначается всем элементам SELECT кроме последнего в списке аргументов, т.к. последний не влияет ни на какой другой элемент SELECT и с ним не нужно синхронизироваться. for (var i=0; i < arguments.length-1; i++) document.getElementById(arguments[i]).onchange = (function (o,id1,id2){return function(){o._sync(id1,id2);};})(this, arguments[i], arguments[i+1]); document.getElementById(arguments[0]).onchange();//запускаем обработчик onchange первого селекта, чтобы при загрузке страницы заполнить дочерние селекты значениями. } //служебный метод _sync - срабатывает при смене выбранного элемента в текущем (старшем) элементе SELECT (по его событию onChange) и изменяет содержимое зависимого селекта на основании значения выбранного в старшем селекте. syncList.prototype._sync = function (firstSelectId, secondSelectId) { var firstSelect = document.getElementById(firstSelectId); var secondSelect = document.getElementById(secondSelectId); secondSelect.length = 0; //обнуляем второй (подчиненный) SELECT if (firstSelect.length>0)//если первый (старший) SELECT не пуст { //из свойства dataList, с данными для заполнения подчиненных селектов, берем ту часть данных, которая соответствует именно значению элемента, //выбранного в первом селекте, и определяет содержимое подчиненного элемента SELECT. var optionData = this.dataList[ firstSelect.options[firstSelect.selectedIndex==-1 ? 0 : firstSelect.selectedIndex].value ]; //заполняем второй (подчиненный) селект значениями (создаем элементы option) for (var key in optionData || null) secondSelect.options[secondSelect.length] = new Option(optionData[key], key); //если в старшем SELECT-е нет выделенного пункта, выделяем первый if (firstSelect.selectedIndex == -1) setTimeout( function(){ firstSelect.options[0].selected = true;}, 1 ); //если во втором списке нет выделенного пункта, выделяем первый его пункт if (secondSelect.length>0) setTimeout( function(){ secondSelect.options[0].selected = true;}, 1 ); } //если второй (подчиненный) селект имеет в свою очередь свои подчиненные селекты (те, для которых он главный), //то запускаем его обработчик onchange, чтобы изменить его подчиненные селекты secondSelect.onchange && secondSelect.onchange(); };
Массив объектов к нему:
$('#List1').change(function () { var cliD = document.getElementById('List1'); var cli = cliD.options[cliD.selectedIndex].value; if ((cli == 'farmin.office') || (cli == 'farmin.sklad')) { $('.apt_id').remove(); $('.apt_num').remove(); } else { if (cli == 'farmin.address') { $('.apt_id').remove(); var req = '<div class=\"apt_num\">\n'; req = req + '<div id=\"addresstext\">Кому:\n'; req = req + '<div class="to">\n<INPUT id=\"to\" VALUE =\"Сергей Дудко\"></INPUT>\n</div>\n'; req = req + 'Телефон:\n'; req = req + '<div class="tomob">\n<INPUT id=\"tomob\" VALUE =\"+375172698833\"></INPUT>\n</div>\n'; req = req + 'Организация:\n'; req = req + '<div class="toorg">\n<INPUT id=\"toorg\" VALUE =\"ОДО Фармин\"></INPUT>\n</div>\n'; req = req + 'Город:\n'; req = req + '<div class="address1">\n<INPUT id=\"address1\" VALUE =\"Минск\"></INPUT>\n</div>\n'; req = req + 'Улица:\n'; req = req + '<div class="address2">\n<INPUT id=\"address2\" VALUE =\"Независимости\"></INPUT>\n</div>\n'; req = req + 'Дом:\n'; req = req + '<div class="address3">\n<INPUT id=\"address3\" VALUE =\"177\"></INPUT>\n</div>\n'; req = req + 'Дополнительное поле:\n'; req = req + '<div class="address4">\n<INPUT id=\"address4\" VALUE =\"3 подъезд, 6 этаж\"></INPUT>\n</div>\n'; req = req + 'Рабочее время:\n'; req = req + '<div class="rtime">\n<INPUT id=\"rtime\" VALUE =\"Пн.-Пт. 8:30 - 17:30\"></INPUT>\n</div>\n</div>\n</div>\n'; $(".aptnum").html(req); } else { var req1 = '<select size=\"1\" id=\"List2\" class=\"apt_id\"></select>'; var req2 = '<select size=\"1\" id=\"List3\" class=\"apt_num\"></select>'; var req3 = '<script type=\"text/javascript\" src=\"massive.js\"></script>' //вызов самого себя для генерации динамических списков $(".aptid").html(req1); $(".aptnum").html(req2); $(".src").html(req3); } } }); // Создаем новый объект связанных списков var syncList1 = new syncList; // Определяем значения подчиненных списков (2 и 3 селектов) syncList1.dataList = { /* Определяем элементы второго списка в зависимости от выбранного значения в первом списке */ 'apt':{ 'farmin.apt':'Фармин', 'fitobel.apt':'Фитобел', 'neska.apt':'НеСка', 'kompovid.apt':'Комповид' }, 'mag':{ 'fitobel.mag':'Фитобел', 'neska.mag':'НеСка' }, /* Определяем элементы третьего списка в зависимости от выбранного значения во втором списке */ 'farmin.apt':{ 'farmin.apt01':'1' }, 'fitobel.apt':{ 'fitobel.apt01':'1', 'fitobel.apt02':'2', 'fitobel.apt03':'3', 'fitobel.apt04':'4', 'fitobel.apt05':'5', 'fitobel.apt06':'6', 'fitobel.apt07':'7', 'fitobel.apt08':'8', 'fitobel.apt09':'9', 'fitobel.apt10':'10', 'fitobel.apt11':'11', 'fitobel.apt12':'12', 'fitobel.apt14':'14', 'fitobel.apt15':'15', 'fitobel.apt16':'16', 'fitobel.apt17':'17', 'fitobel.apt18':'18', 'fitobel.apt19':'19', 'fitobel.apt20':'20', 'fitobel.apt21':'21', 'fitobel.apt22':'22', 'fitobel.apt23':'23', 'fitobel.apt24':'24', 'fitobel.apt25':'25', 'fitobel.apt26':'26', 'fitobel.apt27':'27', 'fitobel.apt28':'28', 'fitobel.apt29':'29', 'fitobel.apt30':'30' }, 'neska.apt':{ 'neska.apt01':'1', 'neska.apt02':'2', 'neska.apt03':'3', 'neska.apt04':'4', 'neska.apt05':'5', 'neska.apt06':'6', 'neska.apt07':'7', 'neska.apt08':'8', 'neska.apt09':'9', 'neska.apt10':'10', 'neska.apt11':'11', 'neska.apt12':'12', 'neska.apt14':'14', 'neska.apt15':'15', 'neska.apt16':'16', 'neska.apt17':'17', 'neska.apt18':'18', 'neska.apt19':'19', 'neska.apt20':'20', 'neska.apt21':'21', 'neska.apt22':'22', 'neska.apt23':'23', 'neska.apt24':'24' }, 'kompovid.apt':{ 'kompovid.apt01':'1', 'kompovid.apt02':'2', 'kompovid.apt03':'3', 'kompovid.apt04':'4', 'kompovid.apt06':'6', 'kompovid.apt07':'7', 'kompovid.apt08':'8', 'kompovid.apt09':'9', 'kompovid.apt11':'11', 'kompovid.apt12':'12' }, 'fitobel.mag':{ 'fitobel.mag02':'2', 'fitobel.mag03':'3', 'fitobel.mag04':'4', 'fitobel.mag07':'7' }, 'neska.mag':{ 'neska.mag01':'1' } }; // Включаем синхронизацию связанных списков syncList1.sync("List1","List2","List3");
Основной скрипт проекта:
$(function(){ /* выбор города */ var apteka, author, text, mobile, ch; $('#button').click(function generate_form(){ //заполнить заявку var chbox; chbox=document.getElementById('checkbox'); var apttypeD = document.getElementById('List1'); var apttype = apttypeD.options[apttypeD.selectedIndex].value; if (chbox.checked) { //удалить карту ch = '1'; } else { ch = '0'; } if ((apttype == 'apt') || (apttype == 'mag')) { var aptidD = document.getElementById('List2'); var aptnumD = document.getElementById('List3'); apteka = aptnumD.options[aptnumD.selectedIndex].value; author = $("#author").val(); mobile = $("#mobile").val(); text = document.getElementById("text").value; var request = 'apteka=' + encodeURIComponent(apteka) + '&' + 'author=' + encodeURIComponent(author) + '&' + 'mobile=' + encodeURIComponent(mobile) + '&' + 'text=' + encodeURIComponent(text) + '&' + 'ch=' + encodeURIComponent(ch); $(".framephp").html('<iframe id="iframe" src="transferecho.php?' + request + '" frameborder="0" scrolling="no">'); } else { if ((apttype == 'farmin.office') || (apttype == 'farmin.sklad')) { //выполняется в случае выбора склада/офиса apteka = apttype; author = $("#author").val(); mobile = $("#mobile").val(); text = document.getElementById("text").value; var request = 'apteka=' + encodeURIComponent(apteka) + '&' + 'author=' + encodeURIComponent(author) + '&' + 'mobile=' + encodeURIComponent(mobile) + '&' + 'text=' + encodeURIComponent(text) + '&' + 'ch=' + encodeURIComponent(ch); $(".framephp").html('<iframe id="iframe" src="transferecho.php?' + request + '" frameborder="0" scrolling="no">'); } else { if (apttype == 'farmin.address'){ //выполняется в случае введения вручную apteka = apttype; author = $("#author").val(); mobile = $("#mobile").val(); text = document.getElementById("text").value; var to = $("#to").val(); var tomob = $("#tomob").val(); var toorg = $("#toorg").val(); var address1 = 'г. ' + $("#address1").val(); var address2 = ', ' + $("#address2").val(); var address3 = ' ' + $("#address3").val(); var address4 = ', ' + $("#address4").val(); var rtime = $("#rtime").val(); var request = 'apteka=' + encodeURIComponent(apteka) + '&' + 'author=' + encodeURIComponent(author) + '&' + 'mobile=' + encodeURIComponent(mobile); request = request + '&' + 'text=' + encodeURIComponent(text) + '&' + 'to=' + encodeURIComponent(to) + '&' + 'tomob=' + encodeURIComponent(tomob) + '&' + 'toorg=' + encodeURIComponent(toorg); address1 = address1 + address2 + address3; address2 = address4; request = request + '&' + 'address1=' + encodeURIComponent(address1) + '&' + 'address2=' + encodeURIComponent(address2); request = request + '&' + 'rtime=' + encodeURIComponent(rtime) + '&' + 'ch=' + encodeURIComponent(ch); $(".framephp").html('<iframe id="iframe" src="transferecho.php?' + request + '" frameborder="0" scrolling="no">'); } else { alert("Ошибка запроса. Обновите страницу(Ctrl + F5) и попробуйте еще раз."); //исключение } } } }); $('#button2').click(function delete_form(){ //печать var iframe = $('#iframe')[0]; iframe.contentWindow.focus(); iframe.contentWindow.print(); }); $('.checkbox').click(function ch1(){ //чекбокс if (document.getElementById("checkbox").checked == false) { document.getElementById("checkbox").checked = true; } else { document.getElementById("checkbox").checked = false; } }); })
Файл стилей для генерируемой формы:
table { background: #f0ffff; /* цвет фона */ border: 1px #000000 solid; /* стиль рамки */ border-collapse: collapse ; /* убирание задвоенности рамки */ background-position: 97% center; } td { border: 1px #000000 solid; /* стиль рамки ячеек */ padding: 0.2em; } th { background: #dcdcdc; border: 1px #000000 solid; /* стиль рамки заголовков */ padding: 0.2em; } #maps { margin: 2px; width: 850px; height: 850px; } .P2 { font-size: 32px; } .textform, .textform1 { background-color: white; color: black; font-size: 24px; height: 50px; } .textform { width: 300px; } .textform1 { width: 550px; } .P2 { margin: 15px; }
Страничка генерируемой формы(пример, подгружаемый до генерации):
<HTML> <HEAD> <TITLE>ЗАЯВКА ДЛЯ ВОДИТЕЛЕЙ</TITLE><link rel="stylesheet" href="transferform.css" type="text/css"> </HEAD> <table class="form"> <tr> <th colspan="2"><p class="P2">ЗАЯВКА ДЛЯ ВОДИТЕЛЕЙ</th> </tr> <tr> <td class="textform">Автор заявки</td> <td class="textform1"><br></td> </tr> <tr> <td class="textform">Адрес доставки/получения :</p></td> <td class="textform1"><br></td> </tr> <tr> <td class="textform">Контактное лицо:</td> <td class="textform1"><br></td> </tr> <tr> <td class="textform">Рабочее время:</td> <td class="textform1"></td> </tr> <tr> <td class="textform">Описание:</td> <td class="textform1"></td> </tr> <tr> <td colspan="2"><iframe id="maps" src="https://www.google.com/maps/embed/v1/place?key=******************************&q=Республика Беларусь, г. Минск" frameborder="0" style="border:0" allowfullscreen></iframe></td> </tr> </table> </HTML>
Скрипт для автоматической вставки автора заявки:
/* СОРТИРОВКА КУКИ */ function getCookie(cName){ // разделение куков var cookieStr = decodeURIComponent(document.cookie), // получаем строку куков cookieArray = cookieStr.split(';'), // вспоминаем о чудесном методе split и разбиваем строку с куками на упорядоченый массив по разделителю ";" i, j; // удалим пробельные символы (если они, вдруг, есть) в начале и в конце у каждой куки for (j=0; j<cookieArray.length; j++) cookieArray[j] = cookieArray[j].replace(/(\s*)\B(\s*)/g, ''); var cookieNameArray = new Array({name: '', value: new Array()}); // результирующий упорядоченный массив // каждый элемент будет объектом с методами name и value // name - имя куки, value - упорядоченный массив значений куки // обрабатываем каждую куку for (i=0; i<cookieArray.length; i++) { var keyValue = cookieArray[i].split('='), // разделяем имя и значение cookieVal = unescape(keyValue[1]).split(';'); // разделяем значения, если они заданы перечнем // удаляем пробельные символы (если они, вдруг, есть) у значений в начале и в конце for (j=0; j<cookieVal.length; j++) cookieVal[j] = cookieVal[j].replace(/(\s*)[\B*](\s*)/g, ''); keyValue[0] = keyValue[0].replace(/(\s*)[\B]*(\s*)/g, ''); // вот получился такой cookie-объект cookieNameArray[i] = { name: keyValue[0], value: cookieVal }; }; var cookieNALen = cookieNameArray.length; // размер полученного массива // выбираем нужную куку if (!cName) return cookieNameArray else for (i=0; i<cookieNALen; i++) if (cookieNameArray[i].name == cName) return cookieNameArray[i].value; return false; }; /* СОРТИРОВКА КУКИ */ var author = getCookie('cn')[0]; var mobile = getCookie('mobile')[0]; author = author.replace("+"," "); mobile = mobile.replace(")",""); mobile = mobile.replace("(",""); mobile = mobile.replace("+","");mobile = mobile.replace("+","");mobile = mobile.replace("+",""); //костыль mobile = mobile.replace(" ","");mobile = mobile.replace(" ","");mobile = mobile.replace(" ",""); //костыль mobile = mobile.replace("-","");mobile = mobile.replace("-","");mobile = mobile.replace("-",""); //костыль (проблемы были с отображением у Оли отображение: +375+(29)+***-**-86) mobile = "+" + mobile; $(".author").html('<INPUT id="author" VALUE ="' + author + '">'); $(".mobile").html('<INPUT id="mobile" class="mobile" VALUE ="' + mobile + '">');
Бэкэнд для сбора статистики:
<?php //НАСТРОЙКИ ---------------------------> include 'settings.php'; //---------------------------------> function GetRealIp(){ if (!empty($_SERVER['HTTP_CLIENT_IP'])){ $ip=$_SERVER['HTTP_CLIENT_IP']; } elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])){ $ip=$_SERVER['HTTP_X_FORWARDED_FOR']; } else{ $ip=$_SERVER['REMOTE_ADDR']; } return $ip; } $ipaddr = gethostbyaddr(GetRealIp()); $username = $_COOKIE['cn']; $transferstat = new mysqli($dbaddr, $dbuser, $dbpass, $db); if ($transferstat->connect_errno) { echo "Не удалось подключиться к MySQL:".$transferstat->connect_error; exit; } $transferstat->query("SET NAMES utf8"); $transferstat->query("insert into `zayavkastat`(DATA, IPADDR, USERNAME, AUTHOR, NUMBER, TEXT, OBJ, ADDR, FIO, MNUM, TIME) values ('".date('d-m-Y')."', '".$ipaddr."', '".$username."', '".$author."', '".$mobile."', '".$text."', '".$row['OBJ']."', '".$row['ADR']."', '".$row['FIO']."', '".$row['MNUM']."', '".$row['TIME']."')"); $transferstat->close(); ?>
Бэкэнд для генерации странички:
<?php //НАСТРОЙКИ ---------------------------> include 'settings.php'; //---------------------------------> $apteka = $_GET['apteka']; $author = $_GET['author']; $mobile = $_GET['mobile']; $text = nl2br($_GET['text']); $ch = $_GET['ch']; if ($apteka == 'farmin.office' || $apteka == 'farmin.sklad' || $apteka == 'farmin.address') { if ($apteka == 'farmin.address') { $row['FIO'] = $_GET['to']; $row['MNUM'] = $_GET['tomob']; $row['OBJ'] = $_GET['toorg']; $row['ADR'] = $_GET['address1']; $row['REAL_ADR'] = $row['ADR']; $row['ADR'] = $row['ADR'].$_GET['address2']; $row['TIME'] = $_GET['rtime']; } if ($apteka == 'farmin.sklad') { $row['FIO'] = 'Моб. +*******'; $row['MNUM'] = 'Гор. +375173859068'; $row['OBJ'] = 'Склад ОДО Фармин'; $row['ADR'] = 'г.Минск, ул Казинца 121А, территория НПО Интеграл(въезд с ул. Корженевского)'; $row['REAL_ADR'] = 'г.Минск, ул Казинца 121А'; $row['TIME'] = 'Пн.-Пт. 8:30 - 17:30'; } if ($apteka == 'farmin.office') { $row['FIO'] = 'Моб. +******'; $row['MNUM'] = 'Гор. +375172698822'; $row['OBJ'] = 'ОДО Фармин'; $row['ADR'] = 'г.Минск, пр-кт Независимости 177, 3 подъезд, 6 этаж'; $row['REAL_ADR'] = 'г.Минск, пр-кт Независимости 177'; $row['TIME'] = 'Пн.-Пт. 8:30 - 17:30'; } } else { $transferform = new mysqli($dbaddr, $dbuser, $dbpass, $db); if ($transferform->connect_errno) { echo "Не удалось подключиться к MySQL:".$transferform->connect_error; exit; } $transferform->query("SET NAMES utf8"); $result = $transferform->query("SELECT * FROM `search.apt` WHERE LOGIN = '$apteka' limit 1"); $row = $result->fetch_array(); $row['MNUM'] = $row['TEL']; } echo "<HTML>"; echo "\n"."<HEAD>"; echo "\n"."<TITLE>ЗАЯВКА ДЛЯ ВОДИТЕЛЕЙ</TITLE>"; echo "<link rel=\"stylesheet\" href=\"transferform.css\" type=\"text/css\">"; echo "\n"."</HEAD>"; echo "\n"."<table class=\"form\">"; echo "\n"."<tr>"; echo "\n"."<th colspan=\"2\"><p class=\"P2\">ЗАЯВКА ДЛЯ ВОДИТЕЛЕЙ</th>"; echo "\n"."</tr>"; echo "\n"."<tr>"; echo "\n"."<td class=\"textform\">Автор заявки</td>"; echo "\n"."<td class=\"textform1\">".$author."<br> ".$mobile."</td>"; echo "\n"."</tr>"; echo "\n"."<tr>"; echo "\n"."<td class=\"textform\">Адрес доставки/получения :</p></td>"; echo "\n"."<td class=\"textform1\">".$row['OBJ']."<br>".$row['ADR']."</td>"; echo "\n"."</tr>"; echo "\n"."<tr>"; echo "\n"."<td class=\"textform\">Контактное лицо:</td>"; echo "\n"."<td class=\"textform1\">".$row['FIO']."<br>".$row['MNUM']."</td>"; echo "\n"."</tr>"; echo "\n"."<tr>"; echo "\n"."<td class=\"textform\">Рабочее время:</td>"; echo "\n"."<td class=\"textform1\">".$row['TIME']."</td>"; echo "\n"."</tr>"; echo "\n"."<tr>"; echo "\n"."<td class=\"textform\">Описание:</td>"; echo "\n"."<td class=\"textform1\">".$text."</td>"; echo "\n"."</tr>"; if ($ch == 0) { echo "\n"."<tr>"; echo "\n"."<td colspan=\"2\"><iframe id=\"maps\" src=\"https://www.google.com/maps/embed/v1/place?key=****************&q=".$row['REAL_ADR']."\" frameborder=\"0\" style=\"border:0\" allowfullscreen></iframe></td>"; echo "\n"."</tr>"; } echo "\n"."</table>"; echo "\n"."</HTML>"; include 'statistic.php'; $transferform->close; //пример запроса /transfer.php?apteka=kompovid.apt01&author=Сергей+Дудко+Тест&mobile=%2B375ХХУУУУУУУ&text=Отправить+картридж ?>
Файл с настройками:
<?php //Настройка MySQL: $dbaddr = '***********'; $dbuser = '*******'; $dbpass = '******'; $db = '**************'; ?>
В итоге, в таком небольшом проекте я получил начальные знания о SQL-запросах, опыт работы с mysqli, некоторые знания о REST-запросах, работа с html5(вставка в DOM, фокус, печать), работа с cookie, работа с чужим кодом(синхронизированные списки), css. Не плохое начало, не правда ли?
Что же произошло дальше с проектом читайте в «Заявка для водителей 2.0».
Результат можно посмотреть ТУТ.