用vue3+ts写个组件库又近了一步

615 阅读2分钟

前言

现在市面上有很多成熟的UI组件库,大多时候也是直接拿过来用,当某一天项目中那些UI组件库满足不了我们或组件风格不是我们想要的,这时就需要自己封装组件来实现,下面列举五种常见的组件封装,当然是比不上那些优秀UI组件库的,我觉得封装组件的过程也是提高代码能力的方式吧!虽然写的不咋地哈,我也很快乐!!!😜😜😜 注意:本文技术栈:Vue3+TypeScript
字体图标地址: //at.alicdn.com/t/font_2143783_iq6z4ey5vu.css

轮播图

参数:说明类型默认值
interval自动切换的时间间隔,单位为毫秒number1500
autoPlay是否开启自动切换booleantrue
imagesList循环的图片数据Array
height轮播图的高度单位pxnumber450
width轮播图的宽度单位pxnumber100%

实现效果图

lbbb.gif

轮播图基本使用

App.vue 举个栗子:


<script lang="ts" setup>
// 导入轮播图组件
import GoodsImages from './components/GoodsImages/GoodsImages.vue'; 
import { ref } from 'vue';
const imagesList = ref([
    {
    id:0,
    img:'https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/aad8fefad8484117b9334982e3834c04~tplv-k3u1fbpfcp-zoom-crop-mark:3024:3024:3024:1702.awebp?'
    },
    {
    id:1,
    img:'https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/563194bb6765480698d2b3ca7b4238ef~tplv-k3u1fbpfcp-zoom-crop-mark:3024:3024:3024:1702.awebp?'
    },
    {
     id:2,
     img:'https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/9720ff461a2446948dba9c4ec1da06e8~tplv-k3u1fbpfcp-zoom-crop-mark:3024:3024:3024:1702.awebp?'
    },

])

</script>
<template>

<GoodsImages :interval="3000" autoPlay :list="imagesList" :height="400" :width="700"></GoodsImages>

</template>

代码

GoodsImages.vue

<script lang="ts" setup>
import {ref,onMounted} from 'vue'


type imglist = {
  id:number,
  img:string
}

const {interval,autoPlay,list} = defineProps<{
  interval:number, // 自动切换的时间间隔,单位为毫秒
  autoPlay:boolean, // 是否开启自动切换
  list:imglist[], // 循环的图片数组
  height?: number, // 轮播图的高度单位px
  width?:number// // 轮播图的宽度单位px
}>()

let active = ref(0)

// 上一张
const prev = ()=>{
  active.value === 0 ? active.value = list.length-1 : active.value--
  // if(active.value === 0){
  //   active.value = list.length-1
  // }else{
  //    active.value--
  // }
 
}
// 下一张
const next = ()=>{
   active.value >= list.length-1 ? active.value = 0 : active.value++
}

// 鼠标经过小圆点切换
const ringlet = (index:number)=>{
  active.value = index 
}

const rbt = ()=>{
  if(autoPlay){
   setInt = setInterval(()=>{
    next()
  },interval)
  }
}

let setInt = -1
onMounted(()=>{
  rbt()
})

// 鼠标经过图片停止定时器
const stop = () =>{
  clearTimeout(setInt)
}
// 鼠标离开开启定时器
const play =()=>{
   rbt()
}
</script>

<template>
  <div class="xtx-carousel" @mouseenter="stop" @mouseleave="play" :style="{height:height+'px',width:width+'px'}">
    <ul class="carousel-body">
      <li class="carousel-item " :class="{fade: active=== item.id}" v-for="item in list" :key="item.id">
          <img :src="item.img" alt=""/>
      </li>
    </ul>
    <a href="javascript:;" class="carousel-btn prev" @click="prev"><i class="iconfont icon-angle-left"></i></a>
    <a href="javascript:;" class="carousel-btn next" @click="next"><i class="iconfont icon-angle-right"></i></a>
    <div class="carousel-indicator">
      <span :class="{active:active===index}"  v-for="(item,index) in list.length" :key="index" @mouseenter="ringlet(index)"
     
     ></span>x
    </div>
  </div>
</template>

