@aquiferre/ui-kit 0.1.4 → 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/components/ConfirmDialog.vue +3 -1
- package/components/Modal.vue +79 -0
- package/composables/useToast.ts +22 -4
- package/index.ts +1 -0
- package/package.json +9 -4
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
<p v-if="content" class="text-text-secondary text-sm mt-2">{{ content }}</p>
|
|
10
10
|
</div>
|
|
11
11
|
<div class="dialog-actions px-6 py-4 border-t border-border-primary bg-bg-secondary/50 rounded-b-2xl">
|
|
12
|
-
<button class="btn-secondary" @click="cancel">
|
|
12
|
+
<button v-if="showCancel" class="btn-secondary" @click="cancel">
|
|
13
13
|
{{ cancelText }}
|
|
14
14
|
</button>
|
|
15
15
|
<button :disabled="loading" :class="confirmClass" @click="confirm">
|
|
@@ -29,6 +29,7 @@ withDefaults(defineProps<{
|
|
|
29
29
|
content?: string
|
|
30
30
|
confirmText?: string
|
|
31
31
|
cancelText?: string
|
|
32
|
+
showCancel?: boolean
|
|
32
33
|
loading?: boolean
|
|
33
34
|
loadingText?: string
|
|
34
35
|
confirmClass?: string
|
|
@@ -36,6 +37,7 @@ withDefaults(defineProps<{
|
|
|
36
37
|
title: '确认删除',
|
|
37
38
|
confirmText: '确认删除',
|
|
38
39
|
cancelText: '取消',
|
|
40
|
+
showCancel: true,
|
|
39
41
|
loadingText: '删除中...',
|
|
40
42
|
confirmClass: 'btn-danger',
|
|
41
43
|
})
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<Teleport to="body">
|
|
3
|
+
<Transition name="fade">
|
|
4
|
+
<div v-if="visible" class="dialog-overlay">
|
|
5
|
+
<div class="fixed inset-0 bg-bg-overlay" @click="onOverlayClick" />
|
|
6
|
+
<div class="panel-glass dialog" :class="[sizeClass, customClass]">
|
|
7
|
+
<div v-if="title" class="flex items-center justify-between px-6 py-4 border-b border-border-primary">
|
|
8
|
+
<h3 class="text-lg font-medium text-text-primary">{{ title }}</h3>
|
|
9
|
+
<button
|
|
10
|
+
v-if="closable"
|
|
11
|
+
class="text-text-tertiary hover:text-text-primary text-2xl leading-none"
|
|
12
|
+
@click="close"
|
|
13
|
+
>×</button>
|
|
14
|
+
</div>
|
|
15
|
+
<div class="px-6 py-4">
|
|
16
|
+
<slot />
|
|
17
|
+
</div>
|
|
18
|
+
<div v-if="$slots.footer" class="px-6 py-4 border-t border-border-primary bg-bg-secondary/50 rounded-b-2xl">
|
|
19
|
+
<slot name="footer" />
|
|
20
|
+
</div>
|
|
21
|
+
</div>
|
|
22
|
+
</div>
|
|
23
|
+
</Transition>
|
|
24
|
+
</Teleport>
|
|
25
|
+
</template>
|
|
26
|
+
|
|
27
|
+
<script setup lang="ts">
|
|
28
|
+
import { computed } from 'vue'
|
|
29
|
+
|
|
30
|
+
const props = withDefaults(defineProps<{
|
|
31
|
+
visible: boolean
|
|
32
|
+
title?: string
|
|
33
|
+
size?: 'sm' | 'md' | 'lg' | 'xl'
|
|
34
|
+
closable?: boolean
|
|
35
|
+
maskClosable?: boolean
|
|
36
|
+
customClass?: string
|
|
37
|
+
}>(), {
|
|
38
|
+
closable: true,
|
|
39
|
+
maskClosable: true,
|
|
40
|
+
size: 'md',
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
const emit = defineEmits<{
|
|
44
|
+
'update:visible': [value: boolean]
|
|
45
|
+
close: []
|
|
46
|
+
}>()
|
|
47
|
+
|
|
48
|
+
const sizeClass = computed(() => {
|
|
49
|
+
const map: Record<string, string> = {
|
|
50
|
+
sm: 'max-w-sm',
|
|
51
|
+
md: 'max-w-lg',
|
|
52
|
+
lg: 'max-w-2xl',
|
|
53
|
+
xl: 'max-w-4xl',
|
|
54
|
+
}
|
|
55
|
+
return map[props.size] || 'max-w-lg'
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
function close() {
|
|
59
|
+
emit('update:visible', false)
|
|
60
|
+
emit('close')
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function onOverlayClick() {
|
|
64
|
+
if (props.maskClosable) {
|
|
65
|
+
close()
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
</script>
|
|
69
|
+
|
|
70
|
+
<style scoped>
|
|
71
|
+
.fade-enter-active,
|
|
72
|
+
.fade-leave-active {
|
|
73
|
+
transition: opacity 0.2s ease;
|
|
74
|
+
}
|
|
75
|
+
.fade-enter-from,
|
|
76
|
+
.fade-leave-to {
|
|
77
|
+
opacity: 0;
|
|
78
|
+
}
|
|
79
|
+
</style>
|
package/composables/useToast.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ref } from 'vue'
|
|
1
|
+
import { ref, type Ref } from 'vue'
|
|
2
2
|
import type { ToastType } from '../types'
|
|
3
3
|
|
|
4
4
|
interface ToastItem {
|
|
@@ -7,20 +7,38 @@ interface ToastItem {
|
|
|
7
7
|
message: string
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
// 在全局对象上存储 toast 状态,确保模块重复加载时也能共享状态
|
|
11
|
+
function getGlobalToasts(): Ref<ToastItem[]> {
|
|
12
|
+
const w = window as any
|
|
13
|
+
if (!w.__TOAST_STATE__) {
|
|
14
|
+
w.__TOAST_STATE__ = ref<ToastItem[]>([])
|
|
15
|
+
}
|
|
16
|
+
return w.__TOAST_STATE__
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function getGlobalNextId(): { value: number } {
|
|
20
|
+
const w = window as any
|
|
21
|
+
if (!w.__TOAST_NEXT_ID__) {
|
|
22
|
+
w.__TOAST_NEXT_ID__ = { value: 0 }
|
|
23
|
+
}
|
|
24
|
+
return w.__TOAST_NEXT_ID__
|
|
25
|
+
}
|
|
12
26
|
|
|
13
27
|
function add(type: ToastType, message: string) {
|
|
14
|
-
const
|
|
28
|
+
const toasts = getGlobalToasts()
|
|
29
|
+
const nextId = getGlobalNextId()
|
|
30
|
+
const id = nextId.value++
|
|
15
31
|
toasts.value.push({ id, type, message })
|
|
16
32
|
setTimeout(() => remove(id), 3000)
|
|
17
33
|
}
|
|
18
34
|
|
|
19
35
|
function remove(id: number) {
|
|
36
|
+
const toasts = getGlobalToasts()
|
|
20
37
|
toasts.value = toasts.value.filter(t => t.id !== id)
|
|
21
38
|
}
|
|
22
39
|
|
|
23
40
|
export function useToast() {
|
|
41
|
+
const toasts = getGlobalToasts()
|
|
24
42
|
return {
|
|
25
43
|
toasts,
|
|
26
44
|
success: (msg: string) => add('success', msg),
|
package/index.ts
CHANGED
|
@@ -6,6 +6,7 @@ export { default as SearchInput } from './components/SearchInput.vue'
|
|
|
6
6
|
export { default as FilterDropdown } from './components/FilterDropdown.vue'
|
|
7
7
|
export { default as FormDialog } from './components/FormDialog.vue'
|
|
8
8
|
export { default as ConfirmDialog } from './components/ConfirmDialog.vue'
|
|
9
|
+
export { default as Modal } from './components/Modal.vue'
|
|
9
10
|
export { default as Toast } from './components/Toast.vue'
|
|
10
11
|
export { default as StatusTag } from './components/StatusTag.vue'
|
|
11
12
|
export { default as FileUploader } from './components/FileUploader.vue'
|
package/package.json
CHANGED
|
@@ -1,17 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aquiferre/ui-kit",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.ts",
|
|
7
7
|
"exports": {
|
|
8
8
|
".": "./index.ts"
|
|
9
9
|
},
|
|
10
|
-
"files": [
|
|
10
|
+
"files": [
|
|
11
|
+
"*.ts",
|
|
12
|
+
"*.vue",
|
|
13
|
+
"components/**/*.vue",
|
|
14
|
+
"composables/**/*.ts"
|
|
15
|
+
],
|
|
11
16
|
"peerDependencies": {
|
|
12
|
-
"
|
|
17
|
+
"@aquiferre/theme-kit": ">=0.1.0",
|
|
13
18
|
"pinia": ">=2.0.0",
|
|
14
|
-
"
|
|
19
|
+
"vue": ">=3.0.0"
|
|
15
20
|
},
|
|
16
21
|
"publishConfig": {
|
|
17
22
|
"access": "public"
|