首页 > 其他分享 >vue快捷工具组件:小地图导航、全屏、刷新

vue快捷工具组件:小地图导航、全屏、刷新

时间:2024-11-22 18:09:02浏览次数:1  
标签:index vue const center 100% full 全屏 background 组件

vue快捷工具组件:小地图导航、全屏、刷新

效果

代码

<!--
* @description 快捷工具
!-->
<template>
    <div class="quick-tools" :style="{ ...quickToolsStyle }">
        <template v-for="val in tools">
            <tooltip v-if="val === 'full'" :key="val" :content="isFullScreen ? '退出全屏' : '全屏'" :is-update-popper="true" placement="top" effect="light" :visible-arrow="false">
                <div class="tool-item" @click="itemClick(val)">
                    <i class="icon" :class="`ic-${val} ${isFullScreen ? 'ic-full-exit' : ''}`"></i>
                </div>
            </tooltip>
            <tooltip v-if="val === 'refresh'" :key="val" content="刷新" placement="top" effect="light" :visible-arrow="false">
                <div class="tool-item" @click="itemClick(val)">
                    <i class="icon" :class="`ic-${val}`"></i>
                </div>
            </tooltip>
            <tooltip v-if="val === 'map'" :key="val" content="小地图" placement="top" effect="light" :visible-arrow="false">
                <div class="tool-item" :class="{ 'is-active': showMapPopover }" @click="itemClick(val)">
                    <i class="icon" :class="`ic-${val}`"></i>
                </div>
            </tooltip>
        </template>
        <div v-if="showMapPopover" ref="mapPopoverRef" class="map-popover">
            <div ref="mapContentRef" class="map-content">
                <div
                    v-for="(arr, index) in mapList"
                    :key="`map_list_${index}`"
                    class="map-box-out"
                    :style="{ width: `${arr.length * 10 + 6}px` }"
                    :class="{ 'is-active': currentMapBoxIndex === index }"
                    @click="mapBoxClick(arr, index)"
                >
                    <div class="map-box">
                        <div v-for="(child, childIndex) in arr" :key="`map_list_${index}_child_${childIndex}`" class="map-li" :style="{ height: `${child.value}%` }"></div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</template>

