@ibiz-template/vue3-components 0.7.41-alpha.152 → 0.7.41-alpha.154

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.
@@ -345,6 +345,14 @@ export class AIChatUtil implements IApiAIChatUtil {
345
345
  ibiz.config.common.aiChatMessagePageSize;
346
346
  }
347
347
 
348
+ // AI会话是否启用工作空间文件系统相关操作
349
+ if (editorParams.srfaienableworkspace) {
350
+ chatOptions.enableWorkSpace =
351
+ editorParams.srfaienableworkspace === 'true';
352
+ } else {
353
+ chatOptions.enableWorkSpace = ibiz.config.common.aiEnableWorkSpace;
354
+ }
355
+
348
356
  // 智能体选项(由内部获取,优化打开速度)
349
357
  chatOptions.fetchAgentList = (args: IParams = {}) => {
350
358
  const { query, agentid } = args;
@@ -375,12 +383,31 @@ export class AIChatUtil implements IApiAIChatUtil {
375
383
  const sessionid = this.getChatSessionId('TEMP');
376
384
  chatOptions.sessionid = sessionid;
377
385
 
378
- let id: string = '';
379
- let abortController: AbortController;
386
+ // 按聊天控制器实例隔离提问状态,避免多话题会话共用闭包变量相互污染
387
+ const chatStateMap = new WeakMap<
388
+ object,
389
+ { id: string; abortController: AbortController }
390
+ >();
391
+ const getChatState = (aiChat: object) => {
392
+ let state = chatStateMap.get(aiChat);
393
+ if (!state) {
394
+ state = { id: '', abortController: new AbortController() };
395
+ chatStateMap.set(aiChat, state);
396
+ }
397
+ return state;
398
+ };
380
399
  const { chatInstance, view, ctrl } = args;
400
+
381
401
  // 文件系统操作参数
382
- const fileSystemOptions = await this.getAIFileSystemOptions();
402
+ // AI聊天框文件预览模式
403
+ let filePreviewMode = ibiz.config.common.aiFilePreviewMode;
404
+ if (editorParams.srfaifilepreviewmode) {
405
+ filePreviewMode = editorParams.srfaifilepreviewmode;
406
+ }
407
+ const fileSystemOptions =
408
+ await this.getAIFileSystemOptions(filePreviewMode);
383
409
  chatOptions.fileSystemOptions = fileSystemOptions;
410
+
384
411
  // 请求历史
385
412
  chatOptions.history = async (
386
413
  ctx: IContext,
@@ -468,8 +495,10 @@ export class AIChatUtil implements IApiAIChatUtil {
468
495
  ) => {
469
496
  // 更新标记,主要用于更新用户消息真实消息ID(每条消息都会回asextparam,只需要设置一次即可)
470
497
  let updateFlag = true;
471
- id = createUUID();
472
- abortController = new AbortController();
498
+ const state = getChatState(aiChat);
499
+ state.id = createUUID();
500
+ state.abortController = new AbortController();
501
+ const { id, abortController } = state;
473
502
  const deService = await ibiz.hub
474
503
  .getApp(ctx.srfappid)
475
504
  .deService.getService(ctx, other.appDataEntityId);
@@ -608,6 +637,7 @@ export class AIChatUtil implements IApiAIChatUtil {
608
637
  param: IParams,
609
638
  other: IParams,
610
639
  ) => {
640
+ const { id, abortController } = getChatState(aiChat);
611
641
  abortController?.abort();
612
642
  const asyncacitonid = aiChat.getActiveAsyncacitonId();
613
643
  if (asyncacitonid) {
@@ -652,8 +682,10 @@ export class AIChatUtil implements IApiAIChatUtil {
652
682
  arr: IChatMessage[],
653
683
  ) => {
654
684
  if (other.asyncacitonid) {
655
- id = createUUID();
656
- abortController = new AbortController();
685
+ const state = getChatState(aiChat);
686
+ state.id = createUUID();
687
+ state.abortController = new AbortController();
688
+ const { id, abortController } = state;
657
689
  const deService = await ibiz.hub
658
690
  .getApp(ctx.srfappid)
659
691
  .deService.getService(ctx, other.appDataEntityId);
@@ -673,14 +705,10 @@ export class AIChatUtil implements IApiAIChatUtil {
673
705
  srfproject: other.srfproject,
674
706
  });
675
707
  try {
676
- // 新增等待回复消息(AI首条内容到达前显示loading气泡)
677
- aiChat.addWaitingMessage(id);
678
708
  await deService.aiResumeSse(
679
709
  (msg: IPortalAsyncAction) => {
680
710
  // 20: 持续回答中,消息会持续推送。同一个消息 id 会显示在同一个框内
681
711
  if (msg.actionstate === 20 && msg.actionresult) {
682
- // 移除等待回复消息
683
- aiChat.removeWaitingMessage(id);
684
712
  // 设置当前聊天在加载中
685
713
  aiChat.isLoading.value = true;
686
714
  // 不存在才设置,存在则不设置(下方设置的时候需要去存储话题数据中)
@@ -758,8 +786,6 @@ export class AIChatUtil implements IApiAIChatUtil {
758
786
  }
759
787
  abortController?.abort();
760
788
  } finally {
761
- // 移除等待回复消息
762
- aiChat.removeWaitingMessage(id);
763
789
  // 设置当前聊天加载状态已完成
764
790
  aiChat.isLoading.value = false;
765
791
  // 标记当前消息已经交互完成
@@ -1354,6 +1380,14 @@ export class AIChatUtil implements IApiAIChatUtil {
1354
1380
  ibiz.config.common.aiChatMessagePageSize;
1355
1381
  }
1356
1382
 
1383
+ // AI会话是否启用工作空间文件系统相关操作
1384
+ if (params.hasOwnProperty('srfaienableworkspace')) {
1385
+ chatOptions.enableWorkSpace = params.srfaienableworkspace === 'true';
1386
+ delete params.srfaienableworkspace;
1387
+ } else {
1388
+ chatOptions.enableWorkSpace = ibiz.config.common.aiEnableWorkSpace;
1389
+ }
1390
+
1357
1391
  // 智能体选项(由内部获取,优化打开速度)
1358
1392
  chatOptions.fetchAgentList = (args: IParams = {}) => {
1359
1393
  const { query, agentid } = args;
@@ -1381,12 +1415,32 @@ export class AIChatUtil implements IApiAIChatUtil {
1381
1415
  chatOptions.otherToolbarItems = otherToolbarItems;
1382
1416
  }
1383
1417
 
1384
- let id: string = '';
1385
- let abortController: AbortController;
1418
+ // 按聊天控制器实例隔离提问状态,避免多话题会话共用闭包变量相互污染
1419
+ const chatStateMap = new WeakMap<
1420
+ object,
1421
+ { id: string; abortController: AbortController }
1422
+ >();
1423
+ const getChatState = (aiChat: object) => {
1424
+ let state = chatStateMap.get(aiChat);
1425
+ if (!state) {
1426
+ state = { id: '', abortController: new AbortController() };
1427
+ chatStateMap.set(aiChat, state);
1428
+ }
1429
+ return state;
1430
+ };
1386
1431
  const { chatInstance, view, ctrl } = args;
1387
1432
  // 文件系统操作参数
1388
- const fileSystemOptions = await this.getAIFileSystemOptions();
1433
+
1434
+ // AI聊天框文件预览模式
1435
+ let filePreviewMode = ibiz.config.common.aiFilePreviewMode;
1436
+ if (params.hasOwnProperty('srfaifilepreviewmode')) {
1437
+ filePreviewMode = params.srfaifilepreviewmode;
1438
+ delete params.srfaifilepreviewmode;
1439
+ }
1440
+ const fileSystemOptions =
1441
+ await this.getAIFileSystemOptions(filePreviewMode);
1389
1442
  chatOptions.fileSystemOptions = fileSystemOptions;
1443
+
1390
1444
  // 查询历史
1391
1445
  chatOptions.history = async (
1392
1446
  ctx: IContext,
@@ -1474,8 +1528,10 @@ export class AIChatUtil implements IApiAIChatUtil {
1474
1528
  ) => {
1475
1529
  // 更新标记,主要用于更新用户消息真实消息ID(每条消息都会回asextparam,只需要设置一次即可)
1476
1530
  let updateFlag = true;
1477
- id = createUUID();
1478
- abortController = new AbortController();
1531
+ const state = getChatState(aiChat);
1532
+ state.id = createUUID();
1533
+ state.abortController = new AbortController();
1534
+ const { id, abortController } = state;
1479
1535
  const deService = await ibiz.hub
1480
1536
  .getApp(ctx.srfappid)
1481
1537
  .deService.getService(ctx, other.appDataEntityId);
@@ -1612,6 +1668,7 @@ export class AIChatUtil implements IApiAIChatUtil {
1612
1668
  param: IParams,
1613
1669
  other: IParams,
1614
1670
  ) => {
1671
+ const { id, abortController } = getChatState(aiChat);
1615
1672
  abortController?.abort();
1616
1673
  const asyncacitonid = aiChat.getActiveAsyncacitonId();
1617
1674
  if (asyncacitonid) {
@@ -1656,8 +1713,10 @@ export class AIChatUtil implements IApiAIChatUtil {
1656
1713
  arr: IChatMessage[],
1657
1714
  ) => {
1658
1715
  if (other.asyncacitonid) {
1659
- id = createUUID();
1660
- abortController = new AbortController();
1716
+ const state = getChatState(aiChat);
1717
+ state.id = createUUID();
1718
+ state.abortController = new AbortController();
1719
+ const { id, abortController } = state;
1661
1720
  const deService = await ibiz.hub
1662
1721
  .getApp(ctx.srfappid)
1663
1722
  .deService.getService(ctx, other.appDataEntityId);
@@ -1677,14 +1736,10 @@ export class AIChatUtil implements IApiAIChatUtil {
1677
1736
  srfproject: other.srfproject,
1678
1737
  });
1679
1738
  try {
1680
- // 新增等待回复消息(AI首条内容到达前显示loading气泡)
1681
- aiChat.addWaitingMessage(id);
1682
1739
  await deService.aiResumeSse(
1683
1740
  (msg: IPortalAsyncAction) => {
1684
1741
  // 20: 持续回答中,消息会持续推送。同一个消息 id 会显示在同一个框内
1685
1742
  if (msg.actionstate === 20 && msg.actionresult) {
1686
- // 移除等待回复消息
1687
- aiChat.removeWaitingMessage(id);
1688
1743
  // 设置当前聊天在加载中
1689
1744
  aiChat.isLoading.value = true;
1690
1745
  // 不存在才设置,存在则不设置(下方设置的时候需要去存储话题数据中)
@@ -1762,8 +1817,6 @@ export class AIChatUtil implements IApiAIChatUtil {
1762
1817
  }
1763
1818
  abortController?.abort();
1764
1819
  } finally {
1765
- // 移除等待回复消息
1766
- aiChat.removeWaitingMessage(id);
1767
1820
  // 设置当前聊天加载状态已完成
1768
1821
  aiChat.isLoading.value = false;
1769
1822
  // 标记当前消息已经交互完成
@@ -2423,8 +2476,42 @@ export class AIChatUtil implements IApiAIChatUtil {
2423
2476
  * @returns {*} {Promise<IData>}
2424
2477
  * @memberof AIChatUtil
2425
2478
  */
2426
- async getAIFileSystemOptions(): Promise<IData> {
2479
+ async getAIFileSystemOptions(
2480
+ filePreviewMode: 'DEFAULT' | 'CUSTOM',
2481
+ ): Promise<IData> {
2427
2482
  const fileSystemOptions: IData = {};
2483
+ // 文件预览模式
2484
+ fileSystemOptions.filePreviewMode = filePreviewMode;
2485
+ // 获取自定义文件预览路径
2486
+ fileSystemOptions.getCustomFilePreviewUrl = async (
2487
+ ctx: IContext,
2488
+ param: IParams,
2489
+ other: IParams,
2490
+ ): Promise<string> => {
2491
+ const filePreviewAddress = ibiz.env.customParams?.file_preview_address;
2492
+ if (!filePreviewAddress) {
2493
+ ibiz.message.error(
2494
+ ibiz.i18n.t('util.aiChartUtil.noFilePreviewAddress'),
2495
+ );
2496
+ return '';
2497
+ }
2498
+ const { id, name, folder } = other;
2499
+ let { downloadUrl } = ibiz.util.file.calcFileUpDownUrl(
2500
+ ctx,
2501
+ param,
2502
+ {},
2503
+ { osscat: folder },
2504
+ );
2505
+ downloadUrl = downloadUrl.replace('%fileId%', id);
2506
+ const { protocol, hostname, port } = window.location;
2507
+ // 没有端口使用默认端口
2508
+ const actualPort = port || (protocol === 'https:' ? '443' : '80');
2509
+ const windowInfo = `${protocol}//${hostname}:${actualPort}`;
2510
+ const filedownloadurl = `${windowInfo}${downloadUrl}?fullfilename=${name}`;
2511
+ const b64Encoded = ibiz.util.base64.encode(filedownloadurl);
2512
+ const previewUrl = `${filePreviewAddress}/onlinePreview?url=${encodeURIComponent(b64Encoded)}`;
2513
+ return previewUrl;
2514
+ };
2428
2515
  // 查询文件列表
2429
2516
  fileSystemOptions.getFileList = async (
2430
2517
  ctx: IContext,
@@ -2454,11 +2541,91 @@ export class AIChatUtil implements IApiAIChatUtil {
2454
2541
  const response = await deService.aiChatProject(
2455
2542
  ctx,
2456
2543
  { srfactag: param.srfactag },
2457
- { ...other, action: 'UPLOAD' },
2544
+ { action: 'UPLOAD', ...other },
2458
2545
  );
2459
2546
  if (response.ok) return response.data;
2460
2547
  return undefined;
2461
2548
  };
2549
+ // 新建目录
2550
+ fileSystemOptions.createFolder = async (
2551
+ ctx: IContext,
2552
+ param: IParams,
2553
+ other: IParams,
2554
+ ): Promise<IData> => {
2555
+ const deService = await ibiz.hub
2556
+ .getApp(ctx.srfappid)
2557
+ .deService.getService(ctx, param.appDataEntityId);
2558
+ const response = await deService.aiChatProject(
2559
+ ctx,
2560
+ { srfactag: param.srfactag },
2561
+ { action: 'MKDIR', ...other },
2562
+ );
2563
+ return response.data;
2564
+ };
2565
+ // 新建文件
2566
+ fileSystemOptions.createFile = async (
2567
+ ctx: IContext,
2568
+ param: IParams,
2569
+ other: IParams,
2570
+ ): Promise<IData> => {
2571
+ const deService = await ibiz.hub
2572
+ .getApp(ctx.srfappid)
2573
+ .deService.getService(ctx, param.appDataEntityId);
2574
+ const response = await deService.aiChatProject(
2575
+ ctx,
2576
+ { srfactag: param.srfactag },
2577
+ { action: 'WRITE', ...other },
2578
+ );
2579
+ return response.data;
2580
+ };
2581
+ // 移动文件夹/文件
2582
+ fileSystemOptions.move = async (
2583
+ ctx: IContext,
2584
+ param: IParams,
2585
+ other: IParams,
2586
+ ): Promise<IData> => {
2587
+ const deService = await ibiz.hub
2588
+ .getApp(ctx.srfappid)
2589
+ .deService.getService(ctx, param.appDataEntityId);
2590
+ const response = await deService.aiChatProject(
2591
+ ctx,
2592
+ { srfactag: param.srfactag },
2593
+ { action: 'MOVE', ...other },
2594
+ );
2595
+ return response.data;
2596
+ };
2597
+ // 删除文件夹/文件
2598
+ fileSystemOptions.delete = async (
2599
+ ctx: IContext,
2600
+ param: IParams,
2601
+ other: IParams,
2602
+ ): Promise<IData> => {
2603
+ const deService = await ibiz.hub
2604
+ .getApp(ctx.srfappid)
2605
+ .deService.getService(ctx, param.appDataEntityId);
2606
+ const response = await deService.aiChatProject(
2607
+ ctx,
2608
+ { srfactag: param.srfactag },
2609
+ { recursive: true, action: 'DELETE', ...other },
2610
+ );
2611
+ return response.data;
2612
+ };
2613
+ // 读取文件内容
2614
+ fileSystemOptions.readFile = async (
2615
+ ctx: IContext,
2616
+ param: IParams,
2617
+ other: IParams,
2618
+ ): Promise<IData> => {
2619
+ const deService = await ibiz.hub
2620
+ .getApp(ctx.srfappid)
2621
+ .deService.getService(ctx, param.appDataEntityId);
2622
+ const response = await deService.aiChatProject(
2623
+ ctx,
2624
+ { srfactag: param.srfactag },
2625
+ { action: 'READ', ...other },
2626
+ );
2627
+ return response.data;
2628
+ };
2462
2629
  // 获取文件内容
2463
2630
  fileSystemOptions.getContent = async (
2464
2631
  ctx: IContext,