vue-chat-kit 0.1.1
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/README.md +214 -0
- package/package.json +59 -0
- package/src/components/AvatarCrop.vue +229 -0
- package/src/components/ChatWindow.vue +1326 -0
- package/src/composables/useChat.js +588 -0
- package/src/config/index.js +111 -0
- package/src/core/api.js +189 -0
- package/src/core/request.js +174 -0
- package/src/core/websocket.js +159 -0
- package/src/index.js +27 -0
package/src/core/api.js
ADDED
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* API 服务层
|
|
3
|
+
*/
|
|
4
|
+
import { HttpClient } from './request.js'
|
|
5
|
+
|
|
6
|
+
export class ChatApi {
|
|
7
|
+
constructor(config, httpClient = null) {
|
|
8
|
+
this.config = config
|
|
9
|
+
this.endpoints = config.api.endpoints
|
|
10
|
+
this.customAdapter = config.api.adapter
|
|
11
|
+
|
|
12
|
+
if (httpClient) {
|
|
13
|
+
this.http = httpClient
|
|
14
|
+
} else {
|
|
15
|
+
this.http = new HttpClient({
|
|
16
|
+
baseUrl: config.api.baseUrl,
|
|
17
|
+
headers: config.headers
|
|
18
|
+
})
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* 使用自定义适配器或默认实现
|
|
24
|
+
*/
|
|
25
|
+
async _call(methodName, ...args) {
|
|
26
|
+
if (this.customAdapter && typeof this.customAdapter[methodName] === 'function') {
|
|
27
|
+
return this.customAdapter[methodName](...args)
|
|
28
|
+
}
|
|
29
|
+
return this[`_${methodName}`](...args)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// ========== 好友相关 ==========
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* 获取好友列表
|
|
36
|
+
*/
|
|
37
|
+
async getFriends(currentUser) {
|
|
38
|
+
return this._call('getFriends', currentUser)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async _getFriends(currentUser) {
|
|
42
|
+
return this.http.get(this.endpoints.getFriends, { currentUser })
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* 获取可添加的用户列表
|
|
47
|
+
*/
|
|
48
|
+
async getAvailableUsers(currentUser) {
|
|
49
|
+
return this._call('getAvailableUsers', currentUser)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async _getAvailableUsers(currentUser) {
|
|
53
|
+
return this.http.get(this.endpoints.getAvailableUsers, { currentUser })
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* 添加好友
|
|
58
|
+
*/
|
|
59
|
+
async addFriend(currentUser, friendUser) {
|
|
60
|
+
return this._call('addFriend', currentUser, friendUser)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async _addFriend(currentUser, friendUser) {
|
|
64
|
+
return this.http.post(this.endpoints.addFriend, { currentUser, friendUser })
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* 获取好友申请列表
|
|
69
|
+
*/
|
|
70
|
+
async getApplyList(currentUser) {
|
|
71
|
+
return this._call('getApplyList', currentUser)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
async _getApplyList(currentUser) {
|
|
75
|
+
return this.http.get(this.endpoints.getApplyList, { currentUser })
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* 同意好友申请
|
|
80
|
+
*/
|
|
81
|
+
async agreeFriend(applyUser, friendUser) {
|
|
82
|
+
return this._call('agreeFriend', applyUser, friendUser)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async _agreeFriend(applyUser, friendUser) {
|
|
86
|
+
return this.http.post(this.endpoints.agreeFriend, { applyUser, friendUser })
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* 设置好友聊天状态
|
|
91
|
+
*/
|
|
92
|
+
async setChatStatus(currentUser, friendUser, status = 1) {
|
|
93
|
+
return this._call('setChatStatus', currentUser, friendUser, status)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async _setChatStatus(currentUser, friendUser, status) {
|
|
97
|
+
return this.http.post(this.endpoints.setChatStatus, null, { params: { currentUser, friendUser, status } })
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// ========== 消息相关 ==========
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* 获取聊天历史
|
|
104
|
+
*/
|
|
105
|
+
async getHistory(fromUser, toUser) {
|
|
106
|
+
return this._call('getHistory', fromUser, toUser)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
async _getHistory(fromUser, toUser) {
|
|
110
|
+
return this.http.get(this.endpoints.getHistory, { fromUser, toUser })
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* 标记消息已读
|
|
115
|
+
*/
|
|
116
|
+
async setRead(currentUser, friendUser) {
|
|
117
|
+
return this._call('setRead', currentUser, friendUser)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async _setRead(currentUser, friendUser) {
|
|
121
|
+
return this.http.post(this.endpoints.setRead, { currentUser, friendUser })
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// ========== 文件相关 ==========
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* 上传文件
|
|
128
|
+
*/
|
|
129
|
+
async uploadFile(file) {
|
|
130
|
+
return this._call('uploadFile', file)
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
async _uploadFile(file) {
|
|
134
|
+
const formData = new FormData()
|
|
135
|
+
formData.append('file', file)
|
|
136
|
+
return this.http.post(this.endpoints.uploadFile, formData)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// ========== 用户相关 ==========
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* 获取用户信息
|
|
143
|
+
*/
|
|
144
|
+
async getUserInfo(username) {
|
|
145
|
+
return this._call('getUserInfo', username)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
async _getUserInfo(username) {
|
|
149
|
+
return this.http.get(this.endpoints.getUserInfo, { username })
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* 更新用户信息
|
|
154
|
+
*/
|
|
155
|
+
async updateUserInfo(username, data) {
|
|
156
|
+
return this._call('updateUserInfo', username, data)
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
async _updateUserInfo(username, data) {
|
|
160
|
+
return this.http.put(this.endpoints.updateUserInfo, { username, ...data })
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* 获取用户头像
|
|
165
|
+
*/
|
|
166
|
+
async getUserAvatar(username) {
|
|
167
|
+
return this._call('getUserAvatar', username)
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
async _getUserAvatar(username) {
|
|
171
|
+
return this.http.get(this.endpoints.getUserAvatar, { username })
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* 上传头像
|
|
176
|
+
*/
|
|
177
|
+
async uploadAvatar(file, username) {
|
|
178
|
+
return this._call('uploadAvatar', file, username)
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
async _uploadAvatar(file, username) {
|
|
182
|
+
const formData = new FormData()
|
|
183
|
+
formData.append('file', file)
|
|
184
|
+
formData.append('username', username)
|
|
185
|
+
return this.http.post(this.endpoints.uploadAvatar, formData)
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
export default ChatApi
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP 请求工具
|
|
3
|
+
*/
|
|
4
|
+
class RequestError extends Error {
|
|
5
|
+
constructor(message, status, code, data) {
|
|
6
|
+
super(message)
|
|
7
|
+
this.name = 'RequestError'
|
|
8
|
+
this.status = status || 0
|
|
9
|
+
this.code = code || 0
|
|
10
|
+
this.data = data
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* 请求类
|
|
16
|
+
*/
|
|
17
|
+
export class HttpClient {
|
|
18
|
+
constructor(config = {}) {
|
|
19
|
+
this.baseUrl = config.baseUrl || ''
|
|
20
|
+
this.timeout = config.timeout || 10000
|
|
21
|
+
this.headers = config.headers || {}
|
|
22
|
+
this.requestInterceptors = []
|
|
23
|
+
this.responseInterceptors = []
|
|
24
|
+
|
|
25
|
+
// 默认添加 Content-Type
|
|
26
|
+
this.addRequestInterceptor((config) => {
|
|
27
|
+
if (!(config.body instanceof FormData)) {
|
|
28
|
+
config.headers = {
|
|
29
|
+
'Content-Type': 'application/json',
|
|
30
|
+
...config.headers
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return config
|
|
34
|
+
})
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* 添加请求拦截器
|
|
39
|
+
*/
|
|
40
|
+
addRequestInterceptor(interceptor) {
|
|
41
|
+
this.requestInterceptors.push(interceptor)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* 添加响应拦截器
|
|
46
|
+
*/
|
|
47
|
+
addResponseInterceptor(interceptor) {
|
|
48
|
+
this.responseInterceptors.push(interceptor)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* 超时控制
|
|
53
|
+
*/
|
|
54
|
+
timeoutPromise(timeout) {
|
|
55
|
+
return new Promise((_, reject) => {
|
|
56
|
+
setTimeout(() => {
|
|
57
|
+
reject(new RequestError('请求超时', 408, 408))
|
|
58
|
+
}, timeout)
|
|
59
|
+
})
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* 核心请求方法
|
|
64
|
+
*/
|
|
65
|
+
async request(url, options = {}) {
|
|
66
|
+
const { method = 'GET', headers = {}, body, params } = options
|
|
67
|
+
|
|
68
|
+
// 构建完整URL
|
|
69
|
+
let fullUrl = url.startsWith('http') ? url : `${this.baseUrl}${url}`
|
|
70
|
+
|
|
71
|
+
// 应用请求拦截器
|
|
72
|
+
let finalConfig = {
|
|
73
|
+
method,
|
|
74
|
+
headers: { ...this.headers, ...headers },
|
|
75
|
+
body,
|
|
76
|
+
params
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
this.requestInterceptors.forEach((interceptor) => {
|
|
80
|
+
finalConfig = interceptor(finalConfig)
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
// 构建fetch配置
|
|
84
|
+
const fetchConfig = {
|
|
85
|
+
method: finalConfig.method,
|
|
86
|
+
headers: finalConfig.headers,
|
|
87
|
+
credentials: 'include'
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// 处理请求体
|
|
91
|
+
if (finalConfig.body && method !== 'GET') {
|
|
92
|
+
if (finalConfig.body instanceof FormData) {
|
|
93
|
+
fetchConfig.body = finalConfig.body
|
|
94
|
+
} else if (typeof finalConfig.body === 'object') {
|
|
95
|
+
fetchConfig.body = JSON.stringify(finalConfig.body)
|
|
96
|
+
} else {
|
|
97
|
+
fetchConfig.body = finalConfig.body
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// 处理GET参数
|
|
102
|
+
if (finalConfig.params) {
|
|
103
|
+
const queryParams = new URLSearchParams()
|
|
104
|
+
for (const key in finalConfig.params) {
|
|
105
|
+
if (finalConfig.params[key] !== undefined && finalConfig.params[key] !== null && finalConfig.params[key] !== '') {
|
|
106
|
+
queryParams.append(key, finalConfig.params[key])
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
const queryString = queryParams.toString()
|
|
110
|
+
if (queryString) {
|
|
111
|
+
fullUrl += (fullUrl.includes('?') ? '&' : '?') + queryString
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
try {
|
|
116
|
+
const response = await Promise.race([
|
|
117
|
+
fetch(fullUrl, fetchConfig),
|
|
118
|
+
this.timeoutPromise(this.timeout)
|
|
119
|
+
])
|
|
120
|
+
|
|
121
|
+
// 应用响应拦截器
|
|
122
|
+
let finalResponse = response
|
|
123
|
+
this.responseInterceptors.forEach((interceptor) => {
|
|
124
|
+
finalResponse = interceptor(finalResponse)
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
if (!finalResponse.ok) {
|
|
128
|
+
throw new RequestError(
|
|
129
|
+
`HTTP ${finalResponse.status}: ${finalResponse.statusText}`,
|
|
130
|
+
finalResponse.status,
|
|
131
|
+
finalResponse.status
|
|
132
|
+
)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// 解析响应
|
|
136
|
+
const contentType = finalResponse.headers.get('content-type')
|
|
137
|
+
let data
|
|
138
|
+
if (contentType && contentType.includes('application/json')) {
|
|
139
|
+
data = await finalResponse.json()
|
|
140
|
+
} else {
|
|
141
|
+
data = await finalResponse.text()
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return data
|
|
145
|
+
} catch (error) {
|
|
146
|
+
if (error instanceof RequestError) {
|
|
147
|
+
throw error
|
|
148
|
+
}
|
|
149
|
+
throw new RequestError(
|
|
150
|
+
error instanceof Error ? error.message : '网络错误',
|
|
151
|
+
0, 0
|
|
152
|
+
)
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// 便捷方法
|
|
157
|
+
get(url, params, config) {
|
|
158
|
+
return this.request(url, { ...config, method: 'GET', params })
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
post(url, data, config) {
|
|
162
|
+
return this.request(url, { ...config, method: 'POST', body: data })
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
put(url, data, config) {
|
|
166
|
+
return this.request(url, { ...config, method: 'PUT', body: data })
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
delete(url, config) {
|
|
170
|
+
return this.request(url, { ...config, method: 'DELETE' })
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export default HttpClient
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WebSocket 通信核心模块
|
|
3
|
+
*/
|
|
4
|
+
export class ChatWebSocket {
|
|
5
|
+
constructor(userId, options = {}) {
|
|
6
|
+
this.userId = userId
|
|
7
|
+
this.wsUrl = options.wsUrl || ''
|
|
8
|
+
this.socket = null
|
|
9
|
+
this.reconnectAttempts = 0
|
|
10
|
+
this.maxReconnectAttempts = options.maxReconnectAttempts || 5
|
|
11
|
+
this.reconnectDelay = options.reconnectDelay || 3000
|
|
12
|
+
this.handlers = {
|
|
13
|
+
message: [],
|
|
14
|
+
open: [],
|
|
15
|
+
close: [],
|
|
16
|
+
error: []
|
|
17
|
+
}
|
|
18
|
+
this.isConnecting = false
|
|
19
|
+
this.manualClose = false
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* 连接 WebSocket
|
|
24
|
+
*/
|
|
25
|
+
connect() {
|
|
26
|
+
if (this.isConnecting || this.isConnected()) return
|
|
27
|
+
|
|
28
|
+
this.isConnecting = true
|
|
29
|
+
this.manualClose = false
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
this.socket = new WebSocket(this.wsUrl)
|
|
33
|
+
|
|
34
|
+
this.socket.onopen = () => {
|
|
35
|
+
console.log('[VueChatKit] WebSocket 连接成功')
|
|
36
|
+
this.isConnecting = false
|
|
37
|
+
this.reconnectAttempts = 0
|
|
38
|
+
this.emit('open')
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
this.socket.onmessage = (event) => {
|
|
42
|
+
try {
|
|
43
|
+
const data = event.data
|
|
44
|
+
this.emit('message', data)
|
|
45
|
+
} catch (e) {
|
|
46
|
+
console.error('[VueChatKit] WebSocket 消息解析失败', e)
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
this.socket.onclose = (event) => {
|
|
51
|
+
console.log('[VueChatKit] WebSocket 连接关闭', event.code)
|
|
52
|
+
this.isConnecting = false
|
|
53
|
+
this.emit('close', event)
|
|
54
|
+
|
|
55
|
+
if (!this.manualClose && event.code !== 1000) {
|
|
56
|
+
this.reconnect()
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
this.socket.onerror = (error) => {
|
|
61
|
+
console.error('[VueChatKit] WebSocket 连接错误', error)
|
|
62
|
+
this.isConnecting = false
|
|
63
|
+
this.emit('error', error)
|
|
64
|
+
}
|
|
65
|
+
} catch (error) {
|
|
66
|
+
console.error('[VueChatKit] WebSocket 创建连接失败', error)
|
|
67
|
+
this.isConnecting = false
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* 发送消息
|
|
73
|
+
*/
|
|
74
|
+
send(to, message, type = 'text', fileUrl = '', fileName = '', fileSize = 0) {
|
|
75
|
+
if (this.isConnected()) {
|
|
76
|
+
const data = JSON.stringify({
|
|
77
|
+
to,
|
|
78
|
+
msg: message,
|
|
79
|
+
type,
|
|
80
|
+
fileUrl,
|
|
81
|
+
fileName,
|
|
82
|
+
fileSize
|
|
83
|
+
})
|
|
84
|
+
this.socket.send(data)
|
|
85
|
+
return true
|
|
86
|
+
}
|
|
87
|
+
console.warn('[VueChatKit] WebSocket 连接未建立,无法发送消息')
|
|
88
|
+
return false
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* 注册事件监听
|
|
93
|
+
*/
|
|
94
|
+
on(event, handler) {
|
|
95
|
+
if (this.handlers[event]) {
|
|
96
|
+
this.handlers[event].push(handler)
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* 移除事件监听
|
|
102
|
+
*/
|
|
103
|
+
off(event, handler) {
|
|
104
|
+
if (this.handlers[event]) {
|
|
105
|
+
const index = this.handlers[event].indexOf(handler)
|
|
106
|
+
if (index > -1) {
|
|
107
|
+
this.handlers[event].splice(index, 1)
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* 触发事件
|
|
114
|
+
*/
|
|
115
|
+
emit(event, ...args) {
|
|
116
|
+
if (this.handlers[event]) {
|
|
117
|
+
this.handlers[event].forEach(fn => fn(...args))
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* 重连
|
|
123
|
+
*/
|
|
124
|
+
reconnect() {
|
|
125
|
+
if (this.reconnectAttempts < this.maxReconnectAttempts) {
|
|
126
|
+
this.reconnectAttempts++
|
|
127
|
+
console.log(`[VueChatKit] 尝试重连 (${this.reconnectAttempts}/${this.maxReconnectAttempts})...`)
|
|
128
|
+
setTimeout(() => this.connect(), this.reconnectDelay)
|
|
129
|
+
} else {
|
|
130
|
+
console.error('[VueChatKit] 重连次数已达上限')
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* 关闭连接
|
|
136
|
+
*/
|
|
137
|
+
close() {
|
|
138
|
+
this.manualClose = true
|
|
139
|
+
if (this.socket) {
|
|
140
|
+
this.socket.close()
|
|
141
|
+
this.socket = null
|
|
142
|
+
this.handlers = {
|
|
143
|
+
message: [],
|
|
144
|
+
open: [],
|
|
145
|
+
close: [],
|
|
146
|
+
error: []
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* 检查连接状态
|
|
153
|
+
*/
|
|
154
|
+
isConnected() {
|
|
155
|
+
return this.socket && this.socket.readyState === WebSocket.OPEN
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export default ChatWebSocket
|
package/src/index.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// 导出组件
|
|
2
|
+
export { default as ChatWindow } from './components/ChatWindow.vue'
|
|
3
|
+
export { default as AvatarCrop } from './components/AvatarCrop.vue'
|
|
4
|
+
|
|
5
|
+
// 导出 composables
|
|
6
|
+
export { useChat } from './composables/useChat.js'
|
|
7
|
+
|
|
8
|
+
// 导出配置
|
|
9
|
+
export { createChatConfig } from './config/index.js'
|
|
10
|
+
|
|
11
|
+
// 导出核心模块
|
|
12
|
+
export { ChatWebSocket } from './core/websocket.js'
|
|
13
|
+
export { ChatApi } from './core/api.js'
|
|
14
|
+
export { HttpClient } from './core/request.js'
|
|
15
|
+
|
|
16
|
+
// 导出类型(如果使用 TypeScript 的话)
|
|
17
|
+
// export * from './types/index.d.ts'
|
|
18
|
+
|
|
19
|
+
// 安装函数,用于 Vue 插件安装
|
|
20
|
+
export const VueChatKit = {
|
|
21
|
+
install(app) {
|
|
22
|
+
app.component('ChatWindow', ChatWindow)
|
|
23
|
+
app.component('AvatarCrop', AvatarCrop)
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export default VueChatKit
|