vue-chat-kit 0.3.9 → 0.3.11
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/dist/vue-chat-kit.css +1 -1
- package/dist/vue-chat-kit.es.js +4420 -2877
- package/dist/vue-chat-kit.umd.js +1 -1
- package/package.json +1 -1
- package/src/components/AvatarCrop.vue +16 -127
- package/src/components/ChatPanel.vue +503 -2818
- package/src/components/EmojiPicker.vue +2 -73
- package/src/components/chat/ChatWindow.vue +177 -0
- package/src/components/chat/ContentList.vue +300 -0
- package/src/components/chat/ContextMenu.vue +32 -0
- package/src/components/chat/GroupSidebar.vue +284 -0
- package/src/components/chat/MainArea.vue +87 -0
- package/src/components/chat/Sidebar.vue +52 -0
- package/src/components/chat/dialogs/AddFriendDialog.vue +62 -0
- package/src/components/chat/dialogs/CreateGroupDialog.vue +86 -0
- package/src/components/chat/dialogs/GroupDetailDialog.vue +132 -0
- package/src/components/ui/Button.vue +190 -0
- package/src/components/ui/Dialog.vue +194 -0
- package/src/components/ui/Empty.vue +66 -0
- package/src/components/ui/Input.vue +166 -0
- package/src/components/ui/Message.vue +186 -0
- package/src/components/ui/MessageBox.vue +143 -0
- package/src/components/ui/MessageManager.vue +92 -0
- package/src/components/ui/Switch.vue +65 -0
- package/src/components/ui/Tag.vue +68 -0
- package/src/components/ui/icons/ArrowDown.vue +5 -0
- package/src/components/ui/icons/ArrowRight.vue +5 -0
- package/src/components/ui/icons/Bell.vue +6 -0
- package/src/components/ui/icons/Camera.vue +6 -0
- package/src/components/ui/icons/ChatDotRound.vue +5 -0
- package/src/components/ui/icons/Check.vue +5 -0
- package/src/components/ui/icons/CircleCheck.vue +6 -0
- package/src/components/ui/icons/Clock.vue +6 -0
- package/src/components/ui/icons/Close.vue +5 -0
- package/src/components/ui/icons/Delete.vue +8 -0
- package/src/components/ui/icons/Edit.vue +6 -0
- package/src/components/ui/icons/Folder.vue +5 -0
- package/src/components/ui/icons/Minus.vue +5 -0
- package/src/components/ui/icons/Monitor.vue +7 -0
- package/src/components/ui/icons/Moon.vue +5 -0
- package/src/components/ui/icons/Picture.vue +7 -0
- package/src/components/ui/icons/Plus.vue +5 -0
- package/src/components/ui/icons/Search.vue +6 -0
- package/src/components/ui/icons/Setting.vue +6 -0
- package/src/components/ui/icons/Sunny.vue +6 -0
- package/src/components/ui/icons/User.vue +6 -0
- package/src/components/ui/icons/UserFilled.vue +6 -0
- package/src/components/ui/icons/Warning.vue +5 -0
- package/src/components/ui/icons/index.js +24 -0
- package/src/components/ui/index.js +10 -0
- package/src/composables/useFriendChat.js +10 -14
- package/src/composables/useGroupChat.js +140 -48
- package/src/composables/useMessage.js +21 -0
- package/src/composables/useMessageBox.js +98 -0
- package/src/composables/useTheme.js +140 -0
- package/src/config/index.js +1 -0
- package/src/const/index.js +1 -0
- package/src/const/theme.js +19 -0
- package/src/core/api.js +13 -2
- package/src/index.js +5 -5
- package/src/styles/_base.scss +38 -0
- package/src/styles/_variables.scss +43 -0
- package/src/styles/components/_add-friend-dialog.scss +45 -0
- package/src/styles/components/_avatar-crop.scss +120 -0
- package/src/styles/components/_chat-panel.scss +546 -0
- package/src/styles/components/_chat-window.scss +239 -0
- package/src/styles/components/_content-list.scss +260 -0
- package/src/styles/components/_context-menu.scss +35 -0
- package/src/styles/components/_create-group-dialog.scss +78 -0
- package/src/styles/components/_dialogs.scss +226 -0
- package/src/styles/components/_emoji-picker.scss +74 -0
- package/src/styles/components/_group-detail-dialog.scss +110 -0
- package/src/styles/components/_group-sidebar.scss +278 -0
- package/src/styles/components/_main-area.scss +94 -0
- package/src/styles/components/_sidebar.scss +83 -0
- package/src/styles/index.scss +18 -0
- package/src/styles/themes/_dark.scss +68 -0
- package/src/styles/themes/_index.scss +7 -0
- package/src/styles/themes/_light.scss +69 -0
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<Dialog
|
|
3
|
+
:model-value="visible"
|
|
4
|
+
@update:model-value="$emit('update:visible', $event)"
|
|
5
|
+
:title="currentSelectGroup?.name"
|
|
6
|
+
width="500px"
|
|
7
|
+
>
|
|
8
|
+
<div class="group-detail-content">
|
|
9
|
+
<div class="group-info-section">
|
|
10
|
+
<div class="group-info-item">
|
|
11
|
+
<span class="info-label">群ID</span>
|
|
12
|
+
<span class="info-value">{{ currentSelectGroup?.groupId }}</span>
|
|
13
|
+
</div>
|
|
14
|
+
<div class="group-info-item">
|
|
15
|
+
<span class="info-label">群主</span>
|
|
16
|
+
<span class="info-value">{{ groupOwnerUsername || currentSelectGroup?.owner }}</span>
|
|
17
|
+
</div>
|
|
18
|
+
<div class="group-info-item">
|
|
19
|
+
<span class="info-label">成员数</span>
|
|
20
|
+
<span class="info-value">{{ groupMemberList.length }}</span>
|
|
21
|
+
</div>
|
|
22
|
+
</div>
|
|
23
|
+
|
|
24
|
+
<div class="group-actions-section" v-if="isGroupOwner">
|
|
25
|
+
<div class="section-header">
|
|
26
|
+
<span class="section-title">群管理</span>
|
|
27
|
+
</div>
|
|
28
|
+
<div class="group-actions-list">
|
|
29
|
+
<Button type="primary" size="small" @click="$emit('edit-info')">
|
|
30
|
+
<template #icon><Edit /></template>编辑群信息
|
|
31
|
+
</Button>
|
|
32
|
+
<Button type="danger" size="small" @click="$emit('delete')">
|
|
33
|
+
<template #icon><Delete /></template>解散群聊
|
|
34
|
+
</Button>
|
|
35
|
+
</div>
|
|
36
|
+
</div>
|
|
37
|
+
|
|
38
|
+
<div class="group-members-section">
|
|
39
|
+
<div class="section-header">
|
|
40
|
+
<span class="section-title">群成员</span>
|
|
41
|
+
<Button size="small" @click="$emit('invite')">
|
|
42
|
+
<template #icon><Plus /></template>邀请成员
|
|
43
|
+
</Button>
|
|
44
|
+
</div>
|
|
45
|
+
<div class="group-members-list">
|
|
46
|
+
<Empty v-if="groupMemberList.length === 0" description="暂无成员" />
|
|
47
|
+
<div
|
|
48
|
+
v-else
|
|
49
|
+
v-for="member in groupMemberList"
|
|
50
|
+
:key="member.id || member.username"
|
|
51
|
+
class="group-member-item"
|
|
52
|
+
>
|
|
53
|
+
<img
|
|
54
|
+
:src="member.avatar ? `${config.api.baseUrl}${member.avatar}` : `https://api.dicebear.com/7.x/avataaars/svg?seed=${member.username}`"
|
|
55
|
+
:alt="member.username"
|
|
56
|
+
class="group-member-avatar"
|
|
57
|
+
/>
|
|
58
|
+
<div class="group-member-info">
|
|
59
|
+
<span class="group-member-name">{{ member.memberNick || member.username }}</span>
|
|
60
|
+
<span v-if="member.memberType === 0 || member.username === groupOwnerUsername || member.username === currentSelectGroup?.owner" class="group-member-badge">群主</span>
|
|
61
|
+
</div>
|
|
62
|
+
<div class="group-member-actions" v-if="isGroupOwner && member.username !== myUsername">
|
|
63
|
+
<Button size="small" link @click="$emit('edit-member-nick', member)">
|
|
64
|
+
改昵称
|
|
65
|
+
</Button>
|
|
66
|
+
<Button size="small" link type="danger" @click="$emit('remove-member', member.username)">
|
|
67
|
+
移除
|
|
68
|
+
</Button>
|
|
69
|
+
<Button size="small" link type="warning" @click="$emit('transfer-owner', member.username)">
|
|
70
|
+
转让
|
|
71
|
+
</Button>
|
|
72
|
+
</div>
|
|
73
|
+
<div class="group-member-actions" v-else-if="member.username === myUsername">
|
|
74
|
+
<Button size="small" link @click="$emit('edit-member-nick', member)">
|
|
75
|
+
改昵称
|
|
76
|
+
</Button>
|
|
77
|
+
</div>
|
|
78
|
+
</div>
|
|
79
|
+
</div>
|
|
80
|
+
</div>
|
|
81
|
+
</div>
|
|
82
|
+
<template #footer>
|
|
83
|
+
<Button @click="$emit('update:visible', false)">关闭</Button>
|
|
84
|
+
<Button type="danger" @click="$emit('quit')">退出群聊</Button>
|
|
85
|
+
</template>
|
|
86
|
+
</Dialog>
|
|
87
|
+
</template>
|
|
88
|
+
|
|
89
|
+
<script setup>
|
|
90
|
+
import { computed } from 'vue'
|
|
91
|
+
import { Edit, Delete, Plus } from '../../ui/icons/index.js'
|
|
92
|
+
import { Dialog, Empty, Button } from '../../ui/index.js'
|
|
93
|
+
|
|
94
|
+
const props = defineProps({
|
|
95
|
+
visible: Boolean,
|
|
96
|
+
currentSelectGroup: Object,
|
|
97
|
+
groupMemberList: Array,
|
|
98
|
+
myUsername: String,
|
|
99
|
+
config: Object,
|
|
100
|
+
groupOwnerUsername: String
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
// 在组件内部直接计算是否群主
|
|
104
|
+
const isGroupOwner = computed(() => {
|
|
105
|
+
console.log('[GroupDetailDialog] 检查群主身份:', {
|
|
106
|
+
myUsername: props.myUsername,
|
|
107
|
+
groupMemberCount: props.groupMemberList?.length
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
if (!props.groupMemberList || props.groupMemberList.length === 0) {
|
|
111
|
+
console.log('[GroupDetailDialog] 无成员列表为空')
|
|
112
|
+
return false
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const currentUserInGroup = props.groupMemberList.find(
|
|
116
|
+
m => m.username === props.myUsername
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
console.log('[GroupDetailDialog] 当前用户在群成员:', currentUserInGroup)
|
|
120
|
+
|
|
121
|
+
const isOwner = currentUserInGroup?.memberType === 0
|
|
122
|
+
|
|
123
|
+
console.log('[GroupDetailDialog] 是否群主:', isOwner)
|
|
124
|
+
return isOwner
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
defineEmits(['update:visible', 'edit-info', 'delete', 'invite', 'edit-member-nick', 'remove-member', 'transfer-owner', 'quit'])
|
|
128
|
+
</script>
|
|
129
|
+
|
|
130
|
+
<style scoped lang="scss">
|
|
131
|
+
@use '../../../styles/components/group-detail-dialog';
|
|
132
|
+
</style>
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<button
|
|
3
|
+
:class="[
|
|
4
|
+
'vc-button',
|
|
5
|
+
`vc-button--${type}`,
|
|
6
|
+
`vc-button--${size}`,
|
|
7
|
+
{
|
|
8
|
+
'vc-button--disabled': disabled || loading,
|
|
9
|
+
'vc-button--loading': loading
|
|
10
|
+
}
|
|
11
|
+
]"
|
|
12
|
+
:disabled="disabled || loading"
|
|
13
|
+
@click="handleClick"
|
|
14
|
+
>
|
|
15
|
+
<span v-if="loading" class="vc-button__loading-icon">
|
|
16
|
+
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
17
|
+
<path d="M12 2C6.48 2 2 6.48 2 12C2 17.52 6.48 22 12 22C17.52 22 22 17.52 22 12C22 10.84 21.78 9.73 21.37 8.72C21.12 8.14 21.41 7.44 21.99 7.19C22.57 6.94 23.27 7.23 23.52 7.81C24.15 9.26 24.5 10.86 24.5 12.5C24.5 19.07 19.07 24.5 12.5 24.5C5.93 24.5 0.5 19.07 0.5 12.5C0.5 5.93 5.93 0.5 12.5 0.5" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
|
18
|
+
</svg>
|
|
19
|
+
</span>
|
|
20
|
+
<span v-if="$slots.icon" class="vc-button__icon">
|
|
21
|
+
<slot name="icon"></slot>
|
|
22
|
+
</span>
|
|
23
|
+
<span v-if="$slots.default" class="vc-button__text">
|
|
24
|
+
<slot></slot>
|
|
25
|
+
</span>
|
|
26
|
+
</button>
|
|
27
|
+
</template>
|
|
28
|
+
|
|
29
|
+
<script setup>
|
|
30
|
+
import { computed } from 'vue'
|
|
31
|
+
|
|
32
|
+
const props = defineProps({
|
|
33
|
+
type: {
|
|
34
|
+
type: String,
|
|
35
|
+
default: 'default',
|
|
36
|
+
validator: (val) => ['default', 'primary', 'success', 'warning', 'danger', 'link'].includes(val)
|
|
37
|
+
},
|
|
38
|
+
size: {
|
|
39
|
+
type: String,
|
|
40
|
+
default: 'default',
|
|
41
|
+
validator: (val) => ['small', 'default', 'large'].includes(val)
|
|
42
|
+
},
|
|
43
|
+
disabled: {
|
|
44
|
+
type: Boolean,
|
|
45
|
+
default: false
|
|
46
|
+
},
|
|
47
|
+
loading: {
|
|
48
|
+
type: Boolean,
|
|
49
|
+
default: false
|
|
50
|
+
}
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
const emit = defineEmits(['click'])
|
|
54
|
+
|
|
55
|
+
const handleClick = (e) => {
|
|
56
|
+
if (!props.disabled && !props.loading) {
|
|
57
|
+
emit('click', e)
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
</script>
|
|
61
|
+
|
|
62
|
+
<style scoped lang="scss">
|
|
63
|
+
.vc-button {
|
|
64
|
+
display: inline-flex;
|
|
65
|
+
align-items: center;
|
|
66
|
+
justify-content: center;
|
|
67
|
+
gap: var(--vck-space-xs);
|
|
68
|
+
padding: var(--vck-space-sm) var(--vck-space);
|
|
69
|
+
font-size: 14px;
|
|
70
|
+
font-weight: 500;
|
|
71
|
+
border: 1px solid var(--vck-border);
|
|
72
|
+
border-radius: var(--vck-radius);
|
|
73
|
+
background-color: var(--vck-bg);
|
|
74
|
+
color: var(--vck-text-primary);
|
|
75
|
+
cursor: pointer;
|
|
76
|
+
transition: all var(--vck-transition-fast);
|
|
77
|
+
line-height: 1;
|
|
78
|
+
user-select: none;
|
|
79
|
+
|
|
80
|
+
&:hover:not(.vc-button--disabled) {
|
|
81
|
+
border-color: var(--vck-primary);
|
|
82
|
+
color: var(--vck-primary);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
&:active:not(.vc-button--disabled) {
|
|
86
|
+
opacity: 0.8;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Types
|
|
90
|
+
&--primary {
|
|
91
|
+
background-color: var(--vck-primary);
|
|
92
|
+
border-color: var(--vck-primary);
|
|
93
|
+
color: var(--vck-text-white);
|
|
94
|
+
|
|
95
|
+
&:hover:not(.vc-button--disabled) {
|
|
96
|
+
background-color: var(--vck-primary-hover);
|
|
97
|
+
border-color: var(--vck-primary-hover);
|
|
98
|
+
color: var(--vck-text-white);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
&--success {
|
|
103
|
+
background-color: var(--vck-success);
|
|
104
|
+
border-color: var(--vck-success);
|
|
105
|
+
color: var(--vck-text-white);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
&--warning {
|
|
109
|
+
background-color: var(--vck-warning);
|
|
110
|
+
border-color: var(--vck-warning);
|
|
111
|
+
color: var(--vck-text-white);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
&--danger {
|
|
115
|
+
background-color: var(--vck-danger);
|
|
116
|
+
border-color: var(--vck-danger);
|
|
117
|
+
color: var(--vck-text-white);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
&--link {
|
|
121
|
+
background-color: transparent;
|
|
122
|
+
border-color: transparent;
|
|
123
|
+
padding: var(--vck-space-xs);
|
|
124
|
+
color: var(--vck-primary);
|
|
125
|
+
|
|
126
|
+
&:hover:not(.vc-button--disabled) {
|
|
127
|
+
color: var(--vck-primary-hover);
|
|
128
|
+
background-color: transparent;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
&.vc-button--danger {
|
|
132
|
+
color: var(--vck-danger);
|
|
133
|
+
|
|
134
|
+
&:hover:not(.vc-button--disabled) {
|
|
135
|
+
color: var(--vck-danger-hover);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
&.vc-button--warning {
|
|
140
|
+
color: var(--vck-warning);
|
|
141
|
+
|
|
142
|
+
&:hover:not(.vc-button--disabled) {
|
|
143
|
+
color: var(--vck-warning-hover);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Sizes
|
|
149
|
+
&--small {
|
|
150
|
+
padding: var(--vck-space-xs) var(--vck-space-sm);
|
|
151
|
+
font-size: 12px;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
&--large {
|
|
155
|
+
padding: var(--vck-space) var(--vck-space-md);
|
|
156
|
+
font-size: 16px;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Disabled
|
|
160
|
+
&--disabled {
|
|
161
|
+
opacity: 0.5;
|
|
162
|
+
cursor: not-allowed;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Loading
|
|
166
|
+
&--loading {
|
|
167
|
+
pointer-events: none;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
&__loading-icon {
|
|
171
|
+
animation: spin 0.8s linear infinite;
|
|
172
|
+
display: inline-flex;
|
|
173
|
+
width: 14px;
|
|
174
|
+
height: 14px;
|
|
175
|
+
|
|
176
|
+
@keyframes spin {
|
|
177
|
+
from {
|
|
178
|
+
transform: rotate(0deg);
|
|
179
|
+
}
|
|
180
|
+
to {
|
|
181
|
+
transform: rotate(360deg);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
&__text {
|
|
187
|
+
display: inline-flex;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
</style>
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<Teleport to="body">
|
|
3
|
+
<transition name="vc-dialog-fade">
|
|
4
|
+
<div v-if="visible" class="vc-dialog__wrapper" @click.self="handleMaskClick">
|
|
5
|
+
<transition name="vc-dialog-zoom">
|
|
6
|
+
<div v-if="visible" class="vc-dialog" :style="dialogStyle">
|
|
7
|
+
<div v-if="showClose || $slots.header" class="vc-dialog__header">
|
|
8
|
+
<span v-if="$slots.header" class="vc-dialog__title">
|
|
9
|
+
<slot name="header">{{ title }}</slot>
|
|
10
|
+
</span>
|
|
11
|
+
<span v-else-if="title" class="vc-dialog__title">
|
|
12
|
+
{{ title }}
|
|
13
|
+
</span>
|
|
14
|
+
<button
|
|
15
|
+
v-if="showClose"
|
|
16
|
+
type="button"
|
|
17
|
+
class="vc-dialog__headerbtn"
|
|
18
|
+
@click="handleClose"
|
|
19
|
+
>
|
|
20
|
+
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" class="vc-dialog__close">
|
|
21
|
+
<path d="M6 6L18 18M18 6L6 18" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
22
|
+
</svg>
|
|
23
|
+
</button>
|
|
24
|
+
</div>
|
|
25
|
+
<div class="vc-dialog__body">
|
|
26
|
+
<slot></slot>
|
|
27
|
+
</div>
|
|
28
|
+
<div v-if="$slots.footer" class="vc-dialog__footer">
|
|
29
|
+
<slot name="footer"></slot>
|
|
30
|
+
</div>
|
|
31
|
+
</div>
|
|
32
|
+
</transition>
|
|
33
|
+
</div>
|
|
34
|
+
</transition>
|
|
35
|
+
</Teleport>
|
|
36
|
+
</template>
|
|
37
|
+
|
|
38
|
+
<script setup>
|
|
39
|
+
import { computed } from 'vue'
|
|
40
|
+
|
|
41
|
+
const props = defineProps({
|
|
42
|
+
modelValue: {
|
|
43
|
+
type: Boolean,
|
|
44
|
+
default: false
|
|
45
|
+
},
|
|
46
|
+
title: {
|
|
47
|
+
type: String,
|
|
48
|
+
default: ''
|
|
49
|
+
},
|
|
50
|
+
width: {
|
|
51
|
+
type: [String, Number],
|
|
52
|
+
default: '50%'
|
|
53
|
+
},
|
|
54
|
+
top: {
|
|
55
|
+
type: [String, Number],
|
|
56
|
+
default: '15vh'
|
|
57
|
+
},
|
|
58
|
+
showClose: {
|
|
59
|
+
type: Boolean,
|
|
60
|
+
default: true
|
|
61
|
+
},
|
|
62
|
+
closeOnClickModal: {
|
|
63
|
+
type: Boolean,
|
|
64
|
+
default: true
|
|
65
|
+
}
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
const emit = defineEmits(['update:modelValue', 'close'])
|
|
69
|
+
|
|
70
|
+
const visible = computed({
|
|
71
|
+
get: () => props.modelValue,
|
|
72
|
+
set: (val) => emit('update:modelValue', val)
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
const dialogStyle = computed(() => ({
|
|
76
|
+
width: typeof props.width === 'number' ? `${props.width}px` : props.width,
|
|
77
|
+
marginTop: typeof props.top === 'number' ? `${props.top}px` : props.top
|
|
78
|
+
}))
|
|
79
|
+
|
|
80
|
+
const handleClose = () => {
|
|
81
|
+
visible.value = false
|
|
82
|
+
emit('close')
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const handleMaskClick = () => {
|
|
86
|
+
if (props.closeOnClickModal) {
|
|
87
|
+
handleClose()
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
</script>
|
|
91
|
+
|
|
92
|
+
<style scoped lang="scss">
|
|
93
|
+
.vc-dialog__wrapper {
|
|
94
|
+
position: fixed;
|
|
95
|
+
top: 0;
|
|
96
|
+
right: 0;
|
|
97
|
+
bottom: 0;
|
|
98
|
+
left: 0;
|
|
99
|
+
overflow: auto;
|
|
100
|
+
margin: 0;
|
|
101
|
+
z-index: 2000;
|
|
102
|
+
background-color: rgba(0, 0, 0, 0.5);
|
|
103
|
+
display: flex;
|
|
104
|
+
align-items: flex-start;
|
|
105
|
+
justify-content: center;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.vc-dialog {
|
|
109
|
+
position: relative;
|
|
110
|
+
margin: 0 auto;
|
|
111
|
+
background-color: var(--vck-bg);
|
|
112
|
+
border-radius: var(--vck-radius-md);
|
|
113
|
+
box-shadow: var(--vck-shadow);
|
|
114
|
+
overflow: hidden;
|
|
115
|
+
backface-visibility: hidden;
|
|
116
|
+
|
|
117
|
+
&__header {
|
|
118
|
+
padding: var(--vck-space-md) var(--vck-space-lg);
|
|
119
|
+
border-bottom: 1px solid var(--vck-border);
|
|
120
|
+
display: flex;
|
|
121
|
+
align-items: center;
|
|
122
|
+
justify-content: space-between;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
&__title {
|
|
126
|
+
font-size: 18px;
|
|
127
|
+
font-weight: 600;
|
|
128
|
+
color: var(--vck-text-primary);
|
|
129
|
+
line-height: 1;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
&__headerbtn {
|
|
133
|
+
display: inline-flex;
|
|
134
|
+
align-items: center;
|
|
135
|
+
justify-content: center;
|
|
136
|
+
width: 24px;
|
|
137
|
+
height: 24px;
|
|
138
|
+
padding: 0;
|
|
139
|
+
margin: 0;
|
|
140
|
+
border: none;
|
|
141
|
+
background: none;
|
|
142
|
+
cursor: pointer;
|
|
143
|
+
color: var(--vck-text-muted);
|
|
144
|
+
transition: color var(--vck-transition-fast);
|
|
145
|
+
|
|
146
|
+
&:hover {
|
|
147
|
+
color: var(--vck-text-primary);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
&__close {
|
|
152
|
+
width: 16px;
|
|
153
|
+
height: 16px;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
&__body {
|
|
157
|
+
padding: var(--vck-space-lg);
|
|
158
|
+
color: var(--vck-text-primary);
|
|
159
|
+
font-size: 14px;
|
|
160
|
+
line-height: 1.6;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
&__footer {
|
|
164
|
+
padding: var(--vck-space-sm) var(--vck-space-lg) var(--vck-space-lg);
|
|
165
|
+
border-top: 1px solid var(--vck-border);
|
|
166
|
+
display: flex;
|
|
167
|
+
align-items: center;
|
|
168
|
+
justify-content: flex-end;
|
|
169
|
+
gap: var(--vck-space-sm);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Transitions
|
|
174
|
+
.vc-dialog-fade-enter-active,
|
|
175
|
+
.vc-dialog-fade-leave-active {
|
|
176
|
+
transition: opacity var(--vck-transition-fast);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
.vc-dialog-fade-enter-from,
|
|
180
|
+
.vc-dialog-fade-leave-to {
|
|
181
|
+
opacity: 0;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
.vc-dialog-zoom-enter-active,
|
|
185
|
+
.vc-dialog-zoom-leave-active {
|
|
186
|
+
transition: transform var(--vck-transition-fast), opacity var(--vck-transition-fast);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
.vc-dialog-zoom-enter-from,
|
|
190
|
+
.vc-dialog-zoom-leave-to {
|
|
191
|
+
opacity: 0;
|
|
192
|
+
transform: scale(0.95);
|
|
193
|
+
}
|
|
194
|
+
</style>
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="vc-empty">
|
|
3
|
+
<div v-if="$slots.image" class="vc-empty__image">
|
|
4
|
+
<slot name="image"></slot>
|
|
5
|
+
</div>
|
|
6
|
+
<div v-else class="vc-empty__image">
|
|
7
|
+
<svg viewBox="0 0 120 120" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
8
|
+
<rect x="10" y="10" width="100" height="80" rx="8" fill="var(--vck-bg-panel)" stroke="var(--vck-border)"/>
|
|
9
|
+
<path d="M30 40H90M30 55H70" stroke="var(--vck-text-muted)" stroke-width="2" stroke-linecap="round"/>
|
|
10
|
+
</svg>
|
|
11
|
+
</div>
|
|
12
|
+
<div v-if="$slots.default" class="vc-empty__description">
|
|
13
|
+
<slot>{{ description }}</slot>
|
|
14
|
+
</div>
|
|
15
|
+
<div v-else-if="description" class="vc-empty__description">
|
|
16
|
+
{{ description }}
|
|
17
|
+
</div>
|
|
18
|
+
<div v-if="$slots.bottom" class="vc-empty__bottom">
|
|
19
|
+
<slot name="bottom"></slot>
|
|
20
|
+
</div>
|
|
21
|
+
</div>
|
|
22
|
+
</template>
|
|
23
|
+
|
|
24
|
+
<script setup>
|
|
25
|
+
const props = defineProps({
|
|
26
|
+
description: {
|
|
27
|
+
type: String,
|
|
28
|
+
default: '暂无数据'
|
|
29
|
+
}
|
|
30
|
+
})
|
|
31
|
+
</script>
|
|
32
|
+
|
|
33
|
+
<style scoped lang="scss">
|
|
34
|
+
.vc-empty {
|
|
35
|
+
display: flex;
|
|
36
|
+
flex-direction: column;
|
|
37
|
+
align-items: center;
|
|
38
|
+
justify-content: center;
|
|
39
|
+
padding: var(--vck-space-lg);
|
|
40
|
+
text-align: center;
|
|
41
|
+
|
|
42
|
+
&__image {
|
|
43
|
+
width: 120px;
|
|
44
|
+
height: 80px;
|
|
45
|
+
margin-bottom: var(--vck-space-md);
|
|
46
|
+
display: flex;
|
|
47
|
+
align-items: center;
|
|
48
|
+
justify-content: center;
|
|
49
|
+
|
|
50
|
+
svg {
|
|
51
|
+
width: 100%;
|
|
52
|
+
height: 100%;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
&__description {
|
|
57
|
+
font-size: 14px;
|
|
58
|
+
color: var(--vck-text-muted);
|
|
59
|
+
line-height: 1.5;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
&__bottom {
|
|
63
|
+
margin-top: var(--vck-space-md);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
</style>
|