mirror of
https://github.com/fawney19/Aether.git
synced 2026-01-03 00:02:28 +08:00
refactor(frontend): 优化 UI 基础组件
- 更新 avatar-image, badge, checkbox, input, switch 等组件 - 优化 dialog, pagination, select-item, tabs 等组件 - 调整 table-card, refresh-button 组件
This commit is contained in:
@@ -19,5 +19,9 @@ const imageClass = computed(() =>
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<AvatarImagePrimitive :class="imageClass" :src="src || ''" :alt="alt" />
|
||||
<AvatarImagePrimitive
|
||||
:class="imageClass"
|
||||
:src="src || ''"
|
||||
:alt="alt"
|
||||
/>
|
||||
</template>
|
||||
|
||||
@@ -3,6 +3,10 @@ import { cva } from 'class-variance-authority'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { computed } from 'vue'
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
variant: 'default',
|
||||
})
|
||||
|
||||
const badgeVariants = cva(
|
||||
'inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2',
|
||||
{
|
||||
@@ -34,10 +38,6 @@ interface Props {
|
||||
class?: string
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
variant: 'default',
|
||||
})
|
||||
|
||||
const badgeClass = computed(() =>
|
||||
cn(badgeVariants({ variant: props.variant }), props.class)
|
||||
)
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
:checked="isChecked"
|
||||
v-bind="$attrs"
|
||||
@change="handleChange"
|
||||
/>
|
||||
>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
<template>
|
||||
<Teleport to="body">
|
||||
<div v-if="isOpen" class="fixed inset-0 overflow-y-auto" :style="{ zIndex: containerZIndex }">
|
||||
<div
|
||||
v-if="isOpen"
|
||||
class="fixed inset-0 overflow-y-auto"
|
||||
:style="{ zIndex: containerZIndex }"
|
||||
>
|
||||
<!-- 背景遮罩 -->
|
||||
<Transition
|
||||
enter-active-class="duration-200 ease-out"
|
||||
@@ -30,21 +34,38 @@
|
||||
>
|
||||
<div
|
||||
v-if="isOpen"
|
||||
@click.stop
|
||||
class="relative transform rounded-lg bg-background text-left shadow-2xl transition-all sm:my-8 sm:w-full border border-border"
|
||||
:style="{ zIndex: contentZIndex }"
|
||||
:class="maxWidthClass"
|
||||
@click.stop
|
||||
>
|
||||
<!-- Header 区域:优先使用 slot,否则使用 title prop -->
|
||||
<slot name="header">
|
||||
<div v-if="title" class="border-b border-border px-6 py-4">
|
||||
<div
|
||||
v-if="title"
|
||||
class="border-b border-border px-6 py-4"
|
||||
>
|
||||
<div class="flex items-center gap-3">
|
||||
<div v-if="icon" class="flex h-9 w-9 items-center justify-center rounded-lg bg-primary/10 flex-shrink-0" :class="iconClass">
|
||||
<component :is="icon" class="h-5 w-5 text-primary" />
|
||||
<div
|
||||
v-if="icon"
|
||||
class="flex h-9 w-9 items-center justify-center rounded-lg bg-primary/10 flex-shrink-0"
|
||||
:class="iconClass"
|
||||
>
|
||||
<component
|
||||
:is="icon"
|
||||
class="h-5 w-5 text-primary"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex-1 min-w-0">
|
||||
<h3 class="text-lg font-semibold text-foreground leading-tight">{{ title }}</h3>
|
||||
<p v-if="description" class="text-xs text-muted-foreground">{{ description }}</p>
|
||||
<h3 class="text-lg font-semibold text-foreground leading-tight">
|
||||
{{ title }}
|
||||
</h3>
|
||||
<p
|
||||
v-if="description"
|
||||
class="text-xs text-muted-foreground"
|
||||
>
|
||||
{{ description }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
:autocomplete="autocompleteAttr"
|
||||
v-bind="$attrs"
|
||||
@input="handleInput"
|
||||
/>
|
||||
>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
@@ -48,7 +48,10 @@
|
||||
</Button>
|
||||
|
||||
<!-- 页码按钮(智能省略) -->
|
||||
<template v-for="page in pageNumbers" :key="page">
|
||||
<template
|
||||
v-for="page in pageNumbers"
|
||||
:key="page"
|
||||
>
|
||||
<Button
|
||||
v-if="typeof page === 'number'"
|
||||
:variant="page === current ? 'default' : 'outline'"
|
||||
@@ -59,7 +62,10 @@
|
||||
>
|
||||
{{ page }}
|
||||
</Button>
|
||||
<span v-else class="px-2 text-muted-foreground select-none">{{ page }}</span>
|
||||
<span
|
||||
v-else
|
||||
class="px-2 text-muted-foreground select-none"
|
||||
>{{ page }}</span>
|
||||
</template>
|
||||
|
||||
<Button
|
||||
|
||||
@@ -7,7 +7,10 @@
|
||||
:title="title"
|
||||
@click="handleClick"
|
||||
>
|
||||
<RefreshCcw class="w-3.5 h-3.5" :class="loading ? 'animate-spin' : ''" />
|
||||
<RefreshCcw
|
||||
class="w-3.5 h-3.5"
|
||||
:class="loading ? 'animate-spin' : ''"
|
||||
/>
|
||||
</Button>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -21,7 +21,11 @@ const itemClass = computed(() =>
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<SelectItemPrimitive :class="itemClass" :value="value" :disabled="disabled">
|
||||
<SelectItemPrimitive
|
||||
:class="itemClass"
|
||||
:value="value"
|
||||
:disabled="disabled"
|
||||
>
|
||||
<span class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
||||
<SelectItemIndicator>
|
||||
<Check class="h-4 w-4" />
|
||||
|
||||
@@ -3,15 +3,15 @@
|
||||
type="button"
|
||||
role="switch"
|
||||
:aria-checked="modelValue"
|
||||
class="relative inline-flex h-6 w-11 items-center rounded-full transition-colors"
|
||||
:class="[
|
||||
'relative inline-flex h-6 w-11 items-center rounded-full transition-colors',
|
||||
modelValue ? 'bg-primary' : 'bg-muted'
|
||||
]"
|
||||
@click="$emit('update:modelValue', !modelValue)"
|
||||
>
|
||||
<span
|
||||
class="inline-block h-4 w-4 transform rounded-full bg-white transition-transform"
|
||||
:class="[
|
||||
'inline-block h-4 w-4 transform rounded-full bg-white transition-transform',
|
||||
modelValue ? 'translate-x-6' : 'translate-x-1'
|
||||
]"
|
||||
/>
|
||||
|
||||
@@ -1,14 +1,22 @@
|
||||
<template>
|
||||
<Card class="overflow-hidden">
|
||||
<!-- 标题和操作栏 -->
|
||||
<div v-if="$slots.header || title" class="px-6 py-3.5 border-b border-border/60">
|
||||
<div
|
||||
v-if="$slots.header || title"
|
||||
class="px-6 py-3.5 border-b border-border/60"
|
||||
>
|
||||
<slot name="header">
|
||||
<div class="flex items-center justify-between gap-4">
|
||||
<!-- 左侧:标题 -->
|
||||
<h3 class="text-base font-semibold">{{ title }}</h3>
|
||||
<h3 class="text-base font-semibold">
|
||||
{{ title }}
|
||||
</h3>
|
||||
|
||||
<!-- 右侧:操作区 -->
|
||||
<div v-if="$slots.actions" class="flex items-center gap-2">
|
||||
<div
|
||||
v-if="$slots.actions"
|
||||
class="flex items-center gap-2"
|
||||
>
|
||||
<slot name="actions" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
<template>
|
||||
<div :class="listClass" ref="listRef">
|
||||
<div
|
||||
ref="listRef"
|
||||
:class="listClass"
|
||||
>
|
||||
<!-- 滑动指示器 - 放在按钮前面 -->
|
||||
<div
|
||||
class="tabs-indicator"
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
:class="triggerClass"
|
||||
:data-state="isActive ? 'active' : 'inactive'"
|
||||
:data-value="props.value"
|
||||
@click="handleClick"
|
||||
type="button"
|
||||
@click="handleClick"
|
||||
>
|
||||
<slot />
|
||||
</button>
|
||||
|
||||
Reference in New Issue
Block a user