@syfei49/mini-dynamic-renderer 1.0.7 → 1.0.9

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 CHANGED
@@ -14,7 +14,7 @@ npm install @syfei49/mini-dynamic-renderer
14
14
  ```json
15
15
  {
16
16
  "dependencies": {
17
- "@syfei49/mini-dynamic-renderer": "^1.0.7"
17
+ "@syfei49/mini-dynamic-renderer": "^1.0.9"
18
18
  }
19
19
  }
20
20
  ```
@@ -33,8 +33,10 @@ import { init } from '@syfei49/mini-dynamic-renderer'
33
33
  import { baseUrl } from '@/utils/request.js'
34
34
 
35
35
  init({
36
- baseUrl: baseUrl,
37
- chatApiPath: '/mini/ai/chat',
36
+ baseUrl: 'https://lygateway.ilyixy.com/prod-api',
37
+ uploadBaseUrl: 'https://jky.mall.ilyixy.com/prod-api',
38
+ appId: 'sass-mall-xxx',
39
+ chatApiPath: '/openapi/mini/ai/chat',
38
40
  uploadApiPath: '/mini/common/upload',
39
41
  getToken: () => uni.getStorageSync('token'),
40
42
  visiblePages: ['pages/user/user'],
@@ -89,11 +91,13 @@ function gotoPath(item) {
89
91
 
90
92
  | 字段 | 类型 | 默认值 | 说明 |
91
93
  |------|------|--------|------|
92
- | `baseUrl` | `string` | `''` | API 根地址 |
94
+ | `baseUrl` | `string` | `''` | API 根地址,示例 `https://lygateway.ilyixy.com/prod-api` |
95
+ | `uploadBaseUrl` | `string` | `''` | 上传接口基地址,默认与 `baseUrl` 一致,示例 `https://jky.mall.ilyixy.com/prod-api` |
96
+ | `appId` | `string` | `''` | 应用标识,配置后每次请求自动带上请求头 `X-App-Id` |
93
97
  | `chatApiPath` | `string` | `'/mini/ai/chat'` | 对话接口路径 |
94
98
  | `uploadApiPath` | `string` | `'/mini/common/upload'` | 图片上传路径 |
95
99
  | `getToken` | `Function` | `() => ''` | 返回 Authorization |
96
- | `getHeaders` | `Function` | `() => ({})` | 额外请求头 |
100
+ | `getHeaders` | `Function` | `() => ({})` | 额外请求头,与自动头合并 |
97
101
  | `visiblePages` | `string[]` | `['pages/user/user']` | 显示浮层图标的页面路由 |
98
102
  | `floatIcon` | `string` | 内置默认图标 | 浮层按钮图片 URL |
99
103
  | `requireLogin` | `boolean` | `true` | 打开前检查登录 |
@@ -101,6 +105,8 @@ function gotoPath(item) {
101
105
  | `debug` | `boolean` | `false` | 调试日志 |
102
106
  | `enableInterceptor` | `boolean` | `false` | 是否启用 wx.request 拦截(预留) |
103
107
 
108
+ > 注意:`appId` 非空时,所有请求头都会自动注入 `X-App-Id: <appId>`。
109
+
104
110
  ## API
105
111
 
106
112
  | 方法 | 说明 |
@@ -115,7 +121,34 @@ function gotoPath(item) {
115
121
 
116
122
  ## 后端协议
117
123
 
118
- 当需要用户上传分类图标时,后端可在 `answer` 中返回:
124
+ ### 1. 动态参数收集(`[RENDER_CONFIG]`)
125
+
126
+ 后端可在 `answer` 中返回 `[RENDER_CONFIG]{...}` 来让插件渲染一个动态表单,用户填写后自动作为 `inputs` 再次发起对话请求。
127
+
128
+ ```text
129
+ 请填写新增商品所需信息 [RENDER_CONFIG]{
130
+ "type": "form",
131
+ "title": "新增商品",
132
+ "description": "请补充以下字段",
133
+ "fields": [
134
+ { "name": "productName", "label": "商品名称", "type": "input" },
135
+ { "name": "price", "label": "价格", "type": "number" },
136
+ { "name": "category", "label": "分类", "type": "select", "options": [{ "label": "保健品", "value": "1" }] },
137
+ { "name": "cover", "label": "封面图", "type": "image" }
138
+ ]
139
+ }
140
+ ```
141
+
142
+ 支持字段类型:
143
+
144
+ - `input`:文本输入框
145
+ - `number`:数字输入框
146
+ - `select`:`picker` 选择器,需配 `options: [{ label, value }]`
147
+ - `image`:拍照 / 相册选择并上传
148
+
149
+ ### 2. 分类图标引导(兼容 `[NEED_ICON]`)
150
+
151
+ 当需要用户上传分类图标时,后端仍可在 `answer` 中返回:
119
152
 
120
153
  ```text
121
154
  [NEED_ICON]{"name": "张三", "sort": 0, "msg": "请上传一张图片作为「张三」的分类图标"}
@@ -37,7 +37,39 @@
37
37
  </view>
38
38
  </scroll-view>
39
39
 
40
- <view v-if="iconPickMode" class="ly-ai-icon-pick-bar">
40
+ <view v-if="renderConfig" class="ly-ai-form-bar">
41
+ <view v-if="renderConfig.title" class="ly-ai-form-title">{{ renderConfig.title }}</view>
42
+ <view v-if="renderConfig.description" class="ly-ai-form-desc">{{ renderConfig.description }}</view>
43
+ <view v-for="(field, fIndex) in renderConfig.fields" :key="fIndex" class="ly-ai-form-item">
44
+ <text class="ly-ai-form-label">{{ field.label || field.name }}</text>
45
+ <template v-if="field.type === 'input' || field.type === 'number'">
46
+ <input class="ly-ai-form-input" :type="field.type === 'number' ? 'number' : 'text'"
47
+ :value="formInputs[field.name]" :placeholder="field.placeholder || '请输入' + (field.label || field.name)"
48
+ :disabled="loading || uploading" @input="onFormInput($event, field.name)" />
49
+ </template>
50
+ <template v-else-if="field.type === 'select'">
51
+ <picker class="ly-ai-form-picker" mode="selector" :range="getFieldOptions(field).labels"
52
+ :value="getFieldIndex(field)" :disabled="loading || uploading" @change="onFormSelect($event, field)">
53
+ <view class="ly-ai-form-picker-text">{{ getFieldSelectedLabel(field) }}</view>
54
+ </picker>
55
+ </template>
56
+ <template v-else-if="field.type === 'image'">
57
+ <view v-if="formInputs[field.name]" class="ly-ai-form-image-wrap">
58
+ <image class="ly-ai-form-image" :src="formInputs[field.name]" mode="aspectFill"
59
+ @click="previewImage(formInputs[field.name], [formInputs[field.name]])" />
60
+ <view class="ly-ai-form-image-remove" @click="onFormImageRemove(field.name)">×</view>
61
+ </view>
62
+ <view v-else class="ly-ai-form-image-btn" :class="{ disabled: loading || uploading }"
63
+ @click="onFormImageChoose(field.name)">拍照 / 相册</view>
64
+ </template>
65
+ </view>
66
+ <view class="ly-ai-form-actions">
67
+ <view class="ly-ai-form-btn confirm" :class="{ disabled: loading || uploading }" @click="submitForm">确认</view>
68
+ <view class="ly-ai-form-btn cancel" :class="{ disabled: loading || uploading }" @click="cancelForm">取消</view>
69
+ </view>
70
+ </view>
71
+
72
+ <view v-else-if="iconPickMode" class="ly-ai-icon-pick-bar">
41
73
  <view class="ly-ai-icon-pick-title">请为「{{ pendingIconCategory && pendingIconCategory.name }}」选择分类图标</view>
42
74
  <view class="ly-ai-icon-pick-actions">
43
75
  <view class="ly-ai-icon-pick-btn" :class="{ disabled: loading || uploading }" @click="pickCategoryIcon('camera')">拍照</view>
@@ -91,6 +123,8 @@
91
123
  const scrollIntoView = ref('')
92
124
  const floatIconUrl = ref('')
93
125
  const keyboardHeight = ref(0)
126
+ const renderConfig = ref(null)
127
+ const formInputs = ref({})
94
128
 
95
129
  const canSend = computed(() => {
96
130
  return !!(inputText.value && inputText.value.trim()) || !!pendingImageUrl.value
@@ -114,6 +148,21 @@
114
148
  let closeHandler = null
115
149
  let keyboardHandler = null
116
150
 
151
+ function initFormInputs() {
152
+ var cfg = renderConfig.value
153
+ var values = {}
154
+ if (cfg && cfg.fields && cfg.fields.length) {
155
+ cfg.fields.forEach(function (field) {
156
+ if (field.type === 'select' && field.options && field.options.length) {
157
+ values[field.name] = field.defaultValue !== undefined ? field.defaultValue : field.options[0].value
158
+ } else {
159
+ values[field.name] = field.defaultValue !== undefined ? field.defaultValue : ''
160
+ }
161
+ })
162
+ }
163
+ formInputs.value = values
164
+ }
165
+
117
166
  function syncState(snapshot) {
118
167
  messages.value = snapshot.messages || []
119
168
  loading.value = snapshot.loading || false
@@ -122,7 +171,9 @@
122
171
  pendingIconCategory.value = snapshot.pendingIconCategory || null
123
172
  pendingImageUrl.value = snapshot.pendingImageUrl || ''
124
173
  inputText.value = snapshot.inputText || ''
174
+ renderConfig.value = snapshot.renderConfig || null
125
175
  quickQuestions.value = snapshot.quickQuestions || []
176
+ initFormInputs()
126
177
  scrollToBottom()
127
178
  }
128
179
 
@@ -208,6 +259,75 @@
208
259
  chatService.cancelIconPick()
209
260
  }
210
261
 
262
+ function getFieldOptions(field) {
263
+ var options = field.options || []
264
+ return {
265
+ labels: options.map(function (opt) {
266
+ return opt.label || opt.value
267
+ }),
268
+ values: options.map(function (opt) {
269
+ return opt.value
270
+ })
271
+ }
272
+ }
273
+
274
+ function getFieldIndex(field) {
275
+ var opts = getFieldOptions(field)
276
+ var index = opts.values.indexOf(formInputs.value[field.name])
277
+ return index >= 0 ? index : 0
278
+ }
279
+
280
+ function getFieldSelectedLabel(field) {
281
+ var index = getFieldIndex(field)
282
+ var opts = getFieldOptions(field)
283
+ var option = field.options ? field.options[index] : null
284
+ return option ? (option.label || option.value) : (opts.labels[index] || '请选择')
285
+ }
286
+
287
+ function onFormInput(e, name) {
288
+ formInputs.value[name] = e.detail.value
289
+ }
290
+
291
+ function onFormSelect(e, field) {
292
+ var index = e.detail.value
293
+ var opts = getFieldOptions(field)
294
+ formInputs.value[field.name] = opts.values[index]
295
+ }
296
+
297
+ function onFormImageChoose(name) {
298
+ chatService.chooseImage()
299
+ .then(function (imageUrl) {
300
+ formInputs.value[name] = imageUrl
301
+ })
302
+ .catch(function (error) {
303
+ uni.showToast({
304
+ title: error && (error.message || error.msg) ? (error.message || error.msg) : '图片上传失败',
305
+ icon: 'none'
306
+ })
307
+ })
308
+ }
309
+
310
+ function onFormImageRemove(name) {
311
+ formInputs.value[name] = ''
312
+ }
313
+
314
+ function submitForm() {
315
+ chatService.submitInputs(formInputs.value)
316
+ .then(function () {
317
+ scrollToBottom()
318
+ })
319
+ .catch(function (error) {
320
+ uni.showToast({
321
+ title: error && (error.message || error.msg) ? (error.message || error.msg) : '提交失败',
322
+ icon: 'none'
323
+ })
324
+ })
325
+ }
326
+
327
+ function cancelForm() {
328
+ chatService.cancelForm()
329
+ }
330
+
211
331
  function clearPendingImage() {
212
332
  chatService.clearPendingImage()
213
333
  }
@@ -554,4 +674,123 @@
554
674
  .ly-ai-send-btn.disabled {
555
675
  opacity: 0.5;
556
676
  }
677
+
678
+ .ly-ai-form-bar {
679
+ padding: 24rpx;
680
+ padding-bottom: calc(24rpx + env(safe-area-inset-bottom));
681
+ background: #fff;
682
+ border-top: 1rpx solid #eee;
683
+ }
684
+
685
+ .ly-ai-form-title {
686
+ font-size: 30rpx;
687
+ font-weight: 600;
688
+ color: #333;
689
+ margin-bottom: 12rpx;
690
+ }
691
+
692
+ .ly-ai-form-desc {
693
+ font-size: 26rpx;
694
+ color: #666;
695
+ line-height: 1.5;
696
+ margin-bottom: 24rpx;
697
+ }
698
+
699
+ .ly-ai-form-item {
700
+ margin-bottom: 24rpx;
701
+ }
702
+
703
+ .ly-ai-form-label {
704
+ display: block;
705
+ font-size: 28rpx;
706
+ color: #333;
707
+ margin-bottom: 12rpx;
708
+ }
709
+
710
+ .ly-ai-form-input {
711
+ height: 72rpx;
712
+ background: #f5f6f8;
713
+ border-radius: 12rpx;
714
+ padding: 0 24rpx;
715
+ font-size: 28rpx;
716
+ color: #333;
717
+ }
718
+
719
+ .ly-ai-form-picker {
720
+ height: 72rpx;
721
+ background: #f5f6f8;
722
+ border-radius: 12rpx;
723
+ padding: 0 24rpx;
724
+ display: flex;
725
+ align-items: center;
726
+ }
727
+
728
+ .ly-ai-form-picker-text {
729
+ font-size: 28rpx;
730
+ color: #333;
731
+ }
732
+
733
+ .ly-ai-form-image-wrap {
734
+ display: flex;
735
+ align-items: center;
736
+ }
737
+
738
+ .ly-ai-form-image {
739
+ width: 160rpx;
740
+ height: 160rpx;
741
+ border-radius: 12rpx;
742
+ }
743
+
744
+ .ly-ai-form-image-remove {
745
+ margin-left: 16rpx;
746
+ width: 48rpx;
747
+ height: 48rpx;
748
+ line-height: 44rpx;
749
+ text-align: center;
750
+ font-size: 36rpx;
751
+ color: #999;
752
+ background: #f5f6f8;
753
+ border-radius: 50%;
754
+ }
755
+
756
+ .ly-ai-form-image-btn {
757
+ width: 200rpx;
758
+ height: 160rpx;
759
+ line-height: 160rpx;
760
+ text-align: center;
761
+ background: #f5f6f8;
762
+ color: #666;
763
+ border-radius: 12rpx;
764
+ font-size: 28rpx;
765
+ }
766
+
767
+ .ly-ai-form-actions {
768
+ display: flex;
769
+ gap: 16rpx;
770
+ margin-top: 12rpx;
771
+ }
772
+
773
+ .ly-ai-form-btn {
774
+ flex: 1;
775
+ height: 80rpx;
776
+ line-height: 80rpx;
777
+ text-align: center;
778
+ border-radius: 12rpx;
779
+ font-size: 28rpx;
780
+ }
781
+
782
+ .ly-ai-form-btn.confirm {
783
+ background: #f84616;
784
+ color: #fff;
785
+ }
786
+
787
+ .ly-ai-form-btn.cancel {
788
+ background: #f5f6f8;
789
+ color: #666;
790
+ }
791
+
792
+ .ly-ai-form-btn.disabled,
793
+ .ly-ai-form-image-btn.disabled {
794
+ opacity: 0.5;
795
+ }
557
796
  </style>
package/index.js CHANGED
@@ -5,7 +5,7 @@ var chatService = require('./src/chat/service');
5
5
  var configModule = require('./src/config');
6
6
 
7
7
  module.exports = {
8
- version: '1.0.7',
8
+ version: '1.0.9',
9
9
  init: initModule.init,
10
10
  getConfig: configModule.getConfig,
11
11
  isInitialized: initModule.isInitialized,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@syfei49/mini-dynamic-renderer",
3
- "version": "1.0.7",
3
+ "version": "1.0.9",
4
4
  "description": "uni-app 浮层 AI 客服插件",
5
5
  "main": "index.js",
6
6
  "files": [
@@ -1,8 +1,9 @@
1
1
  var configModule = require('../config');
2
2
 
3
- function getFullUrl(path) {
3
+ function getFullUrl(path, useUploadBase) {
4
4
  var config = configModule.getConfig();
5
- var baseUrl = config.baseUrl || '';
5
+ var baseUrl = (useUploadBase && config.uploadBaseUrl) ? config.uploadBaseUrl : config.baseUrl;
6
+ baseUrl = baseUrl || '';
6
7
  var normalizedPath = (path || '').replace(/^\/+/, '/');
7
8
  if (baseUrl.slice(-1) === '/' && normalizedPath.indexOf('/') === 0) {
8
9
  return baseUrl + normalizedPath.slice(1);
@@ -20,6 +21,9 @@ function getHeaders(extraHeaders) {
20
21
  if (token) {
21
22
  headers.Authorization = token;
22
23
  }
24
+ if (config.appId) {
25
+ headers['X-App-Id'] = config.appId;
26
+ }
23
27
  if (extraHeaders) {
24
28
  Object.assign(headers, extraHeaders);
25
29
  }
@@ -32,16 +36,20 @@ function sendChat(payload) {
32
36
  var headers = getHeaders({
33
37
  'Content-Type': 'application/json'
34
38
  });
39
+ var data = Object.assign({}, payload || {});
40
+ if (!data.inputs) {
41
+ delete data.inputs;
42
+ }
35
43
 
36
44
  if (config.debug) {
37
- console.log('[mini-dynamic-renderer] sendChat', url, payload);
45
+ console.log('[mini-dynamic-renderer] sendChat', url, data);
38
46
  }
39
47
 
40
48
  return new Promise(function (resolve, reject) {
41
49
  uni.request({
42
50
  url: url,
43
51
  method: 'POST',
44
- data: payload,
52
+ data: data,
45
53
  header: headers,
46
54
  timeout: 120000,
47
55
  success: function (res) {
@@ -61,7 +69,7 @@ function sendChat(payload) {
61
69
 
62
70
  function uploadImage(filePath) {
63
71
  var config = configModule.getConfig();
64
- var url = getFullUrl(config.uploadApiPath);
72
+ var url = getFullUrl(config.uploadApiPath, true);
65
73
  var headers = getHeaders();
66
74
 
67
75
  if (config.debug) {
@@ -48,8 +48,36 @@ function parseNeedIcon(answer) {
48
48
  }
49
49
  }
50
50
 
51
+ function parseRenderConfig(answer) {
52
+ if (!answer || typeof answer !== 'string') {
53
+ return null;
54
+ }
55
+ var renderIdx = answer.indexOf('[RENDER_CONFIG]');
56
+ if (renderIdx !== -1) {
57
+ var jsonPart = answer.substring(renderIdx + '[RENDER_CONFIG]'.length);
58
+ var start = jsonPart.indexOf('{');
59
+ var end = jsonPart.lastIndexOf('}');
60
+ if (start === -1 || end === -1 || end < start) {
61
+ return null;
62
+ }
63
+ try {
64
+ return JSON.parse(jsonPart.substring(start, end + 1));
65
+ } catch (e) {
66
+ return null;
67
+ }
68
+ }
69
+ if (answer.indexOf('[NEED_ICON]') !== -1) {
70
+ return {
71
+ type: 'iconPicker',
72
+ fields: [{ name: 'imageUrl', label: '分类图标', type: 'image' }]
73
+ };
74
+ }
75
+ return null;
76
+ }
77
+
51
78
  module.exports = {
52
79
  cleanImageUrl: cleanImageUrl,
53
80
  parseAssistantMessage: parseAssistantMessage,
54
- parseNeedIcon: parseNeedIcon
81
+ parseNeedIcon: parseNeedIcon,
82
+ parseRenderConfig: parseRenderConfig
55
83
  };
@@ -11,7 +11,10 @@ var state = {
11
11
  iconPickMode: false,
12
12
  pendingIconCategory: null,
13
13
  pendingImageUrl: '',
14
- inputText: ''
14
+ inputText: '',
15
+ renderConfig: null,
16
+ pendingInputs: {},
17
+ lastUserQuery: ''
15
18
  };
16
19
 
17
20
  var quickQuestions = [
@@ -29,6 +32,9 @@ function reset() {
29
32
  state.pendingIconCategory = null;
30
33
  state.pendingImageUrl = '';
31
34
  state.inputText = '';
35
+ state.renderConfig = null;
36
+ state.pendingInputs = {};
37
+ state.lastUserQuery = '';
32
38
  }
33
39
 
34
40
  function ensureLogin() {
@@ -65,6 +71,8 @@ function getSnapshot() {
65
71
  pendingIconCategory: state.pendingIconCategory,
66
72
  pendingImageUrl: state.pendingImageUrl,
67
73
  inputText: state.inputText,
74
+ renderConfig: state.renderConfig,
75
+ pendingInputs: Object.assign({}, state.pendingInputs),
68
76
  quickQuestions: quickQuestions.slice()
69
77
  };
70
78
  }
@@ -86,6 +94,7 @@ function handleAssistantAnswer(answer) {
86
94
  sort: needIcon.sort
87
95
  };
88
96
  state.iconPickMode = true;
97
+ state.renderConfig = null;
89
98
  state.messages.push({
90
99
  role: 'assistant',
91
100
  content: needIcon.msg,
@@ -94,8 +103,19 @@ function handleAssistantAnswer(answer) {
94
103
  notifyUpdate();
95
104
  return;
96
105
  }
106
+ var renderConfig = parser.parseRenderConfig(answer);
107
+ if (renderConfig) {
108
+ state.iconPickMode = false;
109
+ state.pendingIconCategory = null;
110
+ state.renderConfig = renderConfig;
111
+ var cleanedAnswer = answer.replace(/\[RENDER_CONFIG\]\{[\s\S]*\}/, '').replace(/\[NEED_ICON\]\{[\s\S]*\}/, '').trim();
112
+ state.messages.push(createAssistantMessage(cleanedAnswer || '请填写以下信息'));
113
+ notifyUpdate();
114
+ return;
115
+ }
97
116
  state.iconPickMode = false;
98
117
  state.pendingIconCategory = null;
118
+ state.renderConfig = null;
99
119
  state.messages.push(createAssistantMessage(answer));
100
120
  notifyUpdate();
101
121
  }
@@ -206,23 +226,11 @@ function sendQuick(text) {
206
226
  return handleSend();
207
227
  }
208
228
 
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
- });
229
+ function doSendChat(payload) {
218
230
  state.loading = true;
219
231
  notifyUpdate();
220
232
 
221
- return requestAdapter.sendChat({
222
- conversationId: state.conversationId || undefined,
223
- query: query || undefined,
224
- imageUrl: imageUrl || undefined
225
- })
233
+ return requestAdapter.sendChat(payload)
226
234
  .then(function (res) {
227
235
  var data = res && res.data ? res.data : res;
228
236
  if (data.conversationId) {
@@ -238,10 +246,63 @@ function sendMessage(query, imageUrl, displayContent) {
238
246
  });
239
247
  }
240
248
 
249
+ function sendMessage(query, imageUrl, displayContent) {
250
+ if (!ensureLogin()) {
251
+ return Promise.resolve();
252
+ }
253
+ state.lastUserQuery = displayContent || query || '';
254
+ state.messages.push({
255
+ role: 'user',
256
+ content: displayContent || query,
257
+ imageUrls: imageUrl ? [imageUrl] : []
258
+ });
259
+
260
+ return doSendChat({
261
+ conversationId: state.conversationId || undefined,
262
+ query: query || undefined,
263
+ imageUrl: imageUrl || undefined,
264
+ inputs: state.pendingInputs || {}
265
+ }).then(function () {
266
+ state.pendingInputs = {};
267
+ });
268
+ }
269
+
270
+ function submitInputs(inputs) {
271
+ inputs = inputs || {};
272
+ var hasOwn = Object.prototype.hasOwnProperty;
273
+ for (var key in inputs) {
274
+ if (hasOwn.call(inputs, key)) {
275
+ state.pendingInputs[key] = inputs[key];
276
+ }
277
+ }
278
+ state.renderConfig = null;
279
+ state.pendingImageUrl = '';
280
+ notifyUpdate();
281
+
282
+ if (!ensureLogin()) {
283
+ return Promise.resolve();
284
+ }
285
+
286
+ return doSendChat({
287
+ conversationId: state.conversationId || undefined,
288
+ query: state.lastUserQuery || undefined,
289
+ inputs: state.pendingInputs
290
+ }).then(function () {
291
+ state.pendingInputs = {};
292
+ });
293
+ }
294
+
295
+ function cancelForm() {
296
+ state.renderConfig = null;
297
+ state.pendingInputs = {};
298
+ state.pendingImageUrl = '';
299
+ notifyUpdate();
300
+ }
301
+
241
302
  function handleSend() {
242
303
  var query = (state.inputText || '').trim();
243
304
  var imageUrl = state.pendingImageUrl;
244
- if ((!query && !imageUrl) || state.loading || state.uploading || state.iconPickMode) {
305
+ if ((!query && !imageUrl) || state.loading || state.uploading || state.iconPickMode || state.renderConfig) {
245
306
  return Promise.resolve();
246
307
  }
247
308
  var displayContent = query || (imageUrl ? '[图片]' : '');
@@ -272,6 +333,8 @@ module.exports = {
272
333
  chooseImage: chooseImage,
273
334
  pickCategoryIcon: pickCategoryIcon,
274
335
  cancelIconPick: cancelIconPick,
336
+ submitInputs: submitInputs,
337
+ cancelForm: cancelForm,
275
338
  sendQuick: sendQuick,
276
339
  handleSend: handleSend,
277
340
  openConversation: openConversation,
package/src/config.js CHANGED
@@ -1,5 +1,7 @@
1
1
  var DEFAULT_CONFIG = {
2
2
  baseUrl: '',
3
+ uploadBaseUrl: '',
4
+ appId: '',
3
5
  chatApiPath: '/mini/ai/chat',
4
6
  uploadApiPath: '/mini/common/upload',
5
7
  getToken: function () {