1
1
<template >
2
2
<k-layout class =" page-chat" >
3
3
<template #header >
4
- {{ header }}
4
+ {{ title }}
5
5
</template >
6
6
7
7
<template #left >
22
22
</el-scrollbar >
23
23
</template >
24
24
25
+ <template #right v-if =" members [activeGuild ]" >
26
+ <virtual-list class =" members" :data =" members[activeGuild].data" pinned key-name =" user.id" >
27
+ <template #header >
28
+ <div ref =" header" class =" header-padding" >
29
+ <div class =" header-title" >成员列表 ({{ members[activeGuild].next ? '加载中' : members[activeGuild].data.length }})</div >
30
+ </div >
31
+ </template >
32
+ <template #=" data " >
33
+ <member-view :data =" data" ></member-view >
34
+ </template >
35
+ <template #footer ><div class =" footer-padding" ></div ></template >
36
+ </virtual-list >
37
+ </template >
38
+
25
39
<keep-alive >
26
- <template v-if =" active " :key =" active " >
27
- <virtual-list :data =" messages[active ]" pinned v-model:active -key =" index" key-name =" messageId" >
28
- <template #header ><div class =" header-padding" ></div ></template >
40
+ <template v-if =" activeChannel " :key =" activeChannel " >
41
+ <virtual-list class = " messages " :data =" messages[activeChannel ]" pinned v-model:activeChannel -key =" index" key-name =" messageId" >
42
+ <template #header ><div ref = " header " class =" header-padding" ></div ></template >
29
43
<template #=" data " >
30
44
<chat-message :successive =" isSuccessive(data, data.index)" :data =" data" ></chat-message >
31
45
</template >
32
46
<template #footer ><div class =" footer-padding" ></div ></template >
33
47
</virtual-list >
34
48
<div class =" card-footer" >
35
- <chat-input v-model =" input" @send =" handleSend" ></chat-input >
49
+ <chat-input v-model =" input" @send =" handleSend" placeholder = " 向频道发送消息 " ></chat-input >
36
50
</div >
37
51
</template >
38
52
<template v-else >
46
60
47
61
<script lang="ts" setup>
48
62
49
- import { ChatInput , Dict , send , store , VirtualList } from ' @koishijs/client'
63
+ import { ChatInput , Dict , send , store , VirtualList , useContext } from ' @koishijs/client'
50
64
import { computed , ref , watch } from ' vue'
51
- import type { ChannelData , Message } from ' koishi-plugin-messages'
52
- import { messages } from ' ./utils'
65
+ import { useIntersectionObserver } from ' @vueuse/core'
66
+ import type { Message , SyncChannel } from ' koishi-plugin-messages'
67
+ import {} from ' koishi-plugin-chat'
68
+ import { messages , members } from ' ./utils'
69
+ import MemberView from ' ./member.vue'
53
70
import ChatMessage from ' ./message.vue'
54
71
55
72
const index = ref <string >()
56
- const active = ref <string >(' ' )
73
+ const activeChannel = ref <string >(' ' )
74
+ const activeGuild = ref <string >(' ' )
57
75
const tree = ref (null )
76
+ const header = ref (null )
58
77
const keyword = ref (' ' )
59
78
const input = ref (' ' )
60
79
80
+ const ctx = useContext ()
81
+
82
+ ctx .action (' chat.message.delete' , {
83
+ action : ({ chat }) => {}, // deleteMessage(chat.message),
84
+ })
85
+
86
+ ctx .action (' chat.message.quote' , {
87
+ action : ({ chat }) => {}, // quote.value = chat.message,
88
+ })
89
+
61
90
watch (keyword , (val ) => {
62
91
tree .value ?.filter (val )
63
92
})
@@ -66,41 +95,41 @@ interface Tree {
66
95
id: string
67
96
label: string
68
97
children? : Tree []
69
- data? : ChannelData
98
+ data? : SyncChannel . Data
70
99
}
71
100
72
101
const data = computed (() => {
73
102
const data: Tree [] = []
74
103
const guilds: Dict <Tree > = {}
75
- for (const key in store .chat ) {
104
+ for (const key in store .chat . channels ) {
76
105
const [platform, guildId, channelId] = key .split (' /' )
77
106
if (guildId === channelId ) {
78
107
data .push ({
79
108
id: key ,
80
- label: store .chat [key ].channelName || ' 未知频道' ,
81
- data: store .chat [key ],
109
+ label: store .chat . channels [key ].channelName || ' 未知频道' ,
110
+ data: store .chat . channels [key ],
82
111
})
83
112
} else {
84
113
let guild = guilds [platform + ' /' + guildId ]
85
114
if (! guild ) {
86
115
data .push (guild = guilds [platform + ' /' + guildId ] = {
87
116
id: platform + ' /' + guildId ,
88
- label: store .chat [key ].guildName || ' 未知群组' ,
117
+ label: store .chat . channels [key ].guildName || ' 未知群组' ,
89
118
children: [],
90
119
})
91
120
}
92
121
guild .children ! .push ({
93
122
id: key ,
94
- label: store .chat [key ].channelName || ' 未知频道' ,
95
- data: store .chat [key ],
123
+ label: store .chat . channels [key ].channelName || ' 未知频道' ,
124
+ data: store .chat . channels [key ],
96
125
})
97
126
}
98
127
}
99
128
return data
100
129
})
101
130
102
- const header = computed (() => {
103
- const channel = store .chat [ active .value ]
131
+ const title = computed (() => {
132
+ const channel = store .chat . channels [ activeChannel .value ]
104
133
if (! channel ) return
105
134
if (channel .channelId === channel .guildId ) {
106
135
return channel .channelName
@@ -115,7 +144,8 @@ function filterNode(value: string, data: Tree) {
115
144
116
145
function handleClick(tree : Tree ) {
117
146
if (tree .children ) return
118
- active .value = tree .id
147
+ activeChannel .value = tree .id
148
+ activeGuild .value = tree .data ! .guildId
119
149
const list = messages .value [tree .id ] || = []
120
150
if (list .length <= 100 ) {
121
151
send (' chat/history' , {
@@ -129,21 +159,42 @@ function handleClick(tree: Tree) {
129
159
130
160
function getClass(tree : Tree ) {
131
161
const words: string [] = []
132
- if (tree .id === active .value ) words .push (' is-active ' )
162
+ if (tree .id === activeChannel .value ) words .push (' is-activeChannel ' )
133
163
return words .join (' ' )
134
164
}
135
165
136
- function isSuccessive({ quoteId , userId , channelId }: Message , index : number ) {
137
- const prev = (messages .value [active .value ] || = [])[index - 1 ]
138
- return ! quoteId && !! prev && prev .userId === userId && prev .channelId === channelId
166
+ function isSuccessive({ quoteId , userId , channelId , username }: Message , index : number ) {
167
+ const prev = (messages .value [activeChannel .value ] || = [])[index - 1 ]
168
+ return ! quoteId && !! prev
169
+ && prev .userId === userId
170
+ && prev .channelId === channelId
171
+ && prev .username === username
139
172
}
140
173
141
174
function handleSend(content : string ) {
142
- if (! active .value ) return
143
- const [platform, guildId, channelId] = active .value .split (' /' )
175
+ if (! activeChannel .value ) return
176
+ const [platform, guildId, channelId] = activeChannel .value .split (' /' )
144
177
send (' chat/send' , { content , platform , channelId , guildId })
145
178
}
146
179
180
+ let task: Promise <void > = null
181
+
182
+ useIntersectionObserver (header , ([{ isIntersecting }]) => {
183
+ if (! isIntersecting || task ) return
184
+ task = send (' chat/history' , {
185
+ platform: store .chat .channels [activeChannel .value ].platform ,
186
+ guildId: store .chat .channels [activeChannel .value ].guildId ,
187
+ channelId: store .chat .channels [activeChannel .value ].channelId ,
188
+ id: messages .value [activeChannel .value ][0 ]?.id ,
189
+ })
190
+ task .then (() => task = null )
191
+ })
192
+
193
+ watch (() => store .chat .channels [activeChannel .value ]?.guildId , async (guildId ) => {
194
+ if (! guildId ) return
195
+ members .value [guildId ] = await send (' chat/members' , store .chat .channels [activeChannel .value ].platform , guildId )
196
+ })
197
+
147
198
</script >
148
199
149
200
<style lang="scss">
@@ -162,13 +213,21 @@ function handleSend(content: string) {
162
213
flex-direction : column ;
163
214
}
164
215
165
- .header-padding , .footer-padding {
166
- padding : 0.25rem 0 ;
216
+ .messages {
217
+ .header-padding , .footer-padding {
218
+ padding : 0.25rem 0 ;
219
+ }
220
+ }
221
+
222
+ .members {
223
+ .header-padding , .footer-padding {
224
+ padding : 0.5rem 1rem ;
225
+ }
167
226
}
168
227
169
228
.card-footer {
170
229
padding : 1rem 1.25rem ;
171
- border-top : 1px solid var (--border );
230
+ border-top : 1px solid var (--k-color- border );
172
231
}
173
232
}
174
233
0 commit comments