前台常见功能解决方案:下载+全屏+引导

目录

01: 文件下载解决方案

02: 通用组件:方法触发的 message 构建分析

03: 通用组件:构建 message 组件基本能力

04: 通用组件:方法触发 message 展示 

05: 模块全屏解决方案 

06: 构建 floating 基础布局 

07: 样式修正:处理难看的 scrollBar

08: 功能引导解决方案 

09: 解决功能引导图标不显示的问题 

10: 总结 


 

01: 文件下载解决方案

想要实现下载图片功能,需要使用到专门的下载包。

目前常用的支持下载功能的包有两个:

        1. 小文件下载:file-saver

        2. 大文件下载:streamsaver

咱们的图片下载属于小文件的下载,所以我们直接使用 file-saver。

1. 安装 file-saver

npm i --save file-saver@2.0.5

2. 在 src/views/main/components/list/item.vue 中,增加下载功能:

<script setup>
import { saveAs } from 'file-saver'

/**
 * 下载按钮点击事件
 */
const onDownload = () => {
  // 提示消息
  message('success', '图片开始下载')
  // 延迟一段时间执行,可以得到更好的体验
  setTimeout(() => {
    /**
     * 接收两个参数:
     * 1. 下载的图片链接
     * 2. 下载的文件名称
     */
    saveAs(
      props.data.photoDownLink,
      `${props.data.title} - 作者:${props.data.author}`
    )
  }, 100)
}

02: 通用组件:方法触发的 message 构建分析

        之前的时候,我们构建过一个 confirm 的通用组件,该组件我们可以直接通过 方法进行调用展示。

        对于接下来打算构建的 message 组件,我们依然希望可以通过方法的调用直接展示对应的组件。

根据我们之前的经验,可以知道:

1. 首先我们需要先构建出一个对应的 message.vue

2. 然后构建出对应的 message.js 模块

3. 在模块中,通过:

        1. h 函数构建 vnode

        2. render 函数,进行渲染

依次进行处理。以上方式,就是咱们 message 的构建过程。

03: 通用组件:构建 message 组件基本能力

- src/libs
- - message
- - - index.vue
- - - index.js
<template>
  <transition name="down" @after-leave="destroy">
    <div
      v-show="isVisable"
      class="min-w-[420px] fixed top-[20px] left-[50%] translate-x-[-50%] z-50 flex items-center px-3 py-1.5 rounded-sm border cursor-pointer"
      :class="styles[type].containerClass"
    >
      <m-svg-icon
        :name="styles[type].icon"
        :fillClass="styles[type].fillClass"
        class="h-1.5 w-1.5 mr-1.5"
      ></m-svg-icon>
      <span class="text-sm" :class="styles[type].textClass">
        {{ content }}
      </span>
    </div>
  </transition>
</template>

<script>
import mSvgIcon from '../svg-icon/index.vue'

/**
 * 消息类型可选项
 */
const typeEnum = ['success', 'warn', 'error']
</script>

<script setup>
import { ref, onMounted } from 'vue'
const props = defineProps({
  /**
   * message 的消息类型
   */
  type: {
    type: String,
    required: true,
    validator(val) {
      const result = typeEnum.includes(val)
      if (!result) {
        throw new Error(`你的 type 必须是 ${typeEnum.join('、')} 中的一个`)
      }
      return result
    }
  },
  /**
   * 描述文本
   */
  content: {
    type: String,
    required: true
  },
  /**
   * 展示时长
   */
  duration: {
    type: Number
  },
  /**
   * 关闭时的回调
   */
  destroy: {
    type: Function
  }
})

// 样式表数据
const styles = {
  // 警告
  warn: {
    icon: 'warn',
    fillClass: 'fill-warn-300',
    textClass: 'text-warn-300',
    containerClass:
      'bg-warn-100 border-warn-200  hover:shadow-lg hover:shadow-warn-100'
  },
  // 错误
  error: {
    icon: 'error',
    fillClass: 'fill-error-300',
    textClass: 'text-error-300',
    containerClass:
      'bg-error-100 border-error-200  hover:shadow-lg hover:shadow-error-100'
  },
  // 成功
  success: {
    icon: 'success',
    fillClass: 'fill-success-300',
    textClass: 'text-success-300',
    containerClass:
      'bg-success-100 border-success-200  hover:shadow-lg hover:shadow-success-100'
  }
}