<style scoped lang="less">
a{
  text-decoration: none;
}
.xtx-carousel {
  width: 100%;
  height: 100%;
  min-width: 300px;
  position: relative;
  .carousel {
    &-body {
      width: 100%;
      height: 100%;
    }
    &-item {
      width: 100%;
      height: 100%;
      position: absolute;
      left: 0;
      top: 0;
      opacity: 0;
      transition: opacity 0.5s linear;
      &.fade {
        opacity: 1;
        z-index: 1;
      }
      img {
        width: 100%;
        height: 100%;
      }
    }
    &-indicator {
      position: absolute;
      left: 0;
      bottom: 20px;
      z-index: 2;
      width: 100%;
      text-align: center;
      span {
        display: inline-block;
        width: 12px;
        height: 12px;
        background: rgba(0, 0, 0, 0.2);
        border-radius: 50%;
        cursor: pointer;
        ~ span {
          margin-left: 12px;
        }
        &.active {
          background: #fff;
        }
      }
    }
    &-btn {
      width: 44px;
      height: 44px;
      background: rgba(0, 0, 0, 0.2);
      color: #fff;
      border-radius: 50%;
      position: absolute;
      top: 228px;
      z-index: 2;
      text-align: center;
      line-height: 44px;
      opacity: 0;
      transition: all 0.5s;
      &.prev {
        left: 20px;
      }
      &.next {
        right: 20px;
      }
    }
  }
  &:hover {
    .carousel-btn {
      opacity: 1;
    }
  }
}
</style>

骨架屏

参数说明类型默认值
bg骨架屏背景颜色string#efefef
width骨架屏宽度pxnumber100
height骨架屏高度pxnumber30
br骨架屏换行stringinline-block,block
animated是否开启动画1booleanfalse
fade是否开启闪烁动画2booleanfalse

骨架屏基本使用

  1. 普通骨架屏

image.png

import XtxSkeleton from './components/XtxSkeleton/XtxSkeleton.vue'; // 导入骨架屏组件

 <XtxSkeleton br="block"></XtxSkeleton> 
 <XtxSkeleton :width="200"></XtxSkeleton> 
  1. 进阶骨架屏

kkk.gif

<template>
 <XtxSkeleton bg="#ef3fef" br="block" animated fade></XtxSkeleton>
 <XtxSkeleton bg="#ef3fef" animated fade :width="200"></XtxSkeleton>
 <XtxSkeleton bg="#ef3fef" br="block" animated fade :width="300"></XtxSkeleton>
</template>
       

代码

<script lang="ts" setup>
import { ref } from "@vue/reactivity"

const a = ref(0)

defineProps<{
   bg?:string, // 背景颜色
   width?:number, // 骨架宽度
   height?:number, // 骨架高度
   br?:string ,// 骨架换行
   animated?:boolean,// 是否开启动画
   fade?:boolean, //  是否开启闪烁动画
 }>()

 

</script>
<template>
  <div
    class="xtx-skeleton"
    :style="{width:width+'px',height:height+'px',display:br}"
    :class="{fade:fade,shan:animated}"
  >
    <!-- 1 盒子-->
    <div class="block" :style="{background:bg}"></div>
    <!-- 2 闪效果 xtx-skeleton 伪元素 --->
  </div>
</template>

<style scoped lang="less">
.xtx-skeleton {
  display: inline-block;
  position: relative;
  overflow: hidden;
  vertical-align: middle;
  margin-top: 10px;
  width: 100px;
  height: 30px;

  .block {
    width: 100%;
    height: 100%;
    border-radius: 2px;
    background: #efefef;
  }
}
.shan {
  &::after {
    content: '';
    position: absolute;
    animation: shan 1.5s ease 0s infinite;
    top: 0;
    width: 50%;
    height: 100%;
    background: linear-gradient(
      to left,
      rgba(255, 255, 255, 0) 0,
      rgba(255, 255, 255, 0.3) 50%,
      rgba(255, 255, 255, 0) 100%
    );
    transform: skewX(-45deg);
  }
}
@keyframes shan {
  0% {
    left: -100%;
  }
  100% {
    left: 120%;
  }
}

.fade {
  animation: fade 1s linear infinite alternate;
}
@keyframes fade {
  from {
    opacity: 0.2;
  }
  to {
    opacity: 1;
  }
}
</style>

button按钮

参数说明类型默认可选值
mini尺寸stringlargemiddle/small/mini
type类型stringdefaultprimary/plain/gray

image.png

button按钮基本用法

 <XtxButton size="mini" type='default'>按钮</XtxButton>
 <XtxButton size="mini" type='gray'>按钮</XtxButton>
 <XtxButton size="mini" type='plain'>按钮</XtxButton>
 <XtxButton size="mini" type='primary'>按钮</XtxButton>

代码

<script lang="ts" setup>
// size='large',type='default'默认值设置
const {size='large',type='default'} = defineProps<{
  size: 'large' | 'middle' | 'small' | 'mini',
  type: 'default' | 'primary' | 'plain' | 'gray',
}>()
</script>
<template>
  <button class="xtx-button ellipsis" :class="[size, type]">
    <slot />
  </button>
</template>

