Написание движка для интерактивного обучения

This commit is contained in:
brusnitsyn
2026-07-15 00:17:17 +09:00
parent 082f2b8b01
commit e48d38f3a2
18 changed files with 1160 additions and 352 deletions

View File

@@ -0,0 +1,5 @@
import welcomeDuty from "./welcomeDuty"
export default {
welcomeDuty
}

View File

@@ -0,0 +1,163 @@
Туры. Они должны описывать:
- какие шаги есть;
- какой элемент подсвечивать;
- какие события ждать;
- какие действия выполнять до/после шага.
Например:
```
resources/js/Tours
├── index.js
├── welcomeDuty.js
└── report.js
```
```Tours/index.js```
```js
import welcomeDuty from "./welcomeDuty"
import report from "./report"
export default {
welcomeDuty,
report
}
```
Пример простого тура
```Tours/welcomeDuty.js```
```js
export default {
id: "welcomeDuty",
name: "Первое знакомство с дежурством",
steps: [
{
id: "department",
target: "#modal-select-user-department",
title: "Выберите отделение",
description: "Начните вводить название отделения и выберите его из списка.",
placement: "right",
highlight: true,
scroll: true,
allowInteraction: true,
nextEvent: "department:selected"
},
{
id: "doctor",
target: "#modal-select-user-doctor",
title: "Выберите врача",
description: "Теперь выберите ответственного врача.",
placement: "bottom",
highlight: true,
allowInteraction: true,
nextEvent: "doctor:selected"
},
{
id: "finish",
target: "#duty-create-button",
title: "Создание дежурства",
description: "После заполнения всех полей нажмите кнопку создания.",
placement: "top",
highlight: true,
allowInteraction: true
}
]
}
```
Пример с переходом между страницами
Например, первый шаг в модалке, второй на странице отчёта:
```Tours/report.js```
```js
export default {
id: "report",
name: "Создание отчёта",
steps: [
{
id: "open-report",
route: "reports.create",
target: "#report-date",
title: "Дата отчёта",
description: "Выберите период формирования отчёта.",
placement: "bottom",
scroll: true,
highlight: true
},
{
id: "department-filter",
target: "#department-filter",
title: "Фильтр отделения",
description: "Можно ограничить отчёт конкретным отделением.",
placement: "right",
nextEvent: "filter:selected"
},
{
id: "export",
target: "#export-report",
title: "Экспорт",
description: "Нажмите кнопку для выгрузки отчёта.",
placement: "left"
}
]
}
```
```Шаг с логикой```
Например, перед открытием модалки:
```js
{
id: "open-user-modal",
title: "Добавление пользователя",
description: "Сначала откройте форму.",
beforeEnter() {
userModal.value = true
},
target: "#user-modal"
}
```
```Шаг с проверкой перед переходом```
Например форма:
```js
{
id: "save-user",
target: "#save-user-button",
title: "Сохранение",
description: "Заполните обязательные поля.",
beforeNext() {
return form.validate()
}
}
```
```Шаг с ручным управлением```
Например поле нельзя пропустить:
```js
{
id: "confirm",
target: "#confirm-checkbox",
title: "Подтвердите",
description: "Необходимо поставить галочку.",
manual: true,
nextEvent: "confirmed"
}
```
И компонент:
```js
onboarding.emit(
"confirmed"
)
```
переведёт дальше.

View File

@@ -0,0 +1,85 @@
export default {
id: 'welcome-duty',
name: 'Знакомство с Метрикой',
steps: [
{
id: 'wellcome',
target: '#user-panel',
route: 'start', // главная страница
title: '👋 Добро пожаловать!',
description: 'Это ваша панель управления. Сейчас мы покажем вам основные возможности.',
popper: { placement: 'bottom-start' }, // опционально: позиция тултипа
on: {
afterStep: () => {
const modalStore = useModalStore()
modalStore.showSelectDutyUserModal = false
},
beforeStep: () => {
const modalStore = useModalStore()
modalStore.showSelectDutyUserModal = false
},
}
},
{
attachTo: { element: '#new-report-duty' },
route: 'start', // переходим на страницу отчётов
content: {
title: '✏️ Создать отчёт',
description: 'Нажмите эту кнопку, чтобы начать создание нового отчёта.'
},
on: {
afterStep: () => {
const modalStore = useModalStore()
const onboarding = useOnboardingStore()
if (onboarding.activeStepIndex === 2) modalStore.showSelectDutyUserModal = true
if (onboarding.activeStepIndex < 2) modalStore.showSelectDutyUserModal = false
},
beforeStep: () => {
const modalStore = useModalStore()
const onboarding = useOnboardingStore()
if (onboarding.activeStepIndex < 2) modalStore.showSelectDutyUserModal = false
},
},
},
{
attachTo: { element: '#modal-select-user-department' },
route: 'start',
content: { title: 'Выберите отделение', description: 'Можете использовать список, либо ввести ФИО для поиска' },
insideModal: true, // флаг, что этот шаг внутри модалки
options: {
disableInteraction: false
}
},
{
attachTo: { element: '#modal-select-user-doctor' },
route: 'start',
content: { title: 'Введите ответственного', description: 'Можете использовать список, либо ввести ФИО для поиска' },
insideModal: true, // флаг, что этот шаг внутри модалки
},
{
attachTo: { element: '#modal-select-user-submit' },
route: 'start',
content: { title: 'Подтвердите ввод' },
insideModal: true, // флаг, что этот шаг внутри модалки
manual: true
},
{
attachTo: { element: '#report-name-input' },
route: 'report', // страница создания отчёта
content: {
title: '📝 Название отчёта',
description: 'Введите название, чтобы легко находить отчёт позже.'
}
},
{
attachTo: { element: '#save-report-btn' },
route: 'reports.create',
content: {
title: '💾 Сохранить',
description: 'После заполнения всех полей нажмите "Сохранить", чтобы завершить.'
}
}
]
}