// 控制显示处理
const isVisable = ref(false)
// 关闭前 动画执行时间
const animDuration = '0.5s'
/**
 * 保证动画展示,需要在 mounted 之后进行展示
 */
onMounted(() => {
  isVisable.value = true
  /**
   * 延迟时间关闭
   */
  setTimeout(() => {
    isVisable.value = false
    setTimeout(() => {
        if (props.destory) {
            props.destory()
        }
    }, parseInt(duration.replace('0.', '').replace('s', '') * 100))
  }, props.duration)
})
</script>

<style lang="scss" scoped>
.down-enter-active,
.down-leave-active {
  transition: all v-bind(animDuration);
}

.down-enter-from,
.down-leave-to {
  opacity: 0;
  transform: translate3d(-50%, -100px, 0);
}
</style>

04: 通用组件:方法触发 message 展示 

// src/libs/message/index.js

import { h, render } from 'vue'
import messageComponent from './index.vue'

export const message = (type, content, duration = 3000) => {
  /**
   * 动画结束时的回调
   */
  const onDestroy = () => {
    // 3. message 销毁
    render(null, document.body)
  }

  // 1. 返回 vnode
  const vnode = h(messageComponent, {
    type,
    content,
    duration,
    destroy: onDestroy
  })
  // 2. render
  render(vnode, document.body)
}

05: 模块全屏解决方案 

        让页面中的指定区域进行全屏展示也是非常常见的一个功能。想要让页面的指定区域进行全屏展示,我们通常可以使用 全屏API,该 API 中提供了两个方法:

        1. Element.requestFullscreen(): 让指定元素进行全屏。

        2. Document.exitFullscreen(): 退出全屏。

        但是这个 API 比较原始,使用起来没有那么方便,所以我们可以使用一个 vueuse 中更加简便的 API:useFullScreen

使用 useFullScreen 生成 img 图片的全屏方法:

<script setup>
    import { useFullscreen } from '@vueuse/core'

    const imgTarget = ref(null)

    /**
     * 生成全屏方法
     */
    const { enter: onImgFullScreen } = useFullscreen(imgTarget)
</script>
<template>
    <m-button @click="onImgFullScreen" />
</template>

06: 构建 floating 基础布局 

- src/views/layout/components
- - floating
- - - index.vue
<template>
  <div class="fixed bottom-10 right-2">
    <!-- 引导页 -->
    <div
      class="guide-start w-4 h-4 mb-1 bg-white dark:bg-zinc-900 border dark:border-0 border-zinc-200 rounded-full flex justify-center items-center cursor-pointer duration-200 group hover:shadow-lg"
      @click="onGuideClick"
    >
      <m-svg-icon
        name="guide"
        class="w-2 h-2"
        fillClass="fill-zinc-900 dark:fill-zinc-200 group-hover:fill-main "
      ></m-svg-icon>
    </div>
    <!-- 反馈 -->
    <m-popover class="flex items-center guide-feedback" placement="top-left">
      <template #reference>
        <div
          class="w-4 h-4 bg-white dark:bg-zinc-900 border dark:border-0 border-zinc-200 rounded-full flex justify-center items-center cursor-pointer duration-200 group hover:shadow-lg"
        >
          <m-svg-icon
            name="feedback"
            class="w-2 h-2"
            fillClass="fill-zinc-900 dark:fill-zinc-200 group-hover:fill-main "
          ></m-svg-icon>
        </div>
      </template>

      <div class="w-[140px] overflow-hidden">
        <div
          class="flex items-center p-1 cursor-pointer rounded hover:bg-zinc-100/60 dark:hover:bg-zinc-800"
          @click="onToFeedback"
        >
          <m-svg-icon
            name="feedback"
            class="w-1.5 h-1.5 mr-1"
            fillClass="fill-zinc-900 dark:fill-zinc-300"
          ></m-svg-icon>
          <span class="text-zinc-800 dark:text-zinc-300 text-sm">立即吐槽</span>
        </div>
      </div>
    </m-popover>
  </div>
</template>

<script setup>
import Driver from 'driver.js'
import 'driver.js/dist/driver.min.css'
import steps from './steps'
import { onMounted } from 'vue'
import { FEEDBACK_URL } from '@/constants'

/**
 * 引导页处理
 */
let driver = null
onMounted(() => {
  driver = new Driver({
    // 禁止点击蒙版关闭
    allowClose: false,
    closeBtnText: '关闭',
    nextBtnText: '下一个',
    prevBtnText: '上一个'
  })
})

