一、前言
2021年8月13日耗费一天时间按照navie-ui的button源码,自己写了一个button组件,css使用的tailwindcss,可以自己重写。那个myload是另一个组件,主要就是加载状态的图标。
二、代码
<script>
import {defineComponent,h} from "vue";
import myload from "../myload/index.vue"
export default defineComponent({
name: "",
props:{
loading:{
default:false,
type:Boolean,
},
disabled:{
default:false,
type:Boolean,
},
},
setup() {
return {}
},
methods:{
},
render(){
return h('button',{
class:{
"min-w-28":true,
"relative":true,
"px-3":true,
"py-2":true,
"h-12":true,
"text-gray-600":true,
"rounded":true,
"border":true,
"border-gray-200":true,
"hover:border-blue-600":!this.$props.loading&&!this.$props.disabled,
"hover:text-gray-50":!this.$props.disabled,
"hover:bg-blue-500":!this.$props.disabled,
"active:bg-gray-400":true,
"flex":true,
"justify-center":true,
"items-center":true,
"cursor-not-allowed":this.$props.disabled,
"text-black":this.$props.disabled,
"bg-gray-200":this.$props.disabled,
"bg-opacity-50":this.$props.disabled,
},disabled: this.$props.disabled,
},[h("span",{class:{"z-0":true}},this.$slots.default())],this.$props.loading?h("div",{
class:{"absolute":true,"inset-0":true,"bg-white":true,"flex":true,"justify-center":true,"items-center":true,"z-50":true}
},this.$slots.loadicon||h(myload,{},"")):null)
},
components: {},
})
</script>
<style scoped>
</style>
这是第一版,只实现了loading和disable状态
三、原因和目的
1.为什么要自己做按钮组件:
现成的按钮组件有很多状态,能适应很多情况,但不能适应所有情况,所以我需要修改他们的按钮组件,但是那些组件可修改的地方并不多,所以需要自己定制。
2.我想要的组件长什么样
a.默认拥有loading和disabled两种状态
b.中间随意放置文字图标等
c.圆角和圆形功能放到外层实现
d.背景透明、白色、主题颜色
e.高度和宽度主要由外层设置
f.边框随主题颜色
g.文字大小随尺寸
h.文字颜色随主题颜色
i.hover和active随主题颜色
j.被点击后的激活状态样式可以被外层覆盖
j.设置否决属性,一票否决所有设置,全部由外层定制
四、增加阻止点击事件冒泡和一些主题颜色配置
<script>
import {defineComponent, h, ref,watch} from "vue";
import myload from "../myload/index.vue"
export default defineComponent({
name: "",
props: {
loading: {
default: false,
type: Boolean,
},
disabled: {
default: false,
type: Boolean,
},
// 组件大小,可选large,big,middle,small,mini
size: {
type: String,
default: "middle",
},
// 主题,可选red,blue,green,default,text,yellow,purple
type: {
type: String,
default: "default",
},
// 默认圆角,直角需要传递false
rounded:{
type:Boolean,
default:true,
},
},
setup(prop, content) {
const classlist = ref({})
const init = () => {
switch (prop.size) {
case "large":
classlist.value = Object.assign(classlist.value, {"w-40": true, "h-18": true, "text-5xl": true})
break
case "big":
classlist.value = Object.assign(classlist.value, {"w-30": true, "h-12": true, "text-4xl": true})
break
case "middle":
classlist.value = Object.assign(classlist.value, {"w-20": true, "h-8": true, "text-3xl": true})
break
case "small":
classlist.value = Object.assign(classlist.value, {"w-10": true, "h-5": true, "text-xl": true})
break
case "mini":
classlist.value = Object.assign(classlist.value, {"w-5": true, "h-2": true, "text-ssss": true})
break
}
switch (prop.type) {
case "default":
classlist.value = Object.assign(classlist.value, {
// 正常状态下,黑色文字和边框,白色背景
"border": true,
"border-gray-500": true,
"text-gray-500": true,
"bg-white": true,
// 鼠标覆盖后,文字和边框变蓝
"over:border-2": !prop.disabled,
"hover:border-blue-400": !prop.disabled,
"hover:text-blue-400": !prop.disabled,
// 点击后,外轮廓变蓝
"active:ring-1": !prop.disabled,
"active:ring-blue-400": !prop.disabled,
})
break
case "red":
classlist.value = Object.assign(classlist.value, {
// 正常状态下,白色文字,红色背景,没有边框
"text-white": true,
"bg-red-400": true,
// 鼠标覆盖后,背景颜色加深
"hover:bg-red-500": !prop.disabled,
// 点击后,外轮廓变红
"active:ring-1": !prop.disabled,
"active:ring-red-400": !prop.disabled,
})
break
case "blue":
classlist.value = Object.assign(classlist.value, {
// 正常状态下,白色文字,蓝色背景,没有边框
"text-white": true,
"bg-blue-400": true,
// 鼠标覆盖后,背景颜色加深
"hover:bg-blue-500": !prop.disabled,
// 点击后,外轮廓变蓝
"active:ring-1": !prop.disabled,
"active:ring-blue-400": !prop.disabled,
})
break
case "green":
classlist.value = Object.assign(classlist.value, {
// 正常状态下,白色文字,绿色背景,没有边框
"text-white": true,
"bg-green-400": true,
// 鼠标覆盖后,背景颜色加深
"hover:bg-green-500": !prop.disabled,
// 点击后,外轮廓变绿
"active:ring-1": !prop.disabled,
"active:ring-green-400": !prop.disabled,
})
break
case "yellow":
classlist.value = Object.assign(classlist.value, {
// 正常状态下,白色文字,黄色背景,没有边框
"text-white": true,
"bg-yellow-400": true,
// 鼠标覆盖后,背景颜色加深
"hover:bg-yellow-500": !prop.disabled,
// 点击后,外轮廓变黄
"active:ring-1": !prop.disabled,
"active:ring-yellow-400": !prop.disabled,
})
break
case "purple":
classlist.value = Object.assign(classlist.value, {
// 正常状态下,白色文字,紫色背景,没有边框
"text-white": true,
"bg-Purple-400": true,
// 鼠标覆盖后,背景颜色加深
"hover:bg-Purple-500": !prop.disabled,
// 点击后,外轮廓变紫
"active:ring-1": !prop.disabled,
"active:ring-Purple-400": !prop.disabled,
})
break
case "text":
classlist.value = Object.assign(classlist.value, {
// 正常状态下,黑色文字,透明背景,没有边框
"text-gray-600": true,
// 鼠标覆盖后,文字变红
"hover:text-red-500": !prop.disabled,
// 点击后,文字变蓝
"active:text-blue-400": !prop.disabled,
})
break
}
}
watch(()=>prop.disabled,()=>{
init()
})
// 开启loading状态或者disabled状态时,阻止点击事件冒泡
const loadclick=(e)=>{
if(prop.loading || prop.loading){
e.stopPropagation();
}
}
// 初始化区域
init()
return {
classlist,
loadclick,
}
},
render() {
return h('button', {
class: {
// 基础属性区域
"min-w-28": true,
"relative": true,
"px-3": true,
"py-2": true,
"h-12": true,
"border": true,
"flex": true,
"rounded":true,
// 主题属性区域
...this.classlist,
// loading和disabled属性区域
"justify-center": true,
"items-center": true,
"cursor-not-allowed": this.$props.disabled,
"text-black": this.$props.disabled,
"bg-gray-200": this.$props.disabled,
"bg-opacity-50": this.$props.disabled,
}, disabled: this.$props.disabled,onClick:this.loadclick,
}, [h("span", {class: {"z-0": true}}, this.$slots.default())], this.$props.loading ? h("div", {
class: {
"absolute": true,
"inset-0": true,
"bg-white": true,
"flex": true,
"justify-center": true,
"items-center": true,
"z-50": true
},onClick:this.loadclick,
}, this.$slots.loadicon || h(myload, {}, "")) : null)
},
components: {},
})
</script>
<style scoped>
</style>
五.添加透明背景
<script>
import {defineComponent, h, ref,watch} from "vue";
import myload from "../myload/index.vue"
export default defineComponent({
name: "",
props: {
loading: {
default: false,
type: Boolean,
},
disabled: {
default: false,
type: Boolean,
},
// 组件大小,可选large,big,middle,small,mini
size: {
type: String,
default: "middle",
},
// 主题,可选red,blue,green,default,text,yellow,purple
type: {
type: String,
default: "default",
},
// 默认圆角,直角需要传递false
rounded:{
type:Boolean,
default:true,
},
// 背景透明
ghost:{
default:false,
type:Boolean,
},
},
setup(prop, content) {
const classlist = ref({})
const init = () => {
switch (prop.size) {
case "large":
classlist.value = Object.assign(classlist.value, {"min-w-36": true, "h-18": true, "text-4xl": true,"p-2":true,})
break
case "big":
classlist.value = Object.assign(classlist.value, {"min-w-30": true, "h-14": true, "text-2xl": true,"p-2":true,})
break
case "middle":
classlist.value = Object.assign(classlist.value, {"min-w-20": true, "h-12": true, "text-xl": true,"p-1":true,})
break
case "small":
classlist.value = Object.assign(classlist.value, {"min-w-10": true, "h-9": true, "text-base": true,"p-1":true,})
break
case "mini":
classlist.value = Object.assign(classlist.value, {"min-w-5": true, "h-6": true, "text-ss": true,"p-1":true,})
break
}
switch (prop.type) {
case "default":
if(prop.ghost){
classlist.value = Object.assign(classlist.value, {
// 正常状态下,黑色文字和边框,白色背景
"border": true,
"border-gray-500": true,
"text-gray-500": true,
"bg-white": true,
// 鼠标覆盖后,文字和边框变蓝
"over:border-2": !prop.disabled,
"hover:border-blue-400": !prop.disabled,
"hover:text-blue-400": !prop.disabled,
// 点击后,外轮廓变蓝
"active:ring-2": !prop.disabled,
"active:ring-blue-400": !prop.disabled,
"bg-opacity-0":true,
})
}else{
classlist.value = Object.assign(classlist.value, {
// 正常状态下,黑色文字和边框,白色背景
"border": true,
"border-gray-500": true,
"text-gray-500": true,
"bg-white": true,
// 鼠标覆盖后,文字和边框变蓝
"over:border-2": !prop.disabled,
"hover:border-blue-400": !prop.disabled,
"hover:text-blue-400": !prop.disabled,
// 点击后,外轮廓变蓝
"active:ring-2": !prop.disabled,
"active:ring-blue-400": !prop.disabled,
})
}
break
case "red":
if(prop.ghost){
classlist.value = Object.assign(classlist.value, {
// 正常状态下,白色文字,红色背景,没有边框
"text-red-400": true,
"bg-red-400": true,
"border": true,
"border-red-400": true,
"bg-opacity-0":true,
// 鼠标覆盖后,背景颜色加深
"hover:text-red-500": !prop.disabled,
"hover:border-red-500": !prop.disabled,
// 点击后,外轮廓变红
"active:ring-2": !prop.disabled,
"active:ring-red-300": !prop.disabled,
})
}else{
classlist.value = Object.assign(classlist.value, {
// 正常状态下,白色文字,红色背景,没有边框
"text-white": true,
"bg-red-400": true,
"border": true,
"border-red-400": true,
// 鼠标覆盖后,背景颜色加深
"hover:bg-red-500": !prop.disabled,
"hover:border-red-500": !prop.disabled,
// 点击后,外轮廓变红
"active:ring-2": !prop.disabled,
"active:ring-red-300": !prop.disabled,
})
}
break
case "blue":
if(prop.ghost){
classlist.value = Object.assign(classlist.value, {
// 正常状态下,白色文字,蓝色背景,没有边框
"text-blue-400": true,
"bg-blue-400": true,
"border": true,
"border-blue-400": true,
"bg-opacity-0":true,
// 鼠标覆盖后,背景颜色加深
"hover:text-blue-500": !prop.disabled,
"hover:border-blue-500": !prop.disabled,
// 点击后,外轮廓变蓝
"active:ring-2": !prop.disabled,
"active:ring-blue-400": !prop.disabled,
})
}else{
classlist.value = Object.assign(classlist.value, {
// 正常状态下,白色文字,蓝色背景,没有边框
"text-white": true,
"bg-blue-400": true,
"border": true,
"border-blue-400": true,
// 鼠标覆盖后,背景颜色加深
"hover:bg-blue-500": !prop.disabled,
"hover:border-blue-500": !prop.disabled,
// 点击后,外轮廓变蓝
"active:ring-2": !prop.disabled,
"active:ring-blue-400": !prop.disabled,
})
}
break
case "green":
if(prop.ghost){
classlist.value = Object.assign(classlist.value, {
// 正常状态下,白色文字,绿色背景,没有边框
"text-green-400": true,
"bg-green-400": true,
"border": true,
"border-green-400": true,
"bg-opacity-0":true,
// 鼠标覆盖后,背景颜色加深
"hover:text-green-500": !prop.disabled,
"hover:border-green-500": !prop.disabled,
// 点击后,外轮廓变绿
"active:ring-2": !prop.disabled,
"active:ring-green-400": !prop.disabled,
})
}else{
classlist.value = Object.assign(classlist.value, {
// 正常状态下,白色文字,绿色背景,没有边框
"text-white": true,
"bg-green-400": true,
"border": true,
"border-green-400": true,
// 鼠标覆盖后,背景颜色加深
"hover:bg-green-500": !prop.disabled,
"hover:border-green-500": !prop.disabled,
// 点击后,外轮廓变绿
"active:ring-2": !prop.disabled,
"active:ring-green-400": !prop.disabled,
})
}
break
case "yellow":
if(prop.ghost){
classlist.value = Object.assign(classlist.value, {
// 正常状态下,白色文字,黄色背景,没有边框
"text-yellow-400": true,
"bg-yellow-400": true,
"border": true,
"border-yellow-400": true,
"bg-opacity-0":true,
// 鼠标覆盖后,背景颜色加深
"hover:text-yellow-500": !prop.disabled,
"hover:border-yellow-500": !prop.disabled,
// 点击后,外轮廓变黄
"active:ring-2": !prop.disabled,
"active:ring-yellow-400": !prop.disabled,
})
}else{
classlist.value = Object.assign(classlist.value, {
// 正常状态下,白色文字,黄色背景,没有边框
"text-white": true,
"bg-yellow-400": true,
"border": true,
"border-yellow-400": true,
// 鼠标覆盖后,背景颜色加深
"hover:bg-yellow-500": !prop.disabled,
"hover:border-yellow-500": !prop.disabled,
// 点击后,外轮廓变黄
"active:ring-2": !prop.disabled,
"active:ring-yellow-400": !prop.disabled,
})
}
break
case "purple":
if(prop.ghost){
classlist.value = Object.assign(classlist.value, {
// 正常状态下,白色文字,紫色背景,没有边框
"text-purple-400": true,
"bg-purple-400": true,
"border": true,
"border-purple-400": true,
"bg-opacity-0":true,
// 鼠标覆盖后,背景颜色加深
"hover:text-purple-500": !prop.disabled,
"hover:border-purple-500": !prop.disabled,
// 点击后,外轮廓变紫
"active:ring-2": !prop.disabled,
"active:ring-purple-400": !prop.disabled,
})
}else{
classlist.value = Object.assign(classlist.value, {
// 正常状态下,白色文字,紫色背景,没有边框
"text-white": true,
"bg-purple-400": true,
"border": true,
"border-purple-400": true,
// 鼠标覆盖后,背景颜色加深
"hover:bg-purple-500": !prop.disabled,
"hover:border-purple-500": !prop.disabled,
// 点击后,外轮廓变紫
"active:ring-2": !prop.disabled,
"active:ring-purple-400": !prop.disabled,
})
}
break
case "text":
classlist.value = Object.assign(classlist.value, {
// 正常状态下,黑色文字,透明背景,没有边框
"text-gray-600": true,
// 鼠标覆盖后,文字变红
"hover:text-red-500": !prop.disabled,
// 点击后,文字变蓝
"active:text-blue-400": !prop.disabled,
"bg-opacity-0":true,
})
break
}
// 将其他元素添加进去,不做了
}
watch(()=>prop.disabled,()=>{
init()
})
// 开启loading状态或者disabled状态时,阻止点击事件冒泡
const loadclick=(e)=>{
if(prop.loading || prop.loading){
e.stopPropagation();
}
}
// 初始化区域
init()
return {
classlist,
loadclick,
}
},
render() {
return h('button', {
class: {
// 基础属性区域
"relative": true,
"flex": true,
"rounded":this.$props.rounded,
// 主题属性区域
...this.classlist,
// loading和disabled属性区域
"justify-center": true,
"items-center": true,
"cursor-not-allowed": this.$props.disabled,
"text-black": this.$props.disabled,
"bg-gray-200": this.$props.disabled,
"bg-opacity-50": this.$props.disabled,
}, disabled: this.$props.disabled,onClick:this.loadclick,
}, [h("span", {class: {"z-0": true}}, this.$slots.default())], this.$props.loading ? h("div", {
class: {
"absolute": true,
"inset-0": true,
"bg-white": true,
"flex": true,
"justify-center": true,
"items-center": true,
"z-50": true
},onClick:this.loadclick,
}, this.$slots.loadicon || h(myload, {}, "")) : null)
},
components: {},
})
</script>
<style scoped>
</style>
本文介绍了作者如何基于navie-ui的button源码和tailwindcss创建一个自定义的Vue按钮组件,支持加载、禁用状态,并提供了多种主题颜色和尺寸配置。组件允许在按钮中自由放置内容,具有灵活的样式覆盖功能,以适应不同的设计需求。
1619

被折叠的 条评论
为什么被折叠?



