一、组件库
本项目的组件: Button,Alert,CheckBox,Link,Message,Tag,Menu,Upload,Card Dialog,Form,Input,Radio,Select,Switch,Upload,Carousel
已更新组件:Button按钮,Message消息提示,Carousel轮播图
二、组件
Button组件
| 需求 | 数据类型 | 默认值 |
|---|---|---|
| 类型type | primary,info,danger,warning,success, | primary |
| plain | Boolean | false |
| 禁用disabled | Boolean | false |
| 圆角round | Boolean | false |
| circle | Boolean | false |
| icon | String | default |
组件样式
.zj-button{
display: inline-block;
line-height: 1;
white-space: nowrap;
cursor: pointer;
background-color: #fff;
border: 1px solid #dcdfe6;
color: #606266;
-webkit-appearance: none;
appearance: none;
text-align: center;
box-sizing: border-box;
outline: none;
margin: 0;
transition: 0.1s;
font-weight: 500;
-moz-user-select: none;
user-select: none;
-webkit-user-select: none;
-ms-user-select: none;
padding: 12px 20px;
font-size: 14px;
border-radius: 4px
}
.zj-button-default{
color: #606266;
background-color: #fff;
border-color: #dcdfe6;
}
.zj-button-default:hover{
background: #ecf5ff;
border-color: #c6e2ff;
color: #409eff;
}
.zj-button-default.is-plain{
color: #606266;
background-color: #fff;
border-color: #dcdfe6;
}
.zj-button-default.is-plain:hover{
color: #409eff;
background-color: #ffffff;
border-color: #409eff;
}
.zj-button-primary{
color: #fff;
background-color: #409eff;
border-color: #409eff;
}
.zj-button-primary:hover{
background: #66b1ff;
border-color: #66b1ff;
color: #fff;
}
.zj-button-primary.is-plain{
color: #409eff;
background-color: #eff7ff;
border-color: #409eff;
}
.zj-button-primary.is-plain:hover{
background: #409eff;
border-color: #409eff;
color: #fff;
}
.zj-button-success{
color: #fff;
background-color: #67c23a;
border-color: #67c23a;
}
.zj-button-success:hover{
background: #85ce61;
border-color: #85ce61;
color: #fff;
}
.zj-button-success.is-plain{
color: #67c23a;
background-color: #f0f9eb;
border-color: #67c23a;
}
.zj-button-success.is-plain:hover{
background: #67c23a;
border-color: #67c23a;
color: #fff;
}
.zj-button-warning{
color: #fff;
background-color: #e6a23c;
border-color: #e6a23c;
}
.zj-button-warning:hover{
background: #ebb563;
border-color: #ebb563;
color: #fff;
}
.zj-button-warning.is-plain{
color: #e6a23c;
background-color: #fdf6ec;
border-color: #e6a23c;
}
.zj-button-warning.is-plain:hover{
background: #e6a23c;
border-color: #e6a23c;
color: #fff;
}
.zj-button-danger{
color: #fff;
background-color: #f56c6c;
border-color: #f56c6c;
}
.zj-button-danger:hover{
background: #f78989;
border-color: #f78989;
color: #fff;
}
.zj-button-danger.is-plain{
color: #f56c6c;
background-color: #fef0f0;
border-color: #f56c6c;
}
.zj-button-danger.is-plain:hover{
background: #f56c6c;
border-color: #f56c6c;
color: #fff;
}
.zj-button-info{
color: #fff;
background-color: #909399;
border-color: #909399;
}
.zj-button-info{
color: #fff;
background-color: #909399;
border-color: #909399;
}
.zj-button-info:hover{
background: #a6a9ad;
border-color: #a6a9ad;
color: #fff;
}
.zj-button-info.is-plain{
color: #909399;
background-color: #f4f4f5;
border-color: #909399;
}
.zj-button-info.is-plain:hover{
background: #909399;
border-color: #909399;
color: #fff;
}
.is-round{
border-radius: 20px;
}
.is-circle{
border-radius: 50%;
padding: 12px;
}
.zj-button+[class*=zj-icon-]+span{
margin-left: 5px;
}
.is-disabled{
cursor: not-allowed;
opacity: 0.6;
}
.is-disabled:hover{
cursor: not-allowed;
opacity: 0.6;
}
组件结构
<template>
<button class="zj-button"
:class="[`zj-button-${type}`,
{'is-plain':plain},
{'is-round':round},
{'is-circle':circle},
{'is-disabled':disabled}]"
:disabled="disabled"
>
<i v-if="icon" :class="` zj-icon-${icon}`"></i>
<span v-if="$slots.default">
<slot></slot>
</span>
</button>
</template>
组件数据和方法
<script>
export default {
name: 'ZjButton',
props: {
type: {
type: String,
default: 'primary'
},
plain: {
type: Boolean,
default: false
},
round: {
type: Boolean,
default: false
},
circle: {
type: Boolean,
default: false
},
icon: {
type: String,
default: 'default'
},
disabled: {
type: Boolean,
default: false
}
}
}
</script>
全局引入
main.js中
import { createApp } from 'vue'
import App from './App.vue'
import ZjButton from '../src/components/Button.vue'
import ZjDialog from '../src/components/Dialog.vue'
import ZjInput from '../src/components/Input.vue'
import ZjSwitch from '../src/components/Switch.vue'
import ZjRadio from '../src/components/Radio.vue'
import ZjRadioGroup from '../src/components/RadioGroup.vue'
import ZjCheckBox from '../src/components/CheckBox.vue'
import ZjCheckBoxGroup from '../src/components/CheckBoxGroup.vue'
import ZjFormItem from '../src/components/FormItem.vue'
import ZjForm from '../src/components/Form.vue'
// import '../src/assets/fonts/font.scss'
import '../src/assets/font2/iconfont.css'
const app = createApp(App)
const components = [ZjButton, ZjDialog, ZjInput, ZjSwitch, ZjRadio, ZjRadioGroup, ZjCheckBox, ZjCheckBoxGroup, ZjFormItem, ZjForm]
components.forEach(component => {
app.component(component.name, component)
})
app.mount('#app')
Message消息提示
Message不同于Input,Form等组件,使用的时候是通过函数传入选项来调用的
import Message from '../Message/Message.vue'
import { render, createVNode } from 'vue'
const instances = []
const MessagePlugin = {
install (app) {
function generateInstance (options) {
let offset = options.offset || 20
instances.forEach(item => {
offset += item.el.offsetHeight + 20
})
const params = {
...options,
offset
}
const div = document.createElement('div') // 创建一个div
const vnode = createVNode(Message, params) // 创建一个message组件的虚拟DOM
vnode.props.onDestroy = () => {
render(null, div)
instances.pop()
}
render(vnode, div) // 渲染虚拟DOM
document.body.appendChild(div.firstElementChild) // 加入到body中
instances.push(vnode)
}
app.config.globalProperties.$message = function (options) {
generateInstance(options)
}
}
}
export default MessagePlugin
轮播图组件
| 需求 | 数据类型 | 默认值 |
|---|---|---|
| height | String | 200px |
| autoplay | Boolean | true |
| duration | Number | 1000 |
| initial | Number | 0 |
| hasDot | Boolean | true |
| hasDirector | Boolean | true |
用法:
<ZjCarousel
:has-director="true"
:has-dot="true"
:initial="0"
indicator-position="outside"
:autoplay="true" :duration="2000">
<ZjCarouselItem v-for="(item,index) in list" :key="index">
<!-- <h3 text="2xl">{{ item }}</h3> -->
<img :src="require(`./assets/${index+1}.png`)" style="width: 300px;" :alt="item">
</ZjCarouselItem>
</ZjCarousel>
Carousel:
Carousel结构
<div class="zj-carousel"
:style="{
height: height,
width: width,
overflow: 'hidden',
}"
@mouseenter="mouseEnter"
@mouseleave="mouseLeave"
>
<div class="zj-carousel_inner" >
<slot></slot>
</div>
<ZjDirective @dirClick="dirClick"></ZjDirective>
<div class="zj-carousel__dots" v-if="hasDot">
<ZjDots
:itemLen="itemLength"
:currentIndex="currentIndex"
@dotClick="dotClick"
></ZjDots>
</div>
</div>
Carousel样式
<style lang="scss" scoped>
.zj-carousel{
width: 100%;
height: auto;
.zj-carousel_inner{
width: 100%;
height: 100%;
position: relative;
text-align: center;
vertical-align:middle;
}
}
.zj-carousel_container{
position: relative;
height: 30px;
z-index: 999;
}
.zj-carousel__dots{
display: flex;
position: absolute;
left: 50%;
align-items: center;
text-align: center;
transform: translateX(-50%);
}
</style>
Carousel数据和方法
<script>
import { reactive, toRefs, onMounted, onBeforeUnmount, getCurrentInstance, watch } from 'vue'
export default {
name: 'ZjCarousel',
props: {
height: {
type: String,
default: '200px'
},
width: {
type: String,
default: '100%'
},
autoplay: {
type: Boolean,
default: true
},
duration: {
type: Number,
default: 1000
},
initial: {
type: Number,
default: 0
},
hasDot: {
type: Boolean,
default: true
},
hasDirector: {
type: Boolean,
default: true
}
},
setup (props) {
const instance = getCurrentInstance()
const state = reactive({
currentIndex: props.initial,
itemLength: 0
})
let timer = null
const mouseEnter = () => {
_clearInterval()
}
const mouseLeave = () => {
autoPlay()
}
function _clearInterval () {
clearInterval(timer)
timer = null
}
const setIndex = (dir) => {
switch (dir) {
case 'prev':
state.currentIndex -= 1
if (state.currentIndex === -1) {
console.log(state.currentIndex)
state.currentIndex = state.itemLength - 1
}
break
case 'next':
state.currentIndex += 1
if (state.currentIndex === state.itemLength) {
state.currentIndex = 0
}
break
default:
break
}
}
const dotClick = (index) => {
console.log(index)
state.currentIndex = index - 1
}
const dirClick = (dir) => {
if (dir === 'prev') {
setIndex('prev')
} else {
setIndex('next')
}
}
const autoPlay = () => {
if (props.autoplay === true) {
timer = setInterval(() => {
setIndex('next')
}, props.duration)
}
}
watch(() => state.itemLength, (val) => {
state.itemLength = val
})
onMounted(() => {
state.itemLength = instance.slots.default()[0].children.length
autoPlay()
})
onBeforeUnmount(() => {
_clearInterval()
})
return {
dotClick,
setIndex,
mouseEnter,
mouseLeave,
dirClick,
...toRefs(state)
}
}
}
</script>
CarouselItem:
CarouselItem结构:
<template>
<transition name="fade">
<div class="zj-carousel-item"
:class="{active: active}"
v-show="selfIndex === currentIndex"
>
<slot></slot>
</div>
</transition>
</template>
CarouselItem数据和方法:
<script>
import { getCurrentInstance, reactive, toRefs, watch } from 'vue'
export default {
name: 'ZjCarouselItem',
data () {
return {
current: 0
}
},
props: {
active: {
type: Boolean,
default: false
}
},
setup () {
const instance = getCurrentInstance()
const state = reactive({
selfIndex: instance.vnode.key,
currentIndex: instance.parent.ctx.currentIndex,
isNext: true
})
console.log(state)
watch(() => instance.parent.ctx.currentIndex, (val) => {
state.currentIndex = val
})
return {
...toRefs(state)
}
}
}
</script>
CarouselItem样式:
<style lang="scss" scoped>
.zj-carousel-item{
width: 100%;
height: auto;
position: absolute;
top:0;
left: 0;
img{
width: 100%;
height: 100%;
margin: 0 auto;
}
}
.fade-enter-active, .fade-leave-active {
transition: all .5s linear;
}
.fade-enter-active{
transform: translateX(100%);
}
.fade-leave-active{
transform: translateX(-100%);
}
.fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */ {
opacity: 0;
}
.fade-leave-to{
transform: translateX(-100%);
}
.fade-enter-to{
transform: translateX(0);
}
</style>
Directive:(左右箭头控制轮播图前进后退) Directive结构:
<template>
<div v-if="hasDirector" class="zj-carousel__indicators">
<div class="left" @click="dirClick('prev')">
<i class="el-icon-arrow-left">{{'<'}}</i>
</div>
<div class="right" @click="dirClick('next')">
<i class="el-icon-arrow-right">{{'>'}}</i>
</div>
</div>
</template>
Directive数据和方法:
<script>
export default {
name: 'ZjDirective',
props: {
hasDirector: {
type: Boolean,
default: true
},
dir: String
},
setup (props, ctx) {
const dirClick = (dir) => {
ctx.emit('dirClick', dir)
}
return {
dirClick
}
}
}
</script>
Directive样式:
<style lang="scss">
.zj-carousel__indicators{
display: flex;
position: absolute;
top: 10%;
left: 0;
justify-content: space-between;
align-items: center;
width: inherit;
height: 40px;
}
.left{
width: 40px;
height: 40px;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
&:hover{
background-color: rgb(218, 218, 218);
color:aliceblue;
width: 40px;
height: 40px;
border-radius: 50%;
}
}
.right{
width: 40px;
height: 40px;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
&:hover{
background-color: rgb(218, 218, 218);
color:aliceblue;
width: 40px;
height: 40px;
border-radius: 50%;
}
}
</style>
Dots:
需要itemLen作为底部导航小圆点个数限制,需要currentIndex活跃的item显示不同样式,点击事件dotClick(item),传递所点击的图片索引,切换父组件(轮播图)显示对应图片
<div class="dots">
<div class="dot"
v-for="item in itemLen"
:key="item"
:class="{activeDot: currentIndex === item-1}"
@click="dotClick(item)"
></div>
</div>
Dots样式
<style lang="scss">
.dots {
display: flex;
justify-content: space-between;
align-items: center;
width: 80px;
height: 20px;
z-index: 3000;
}
.dot {
width: 10px;
height: 10px;
border-radius: 50%;
z-index: 3000;
background: #8d8d8d;
}
.activeDot{
background: #ffcaca;
}
</style>
<script>
export default {
name: 'ZjDots',
props: {
itemLen: {
type: Number,
default: 4
},
currentIndex: {
type: Number,
default: 0
}
},
setup (props, ctx) {
const dotClick = (index) => {
ctx.emit('dotClick', index)
}
return {
dotClick
}
}
}
</script>
Card组件
用法示例:
<ZjCard style="max-width: 480px" shadow="Never">
<template #header>
<div class="card-header">
<span>Card name</span>
</div>
</template>
<p v-for="o in 4" :key="o" class="text item">{{ 'List item ' + o }}</p>
<template #footer>Footer content</template>
</ZjCard>
| 需求 | 类型 | 默认值 |
|---|---|---|
| shadow | String | always/Never/hover |
样式
<style lang="scss">
.zj-card{
width: 100%;
height: 100%;
border: 1px solid #ccc;
border-radius: 5px;
padding: 10px 0;
margin: 5px;
box-sizing: border-box;
transition: all 0.3s;
.zj-card-header{
padding: 15px;
border-bottom: 1px solid #ccc;
}
.zj-card-body{
padding: 10px 15px;
}
.zj-card-footer{
padding: 15px;
border-top: 1px solid #ccc;
}
}
.zj-card-always{
box-shadow: 0 0 10px #ccc;
}
.zj-card-hover{
&:hover{
box-shadow: 0 0 10px #ccc;
}
}
.zj-card-never{
box-shadow: none;
}
</style>
Slots插槽
Card结构
<template>
<div class="zj-card" :class="`zj-card-${shadow}`">
<div class="zj-card-header" v-if="$slots.header">
<slot name="header"></slot>
</div>
<div class="zj-card-body">
<slot></slot>
</div>
<div class="zj-card-footer" v-if="$slots.footer">
<slot name="footer"></slot>
</div>
</div>
</template>
Card
<script>
export default {
name: 'ZjCard',
props: {
shadow: {
type: String,
default: 'always'
}
}
}
</script>