Работа над интерактивным обучением для дежурного и мед. сестры

This commit is contained in:
brusnitsyn
2026-07-15 16:53:38 +09:00
parent e48d38f3a2
commit d9cb1aca28
12 changed files with 377 additions and 209 deletions

View File

@@ -135,7 +135,11 @@ function delay(ms) {
<template>
<Teleport to="body">
<TourOverlay
v-if="onboarding.active && target"
v-if="onboarding.active &&
target &&
onboarding.currentStep?.highlight !== false &&
rect"
:allow-interaction="onboarding.currentStep?.allowInteraction ?? true"
:rect="rect"
:padding="onboarding.currentStep.padding"
/>
@@ -148,7 +152,7 @@ function delay(ms) {
:visible="onboarding.active"
:is-first="onboarding.stepIndex === 0"
:is-last="onboarding.stepIndex === onboarding.tour.steps.length - 1"
:is-last="onboarding.stepIndex === onboarding.tour?.steps.length - 1"
@next="onboarding.next"
@previous="onboarding.previous"
@@ -156,4 +160,4 @@ function delay(ms) {
@close="onboarding.finish"
/>
</Teleport>
</template>
</template>

View File

@@ -5,16 +5,50 @@ const props = defineProps({
rect: {
type: Object,
required: true
},
allowInteraction: {
type: Boolean,
default: true
}
})
const style = computed(() => {
const r = props.rect
const overlayStyle = computed(() => {
if (!props.rect) return null;
const { top, left, width, height } = props.rect
return {
"--top": `${r.top}px`,
"--left": `${r.left}px`,
"--width": `${r.width}px`,
"--height": `${r.height}px`
top: {
top: 0,
left: 0,
width: '100vw',
height: `${top}px`
},
left: {
top: `${top}px`,
left: 0,
width: `${left}px`,
height: `${height}px`
},
right: {
top: `${top}px`,
left: `${left+width}px`,
width: `calc(100vw - ${left+width}px)`,
height: `${height}px`
},
bottom: {
top: `${top+height}px`,
left: 0,
width: "100vw",
height: `calc(100vh - ${top+height}px)`
},
highlight: {
top: `${top}px`,
left: `${left}px`,
width: `${width}px`,
height: `${height}px`
},
}
})
</script>
@@ -24,10 +58,32 @@ const style = computed(() => {
<Teleport to="body">
<Transition name="tour-fade">
<div
class="tour-overlay"
:style="style"
/>
<div v-if="rect && overlayStyle">
<div
class="tour-mask"
:style="overlayStyle.top"
/>
<div
class="tour-mask"
:style="overlayStyle.left"
/>
<div
class="tour-mask"
:style="overlayStyle.right"
/>
<div
class="tour-mask"
:style="overlayStyle.bottom"
/>
<div
class="tour-highlight"
:style="overlayStyle.highlight"
/>
<div
v-if="!allowInteraction"
class="tour-blocker"
/>
</div>
</Transition>
</Teleport>
@@ -35,28 +91,27 @@ const style = computed(() => {
<style scoped>
.tour-mask {
position: fixed;
z-index: 9998;
background: rgba(0,0,0,.55);
pointer-events: auto;
}
.tour-overlay {
.tour-blocker {
position: fixed;
inset: 0;
z-index: 9998;
pointer-events: none;
background: rgba(0,0,0,.55);
background: transparent;
pointer-events: auto;
}
/*
вырезаем окно
*/
mask:
linear-gradient(#000 0 0),
linear-gradient(#000 0 0);
mask-size:
100% 100%,
var(--width) var(--height);
mask-position:
0 0,
var(--left) var(--top);
mask-repeat:no-repeat;
mask-composite: exclude;
.tour-highlight {
position: fixed;
z-index: 9998;
border: 2px solid white;
border-radius: 8px;
pointer-events: none;
}
.tour-fade-enter-active,
@@ -68,4 +123,4 @@ const style = computed(() => {
.tour-fade-leave-to {
opacity: 0;
}
</style>
</style>

View File

@@ -23,6 +23,10 @@ const props = defineProps({
type: [HTMLElement, String, Object],
default: null
},
disabledControllers: {
type: Boolean,
default: false
}
})
const emit = defineEmits([
@@ -53,83 +57,83 @@ const style = computed(() => ({
</script>
<template>
<Transition name="tour-tooltip">
<div
ref="floating"
v-if="visible && step"
class="tour-tooltip"
:style="style"
<Transition name="tour-tooltip">
<div
ref="floating"
v-if="visible && step"
class="tour-tooltip"
:style="style"
>
<NCard
style="width:320px"
size="small"
>
<NCard
style="width:320px"
size="small"
>
<template #header>
<div class="flex justify-between items-center">
<span class="font-semibold">
{{ step.title }}
</span>
<template #header>
<div class="flex justify-between items-center">
<span class="font-semibold">
{{ step.title }}
</span>
<NTag
v-if="step.tag"
size="small"
>
{{ step.tag }}
</NTag>
</div>
</template>
<div class="text-sm whitespace-pre-line">
{{ step.description }}
<NTag
v-if="step.tag"
size="small"
>
{{ step.tag }}
</NTag>
</div>
</template>
<slot />
<div class="text-sm whitespace-pre-line">
{{ step.description }}
</div>
<template #footer>
<NSpace justify="space-between">
<div>
<NButton
quaternary
size="small"
@click="emit('close')"
>
Пропустить
</NButton>
</div>
<slot />
<NSpace>
<NButton
v-if="!isFirst"
size="small"
@click="emit('previous')"
>
Назад
</NButton>
<template #footer>
<NSpace justify="space-between">
<div>
<NButton
quaternary
size="small"
@click="emit('close')"
>
Пропустить
</NButton>
</div>
<NButton
v-if="!isLast"
type="primary"
size="small"
:disabled="step.manual"
@click="emit('next')"
>
Далее
</NButton>
<NSpace>
<NButton
v-if="!isFirst"
size="small"
@click="emit('previous')"
>
Назад
</NButton>
<NButton
v-else
type="primary"
size="small"
@click="emit('finish')"
>
Завершить
</NButton>
</NSpace>
<NButton
v-if="!isLast"
type="primary"
size="small"
:disabled="step.manual"
@click="emit('next')"
>
Далее
</NButton>
<NButton
v-else
type="primary"
size="small"
@click="emit('finish')"
>
Завершить
</NButton>
</NSpace>
</template>
</NCard>
</div>
</Transition>
</NSpace>
</template>
</NCard>
</div>
</Transition>
</template>
<style scoped>
@@ -151,4 +155,4 @@ const style = computed(() => ({
transform:translateY(6px);
}
</style>
</style>