272 lines
5.9 KiB
Vue
272 lines
5.9 KiB
Vue
<template>
|
||
<view
|
||
class="clay-button-wrapper"
|
||
:class="[
|
||
`clay-btn-${size}`,
|
||
{ 'clay-btn-block': block },
|
||
{ 'clay-btn-disabled': disabled }
|
||
]"
|
||
>
|
||
<view
|
||
class="clay-button"
|
||
:class="[
|
||
`clay-btn-${variant}`,
|
||
{ 'clay-btn-outline': outline },
|
||
{ 'is-loading': loading }
|
||
]"
|
||
:style="customStyle"
|
||
@tap="handleTap"
|
||
>
|
||
<!-- 加载动画 -->
|
||
<view v-if="loading" class="clay-loading">
|
||
<view class="loading-spinner"></view>
|
||
</view>
|
||
|
||
<!-- 图标 -->
|
||
<view v-if="icon && !loading" class="clay-icon">
|
||
<text>{{ icon }}</text>
|
||
</view>
|
||
|
||
<!-- 按钮文字 -->
|
||
<text class="clay-text">{{ text }}</text>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
export default {
|
||
name: 'ClayButton',
|
||
props: {
|
||
// 按钮文字
|
||
text: {
|
||
type: String,
|
||
default: '按钮'
|
||
},
|
||
// 尺寸:sm, md, lg
|
||
size: {
|
||
type: String,
|
||
default: 'md'
|
||
},
|
||
// 变体:primary, secondary, success, warning, error
|
||
variant: {
|
||
type: String,
|
||
default: 'primary'
|
||
},
|
||
// 是否轮廓样式
|
||
outline: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
// 是否块级按钮
|
||
block: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
// 是否禁用
|
||
disabled: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
// 是否加载中
|
||
loading: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
// 图标(emoji)
|
||
icon: {
|
||
type: String,
|
||
default: ''
|
||
},
|
||
// 自定义样式
|
||
customStyle: {
|
||
type: Object,
|
||
default: () => ({})
|
||
}
|
||
},
|
||
methods: {
|
||
handleTap(e) {
|
||
if (this.disabled || this.loading) return
|
||
this.$emit('tap', e)
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
/* ============================================
|
||
Claymorphism 按钮组件
|
||
使用示例:
|
||
<ClayButton text="确认" variant="primary" size="lg" @tap="handleConfirm" />
|
||
============================================ */
|
||
|
||
.clay-button-wrapper {
|
||
display: inline-flex;
|
||
|
||
&.clay-btn-block {
|
||
display: flex;
|
||
width: 100%;
|
||
}
|
||
|
||
&.clay-btn-disabled {
|
||
opacity: 0.5;
|
||
pointer-events: none;
|
||
}
|
||
}
|
||
|
||
.clay-button {
|
||
border-radius: 50rpx;
|
||
font-weight: 700;
|
||
position: relative;
|
||
transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
|
||
border: none;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 8rpx;
|
||
|
||
/* Claymorphism 双阴影 - 创造凸起感 */
|
||
box-shadow:
|
||
8rpx 8rpx 16rpx rgba(0, 0, 0, 0.08),
|
||
-8rpx -8rpx 16rpx rgba(255, 255, 255, 0.8),
|
||
inset 2rpx 2rpx 4rpx rgba(255, 255, 255, 0.5),
|
||
inset -2rpx -2rpx 4rpx rgba(0, 0, 0, 0.05);
|
||
|
||
&.clay-btn-sm {
|
||
padding: 12rpx 32rpx;
|
||
font-size: 24rpx;
|
||
border-radius: 40rpx;
|
||
box-shadow:
|
||
6rpx 6rpx 12rpx rgba(0, 0, 0, 0.06),
|
||
-6rpx -6rpx 12rpx rgba(255, 255, 255, 0.8),
|
||
inset 2rpx 2rpx 4rpx rgba(255, 255, 255, 0.5),
|
||
inset -2rpx -2rpx 4rpx rgba(0, 0, 0, 0.05);
|
||
}
|
||
|
||
&.clay-btn-md {
|
||
padding: 20rpx 48rpx;
|
||
font-size: 28rpx;
|
||
border-radius: 50rpx;
|
||
}
|
||
|
||
&.clay-btn-lg {
|
||
padding: 28rpx 64rpx;
|
||
font-size: 32rpx;
|
||
border-radius: 60rpx;
|
||
box-shadow:
|
||
10rpx 10rpx 20rpx rgba(0, 0, 0, 0.1),
|
||
-10rpx -10rpx 20rpx rgba(255, 255, 255, 0.7),
|
||
inset 3rpx 3rpx 6rpx rgba(255, 255, 255, 0.5),
|
||
inset -3rpx -3rpx 6rpx rgba(0, 0, 0, 0.06);
|
||
}
|
||
|
||
/* 按钮变体 */
|
||
&.clay-btn-primary {
|
||
background: linear-gradient(145deg, #FF9500, #FF6B00);
|
||
color: #fff;
|
||
box-shadow:
|
||
10rpx 10rpx 20rpx rgba(255, 107, 0, 0.15),
|
||
-10rpx -10rpx 20rpx rgba(255, 255, 255, 0.7),
|
||
inset 3rpx 3rpx 6rpx rgba(255, 255, 255, 0.4),
|
||
inset -3rpx -3rpx 6rpx rgba(0, 0, 0, 0.1);
|
||
}
|
||
|
||
&.clay-btn-secondary {
|
||
background: linear-gradient(145deg, #ffffff, #f0f0f0);
|
||
color: #1D1D1F;
|
||
}
|
||
|
||
&.clay-btn-success {
|
||
background: linear-gradient(145deg, #34C759, #30D158);
|
||
color: #fff;
|
||
box-shadow:
|
||
10rpx 10rpx 20rpx rgba(52, 199, 89, 0.15),
|
||
-10rpx -10rpx 20rpx rgba(255, 255, 255, 0.7),
|
||
inset 3rpx 3rpx 6rpx rgba(255, 255, 255, 0.4),
|
||
inset -3rpx -3rpx 6rpx rgba(0, 0, 0, 0.1);
|
||
}
|
||
|
||
&.clay-btn-warning {
|
||
background: linear-gradient(145deg, #FF9F0A, #FF9500);
|
||
color: #fff;
|
||
box-shadow:
|
||
10rpx 10rpx 20rpx rgba(255, 159, 10, 0.15),
|
||
-10rpx -10rpx 20rpx rgba(255, 255, 255, 0.7),
|
||
inset 3rpx 3rpx 6rpx rgba(255, 255, 255, 0.4),
|
||
inset -3rpx -3rpx 6rpx rgba(0, 0, 0, 0.1);
|
||
}
|
||
|
||
&.clay-btn-error {
|
||
background: linear-gradient(145deg, #FF3B30, #FF453A);
|
||
color: #fff;
|
||
box-shadow:
|
||
10rpx 10rpx 20rpx rgba(255, 59, 48, 0.15),
|
||
-10rpx -10rpx 20rpx rgba(255, 255, 255, 0.7),
|
||
inset 3rpx 3rpx 6rpx rgba(255, 255, 255, 0.4),
|
||
inset -3rpx -3rpx 6rpx rgba(0, 0, 0, 0.1);
|
||
}
|
||
|
||
/* 轮廓样式 */
|
||
&.clay-btn-outline {
|
||
background: transparent;
|
||
border: 3rpx solid currentColor;
|
||
|
||
&.clay-btn-primary {
|
||
color: #FF6B00;
|
||
background: rgba(255, 107, 0, 0.05);
|
||
}
|
||
|
||
&.clay-btn-secondary {
|
||
color: #86868B;
|
||
background: rgba(134, 134, 139, 0.05);
|
||
}
|
||
}
|
||
|
||
/* 按下效果 */
|
||
&:active {
|
||
transform: scale(0.96);
|
||
box-shadow:
|
||
4rpx 4rpx 8rpx rgba(0, 0, 0, 0.1),
|
||
-4rpx -4rpx 8rpx rgba(255, 255, 255, 0.5),
|
||
inset 4rpx 4rpx 8rpx rgba(0, 0, 0, 0.08),
|
||
inset -4rpx -4rpx 8rpx rgba(255, 255, 255, 0.3);
|
||
}
|
||
|
||
/* 加载状态 */
|
||
&.is-loading {
|
||
pointer-events: none;
|
||
}
|
||
}
|
||
|
||
/* 加载动画 */
|
||
.clay-loading {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.loading-spinner {
|
||
width: 32rpx;
|
||
height: 32rpx;
|
||
border: 4rpx solid rgba(255, 255, 255, 0.3);
|
||
border-top-color: #fff;
|
||
border-radius: 50%;
|
||
animation: clay-spin 0.8s linear infinite;
|
||
}
|
||
|
||
@keyframes clay-spin {
|
||
to { transform: rotate(360deg); }
|
||
}
|
||
|
||
/* 图标 */
|
||
.clay-icon {
|
||
font-size: 32rpx;
|
||
line-height: 1;
|
||
}
|
||
|
||
/* 文字 */
|
||
.clay-text {
|
||
line-height: 1;
|
||
white-space: nowrap;
|
||
}
|
||
</style>
|