u
This commit is contained in:
1 parent
261a263180
commit
de2351e1b2
10 files changed
+269
-119
No files matched your search
@@ -3,3 +3,4 @@ export { default as Button } from './button'
|
||||
export { default as Icon } from './icon'
|
||||
export { default as Input } from './input'
|
||||
export { default as Spin } from './spin'
|
||||
export { default as Textarea } from './textarea'
|
||||
@@ -6,27 +6,16 @@ import './index.scss'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'AIcon',
|
||||
inheritAttrs: false,
|
||||
props: {
|
||||
icon: { type: String as PropType<string>, required: true },
|
||||
size: { type: Number, default: 16 },
|
||||
color: { type: String, default: 'currentColor' },
|
||||
spin: { type: Boolean, default: false },
|
||||
rotate: { type: Number, default: 360 },
|
||||
onClick: Function as PropType<(e: MouseEvent) => void>, // 明确 MouseEvent 类型
|
||||
},
|
||||
setup(props, { attrs }) {
|
||||
setup(props) {
|
||||
const prefixCls = getPrefixCls('icon')
|
||||
|
||||
// 安全调用 onClick
|
||||
const handleClick = (e: MouseEvent) => {
|
||||
props.onClick?.(e)
|
||||
// 如果 attrs 上也传了 onClick,做类型断言
|
||||
if (attrs.onClick) {
|
||||
(attrs.onClick as (e: MouseEvent) => void)(e)
|
||||
}
|
||||
}
|
||||
|
||||
return () => (
|
||||
<i
|
||||
class={props.spin ? `${prefixCls}-spin` : ''}
|
||||
@@ -37,10 +26,8 @@ export default defineComponent({
|
||||
'color': props.color,
|
||||
'--rotate-angle': `${props.rotate}deg`,
|
||||
}}
|
||||
onClick={handleClick}
|
||||
>
|
||||
<Icon
|
||||
{...attrs}
|
||||
icon={props.icon}
|
||||
width={props.size}
|
||||
height={props.size}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
@use './button/index.scss' as *;
|
||||
@use './input/index.scss' as *;
|
||||
@use './textarea/index.scss' as *;
|
||||
|
||||
input,
|
||||
button,
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
<template>
|
||||
<div
|
||||
style="
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 100vh;
|
||||
"
|
||||
>
|
||||
<!-- 内层保留原来的布局 -->
|
||||
<div
|
||||
style="
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 300px;
|
||||
"
|
||||
>
|
||||
<AInput size="small" placeholder="Small 输入框" password />
|
||||
|
||||
<AInput
|
||||
size="medium"
|
||||
placeholder="Medium 输入框"
|
||||
clearable
|
||||
/>
|
||||
|
||||
<AInput
|
||||
size="large"
|
||||
placeholder="Large 输入框"
|
||||
/>
|
||||
|
||||
<AInput
|
||||
size="small"
|
||||
placeholder="Large 输入框"
|
||||
prepend="http"
|
||||
append=".com"
|
||||
/>
|
||||
|
||||
<AInput
|
||||
size="medium"
|
||||
placeholder="Large 输入框"
|
||||
prepend="http"
|
||||
append=".com"
|
||||
/>
|
||||
|
||||
<AInput
|
||||
size="large"
|
||||
placeholder="Large 输入框"
|
||||
prepend="http"
|
||||
append=".com"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -3,16 +3,81 @@
|
||||
|
||||
$input-prefix: #{$prefix}-input;
|
||||
|
||||
.#{$input-prefix}-adornment {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
|
||||
&.#{$input-prefix}-size {
|
||||
&-small {
|
||||
height: 24px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
&-medium {
|
||||
height: 28px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
&-large {
|
||||
height: 32px;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
.#{$input-prefix}-adornment-prepend,
|
||||
.#{$input-prefix}-adornment-append {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-sizing: border-box;
|
||||
|
||||
background-color: #f3f3f3;
|
||||
color: rgba(0, 0, 0, 0.9);
|
||||
border: 1px solid #ddd;
|
||||
|
||||
span {
|
||||
white-space: nowrap;
|
||||
word-break: keep-all;
|
||||
padding: 0 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.#{$input-prefix}-adornment-prepend {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
.#{$input-prefix}-adornment-append {
|
||||
border-left: none;
|
||||
}
|
||||
|
||||
.#{$input-prefix}-wrapper {
|
||||
border-radius: 0px;
|
||||
}
|
||||
|
||||
> :first-child {
|
||||
border-top-left-radius: 2px;
|
||||
border-bottom-left-radius: 2px;
|
||||
}
|
||||
|
||||
> :last-child {
|
||||
border-top-right-radius: 2px;
|
||||
border-bottom-right-radius: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
.#{$input-prefix}-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
border: 1px solid #ddd;
|
||||
|
||||
z-index: 2;
|
||||
box-sizing: border-box;
|
||||
border-radius: 2px;
|
||||
padding: 0 8px;
|
||||
|
||||
transition:
|
||||
border cubic-bezier(0.38, 0, 0.24, 1) 0.2s,
|
||||
box-shadow cubic-bezier(0.38, 0, 0.24, 1) 0.2s,
|
||||
@@ -45,23 +110,6 @@ $input-prefix: #{$prefix}-input;
|
||||
box-shadow: $input-focus-shadow;
|
||||
}
|
||||
|
||||
&.#{$input-prefix}-size {
|
||||
&-small {
|
||||
height: 24px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
&-medium {
|
||||
height: 28px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
&-large {
|
||||
height: 32px;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
.#{$input-prefix}-align {
|
||||
&-left {
|
||||
text-align: left;
|
||||
@@ -119,7 +167,6 @@ $input-prefix: #{$prefix}-input;
|
||||
.#{$input-prefix}-clear {
|
||||
cursor: pointer;
|
||||
transition: color 0.2s;
|
||||
margin-left: 4px;
|
||||
|
||||
&:hover {
|
||||
color: red !important;
|
||||
@@ -132,14 +179,7 @@ $input-prefix: #{$prefix}-input;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.#{$input-prefix}-password-toggle {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
margin-left: 4px;
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
.#{$input-prefix}-password {
|
||||
cursor: pointer !important;
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ import { getPrefixCls } from '../_utils/getPrefixCls'
|
||||
import Icon from '../icon'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'AInput',
|
||||
name: 'Input',
|
||||
props: {
|
||||
modelValue: String,
|
||||
size: {
|
||||
@@ -24,6 +24,8 @@ export default defineComponent({
|
||||
showCount: Boolean,
|
||||
maxlength: Number,
|
||||
password: Boolean,
|
||||
prepend: String,
|
||||
append: String,
|
||||
},
|
||||
emits: ['update:modelValue'],
|
||||
setup(props, { slots, emit }) {
|
||||
@@ -51,65 +53,89 @@ export default defineComponent({
|
||||
return showPassword.value ? 'text' : 'password'
|
||||
})
|
||||
|
||||
return () => (
|
||||
<div
|
||||
class={[
|
||||
`${prefixCls}-wrapper`,
|
||||
`${prefixCls}-size-${props.size}`,
|
||||
props.disabled && `${prefixCls}-disabled`,
|
||||
props.readonly && `${prefixCls}-readonly`,
|
||||
]}
|
||||
>
|
||||
{(slots.prefix || props.prefixIcon) && (
|
||||
<div class={`${prefixCls}-prefix`}>
|
||||
{slots.prefix?.() ?? <Icon icon={props.prefixIcon!} size={14} />}
|
||||
</div>
|
||||
)}
|
||||
const inputWrapper = () => {
|
||||
return (
|
||||
<div
|
||||
class={[
|
||||
`${prefixCls}-wrapper`,
|
||||
props.disabled && `${prefixCls}-disabled`,
|
||||
props.readonly && `${prefixCls}-readonly`,
|
||||
]}
|
||||
>
|
||||
{(slots.prefix || props.prefixIcon) && (
|
||||
<div class={`${prefixCls}-prefix`}>
|
||||
{slots.prefix?.() ?? <Icon icon={props.prefixIcon!} size={14} />}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<input
|
||||
class={`${prefixCls}-input ${prefixCls}-align-${props.align}`}
|
||||
type={inputType.value}
|
||||
value={inputValue.value}
|
||||
placeholder={props.placeholder}
|
||||
disabled={props.disabled}
|
||||
readonly={props.readonly}
|
||||
onInput={(e: Event) => updateValue((e.target as HTMLInputElement).value)}
|
||||
/>
|
||||
|
||||
{props.clearable && inputValue.value && !props.disabled && !props.readonly && (
|
||||
<Icon
|
||||
icon="streamline:interface-delete-circle-button-delete-remove-add-circle-buttons"
|
||||
class={`${prefixCls}-clear`}
|
||||
onClick={() => updateValue('')}
|
||||
<input
|
||||
class={`${prefixCls}-input ${prefixCls}-align-${props.align}`}
|
||||
type={inputType.value}
|
||||
value={inputValue.value}
|
||||
placeholder={props.placeholder}
|
||||
disabled={props.disabled}
|
||||
readonly={props.readonly}
|
||||
onInput={(e: Event) => updateValue((e.target as HTMLInputElement).value)}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* 密码切换图标 */}
|
||||
{props.password && (
|
||||
<div class={`${prefixCls}-password-toggle`}>
|
||||
<Icon
|
||||
icon="mdi:eye"
|
||||
class={{ hidden: showPassword.value }}
|
||||
onClick={() => (showPassword.value = true)}
|
||||
/>
|
||||
<Icon
|
||||
icon="mdi:eye-off"
|
||||
class={{ hidden: !showPassword.value }}
|
||||
onClick={() => (showPassword.value = false)}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{props.clearable && inputValue.value && !props.disabled && !props.readonly && (
|
||||
<span
|
||||
class={`${prefixCls}-suffix`}
|
||||
onClick={() => updateValue('')}
|
||||
>
|
||||
<Icon
|
||||
class={`${prefixCls}-clear`}
|
||||
icon="material-symbols:cancel-outline-rounded"
|
||||
/>
|
||||
</span>
|
||||
|
||||
{(slots.suffix || props.suffixIcon) && !props.password && (
|
||||
<div class={`${prefixCls}-suffix`}>
|
||||
{slots.suffix?.() ?? <Icon icon={props.suffixIcon!} size={14} />}
|
||||
</div>
|
||||
)}
|
||||
)}
|
||||
|
||||
{props.showCount && (
|
||||
<span class={`${prefixCls}-count`}>{countText.value}</span>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
{/* 密码切换图标 */}
|
||||
{props.password && (
|
||||
<span
|
||||
class={`${prefixCls}-suffix`}
|
||||
onClick={() => (showPassword.value = !showPassword.value)}
|
||||
>
|
||||
<Icon icon={showPassword.value ? 'mage:eye-off' : 'mage:eye'} class={`${prefixCls}-password`} />
|
||||
</span>
|
||||
)}
|
||||
|
||||
{(slots.suffix || props.suffixIcon) && !props.password && (
|
||||
<div class={`${prefixCls}-suffix`}>
|
||||
{slots.suffix?.() ?? <Icon icon={props.suffixIcon!} size={14} />}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{props.showCount && (
|
||||
<span class={`${prefixCls}-count`}>{countText.value}</span>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return () => {
|
||||
return (
|
||||
<div class={`${prefixCls}-adornment ${prefixCls}-size-${props.size}`}>
|
||||
{props.prepend || slots.prepend
|
||||
? (
|
||||
<div class={`${prefixCls}-adornment-prepend`}>
|
||||
{slots.prepend?.() ?? (props.prepend && <span>{props.prepend}</span>)}
|
||||
</div>
|
||||
)
|
||||
: null}
|
||||
|
||||
{inputWrapper()}
|
||||
|
||||
{props.append || slots.append
|
||||
? (
|
||||
<div class={`${prefixCls}-adornment-append`}>
|
||||
{slots.append?.() ?? (props.append && <span>{props.append}</span>)}
|
||||
</div>
|
||||
)
|
||||
: null}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
},
|
||||
})
|
||||
@@ -0,0 +1,38 @@
|
||||
@use '../_styles/theme/index' as *;
|
||||
@use 'sass:color';
|
||||
|
||||
$textarea-prefix: #{$prefix}-textarea;
|
||||
|
||||
.#{$textarea-prefix}-wrapper {
|
||||
width: 100%;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 2px;
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
padding: 0;
|
||||
|
||||
&:hover {
|
||||
border-color: $input-hover-color;
|
||||
}
|
||||
|
||||
&:focus-within {
|
||||
border-color: $input-focus-color;
|
||||
box-shadow: $input-focus-shadow;
|
||||
}
|
||||
|
||||
.#{$textarea-prefix}-inner {
|
||||
width: 100%;
|
||||
min-height: 80px; // 可根据需要
|
||||
padding: 8px; // 内边距
|
||||
border: none; // 去掉默认边框
|
||||
outline: none;
|
||||
resize: vertical;
|
||||
box-sizing: border-box;
|
||||
font-size: 13px;
|
||||
color: #000;
|
||||
|
||||
&::placeholder {
|
||||
color: #999999;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import type { PropType } from 'vue'
|
||||
import { computed, defineComponent, ref } from 'vue'
|
||||
import { getPrefixCls } from '../_utils/getPrefixCls'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Textarea',
|
||||
emits: ['update:modelValue'],
|
||||
setup() {
|
||||
const prefixCls = getPrefixCls('input')
|
||||
|
||||
return () => {
|
||||
return (
|
||||
<div class={`${prefixCls}-wrapper`}>
|
||||
<textarea class={`${prefixCls}-inner`} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
},
|
||||
})
|
||||
Reference in new issue
Block a user