You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
293 lines
7.2 KiB
Vue
293 lines
7.2 KiB
Vue
|
1 month ago
|
<template>
|
||
|
|
<button :class="[
|
||
|
|
'icon-text-button',
|
||
|
|
buttonSize,
|
||
|
|
buttonShape,
|
||
|
|
buttonType,
|
||
|
|
{ 'disabled': disabled },
|
||
|
|
{ 'loading': loading }
|
||
|
|
]" :style="buttonStyle" @click="handleClick" :disabled="disabled">
|
||
|
|
<div class="icon-wrapper" :style="iconStyle">
|
||
|
|
<!-- 支持Element UI图标、Font Awesome图标和自定义组件 -->
|
||
|
|
<el-icon v-if="isElementIcon && icon" :size="iconSize">
|
||
|
|
<component :is="icon" />
|
||
|
|
</el-icon>
|
||
|
|
<i v-else-if="iconType === 'fa' && icon" :class="['fa', icon]" :style="iconStyle"></i>
|
||
|
|
<component v-else-if="iconType === 'component' && icon" :is="icon" :size="iconSize" :color="iconColor" />
|
||
|
|
<slot v-else name="icon"></slot>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<span class="button-text" :style="textStyle" v-if="$slots.default">
|
||
|
|
<slot></slot>
|
||
|
|
</span>
|
||
|
|
</button>
|
||
|
|
</template>
|
||
|
|
|
||
|
|
<script>
|
||
|
|
import { defineComponent, computed } from 'vue';
|
||
|
|
import { ElIcon } from 'element-plus';
|
||
|
|
import 'element-plus/dist/index.css';
|
||
|
|
import '@fortawesome/fontawesome-free/css/all.css';
|
||
|
|
|
||
|
|
export default defineComponent({
|
||
|
|
name: 'IconTextButton',
|
||
|
|
components: { ElIcon },
|
||
|
|
props: {
|
||
|
|
// 图标相关属性
|
||
|
|
icon: { type: [String, Object], default: null },
|
||
|
|
iconSize: { type: [String, Number], default: '1.5em' },
|
||
|
|
iconColor: { type: String, default: 'inherit' },
|
||
|
|
iconType: {
|
||
|
|
type: String,
|
||
|
|
default: 'el',
|
||
|
|
validator: (value) => ['el', 'fa', 'component', 'custom'].includes(value)
|
||
|
|
},
|
||
|
|
|
||
|
|
// 文字相关属性
|
||
|
|
textSize: { type: [String, Number], default: '0.9em' },
|
||
|
|
textColor: { type: String, default: 'inherit' },
|
||
|
|
textBold: { type: Boolean, default: false },
|
||
|
|
|
||
|
|
// 按钮外观控制
|
||
|
|
bgColor: { type: String, default: '#409EFF' },
|
||
|
|
hoverColor: { type: String, default: null },
|
||
|
|
activeColor: { type: String, default: null },
|
||
|
|
border: { type: Boolean, default: false },
|
||
|
|
borderColor: { type: String, default: '#409EFF' },
|
||
|
|
borderWidth: { type: String, default: '1px' },
|
||
|
|
borderRadius: { type: String, default: '4px' },
|
||
|
|
|
||
|
|
// 尺寸控制
|
||
|
|
size: {
|
||
|
|
type: String,
|
||
|
|
default: 'medium',
|
||
|
|
validator: (value) => ['small', 'medium', 'large', 'xlarge'].includes(value)
|
||
|
|
},
|
||
|
|
width: { type: String, default: null },
|
||
|
|
height: { type: String, default: null },
|
||
|
|
|
||
|
|
// 功能控制
|
||
|
|
disabled: { type: Boolean, default: false },
|
||
|
|
loading: { type: Boolean, default: false },
|
||
|
|
type: {
|
||
|
|
type: String,
|
||
|
|
default: 'primary',
|
||
|
|
validator: (value) => ['primary', 'success', 'warning', 'danger', 'info', 'text'].includes(value)
|
||
|
|
},
|
||
|
|
shape: {
|
||
|
|
type: String,
|
||
|
|
default: 'rectangle',
|
||
|
|
validator: (value) => ['rectangle', 'round', 'circle'].includes(value)
|
||
|
|
}
|
||
|
|
},
|
||
|
|
emits: ['click'],
|
||
|
|
setup(props, { emit }) {
|
||
|
|
// 计算属性
|
||
|
|
const isElementIcon = computed(() => props.iconType === 'el');
|
||
|
|
|
||
|
|
// 按钮样式
|
||
|
|
const buttonStyle = computed(() => ({
|
||
|
|
width: props.width || '',
|
||
|
|
height: props.height || '',
|
||
|
|
backgroundColor: props.bgColor,
|
||
|
|
borderColor: props.border ? props.borderColor : 'transparent',
|
||
|
|
borderWidth: props.border ? props.borderWidth : 0,
|
||
|
|
borderStyle: 'solid',
|
||
|
|
borderRadius: {
|
||
|
|
rectangle: props.borderRadius,
|
||
|
|
round: '999px',
|
||
|
|
circle: '50%'
|
||
|
|
}[props.shape]
|
||
|
|
}));
|
||
|
|
|
||
|
|
// 图标样式
|
||
|
|
const iconStyle = computed(() => ({
|
||
|
|
fontSize: typeof props.iconSize === 'string' ? props.iconSize : `${props.iconSize}px`,
|
||
|
|
color: props.iconColor
|
||
|
|
}));
|
||
|
|
|
||
|
|
// 文字样式
|
||
|
|
const textStyle = computed(() => ({
|
||
|
|
fontSize: typeof props.textSize === 'string' ? props.textSize : `${props.textSize}px`,
|
||
|
|
color: props.textColor,
|
||
|
|
fontWeight: props.textBold ? 'bold' : 'normal'
|
||
|
|
}));
|
||
|
|
|
||
|
|
// 尺寸类
|
||
|
|
const buttonSize = computed(() => `size-${props.size}`);
|
||
|
|
|
||
|
|
// 类型类
|
||
|
|
const buttonType = computed(() => `type-${props.type}`);
|
||
|
|
|
||
|
|
// 形状类
|
||
|
|
const buttonShape = computed(() => `shape-${props.shape}`);
|
||
|
|
|
||
|
|
// 点击处理
|
||
|
|
const handleClick = (event) => {
|
||
|
|
if (!props.disabled) {
|
||
|
|
emit('click', event);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
return {
|
||
|
|
isElementIcon,
|
||
|
|
buttonStyle,
|
||
|
|
iconStyle,
|
||
|
|
textStyle,
|
||
|
|
buttonSize,
|
||
|
|
buttonType,
|
||
|
|
buttonShape,
|
||
|
|
handleClick
|
||
|
|
};
|
||
|
|
}
|
||
|
|
});
|
||
|
|
</script>
|
||
|
|
|
||
|
|
<style scoped>
|
||
|
|
.icon-text-button {
|
||
|
|
display: flex;
|
||
|
|
flex-direction: column;
|
||
|
|
align-items: center;
|
||
|
|
justify-content: center;
|
||
|
|
padding: 12px;
|
||
|
|
box-sizing: border-box;
|
||
|
|
cursor: pointer;
|
||
|
|
transition: all 0.3s ease;
|
||
|
|
font-family: 'Helvetica Neue', Helvetica, 'PingFang SC', 'Hiragino Sans GB',
|
||
|
|
'Microsoft YaHei', Arial, sans-serif;
|
||
|
|
user-select: none;
|
||
|
|
outline: none;
|
||
|
|
position: relative;
|
||
|
|
overflow: hidden;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* 按钮形状 */
|
||
|
|
.shape-rectangle {
|
||
|
|
border-radius: 4px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.shape-round {
|
||
|
|
border-radius: 24px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.shape-circle {
|
||
|
|
border-radius: 50%;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* 按钮尺寸 */
|
||
|
|
.size-small {
|
||
|
|
width: 60px;
|
||
|
|
height: 70px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.size-medium {
|
||
|
|
width: 80px;
|
||
|
|
height: 90px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.size-large {
|
||
|
|
width: 100px;
|
||
|
|
height: 110px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.size-xlarge {
|
||
|
|
width: 120px;
|
||
|
|
height: 130px;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* 按钮类型样式 */
|
||
|
|
.type-primary {
|
||
|
|
background-color: #409EFF;
|
||
|
|
color: white;
|
||
|
|
}
|
||
|
|
|
||
|
|
.type-success {
|
||
|
|
background-color: #67C23A;
|
||
|
|
color: white;
|
||
|
|
}
|
||
|
|
|
||
|
|
.type-warning {
|
||
|
|
background-color: #E6A23C;
|
||
|
|
color: white;
|
||
|
|
}
|
||
|
|
|
||
|
|
.type-danger {
|
||
|
|
background-color: #F56C6C;
|
||
|
|
color: white;
|
||
|
|
}
|
||
|
|
|
||
|
|
.type-info {
|
||
|
|
background-color: #909399;
|
||
|
|
color: white;
|
||
|
|
}
|
||
|
|
|
||
|
|
.type-text {
|
||
|
|
background-color: transparent;
|
||
|
|
color: #606266;
|
||
|
|
}
|
||
|
|
|
||
|
|
.icon-wrapper {
|
||
|
|
display: flex;
|
||
|
|
align-items: center;
|
||
|
|
justify-content: center;
|
||
|
|
margin-bottom: 6px;
|
||
|
|
transition: transform 0.2s ease;
|
||
|
|
}
|
||
|
|
|
||
|
|
.button-text {
|
||
|
|
text-align: center;
|
||
|
|
white-space: nowrap;
|
||
|
|
overflow: hidden;
|
||
|
|
text-overflow: ellipsis;
|
||
|
|
max-width: 100%;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* 悬浮效果 */
|
||
|
|
.icon-text-button:not(.disabled):hover {
|
||
|
|
opacity: 0.9;
|
||
|
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
|
||
|
|
}
|
||
|
|
|
||
|
|
.icon-text-button:not(.disabled):hover .icon-wrapper {
|
||
|
|
transform: translateY(-2px);
|
||
|
|
}
|
||
|
|
|
||
|
|
/* 激活状态 */
|
||
|
|
.icon-text-button:not(.disabled):active {
|
||
|
|
opacity: 0.85;
|
||
|
|
transform: scale(0.98);
|
||
|
|
}
|
||
|
|
|
||
|
|
/* 禁用状态 */
|
||
|
|
.disabled {
|
||
|
|
cursor: not-allowed;
|
||
|
|
opacity: 0.5;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* 加载状态 */
|
||
|
|
.loading::after {
|
||
|
|
content: "";
|
||
|
|
position: absolute;
|
||
|
|
top: 0;
|
||
|
|
left: 0;
|
||
|
|
right: 0;
|
||
|
|
bottom: 0;
|
||
|
|
background: repeating-linear-gradient(45deg,
|
||
|
|
transparent,
|
||
|
|
rgba(255, 255, 255, 0.3),
|
||
|
|
transparent 25%);
|
||
|
|
background-size: 200% 100%;
|
||
|
|
animation: loading 2s linear infinite;
|
||
|
|
pointer-events: none;
|
||
|
|
}
|
||
|
|
|
||
|
|
@keyframes loading {
|
||
|
|
0% {
|
||
|
|
background-position: 200% 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
100% {
|
||
|
|
background-position: -200% 0;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
</style>
|