This commit is contained in:
oneao committed 2025-09-02 17:30:58 +08:00
1 parent 9a432e7ab2
commit 261a263180
13 files changed
+635 -199

No files matched your search

+1
View File
@@ -11,6 +11,7 @@
"dependencies": {
"@ao/ao-design": "workspace:*",
"@iconify/vue": "5.0.0",
"naive-ui": "^2.42.0",
"vue": "^3.5.18"
},
"devDependencies": {
@@ -5,3 +5,7 @@ $primary-color: $default-color !default;
$danger-color: #f93920 !default;
$warning-color: #fc8800 !default;
$success-color: #22c55e !default;
$input-hover-color: $default-color;
$input-focus-color: $default-color;
$input-focus-shadow: 0 0 0 2px #edf4ff;
@@ -5,54 +5,69 @@ $btn-prefix: #{$prefix}-btn;
$btn-hover-bgcolor: #f4f5f5;
$btn-active-bgcolor: #eceded;
/* ====== 通用颜色交互函数(新版写法) ====== */
@function color-hover($color, $amount: 8%) {
$l: color.channel($color, 'lightness', $space: hsl);
@if $l > 50% {
// 亮色:向暗靠近
@return color.scale($color, $lightness: -$amount);
} @else {
// 暗色:向亮靠近
@return color.scale($color, $lightness: $amount);
}
@return color.scale($color, $lightness: -$amount);
}
@function color-active($color, $amount: 12%) {
$l: color.channel($color, 'lightness', $space: hsl);
@if $l > 50% {
@return color.scale($color, $lightness: -$amount);
} @else {
@return color.scale($color, $lightness: $amount);
}
@return color.scale($color, $lightness: -$amount);
}
.#{$btn-prefix} {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 4px;
box-sizing: border-box;
outline: none;
vertical-align: middle;
user-select: none;
vertical-align: middle;
border-radius: 2px;
border: 1px solid transparent;
cursor: pointer;
transition: all 0.2s ease;
font-weight: 500;
border: 1px solid transparent;
&-small {
height: 24px;
padding: 0 12px;
font-size: 12px;
}
&-medium {
height: 28px;
padding: 0 14px;
font-size: 13px;
}
&-large {
height: 32px;
padding: 0 16px;
font-size: 14px;
}
&-block {
width: 100%;
}
&-icon-left {
margin-right: 4px;
display: inline-flex;
align-items: center;
}
&-icon-right {
margin-left: 4px;
display: inline-flex;
align-items: center;
}
&-icon-left-has-text {
margin-right: 4px;
}
&-icon-right-has-text {
margin-left: 4px;
}
&:disabled {
cursor: not-allowed;
opacity: 0.6;
@@ -176,23 +191,4 @@ $btn-active-bgcolor: #eceded;
}
}
}
/* ===== 尺寸 ===== */
&-small {
height: 24px;
padding: 0 12px;
font-size: 12px;
}
&-medium {
height: 28px;
padding: 0 14px;
font-size: 12px;
}
&-large {
height: 32px;
padding: 0 16px;
font-size: 13px;
}
}
@@ -36,35 +36,39 @@ export default defineComponent({
const map = { small: 14, medium: 16, large: 16 }
return map[props.size]
})
const hasContent = !!slots.default?.().length
return () => (
<button class={cls.value} disabled={props.disabled || props.loading}>
{/* 左侧图标 */}
{props.iconPosition === 'left' && (props.loading || props.icon) && (
<Icon
icon={props.loading ? 'mingcute:loading-line' : props.icon}
class={`${prefixCls}-icon-left`}
spin={props.loading}
width={iconSize.value}
height={iconSize.value}
/>
)}
return () => {
return (
<button class={cls.value} disabled={props.disabled || props.loading}>
{props.iconPosition === 'left' && (props.loading || props.icon) && (
<Icon
icon={props.loading ? 'mingcute:loading-line' : props.icon}
class={[
`${prefixCls}-icon-left`,
hasContent ? `${prefixCls}-icon-left-has-text` : '',
]}
spin={props.loading}
size={iconSize.value}
/>
)}
{/* 插槽文本 */}
<span>
{slots.default?.()}
</span>
<span>
{slots.default?.()}
</span>
{/* 右侧图标 */}
{props.iconPosition === 'right' && props.icon && (
<Icon
icon={props.icon}
class={`${prefixCls}-icon-right`}
width={iconSize.value}
height={iconSize.value}
/>
)}
</button>
)
{props.iconPosition === 'right' && props.icon && (
<Icon
icon={props.icon}
class={[
`${prefixCls}-icon-right`,
hasContent ? `${prefixCls}-icon-right-has-text` : '',
]}
size={iconSize.value}
/>
)}
</button>
)
}
},
})
@@ -5,29 +5,50 @@ import { getPrefixCls } from '../_utils/getPrefixCls'
import './index.scss'
export default defineComponent({
name: 'Icon',
name: 'AIcon',
inheritAttrs: false,
props: {
icon: { type: String as PropType<string>, required: true },
width: { type: Number, default: 16 },
height: { type: Number, default: 16 },
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) {
setup(props, { attrs }) {
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 () => (
<Icon
icon={props.icon}
width={props.width}
height={props.height}
<i
class={props.spin ? `${prefixCls}-spin` : ''}
style={{
'display': 'inline-flex',
'alignItems': 'center',
'justifyContent': 'center',
'color': props.color,
'verticalAlign': 'middle',
'--rotate-angle': `${props.rotate}deg`,
}}
class={props.spin ? `${prefixCls}-spin` : ''}
/>
onClick={handleClick}
>
<Icon
{...attrs}
icon={props.icon}
width={props.size}
height={props.size}
style={{
verticalAlign: 'middle',
}}
/>
</i>
)
},
})
@@ -1,2 +1,9 @@
@use './button/index.scss' as *;
@use './input/index.scss' as *;
input,
button,
select,
textarea {
font-family: 'PingFang SC', 'Microsoft YaHei', 'Helvetica Neue', Helvetica, Arial, sans-serif;
}
@@ -7,65 +7,139 @@ $input-prefix: #{$prefix}-input;
display: flex;
align-items: center;
width: 100%;
border: 1px solid #ddd;
box-sizing: border-box;
border: 1px solid #d9d9d9;
border-radius: 2px;
background-color: #fff;
color: #000000d9;
padding: 0 8px;
transition:
border-color 0.2s,
box-shadow 0.2s;
border cubic-bezier(0.38, 0, 0.24, 1) 0.2s,
box-shadow cubic-bezier(0.38, 0, 0.24, 1) 0.2s,
background-color cubic-bezier(0.38, 0, 0.24, 1) 0.2s;
&:hover:not(&-disabled) {
border-color: $primary-color;
.#{$input-prefix}-input {
width: 100%;
border: none;
outline: none;
background: transparent;
box-sizing: border-box;
padding: 0;
font-size: inherit;
line-height: inherit;
display: flex;
align-items: center;
height: 100%;
&::placeholder {
color: #999999;
}
}
&:focus-within:not(&-disabled) {
border-color: $primary-color;
box-shadow: 0 0 0 1px color.change($primary-color, $alpha: 0.1);
&:hover {
border-color: $input-hover-color;
}
&-disabled {
color: #00000040;
background-color: #f5f5f5;
border-color: #d9d9d9;
&:focus-within {
border-color: $input-focus-color;
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;
}
&-right {
text-align: right;
}
&-center {
text-align: center;
}
}
.#{$input-prefix}-prefix,
.#{$input-prefix}-suffix {
display: inline-flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.#{$input-prefix}-prefix {
margin-right: 4px;
}
.#{$input-prefix}-suffix {
margin-left: 4px;
}
&.#{$input-prefix}-disabled {
background-color: #fafafc;
cursor: not-allowed;
.#{$input-prefix}-input {
color: #a8a8a8;
}
.#{$input-prefix}-input::placeholder {
color: #a8a8a8;
}
.#{$input-prefix}-prefix,
.#{$input-prefix}-suffix {
color: #a8a8a8;
}
&:hover,
&:focus-within {
border-color: #ddd;
box-shadow: none;
}
}
/* 尺寸变体 */
&-small {
height: 24px;
font-size: 12px;
&.#{$input-prefix}-readonly {
background-color: #fdfdfd;
}
&-medium {
height: 28px;
font-size: 14px;
.#{$input-prefix}-clear {
cursor: pointer;
transition: color 0.2s;
margin-left: 4px;
&:hover {
color: red !important;
}
}
&-large {
height: 32px;
font-size: 16px;
.#{$input-prefix}-count {
flex-shrink: 0;
margin-left: 4px;
white-space: nowrap;
}
.#{$input-prefix}-password-toggle {
display: inline-flex;
align-items: center;
cursor: pointer;
margin-left: 4px;
.hidden {
display: none;
}
}
}
.#{$input-prefix}-inner {
flex: 1;
border: none;
outline: none;
background: transparent;
width: 100%;
height: 100%;
font-family: inherit;
&::placeholder {
color: #00000040;
}
}
.#{$input-prefix}-icon {
display: flex;
align-items: center;
justify-content: center;
color: #00000040;
margin: 0 4px;
}
@@ -1,59 +1,113 @@
import type { PropType } from 'vue'
import { computed, defineComponent, h, ref } from 'vue'
import { computed, defineComponent, ref } from 'vue'
import { getPrefixCls } from '../_utils/getPrefixCls'
import Icon from '../icon'
export default defineComponent({
name: 'AInput',
props: {
modelValue: String,
size: {
type: String as PropType<'small' | 'medium' | 'large'>,
default: 'medium',
},
align: {
type: String as PropType<'left' | 'center' | 'right'>,
default: 'left',
},
placeholder: String,
disabled: Boolean,
readonly: Boolean,
clearable: Boolean,
prefixIcon: String,
suffixIcon: String,
showCount: Boolean,
maxlength: Number,
password: Boolean,
},
setup(props, { slots }) {
const inputRef = ref<HTMLInputElement | null>(null)
emits: ['update:modelValue'],
setup(props, { slots, emit }) {
const prefixCls = getPrefixCls('input')
const inputValue = ref(props.modelValue || '')
const wrapperCls = computed(() => [
`${prefixCls}-wrapper`,
`${prefixCls}-wrapper-${props.size}`,
{ [`${prefixCls}-wrapper-disabled`]: props.disabled },
])
// 控制密码显示/隐藏
const showPassword = ref(false)
const iconSizeMap: Record<string, number> = { small: 12, medium: 14, large: 16 }
const renderIcon = (slot: any, iconProp?: string) => {
if (slot)
return typeof slot === 'function' ? slot() : slot
if (iconProp)
return h(Icon, { icon: iconProp, width: iconSizeMap[props.size], height: iconSizeMap[props.size] })
return null
const updateValue = (val: string) => {
if (props.maxlength)
val = val.slice(0, props.maxlength)
inputValue.value = val
emit('update:modelValue', val)
}
const countText = computed(() => {
const len = inputValue.value.length
return props.maxlength ? `${len} / ${props.maxlength}` : `${len}`
})
const inputType = computed(() => {
if (!props.password)
return 'text'
return showPassword.value ? 'text' : 'password'
})
return () => (
<div class={wrapperCls.value}>
<div
class={[
`${prefixCls}-wrapper`,
`${prefixCls}-size-${props.size}`,
props.disabled && `${prefixCls}-disabled`,
props.readonly && `${prefixCls}-readonly`,
]}
>
{(slots.prefix || props.prefixIcon) && (
<span class={`${prefixCls}-icon`}>
{renderIcon(slots.prefix, props.prefixIcon)}
</span>
<div class={`${prefixCls}-prefix`}>
{slots.prefix?.() ?? <Icon icon={props.prefixIcon!} size={14} />}
</div>
)}
<input
ref={inputRef}
class={`${prefixCls}-inner`}
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)}
/>
{(slots.suffix || props.suffixIcon) && (
<span class={`${prefixCls}-icon`}>
{renderIcon(slots.suffix, props.suffixIcon)}
</span>
{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('')}
/>
)}
{/* 密码切换图标 */}
{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>
)}
{(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>
)
+187
View File
@@ -14,6 +14,9 @@ importers:
'@iconify/vue':
specifier: 5.0.0
version: 5.0.0(vue@3.5.19(typescript@5.8.3))
naive-ui:
specifier: ^2.42.0
version: 2.42.0(vue@3.5.19(typescript@5.8.3))
vue:
specifier: ^3.5.18
version: 3.5.19(typescript@5.8.3)
@@ -264,6 +267,16 @@ packages:
'@clack/prompts@0.11.0':
resolution: {integrity: sha512-pMN5FcrEw9hUkZA4f+zLlzivQSeQf5dRGJjSUbvVYDLvpKCdQx5OaknvKzgbtXOizhP+SJJJjqEbOe55uKKfAw==}
'@css-render/plugin-bem@0.15.14':
resolution: {integrity: sha512-QK513CJ7yEQxm/P3EwsI+d+ha8kSOcjGvD6SevM41neEMxdULE+18iuQK6tEChAWMOQNQPLG/Rw3Khb69r5neg==}
peerDependencies:
css-render: ~0.15.14
'@css-render/vue3-ssr@0.15.14':
resolution: {integrity: sha512-//8027GSbxE9n3QlD73xFY6z4ZbHbvrOVB7AO6hsmrEzGbg+h2A09HboUyDgu+xsmj7JnvJD39Irt+2D0+iV8g==}
peerDependencies:
vue: ^3.0.11
'@dprint/formatter@0.3.0':
resolution: {integrity: sha512-N9fxCxbaBOrDkteSOzaCqwWjso5iAe+WJPsHC021JfHNj2ThInPNEF13ORDKta3llq5D1TlclODCvOvipH7bWQ==}
@@ -273,6 +286,9 @@ packages:
'@dprint/toml@0.6.4':
resolution: {integrity: sha512-bZXIUjxr0LIuHWshZr/5mtUkOrnh0NKVZEF6ACojW5z7zkJu7s9sV2mMXm8XQDqN4cJzdHYUYzUyEGdfciaLJA==}
'@emotion/hash@0.8.0':
resolution: {integrity: sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==}
'@es-joy/jsdoccomment@0.50.2':
resolution: {integrity: sha512-YAdE/IJSpwbOTiaURNCKECdAwqrJuFiZhylmesBcIRawtYKnBR2wxPhoIewMg+Yu+QuYvHfJNReWpoxGBKOChA==}
engines: {node: '>=18'}
@@ -538,6 +554,9 @@ packages:
'@jridgewell/trace-mapping@0.3.30':
resolution: {integrity: sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==}
'@juggle/resize-observer@3.4.0':
resolution: {integrity: sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA==}
'@nodelib/fs.scandir@2.1.5':
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
engines: {node: '>= 8'}
@@ -579,36 +598,42 @@ packages:
engines: {node: '>= 10.0.0'}
cpu: [arm]
os: [linux]
libc: [glibc]
'@parcel/watcher-linux-arm-musl@2.5.1':
resolution: {integrity: sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==}
engines: {node: '>= 10.0.0'}
cpu: [arm]
os: [linux]
libc: [musl]
'@parcel/watcher-linux-arm64-glibc@2.5.1':
resolution: {integrity: sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==}
engines: {node: '>= 10.0.0'}
cpu: [arm64]
os: [linux]
libc: [glibc]
'@parcel/watcher-linux-arm64-musl@2.5.1':
resolution: {integrity: sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==}
engines: {node: '>= 10.0.0'}
cpu: [arm64]
os: [linux]
libc: [musl]
'@parcel/watcher-linux-x64-glibc@2.5.1':
resolution: {integrity: sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==}
engines: {node: '>= 10.0.0'}
cpu: [x64]
os: [linux]
libc: [glibc]
'@parcel/watcher-linux-x64-musl@2.5.1':
resolution: {integrity: sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==}
engines: {node: '>= 10.0.0'}
cpu: [x64]
os: [linux]
libc: [musl]
'@parcel/watcher-win32-arm64@2.5.1':
resolution: {integrity: sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==}
@@ -680,56 +705,67 @@ packages:
resolution: {integrity: sha512-JWnrj8qZgLWRNHr7NbpdnrQ8kcg09EBBq8jVOjmtlB3c8C6IrynAJSMhMVGME4YfTJzIkJqvSUSVJRqkDnu/aA==}
cpu: [arm]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-arm-musleabihf@4.48.0':
resolution: {integrity: sha512-9xu92F0TxuMH0tD6tG3+GtngwdgSf8Bnz+YcsPG91/r5Vgh5LNofO48jV55priA95p3c92FLmPM7CvsVlnSbGQ==}
cpu: [arm]
os: [linux]
libc: [musl]
'@rollup/rollup-linux-arm64-gnu@4.48.0':
resolution: {integrity: sha512-NLtvJB5YpWn7jlp1rJiY0s+G1Z1IVmkDuiywiqUhh96MIraC0n7XQc2SZ1CZz14shqkM+XN2UrfIo7JB6UufOA==}
cpu: [arm64]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-arm64-musl@4.48.0':
resolution: {integrity: sha512-QJ4hCOnz2SXgCh+HmpvZkM+0NSGcZACyYS8DGbWn2PbmA0e5xUk4bIP8eqJyNXLtyB4gZ3/XyvKtQ1IFH671vQ==}
cpu: [arm64]
os: [linux]
libc: [musl]
'@rollup/rollup-linux-loongarch64-gnu@4.48.0':
resolution: {integrity: sha512-Pk0qlGJnhILdIC5zSKQnprFjrGmjfDM7TPZ0FKJxRkoo+kgMRAg4ps1VlTZf8u2vohSicLg7NP+cA5qE96PaFg==}
cpu: [loong64]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-ppc64-gnu@4.48.0':
resolution: {integrity: sha512-/dNFc6rTpoOzgp5GKoYjT6uLo8okR/Chi2ECOmCZiS4oqh3mc95pThWma7Bgyk6/WTEvjDINpiBCuecPLOgBLQ==}
cpu: [ppc64]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-riscv64-gnu@4.48.0':
resolution: {integrity: sha512-YBwXsvsFI8CVA4ej+bJF2d9uAeIiSkqKSPQNn0Wyh4eMDY4wxuSp71BauPjQNCKK2tD2/ksJ7uhJ8X/PVY9bHQ==}
cpu: [riscv64]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-riscv64-musl@4.48.0':
resolution: {integrity: sha512-FI3Rr2aGAtl1aHzbkBIamsQyuauYtTF9SDUJ8n2wMXuuxwchC3QkumZa1TEXYIv/1AUp1a25Kwy6ONArvnyeVQ==}
cpu: [riscv64]
os: [linux]
libc: [musl]
'@rollup/rollup-linux-s390x-gnu@4.48.0':
resolution: {integrity: sha512-Dx7qH0/rvNNFmCcIRe1pyQ9/H0XO4v/f0SDoafwRYwc2J7bJZ5N4CHL/cdjamISZ5Cgnon6iazAVRFlxSoHQnQ==}
cpu: [s390x]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-x64-gnu@4.48.0':
resolution: {integrity: sha512-GUdZKTeKBq9WmEBzvFYuC88yk26vT66lQV8D5+9TgkfbewhLaTHRNATyzpQwwbHIfJvDJ3N9WJ90wK/uR3cy3Q==}
cpu: [x64]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-x64-musl@4.48.0':
resolution: {integrity: sha512-ao58Adz/v14MWpQgYAb4a4h3fdw73DrDGtaiF7Opds5wNyEQwtO6M9dBh89nke0yoZzzaegq6J/EXs7eBebG8A==}
cpu: [x64]
os: [linux]
libc: [musl]
'@rollup/rollup-win32-arm64-msvc@4.48.0':
resolution: {integrity: sha512-kpFno46bHtjZVdRIOxqaGeiABiToo2J+st7Yce+aiAoo1H0xPi2keyQIP04n2JjDVuxBN6bSz9R6RdTK5hIppw==}
@@ -761,6 +797,15 @@ packages:
'@types/json-schema@7.0.15':
resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
'@types/katex@0.16.7':
resolution: {integrity: sha512-HMwFiRujE5PjrgwHQ25+bsLJgowjGjm5Z8FVSf0N6PwgJrwxH0QxzHYDcKsTfV3wva0vzrpqMTJS2jXPr5BMEQ==}
'@types/lodash-es@4.17.12':
resolution: {integrity: sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==}
'@types/lodash@4.17.20':
resolution: {integrity: sha512-H3MHACvFUEiujabxhaI/ImO6gUrd8oOurg7LQtS7mbwIXA/cUqWrvBsaeJ23aZEPk1TAYkurjfMbSELfoCXlGA==}
'@types/mdast@4.0.4':
resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==}
@@ -969,6 +1014,9 @@ packages:
argparse@2.0.1:
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
async-validator@4.2.5:
resolution: {integrity: sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==}
balanced-match@1.0.2:
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
@@ -1071,14 +1119,28 @@ packages:
resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
engines: {node: '>= 8'}
css-render@0.15.14:
resolution: {integrity: sha512-9nF4PdUle+5ta4W5SyZdLCCmFd37uVimSjg1evcTqKJCyvCEEj12WKzOSBNak6r4im4J4iYXKH1OWpUV5LBYFg==}
cssesc@3.0.0:
resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==}
engines: {node: '>=4'}
hasBin: true
csstype@3.0.11:
resolution: {integrity: sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw==}
csstype@3.1.3:
resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
date-fns-tz@3.2.0:
resolution: {integrity: sha512-sg8HqoTEulcbbbVXeg84u5UnlsQa8GS5QXMqjjYIhS4abEVVKIUwe0/l/UhrZdKaL/W5eWZNlbTeEIiOXTcsBQ==}
peerDependencies:
date-fns: ^3.0.0 || ^4.0.0
date-fns@3.6.0:
resolution: {integrity: sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==}
de-indent@1.0.2:
resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==}
@@ -1359,6 +1421,9 @@ packages:
resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
engines: {node: '>=0.10.0'}
evtd@0.2.4:
resolution: {integrity: sha512-qaeGN5bx63s/AXgQo8gj6fBkxge+OoLddLniox5qtLAEY5HSnuSlISXVPxnSae1dWblvTh4/HoMIB+mbMsvZzw==}
exsolve@1.0.7:
resolution: {integrity: sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw==}
@@ -1472,6 +1537,10 @@ packages:
resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==}
hasBin: true
highlight.js@11.11.1:
resolution: {integrity: sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==}
engines: {node: '>=12.0.0'}
iconv-lite@0.6.3:
resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==}
engines: {node: '>=0.10.0'}
@@ -1596,6 +1665,9 @@ packages:
resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
engines: {node: '>=10'}
lodash-es@4.17.21:
resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==}
lodash.merge@4.6.2:
resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
@@ -1774,6 +1846,11 @@ packages:
muggle-string@0.4.1:
resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==}
naive-ui@2.42.0:
resolution: {integrity: sha512-c7cXR2YgOjgtBadXHwiWL4Y0tpGLAI5W5QzzHksOi22iuHXoSGMAzdkVTGVPE/PM0MSGQ/JtUIzCx2Y0hU0vTQ==}
peerDependencies:
vue: ^3.0.0
nanoid@3.3.11:
resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==}
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
@@ -1974,6 +2051,9 @@ packages:
scule@1.3.0:
resolution: {integrity: sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==}
seemly@0.3.10:
resolution: {integrity: sha512-2+SMxtG1PcsL0uyhkumlOU6Qo9TAQ/WyH7tthnPIOQB05/12jz9naq6GZ6iZ6ApVsO3rr2gsnTf3++OV63kE1Q==}
semver@5.7.2:
resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==}
hasBin: true
@@ -2057,6 +2137,9 @@ packages:
resolution: {integrity: sha512-khrZo4buq4qVmsGzS5yQjKe/WsFvV8fGfOjDQN0q4iy9FjRfPWRgTFrU8u1R2iu/SfWLhY9WnCi4Jhdrcbtg+g==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
treemate@0.3.11:
resolution: {integrity: sha512-M8RGFoKtZ8dF+iwJfAJTOH/SM4KluKOKRJpjCMhI8bG3qB74zrFoArKZ62ll0Fr3mqkMJiQOmWYkdYgDeITYQg==}
ts-api-utils@2.1.0:
resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==}
engines: {node: '>=18.12'}
@@ -2156,6 +2239,11 @@ packages:
util-deprecate@1.0.2:
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
vdirs@0.1.8:
resolution: {integrity: sha512-H9V1zGRLQZg9b+GdMk8MXDN2Lva0zx72MPahDKc30v+DtwKjfyOSXWRIX4t2mhDubM1H09gPhWeth/BJWPHGUw==}
peerDependencies:
vue: ^3.0.11
vite@7.1.3:
resolution: {integrity: sha512-OOUi5zjkDxYrKhTV3V7iKsoS37VUM7v40+HuwEmcrsf11Cdx9y3DIr2Px6liIcZFwt3XSRpQvFpL3WVy7ApkGw==}
engines: {node: ^20.19.0 || >=22.12.0}
@@ -2196,6 +2284,11 @@ packages:
yaml:
optional: true
vooks@0.2.12:
resolution: {integrity: sha512-iox0I3RZzxtKlcgYaStQYKEzWWGAduMmq+jS7OrNdQo1FgGfPMubGL3uGHOU9n97NIvfFDBGnpSvkWyb/NSn/Q==}
peerDependencies:
vue: ^3.0.0
vscode-uri@3.1.0:
resolution: {integrity: sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==}
@@ -2219,6 +2312,11 @@ packages:
typescript:
optional: true
vueuc@0.4.64:
resolution: {integrity: sha512-wlJQj7fIwKK2pOEoOq4Aro8JdPOGpX8aWQhV8YkTW9OgWD2uj2O8ANzvSsIGjx7LTOc7QbS7sXdxHi6XvRnHPA==}
peerDependencies:
vue: ^3.0.11
webpack-virtual-modules@0.6.2:
resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==}
@@ -2492,12 +2590,22 @@ snapshots:
picocolors: 1.1.1
sisteransi: 1.0.5
'@css-render/plugin-bem@0.15.14(css-render@0.15.14)':
dependencies:
css-render: 0.15.14
'@css-render/vue3-ssr@0.15.14(vue@3.5.19(typescript@5.8.3))':
dependencies:
vue: 3.5.19(typescript@5.8.3)
'@dprint/formatter@0.3.0': {}
'@dprint/markdown@0.17.8': {}
'@dprint/toml@0.6.4': {}
'@emotion/hash@0.8.0': {}
'@es-joy/jsdoccomment@0.50.2':
dependencies:
'@types/estree': 1.0.8
@@ -2699,6 +2807,8 @@ snapshots:
'@jridgewell/resolve-uri': 3.1.2
'@jridgewell/sourcemap-codec': 1.5.5
'@juggle/resize-observer@3.4.0': {}
'@nodelib/fs.scandir@2.1.5':
dependencies:
'@nodelib/fs.stat': 2.0.5
@@ -2858,6 +2968,14 @@ snapshots:
'@types/json-schema@7.0.15': {}
'@types/katex@0.16.7': {}
'@types/lodash-es@4.17.12':
dependencies:
'@types/lodash': 4.17.20
'@types/lodash@4.17.20': {}
'@types/mdast@4.0.4':
dependencies:
'@types/unist': 3.0.3
@@ -3138,6 +3256,8 @@ snapshots:
argparse@2.0.1: {}
async-validator@4.2.5: {}
balanced-match@1.0.2: {}
binary-extensions@2.3.0: {}
@@ -3236,10 +3356,23 @@ snapshots:
shebang-command: 2.0.0
which: 2.0.2
css-render@0.15.14:
dependencies:
'@emotion/hash': 0.8.0
csstype: 3.0.11
cssesc@3.0.0: {}
csstype@3.0.11: {}
csstype@3.1.3: {}
date-fns-tz@3.2.0(date-fns@3.6.0):
dependencies:
date-fns: 3.6.0
date-fns@3.6.0: {}
de-indent@1.0.2: {}
debug@4.4.1:
@@ -3609,6 +3742,8 @@ snapshots:
esutils@2.0.3: {}
evtd@0.2.4: {}
exsolve@1.0.7: {}
fast-deep-equal@3.1.3: {}
@@ -3698,6 +3833,8 @@ snapshots:
he@1.2.0: {}
highlight.js@11.11.1: {}
iconv-lite@0.6.3:
dependencies:
safer-buffer: 2.1.2
@@ -3807,6 +3944,8 @@ snapshots:
dependencies:
p-locate: 5.0.0
lodash-es@4.17.21: {}
lodash.merge@4.6.2: {}
lodash@4.17.21: {}
@@ -4171,6 +4310,29 @@ snapshots:
muggle-string@0.4.1: {}
naive-ui@2.42.0(vue@3.5.19(typescript@5.8.3)):
dependencies:
'@css-render/plugin-bem': 0.15.14(css-render@0.15.14)
'@css-render/vue3-ssr': 0.15.14(vue@3.5.19(typescript@5.8.3))
'@types/katex': 0.16.7
'@types/lodash': 4.17.20
'@types/lodash-es': 4.17.12
async-validator: 4.2.5
css-render: 0.15.14
csstype: 3.1.3
date-fns: 3.6.0
date-fns-tz: 3.2.0(date-fns@3.6.0)
evtd: 0.2.4
highlight.js: 11.11.1
lodash: 4.17.21
lodash-es: 4.17.21
seemly: 0.3.10
treemate: 0.3.11
vdirs: 0.1.8(vue@3.5.19(typescript@5.8.3))
vooks: 0.2.12(vue@3.5.19(typescript@5.8.3))
vue: 3.5.19(typescript@5.8.3)
vueuc: 0.4.64(vue@3.5.19(typescript@5.8.3))
nanoid@3.3.11: {}
natural-compare@1.4.0: {}
@@ -4370,6 +4532,8 @@ snapshots:
scule@1.3.0: {}
seemly@0.3.10: {}
semver@5.7.2:
optional: true
@@ -4439,6 +4603,8 @@ snapshots:
dependencies:
eslint-visitor-keys: 3.4.3
treemate@0.3.11: {}
ts-api-utils@2.1.0(typescript@5.8.3):
dependencies:
typescript: 5.8.3
@@ -4557,6 +4723,11 @@ snapshots:
util-deprecate@1.0.2: {}
vdirs@0.1.8(vue@3.5.19(typescript@5.8.3)):
dependencies:
evtd: 0.2.4
vue: 3.5.19(typescript@5.8.3)
vite@7.1.3(@types/node@24.3.0)(less@4.4.1)(sass@1.90.0)(tsx@4.20.5)(yaml@2.8.1):
dependencies:
esbuild: 0.25.9
@@ -4573,6 +4744,11 @@ snapshots:
tsx: 4.20.5
yaml: 2.8.1
vooks@0.2.12(vue@3.5.19(typescript@5.8.3)):
dependencies:
evtd: 0.2.4
vue: 3.5.19(typescript@5.8.3)
vscode-uri@3.1.0: {}
vue-eslint-parser@10.2.0(eslint@9.34.0):
@@ -4603,6 +4779,17 @@ snapshots:
optionalDependencies:
typescript: 5.8.3
vueuc@0.4.64(vue@3.5.19(typescript@5.8.3)):
dependencies:
'@css-render/vue3-ssr': 0.15.14(vue@3.5.19(typescript@5.8.3))
'@juggle/resize-observer': 3.4.0
css-render: 0.15.14
evtd: 0.2.4
seemly: 0.3.10
vdirs: 0.1.8(vue@3.5.19(typescript@5.8.3))
vooks: 0.2.12(vue@3.5.19(typescript@5.8.3))
vue: 3.5.19(typescript@5.8.3)
webpack-virtual-modules@0.6.2: {}
which@2.0.2:
+39 -23
View File
@@ -1,31 +1,47 @@
<script setup lang="ts">
import { NButton, NInput } from 'naive-ui'
</script>
<template>
<div style="display: flex; gap: 12px; flex-wrap: wrap; width: 300px;">
<!-- small 输入框,使用 slot 图标 -->
<AInput size="small" placeholder="Small 输入框">
<template #prefix>
<AIcon icon="material-symbols:10k-outline" />
</template>
<template #suffix>
<AIcon icon="material-symbols:10k-outline" />
</template>
</AInput>
<!-- 外层负责整个页面居中 -->
<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;
"
>
<AButton icon="solar:map-point-search-linear">
ABC
</AButton>
<AButton icon="solar:map-point-search-linear" />
<AInput size="small" placeholder="Small 输入框" password />
<!-- medium 输入框,使用 prop 图标 -->
<AInput
size="medium"
placeholder="Medium 输入框"
prefix-icon="material-symbols:10k-outline"
suffix-icon="material-symbols:10k-outline"
/>
<AInput
size="medium"
placeholder="Medium 输入框"
clearable
/>
<!-- large 输入框,prefix 用 slot,suffix 用 prop -->
<AInput size="large" placeholder="Large 输入框" suffix-icon="material-symbols:10k-outline">
<template #prefix>
<AIcon icon="material-symbols:10k-outline" />
</template>
</AInput>
<AInput
size="large"
placeholder="Large 输入框"
suffix-icon="solar:map-point-search-linear"
>
<template #prefix />
</AInput>
</div>
</div>
</template>
+1
View File
@@ -2,4 +2,5 @@ import { createApp } from 'vue'
import App from './App.vue'
import './style.scss'
createApp(App).mount('#app')
+3 -1
View File
@@ -1,5 +1,7 @@
@use '@ao/ao-design/index.scss' as *;
html,
body {
font-family: 'PingFang SC', 'Microsoft YaHei', 'Helvetica Neue', Arial, sans-serif;
margin: 0;
padding: 0;
}
+95 -26
View File
@@ -3,44 +3,113 @@
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>AntD 风格按钮点击反馈</title>
<title>按钮示例</title>
<!-- 这里使用 Font Awesome CDN,你可以换成自己的图标库 -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" />
<style>
/* 基础按钮样式 */
.btn {
background-color: #1677ff; /* 主色 */
color: #fff;
border: none;
padding: 10px 24px;
font-size: 16px;
border-radius: 6px;
display: inline-flex; /* 内部水平排列 */
align-items: center; /* 垂直居中 */
justify-content: center; /* 水平居中 */
gap: 4px; /* 图标和文字间距 */
padding: 8px 16px;
border: 1px solid #ccc;
border-radius: 4px;
background-color: #f4f5f5;
cursor: pointer;
transition: all 0.15s ease;
position: relative;
outline: none;
font-size: 14px;
color: #333;
font-family: sans-serif;
}
/* hover 高亮 */
.btn:hover:not(:disabled) {
background-color: #4096ff; /* 调亮 */
.btn:hover {
background-color: #eaeaea;
}
/* 按下 active 效果 */
.btn:active:not(:disabled) {
background-color: #0958d9; /* 调暗 */
transform: translateY(1px); /* 轻微下移,模拟按下 */
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15); /* 按下阴影 */
.btn:active {
background-color: #dcdcdc;
}
/* disabled 状态 */
.btn:disabled {
background-color: #cfe0ff;
color: #a0bfff;
cursor: not-allowed;
.btn-icon {
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 16px; /* 控制图标大小 */
}
.btn-text {
display: inline-block;
}
</style>
</head>
<body>
<button class="btn">Primary Button</button>
<button class="btn" disabled>Disabled</button>
<!-- 图标在左 -->
<button class="btn">
<i class="fas fa-check btn-icon"></i>
<span class="btn-text">按钮文字</span>
</button>
<!-- 图标在右 -->
<button class="btn">
<span class="btn-text">按钮文字</span>
<i class="fas fa-arrow-right btn-icon"></i>
</button>
</body>
</html>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>按钮示例</title>
<!-- 这里使用 Font Awesome CDN,你可以换成自己的图标库 -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" />
<style>
.btn {
display: inline-flex; /* 内部水平排列 */
align-items: center; /* 垂直居中 */
justify-content: center; /* 水平居中 */
gap: 4px; /* 图标和文字间距 */
padding: 8px 16px;
border: 1px solid #ccc;
border-radius: 4px;
background-color: #f4f5f5;
cursor: pointer;
font-size: 14px;
color: #333;
}
.btn:hover {
background-color: #eaeaea;
}
.btn:active {
background-color: #dcdcdc;
}
.btn-icon {
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 16px; /* 控制图标大小 */
}
.btn-text {
display: inline-block;
}
</style>
</head>
<body>
<!-- 图标在左 -->
<button class="btn">
<i class="fas fa-check btn-icon"></i>
<span class="btn-text">按钮文字</span>
</button>
<!-- 图标在右 -->
<button class="btn">
<span class="btn-text">按钮文字</span>
<i class="fas fa-arrow-right btn-icon"></i>
</button>
</body>
</html>