<style scoped lang="less">
.xtx-button {
  appearance: none;
  border: none;
  outline: none;
  background: #fff;
  text-align: center;
  border: 1px solid transparent;
  border-radius: 4px;
  cursor: pointer;
  margin-right: 10px;
}
.large {
  width: 240px;
  height: 50px;
  font-size: 16px;
}
.middle {
  width: 180px;
  height: 50px;
  font-size: 16px;
}
.small {
  width: 100px;
  height: 32px;
}
.mini {
  width: 60px;
  height: 32px;
}
.default {
  border-color: #e4e4e4;
  color: #666;
}
.primary {
  border-color:#27ba9b;
  background:#27ba9b;
  color: #fff;
}
.plain {
  border-color:#27ba9b;
  color:#27ba9b;
  background: lighten(#27ba9b, 50%);
}
.gray {
  border-color: #ccc;
  background: #ccc;
  color: #fff;
}
</style>

面包屑

# vue3+ts造个面包屑吃?

消息提示框

参数说明类型默认值可选值
type类型string---success/errorwarning
text提示文字string-------

33322.gif

消息提示框基本使用

  1. 用法一:
<template>
<XtxMessage type="success" text="成功消息提示!"></XtxMessage>
<XtxMessage type="warning" text="警告消息提示!"></XtxMessage>
<XtxMessage type="error" text="错误消息提示!"></XtxMessage>
</template>
  1. 用法二:
<script setup lang="ts">
import Message from './components/XtxMessage';
Message.success('成功!')
Message.warning('警告!')
Message.error('错误!')
</script>

代码

用法一代码:SyMessage.vue

<script lang="ts" setup>
import { onMounted, ref } from 'vue';

type MessageType = 'success' | 'error' | 'warning'
 defineProps<{type:MessageType,text:string}>()

// 定义一个对象,包含三种情况的样式,对象key就是类型字符串
const style = {
  warning: {
    icon: 'icon-warning',
    color: '#E6A23C',
    backgroundColor: 'rgb(253, 246, 236)',
    borderColor: 'rgb(250, 236, 216)',
  },
  error: {
    icon: 'icon-shanchu',
    color: '#F56C6C',
    backgroundColor: 'rgb(254, 240, 240)',
    borderColor: 'rgb(253, 226, 226)',
  },
  success: {
    icon: 'icon-queren2',
    color: '#67C23A',
    backgroundColor: 'rgb(240, 249, 235)',
    borderColor: 'rgb(225, 243, 216)',
  },
}
const isShow = ref(false)
onMounted(()=>{
  isShow.value = true
})
</script>

<template>
<Transition name="down">
  <div class="xtx-message" :style="style[type]" v-if="isShow">
    <i class="iconfont" :class="style[type].icon"></i>
    <span class="text">{{text}}</span>
  </div>
  </Transition>
</template>

<style scoped lang="less">
.down {
  &-enter {
    &-from {
      transform: translate3d(0, -75px, 0);
      opacity: 0;
    }
    &-active {
      transition: all 0.5s;
    }
    &-to {
      transform: none;
      opacity: 1;
    }
  }
}
.xtx-message {
  width: 300px;
  height: 50px;
  position: fixed;
  z-index: 9999;
  left: 50%;
  margin-left: -150px;
  top: 25px;
  line-height: 50px;
  padding: 0 25px;
  border: 1px solid #e4e4e4;
  background: #f5f5f5;
  color: #999;
  border-radius: 4px;
  i {
    margin-right: 4px;
    vertical-align: middle;
  }
  .text {
    vertical-align: middle;
  }
}
</style>

用法二代码:ts代码


import { h, render } from 'vue'
import XtxMessage from './SyMessage.vue' // 导入SyMessage.vue
type MessageType = 'success' | 'error' | 'warning'
type Props = {
  type:MessageType
  text: string
  duration?: number
}

const div = document.createElement('div')
div.setAttribute('class', 'xtx-message-container')
document.body.appendChild(div)


export default function Message({ type, text, duration = 2000 }: Props) {
  const vNode = h(XtxMessage, { type, text })
  render(vNode, div)
  setTimeout(() => {
    render(null, div)
  }, duration)
}

Message.error = function (text: string, duration = 2000) {
  Message({
    type: 'error',
    text,
    duration,
  })
}
Message.success = function (text: string, duration = 2000) {
  Message({
    type: 'success',
    text,
    duration,
  })
}
Message.warning = function (text: string, duration = 2000) {
  Message({
    type: 'warning',
    text,
    duration,
  })
}

结束语

虽然写的不咋地哈,但我还是很快乐!!!😜😜 还请前端大佬指点指点 后续还会不断更新 请继续关注 感谢阅读^_^