@syfei49/mini-dynamic-renderer 1.0.3 → 1.0.7

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
@@ -1,6 +1,6 @@
1
1
  # @syfei49/mini-dynamic-renderer
2
2
 
3
- uni-app 浮层 AI 客服插件(v1.0.3)。在指定页面右下角显示 AI 浮层图标,点击后从底部弹出抽屉聊天面板,支持文字、图片、拍照/相册上传及 `[NEED_ICON]` 分类图标引导。
3
+ uni-app 浮层 AI 客服插件。在指定页面右下角显示 AI 浮层图标,点击后从底部弹出抽屉聊天面板,支持文字、图片、拍照/相册上传及 `[NEED_ICON]` 分类图标引导。
4
4
 
5
5
  ## 安装
6
6
 
@@ -9,54 +9,80 @@ cd your-uniapp-project
9
9
  npm install @syfei49/mini-dynamic-renderer
10
10
  ```
11
11
 
12
+ 本地调试可指向 tar 包:
13
+
14
+ ```json
15
+ {
16
+ "dependencies": {
17
+ "@syfei49/mini-dynamic-renderer": "^1.0.7"
18
+ }
19
+ }
20
+ ```
21
+
12
22
  微信开发者工具 → **工具** → **构建 npm**
13
23
 
14
24
  ## 接入
15
25
 
16
26
  ### 1. App.vue 初始化
17
27
 
28
+ 只负责注入配置,不渲染 UI(微信小程序 `App.vue` 的 template 不会显示)。
29
+
18
30
  ```vue
19
31
  <script setup>
20
32
  import { init } from '@syfei49/mini-dynamic-renderer'
33
+ import { baseUrl } from '@/utils/request.js'
21
34
 
22
35
  init({
23
- baseUrl: 'https://your-api.com/prod-api',
36
+ baseUrl: baseUrl,
24
37
  chatApiPath: '/mini/ai/chat',
25
38
  uploadApiPath: '/mini/common/upload',
26
39
  getToken: () => uni.getStorageSync('token'),
27
40
  visiblePages: ['pages/user/user'],
28
41
  requireLogin: true,
29
- loginPage: '/pages/login/login?noauto=1'
42
+ loginPage: '/pages/login/login?noauto=1',
43
+ debug: process.env.NODE_ENV === 'development'
30
44
  })
31
45
  </script>
32
46
  ```
33
47
 
34
- ### 2. 在需要显示浮层的页面引入组件
48
+ ### 2. 目标页面引入组件
35
49
 
36
- 以 `pages/user/user.vue` 为例:
50
+ 以 `pages/user/user.vue` 为例,模板末尾挂载一次:
37
51
 
38
52
  ```vue
39
- <script setup>
40
- import LyAiAssistant from '@syfei49/mini-dynamic-renderer/components/ly-ai-assistant/ly-ai-assistant.vue'
41
- import { openChat } from '@syfei49/mini-dynamic-renderer'
42
- </script>
43
-
44
53
  <template>
45
- <view>
46
- <!-- 页面内容 -->
54
+ <view class="container">
55
+ <!-- 页面原有内容 -->
56
+ <lyTabbar server="sc"></lyTabbar>
47
57
  <LyAiAssistant />
48
58
  </view>
49
59
  </template>
60
+
61
+ <script setup>
62
+ import LyAiAssistant from '@syfei49/mini-dynamic-renderer/components/ly-ai-assistant/ly-ai-assistant.vue'
63
+ import { openChat } from '@syfei49/mini-dynamic-renderer'
64
+ </script>
50
65
  ```
51
66
 
52
- > 微信小程序 `App.vue` template 不会被渲染,所以浮层组件需要放在具体页面里。
67
+ ### 3. openChat 打开抽屉
53
68
 
54
- ### 2. 在任意页面打开抽屉(可选)
69
+ 把旧入口从 `uni.navigateTo('/pages/user/aiChat')` 改为直接打开抽屉:
55
70
 
56
71
  ```javascript
