按钮类型实现
1 .通过设置type为primary、dashed、text、info、success、warning、error创建不同样式的按钮,不设置为默认样式。
2 .预先写好了type==info的css样式,当选择的时候直接加上这个class。
3 .js代码中
type: {
validator (value) {
return oneOf(value, ['default', 'primary', 'dashed', 'text', 'info', 'success', 'warning', 'error']);
},
default: 'default'
},
//props先验证一下传入的值是否在范围内
computed:{
classes () {
return [
`${prefixCls}`,
//必加上自己的默认样式 mix
`${prefixCls}-${this.type}`,
//必加上按钮样式类型,默认是default样式 mix
{
[`${prefixCls}-long`]: this.long,
//决定是否要让按钮填满父元素 com
&-long{
width: 100%;
}
[`${prefixCls}-${this.shape}`]: !!this.shape,
//决定按钮的形状
[`${prefixCls}-${this.size}`]: this.size !== 'default',
//决定按钮的大小
[`${prefixCls}-loading`]: this.loading != null && this.loading,
//决定是否显示load的样式
[`${prefixCls}-icon-only`]: !this.showSlot && (!!this.icon || !!this.customIcon || this.loading),
[`${prefixCls}-ghost`]: this.ghost
//决定是否是幽灵按钮
}
];
},
}
//直接返回一个class数组,有必显示的样式,有根据传入的值显示的数据
4 .结论
1 .所有可能出现的情况的样式都是事先写好,根据传入的参数选择要使用哪些样式
2 .注意排列顺序,有的样式是会覆盖的。或者一开始就明确的把属性区分开来.比如ghost的样式可能会覆盖type的样式
3 .mixins文件夹里面会定义一些可以被复用的代码片段,然后components里面的文件会传入特定的参数来使用它定义的函数片段
4 .他的样式没有使用:style在组件里面现场编成的。都是使用预定义好的
5 .
按钮图标以及形状
1 .通过设置icon属性在Button内嵌入一个Icon,或者直接在Button内使用Icon组件。
使用Button的icon属性,图标位置将在最左边,如果需要自定义位置,需使用Icon组件。
通过设置shape属性为circle,可将按钮置为圆的形状。
2 .解读
1 .要实现这个,就必须在button里面使用slot属性,这样才能保证他可以传入别的组件,不然就只能传入字符串,icon也必须是最前写好的。
2 .我们是可以通过想象写出所有的css样式,比如icon在按钮文字的各个方向,但是这样以想还是有很多情况是用户想要的。
3 .要释放出这一部分的自主性让用户自己选择自己想要的效果
3 .第一种,传入一个参数使用左右分布的icon样式
<Icon class="ivu-load-loop" type="ios-loading" v-if="loading"></Icon>
//自定义给你一个loading icon
<Icon :type="icon" :custom="customIcon" v-if="(icon || customIcon) && !loading"></Icon>
//别的icon就需要传入参数自己选了。
4 .第二种,slot接收外部传入的icon组件
5 .
button跳转
1 .通过设置 to 可以实现点击按钮直接跳转,支持传入 vue-router 对象。设置 replace 则不会保存历史记录。设置 target,会跟 a 标签一样的行为。
2 .实现这个功能其实要使用button的样式,实际渲染出来的确实a标签,其实他是用标签样式替代了js的部分功能
if(this.to.to){
// this.$router.push({path:this.to})
// window.open('http://localhost:8080/#/help')
if(this.to.target){
// 如果这个有属性的话,先走打开新页面的路径
window.open(`http://localhost:8080/#/${this.to.to}`,this.to.target)
}else{
if(this.to.replace){
this.$router.replace({path:this.to.to})
}else{
this.$router.push({path:this.to.to})
}
}
}
//我的实现方式是直接用js实现想要的功能,因为你a标签好像不能跳转router路由的部分吧。
3 .不过这个写法还是实现一下吧,看下这种写法
4 .为什么他的to没有在props里面添加验证,所以取得时候也是这样
const {to}=this
//直接从最外层的this取
5 .
渲染成原生的代码
1 .html-type 设置button原生的type,可选值为button、submit、reset
button group实现
1 .
疑惑部分
1 .const {to}=this
1 .为什么这个传的to不走props流程
2 .this是整个组件,但是里面并没有上面props的值,是在this.$attrs里面才有,他是怎么取到的呢
3 .实测添加了props之后,才会把attrs里面的属性转到this上面
2 .v-bind="tagProps" :给原生的div或者a元素传递一个属性,比如href
computed里面直接计算
return {
href:this.to.href,
tagtet:this.to.tagtet
}
3 .class实现
1 .类似于react一个classes的包,满足对应的条件就加一个class进去
classes(){
return [
`${pre}`,
`${pre}-${this.type}`,
{
[`${pre}-long`]:true,
//注意这里只能包一个[]。
//不加就是语法错误了。。
}
]
}
2 .最后渲染出来的是这样的["red",{red:true}]
3 .这个是vue官网给出的绑定class的数组语法。
4 .mixins语法
1 .一个混入对象可以包含任意组件选项,当组件使用混入对象时,所有混入对象的选项被“混入”进入改组件本身的选项
2 .也就是别的.vue组件所有的script部分都可以写在这里
3 .打印里面的this。都是返回引用组件的this
4 .对象合并:如果mix里面和组件内部的对象重复,采取递归合并,发生冲突时以组件数据优先
5 .相同生命周期函数将会合并为一个数组,而且会先执行混入对象的钩子函数
6 .值位对象的选项,例如methods,components,directives.将被合并为一个对象,如果对象key冲突,取组件对象的key
跳转逻辑
1 .看起来所有的跳转逻辑,不论是组件还是别的a链接,最后都是由js函数实现的,为什么还要在一开始区分什么a标签和button.还要给a标签绑定一堆属性
2 .还是他想用一些a标签的默认样式,或者他html标签的本来特性。有点不理解
3 .
新的css属性
1 .touch-action:manipulation 触摸屏如何操作元素
2 .
源码
1 .按钮
<template>
<component
:is="tagName"
v-bind="tagProps"
@click="handleClick"
:class="classes"
:disabled="disabled"
>
<!-- 决定这个最后渲染出的元素 -->
<!-- <span v-if="showSlot" ref="slot"><slot ></slot></span> -->
<slot></slot>
</component>
</template>
<script>
import mixinsLink from '../../../mixins/link'
import Icon from '@/components/base/icon/icon.vue'
const pre='li-btn'
// 组件样式的前缀,避免重复,这里和.less文件都需要
export default {
mixins:[mixinsLink,],
components:{Icon,},
props:{
htmlType:{
type:String,
default:'div'
},
// 设置button原生的type:reset,button,submit
type:{
validator(value){
return ["default","primary",'dashed','text','text','info','success','warning','error'].includes(value)
},
// 复杂验证,必须满足这个数组里面的值
default:'default'
},
// btn的类型
shape:{
validator(value){
return ['circle','circle-outline'].includes(value)
}
},
size:{
validator(value){
return ['default','small','large'].includes(value)
},
},
disabled:Boolean,
long:{
type:Boolean,
default:false,
},
// 宽度是否要覆盖全部的父级宽度
ghost:{
type:Boolean,
default:false,
}
},
computed:{
isHerfPattern(){
if(this.to){
return true
}return false
},
// 没人使用这个计算属性,他是不会走计算逻辑,依赖的没有发生改变,也不会重新计算
tagName(){
if(this.isHerfPattern){
return 'div'
}return 'div'
},
tagProps(){
if(this.isHerfPattern){
return {
href:this.linkUrl,
tagtet:this.target,
// 这两个原生属性会被绑定到上面的最后确定的元素上面
}
}else{
if(this.htmlType){
return {
type:this.htmlType
}
}
}
},
classes(){
return [
`${pre}`,
`${pre}-${this.type}`,
{
[`${pre}-long`]:this.long,
[`${pre}-${this.shape}`]:!!this.shape,
// !!a 就是他真实的布尔值
[`${pre}-${this.size}`]:this.size!='default'&&this.size!=undefined,
[`${pre}-loading`]:this.loading!=null&&this.loading,
[`${pre}-icon-only`]:this.showSlot,
[`${pre}-ghost`]:this.ghost,
}
]
},
showSlot(){
if(this.$slots.default.length==1){
if(this.$slots.default[0].componentOptions&&this.$slots.default[0].componentOptions['tag']=='Icon'&&this.$parent._name!="<BtnG>"){
return true
}
}
return false
// 如果父级没有传入任何东西的话,这个就不显示。
}
},
methods:{
handleClick(event){
if(this.disabled)return
this.$emit("click",event)
console.log("组件内")
// 向外派发一个事件
const openInNewWindow=event.ctrlKey||event.metaKey
this.handleCheckClick(event,openInNewWindow)
// 只要点击就触发混入里面的函数,然后根据传入的参数来判断是否打开新界面
}
}
}
</script>
<style lang="less" src="./btnP.less">
</style>
2 .按钮组
<template>
<div :class="classes">
<slot></slot>
</div>
</template>
<script>
const pre="li-btn-group"
export default {
name:"btnG",
props:{
size:{
validator(value){
return ['small',"large",'default'].includes(value)
}
},
shape:{
validator(value){
return ['circle','circle-outline'].includes(value)
},
default:"circle"
},
vertical:{
type:Boolean,
default:true,
}
},
computed:{
classes(){
return [
`${pre}`,
{
[`${pre}-${this.size}`]:!!this.size,
[`${pre}-shu-circle`]:this.shape&&this.vertical,
[`${pre}-heng-circle`]:this.shape&&!this.vertical,
[`${pre}-shu`]:this.vertical,
[`${pre}-heng`]:!this.vertical,
}
]
}
}
}
</script>
<style lang="less" src="./btnP.less">
</style>
3 .less样式
@name:.li-btn;
// mixins部分,可以被复用的部分
.button-size(@height;@padding;@font-size;@border-radius){
height:@height;
padding: @padding;
font-size: @font-size;
border-radius: @border-radius;
}
// color
.button-color(@color;@background;@border){
color:@color;
background-color:@background;
border-color:@border;
}
.active-btn-color(@color){
&:focus{
box-shadow: 0 0 0 2px fade(@color,20%)
}
}
.btn-color(@color){
.button-variant(@btn-primary-color, @color, @color);
&:hover,
&:active,
&.active{
color:@btn-primary-color;
}
.active-btn-color(@color)
}
// 聚合一些样式,这里要看实际使用情况决定聚合以及拆分
.button-variant(@color;@background;@border){
.button-color(@color, @background, @border);
&:hover{
.button-color(tint(@color,20%);tint(@background,20%);tint(@border,20%));
}
&:active,
&.active{
.button-color(shade(@color,5%);shade(@background,5%);shade(@background,5%));
}
}
.btn(){
display: inline-flex;
justify-content: center;
align-items: center;
// display: inline-block;
margin-bottom:0;
font-weight: @btn-font-weight;
text-align: center;
vertical-align: middle;
touch-action: manipulation;
cursor: pointer;
border:1px solid transparent;
white-space: nowrap;
line-height: @line-height-base;
user-select: none;
.button-size(@btn-height-base;@btn-padding-small;@btn-font-size;@btn-border-radius);
transition: color @transition-time linear, background-color @transition-time linear, border @transition-time linear, box-shadow @transition-time linear;
&:active,
&:focus{
// 这个&其实是最后使用的地方的前缀
outline: none;
}
&.disabled,
&[disabled]{
cursor: @cursor-disabled;
>*{
pointer-events: none;
}
}
>.@{css-prefix-iconfont}{
line-height:@line-height-base;
}
&-large{
.button-size(@btn-height-large, @btn-padding-large, @btn-font-size-large, @btn-border-radius)
}
&-small{
.button-size(@btn-height-small, @btn-padding-small, @btn-font-size-small, @btn-border-radius)
}
&-disabled{
.button-color(@btn-disable-color;@btn-disable-bg;@btn-disable-border)
}
&-icon-only{
width:32px;
height: 32px;
border-radius: 50%;
padding:0px;
}
&-icon-only&-small{
// .li-btn-icon-only .li-btn-small
.button-size(@btn-height-base;@btn-padding-small-icon;@btn-font-size;@btn-border-radius)
}
&-icon-only&-large{
.button-size(@btn-height-base;@btn-padding-small-icon;@btn-font-size;@btn-border-radius)
}
}
.btn-circle(@btnClassName:li-btn){
border-radius:@btn-circle-size;
&.@{btnClassName}-large{
border-radius:@btn-circle-size-large;
}
&.@{btnClassName}-small{
border-radius:@btn-circle-size-small;
}
}
.btn-default(){
.button-variant(@btn-default-color;@btn-default-bg;@btn-default-border);
&:hover{
.button-color(tint(@primary-color,20%);white;tint(@primary-color,20%));
}
&:active,
&.active{
.button-color(shade(@primary-color,5%), white, shade(@primary-color,5%))
}
.active-btn-color(@primary-color);
}
.btn-primary() {
.button-variant(@btn-primary-color; @btn-primary-bg; @primary-color);
&:hover,
//&:focus,
&:active,
&.active {
color: @btn-primary-color;
}
.active-btn-color(@primary-color);
}
.btn-dashed() {
.button-variant(@btn-ghost-color, @btn-ghost-bg, @btn-ghost-border);
border-style: dashed;
&:hover
//&:focus
{
.button-color(tint(@primary-color, 20%); @btn-ghost-bg; tint(@primary-color, 20%));
}
&:active,
&.active {
.button-color(shade(@primary-color, 5%); @btn-ghost-bg; shade(@primary-color, 5%));
}
.active-btn-color(@primary-color);
}
.btn-ghost(){
.button-variant(@btn-ghost-color, @btn-ghost-bg, @btn-ghost-border);
&:hover{
.button-color(tint(@primary-color,20%);@btn-ghost-bg;tint(@primary-color,20%))
}
&:active
&.active{
.button-variant(shade(@primary-color,5%), @btn-ghost-bg, shade(@primary-color,5%))
}
.active-btn-color(@primary-color)
}
.btn-text() {
.button-variant(@btn-ghost-color, transparent, transparent);
// for disabled
&.disabled,
&[disabled],
fieldset[disabled] & {
&,
&:hover,
&:focus,
&:active,
&.active {
.button-color(@btn-disable-color; @btn-ghost-bg; transparent);
}
}
&:hover
//&:focus
{
.button-color(tint(@primary-color, 20%); @btn-ghost-bg; transparent);
}
&:active,
&.active {
.button-color(shade(@primary-color, 5%); @btn-ghost-bg; transparent);
}
.active-btn-color(@primary-color);
}
.btn-base(){
display: inline-flex;
}
// 真正里面的使用
@{name}{
.btn;
.btn-default;
>span{
display: inline-block;
box-sizing: border-box;
vertical-align: middle;
}
&-long{
width:100%;
}
// primary的样式
&-primary{
.btn-primary;
}
&-dashed{
.btn-dashed;
}
&-text{
.btn-text;
}
// 这三个都是和样式有关的变化情况
&-success{
.btn-color(@success-color)
}
&-warning{
.btn-color(@warning-color)
}
&-error{
.btn-color(@error-color)
}
&-info{
.btn-color(@info-color)
}
&-loading{
pointer-events: none;
position: relative;
}
&-ghost{
color:#fff;
background: transparent;
&:hover{
background: transparent;
}
}
&-ghost&-dashed,&-ghost-default{
color:#fff;
border-color:#fff;
&:hover{
color:tint(@primary-color,20%);
border-color:tint(@primary-color,20%)
}
}
&-ghost&-primary{
color:@primary-color;
&:hover{
color:tint(@primary-color,20%);
background:fade(tint(@primary-color,95%),50%);
}
}
&-ghost&-info{
color:@info-color;
&:hover{
color:tint(@info-color,20%);
background: fade(tint(@info-color,95%),50%);
}
}
&-ghost&-success{
color:@success-color;
&:hover{
color:tint(@success-color,20%);
background:fade(tint(@success-color,95%),50%)
}
}
&-ghost&-warning{
color:@warning-color;
&:hover{
color:tint(@warning-color,20%);
background:fade(tint(@warning-color,95%),50%)
}
}
&-ghost&-error{
color:@error-color;
&:hover{
color:tint(@error-color,20%);
background:fade(tint(@error-color,95%),50%)
}
}
&-ghost&-default[disabled], &-ghost&-dashed[disabled], &-ghost&-primary[disabled], &-ghost&-info[disabled], &-ghost&-success[disabled], &-ghost&-warning[disabled], &-ghost&-error[disabled]{
background: transparent;
color: fade(#000, 25%);
border-color: @btn-disable-border;
}
&-ghost-text[disabled]{
background: transparent;
color:fade(#000,25%)
}
&-circle,&-ciecle-outline{
.btn-circle(@name)
}
> .li-icon{
padding: 0px 5px;
}
// 组样式
&-group{
&-heng{
.btn-base;
& div:first-child{
border-bottom-left-radius: 10%;
border-top-left-radius: 10%;
}
& div:last-child{
border-bottom-right-radius: 10%;
border-top-right-radius: 10%;
}
& div{
border-radius:0;
&:hover{
z-index:2;
// 竟然在这里
}
}
& div:not(:first-child){
margin-left:-1px;
}
&-circle{
& div:first-child{
border-bottom-left-radius: 50%;
border-top-left-radius: 50%;
}
& div:last-child{
border-bottom-right-radius: 50%;
border-top-right-radius: 0%;
}
}
}
&-shu{
display: inline-flex;
flex-direction: column;
& div:first-child{
border-top-right-radius: 10%;
border-top-left-radius: 10%;
}
& div:last-child{
border-bottom-right-radius: 10%;
border-bottom-left-radius: 10%;
}
& div{
border-radius:0;
&:hover{
z-index:2;
// 竟然在这里
}
}
& div:not(:first-child){
margin-top:-1px;
}
&-circle{
& div:first-child{
border-top-right-radius: 50%;
border-top-left-radius: 50%;
}
& div:last-child{
border-bottom-right-radius: 50%;
border-bottom-left-radius: 50%;
}
}
}
}
}