文章目录
- 前言
- Button组件
- 1. 功能分析
- 2. 代码+注释说明
- 3. 使用方式
- 4. 效果展示
- (1)有加载动画,执行promise函数
- (2)无加载动画,执行click事件
- 总结
前言
今天这篇主要讲全局按钮组件封装,可根据UI设计师要求自定义修改。
Button组件
1. 功能分析
(1)可以通过className属性自定义按钮样式,传递样式类名来修改按钮的样式
(2)是否可点击由disabled属性控制,当disabled为true时,按钮被禁用
(3)加载状态由loading属性控制,当loading为true时,按钮显示加载动画
(4)当点击事件回调是Promise函数,执行后续处理;否则直接调用click点击事件
2. 代码+注释说明
// @/components/Button/index.tsx
import { useState } from "react";
import classNames from "classnames";
import styles from "./index.module.scss";
// 组件的属性类型
type Props = {
// 按钮的文本
text: string;
// 自定义的类名
className?: string;
// 是否禁用按钮
disabled?: boolean;
// 是否显示加载动画
loading?: boolean;
// 点击按钮时的回调函数
click?: () => void;
beforeChange?: (() => Promise<any>) | undefined;
};
// 按钮组件
export default (props: Props) => {
// 解构属性
const { text, className, disabled, loading, beforeChange, click } = props;
const [load, setLoad] = useState(false);
/**
* 点击按钮时的事件处理函数
* - 如果按钮被禁用,则直接返回
* - 如果 beforeChange 是一个Promise函数,则调用其后续处理
* - 否则直接调用 click
*/
const handleClick = () => {
if (disabled) return undefined;
const isFunction = Object.prototype.toString.call(beforeChange) === "[object Function]";
if (!isFunction) {
click?.();
return false;
}
// 启用加载动画
setLoad(true);
beforeChange?.().finally(() => setLoad(false));
};
return (
// 按钮元素
<button
type="button"
// 设置类名
className={classNames(
styles.container,
// 禁用或加载时增加特定的类名
(disabled || loading) && styles.isDisabled,
className
)}
// 禁用时禁用快捷键操作
onKeyDown={handleClick}
// 禁用时禁用点击事件
onClick={handleClick}
>
{/* 加载动画 */}
{loading && load && <i className={`${styles.loading} iconfont icon-loading`}></i>}
{/* 按钮文本 */}
<span>{text}</span>
</button>
);
};
------------------------------------------------------------------------------
// @/components/Button/index.module.scss
.container {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 40px;
color: #fff;
background-color: var(--cd-primary-color);
border-radius: 4px;
border: none;
cursor: pointer;
span {
font-size: 14px;
line-height: 14px;
}
&:hover {
background-color: var(--cd-primary-color);
}
.isDisabled {
opacity: 0.5;
cursor: not-allowed;
}
@keyframes rotate {
0% {
transform: rotate(0deg);
}
50% {
transform: rotate(180deg);
}
100% {
transform: rotate(360deg);
}
}
.loading {
font-size: 24px;
animation: rotate 2s linear infinite;
}
}
3. 使用方式
// 引入组件
import Button from "@/components/Button";
// 有加载动画使用方式
<Button text="有loading" loading={true} beforeChange={onDoneChange}></Button>
// 点击按钮触发loading
const onDoneChange = () => {
return new Promise((resolve) => {
setTimeout(() => {
console.log("onDoneChange");
resolve(true);
}, 2000);
});
};
-----------------------------------------------------------------------------------------------------
// 无加载动画使用方式
<Button text="有loading" loading={false} beforeChange={onDoneClick}></Button>
// 点击按钮不触发loading
const onDoneClick = () => {
console.log("onDoneClick");
};
4. 效果展示
(1)有加载动画,执行promise函数
(2)无加载动画,执行click事件
总结
下一篇讲【全局模态框Modal组件、公共弹窗Dialog组件封装】。关注本栏目,将实时更新。