Conversations Session Management Component ๐ฑ โ
Introduction โ
Conversations
is a session management component developed based on Vue 3 and Element Plus, supporting grouped display, menu interaction, scroll loading, custom styles and other features. Suitable for message lists, file management, task grouping and other scenarios, it meets diverse business needs through flexible configuration and slot extensions.
Code Examples โ
Basic Usage โ
Get selected session information through @change
event. v-model:active
binds the currently selected session.
<script setup lang="ts">
import type { ConversationItem } from 'vue-element-plus-x/types/Conversations';
const timeBasedItems = ref<ConversationItem<{ id: string; label: string }>[]>([
{
id: '1',
label: 'Today\'s Session 111111111111111111111111111',
group: 'today'
},
{
id: '2',
group: 'today',
label: 'Today\'s Session 2',
disabled: true
},
{
id: '3',
group: 'yesterday',
label: 'Yesterday\'s Session 1'
},
{
id: '4',
label: 'Yesterday\'s Session 2'
},
{
id: '5',
label: 'Session from a week ago'
},
{
id: '6',
label: 'Session from a month ago'
},
{
id: '7',
label: 'Session from long ago'
}
]);
const activeKey1 = ref();
function handleChange(item: ConversationItem<{ id: string; label: string }>) {
ElMessage.success(`Selected: ${item.label}`);
}
</script>
<template>
<div style="display: flex; flex-direction: column; gap: 12px; height: 420px">
<Conversations
v-model:active="activeKey1"
:items="timeBasedItems"
:label-max-width="200"
:show-tooltip="true"
row-key="id"
@change="handleChange"
/>
</div>
</template>
<style scoped lang="less"></style>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
Time Grouping and Sticky Effect โ
- todayyesterdayๆชๅ็ป
Automatically groups by the group
field of session items, group titles stick to top when scrolling, enhancing navigation experience.
<script setup lang="ts">
import type { ConversationItem } from 'vue-element-plus-x/types/Conversations';
const timeBasedItems = ref<ConversationItem<{ id: string; label: string }>[]>([
{
id: '1',
label: 'Today\'s Session 111111111111111111111111111',
group: 'today',
disabled: true
},
{
id: '2',
group: 'today',
label: 'Today\'s Session 2'
},
{
id: '3',
group: 'yesterday',
label: 'Yesterday\'s Session 1'
},
{
id: '4',
label: 'Yesterday\'s Session 2'
},
{
id: '5',
label: 'Session from a week ago'
},
{
id: '6',
label: 'Session from a month ago'
},
{
id: '7',
label: 'Session from long ago'
}
]);
const activeKey1 = ref('1');
</script>
<template>
<div style="display: flex; flex-direction: column; gap: 12px; height: 420px">
<Conversations
v-model:active="activeKey1"
:items="timeBasedItems"
groupable
:label-max-width="200"
:show-tooltip="false"
row-key="id"
/>
</div>
</template>
<style scoped lang="less"></style>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
Custom Group Sorting โ
- ๐ Study๐ Work๐ Personal๐ ๆชๅ็ป
Pass sorting function through groupable
property to customize group order (e.g.: Study > Work > Personal > Ungrouped).
<script setup lang="ts">
import type { GroupableOptions } from 'vue-element-plus-x/types/Conversations';
const groupBasedItems = ref([
{
key: 'g1',
label: 'Work Document 1',
group: 'Work'
},
{
key: 'g2',
label: 'Work Document 11111111111111111111111111111111111111111',
group: 'Work'
},
{
key: 'g3',
label: 'Work Document 3',
group: 'Work'
},
{
key: 'g4',
label: 'Work Document 4',
group: 'Work'
},
{
key: 'g5',
label: 'Work Document 5',
group: 'Work'
},
{
key: 'g6',
label: 'Work Document 6',
group: 'Work'
},
{
key: 'g7',
label: 'Study Notes 1',
group: 'Study'
},
{
key: 'g8',
label: 'Study Notes 2',
group: 'Study'
},
{
key: 'g9',
label: 'Personal Document 1',
group: 'Personal'
},
{
key: 'g10',
label: 'Ungrouped Item'
}
]);
// Custom group options
const customGroupOptions: GroupableOptions = {
// Custom group sorting, Study > Work > Personal > Ungrouped
sort: (a: any, b: any) => {
const order: Record<string, number> = {
Study: 0,
Work: 1,
Personal: 2,
Ungrouped: 3
};
const orderA = order[a] !== undefined ? order[a] : 999;
const orderB = order[b] !== undefined ? order[b] : 999;
return orderA - orderB;
}
};
const activeKey2 = ref('g1');
</script>
<template>
<div style="display: flex; flex-direction: column; gap: 12px; height: 420px">
<Conversations
v-model:active="activeKey2"
:items="groupBasedItems"
:groupable="customGroupOptions"
:label-max-width="200"
:show-tooltip="true"
show-to-top-btn
row-key="key"
>
<template #groupTitle="{ group }">
<div class="custom-group-title">
<!-- Add different prefixes for different groups -->
<span v-if="group.title === 'Work'">๐ </span>
<span v-else-if="group.title === 'Study'">๐ </span>
<span v-else-if="group.title === 'Personal'">๐ </span>
<span v-else>๐ </span>
{{ group.title }}
</div>
</template>
</Conversations>
</div>
</template>
<style scoped lang="less">
.custom-group-title {
display: flex;
align-items: center;
font-weight: 500;
color: #409eff;
}
</style>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
Built-in Dropdown Menu โ
Built-in basic menu functionality (rename, delete), supports menu command callbacks, easily implements quick operations for session items.
@menu-command
triggers built-in menu click events.
<script setup lang="ts">
import type {
ConversationItem,
ConversationMenuCommand
} from 'vue-element-plus-x/types/Conversations';
const menuTestItems = ref([
{
key: 'm1',
label:
'Menu Test Item 1 - Long text effect demonstration text length overflow effect test'.repeat(
2
)
},
{
key: 'm2',
label: 'Menu Test Item 2',
disabled: true
},
{
key: 'm3',
label: 'Menu Test Item 3'
},
{
key: 'm4',
label: 'Menu Test Item 4'
},
{
key: 'm5',
label: 'Menu Test Item 5'
},
{
key: 'm6',
label: 'Menu Test Item 6'
},
{
key: 'm7',
label: 'Menu Test Item 7'
},
{
key: 'm8',
label: 'Menu Test Item 8'
},
{
key: 'm9',
label: 'Menu Test Item 9'
},
{
key: 'm10',
label: 'Menu Test Item 10'
},
{
key: 'm11',
label: 'Menu Test Item 11'
},
{
key: 'm12',
label: 'Menu Test Item 12'
},
{
key: 'm13',
label: 'Menu Test Item 13'
},
{
key: 'm14',
label: 'Menu Test Item 14'
}
]);
const activeKey4 = ref('m1');
// Built-in menu click method
function handleMenuCommand(
command: ConversationMenuCommand,
item: ConversationItem
) {
console.log('Built-in menu click event:', command, item);
// Whether directly modifying item takes effect
if (command === 'delete') {
const index = menuTestItems.value.findIndex(
itemSlef => itemSlef.key === item.key
);
if (index !== -1) {
menuTestItems.value.splice(index, 1);
console.log('Delete successful');
ElMessage.success('Delete successful');
}
}
if (command === 'rename') {
item.label = 'Modified';
console.log('Rename successful');
ElMessage.success('Rename successful');
}
}
</script>
<template>
<div style="display: flex; flex-direction: column; gap: 12px; height: 420px">
<Conversations
v-model:active="activeKey4"
:items="menuTestItems"
:label-max-width="200"
:show-tooltip="true"
row-key="key"
tooltip-placement="right"
:tooltip-offset="35"
show-to-top-btn
show-built-in-menu
@menu-command="handleMenuCommand"
/>
</div>
</template>
<style scoped lang="less"></style>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
Built-in Dropdown Menu Button Type โ
Use the showBuiltInMenuType
property to set whether to always display the built-in menu button.
The optional values for the showBuiltInMenuType
property are:
hover
: Show built-in menu button on mouse hover (default)always
: Always show built-in menu button
<script setup lang="ts">
import type {
ConversationItem,
ConversationMenuCommand
} from 'vue-element-plus-x/types/Conversations';
const menuTestItems = ref([
{
key: 'm1',
label:
'Menu Test Item 1 - Long Text Effect Demo Text Length Overflow Effect Test'.repeat(
2
)
},
{
key: 'm2',
label: 'Menu Test Item 2',
disabled: true
},
{
key: 'm3',
label: 'Menu Test Item 3'
},
{
key: 'm4',
label: 'Menu Test Item 4'
},
{
key: 'm5',
label: 'Menu Test Item 5'
},
{
key: 'm6',
label: 'Menu Test Item 6'
},
{
key: 'm7',
label: 'Menu Test Item 7'
},
{
key: 'm8',
label: 'Menu Test Item 8'
},
{
key: 'm9',
label: 'Menu Test Item 9'
},
{
key: 'm10',
label: 'Menu Test Item 10'
},
{
key: 'm11',
label: 'Menu Test Item 11'
},
{
key: 'm12',
label: 'Menu Test Item 12'
},
{
key: 'm13',
label: 'Menu Test Item 13'
},
{
key: 'm14',
label: 'Menu Test Item 14'
}
]);
const activeKey4 = ref('m1');
// Built-in menu click method
function handleMenuCommand(
command: ConversationMenuCommand,
item: ConversationItem
) {
console.log('Built-in menu click event:', command, item);
// Check if directly modifying item works
if (command === 'delete') {
const index = menuTestItems.value.findIndex(
itemSelf => itemSelf.key === item.key
);
if (index !== -1) {
menuTestItems.value.splice(index, 1);
console.log('Deletion successful');
ElMessage.success('Deletion successful');
}
}
if (command === 'rename') {
item.label = 'Modified';
console.log('Renaming successful');
ElMessage.success('Renaming successful');
}
}
</script>
<template>
<div style="display: flex; flex-direction: column; gap: 12px; height: 420px">
<Conversations
v-model:active="activeKey4"
:items="menuTestItems"
:label-max-width="200"
:show-tooltip="true"
row-key="key"
tooltip-placement="right"
:tooltip-offset="35"
show-to-top-btn
show-built-in-menu
show-built-in-menu-type="always"
@menu-command="handleMenuCommand"
/>
</div>
</template>
<style scoped lang="less"></style>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
Custom Menu Interaction โ
Extend menu content through slots, supports icons, text and custom commands, meets complex business logic.
<script setup lang="ts">
import { Delete, Edit, EditPen, Share } from '@element-plus/icons-vue';
const menuTestItems = ref([
{
key: 'm1',
label:
'Menu Test Item 1 - Long text effect demonstration text length overflow effect test'.repeat(
2
)
},
{
key: 'm2',
label: 'Menu Test Item 2',
disabled: true
},
{
key: 'm3',
label: 'Menu Test Item 3'
},
{
key: 'm4',
label: 'Menu Test Item 4'
},
{
key: 'm5',
label: 'Menu Test Item 5'
},
{
key: 'm6',
label: 'Menu Test Item 6'
},
{
key: 'm7',
label: 'Menu Test Item 7'
},
{
key: 'm8',
label: 'Menu Test Item 8'
},
{
key: 'm9',
label: 'Menu Test Item 9'
},
{
key: 'm10',
label: 'Menu Test Item 10'
},
{
key: 'm11',
label: 'Menu Test Item 11'
},
{
key: 'm12',
label: 'Menu Test Item 12'
},
{
key: 'm13',
label: 'Menu Test Item 13'
},
{
key: 'm14',
label: 'Menu Test Item 14'
}
]);
const conversationMenuItems = [
{
key: 'edit',
label: 'Edit',
icon: Edit,
command: {
self_id: '1',
self_message: 'Edit',
self_type: 'primary'
}
},
{
key: 'delete',
label: 'Delete',
icon: Delete,
disabled: true,
divided: true
},
{
key: 'share',
label: 'Share',
icon: Share,
command: 'share'
}
];
const activeKey4 = ref('m1');
// Handle menu click
function handleMenuClick(menuKey: string, item: any) {
console.log('Menu click', menuKey, item);
switch (menuKey) {
case 'edit':
console.log(`Edit: ${item.label}`);
ElMessage.warning(`Edit: ${item.label}`);
break;
case 'delete':
console.log(`Delete: ${item.label}`);
ElMessage.error(`Delete: ${item.label}`);
break;
case 'share':
console.log(`Share: ${item.label}`);
ElMessage.success(`Share: ${item.label}`);
break;
}
}
</script>
<template>
<div style="display: flex; flex-direction: column; gap: 12px; height: 420px">
<Conversations
v-model:active="activeKey4"
:items="menuTestItems"
row-key="key"
:label-max-width="200"
:show-tooltip="true"
show-to-top-btn
show-built-in-menu
>
<template #more-filled>
<el-icon>
<EditPen />
</el-icon>
</template>
<template #menu="{ item }">
<div class="menu-buttons">
<el-button
v-for="menuItem in conversationMenuItems"
:key="menuItem.key"
link
size="small"
@click.stop="handleMenuClick(menuItem.key, item)"
>
<el-icon v-if="menuItem.icon">
<component :is="menuItem.icon" />
</el-icon>
<span v-if="menuItem.label">{{ menuItem.label }}</span>
</el-button>
</div>
</template>
</Conversations>
</div>
</template>
<style scoped lang="less">
.menu-buttons {
display: flex;
flex-direction: column;
gap: 8px;
align-items: center;
padding: 12px;
// Custom menu button-el-button styles
.el-button {
padding: 4px 8px;
margin-left: 0;
.el-icon {
margin-right: 8px;
}
}
}
</style>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
Lazy Loading Feature โ
Automatically triggers loading more data when scrolling to bottom, supports loading state display, optimizes performance for large data scenarios.
<script setup lang="ts">
import { ChatDotRound, ChatLineRound } from '@element-plus/icons-vue';
const lazyItems = shallowRef([
{
key: 'l1',
label: 'Initial Item 1',
prefixIcon: ChatLineRound
},
{
key: 'l2',
label: 'Initial Item 2',
prefixIcon: ChatDotRound
},
{
key: 'l3',
label: 'Initial Item 3',
prefixIcon: ChatLineRound
},
{
key: 'l4',
label: 'Initial Item 1',
prefixIcon: ChatLineRound
},
{
key: 'l5',
label: 'Initial Item 2',
prefixIcon: ChatDotRound
},
{
key: 'l6',
label: 'Initial Item 3',
prefixIcon: ChatLineRound
},
{
key: 'l7',
label: 'Initial Item 1',
prefixIcon: ChatLineRound
},
{
key: 'l8',
label: 'Initial Item 2',
prefixIcon: ChatDotRound
},
{
key: 'l9',
label: 'Initial Item 3',
prefixIcon: ChatLineRound
}
]);
// Load more handling
const isLoading = ref(false);
function loadMoreItems() {
if (isLoading.value)
return;
isLoading.value = true;
console.log('Loading more data...');
// Simulate async loading
setTimeout(() => {
const newItems = [
{
key: `l${lazyItems.value.length + 1}`,
label: `Loaded Item ${lazyItems.value.length + 1}`,
prefixIcon: markRaw(ChatLineRound)
},
{
key: `l${lazyItems.value.length + 2}`,
label: `Loaded Item ${lazyItems.value.length + 2}`,
prefixIcon: markRaw(ChatDotRound)
}
];
lazyItems.value = [...lazyItems.value, ...newItems];
isLoading.value = false;
}, 2000);
}
const activeKey6 = ref('l1');
</script>
<template>
<div style="display: flex; flex-direction: column; gap: 12px; height: 420px">
<Conversations
v-model:active="activeKey6"
:items="lazyItems"
:label-max-width="200"
row-key="key"
:show-tooltip="true"
:load-more="loadMoreItems"
:load-more-loading="isLoading"
show-to-top-btn
/>
</div>
</template>
<style scoped lang="less"></style>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
Custom Styles and Group Titles โ
- ๐ StudyMenu Test Item 4Menu Test Item 5Menu Test Item 6Menu Test Item 7๐ WorkMenu Test Item 1 - Long text effect demonstration text length overflow effect testMenu Test Item 1 - Long text effect demonstration text length overflow effect testโMenu Test Item 2Menu Test Item 3๐ PersonalMenu Test Item 8Menu Test Item 9Menu Test Item 10Menu Test Item 11๐ ๆชๅ็ปMenu Test Item 12Menu Test Item 13Menu Test Item 14
Customize session item appearance and group title icons through slots and style properties, supports hover, active, menu opened state style customization.
<script setup lang="ts">
import type { GroupableOptions } from 'vue-element-plus-x/types/Conversations';
const menuTestItems1 = ref([
{
key: 'm1',
label:
'Menu Test Item 1 - Long text effect demonstration text length overflow effect test'.repeat(
2
),
group: 'Work'
},
{
key: 'm2',
label: 'Menu Test Item 2',
disabled: true,
group: 'Work'
},
{
key: 'm3',
label: 'Menu Test Item 3',
group: 'Work'
},
{
key: 'm4',
label: 'Menu Test Item 4',
group: 'Study'
},
{
key: 'm5',
label: 'Menu Test Item 5',
group: 'Study'
},
{
key: 'm6',
label: 'Menu Test Item 6',
group: 'Study'
},
{
key: 'm7',
label: 'Menu Test Item 7',
group: 'Study'
},
{
key: 'm8',
label: 'Menu Test Item 8',
group: 'Personal'
},
{
key: 'm9',
label: 'Menu Test Item 9',
group: 'Personal'
},
{
key: 'm10',
label: 'Menu Test Item 10',
group: 'Personal'
},
{
key: 'm11',
label: 'Menu Test Item 11',
group: 'Personal'
},
{
key: 'm12',
label: 'Menu Test Item 12'
},
{
key: 'm13',
label: 'Menu Test Item 13'
},
{
key: 'm14',
label: 'Menu Test Item 14'
}
]);
const conversationMenuItems1 = [
{
key: 'edit',
label: 'Edit',
icon: '๐',
command: {
self_id: '1',
self_message: 'Edit',
self_type: 'primary'
}
},
{
key: 'delete',
label: 'Delete',
icon: '๐',
disabled: true,
divided: true
},
{
key: 'share',
label: 'Share',
icon: '๐',
command: 'share'
}
];
const activeKey5 = ref('m1');
// Custom group options
const customGroupOptions: GroupableOptions = {
// Custom group sorting, Study > Work > Personal > Ungrouped
sort: (a: any, b: any) => {
const order: Record<string, number> = {
Study: 0,
Work: 1,
Personal: 2,
Ungrouped: 3
};
const orderA = order[a] !== undefined ? order[a] : 999;
const orderB = order[b] !== undefined ? order[b] : 999;
return orderA - orderB;
}
};
// Handle menu click
function handleMenuClick(menuKey: string, item: any) {
console.log('Menu click', menuKey, item);
switch (menuKey) {
case 'edit':
console.log(`Edit: ${item.label}`);
ElMessage.warning(`Edit: ${item.label}`);
break;
case 'delete':
console.log(`Delete: ${item.label}`);
ElMessage.error(`Delete: ${item.label}`);
break;
case 'share':
console.log(`Share: ${item.label}`);
ElMessage.success(`Share: ${item.label}`);
break;
}
}
</script>
<template>
<div style="display: flex; flex-direction: column; gap: 12px; height: 420px">
<Conversations
v-model:active="activeKey5"
:items="menuTestItems1"
:label-max-width="200"
:show-tooltip="true"
tooltip-placement="right"
:tooltip-offset="35"
show-built-in-menu
:groupable="customGroupOptions"
row-key="key"
:items-style="{
padding: '10px 20px',
borderRadius: '10px',
fontSize: '16px',
fontWeight: 'bold',
textAlign: 'center',
boxShadow: '0 2px 12px 0 rgba(0, 0, 0, 0.1)',
transition: 'all 0.3s',
marginBottom: '20px',
border: '2px dashed transparent'
}"
:items-hover-style="{
background: '#FAFAD2',
border: '2px dashed #006400'
}"
:items-active-style="{
background: '#006400',
color: '#FFFAFA',
border: '2px dashed transparent'
}"
:items-menu-opened-style="{
border: '2px dashed transparent'
}"
:menu-style="{
backgroundColor: 'red',
boxShadow: '0 2px 12px 0 rgba(0, 0, 0, 0.1)',
padding: '10px 20px',
height: '200px'
}"
>
<template #label="{ item }">
<div class="custom-label">
{{ item.label }}
</div>
</template>
<template #groupTitle="{ group }">
<div class="custom-group-title">
<!-- Add different prefixes for different groups -->
<span v-if="group.title === 'Work'">๐ </span>
<span v-else-if="group.title === 'Study'">๐ </span>
<span v-else-if="group.title === 'Personal'">๐ </span>
<span v-else>๐ </span>
{{ group.title }}
</div>
</template>
<template
#more-filled="{ item, isHovered, isActive, isMenuOpened, isDisabled }"
>
<span v-if="isHovered">โ๏ธ</span>
<span v-if="isActive">โ
</span>
<span v-if="isMenuOpened">๐ฅฐ</span>
<span
v-if="isDisabled"
:style="{
background: 'black',
padding: '5px',
borderRadius: '10px',
color: 'white',
fontSize: '12px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center'
}"
>
๐ซฅIs Disabled: {{ item?.disabled }}
</span>
</template>
<template #menu="{ item }">
<div class="menu-buttons">
<div
v-for="menuItem in conversationMenuItems1"
:key="menuItem.key"
class="menu-self-button"
@click.stop="handleMenuClick(menuItem.key, item)"
>
<span v-if="menuItem.icon">{{ menuItem.icon }}</span>
<span v-if="menuItem.label">{{ menuItem.label }}</span>
</div>
</div>
</template>
</Conversations>
</div>
</template>
<style scoped lang="less">
.custom-group-title {
display: flex;
align-items: center;
font-weight: 500;
color: #409eff;
}
.menu-buttons {
display: flex;
flex-direction: column;
gap: 8px;
align-items: center;
padding: 12px;
// Custom menu button-el-button styles
.el-button {
padding: 4px 8px;
margin-left: 0;
.el-icon {
margin-right: 8px;
}
}
// Custom menu button-custom styles
.menu-self-button {
display: flex;
padding: 4px 8px;
align-items: center;
border-radius: 5px;
margin-left: 0;
cursor: pointer;
gap: 8px;
&:hover {
background-color: #f5f7fa;
color: #409eff;
}
}
}
.custom-label {
display: flex;
align-items: center;
// Overflow hidden
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
width: 100%;
}
</style>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
Properties โ
Property Name | Type | Required | Default | Description |
---|---|---|---|---|
items | ConversationItem<T>[] | No | [] | Session item data list, containing label , group , disabled and other fields |
groupable | boolean | GroupableOptions | No | false | Whether to enable grouping, passing object can customize group sorting (sort function) |
showBuiltInMenu | boolean | No | false | Whether to show built-in menu (rename, delete) |
loadMore | () => void | No | - | Lazy loading callback function, triggered when scrolling to bottom |
loadMoreLoading | boolean | No | false | Load more state, controls loading animation display |
showToTopBtn | boolean | No | false | Whether to show back to top button |
labelKey | string | No | 'label' | Session item label field name |
rowKey | string | No | 'id' | Session item unique identifier field name |
itemsStyle | CSSProperties | No | {} | Session item default style |
itemsHoverStyle | CSSProperties | No | {} | Session item hover style |
itemsActiveStyle | CSSProperties | No | {} | Session item active style |
itemsMenuOpenedStyle | CSSProperties | No | {} | Session item style when menu is opened |
Slots โ
Slot Name | Parameters | Description |
---|---|---|
#groupTitle | { group: GroupItem } | Custom group title, supports adding icons or special styles |
#label | { item: ConversationItem<T> } | Custom session item label content, supports text overflow handling or rich text |
#more-filled | { item, isHovered, isActive, isMenuOpened, isDisabled } | Session item right side additional content, displays status indicators (e.g.: disabled mark, action icons) |
#menu | { item: ConversationItem<T> } | Custom menu content, supports buttons, icons or complex interactive components |
#header | - | Container header slot, for adding search bars, filter buttons and other custom content |
#footer | - | Container footer slot, for adding pagination, statistics and other custom content |
Events โ
Event | Parameters | Description |
---|---|---|
@menuCommand | (command: ConversationMenuCommand, item: ConversationItem): void | Menu command callback, supports rename, delete and other operations. If you choose custom menu, this method is disabled, you need to handle menu click logic yourself. |
:loadMore | -- | Bind lazy loading callback, triggered when scrolling to bottom |
Features โ
- Flexible Group Management
- Automatically groups by
group
field, ungrouped items are unified under "Ungrouped" title - Supports custom group sorting (through
groupable.sort
function), implements business logic customization - Group title sticky display, maintains navigation visibility when scrolling
- Rich Interaction Support
- Built-in basic menu (rename, delete), supports monitoring command callbacks through
@menu-command
- Custom menu slots, easily extend sharing, editing and other complex operations
- Session item state styles independently configured (default, hover, active, menu opened), clear visual feedback
- Performance Optimization
- Lazy loading feature: automatically loads more data when scrolling to bottom, reduces initial rendering pressure
- Virtual scrolling (planned): supports ultra-large list scenarios, improves memory usage efficiency
- Highly Customizable
- Full style properties: customize session item appearance through
itemsStyle
series properties - Deep slot extensions: labels, group titles, menu content can all be completely customized through slots
- Responsive design: supports adaptive width and scrollbar hiding, adapts to different container sizes