|
@@ -1,7 +1,8 @@
|
|
|
<template>
|
|
<template>
|
|
|
<div :class="classes">
|
|
<div :class="classes">
|
|
|
- <div class="nut-tour-masked" v-if="showTour"></div>
|
|
|
|
|
- <div v-for="(step, i) in steps" :key="i">
|
|
|
|
|
|
|
+ <div class="nut-tour-masked" v-show="showTour" @click="handleClickMask"></div>
|
|
|
|
|
+
|
|
|
|
|
+ <div v-for="(step, i) in steps" :key="i" style="height: 0">
|
|
|
<template v-if="i == active">
|
|
<template v-if="i == active">
|
|
|
<div
|
|
<div
|
|
|
class="nut-tour-mask"
|
|
class="nut-tour-mask"
|
|
@@ -17,41 +18,45 @@
|
|
|
:bgColor="bgColor"
|
|
:bgColor="bgColor"
|
|
|
:theme="theme"
|
|
:theme="theme"
|
|
|
:close-on-click-outside="false"
|
|
:close-on-click-outside="false"
|
|
|
|
|
+ :offset="step.popoverOffset || [0, 12]"
|
|
|
|
|
+ :arrowOffset="step.arrowOffset || 0"
|
|
|
>
|
|
>
|
|
|
<template #content>
|
|
<template #content>
|
|
|
- <div class="nut-tour-content" v-if="type == 'step'">
|
|
|
|
|
- <div class="nut-tour-content-top">
|
|
|
|
|
- <div class="nut-tour-content-top-close" @click="close">
|
|
|
|
|
- <Close size="10" name="close" />
|
|
|
|
|
|
|
+ <slot>
|
|
|
|
|
+ <div class="nut-tour-content" v-if="type == 'step'">
|
|
|
|
|
+ <div class="nut-tour-content-top">
|
|
|
|
|
+ <div @click="close">
|
|
|
|
|
+ <Close class="nut-tour-content-top-close" />
|
|
|
|
|
+ </div>
|
|
|
</div>
|
|
</div>
|
|
|
- </div>
|
|
|
|
|
- <div class="nut-tour-content-inner">
|
|
|
|
|
- {{ step.content }}
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="nut-tour-content-bottom">
|
|
|
|
|
- <div class="nut-tour-content-bottom-init">{{ active + 1 }}/{{ steps.length }}</div>
|
|
|
|
|
- <div class="nut-tour-content-bottom-operate">
|
|
|
|
|
- <div class="nut-tour-content-bottom-operate-btn" @click="changeStep('prev')" v-if="active != 0">{{
|
|
|
|
|
- prevStepTxt
|
|
|
|
|
- }}</div>
|
|
|
|
|
- <div
|
|
|
|
|
- class="nut-tour-content-bottom-operate-btn active"
|
|
|
|
|
- @click="close"
|
|
|
|
|
- v-if="steps.length - 1 == active"
|
|
|
|
|
- >{{ completeTxt }}</div
|
|
|
|
|
- >
|
|
|
|
|
- <div class="nut-tour-content-bottom-operate-btn active" @click="changeStep('next')" v-else>{{
|
|
|
|
|
- nextStepTxt
|
|
|
|
|
- }}</div>
|
|
|
|
|
|
|
+ <div class="nut-tour-content-inner">
|
|
|
|
|
+ {{ step.content }}
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="nut-tour-content-bottom">
|
|
|
|
|
+ <div class="nut-tour-content-bottom-init">{{ active + 1 }}/{{ steps.length }}</div>
|
|
|
|
|
+ <div class="nut-tour-content-bottom-operate">
|
|
|
|
|
+ <div class="nut-tour-content-bottom-operate-btn" @click="changeStep('prev')" v-if="active != 0">{{
|
|
|
|
|
+ prevStepTxt
|
|
|
|
|
+ }}</div>
|
|
|
|
|
+ <div
|
|
|
|
|
+ class="nut-tour-content-bottom-operate-btn active"
|
|
|
|
|
+ @click="close"
|
|
|
|
|
+ v-if="steps.length - 1 == active"
|
|
|
|
|
+ >{{ completeTxt }}</div
|
|
|
|
|
+ >
|
|
|
|
|
+ <div class="nut-tour-content-bottom-operate-btn active" @click="changeStep('next')" v-else>{{
|
|
|
|
|
+ nextStepTxt
|
|
|
|
|
+ }}</div>
|
|
|
|
|
+ </div>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
- </div>
|
|
|
|
|
|
|
|
|
|
- <div class="nut-tour-content nut-tour-content-tile" v-if="type == 'tile'">
|
|
|
|
|
- <div class="nut-tour-content-inner">
|
|
|
|
|
- {{ step.content }}
|
|
|
|
|
|
|
+ <div class="nut-tour-content nut-tour-content-tile" v-if="type == 'tile'">
|
|
|
|
|
+ <div class="nut-tour-content-inner">
|
|
|
|
|
+ {{ step.content }}
|
|
|
|
|
+ </div>
|
|
|
</div>
|
|
</div>
|
|
|
- </div>
|
|
|
|
|
|
|
+ </slot>
|
|
|
</template>
|
|
</template>
|
|
|
</nut-popover>
|
|
</nut-popover>
|
|
|
</template>
|
|
</template>
|
|
@@ -68,7 +73,9 @@ import { Close } from '@nutui/icons-vue';
|
|
|
interface StepOptions {
|
|
interface StepOptions {
|
|
|
target: Element;
|
|
target: Element;
|
|
|
content: String;
|
|
content: String;
|
|
|
- location: PopoverLocation;
|
|
|
|
|
|
|
+ location?: PopoverLocation;
|
|
|
|
|
+ popoverOffset?: number[];
|
|
|
|
|
+ arrowOffset?: number;
|
|
|
}
|
|
}
|
|
|
const { create } = createComponent('tour');
|
|
const { create } = createComponent('tour');
|
|
|
export default create({
|
|
export default create({
|
|
@@ -120,6 +127,18 @@ export default create({
|
|
|
theme: {
|
|
theme: {
|
|
|
type: String,
|
|
type: String,
|
|
|
default: 'light'
|
|
default: 'light'
|
|
|
|
|
+ },
|
|
|
|
|
+ maskWidth: {
|
|
|
|
|
+ type: [Number, String],
|
|
|
|
|
+ default: ''
|
|
|
|
|
+ },
|
|
|
|
|
+ maskHeight: {
|
|
|
|
|
+ type: [Number, String],
|
|
|
|
|
+ default: ''
|
|
|
|
|
+ },
|
|
|
|
|
+ closeOnClickOverlay: {
|
|
|
|
|
+ type: Boolean,
|
|
|
|
|
+ default: true
|
|
|
}
|
|
}
|
|
|
},
|
|
},
|
|
|
emits: ['update:visible', 'change', 'close'],
|
|
emits: ['update:visible', 'change', 'close'],
|
|
@@ -130,7 +149,9 @@ export default create({
|
|
|
active: 0
|
|
active: 0
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
- const maskRect = ref<any>({});
|
|
|
|
|
|
|
+ const maskRect = ref<{
|
|
|
|
|
+ [props: string]: number;
|
|
|
|
|
+ }>({});
|
|
|
|
|
|
|
|
const classes = computed(() => {
|
|
const classes = computed(() => {
|
|
|
const prefixCls = 'nut-tour';
|
|
const prefixCls = 'nut-tour';
|
|
@@ -138,13 +159,18 @@ export default create({
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
const maskStyle = computed(() => {
|
|
const maskStyle = computed(() => {
|
|
|
- const { offset } = props;
|
|
|
|
|
|
|
+ const { offset, maskWidth, maskHeight } = props;
|
|
|
const { width, height, left, top } = maskRect.value;
|
|
const { width, height, left, top } = maskRect.value;
|
|
|
|
|
+
|
|
|
|
|
+ const center = [left + width / 2, top + height / 2]; // 中心点 【横,纵】
|
|
|
|
|
+ const w: number = Number(maskWidth ? maskWidth : width);
|
|
|
|
|
+ const h: number = Number(maskHeight ? maskHeight : height);
|
|
|
|
|
+
|
|
|
const styles = {
|
|
const styles = {
|
|
|
- width: `${width + offset[1] * 2}px`,
|
|
|
|
|
- height: `${height + offset[0] * 2}px`,
|
|
|
|
|
- top: `${top - offset[0]}px`,
|
|
|
|
|
- left: `${left - offset[1]}px`
|
|
|
|
|
|
|
+ width: `${w + +offset[1] * 2}px`,
|
|
|
|
|
+ height: `${h + +offset[0] * 2}px`,
|
|
|
|
|
+ top: `${center[1] - h / 2 - +offset[0]}px`,
|
|
|
|
|
+ left: `${center[0] - w / 2 - +offset[1]}px`
|
|
|
};
|
|
};
|
|
|
return styles;
|
|
return styles;
|
|
|
});
|
|
});
|
|
@@ -174,16 +200,20 @@ export default create({
|
|
|
const close = () => {
|
|
const close = () => {
|
|
|
state.showTour = false;
|
|
state.showTour = false;
|
|
|
state.showPopup = false;
|
|
state.showPopup = false;
|
|
|
|
|
+ emit('close', state.active);
|
|
|
emit('update:visible', false);
|
|
emit('update:visible', false);
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
|
|
+ const handleClickMask = () => {
|
|
|
|
|
+ props.closeOnClickOverlay && close();
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
watch(
|
|
watch(
|
|
|
() => props.visible,
|
|
() => props.visible,
|
|
|
(val) => {
|
|
(val) => {
|
|
|
state.showTour = val;
|
|
state.showTour = val;
|
|
|
-
|
|
|
|
|
|
|
+ state.showPopup = val;
|
|
|
if (val) {
|
|
if (val) {
|
|
|
- state.showPopup = true;
|
|
|
|
|
getRootPosition();
|
|
getRootPosition();
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
@@ -194,7 +224,8 @@ export default create({
|
|
|
classes,
|
|
classes,
|
|
|
maskStyle,
|
|
maskStyle,
|
|
|
changeStep,
|
|
changeStep,
|
|
|
- close
|
|
|
|
|
|
|
+ close,
|
|
|
|
|
+ handleClickMask
|
|
|
};
|
|
};
|
|
|
}
|
|
}
|
|
|
});
|
|
});
|