@syfei49/mini-dynamic-renderer 1.0.0 → 1.0.2
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 +84 -102
- package/components/ly-ai-assistant/ly-ai-assistant.vue +523 -0
- package/index.js +16 -14
- package/package.json +27 -18
- package/src/adapters/request.js +106 -0
- package/src/chat/parser.js +55 -0
- package/src/chat/service.js +296 -0
- package/src/config.js +51 -0
- package/src/event.js +43 -0
- package/src/handler.js +43 -49
- package/src/init.js +38 -57
- package/src/interceptor.js +105 -104
package/index.js
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
|
-
var initModule = require('./src/init');
|
|
2
|
-
var interceptor = require('./src/interceptor');
|
|
3
|
-
var handler = require('./src/handler');
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
1
|
+
var initModule = require('./src/init');
|
|
2
|
+
var interceptor = require('./src/interceptor');
|
|
3
|
+
var handler = require('./src/handler');
|
|
4
|
+
var chatService = require('./src/chat/service');
|
|
5
|
+
var configModule = require('./src/config');
|
|
6
|
+
|
|
7
|
+
module.exports = {
|
|
8
|
+
version: '1.0.1',
|
|
9
|
+
init: initModule.init,
|
|
10
|
+
getConfig: configModule.getConfig,
|
|
11
|
+
isInitialized: initModule.isInitialized,
|
|
12
|
+
uninstall: interceptor.uninstallInterceptor,
|
|
13
|
+
parseRenderConfig: handler.parseRenderConfig,
|
|
14
|
+
openChat: chatService.openConversation,
|
|
15
|
+
closeChat: chatService.closeConversation
|
|
16
|
+
};
|
package/package.json
CHANGED
|
@@ -1,18 +1,27 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@syfei49/mini-dynamic-renderer",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "
|
|
5
|
-
"main": "index.js",
|
|
6
|
-
"
|
|
7
|
-
|
|
8
|
-
"
|
|
9
|
-
"
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
"
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "@syfei49/mini-dynamic-renderer",
|
|
3
|
+
"version": "1.0.2",
|
|
4
|
+
"description": "uni-app 浮层 AI 客服插件",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"files": [
|
|
7
|
+
"index.js",
|
|
8
|
+
"src/**/*.js",
|
|
9
|
+
"components/**/*.vue",
|
|
10
|
+
"README.md"
|
|
11
|
+
],
|
|
12
|
+
"scripts": {
|
|
13
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"uni-app",
|
|
17
|
+
"mini-program",
|
|
18
|
+
"ai",
|
|
19
|
+
"chat",
|
|
20
|
+
"float"
|
|
21
|
+
],
|
|
22
|
+
"author": "syfei49",
|
|
23
|
+
"license": "MIT",
|
|
24
|
+
"publishConfig": {
|
|
25
|
+
"access": "public"
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
var configModule = require('../config');
|
|
2
|
+
|
|
3
|
+
function getFullUrl(path) {
|
|
4
|
+
var config = configModule.getConfig();
|
|
5
|
+
var baseUrl = config.baseUrl || '';
|
|
6
|
+
var normalizedPath = (path || '').replace(/^\/+/, '/');
|
|
7
|
+
if (baseUrl.slice(-1) === '/' && normalizedPath.indexOf('/') === 0) {
|
|
8
|
+
return baseUrl + normalizedPath.slice(1);
|
|
9
|
+
}
|
|
10
|
+
if (baseUrl && normalizedPath.indexOf('/') !== 0) {
|
|
11
|
+
normalizedPath = '/' + normalizedPath;
|
|
12
|
+
}
|
|
13
|
+
return baseUrl + normalizedPath;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function getHeaders(extraHeaders) {
|
|
17
|
+
var config = configModule.getConfig();
|
|
18
|
+
var headers = Object.assign({}, config.getHeaders() || {});
|
|
19
|
+
var token = typeof config.getToken === 'function' ? config.getToken() : '';
|
|
20
|
+
if (token) {
|
|
21
|
+
headers.Authorization = token;
|
|
22
|
+
}
|
|
23
|
+
if (extraHeaders) {
|
|
24
|
+
Object.assign(headers, extraHeaders);
|
|
25
|
+
}
|
|
26
|
+
return headers;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function sendChat(payload) {
|
|
30
|
+
var config = configModule.getConfig();
|
|
31
|
+
var url = getFullUrl(config.chatApiPath);
|
|
32
|
+
var headers = getHeaders({
|
|
33
|
+
'Content-Type': 'application/json'
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
if (config.debug) {
|
|
37
|
+
console.log('[mini-dynamic-renderer] sendChat', url, payload);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return new Promise(function (resolve, reject) {
|
|
41
|
+
uni.request({
|
|
42
|
+
url: url,
|
|
43
|
+
method: 'POST',
|
|
44
|
+
data: payload,
|
|
45
|
+
header: headers,
|
|
46
|
+
timeout: 120000,
|
|
47
|
+
success: function (res) {
|
|
48
|
+
var data = res.data || {};
|
|
49
|
+
if (res.statusCode >= 200 && res.statusCode < 300 && data.code !== 500) {
|
|
50
|
+
resolve(data);
|
|
51
|
+
} else {
|
|
52
|
+
reject(new Error(data.msg || data.message || ('请求失败 ' + res.statusCode)));
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
fail: function (err) {
|
|
56
|
+
reject(err || new Error('网络请求失败'));
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function uploadImage(filePath) {
|
|
63
|
+
var config = configModule.getConfig();
|
|
64
|
+
var url = getFullUrl(config.uploadApiPath);
|
|
65
|
+
var headers = getHeaders();
|
|
66
|
+
|
|
67
|
+
if (config.debug) {
|
|
68
|
+
console.log('[mini-dynamic-renderer] uploadImage', url, filePath);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return new Promise(function (resolve, reject) {
|
|
72
|
+
uni.uploadFile({
|
|
73
|
+
url: url,
|
|
74
|
+
filePath: filePath,
|
|
75
|
+
name: 'file',
|
|
76
|
+
header: headers,
|
|
77
|
+
success: function (res) {
|
|
78
|
+
var data = res.data || '{}';
|
|
79
|
+
try {
|
|
80
|
+
data = typeof data === 'string' ? JSON.parse(data) : data;
|
|
81
|
+
} catch (e) {
|
|
82
|
+
reject(new Error('上传响应解析失败'));
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
var dataObj = data.data || {};
|
|
86
|
+
var urlValue = data.url || dataObj.url || dataObj.fileName || dataObj || '';
|
|
87
|
+
if (urlValue && typeof urlValue === 'object' && urlValue.url) {
|
|
88
|
+
urlValue = urlValue.url;
|
|
89
|
+
}
|
|
90
|
+
if (urlValue) {
|
|
91
|
+
resolve(urlValue);
|
|
92
|
+
} else {
|
|
93
|
+
reject(new Error(data.msg || data.message || '上传失败'));
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
fail: function (err) {
|
|
97
|
+
reject(err || new Error('图片上传失败'));
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
module.exports = {
|
|
104
|
+
sendChat: sendChat,
|
|
105
|
+
uploadImage: uploadImage
|
|
106
|
+
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
function cleanImageUrl(url) {
|
|
2
|
+
return String(url || '').replace(/[)\]}>.,;,。]+$/g, '');
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
function parseAssistantMessage(answer) {
|
|
6
|
+
if (!answer) {
|
|
7
|
+
return {
|
|
8
|
+
content: '',
|
|
9
|
+
imageUrls: []
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
var imageUrls = [];
|
|
13
|
+
var iconLineRegex = /[--•]\s*图标[::]\s*(https?:\/\/\S+)/gi;
|
|
14
|
+
var match;
|
|
15
|
+
while ((match = iconLineRegex.exec(answer)) !== null) {
|
|
16
|
+
var url = cleanImageUrl(match[1]);
|
|
17
|
+
if (url && imageUrls.indexOf(url) === -1) {
|
|
18
|
+
imageUrls.push(url);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
var content = answer;
|
|
22
|
+
if (imageUrls.length) {
|
|
23
|
+
content = answer.replace(/[--•]\s*图标[::]\s*https?:\/\/\S+/gi, '- 图标:');
|
|
24
|
+
}
|
|
25
|
+
return {
|
|
26
|
+
content: content.trim(),
|
|
27
|
+
imageUrls: imageUrls
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function parseNeedIcon(answer) {
|
|
32
|
+
if (!answer || answer.indexOf('[NEED_ICON]') === -1) {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
var jsonPart = answer.replace('[NEED_ICON]', '').trim();
|
|
36
|
+
try {
|
|
37
|
+
var data = JSON.parse(jsonPart);
|
|
38
|
+
if (!data || !data.name) {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
return {
|
|
42
|
+
name: data.name,
|
|
43
|
+
sort: Number(data.sort != null ? data.sort : 0),
|
|
44
|
+
msg: data.msg || ('请上传一张图片作为「' + data.name + '」的分类图标')
|
|
45
|
+
};
|
|
46
|
+
} catch (error) {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
module.exports = {
|
|
52
|
+
cleanImageUrl: cleanImageUrl,
|
|
53
|
+
parseAssistantMessage: parseAssistantMessage,
|
|
54
|
+
parseNeedIcon: parseNeedIcon
|
|
55
|
+
};
|
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
var requestAdapter = require('../adapters/request');
|
|
2
|
+
var parser = require('./parser');
|
|
3
|
+
var event = require('../event');
|
|
4
|
+
var configModule = require('../config');
|
|
5
|
+
|
|
6
|
+
var state = {
|
|
7
|
+
messages: [],
|
|
8
|
+
conversationId: '',
|
|
9
|
+
loading: false,
|
|
10
|
+
uploading: false,
|
|
11
|
+
iconPickMode: false,
|
|
12
|
+
pendingIconCategory: null,
|
|
13
|
+
pendingImageUrl: '',
|
|
14
|
+
inputText: ''
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
var quickQuestions = [
|
|
18
|
+
'你好,你能帮我做什么?',
|
|
19
|
+
'在「保健品」下新增二级分类:维生素,排序0',
|
|
20
|
+
'帮我新增一级分类 保健品3'
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
function reset() {
|
|
24
|
+
state.messages = [];
|
|
25
|
+
state.conversationId = '';
|
|
26
|
+
state.loading = false;
|
|
27
|
+
state.uploading = false;
|
|
28
|
+
state.iconPickMode = false;
|
|
29
|
+
state.pendingIconCategory = null;
|
|
30
|
+
state.pendingImageUrl = '';
|
|
31
|
+
state.inputText = '';
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function ensureLogin() {
|
|
35
|
+
var config = configModule.getConfig();
|
|
36
|
+
if (!config.requireLogin) {
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
var token = typeof config.getToken === 'function' ? config.getToken() : '';
|
|
40
|
+
if (token) {
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
uni.showToast({
|
|
44
|
+
title: '请先登录',
|
|
45
|
+
icon: 'none'
|
|
46
|
+
});
|
|
47
|
+
setTimeout(function () {
|
|
48
|
+
uni.navigateTo({
|
|
49
|
+
url: config.loginPage
|
|
50
|
+
});
|
|
51
|
+
}, 800);
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function notifyUpdate() {
|
|
56
|
+
event.emit('chat:update', getSnapshot());
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function getSnapshot() {
|
|
60
|
+
return {
|
|
61
|
+
messages: state.messages.slice(),
|
|
62
|
+
loading: state.loading,
|
|
63
|
+
uploading: state.uploading,
|
|
64
|
+
iconPickMode: state.iconPickMode,
|
|
65
|
+
pendingIconCategory: state.pendingIconCategory,
|
|
66
|
+
pendingImageUrl: state.pendingImageUrl,
|
|
67
|
+
inputText: state.inputText,
|
|
68
|
+
quickQuestions: quickQuestions.slice()
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function createAssistantMessage(content) {
|
|
73
|
+
var parsed = parser.parseAssistantMessage(content);
|
|
74
|
+
return {
|
|
75
|
+
role: 'assistant',
|
|
76
|
+
content: parsed.content,
|
|
77
|
+
imageUrls: parsed.imageUrls
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function handleAssistantAnswer(answer) {
|
|
82
|
+
var needIcon = parser.parseNeedIcon(answer);
|
|
83
|
+
if (needIcon) {
|
|
84
|
+
state.pendingIconCategory = {
|
|
85
|
+
name: needIcon.name,
|
|
86
|
+
sort: needIcon.sort
|
|
87
|
+
};
|
|
88
|
+
state.iconPickMode = true;
|
|
89
|
+
state.messages.push({
|
|
90
|
+
role: 'assistant',
|
|
91
|
+
content: needIcon.msg,
|
|
92
|
+
imageUrls: []
|
|
93
|
+
});
|
|
94
|
+
notifyUpdate();
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
state.iconPickMode = false;
|
|
98
|
+
state.pendingIconCategory = null;
|
|
99
|
+
state.messages.push(createAssistantMessage(answer));
|
|
100
|
+
notifyUpdate();
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function setInputText(text) {
|
|
104
|
+
state.inputText = text;
|
|
105
|
+
notifyUpdate();
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function clearPendingImage() {
|
|
109
|
+
state.pendingImageUrl = '';
|
|
110
|
+
notifyUpdate();
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function chooseImage() {
|
|
114
|
+
if (state.loading || state.uploading || state.iconPickMode) {
|
|
115
|
+
return Promise.resolve();
|
|
116
|
+
}
|
|
117
|
+
return new Promise(function (resolve, reject) {
|
|
118
|
+
uni.chooseImage({
|
|
119
|
+
count: 1,
|
|
120
|
+
sizeType: ['compressed'],
|
|
121
|
+
sourceType: ['album', 'camera'],
|
|
122
|
+
success: function (chooseRes) {
|
|
123
|
+
var filePath = chooseRes.tempFilePaths && chooseRes.tempFilePaths[0];
|
|
124
|
+
if (!filePath) {
|
|
125
|
+
reject(new Error('未选择图片'));
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
state.uploading = true;
|
|
129
|
+
notifyUpdate();
|
|
130
|
+
requestAdapter.uploadImage(filePath)
|
|
131
|
+
.then(function (imageUrl) {
|
|
132
|
+
state.pendingImageUrl = imageUrl;
|
|
133
|
+
state.uploading = false;
|
|
134
|
+
notifyUpdate();
|
|
135
|
+
resolve(imageUrl);
|
|
136
|
+
})
|
|
137
|
+
.catch(function (error) {
|
|
138
|
+
state.uploading = false;
|
|
139
|
+
notifyUpdate();
|
|
140
|
+
reject(error);
|
|
141
|
+
});
|
|
142
|
+
},
|
|
143
|
+
fail: function (err) {
|
|
144
|
+
reject(err || new Error('选择图片失败'));
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function pickCategoryIcon(source) {
|
|
151
|
+
if (state.loading || state.uploading || !state.pendingIconCategory) {
|
|
152
|
+
return Promise.resolve();
|
|
153
|
+
}
|
|
154
|
+
if (!ensureLogin()) {
|
|
155
|
+
return Promise.resolve();
|
|
156
|
+
}
|
|
157
|
+
var sourceType = source === 'camera' ? ['camera'] : ['album'];
|
|
158
|
+
return new Promise(function (resolve, reject) {
|
|
159
|
+
uni.chooseImage({
|
|
160
|
+
count: 1,
|
|
161
|
+
sizeType: ['compressed'],
|
|
162
|
+
sourceType: sourceType,
|
|
163
|
+
success: function (chooseRes) {
|
|
164
|
+
var filePath = chooseRes.tempFilePaths && chooseRes.tempFilePaths[0];
|
|
165
|
+
if (!filePath) {
|
|
166
|
+
reject(new Error('未选择图片'));
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
state.uploading = true;
|
|
170
|
+
notifyUpdate();
|
|
171
|
+
requestAdapter.uploadImage(filePath)
|
|
172
|
+
.then(function (imageUrl) {
|
|
173
|
+
var category = state.pendingIconCategory;
|
|
174
|
+
var query = '新增一级分类:' + category.name + ',排序' + category.sort + ',图标 ' + imageUrl;
|
|
175
|
+
state.iconPickMode = false;
|
|
176
|
+
state.pendingIconCategory = null;
|
|
177
|
+
state.uploading = false;
|
|
178
|
+
notifyUpdate();
|
|
179
|
+
return sendMessage(query, imageUrl, '[图片] ' + category.name);
|
|
180
|
+
})
|
|
181
|
+
.then(function () {
|
|
182
|
+
resolve();
|
|
183
|
+
})
|
|
184
|
+
.catch(function (error) {
|
|
185
|
+
state.uploading = false;
|
|
186
|
+
notifyUpdate();
|
|
187
|
+
reject(error);
|
|
188
|
+
});
|
|
189
|
+
},
|
|
190
|
+
fail: function (err) {
|
|
191
|
+
reject(err || new Error('选择图片失败'));
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function cancelIconPick() {
|
|
198
|
+
state.iconPickMode = false;
|
|
199
|
+
state.pendingIconCategory = null;
|
|
200
|
+
notifyUpdate();
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function sendQuick(text) {
|
|
204
|
+
state.inputText = text;
|
|
205
|
+
notifyUpdate();
|
|
206
|
+
return handleSend();
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function sendMessage(query, imageUrl, displayContent) {
|
|
210
|
+
if (!ensureLogin()) {
|
|
211
|
+
return Promise.resolve();
|
|
212
|
+
}
|
|
213
|
+
state.messages.push({
|
|
214
|
+
role: 'user',
|
|
215
|
+
content: displayContent || query,
|
|
216
|
+
imageUrls: imageUrl ? [imageUrl] : []
|
|
217
|
+
});
|
|
218
|
+
state.loading = true;
|
|
219
|
+
notifyUpdate();
|
|
220
|
+
|
|
221
|
+
return requestAdapter.sendChat({
|
|
222
|
+
conversationId: state.conversationId || undefined,
|
|
223
|
+
query: query || undefined,
|
|
224
|
+
imageUrl: imageUrl || undefined
|
|
225
|
+
})
|
|
226
|
+
.then(function (data) {
|
|
227
|
+
if (data.conversationId) {
|
|
228
|
+
state.conversationId = data.conversationId;
|
|
229
|
+
}
|
|
230
|
+
state.loading = false;
|
|
231
|
+
handleAssistantAnswer(data.answer || '暂无回复');
|
|
232
|
+
})
|
|
233
|
+
.catch(function (error) {
|
|
234
|
+
state.loading = false;
|
|
235
|
+
state.messages.push(createAssistantMessage(error && (error.msg || error.message) ? (error.msg || error.message) : '发送失败,请稍后重试'));
|
|
236
|
+
notifyUpdate();
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function handleSend() {
|
|
241
|
+
var query = (state.inputText || '').trim();
|
|
242
|
+
var imageUrl = state.pendingImageUrl;
|
|
243
|
+
if ((!query && !imageUrl) || state.loading || state.uploading || state.iconPickMode) {
|
|
244
|
+
return Promise.resolve();
|
|
245
|
+
}
|
|
246
|
+
var displayContent = query || (imageUrl ? '[图片]' : '');
|
|
247
|
+
state.inputText = '';
|
|
248
|
+
state.pendingImageUrl = '';
|
|
249
|
+
notifyUpdate();
|
|
250
|
+
return sendMessage(query, imageUrl, displayContent);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function openConversation() {
|
|
254
|
+
if (!ensureLogin()) {
|
|
255
|
+
return false;
|
|
256
|
+
}
|
|
257
|
+
event.emit('chat:open');
|
|
258
|
+
return true;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
function closeConversation() {
|
|
262
|
+
event.emit('chat:close');
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
module.exports = {
|
|
266
|
+
quickQuestions: quickQuestions,
|
|
267
|
+
reset: reset,
|
|
268
|
+
getSnapshot: getSnapshot,
|
|
269
|
+
setInputText: setInputText,
|
|
270
|
+
clearPendingImage: clearPendingImage,
|
|
271
|
+
chooseImage: chooseImage,
|
|
272
|
+
pickCategoryIcon: pickCategoryIcon,
|
|
273
|
+
cancelIconPick: cancelIconPick,
|
|
274
|
+
sendQuick: sendQuick,
|
|
275
|
+
handleSend: handleSend,
|
|
276
|
+
openConversation: openConversation,
|
|
277
|
+
closeConversation: closeConversation,
|
|
278
|
+
onUpdate: function (handler) {
|
|
279
|
+
event.on('chat:update', handler);
|
|
280
|
+
},
|
|
281
|
+
offUpdate: function (handler) {
|
|
282
|
+
event.off('chat:update', handler);
|
|
283
|
+
},
|
|
284
|
+
onOpen: function (handler) {
|
|
285
|
+
event.on('chat:open', handler);
|
|
286
|
+
},
|
|
287
|
+
offOpen: function (handler) {
|
|
288
|
+
event.off('chat:open', handler);
|
|
289
|
+
},
|
|
290
|
+
onClose: function (handler) {
|
|
291
|
+
event.on('chat:close', handler);
|
|
292
|
+
},
|
|
293
|
+
offClose: function (handler) {
|
|
294
|
+
event.off('chat:close', handler);
|
|
295
|
+
}
|
|
296
|
+
};
|
package/src/config.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
var DEFAULT_CONFIG = {
|
|
2
|
+
baseUrl: '',
|
|
3
|
+
chatApiPath: '/mini/ai/chat',
|
|
4
|
+
uploadApiPath: '/mini/common/upload',
|
|
5
|
+
getToken: function () {
|
|
6
|
+
return '';
|
|
7
|
+
},
|
|
8
|
+
getHeaders: function () {
|
|
9
|
+
return {};
|
|
10
|
+
},
|
|
11
|
+
visiblePages: ['pages/user/user'],
|
|
12
|
+
floatIcon: '',
|
|
13
|
+
requireLogin: true,
|
|
14
|
+
loginPage: '/pages/login/login',
|
|
15
|
+
debug: false,
|
|
16
|
+
enableInterceptor: false
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
var currentConfig = null;
|
|
20
|
+
|
|
21
|
+
function mergeConfig(userConfig) {
|
|
22
|
+
var config = userConfig || {};
|
|
23
|
+
var merged = {};
|
|
24
|
+
for (var key in DEFAULT_CONFIG) {
|
|
25
|
+
if (Object.prototype.hasOwnProperty.call(DEFAULT_CONFIG, key)) {
|
|
26
|
+
var defaultValue = DEFAULT_CONFIG[key];
|
|
27
|
+
var userValue = config[key];
|
|
28
|
+
if (userValue === undefined || userValue === null) {
|
|
29
|
+
merged[key] = defaultValue;
|
|
30
|
+
} else {
|
|
31
|
+
merged[key] = userValue;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return merged;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function setConfig(userConfig) {
|
|
39
|
+
currentConfig = mergeConfig(userConfig);
|
|
40
|
+
return currentConfig;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function getConfig() {
|
|
44
|
+
return currentConfig || mergeConfig(null);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
module.exports = {
|
|
48
|
+
DEFAULT_CONFIG: DEFAULT_CONFIG,
|
|
49
|
+
setConfig: setConfig,
|
|
50
|
+
getConfig: getConfig
|
|
51
|
+
};
|
package/src/event.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
var listeners = {};
|
|
2
|
+
|
|
3
|
+
function on(event, handler) {
|
|
4
|
+
if (typeof handler !== 'function') {
|
|
5
|
+
return;
|
|
6
|
+
}
|
|
7
|
+
if (!listeners[event]) {
|
|
8
|
+
listeners[event] = [];
|
|
9
|
+
}
|
|
10
|
+
listeners[event].push(handler);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function off(event, handler) {
|
|
14
|
+
if (!listeners[event]) {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
if (!handler) {
|
|
18
|
+
listeners[event] = [];
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
listeners[event] = listeners[event].filter(function (fn) {
|
|
22
|
+
return fn !== handler;
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function emit(event, payload) {
|
|
27
|
+
if (!listeners[event]) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
listeners[event].forEach(function (handler) {
|
|
31
|
+
try {
|
|
32
|
+
handler(payload);
|
|
33
|
+
} catch (err) {
|
|
34
|
+
console.error('[mini-dynamic-renderer] event handler error:', err);
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
module.exports = {
|
|
40
|
+
on: on,
|
|
41
|
+
off: off,
|
|
42
|
+
emit: emit
|
|
43
|
+
};
|