57
- import { openChat } from '@syfei49/mini-dynamic-renderer'
58
-
59
- openChat()
72
+ function gotoPath(item) {
73
+ if (item.title === '分销中心') {
74
+ handleDistributionCenter()
75
+ return
76
+ }
77
+
78
+ if (item.title === 'AI客服') {
79
+ openChat()
80
+ return
81
+ }
82
+
83
+ // 其他页面保持原来的 uni.navigateTo 逻辑
84
+ uni.navigateTo({ url: item.path })
85
+ }
60
86
  ```
61
87
 
62
88
  ## 配置项
@@ -87,8 +113,18 @@ openChat()
87
113
  | `uninstall()` | 卸载拦截器,恢复原始 `wx.request` |
88
114
  | `parseRenderConfig(data)` | 解析响应中的渲染配置(预留) |
89
115
 
116
+ ## 后端协议
117
+
118
+ 当需要用户上传分类图标时,后端可在 `answer` 中返回:
119
+
120
+ ```text
121
+ [NEED_ICON]{"name": "张三", "sort": 0, "msg": "请上传一张图片作为「张三」的分类图标"}
122
+ ```
123
+
124
+ 插件会弹出「拍照 / 从相册选择 / 取消」选择条,并在上传后自动把图标 URL 拼回对话继续请求。
125
+
90
126
  ## 版本说明
91
127
 
92
- - **v1.0.3**:uni-app 浮层 AI 插件、抽屉聊天、迁移 aiChat 全能力、文档拆分
128
+ - **v1.0.x**:uni-app 浮层 AI 插件、抽屉聊天、迁移 aiChat 全能力、文档拆分
93
129
  - v1.0.0:wx.request 拦截 + hello world 弹窗(测试版)
94
130
  - 后续版本将根据后端 `renderConfig` 动态渲染更多组件类型
@@ -5,8 +5,8 @@
5
5
  <view v-else class="ly-ai-float-text">AI</view>
6
6
  </view>
7
7
 
8
- <view v-if="opened" class="ly-ai-overlay" @click.self="closeChat">
9
- <view class="ly-ai-drawer">
8
+ <view v-if="opened" class="ly-ai-overlay" @click="onOverlayClick">
9
+ <view class="ly-ai-drawer" :style="drawerStyle" @click.stop>
10
10
  <view class="ly-ai-header">
11
11
  <text class="ly-ai-title">AI 智能客服</text>
12
12
  <view class="ly-ai-close" @click="closeChat">×</view>
@@ -55,7 +55,7 @@
55
55
  <view class="ly-ai-input-bar">
56
56
  <view class="ly-ai-attach-btn" :class="{ disabled: loading || uploading }" @click="chooseImage">+</view>
57
57
  <input class="ly-ai-chat-input" :value="inputText" placeholder="请输入您的问题" confirm-type="send"
58
- :disabled="loading || uploading" @input="onInput" @confirm="handleSend" />
58
+ :disabled="loading || uploading" :adjust-position="false" :hold-keyboard="true" @input="onInput" @confirm="handleSend" />
59
59
  <view class="ly-ai-send-btn" :class="{ disabled: loading || uploading || !canSend }" @click="handleSend">发送</view>
60
60
  </view>
61
61
  </template>
@@ -90,6 +90,7 @@
90
90
  const quickQuestions = ref([])
91
91
  const scrollIntoView = ref('')
92
92
  const floatIconUrl = ref('')
93
+ const keyboardHeight = ref(0)
93
94
 
94
95
  const canSend = computed(() => {
95
96
  return !!(inputText.value && inputText.value.trim()) || !!pendingImageUrl.value
@@ -102,9 +103,16 @@
102
103
  }
103
104
  })
104
105
 
106
+ const drawerStyle = computed(() => {
107
+ return {
108
+ bottom: keyboardHeight.value + 'px'
109
+ }
110
+ })
111
+
105
112
  let updateHandler = null
106
113
  let openHandler = null
107
114
  let closeHandler = null
115
+ let keyboardHandler = null
108
116
 
109
117
  function syncState(snapshot) {
110
118
  messages.value = snapshot.messages || []
@@ -144,14 +152,23 @@
144
152
  return
145
153
  }
146
154
  opened.value = true
155
+ keyboardHeight.value = 0
147
156
  scrollToBottom()
148
157
  }
149
158
 
150
159
  function closeChat() {
151
160
  opened.value = false
161
+ keyboardHeight.value = 0
152
162
  chatService.closeConversation()
153
163
  }
154
164
 
165
+ function onOverlayClick(e) {
166
+ if (e && e.target && e.currentTarget && e.target !== e.currentTarget) {
167
+ return
168
+ }
169
+ closeChat()
170
+ }
171
+
155
172
  function onInput(e) {
156
173
  inputText.value = e.detail.value
157
174
  chatService.setInputText(e.detail.value)
@@ -214,14 +231,25 @@
214
231
  }
215
232
  openHandler = function () {
216
233
  opened.value = true
234
+ keyboardHeight.value = 0
217
235
  scrollToBottom()
218
236
  }
219
237
  closeHandler = function () {
220
238
  opened.value = false
239
+ keyboardHeight.value = 0
240
+ }
241
+ keyboardHandler = function (res) {
242
+ keyboardHeight.value = res.height || 0
243
+ if (keyboardHeight.value > 0) {
244
+ scrollToBottom()
245
+ }
221
246
  }
222
247
  chatService.onUpdate(updateHandler)
223
248
  chatService.onOpen(openHandler)
224
249
  chatService.onClose(closeHandler)
250
+ if (typeof uni.onKeyboardHeightChange === 'function') {
251
+ uni.onKeyboardHeightChange(keyboardHandler)
252
+ }
225
253
  syncState(chatService.getSnapshot())
226
254
  checkVisible()
227
255
  })
@@ -236,6 +264,9 @@
236
264
  if (closeHandler) {
237
265
  chatService.offClose(closeHandler)
238
266
  }
267
+ if (keyboardHandler && typeof uni.offKeyboardHeightChange === 'function') {
268
+ uni.offKeyboardHeightChange(keyboardHandler)
269
+ }
239
270
  })
240
271
 
241
272
  onShow(() => {
@@ -276,18 +307,20 @@
276
307
  bottom: 0;
277
308
  z-index: 10000;
278
309
  background: rgba(0, 0, 0, 0.4);
279
- display: flex;
280
- flex-direction: column;
281
- justify-content: flex-end;
282
310
  }
283
311
 
284
312
  .ly-ai-drawer {
313
+ position: absolute;
314
+ left: 0;
315
+ right: 0;
316
+ bottom: 0;
285
317
  height: 82vh;
286
318
  background: #f5f6f8;
287
319
  border-radius: 32rpx 32rpx 0 0;
288
320
  display: flex;
289
321
  flex-direction: column;
290
322
  overflow: hidden;
323
+ transition: bottom 0.2s ease;
291
324
  }
292
325
 
293
326
  .ly-ai-header {
@@ -322,6 +355,7 @@
322
355
  .ly-ai-list {
323
356
  flex: 1;
324
357
  padding: 24rpx;
358
+ padding-bottom: calc(24rpx + env(safe-area-inset-bottom));
325
359
  box-sizing: border-box;
326
360
  }
327
361
 
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.1',
8
+ version: '1.0.7',
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.3",
3
+ "version": "1.0.7",
4
4
  "description": "uni-app 浮层 AI 客服插件",
5
5
  "main": "index.js",
6
6
  "files": [
@@ -223,7 +223,8 @@ function sendMessage(query, imageUrl, displayContent) {
223
223
  query: query || undefined,
224
224
  imageUrl: imageUrl || undefined
225
225
  })
226
- .then(function (data) {
226
+ .then(function (res) {
227
+ var data = res && res.data ? res.data : res;
227
228
  if (data.conversationId) {
228
229
  state.conversationId = data.conversationId;
229
230
  }