bindbox-mini/components/ClayInput.vue
2026-02-27 20:57:24 +08:00

283 lines
5.7 KiB
Vue
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view
class="clay-input-wrapper"
:class="[
`clay-input-${size}`,
{ 'clay-input-focused': isFocused },
{ 'clay-input-error': error },
{ 'clay-input-disabled': disabled },
customClass
]"
>
<!-- 前缀图标 -->
<view v-if="prefixIcon" class="clay-prefix-icon">
<text>{{ prefixIcon }}</text>
</view>
<!-- 输入框 -->
<input
class="clay-input-field"
:type="type"
:value="modelValue"
:placeholder="placeholder"
:disabled="disabled"
:maxlength="maxlength"
@input="handleInput"
@focus="handleFocus"
@blur="handleBlur"
@confirm="handleConfirm"
/>
<!-- 后缀图标/按钮 -->
<view v-if="suffixIcon || clearable" class="clay-suffix-icon" @tap="handleSuffixTap">
<text v-if="showClearButton" class="clear-button">×</text>
<text v-else-if="suffixIcon">{{ suffixIcon }}</text>
</view>
<!-- 错误提示 -->
<view v-if="error && errorText" class="clay-error-text">
<text>{{ errorText }}</text>
</view>
</view>
</template>
<script>
export default {
name: 'ClayInput',
props: {
// v-model 绑定值
modelValue: {
type: [String, Number],
default: ''
},
// 输入框类型
type: {
type: String,
default: 'text'
},
// 占位符
placeholder: {
type: String,
default: '请输入'
},
// 尺寸sm, md, lg
size: {
type: String,
default: 'md'
},
// 是否禁用
disabled: {
type: Boolean,
default: false
},
// 是否显示错误状态
error: {
type: Boolean,
default: false
},
// 错误提示文字
errorText: {
type: String,
default: ''
},
// 最大长度
maxlength: {
type: [String, Number],
default: 140
},
// 是否可清空
clearable: {
type: Boolean,
default: false
},
// 前缀图标emoji
prefixIcon: {
type: String,
default: ''
},
// 后缀图标emoji
suffixIcon: {
type: String,
default: ''
},
// 自定义类名
customClass: {
type: String,
default: ''
}
},
data() {
return {
isFocused: false
}
},
computed: {
showClearButton() {
return this.clearable && this.modelValue && !this.disabled
}
},
methods: {
handleInput(e) {
this.$emit('update:modelValue', e.detail.value)
this.$emit('input', e.detail.value)
},
handleFocus(e) {
this.isFocused = true
this.$emit('focus', e)
},
handleBlur(e) {
this.isFocused = false
this.$emit('blur', e)
},
handleConfirm(e) {
this.$emit('confirm', e)
},
handleSuffixTap() {
if (this.showClearButton) {
this.$emit('update:modelValue', '')
this.$emit('clear')
} else if (this.suffixIcon) {
this.$emit('suffix-tap')
}
}
}
}
</script>
<style lang="scss" scoped>
/* ============================================
Claymorphism 输入框组件
使用示例:
<ClayInput v-model="value" placeholder="请输入" prefixIcon="🔍" />
============================================ */
.clay-input-wrapper {
position: relative;
display: flex;
align-items: center;
background: linear-gradient(145deg, #ffffff, #f5f5f5);
border-radius: 24rpx;
transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
overflow: hidden;
/* Claymorphism 双阴影 */
box-shadow:
inset 4rpx 4rpx 8rpx rgba(0, 0, 0, 0.06),
inset -4rpx -4rpx 8rpx rgba(255, 255, 255, 0.9),
4rpx 4rpx 8rpx rgba(0, 0, 0, 0.04),
-4rpx -4rpx 8rpx rgba(255, 255, 255, 0.7);
&.clay-input-sm {
border-radius: 20rpx;
padding: 16rpx 24rpx;
.clay-input-field {
font-size: 24rpx;
}
}
&.clay-input-md {
border-radius: 24rpx;
padding: 24rpx 28rpx;
.clay-input-field {
font-size: 28rpx;
}
}
&.clay-input-lg {
border-radius: 28rpx;
padding: 28rpx 32rpx;
.clay-input-field {
font-size: 32rpx;
}
}
/* 聚焦状态 */
&.clay-input-focused {
box-shadow:
inset 6rpx 6rpx 12rpx rgba(255, 107, 0, 0.08),
inset -6rpx -6rpx 12rpx rgba(255, 255, 255, 0.85),
6rpx 6rpx 12rpx rgba(255, 107, 0, 0.1),
-6rpx -6rpx 12rpx rgba(255, 255, 255, 0.6);
}
/* 错误状态 */
&.clay-input-error {
box-shadow:
inset 4rpx 4rpx 8rpx rgba(255, 59, 48, 0.1),
inset -4rpx -4rpx 8rpx rgba(255, 255, 255, 0.9),
4rpx 4rpx 8rpx rgba(255, 59, 48, 0.1),
-4rpx -4rpx 8rpx rgba(255, 255, 255, 0.7);
}
/* 禁用状态 */
&.clay-input-disabled {
opacity: 0.5;
background: linear-gradient(145deg, #f0f0f0, #e8e8e8);
}
}
.clay-input-field {
flex: 1;
border: none;
background: transparent;
color: #1D1D1F;
outline: none;
line-height: 1.5;
&::placeholder {
color: #C7C7CC;
}
}
/* 前缀图标 */
.clay-prefix-icon {
margin-right: 16rpx;
font-size: 32rpx;
opacity: 0.6;
}
/* 后缀图标 */
.clay-suffix-icon {
margin-left: 16rpx;
font-size: 32rpx;
opacity: 0.6;
cursor: pointer;
transition: all 0.2s;
&:active {
opacity: 1;
transform: scale(1.1);
}
.clear-button {
display: inline-block;
width: 40rpx;
height: 40rpx;
line-height: 36rpx;
text-align: center;
font-size: 40rpx;
color: #86868B;
background: linear-gradient(145deg, #e8e8e8, #f8f8f8);
border-radius: 50%;
/* Claymorphism 凹陷效果 */
box-shadow:
inset 2rpx 2rpx 4rpx rgba(0, 0, 0, 0.1),
inset -2rpx -2rpx 4rpx rgba(255, 255, 255, 0.9);
}
}
/* 错误提示 */
.clay-error-text {
position: absolute;
bottom: -40rpx;
left: 0;
font-size: 22rpx;
color: #FF3B30;
white-space: nowrap;
}
</style>