Если Вам необходима система опроса пользователя, построенная на прогрессивном фреймврке VueJS, то она перед Вами. Данная система удобная и простая в использовании. Вы сможете задать своим пользователям вопросы и получить развернутые ответы. При необходимости её можно легко усовершенствовать, но даже «из коробки», она достаточно гибкая и мощная.
После прохождения опроса, пользователю предлагается заполнить свои контактные данные: имя, номер телефона, email и написать сопроводительный текст, если имеется.
Данную систему очень легко собрать, для неё не нужна база данных. Но Вы, если хотите, можете все сделать через REST API, при желании.
Вопросы хранятся в ../public/questions.json
Все очень просто и понятно. Чтобы все заработало, установите NodeJS на Ваш PC. Гугли: nodejs install. Скачайте файл и установите программу. После нужно установить Git или просто скачать и разархивировать мой проект.
Например, Вы разархивировали все в папку D:/nodejsprojects/downloaded/vue-quiz-master
Далее через командную строку (Win+R
) или через командную строку NodeJS:
cd /d D:/nodejsprojects/downloaded/vue-quiz-master
вы окажитесь в этой папке командной строкой. Далее:
npm install
После того, как все пакеты установятся, запустите команду:
npm run serve
и откройте в браузере (после того, как все сгенерируется):
http://localhost:8080
Вы увидите проект.
Чтобы его выгрузить себе на хостинг:
npm run build
После компиляции файлов, появится папка dist
там же. Все её содержимое выгружайте на хостинг.
Чтобы создать свой опрос, необходимо изменить все в файле ../public/questions.json
Там есть 4 вопроса для примера с разными вариантами отображения. Например, есть чекбоксы, где можно выбрать несколько вариантов. Есть радио переключалетели (да/нет, или что-то подобное).
Можно также ПРИ УСЛОВИИ, если человек отвечает на ДА/НЕТ (всего два вопроса) — отвечает ДА, то можно также добавить форму текстовый input, числовой input и textarea.
Все это вполне понятно по записям в json файле:
"type": "single"
, //single (один вариант из нескольких [да/нет, услуга/товар/еще что-то]) или multiple (много вариантов можно выбрать)"question": "Искали ли Вы сайты конкурентов?"
, //сам вопрос "answers": [//ответы "Да", "Нет" ]"ifYes":true
,//если отвечаем на первый (ДА или УСЛУГА [см. файл ../public/questions.json]), то можно здесь поставить 'true' и ниже написать описание"ifYesDataType":"textarea"
,//тип (texarea,input) /*Здесь в файле примера есть еще "ifYesDataTypeInput":"number", это значит, что тип input будет с номером (ввод номера или количества цифрами), например: ваше количество товара: 700 штук */"ifYesDataText":"Введите ссылку или ссылки, если есть"
//пояснение, что нужно вводить в поле texarea или input
PHP обработка и JS код, как в видео....
На своем проекте я использую подгрузку данного опроса, только если пользователь долистал до определенного места на странице, чтобы облегчить её вес при первой отрисовке.
Код JS выглядит так, который посылает запрос к серверу... Этот код может быть прямо на странице или в любом уже подключенном JS файле на странице.
if (window.fetch) {/*если да*/
function formEncode(obj) {/*эта функция позволит преобразовать JS Object в валидные данные для POST-запроса*/
var str = [];
for(let p in obj)
str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));
return str.join("&");
};
async function getQuiz(){
let response = await fetch("/", {
method: "POST",
headers: { "Content-type": "application/x-www-form-urlencoded"},
mode: "same-origin",
credentials: "same-origin",
body:formEncode({get:"quiz",location_:location.href})
});
let data = await response.text();
return data;
};
getQuiz().then(data => {
loadedQuiz=true;
document.querySelector('.loadQuiz').innerHTML=data
});
function getJS(src){/*Эта функция позволяет добавить JS код на страницу, чтобы он интерпретировался браузером, как именно JS, а не text*/
fetch(src, {
method: "GET",
headers: {
Accept: 'text/javascript',
'Content-Type': 'text/javascript',
},
cache: "no-cache"
}).then(response => response.text())
.then((data) => {
const script = document.createElement("script");
script.innerHTML = data;
document.body.appendChild(script);
return
})
};
(async function() {
try {
await getJS('/sys/quiz/js/chunk-vendors.c6204bf9.js'),/*асинхронность используется, чтобы первым был подключен VUE и другие библиотеки*/
await getJS('/sys/quiz/js/app.a0a30381.js')/*а затем уже используются эти библиотеки в этом скрипте и вся логика...*/
} catch (err) {
console.error(err);
}
})();
};/*\ IF FETCH*/
При этом на Вашей странице уже должен быть:
<div class=loadQuiz></div>
Маршрутизатор в index.php корня сайта (запрос посылается именно на '/' корень сайта):
<?php
define('ROOT',getenv('DOCUMENT_ROOT'));
/*определяем какой у нас адрес сайта (https://order.inverser.pro , к примеру)*/
function url(){
if(isset($_SERVER['HTTPS'])){
$protocol = ($_SERVER['HTTPS'] && $_SERVER['HTTPS'] != "off") ? "https" : "http";
}
else{
$protocol = 'http';
}
return $protocol . "://" . $_SERVER['HTTP_HOST'];
}
/*Очистка пользовательского кода*/
function challsrt($data){
$array1 = array ('/*','*/','\'','"','//','wss:','blob:','localhost','^','*','http:','https:','<script','base64','where','/*','mysql','union','select','drop','\0x');
$data = strip_tags($data);
$data = htmlspecialchars($data, ENT_QUOTES);
$data = trim($data);
return str_ireplace($array1, '*', $data);
}
/*Ниже проверяем если POST запрос (от JS наш fetch) и location_ такой же, как и адрес нашего сайта (на всякий случай), то подключаем нужные файлы обработки запроса*/
#FETCH
if($_SERVER['REQUEST_METHOD']=='POST' && stripos($_POST['location_'],url())!==false){
if (!empty($_POST["get"])) {/*сама форма Quiz со стилями и HTML*/
//load quiz
if($_POST["get"]=='quiz'){
$f=ROOT.'/FetchQuiz.php';
if(!is_file($f)){
exit;
}
include $f;
exit;
}
}
/*Обработка данных опроса и самой контактной формы*/
if (!empty($_POST["set"])) {
//load quiz
if($_POST["set"]=='quizData'){
$f=ROOT.'/FetchQuizData.php';
if(!is_file($f)){
exit;
}
include $f;
exit;
}
}
exit;
}
#\FETCH
Подгрузка формы — файл FetchQuiz.php
(для первого запроса из JS файла):
<?php
if($_SERVER['REQUEST_METHOD']=='POST' && stripos($_POST['location_'],url())!==false){
$out= '
<style>
.quiz{overflow-x:auto;max-height:83vh}
.qDiv{border-radius:0 0 4px 4px;transition:opacity .5s linear,transform .7s linear;box-shadow:0 6px 0 -3px #333, 0 12px 0 -6px #333;max-width:504px;margin:42px auto 65px auto;background:#1d1d1d;padding:2rem 4px 4px 4px}
.custom-control-label{cursor:pointer}.list-group,.show,.tr{-webkit-transition:all .5s linear;transition:all .5s linear}.list-group{border-radius:3px}.d-block,..list-group{margin-bottom:15px}.selected{opacity:0}.show{opacity:1;margin:10px 0}.custom-control,.custom-control-label{display:block;text-align:left;padding:4px}.list-group{line-height:1;margin-bottom:0;padding:0 0 0 4px;-webkit-box-shadow:inset 0 0 0 1px #333;box-shadow:inset 0 0 0 1px #333}.list-group:hover{background:rgba(255,255,255,0.10)}.custom-control-label:after,.custom-control-label:before{left:0}.custom-control-label{padding-left:2rem}.nav-tabs{margin:10px 0}
.btn-success{position:relative;width:100%;padding:20px 7px;font-size:22px;background:rgba(0,91,253,.37);margin:10px 0;color:#fff}
.btn-success.disabled{background:rgba(255,255,255,.37)}
fieldset{border:0}
.custom-control-input{position: absolute;z-index: -1;opacity: 0}
.custom-control-input:checked ~ .custom-control-label::before {color: #fff;border-color: #007bff;background-color: #007bff}
.custom-control-input:focus ~ .custom-control-label::before {box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25)}
.custom-control-input:focus:not(:checked) ~ .custom-control-label::before {border-color: #80bdff}
.custom-control-label {position: relative;margin-bottom: 0;vertical-align: top}
.custom-control-label::before,.custom-control-label::after{position:absolute;content:"";top:0.25rem;width:1rem;height:1rem;pointer-events:none;transition:all .3s;background-color:#fff;border:#adb5bd solid 1px;border-radius:50%}
.custom-control-label::after {background: no-repeat 50% / 50% 50%}
.custom-control-input{position:absolute!important;z-index:-1!important;opacity:0}
.custom-checkbox .custom-control-input:checked~.custom-control-label::after{background-image: url("data:image/svg+xml,%3csvg xmlns=\'http://www.w3.org/2000/svg\' viewBox=\'0 0 8 8\'%3e%3cpath fill=\'%23fff\' d=\'M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z\'/%3e%3c/svg%3e")}
.custom-radio .custom-control-input:checked~.custom-control-label::after{background:#fff;top:.5rem;left:.25rem;width:.5rem;height:.5rem}
.invalid-feedback{position:absolute;z-index:2;left:0;display:none;border:1px solid #464646;width:100%;font-size:80%;color:#ffd3a3;background:#151414;padding:4px;border-radius:4px;line-height:1.2}
.invalid-feedback::after{content:"";position:absolute;top:-1.25rem;;left:1rem;border:10px solid;border-color:transparent transparent rgba(255, 255, 255, 0.52) transparent}
.form-control.is-invalid~.invalid-feedback, .form-control.is-invalid~.invalid-tooltip, .was-validated .form-control:invalid~.invalid-feedback, .was-validated .form-control:invalid~.invalid-tooltip{display:block}
#app svg{fill:#fff}
.form-control.is-invalid, .was-validated .form-control:invalid {border-color: #dc3545;padding-right: calc(1.5em + .75rem);background-image: url("data:image/svg+xml,%3csvg xmlns=\'http://www.w3.org/2000/svg\' fill=\'%23dc3545\' viewBox=\'-2 -2 7 7\'%3e%3cpath stroke=\'%23dc3545\' d=\'M0 0l3 3m0-3L0 3\'/%3e%3ccircle r=\'.5\'/%3e%3ccircle cx=\'3\' r=\'.5\'/%3e%3ccircle cy=\'3\' r=\'.5\'/%3e%3ccircle cx=\'3\' cy=\'3\' r=\'.5\'/%3e%3c/svg%3E");background-repeat: no-repeat;
background-position: center right calc(.375em + .1875rem);background-size: calc(.75em + .375rem) calc(.75em + .375rem)}
.cancelQuiz{background:#4286ff;top:0;left:0;border-radius:4px 4px 0 0;width:100%;padding:8px 0;color:#fff;box-shadow:0 3px 3px #151515}
.icon-cancel,.icon-cancel::after{width:16px;height: 2px;background: #fff;transform: rotate(45deg);transform-origin:70% 0}
.icon-cancel::after{content:"";position:absolute;top:0;right:0;transform:rotate(-90deg);transform-origin:50%}
.question-box-container form .fc fieldset{position:relative;width:44%}
.ok{position:absolute;z-index:2;top:0;right:0;bottom:0;left:0;background:#252525;background:linear-gradient(45deg, black, #333333);display:flex;justify-content:center;align-items:center;transition:.5s;transform:rotateX(90deg)}
.okCl{transform:rotateX(0)}
#app{width:500px;min-height:30vh}
.loadingQ{width:100%}
.loadingQ::after,.loadingQ::before{content:"";position:absolute;z-index:3;top:auto;right:0;bottom:50%;left:0;width:50%;height:1px;background:linear-gradient(90deg,transparent 0,#fff 50%,transparent 100%);margin:1rem 0 0 0;animation:aqz 1s infinite}
@keyframes aqz{50%{transform:translateX(100%)}}
.loadingQ::after{z-index:2;background:rgba(29,29,29,.9);top:0;bottom:0;width:auto;height:auto;animation:none;margin:0}
@media(max-width: 1024px){.quiz{overflow-x:inherit;max-height:unset}.qDiv{border-radius:4px!important;padding:4px!important;}}
@media(max-width: 524px){.question-box-container form .fc fieldset{width:100%}}
@media(max-height: 640px){.loadQuiz{display:block}}
</style>
<div class="pr cf qDiv">';
/*if (!$detect->isMobile() ){*/$out.='<div class="p tac cancelQuiz"><div class="pr in icon-cancel"></div><button class="cf">Отменить опрос и перейти к первому слайду</button></div>';/*}*/
$out.='<div class=pr>
<div id=app>
<div class=loadingQ></div>
</div>
</div>
</div>
';
echo $out;
exit;
}else{
header('HTTP/1.1 404 Not found');
header('location:/#err');
exit;
}
Обработка данных формы – файл FetchQuizData.php
:
<?php
if($_SERVER['REQUEST_METHOD']=='POST' && stripos($_POST['location_'],url())!==false){
$dataQuiz=$_POST['data'];
$dataForm=json_decode($_POST['form']);
$name=$phone=$email=$txt='';
if(!empty($dataForm->name))$name=(string)$dataForm->name;
if(!empty($dataForm->phone))$phone=(string)$dataForm->phone;
if(!empty($dataForm->email))$email=(string)$dataForm->email;
if(!empty($dataForm->userText))$txt=(string)$dataForm->userText;
$IP=getenv("REMOTE_ADDR");
$agent=getenv("HTTP_USER_AGENT");
$date=date("Y-m-d H:i:s");
$name=challsrt($name);
$phone=challsrt($phone);
$email=challsrt($email);
$txt=challsrt($txt);
$nameLen=iconv_strlen($name,'UTF-8');
$phoneLen=iconv_strlen($phone,'UTF-8');
$txtLen=iconv_strlen($txt,'UTF-8');
if($nameLen<3 AND $phoneLen<9){
echo json_encode(['error'=>'Проверьте правильность ввода имени и телефона','ok'=>'false']);
exit;
}
if(filter_var($email, FILTER_VALIDATE_EMAIL)===false){
echo json_encode(["error"=>'Проверьте правильность ввода адреса электронной почты. Например: irina@gmail.com',"ok"=>'false']);
exit;
}
$message='<p><a href="https://order.inverser.pro" style="font-size:22px;color:#0086ff;font-weight:bolder;">order.inverser.pro</a></p>
<p><strong>Имя:</strong> '.$name.'</p>
<p><strong>Email:</strong> '.$email.'</p>
<p><strong>Телефон:</strong> '.$phone.'</p>
<div>
<strong>Данные Quiz:</strong>
<pre>
'.challsrt($dataQuiz).'
</pre>
</div>
';
if($txtLen>0){$message .='<p><strong>Текст обращения: </strong><pre>'.$txt.'</pre></p>';}
$message .='
<p><strong>IP:</strong> '.$IP.'</p>
<p><strong>Дата:</strong> '.$date.'</p>
<p><strong>UserAgent:</strong> '.$agent.'</p>
';
$to="<noisset@inverser.pro>";/*TO ME*/
$headers ="Content-type: text/html; charset=utf-8 \r\n";
$headers .= "From: Inverser.PRO <noisset@inverser.pro>\r\n";
$headers .= "Reply-To: Inverser.PRO <noisset@inverser.pro>\r\n";
if(!mail($to, 'Мой Quiz /4 '.$date, $message, $headers)){
echo json_encode(['error'=>'Возникла ошибка... Попробуйте позже','ok'=>'false']);
exit;
}
echo json_encode(['error'=>'false','ok'=>'Благодарим за Ваше обращение. Мы скоро свяжемся с Вами.']);
/*TO USER*/
$to=$email;
$message='
<div style="border:3px solid #333;font-family:Calibri,monospace;background:#2f2f2f;color:#fff;border-radius:3px;max-width:630px;margin:0 auto">
<img style="display:block;margin:0;border-bottom: 1px solid #333;border-radius: 3px 3px 0 0;width:100%;max-width:630px" src="https://order.inverser.pro/sys/logo.jpg" alt="order.inverser.pro">
<div style="padding:9px;background:url(\'https://order.inverser.pro/sys/mailBg.jpg\')50% 50% /cover no-repeat"">
<h1 style="text-align: center;color:#f5f5f5">Здравствуйте, '.$name.'.</h1>
Вы обратились к нам через наш сайт <a href="https://order.inverser.pro/#email" style="color:#0086ff">order.inverser.pro</a>
<br>Мы постараемся связаться с Вами как можно скорее, чтобы обсудить все детали Вашего обращения.
<hr style="border:0;border-bottom:1px dotted #333">
<p>Будьте так добры, проверьте данные, которые Вы указали:</p>
<blockquote>
Ваш телефон: '.$phone.'
<br>Текст обращения:
<pre>
'.$txt.'
</pre>
</blockquote>
<br>Если все верно, ожидайте звонка или email-сообщения от нас.
<hr style="border:0;border-bottom:1px dotted #333">
<p><strong>За дополнительной информацией обращайтесь</strong>:
<br><div style="width:90px;display: inline-block">Тел:</div> <a style="color:#0086ff" href="tel:+380665532416">+38 (066) 553-24-16</a> (Vodafone UA) [Viber, WhatsApp]
<br><div style="width:90px;display: inline-block">VK:</div> <a style="color:#0086ff" href="https://vk.com/inverser">vk.com/inverser</a>
<br><div style="width:90px;display: inline-block">Facebook:</div> <a style="color:#0086ff" href="https://facebook.com/inverser">facebook.com/inverser</a>
<br><div style="width:90px;display: inline-block">Telegram:</div> <a style="color:#0086ff" href="https://t.me/inverserpro">telegram</a>
</p>
<hr style="border:0;border-bottom:1px dotted #333">
<p style="font-size: 12px;color:#ccc;text-align: justify">Данное письмо сгенерировано автоматически. Однако, Вы можете ответить на него.</p>
<div style="text-transform:uppercase;display:inline-block;text-align:center;width:100%"><a href="https://order.inverser.pro/#email" style="color:#fff;text-decoration:none">Inverser – Powerful web-based technologies</a></div>
</div>
</div>
';
if(!mail($to, 'Благодарим за обращение '.$date, $message, $headers)){
echo json_encode(['error'=>'Возникла ошибка... Попробуйте позже','ok'=>'false']);
exit;
}
exit;
}else{
header('HTTP/1.1 404 Not found');
header('location:/#err');
exit;
}