<script>
import Tooltip from '@/views/flow/components/tooltip.vue'
export default {
    name: 'QuickTools',
    components: { Tooltip },
    props: {
        tools: {
            type: Array,
            default: () => ['full', 'refresh', 'map']
        },
        // 地图导航,滚动的容器
        scrollContainerClass: {
            type: String,
            default: 'scroll-container-wp'
        },
        // 节点垂直滚动容器
        nodeScrollContainerClass: {
            type: String,
            default: 'content-box-wp'
        },
        // 节点滚动容器距离头部的高度
        nodeTopHeight: {
            type: Number,
            default: 90
        },
        quickToolsStyle: {
            type: Object,
            default: () => ({})
        },
        // 需要全屏的容器
        fullScreenContainerClass: {
            type: String,
            default: 'full-screen-container'
        },
        refreshFunc: {
            type: Function,
            default: null
        }
    },
    data() {
        return {
            showMapPopover: false,
            mapList: [],
            currentMapBoxIndex: null,
            nodeScrollContainerAll: [],
            isFullScreen: false
        }
    },
    watch: {
        showMapPopover(val) {
            if (val) {
                this.$nextTick(() => {
                    const mapPopoverWidth = this.$refs.mapPopoverRef.clientWidth
                    const mapContentWidth = this.$refs.mapContentRef.clientWidth
                    if (mapPopoverWidth > mapContentWidth) return
                    const ratio = (mapPopoverWidth - 16) / mapContentWidth
                    this.$refs.mapContentRef.style.transform = `scale(${ratio}, 1)`
                })
            }
        }
    },
    mounted() {
        document.addEventListener('click', this.clsoeMapPopover, true)
    },
    methods: {
        clsoeMapPopover(e) {
            if (this.$refs.mapPopoverRef && !this.$refs.mapPopoverRef.contains(e.target)) {
                this.showMapPopover = false
            }
        },
        itemClick(val) {
            switch (val) {
                case 'full':
                    this.isFullScreen = !this.isFullScreen
                    this.fullScreen()
                    break
                case 'refresh':
                    this.refreshFunc && this.refreshFunc()
                    break
                case 'map':
                    this.showMapPopover = !this.showMapPopover
                    this.getMapList()
                    break
                default:
                    break
            }
        },
        fullScreen() {
            const fullScreenContainer = document.querySelector(`.${this.fullScreenContainerClass}`)
            if (this.isFullScreen) {
                fullScreenContainer.classList.add('full-screen-container-style')
            } else {
                fullScreenContainer.classList.remove('full-screen-container-style')
            }
        },
        mapBoxClick(arr, index) {
            this.currentMapBoxIndex = index
            const scrollContainer = document.querySelector(`.${this.scrollContainerClass}`)
            const scrollContainerScrollWidth = scrollContainer.scrollWidth
            // 第一页
            if (index === 0) {
                scrollContainer.scrollTo(0, 0)
                return
            }
            // 最后一页
            if (index === this.mapList.length - 1) {
                scrollContainer.scrollTo(scrollContainerScrollWidth, 0)
                return
            }
            const ele = this.nodeScrollContainerAll[arr[0].index]
            const eleOffsetLeft = ele.offsetLeft
            const scrollContainerScrollLeft = scrollContainer.scrollLeft
            if (eleOffsetLeft === scrollContainerScrollLeft) {
                return
            }
            let left = eleOffsetLeft
            if (left === 16) {
                left = 0
            }
            scrollContainer.scrollTo({
                left: left
            })
        },
        getMapList() {
            if (!this.showMapPopover) return
            this.currentMapBoxIndex = null
            this.mapList = []
            const scrollContainer = document.querySelector(`.${this.scrollContainerClass}`)
            this.nodeScrollContainerAll = document.querySelectorAll(`.${this.nodeScrollContainerClass}`)
            const scrollContainerClientWidth = scrollContainer.clientWidth
            const pageSize = Math.floor(scrollContainerClientWidth / 266)
            const nodeTotal = this.nodeScrollContainerAll.length
            const pageTotal = Math.ceil(nodeTotal / pageSize)
            const noedeHeightArr = []
            this.nodeScrollContainerAll.forEach(item => {
                noedeHeightArr.push(item.scrollHeight + 90)
            })
            const nodeMaxHeight = Math.max(...noedeHeightArr)
            const noedeHeightPercentageArr = noedeHeightArr.map((val, index) => {
                return {
                    index: index,
                    value: (val / nodeMaxHeight) * 100
                }
            })
            for (let i = 0; i < pageTotal; i++) {
                const arr = this.paginateArray(pageSize, i + 1, noedeHeightPercentageArr)
                this.mapList.push(arr)
            }
        },
        paginateArray(pageSize, pageNumber, arr) {
            const startIndex = (pageNumber - 1) * pageSize
            const endIndex = Math.min(startIndex + pageSize, arr.length)
            return arr.slice(startIndex, endIndex)
        }
    }
}
</script>
<style lang="scss" scoped>
.quick-tools {
    position: absolute;
    display: flex;
    height: 32px;
    width: max-content;
    right: 32px;
    bottom: 32px;
    align-items: center;
    z-index: 99;
    .tool-item {
        display: flex;
        width: 32px;
        height: 32px;
        margin-left: 8px;
        justify-content: center;
        align-items: center;
        background: #ffffff;
        border-radius: 4px;
        border: 1px solid #f2f3f5;
        cursor: pointer;
        &.is-active {
            background: #e4e7ec;
        }
        &:first-child {
            margin-left: 0;
        }
        &:hover {
            background: #edf0f3;
        }
        .icon {
            display: inline-block;
            width: 16px;
            height: 16px;
            &.ic-full {
                background: url('~@/assets/images/flow/ic_full.png') no-repeat center center;
                background-size: 100% 100%;
                &.ic-full-exit {
                    background: url('~@/assets/images/flow/ic_full_exit.png') no-repeat center center;
                    background-size: 100% 100%;
                }
            }
            &.ic-refresh {
                background: url('~@/assets/images/flow/ic_refresh.png') no-repeat center center;
                background-size: 100% 100%;
            }
            &.ic-map {
                background: url('~@/assets/images/flow/ic_map.png') no-repeat center center;
                background-size: 100% 100%;
            }
        }
    }
    .map-popover {
        position: absolute;
        min-width: 302px;
        max-width: 1000px;
        height: 192px;
        right: 0;
        bottom: 36px;
        padding: 8px;
        background: #ffffff;
        border-radius: 4px;
        border: 1px solid #ebedf3;
        overflow: hidden;
        .map-content {
            display: flex;
            min-width: 100%;
            width: max-content;
            height: 100%;
            flex-wrap: nowrap;
            justify-content: center;
            white-space: nowrap;
            transform-origin: left center;
            .map-box-out {
                display: flex;
                height: 100%;
                justify-content: center;
                align-items: center;
                box-sizing: border-box;
                cursor: pointer;
                border-radius: 4px;
                overflow: hidden;
                &.is-active {
                    background: #db9130;
                }
                .map-box {
                    display: flex;
                    height: calc(100% - 4px);
                    justify-content: center;
                    box-sizing: border-box;
                    cursor: pointer;
                    border-radius: 4px;
                    background: #fff;
                    overflow: hidden;
                    .map-li {
                        display: inline-block;
                        width: 6px;
                        margin-left: 2px;
                        margin-right: 2px;
                        white-space: nowrap;
                        background: #fff7eb;
                    }
                }
            }
        }
    }
}
</style>

