first commit

This commit is contained in:
lingxiao865
2026-02-10 08:05:03 +08:00
commit c5af079d8c
1094 changed files with 97530 additions and 0 deletions

View File

@@ -0,0 +1,544 @@
<template>
<view>
<t-overlay
v-if="modeType === 'popover'"
:visible="visible"
:using-custom-navbar="usingCustomNavbar"
:custom-navbar-height="customNavbarHeight"
background-color="transparent"
:z-index="zIndex"
>
<view :class="tClass + ' ' + classPrefix">
<view
:class="tClassReference + ' ' + classPrefix + '__reference ' + (nonOverlay ? classPrefix + '__reference--nonoverlay' : '')"
:style="referenceStyle"
/>
<view
:class="tClassPopover + ' ' + classPrefix + '__container ' + (title || body ? classPrefix + '__container--' + modeType : '')"
:style="popoverStyle"
>
<ContentComp
:title="title"
:body="body"
:current="_current"
:class-prefix="classPrefix"
:prefix="prefix"
:skip-button="skipButton"
:back-button="backButton"
:next-button="nextButton"
:finish-button="finishButton"
:mode-type="modeType"
:hide-skip="hideSkip"
:hide-back="hideBack"
:steps="steps"
:t-class-tooltip="tClassTooltip"
:t-class-body="tClassBody"
:t-class-title="tClassTitle"
:t-class-footer="tClassFooter"
@onTplButtonTap="onTplButtonTap"
>
<template #content-0>
<slot name="content-0" />
</template>
<template #content-1>
<slot name="content-1" />
</template>
<template #content-2>
<slot name="content-2" />
</template>
<template #content-3>
<slot name="content-3" />
</template>
<template #content-4>
<slot name="content-4" />
</template>
<template #content-5>
<slot name="content-5" />
</template>
<template #content-6>
<slot name="content-6" />
</template>
<template #title-0>
<slot name="title-0" />
</template>
<template #title-1>
<slot name="title-1" />
</template>
<template #title-2>
<slot name="title-2" />
</template>
<template #title-3>
<slot name="title-3" />
</template>
<template #title-4>
<slot name="title-4" />
</template>
<template #title-5>
<slot name="title-5" />
</template>
<template #title-6>
<slot name="title-6" />
</template>
<template #body-0>
<slot name="body-0" />
</template>
<template #body-1>
<slot name="body-1" />
</template>
<template #body-2>
<slot name="body-2" />
</template>
<template #body-3>
<slot name="body-3" />
</template>
<template #body-4>
<slot name="body-4" />
</template>
<template #body-5>
<slot name="body-5" />
</template>
<template #body-6>
<slot name="body-6" />
</template>
</ContentComp>
</view>
</view>
</t-overlay>
<t-popup
v-else-if="modeType === 'dialog'"
:visible="visible"
:show-overlay="!nonOverlay"
:using-custom-navbar="usingCustomNavbar"
:custom-navbar-height="customNavbarHeight"
:z-index="zIndex"
placement="center"
>
<view :class="tClass + ' ' + classPrefix">
<view :class="tClassPopover + ' ' + classPrefix + '__container ' + (title || body ? classPrefix + '__container--' + modeType : '')">
<ContentComp
:title="title"
:body="body"
:current="_current"
:class-prefix="classPrefix"
:prefix="prefix"
:skip-button="skipButton"
:back-button="backButton"
:next-button="nextButton"
:finish-button="finishButton"
:mode-type="modeType"
:hide-skip="hideSkip"
:hide-back="hideBack"
:steps="steps"
:t-class-tooltip="tClassTooltip"
:t-class-body="tClassBody"
:t-class-title="tClassTitle"
:t-class-footer="tClassFooter"
@onTplButtonTap="onTplButtonTap"
>
<template #content-0>
<slot name="content-0" />
</template>
<template #content-1>
<slot name="content-1" />
</template>
<template #content-2>
<slot name="content-2" />
</template>
<template #content-3>
<slot name="content-3" />
</template>
<template #content-4>
<slot name="content-4" />
</template>
<template #content-5>
<slot name="content-5" />
</template>
<template #content-6>
<slot name="content-6" />
</template>
<template #title-0>
<slot name="title-0" />
</template>
<template #title-1>
<slot name="title-1" />
</template>
<template #title-2>
<slot name="title-2" />
</template>
<template #title-3>
<slot name="title-3" />
</template>
<template #title-4>
<slot name="title-4" />
</template>
<template #title-5>
<slot name="title-5" />
</template>
<template #title-6>
<slot name="title-6" />
</template>
<template #body-0>
<slot name="body-0" />
</template>
<template #body-1>
<slot name="body-1" />
</template>
<template #body-2>
<slot name="body-2" />
</template>
<template #body-3>
<slot name="body-3" />
</template>
<template #body-4>
<slot name="body-4" />
</template>
<template #body-5>
<slot name="body-5" />
</template>
<template #body-6>
<slot name="body-6" />
</template>
</ContentComp>
</view>
</view>
</t-popup>
</view>
</template>
<script>
import TOverlay from '../overlay/overlay';
import TButton from '../button/button';
import TPopup from '../popup/popup';
import { uniComponent } from '../common/src/index';
import props from './props';
import { prefix } from '../common/config';
import { isFunction, isNumeric } from '../common/validator';
import { debounce, getRect, rpx2px, styles, unitConvert, nextTick, systemInfo, coalesce } from '../common/utils';
import ContentComp from './content.vue';
import useCustomNavbar from '../mixins/using-custom-navbar';
const name = `${prefix}-guide`;
let that;
export default uniComponent({
name,
options: {
styleIsolation: 'shared',
},
controlledProps: [
{
key: 'current',
event: 'change',
},
],
externalClasses: [
`${prefix}-class`,
`${prefix}-class-reference`,
`${prefix}-class-popover`,
`${prefix}-class-tooltip`,
`${prefix}-class-title`,
`${prefix}-class-body`,
`${prefix}-class-footer`,
`${prefix}-class-skip`,
`${prefix}-class-next`,
`${prefix}-class-back`,
`${prefix}-class-finish`,
],
mixins: [
useCustomNavbar,
],
components: {
TOverlay,
TButton,
TPopup,
ContentComp,
},
props: {
...props,
},
data() {
return {
prefix,
classPrefix: name,
visible: false,
_current: -1,
_steps: [],
referenceStyle: '',
popoverStyle: '',
title: '',
body: '',
nonOverlay: false,
modeType: '',
skipButton: {},
backButton: {},
nextButton: {},
finishButton: {},
};
},
watch: {
steps: {
handler() {
this._init();
},
deep: true,
},
current: {
handler(v) {
this._current = v;
},
},
_current: '_init',
showOverlay: '_init',
},
created() {
that = this;
// this._init =
this._getPlacement = this.getPlacement();
},
mounted() {
this._init();
},
methods: {
_init: debounce(() => that.init(), 20),
async init() {
console.log('doing init');
const { steps } = this;
const { _current } = this;
const step = steps[_current];
if (!step) {
this.visible = false;
return;
}
const modeType = (coalesce(step.mode, this.mode)) === 'dialog' ? 'dialog' : 'popover';
const showOverlay = coalesce(step.showOverlay, this.showOverlay);
this.nonOverlay = !showOverlay;
this.modeType = modeType;
// if (current === _current) return;
if (modeType === 'popover') {
const rect = await step.element();
console.log('rect', rect);
if (!rect) return;
const highlightPadding = rpx2px(coalesce(step.highlightPadding, this.highlightPadding));
const referenceTop = rect.top - highlightPadding;
const referenceRight = systemInfo.windowWidth - rect.right - highlightPadding;
const referenceLeft = rect.left - highlightPadding;
const referenceWidth = rect.width + 2 * highlightPadding;
const referenceHeight = rect.height + 2 * highlightPadding;
const style = {
top: `${referenceTop}px`,
right: `${referenceRight}px`,
left: `${referenceLeft}px`,
width: `${referenceWidth}px`,
height: `${referenceHeight}px`,
};
this._steps = this.steps;
// this._current = this.current;
this.visible = true;
this.referenceStyle = styles(style);
this.title = coalesce(step.title, '');
this.body = coalesce(step.body, '');
this.makeButtonProps(step, 'popover');
const popoverStyle = await this.placementOffset(step, style);
this.popoverStyle = popoverStyle;
} else {
this._steps = this.steps;
// this._current = this.current;
this.visible = true;
this.title = coalesce(step.title, '');
this.body = coalesce(step.body, '');
this.makeButtonProps(step, 'dialog');
}
},
async placementOffset({ placement, offset }, place) {
await nextTick();
const rect = await getRect(this, `.${name}__container`);
const style = this._getPlacement[placement]?.(rect, place, offset);
return styles({ position: 'absolute', ...style });
},
makeButtonProps(step, mode) {
const {
tClassSkip,
tClassNext,
tClassBack,
tClassFinish,
} = this;
let skipButton = coalesce(step.skipButtonProps, this.skipButtonProps);
const size = mode === 'popover' ? 'extra-small' : 'medium';
skipButton = {
theme: 'light',
content: '跳过',
size,
...skipButton,
tClass: `${tClassSkip} ${name}__button ${skipButton?.class || ''}`,
type: 'skip',
};
let nextButton = coalesce(step.nextButtonProps, this.nextButtonProps);
nextButton = {
theme: 'primary',
content: '下一步',
size,
...nextButton,
tClass: `${tClassNext} ${name}__button ${nextButton?.class || ''}`,
type: 'next',
};
nextButton = { ...nextButton, content: this.buttonContent(nextButton) };
let backButton = coalesce(step.backButtonProps, this.backButtonProps);
backButton = {
theme: 'light',
content: '返回',
size,
...backButton,
tClass: `${tClassBack} ${name}__button ${backButton?.class || ''}`,
type: 'back',
};
let finishButton = coalesce(step.finishButtonProps, this.finishButtonProps);
finishButton = {
theme: 'primary',
content: '完成',
size,
...finishButton,
tClass: `${tClassFinish} ${name}__button ${finishButton?.class || ''}`,
type: 'finish',
};
finishButton = { ...finishButton, content: this.buttonContent(finishButton) };
this.skipButton = skipButton;
this.nextButton = nextButton;
this.backButton = backButton;
this.finishButton = finishButton;
},
renderCounter() {
const { steps, _current, counter } = this;
const stepsTotal = steps.length;
const innerCurrent = _current + 1;
const popupSlotCounter = isFunction(counter) ? counter({ total: stepsTotal, current: innerCurrent }) : counter;
return counter ? popupSlotCounter : `(${innerCurrent}/${stepsTotal})`;
},
buttonContent(button) {
const { hideCounter } = this;
return `${button.content.replace(/ \(.*?\)/, '')} ${hideCounter ? '' : this.renderCounter()}`;
},
onTplButtonTap(e, { type }) {
console.log('onTplButtonTap.type', type);
const params = { e, current: this._current, total: this.steps.length };
switch (type) {
case 'next':
this.$emit('next-step-click', { next: this._current + 1, ...params });
// this.setData({ current: this..current + 1 });
this._current = this._current + 1;
break;
case 'skip':
this.$emit('skip', params);
// this.setData({ current: -1 });
this._current = -1;
break;
case 'back':
this.$emit('back', params);
// this.setData({ current: 0 });
this._current = 0;
break;
case 'finish':
this.$emit('finish', params);
// this.setData({ current: -1 });
this._current = -1;
break;
default:
break;
}
console.log('_current', this._current);
this.$emit('change', { current: this._current });
},
getPlacement() {
const space = rpx2px(32);
const offsetLeft = offset => unitConvert(isNumeric(offset?.[0]) ? `${offset?.[0]}rpx` : offset?.[0] || 0);
const offsetTop = offset => unitConvert(isNumeric(offset?.[1]) ? `${offset?.[1]}rpx` : offset?.[1] || 0);
const left = place => parseFloat(place.left);
const right = place => parseFloat(place.right);
const top = place => parseFloat(place.top);
const height = place => parseFloat(place.height);
const width = place => parseFloat(place.width);
return {
center: (rect, place, offset) => ({
top: `${Math.max(height(place) + top(place) + space + offsetTop(offset), 1)}px`,
left: `${Math.max(width(place) / 2 + left(place) - rect.width / 2 + offsetLeft(offset), 1)}px`,
}),
bottom: (rect, place, offset) => ({
top: `${Math.max(height(place) + top(place) + space + offsetTop(offset), 1)}px`,
left: `${Math.max(width(place) / 2 + left(place) - rect.width / 2 + offsetLeft(offset), 1)}px`,
}),
'bottom-left': (rect, place, offset) => ({
top: `${Math.max(height(place) + top(place) + space + offsetTop(offset), 1)}px`,
left: `${Math.max(left(place) + offsetLeft(offset), 1)}px`,
}),
'bottom-right': (rect, place, offset) => ({
top: `${Math.max(height(place) + top(place) + space + offsetTop(offset), 1)}px`,
right: `${Math.max(right(place) - offsetLeft(offset), 1)}px`,
}),
left: (rect, place, offset) => ({
top: `${Math.max(height(place) / 2 + top(place) - rect.height / 2 + offsetTop(offset), 1)}px`,
right: `${Math.max(width(place) + right(place) + space - offsetLeft(offset), 1)}px`,
}),
'left-top': (rect, place, offset) => ({
top: `${Math.max(top(place) + offsetTop(offset), 1)}px`,
right: `${Math.max(width(place) + right(place) + space - offsetLeft(offset), 1)}px`,
}),
'left-bottom': (rect, place, offset) => ({
top: `${Math.max(top(place) + height(place) - rect.height - offsetTop(offset), 1)}px`,
right: `${Math.max(width(place) + right(place) + space - offsetLeft(offset), 1)}px`,
}),
right: (rect, place, offset) => ({
top: `${Math.max(height(place) / 2 + top(place) - rect.height / 2 + offsetTop(offset), 1)}px`,
left: `${Math.max(left(place) + width(place) + space + offsetLeft(offset), 1)}px`,
}),
'right-top': (rect, place, offset) => ({
top: `${Math.max(top(place) + offsetTop(offset), 1)}px`,
left: `${Math.max(left(place) + width(place) + space + offsetLeft(offset), 1)}px`,
}),
'right-bottom': (rect, place, offset) => ({
top: `${Math.max(top(place) + height(place) - rect.height - offsetTop(offset), 1)}px`,
left: `${Math.max(left(place) + width(place) + space + offsetLeft(offset), 1)}px`,
}),
top: (rect, place, offset) => ({
top: `${Math.max(top(place) - rect.height - space + offsetTop(offset), 1)}px`,
left: `${Math.max(width(place) / 2 + left(place) - rect.width / 2 + offsetLeft(offset), 1)}px`,
}),
'top-left': (rect, place, offset) => ({
top: `${Math.max(top(place) - rect.height - space + offsetTop(offset), 1)}px`,
left: `${Math.max(left(place) + offsetLeft(offset), 1)}px`,
}),
'top-right': (rect, place, offset) => ({
top: `${Math.max(top(place) - rect.height - space + offsetTop(offset), 1)}px`,
right: `${Math.max(right(place) - offsetLeft(offset), 1)}px`,
}),
};
},
},
});
</script>
<style scoped>
@import './guide.css';
</style>
<style scoped lang="less">
.t-guide__footer--dialog {
// 适配 QQ 小程序等
display: inline-flex;
.t-button {
flex-grow: 1;
}
}
</style>