vue_zhongyou 1.0.23 → 1.0.24

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vue_zhongyou",
3
- "version": "1.0.23",
3
+ "version": "1.0.24",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "keywords": [],
@@ -30,10 +30,13 @@
30
30
  <!-- 文字介绍 -->
31
31
  <div v-if="message.output" class="message-output" v-html="formatMessage(message.output)"></div>
32
32
  <!-- 表单内容 -->
33
- <ChatForm
33
+ <DynamicMobileForm
34
+ v-model="message.formData"
34
35
  :schema="message.formSchema"
35
- :model-value="message.formData || {}"
36
- @submit="handleFormSubmit(index, $event)"
36
+ submit-button-label="确认"
37
+ reset-button-label="重置"
38
+ @submit="handleSubmit"
39
+ @change="handleChange"
37
40
  />
38
41
  </div>
39
42
 
@@ -81,7 +84,12 @@
81
84
  </div>
82
85
 
83
86
  <!-- 输入区域 -->
84
- <div class="input-area">
87
+ <inputCom
88
+ ref="inputComRef"
89
+ :isLoading="isLoading"
90
+ @sendMessage="sendMessage"
91
+ ></inputCom>
92
+ <!-- <div class="input-area">
85
93
  <div class="input-wrapper">
86
94
  <van-field
87
95
  v-model="inputText"
@@ -106,15 +114,17 @@
106
114
  发送
107
115
  </van-button>
108
116
  </div>
109
- </div>
117
+ </div> -->
110
118
  </div>
111
119
  </template>
112
120
 
113
121
  <script setup>
114
- import { ref, nextTick, onMounted, watch } from 'vue'
122
+ import { ref, nextTick, onMounted, watch, onUnmounted } from 'vue'
115
123
  import { showToast } from 'vant'
116
124
  import ChatForm from '@/components/ChatForm.vue'
117
125
  import NewChatCardList from '@/components/newChatCardList.vue'
126
+ import DynamicMobileForm from '@/components/dynamicMobileForm.vue'
127
+ import inputCom from '@/components/inputCom.vue'
118
128
 
119
129
  // 消息列表
120
130
  const messages = ref([])
@@ -126,6 +136,13 @@ const isLoading = ref(false)
126
136
  const chatContainerRef = ref(null)
127
137
  // 会话ID
128
138
  const sessionId = ref('')
139
+ // 输入组件引用
140
+ const inputComRef = ref(null)
141
+ // SSE连接引用
142
+ const eventSourceRef = ref(null)
143
+ // 当前正在接收流式响应的消息索引
144
+ const streamingMessageIndex = ref(-1)
145
+
129
146
 
130
147
  // 判断消息类型
