<template>
    <div
        ref="boxRef"
        class="box"
        @scroll.passive="handleScroll"
    >
        <div v-loading="loading">
            <div
                v-for="(item, index) in list"
                :key="index"
                class="html-item"
            >
                <h2>标题：{{ item.title }}</h2>
                <div
                    v-html="item.html"
                    @click="handleClickHtml($event, item)"
                ></div>
                <el-divider />
            </div>
        </div>
    </div>
</template>

<script setup lang="jsx">
import { ref, onMounted, onBeforeUnmount, nextTick } from 'vue';
import { debounce } from '@pureadmin/utils';

const boxRef = ref(null);
const list = ref([]);
const loading = ref(false);
const videoAddEventListeners = ref([]);

/**
 * 将富文本内的所有点击链接，点击图片，点击视频，点击按钮等绑定埋点事件
 */
const html = `<a href='https://www.baidu.com' style='text-decoration: underline;'>点击链接</a><br /><br/>
<img src='https://www.baidu.com/img/flexible/logo/pc/result.png' /><br /><br/>
<button style='background-color: #4CAF50; border: none; color: white; padding: 10px 20px; text-align: center; text-decoration: none; display: inline-block; font-size: 16px; margin: 4px 2px; cursor: pointer;'>按钮</button>
<video src="https://www.w3school.com.cn/i/movie.ogg" controls="controls"></video>
<video controls="controls"><source src="https://www.w3school.com.cn/i/movie.mp4" type="video/ogg"></video>
<a href='https://www.baidu.com' style='text-decoration: underline;'><span>有内容标签的链接</span></a><br /><br/>
<a href='https://www.baidu.com' style='text-decoration: underline;'><img alt='可以跳转的图片' src='https://www.baidu.com/img/flexible/logo/pc/result.png' /></a><br /><br/>
`;

const getHerfAttribute = (dom) => {
    return dom.getAttribute('href') || dom.href || '';
};

// 通过v-html渲染的富文本，模拟事件委托
const handleClickHtml = async (event, item) => {
    const currentDom = event.target;
    const parentDom = currentDom.parentNode;
    let currentDomHref = getHerfAttribute(currentDom);
    let parentHref = getHerfAttribute(parentDom);

    // 跳转链接
    let jumpLink = '';
    // 埋点参数
    let params = {};
    switch (currentDom.tagName) {
        case 'A':
            event.preventDefault();
            jumpLink = currentDomHref;
            params.link = currentDomHref;
            params.type = 'link';
            break;
        case 'IMG':
            if (parentDom.tagName === 'A') {
                event.preventDefault();
                jumpLink = parentHref;
                params.link = parentHref;
            }
            params.type = 'image';
            params.src = currentDom.getAttribute('src') || currentDom.src;
            break;
        case 'BUTTON':
            params.type = 'button';
            params.text = currentDom.textContent;
            break;
        default:
            if (parentDom.tagName === 'A') {
                event.preventDefault();
                jumpLink = parentHref;
                params.link = parentHref;
                params.type = 'link';
            }
            break;
    }

    // 有需要埋点的事件
    if (params.type) {
        await new Promise((resolve) => {
            params.title = item.title;
            setTimeout(() => {
                console.log(`触发${params.type}埋点事件`);
                console.table(params);
                resolve();
            }, 500);
        });
        // 跳转链接
        if (jumpLink) {
            console.log(`跳转链接：${jumpLink}`);
            // window.location.href = jumpLink;
        }
    }
};

const videoBuryingPoint = (type, src, title) => {
    console.log(`视频${type}事件，视频地址：${src}, 视频所属标题：${title}`);
};

const videoAddEventListener = () => {
    let videos = boxRef.value.querySelectorAll('video');
    for (let i = 0; i < videos.length; i++) {
        const video = videos[i];
        // 防止重复绑定事件
        if (!video.dataset.listenerAdded) {
            //video 属性上有src，则直接通过video.src获取视频地址
            const src = video.src;
            //video 内有source标签，则只能通过获取source标签的src来获取视频地址
            const sources = video.getElementsByTagName('source');
            //如果有多个source标签，则取第一个source标签的src
            const sourceSrc = sources.length ? sources[0].src : '';
            // 查找最近的祖先节点，直到找到具有 'html-item' 类名的元素
            const ancestorWithHtmlItemClass = video.closest('.html-item');
            const title = ancestorWithHtmlItemClass.querySelector('h2').textContent;
            const playHandler = () => videoBuryingPoint('play', src || sourceSrc, title);
            const pauseHandler = () => videoBuryingPoint('pause', src || sourceSrc, title);
            const endedHandler = () => videoBuryingPoint('ended', src || sourceSrc, title);
            const volumeChangeHandler = () => videoBuryingPoint('volumechange', src || sourceSrc, title);
            video.addEventListener('play', playHandler);
            video.addEventListener('pause', pauseHandler);
            video.addEventListener('ended', endedHandler);
            video.addEventListener('volumechange', volumeChangeHandler);

            // 存储事件处理函数以便后续移除
            videoAddEventListeners.value.push({
                video,
                playHandler,
                pauseHandler,
                endedHandler,
                volumeChangeHandler,
            });
            video.dataset.listenerAdded = true;
        }
    }
};

const videoRemoveEventListener = () => {
    videoAddEventListeners.value.forEach((item) => {
        const { video, playHandler, pauseHandler, endedHandler, volumeChangeHandler } = item;
        video.removeEventListener('play', playHandler);
        video.removeEventListener('pause', pauseHandler);
        video.removeEventListener('ended', endedHandler);
        video.removeEventListener('volumechange', volumeChangeHandler);
    });
    videoAddEventListeners.value = [];
};

const getData = () => {
    loading.value = true;
    console.log('加载数据');
    setTimeout(() => {
        loading.value = false;
        let tempList = [];
        let currentLength = list.value.length;
        for (let i = 0; i < 5; i++) {
            tempList.push({
                title: `模拟数据${i + currentLength}`,
                html,
            });
        }
        list.value = list.value.concat(tempList);
        nextTick(() => {
            videoAddEventListener();
        });
    }, 2000);
};

const scrollLoadData = () => {
    console.log('监听滚动事件');
    /**
     * scrollTop：滚动元素的顶部到可视区域顶部的距离。
     * clientHeight：可视区域的高度。
     * scrollHeight：内容的总高度。
     */
    const { scrollTop, clientHeight, scrollHeight } = boxRef.value;
    console.log(scrollTop, clientHeight, scrollHeight);
    if (scrollTop + clientHeight >= scrollHeight - 20) {
        console.log('距离底部还有20px, 加载数据');
        getData();
    }
};

const handleScroll = debounce(scrollLoadData, 500);

onMounted(() => {
    getData();
});

onBeforeUnmount(() => {
    videoRemoveEventListener();
});
</script>

<style lang="scss" scoped>
.box {
    display: flex;
    width: 100%;
    flex-direction: column;
    align-items: center;
    background: #fff;
    // 要监听滚动事件，必须设置高度，并且设置滚动为auto，否则监听事件不生效
    height: calc(100vh - 81px);
    overflow-y: auto;

    & > div {
        width: 100%;
        padding: 10px 20px;
    }
}
</style>
