-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ toast.message }}
+
+
+
+
+
+
+
-
-
-
-
-
+
+
diff --git a/src/main.ts b/src/main.ts
index fe5bae39..72293083 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -1,5 +1,8 @@
import { createApp } from 'vue'
import App from './App.vue'
import './style.css'
+import ToastPlugin from './plugins/toast'
-createApp(App).mount('#app')
+createApp(App)
+ .use(ToastPlugin)
+ .mount('#app')
diff --git a/src/plugins/toast.ts b/src/plugins/toast.ts
new file mode 100644
index 00000000..0fff4f63
--- /dev/null
+++ b/src/plugins/toast.ts
@@ -0,0 +1,125 @@
+import { App, reactive } from 'vue'
+
+export interface ToastOptions
+{
+ message: string
+ type?: 'success' | 'error' | 'warning' | 'info'
+ duration?: number
+ showProgress?: boolean
+}
+
+interface ToastItem
+ extends ToastOptions
+{
+ id: number
+ show: boolean
+}
+
+export class ToastManager
+{
+ private toasts = reactive
([])
+ private toastId = 0
+
+ show(options: ToastOptions | string)
+ {
+ const config: ToastOptions = typeof options === 'string'
+ ? { message: options }
+ : options
+
+ // 清除现有的 toast
+ if (this.toasts.length > 0) {
+ this.toasts.splice(0, this.toasts.length)
+ }
+
+ const toast: ToastItem = {
+ id: ++this.toastId,
+ show: true,
+ type: 'success',
+ duration: 3000,
+ showProgress: true,
+ ...config
+ }
+
+ this.toasts.push(toast)
+
+ // 自动移除
+ if (toast.duration as number > 0) {
+ setTimeout(() => {
+ this.remove(toast.id)
+ }, toast.duration)
+ }
+
+ return toast.id
+ }
+
+ success(message: string, options?: Partial)
+ {
+ return this.show({ message, type: 'success', ...options })
+ }
+
+ error(message: string, options?: Partial)
+ {
+ return this.show({ message, type: 'error', ...options })
+ }
+
+ warning(message: string, options?: Partial)
+ {
+ return this.show({ message, type: 'warning', ...options })
+ }
+
+ info(message: string, options?: Partial)
+ {
+ return this.show({ message, type: 'info', ...options })
+ }
+
+ remove(id: number)
+ {
+ const index = this.toasts.findIndex(toast => toast.id === id)
+ if (index > -1) {
+ this.toasts[index].show = false
+ // 等待动画完成后移除
+ setTimeout(() => {
+ const currentIndex = this.toasts.findIndex(toast => toast.id === id)
+ if (currentIndex > -1) {
+ this.toasts.splice(currentIndex, 1)
+ }
+ }, 300)
+ }
+ }
+
+ clear()
+ {
+ this.toasts.forEach(toast => {
+ toast.show = false
+ })
+ setTimeout(() => {
+ this.toasts.length = 0
+ }, 300)
+ }
+
+ getToasts()
+ {
+ return this.toasts
+ }
+}
+
+const toastManager = new ToastManager()
+
+export const ToastPlugin = {
+ install(app: App)
+ {
+ // 全局属性
+ app.config.globalProperties.$toast = toastManager
+ app.config.globalProperties.$Toast = toastManager
+
+ // 提供/注入
+ app.provide('toast', toastManager)
+ app.provide('$toast', toastManager)
+ }
+}
+
+export const useToast = () => toastManager
+export const $toast = toastManager
+
+// 默认导出
+export default ToastPlugin
diff --git a/src/types/global.d.ts b/src/types/global.d.ts
new file mode 100644
index 00000000..9cb1be2c
--- /dev/null
+++ b/src/types/global.d.ts
@@ -0,0 +1,10 @@
+import { ToastManager } from '../plugins/toast.ts'
+
+declare module '@vue/runtime-core'
+{
+ interface ComponentCustomProperties
+ {
+ $toast: ToastManager
+ $Toast: ToastManager
+ }
+}
diff --git a/src/ui/Select.vue b/src/ui/Select.vue
index 3762827f..1674235f 100644
--- a/src/ui/Select.vue
+++ b/src/ui/Select.vue
@@ -9,22 +9,27 @@
@keydown.arrow-down.prevent="openDropdown"
@keydown.arrow-up.prevent="openDropdown"
:class="[
- 'relative w-full cursor-pointer rounded-lg border bg-white py-1 pl-3 pr-10 text-left shadow-sm transition-all duration-200',
+ 'relative w-full cursor-pointer rounded-lg border bg-white py-1 pl-3 pr-10 text-left transition-all duration-200 ring-1 ring-blue-200',
disabled ? 'cursor-not-allowed bg-gray-50 text-gray-400' : 'hover:border-gray-400',
...buttonClasses
]"
:disabled="disabled"
:aria-expanded="isOpen"
:aria-haspopup="true"
+ :style="{
+ border: 'none !important',
+ outline: 'none !important',
+ boxShadow: '0 0 0 1px rgb(147 197 253)'
+ }"
role="combobox">
{{ selectedLabel || placeholder }}
-
-
+
+
@@ -40,7 +45,7 @@
@before-leave="$emit('before-close')"
@after-leave="$emit('after-close')">
@@ -94,7 +99,7 @@