131
148
  const isTextMessage = (message) => {
@@ -190,10 +207,8 @@ const generateSuggestions = (content) => {
190
207
  // 这里可以根据实际内容生成相关的推荐信息
191
208
  // 目前先使用一些示例推荐信息
192
209
  const sampleSuggestions = [
193
- '您还可以询问相关问题',
194
- '了解更多详细信息',
195
- '查看相关文档',
196
- '获取更多帮助'
210
+ '查找当前待办',
211
+ '发起出差流程',
197
212
  ]
198
213
 
199
214
  // 根据内容类型生成不同的推荐信息
@@ -206,9 +221,9 @@ const generateSuggestions = (content) => {
206
221
  return ['比较不同产品', '查看用户评价', '获取优惠信息']
207
222
  }
208
223
  } else if (content && content.type === 'form') {
209
- return ['查看表单填写示例', '了解字段含义', '下载填写模板']
224
+ return ['查找当前待办', '查看我发起的流程',]
210
225
  } else if (content && content.type === 'cardList') {
211
- return ['筛选产品类别', '按价格排序', '查看更多产品']
226
+ return ['创建出差流程', '创建请假流程', '查看待办列表']
212
227
  }
213
228
 
214
229
  // 默认推荐信息
@@ -217,169 +232,165 @@ const generateSuggestions = (content) => {
217
232
 
218
233
  // 发送消息
219
234
  const sendMessage = async () => {
220
- const text = inputText.value.trim()
235
+ const text = inputComRef.value.inputText.trim()
221
236
  if (!text || isLoading.value) return
222
237
 
238
+ // 关闭之前的SSE连接
239
+ if (eventSourceRef.value) {
240
+ eventSourceRef.value.close()
241
+ eventSourceRef.value = null
242
+ }
243
+
223
244
  // 添加用户消息
224
245
  addMessage('user', text)
225
- inputText.value = ''
246
+ inputComRef.value.inputText = ''
226
247
 
227
248
  // 添加AI加载消息
228
249
  addMessage('assistant', '', true)
229
250
  isLoading.value = true
230
251
 
231
- // 拼装一下请求体
232
- let requestBody = {
252
+ // 记录当前消息索引
253
+ streamingMessageIndex.value = messages.value.length - 1
254
+
255
+ // 拼装请求体
256
+ const requestBody = {
233
257
  sessionId: sessionId.value,
234
258
  input: text,
235
259
  payload: {
236
- sceneType : "QUERY",
237
- queryType : "CREATOR",
238
- processType : "BUSINESS_TRIP",
260
+ sceneType: "QUERY",
261
+ queryType: "CREATOR",
262
+ processType: "BUSINESS_TRIP",
239
263
  queryBackLogReq: {
240
264
  userId: '123456',
241
- pageNum:1,
265
+ pageNum: 1,
242
266
  pageSize: 10
243
267
  }
244
268
  }
245
269
  }
246
270
 
247
271
  try {
248
- // 模拟AI回复(实际应该调用API)
249
- await simulateAIResponse(text)
272
+ // 创建SSE连接
273
+ const baseURL = import.meta.env.VITE_API_BASE_URL || ''
274
+ const url = `${baseURL}/api/ai/chat/stream`
275
+
276
+ // 构建查询参数
277
+ const params = new URLSearchParams()
278
+ params.append('sessionId', requestBody.sessionId)
279
+ params.append('input', requestBody.input)
280
+ params.append('payload', JSON.stringify(requestBody.payload))
281
+
282
+ const fullUrl = `${url}?${params.toString()}`
283
+
284
+ // 创建EventSource连接
285
+ eventSourceRef.value = new EventSource(fullUrl)
286
+
287
+ // 监听消息事件
288
+ eventSourceRef.value.onmessage = (event) => {
289
+ try {
290
+ const data = JSON.parse(event.data)
291
+ handleSSEMessage(data)
292
+ } catch (error) {
293
+ console.error('解析SSE消息失败:', error)
294
+ }
295
+ }
296
+
297
+ // 监听错误事件
298
+ eventSourceRef.value.onerror = (error) => {
299
+ console.error('SSE连接错误:', error)
300
+ handleSSEError()
301
+ }
302
+
303
+ // 监听连接打开事件
304
+ eventSourceRef.value.onopen = () => {
305
+ console.log('SSE连接已建立')
306
+ }
307
+
250
308
  } catch (error) {
251
- console.error('发送消息失败:', error)
252
- showToast('发送失败,请重试')
253
- // 移除加载中的消息
254
- messages.value.pop()
255
- } finally {
256
- isLoading.value = false
309
+ console.error('创建SSE连接失败:', error)
310
+ showToast('连接失败,请重试')
311
+ handleSSEError()
257
312
  }
258
313
  }
259
314
 
260
- // 模拟AI回复(实际应该替换为真实的API调用)
261
- const simulateAIResponse = async (userMessage) => {
262
- // 模拟网络延迟
263
- await new Promise(resolve => setTimeout(resolve, 1000 + Math.random() * 1000))
264
-
265
- // 移除加载中的消息
266
- const loadingMessage = messages.value.pop()
267
-
268
- // 生成模拟回复
269
- let response = ''
270
- if (userMessage.includes('你好') || userMessage.includes('您好')) {
271
- response = '您好!很高兴为您服务。'
272
- } else if (userMessage.includes('帮助') || userMessage.includes('功能')) {
273
- response = '我可以帮您解答问题、提供建议、分析数据等。请告诉我您需要什么帮助?'
274
- } else if (userMessage.includes('时间')) {
275
- response = `当前时间是:${new Date().toLocaleString('zh-CN')}`
276
- } else if (userMessage.includes('表单')) {
277
- // 返回表单类型消息,带介绍文字
278
- response = {
279
- type: 'form',
280
- output: '请您填写以下信息,以便我们为您提供更好的服务。所有信息都将严格保密,请放心填写。',
281
- schema: [
282
- {
283
- field: 'name',
284
- label: '姓名',
285
- type: 'input',
286
- placeholder: '请输入您的姓名',
287
- rules: [{ required: true, message: '请输入姓名' }]
288
- },
289
- {
290
- field: 'email',
291
- label: '邮箱',
292
- type: 'input',
293
- placeholder: '请输入您的邮箱',
294
- rules: [{ required: true, message: '请输入邮箱' }, { pattern: /\w+@\w+\.\w+/, message: '邮箱格式不正确' }]
295
- },
296
- {
297
- field: 'age',
298
- label: '年龄',
299
- type: 'input',
300
- placeholder: '请输入您的年龄'
301
- },
302
- {
303
- field: 'gender',
304
- label: '性别',
305
- type: 'radio',
306
- options: [
307
- { label: '男', value: 'male' },
308
- { label: '女', value: 'female' }
309
- ]
310
- },
311
- {
312
- field: 'hobbies',
313
- label: '爱好',
314
- type: 'checkbox',
315
- options: [
316
- { label: '阅读', value: 'reading' },
317
- { label: '运动', value: 'sports' },
318
- { label: '音乐', value: 'music' },
319
- { label: '旅行', value: 'travel' }
320
- ]
321
- },
322
- {
323
- field: 'bio',
324
- label: '个人简介',
325
- type: 'textarea',
326
- placeholder: '请简单介绍一下自己',
327
- rows: 3
328
- }
329
- ]
315
+ // 处理SSE消息
316
+ const handleSSEMessage = (data) => {
317
+ const messageIndex = streamingMessageIndex.value
318
+ if (messageIndex === -1 || !messages.value[messageIndex]) return
319
+
320
+ const currentMessage = messages.value[messageIndex]
321
+
322
+ // 根据消息类型处理
323
+ if (data.type === 'text') {
324
+ // 文本流式更新
325
+ if (typeof currentMessage.content !== 'string') {
326
+ currentMessage.content = ''
330
327
  }
331
- } else if (userMessage.includes('列表') || userMessage.includes('卡片')) {
332
- // 返回列表卡片类型消息,带介绍文字
333
- // TODO 生成这种卡片列表的时候,要初始化一个这张卡片的独有的page,用来做分页
334
- const items = [
335
- {
336
- title: '产品A',
337
- subtitle: '高性能计算服务',
338
- statusName : '待审',
339
- creator : '刘松煜',
340
- createTime : '2023-10-01 10:00:00',
341
- currentNode : '节点1',
342
- dpName : '信息科技部',
343
- },
344
- {
345
- title: '产品B',
346
- subtitle: '云存储解决方案',
347
- statusName : '驳回',
348
- creator : '刘松煜',
349
- createTime : '2023-10-01 10:00:00',
350
- currentNode : '节点1',
351
- dpName : '固定收益投资部',
352
- },
353
- {
354
- title: '信息科技部xxxaaaa啊啊啊你还是低哦是大家扫IDiOS滴哦啊啊大家送啊点喝酒哦洒几滴撒娇都i爱睡觉',
355
- subtitle: '人工智能平台',
356
- statusName : '待审',
357
- creator : '刘松煜',
358
- createTime : '2023-10-01',
359
- currentNode : '节点1',
360
- dpName : '信息科技部',
361
- }
362
- ];
363
- const total = 10
328
+ currentMessage.content += data.content || ''
329
+ scrollToBottom()
330
+ } else if (data.type === 'form') {
331
+ // 表单消息
332
+ currentMessage.type = 'form'
333
+ currentMessage.formSchema = data.schema
334
+ currentMessage.formData = data.data || {}
335
+ if (data.output) {
336
+ currentMessage.output = data.output
337
+ }
338
+ } else if (data.type === 'cardList') {
339
+ // 列表卡片消息
340
+ for (let key in data) {
341
+ currentMessage[key] = data[key]
342
+ }
343
+ } else if (data.type === 'done') {
344
+ // 流式传输完成
345
+ currentMessage.loading = false
346
+ isLoading.value = false
347
+
348
+ // 为AI助手消息添加推荐信息
349
+ currentMessage.suggestions = generateSuggestions(currentMessage.content)
364
350
 
365
- response = {
366
- type: 'cardList',
367
- output: '根据您的需求,我们为您推荐以下几款热门产品,您可以点击查看详细信息或进行相关操作:',
368
- showLoadMore: items.length < total, // 根据当前items长度与总数的比较来确定是否显示"查看更多"
369
- currentPage: 1,
370
- pageSize: 10,
371
- total: total,
372
- items: items,
373
- loading: false // 初始化加载状态
351
+ // 关闭SSE连接
352
+ if (eventSourceRef.value) {
353
+ eventSourceRef.value.close()
354
+ eventSourceRef.value = null
374
355
  }
375
- } else {
376
- response = `我理解您说的是:"${userMessage}"。这是一个很好的问题,让我为您详细解答...\n\n(这是模拟回复,实际使用时需要接入真实的AI API)`
356
+ streamingMessageIndex.value = -1
357
+ } else if (data.type === 'error') {
358
+ // 错误消息
359
+ handleSSEError(data.message || '服务器错误')
377
360
  }
361
+ }
378
362
 
379
- // 添加AI回复
380
- addMessage('assistant', response, false)
363
+ // 处理SSE错误
364
+ const handleSSEError = (errorMessage = '连接中断,请重试') => {
365
+ console.error('SSE错误:', errorMessage)
366
+
367
+ const messageIndex = streamingMessageIndex.value
368
+ if (messageIndex !== -1 && messages.value[messageIndex]) {
369
+ messages.value[messageIndex].loading = false
370
+ if (!messages.value[messageIndex].content) {
371
+ messages.value[messageIndex].content = errorMessage
372
+ }
373
+ }
374
+
375
+ isLoading.value = false
376
+ showToast(errorMessage)
377
+
378
+ // 关闭SSE连接
379
+ if (eventSourceRef.value) {
380
+ eventSourceRef.value.close()
381
+ eventSourceRef.value = null
382
+ }
383
+ streamingMessageIndex.value = -1
381
384
  }
382
385
 
386
+ // 组件卸载时清理SSE连接
387
+ onUnmounted(() => {
388
+ if (eventSourceRef.value) {
389
+ eventSourceRef.value.close()
390
+ eventSourceRef.value = null
391
+ }
392
+ })
393
+
383
394
  // 格式化消息内容(支持换行)
384
395
  const formatMessage = (content) => {
385
396
  if (!content) return ''
@@ -440,7 +451,7 @@ const handleShiftEnter = () => {
440
451
  }
441
452
 
442
453
  // 处理表单提交
443
- const handleFormSubmit = (index, formData) => {
454
+ const handleSubmit = (formData) => {
444
455
  // 在实际应用中,这里应该将表单数据发送到服务器
445
456
  console.log('表单提交数据:', formData)
446
457
 
@@ -463,7 +474,7 @@ const handleCardActionClick = (index, { action, item, index: itemIndex }) => {
463
474
  const handleSuggestionClick = (suggestion) => {
464
475
  console.log('Suggestion clicked:', suggestion)
465
476
  // 将推荐信息作为用户消息发送
466
- inputText.value = suggestion
477
+ inputComRef.value.inputText = suggestion
467
478
  sendMessage()
468
479
  }
469
480
 
@@ -612,6 +623,7 @@ const handleLoadMore = (message) => {
612
623
  display: flex;
613
624
  flex-direction: column;
614
625
  // max-width: 75%;
626
+ flex: 1;
615
627
  gap: 6px;
616
628
  }
617
629
 
@@ -688,45 +700,7 @@ const handleLoadMore = (message) => {
688
700
  }
689
701
  }
690
702
 
691
- .input-area {
692
- position: fixed;
693
- bottom: 0;
694
- left: 0;
695
- right: 0;
696
- background: #fff;
697
- border-top: 1px solid #ebedf0;
698
- padding: 8px 12px;
699
- padding-bottom: calc(8px + env(safe-area-inset-bottom));
700
- z-index: 100;
701
-
702
- .input-wrapper {
703
- display: flex;
704
- align-items: flex-end;
705
- gap: 8px;
706
- max-width: 100%;
707
- }
708
-
709
- .chat-input {
710
- flex: 1;
711
- background: #f7f8fa;
712
- border-radius: 20px;
713
- padding: 8px 12px;
714
- min-height: 40px;
715
- max-height: 120px;
716
-
717
- :deep(.van-field__control) {
718
- font-size: 15px;
719
- line-height: 1.5;
720
- }
721
- }
722
703
 
723
- .send-btn {
724
- flex-shrink: 0;
725
- height: 40px;
726
- padding: 0 20px;
727
- border-radius: 20px;
728
- }
729
- }
730
704
 
731
705
 
732
706
  /* 推荐信息样式 */
@@ -745,6 +719,7 @@ const handleLoadMore = (message) => {
745
719
  .suggestion-tag {
746
720
  margin-right: 8px;
747
721
  margin-bottom: 8px;
722
+ padding: 4px;
748
723
  background-color: #f0f0f0;
749
724
  border-color: #ddd;
750
725
  color: #666;
@@ -0,0 +1,20 @@
1
+ import { defineConfig } from 'vite'
2
+ import { createVuePlugin } from 'vite-plugin-vue2'
3
+ import px2rem from 'postcss-pxtorem'
4
+
5
+ export default defineConfig({
6
+ plugins: [
7
+ createVuePlugin()
8
+ ],
9
+ css: {
10
+ postcss: {
11
+ plugins: [
12
+ px2rem({
13
+ rootValue: 16, // 设计稿为375px时,rootValue设为16(16px * 375/375 = 16px)
14
+ propList: ['*'],
15
+ minPixelValue: 1,
16
+ })
17
+ ]
18
+ }
19
+ }
20
+ })