Если Вам необходима система опроса пользователя, построенная на прогрессивном фреймврке 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;
}

 

Компонент опроса для VueJS

Файлы Компонент опроса для VueJS:

Скачать Компонент опроса для VueJS
DEMO