This commit is contained in:
oneao committed 2025-09-02 21:57:27 +08:00
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>
)
}
},
})
+1 -20
View File
@@ -1,5 +1,4 @@
<script setup lang="ts">
import { NButton, NInput } from 'naive-ui'
</script>
<template>
@@ -23,25 +22,7 @@ import { NButton, NInput } from 'naive-ui'
width: 300px;
"
>
<AButton icon="solar:map-point-search-linear">
ABC
</AButton>
<AButton icon="solar:map-point-search-linear" />
<AInput size="small" placeholder="Small 输入框" password />
<AInput
size="medium"
placeholder="Medium 输入框"
clearable
/>
<AInput
size="large"
placeholder="Large 输入框"
suffix-icon="solar:map-point-search-linear"
>
<template #prefix />
</AInput>
<ATextarea />
</div>
</div>
</template>
+1
View File
@@ -15,5 +15,6 @@ declare module 'vue' {
ALoading: typeof import('@ao/ao-design')['Loading']
ALoding: typeof import('@ao/ao-design')['Loding']
ASpin: typeof import('@ao/ao-design')['Spin']
ATextarea: typeof import('@ao/ao-design')['Textarea']
}
}