<style lang="scss">
.full-screen-container-style {
    position: fixed !important;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 99;
}
</style>

调用

<quick-tools v-if="showQuickTools" :tools="['full', 'refresh', 'map']" :refresh-func="getDetail"></quick-tools>

 

标签:index,vue,const,center,100%,full,全屏,background,组件
From: https://www.cnblogs.com/hong1/p/18563398

相关文章

  • 盘点Vue3 watch的一些关键时刻能够大显身手的功能
    前言watch这个API大家应该都不陌生,在Vue3版本中给watch增加不少有用的功能,比如deep选项支持传入数字、pause、resume、stop方法、once选项、onCleanup函数。这些功能大家平时都不怎么用得上,但是在一些特定的场景中,他们能够起大作用,这篇文章欧阳就来带你盘点一下这些功能。d......
  • Vue项目--从安装到发布
    一、安装node1、下载nvm我这里用的nvm(node.jsversionmanagement)一个node.js的版本管理工具,可以安装和切换不同版本的node.js。已经有或者只想安装node.js可跳过下载链接:https://github.com/coreybutler/nvm-windows/releases我的百度资源:https://pan.baidu.com/s/1r_9DN......
  • 微信小程序上传启用组件按需注入
    微信小程序在预览或上传的时候会进行代码质量检测,有时候会提示‘组件需按需注入’,如下图所示:这是只要加一句代码"lazyCodeLoading":"requiredComponents"就行了,添加的位置在app.json文件的里面,手动去app.json文件添加"lazyCodeLoading":"requiredComponents"......
  • 智慧城市页面小组件
    上传单张图片:效果图如:  usecommon\helpers\ComponentHelper;ComponentHelper::loadComponentView('com-attachment');<el-form-itemlabel="门店套餐图片:"><com-attachmentv-if="!dataInfo.cover_pic"......
  • 基于Springboot+Vue的在线考试系统 (含源码数据库)
    1.开发环境开发系统:Windows10/11架构模式:MVC/前后端分离JDK版本:JavaJDK1.8开发工具:IDEA数据库版本:mysql5.7或8.0数据库可视化工具:navicat服务器:SpringBoot自带apachetomcat主要技术:Java,Springboot,mybatis,mysql,vue2.视频演示地址3.功能该系统......
  • 基于Nodejs+Vue的游戏点单陪玩系统 (含源码数据库)
    1.开发环境开发系统:Windows10/11架构模式:MVC/前后端分离JDK版本:JavaJDK1.8开发工具:IDEA数据库版本:mysql5.7或8.0数据库可视化工具:navicat服务器:SpringBoot自带apachetomcat主要技术:Java,Springboot,mybatis,mysql,vue2.视频演示地址3.功能该系统......
  • 基于Springboot+Vue的汽车销售系统 (含源码数据库)
    1.开发环境开发系统:Windows10/11架构模式:MVC/前后端分离JDK版本:JavaJDK1.8开发工具:IDEA数据库版本:mysql5.7或8.0数据库可视化工具:navicat服务器:SpringBoot自带apachetomcat主要技术:Java,Springboot,mybatis,mysql,vue2.视频演示地址3.功能这个系......
  • SpringBoot3+Vue3+AntDesign电商后台管理系统 | 小蚂蚁云
     项目介绍基于SpringBoot3、SpringSecurity、MybatisPlus、Vue3、TypeScript、Vite、AntDesign、MySQL等技术栈实现的单体前后端分离后台管理系统;后端基于Java语言采用SpringBoot3、SpringSecurity、MybatisPlus、MySQL等主流技术栈,前端基于Vue3、TypeScript、Vite等技术栈实......
  • SpringBoot3+Vue3+AntDesign单体多模块项目实践 | 小蚂蚁云
     项目介绍基于SpringBoot3、SpringSecurity、MybatisPlus、Vue3、TypeScript、Vite、AntDesign、MySQL等技术栈实现的单体前后端分离后台管理系统;后端基于Java语言采用SpringBoot3、SpringSecurity、MybatisPlus、MySQL等主流技术栈,前端基于Vue3、TypeScript、Vite等技术栈实......
  • SpringBoot3+Vue3+AntDesign博客后台管理系统源码 | 小蚂蚁云
     项目介绍基于SpringBoot3、SpringSecurity、MybatisPlus、Vue3、TypeScript、Vite、AntDesign、MySQL等技术栈实现的单体前后端分离后台管理系统;后端基于Java语言采用SpringBoot3、SpringSecurity、MybatisPlus、MySQL等主流技术栈,前端基于Vue3、TypeScript、Vite等技术栈实......