/**
 * 开始引导
 */
const onGuideClick = () => {
  driver.defineSteps(steps)
  driver.start()
}

/**
 * 反馈处理
 */
const onToFeedback = () => {
  window.open(FEEDBACK_URL, '_blank')
}
</script>

<style lang="scss" scoped>
.driver-fix-stacking {
  position: fixed;
  z-index: 100004 !important;
}
</style>

07: 样式修正:处理难看的 scrollBar

scrollBar 看着实在是有点难看,接下来就需要修正一下这个 scrollBar 的样式。

对于 tailwind 而言,默认没有提供 scrollBar 的样式类名。想要处理 scrollBar 的样式,需要安装单独的插件:tailwind-scrollbar

1. 安装插件

npm install --save-dev tailwind-scrollbar@1.3.1

2. 在 tailwind.config.js 中注册该插件

plugins: [require('tailwind-scrollbar')]

3. 在 tailwind.config.js 中,让 scrollBar 支持 dark 模式

module.exports = {
    ……
    theme: {
        extend: {
            ……
            variants: {
                scrollbar: ['dark']
            }
        }
    }
    ……
}

4. 在 src/views/min/index.vue 中使用

scrollbar-thin 
scrollbar-thumb-transparent 
xl:scrollbar-thumb-zinc-200 
xl:dark:scrollbar-thumb-zinc-900 
scrollbar-track-transparent
scrollbar-thumb-transparent  // 滚道样式

5. 在 src/libs/search/index.vue 中使用

scrollbar-thin 
scrollbar-thumb-zinc-200 
dark:scrollbar-thumb-zinc-900 
scrollbar-track-transparent

6. 补充 边弧度 样式 

/* src/styles/index.scss */

::-webkit-scrollbar-thumb {
    border-radius: 10px;
}

08: 功能引导解决方案 

想要完成功能引导的实现,我们需要借助一个第三方包:driver.js

下面我们就使用这个第三方包来实现咱们的功能引导:

1. 安装 driver.js

npm install --save driver.js@0.9.8

2. 在 src/views/layout/components/floating/index.vue 中导入对应模块

import Driver from 'driver.js'
import 'driver.js/dist/driver.min.css'

3. 初始化 Driver

/**
 * 引导页处理
 */
let driver = null
onMounted(() => {
  driver = new Driver({
    // 禁止点击蒙版关闭
    allowClose: false,
    closeBtnText: '关闭',
    nextBtnText: '下一个',
    prevBtnText: '上一个'
  })
})

4. 构建引导步骤

// src/views/layout/components/steps.js

export default [
  {
    // 在哪个元素中高亮
    element: '.guide-home',
    // 配置对象
    popover: {
      // 标题
      title: 'logo',
      // 描述
      description: '点击可返回首页'
    }
  },
  {
    element: '.guide-search',
    popover: {
      title: '搜索',
      description: '搜索您期望的图片'
    }
  },
  {
    element: '.guide-theme',
    popover: {
      title: '风格',
      description: '选择一个您喜欢的风格',
      // 弹出的位置
      position: 'left'
    }
  },
  {
    element: '.guide-my',
    popover: {
      title: '账户',
      description: '这里标记了您的账户信息',
      position: 'left'
    }
  },
  {
    element: '.guide-start',
    popover: {
      title: '引导',
      description: '这里可再次查看引导信息',
      position: 'left'
    }
  },
  {
    element: '.guide-feedback',
    popover: {
      title: '反馈',
      description: '您的任何不满都可以在这里告诉我们',
      position: 'left'
    }
  }
]

使用: 

<script setup>
import steps from './steps'

/**
 * 开始引导
 */
const onGuideClick = () => {
  driver.defineSteps(steps)
  driver.start()
}

</script>

09: 解决功能引导图标不显示的问题 

/* src/styles/index.scss */


.driver-fix-stacking {
  position: relative;
}

.driver-navigation-btns {
  line-height: 0;
}
// src/views/layout/components/floating/index.vue

<style lang="scss" scoped>
.driver-fix-stacking {
  position: fixed;
  z-index: 100004 !important;
}
</style>

10: 总结 

本篇文章中,我们处理了4个核心功能:

1. 文件下载

2. 通用组件:message 构建

3. 全屏处理

4. 引导处理 

  • 16
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

chengbo_eva

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值