Compare commits

...

22 Commits

Author SHA1 Message Date
archer
361e255af8 fix: tool call 2025-06-03 11:04:17 +08:00
archer
2b888fb0fa perf: tip 2025-06-03 10:41:18 +08:00
archer
2128d306ad perf: per 2025-06-03 10:41:17 +08:00
archer
e59816aba4 fix: ux 2025-06-03 10:41:16 +08:00
archer
ded0383ac4 perf: gate 2025-06-03 10:41:15 +08:00
archer
81202c53a8 feat: gate permission 2025-06-03 10:41:15 +08:00
archer
e74ab643fe fix: ui 2025-06-03 10:41:14 +08:00
Theresa
3b0f0a8108 Gate new (#4928)
* feat: Add portal management related icons

* feat: Add portal configuration pages and related translations

* feat: Add new gateway configuration components and icons

- Introduced `ConfigButtons` component for save and share actions with new SVG icons.
- Added `CopyrightTable` and `HomeTable` components for managing copyright and home settings.
- Implemented `SectionHeader` for consistent section titles in the gateway configuration.
- Updated `FillRowTabs` to support new tabs for home and copyright configurations.
- Modified translations for gateway-related terms in English, Simplified Chinese, and Traditional Chinese.
- Removed unused gateway tab from `AccountContainer`.

* feat(gate): add API and schema for team gate configurations

- Introduced new TypeScript definitions for gate configuration parameters and data structures.
- Created constants for gate status and tools.
- Implemented MongoDB schema for team gate configurations.
- Added API functions for getting, creating, updating, and deleting team gate configurations and logos.
- Developed ShareGateModal component for sharing portal links and custom domains.
- Updated ConfigButtons component to handle saving configurations and opening the share modal.
- Added new icons for gate functionalities.
- Updated English and Chinese translations for gateway-related texts.

* feat(gate): refactor gate configuration API and remove unused logo handling

* feat(gate): enhance team gate configuration with new error handling and chat features

- Added new error codes to CommonErrEnum for method not allowed, system error, and unauthorized access.
- Updated datasetErr to include corresponding error messages for new error codes.
- Refactored API to support updating team gate configurations and copyright information.
- Introduced ChatInputBox component for chat functionalities, including file and image uploads.
- Enhanced HomeTable and CopyrightTable components to manage settings more effectively.
- Updated translations for new terms in English and Chinese.
- Improved layout and user experience in the gateway configuration pages.

* feat: Refactor gateway configuration and chat components

- Replaced direct API calls with Zustand store for gate configuration management.
- Introduced `useGateStore` for managing gate and copyright configurations.
- Updated `GatewayConfig` component to utilize the new store and remove redundant state management.
- Enhanced chat functionality in `application.tsx` and `index.tsx` to support gate model.
- Created new `application.tsx` for handling chat interactions with the gate application.
- Improved error handling and loading states in chat components.
- Added dynamic imports for better performance and code splitting.

* feat(gate): update GateSideBar to conditionally render recent apps based on chat page state

* fix(HomeTable): comment out unused FormControl for better readability

* feat(gate): enhance copyright configuration and file upload functionality

- Updated ConfigButtons to handle team avatar updates and save copyright configurations.
- Refactored CopyrightTable to integrate file selection for team avatars and improve form handling.
- Added animations and hover effects for better user experience during file uploads.
- Improved toast notifications for success and error handling in configuration processes.

* feat(gate): add gate service availability check and update translations

- Implemented gate service availability check in application and index pages, redirecting users if the service is unavailable.
- Added new translation keys for gate service status in English and Chinese.
- Refactored GateSideBar to improve rendering logic for recent apps based on gate status.

* feat(chat): add route check to ToolMenu for app detail visibility

- Implemented a check to prevent displaying app details when the current route starts with '/chat/gate'.
- Updated menu rendering logic to conditionally show app details based on the new route check.

* feat(constants): add 'gate' type to AppTypeEnum

* refactor: rename "Portal" to "Gate" across the application

- Updated schema to remove the slogan field from GateConfigSchema.
- Modified SVG icon dimensions for gateLight.svg.
- Changed localization keys and values from "Portal" to "Gate" in various JSON files.
- Added support for gate applications in the app creation and management logic.
- Enhanced ChatBox component to handle gate-specific routes and configurations.
- Updated ConfigButtons to manage gate configurations and intros.
- Adjusted ShareGateModal to generate correct gate URLs.
- Expanded emptyTemplates to include gate-specific templates and configurations.
- Refactored chatItemContext to include intro for gate applications.
- Updated useGateStore to initialize gate configurations with intros from existing gate applications.

* fix: add isResponseDetail prop to ChatItemContextProvider

* feat: refactor gate-related API and components for improved functionality

* feat: 添加工具选择和工具选择模态框组件

* refactor: Update GateConfig related types, remove unnecessary constants and enums

* feat: Enhance Gate configuration components and API integration

- Updated ConfigButtons and HomeTable to use string arrays for tools instead of GateTool type.
- Implemented batch plugin loading in HomeTable with error handling.
- Added ToolSelect and ToolSelectModal components for improved tool management.
- Introduced AppCard and ChatTest components for app detail editing.
- Enhanced Edit and EditForm components for better app configuration management.
- Added new API endpoint for batch plugin retrieval.
- Improved overall structure and styling for better user experience.

* fix: Update ChatBoxDataType to make intro optional in chatItemContext.tsx

* fix: Add isResponseDetail prop to ChatItemContextProvider in ChatPage component

* feat: Enhance ToolSelectModal and GatewayConfig with new functionalities

- Updated ToolSelectModal to handle tool selection and configuration, integrating new props for selected tools and chat configuration.
- Implemented loading and error handling for Gate applications in GatewayConfig, including a retry mechanism for fetching apps.
- Added selectedTool parameter to chat completions API to enable tool activation during chat.
- Refactored chat component to support app form context and debug mode for testing.
- Enhanced useGateStore to manage gate applications, including loading and updating functionalities.

* feat: Refactor GateSideBar to enhance recent apps display and add resource selection

* refactor: 移除门户删除确认功能

* feat: 更新 Chat 组件以使用 AppContextProvider 并修正 localAppDetail 的类型

* refactor: Remove the tool menu logic in the GateChatInput component to simplify the code structure

* refactor:
Remove the tool menu logic from the GateChatInput component to simplify the code structure

* feat:
Simplify the ShareGateModal component by removing unused states and logic

* fix: Update chatGray.svg to remove fill attributes for paths, improving SVG structure

* feat: Added new chat icons and updated internationalized text to support new chat features

* feat: Refactor chat components and introduce GateChat functionality

- Updated ChatHistorySlider to remove isGateRoute check for PC view.
- Added new GateChatHistorySlider component for handling chat history in gate context.
- Removed obsolete ChatPage component related to gate chat.
- Modified GateSideBar styles for improved UI consistency.
- Implemented new API endpoint for chat gate functionality.
- Refactored chat gate index page to utilize GateChatHistorySlider and streamline chat initialization.
- Cleaned up unused imports and code related to debugging and legacy chat handling.

* feat: Update GateSideBar styles for improved responsiveness and animation

- Adjusted width and padding for collapsed and expanded states.
- Enhanced transition effects for smoother UI interactions.
- Modified alignment and positioning of navigation items and user profile for better layout consistency.
- Improved accessibility by ensuring elements are centered when collapsed.

* feat: 添加新的聊天图标和更新分享门户组件样式以提升用户体验

* Refactor chat gate components and implement sidebar functionality

- Updated ChatGate component to use ChatItemContextProvider and ChatRecordContextProvider for better context management.
- Introduced FoldButton component for sidebar collapsing functionality.
- Created GateNavBar component to replace GateSideBar for improved navigation.
- Refactored GateSideBar to handle folding state and external triggers.
- Updated application and index pages to integrate new components and manage sidebar state.
- Enhanced useChatGate hook to include appDetail.intro.

* feat: Updated team structure, set default banner image and refactored LogoBox component to support diagonal background

* feat: Enhance GateNavBar with user popover functionality and logout feature

- Added user popover for displaying user information and logout option.
- Implemented mouse enter/leave handlers for popover visibility.
- Updated user profile section to include popover and improved layout.
- Modified index page to include 'account' in server-side props for better context management.

* feat: Add a bottom line statement in the ChatBox component to remind users that the content is generated by third-party AI

* feat: Update placeholder text in ChatBox and GateChatInput components for better user guidance

- Added internationalized placeholder text for user input in both English and Chinese.
- Updated ChatBox and GateChatInput components to utilize the new placeholder text from localization files.

* feat: Add upload icon and enhance ChatBox layout for better user experience

- Introduced a new upload icon in the Icon component for improved visual representation.
- Updated ChatBox layout to enhance responsiveness and user interaction, including adjustments to padding and structure.
- Added hover overlay effect for logo upload areas in the CopyrightTable component to improve user guidance.

* feat: Refactor Chat component to integrate GateSideBar and GateChatHistorySlider for improved layout and functionality

* refactor: Update imports to use 'import type' for type-only imports across multiple files

- Changed standard imports to type imports for better clarity and performance in TypeScript.
- Updated files in the global support, service, and app components to reflect this change.

* feat: Update localization strings and improve toast messages for better user feedback

- Added new success and failure messages for create, delete, save, and update actions in English and Chinese localization files.
- Refactored toast message keys in the ConfigButtons, CopyrightTable, HomeTable, ToolSelect, and other components to use updated localization keys for consistency.
- Enhanced user experience by providing clearer feedback on actions performed within the application.

* feat: Implement tag management functionality with CRUD operations

- Added new Tag schema and controller for managing application tags.
- Implemented API endpoints for creating, updating, deleting, and listing tags.
- Enhanced the App schema to include a reference to tags.
- Updated localization files for new tag-related messages.
- Improved user experience by providing clear feedback on tag operations.

* feat: Enhance ChatWelcome and GateNavBar components with conditional rendering for team avatars

- Updated ChatWelcome and GateNavBar components to conditionally render avatars based on availability.
- Improved layout by using Flex components for better alignment and responsiveness.
- Ensured consistent styling and structure for avatar display across both components.

* fix: Update parameter name in getBatchPlugins API for consistency

- Changed parameter name from 'id' to 'appId' in getChildAppPreviewNode function call for better clarity and consistency with the rest of the codebase.

* feat: Enhance ToolSelectModal with gate plugins integration and improved filtering

- Added useEffect to load plugins from gateStore and set them in state.
- Introduced ExtendedNodeTemplateItemType to include cost-related properties.
- Updated filtering logic for plugins based on search input.
- Refactored RenderList to display plugins with cost information and improved layout.

* refactor: Update ToolSelect and ToolSelectModal components for improved UI and state management

- Replaced Button with Flex component in ToolSelect for better styling and hover effects.
- Adjusted layout and styling in ToolSelect for a more responsive design.
- Removed ExtendedNodeTemplateItemType and reverted to NodeTemplateListItemType in ToolSelectModal for simplified state management.
- Updated RenderList to reflect changes in template type and maintain consistency.

* refactor: Replace Flex with Button for add tool action and enhance loading state UI

* feat: Enhance application tag management and localization support

- Added 'tags' property to AppListItemType for better tag management.
- Updated localization files for English and Chinese to include new tag-related strings.
- Implemented new AppTable component in the gateway for managing applications.
- Adjusted routes and components to support the new app management features.

* feat: Update localization and refactor chat components

- Added new localization strings for "enlarge" in English, Simplified Chinese, and Traditional Chinese.
- Refactored chat components to replace `quoteData` with `datasetCiteData` for improved state management.
- Enhanced `ToolSelect` and related components by removing error handling logic for a cleaner UI.
- Updated `AppTable` component to remove unnecessary props for better clarity.

* feat: Initialize copyright configuration in GateNavBar component

* feat: Add appDetail property to ChatGate component and update related logic

* feat: Update GateNavBar routing logic for chat page refresh and enhance avatar display

* feat: Enhance tag management and app detail handling in Chat component

* feat: 更新聊天组件中的国际化文本和输入逻辑,优化用户体验

* feat: Refactor gate configuration management

- Updated API endpoints for fetching and updating gate configurations.
- Changed `avatar` field to `logo` and added `banner` in gate configuration types.
- Implemented new controller methods for creating, retrieving, updating, and deleting gate configurations.
- Enhanced `ConfigButtons` and `CopyrightTable` components to handle new configuration fields.
- Added new SVG icon for sidebar collapse button.
- Improved internationalization support by adding new translation keys.
- Refactored `HomeTable` to manage gate configuration state and handle updates.
- Updated `ShareGateModal` to accept gate configuration as props.
- Cleaned up unused imports and optimized component structures.

* feat: 加载和管理 Gate 配置及版权信息,优化相关组件逻辑

* feat: 更新国际化文本,优化聊天组件中的配置和状态检查逻辑

* feat: Update template configuration and adjust default open state to improve user experience

* feat: Enhance gate management features and update related components

- Added `featuredApps` and `quickApps` fields to `GateSchemaType` for better app management.
- Implemented new methods for updating and managing featured and quick apps in the `controller` and `featureApp` modules.
- Introduced `AddFeatureAppModal` for selecting and adding featured apps.
- Updated `AppTable` and `HomeTable` components to integrate new app management functionalities.
- Enhanced internationalization support by adding new translation keys for app management features.
- Refactored existing components to improve code clarity and maintainability.

* feat: Enhance chat tool selection and quick app management features

- Added `selectedToolIds` and `onSelectedToolIdsChange` props to `ChatBox` and `GateChatInput` components for better tool management.
- Introduced `GateToolSelect` component for selecting tools with improved UI and functionality.
- Implemented `AddQuickAppModal` for managing quick apps, including selection and drag-and-drop functionality.
- Updated `HomeTable` to integrate quick app management and display selected apps.
- Refactored related components to improve code clarity and maintainability.

* refactor: Remove unused AppContext import in useChatGate.tsx to clean up code

* refactor: Update plugin ID handling and clean up unused imports

- Renamed `splitCombinePluginId` to `splitCombineToolId` for consistency in plugin ID processing.
- Removed unused `checkNode` import from `featureApp/detail.ts` and `quickApp/detail.ts` files to streamline the code.
- Added `ownerTmbId` to the parameters in `rewriteAppWorkflowToDetail` for better context management.

* refactor: Rename storeEdgesRenderEdge to storeEdge2RenderEdge for consistency

- Updated the function name from `storeEdgesRenderEdge` to `storeEdge2RenderEdge` in the Header component to maintain naming consistency.
- Adjusted the mapping of edges to use the new function name for improved clarity in the workflow processing.
2025-06-03 10:41:13 +08:00
archer
165b783a95 perf: ux 2025-06-03 10:41:13 +08:00
Finley Ge
d7b9f94270 feat: add app log permission (#4932)
* feat: add app log permission

* fix: org search bug
2025-06-03 10:41:12 +08:00
gggaaallleee
5a5367d30b add Bocha search template (#4933)
* add bocha

* Delete packages/service/support/operationLog/util.ts
2025-05-30 21:07:49 +08:00
Archer
8ed35ffe7e Update dataset.md (#4927) 2025-05-29 18:25:59 +08:00
Archer
0f866fc552 feat: text collecion auto save for a txt file (#4924) 2025-05-29 17:57:27 +08:00
Archer
05c7ba4483 feat: Workflow node search (#4920)
* add node find (#4902)

* add node find

* plugin header

* fix

* fix

* remove

* type

* add searched status

* optimize

* perf: search nodes

---------

Co-authored-by: heheer <heheer@sealos.io>
2025-05-29 14:29:28 +08:00
heheer
fa80ce3a77 fix child app external variables (#4919) 2025-05-29 13:37:59 +08:00
Archer
830358aa72 remove invalid code (#4915) 2025-05-28 22:11:40 +08:00
Archer
02b214b3ec feat: remove buffer;fix: custom pdf parse (#4914)
* fix: doc

* fix: remove buffer

* fix: pdf parse
2025-05-28 21:48:10 +08:00
Archer
a171c7b11c perf: buffer;fix: back up split (#4913)
* perf: buffer

* fix: back up split

* fix: app limit

* doc
2025-05-28 18:18:25 +08:00
heheer
802de11363 fix runtool empty message (#4911)
* fix runtool empty message

* del unused code

* fix
2025-05-28 17:48:30 +08:00
Archer
b4ecfb0b79 Feat: Node latest version (#4905)
* node versions add keep the latest option (#4899)

* node versions add keep the latest option

* i18n

* perf: version code

* fix: ts

* hide system version

* hide system version

* hide system version

* fix: ts

* fix: ts

---------

Co-authored-by: heheer <heheer@sealos.io>
2025-05-28 10:46:32 +08:00
heheer
331b851a78 fix has tool node condition (#4907) 2025-05-28 10:34:02 +08:00
Archer
50d235c42a fix: i18n (#4898) 2025-05-27 10:45:25 +08:00
240 changed files with 16865 additions and 528 deletions

View File

@@ -27,5 +27,8 @@
},
"markdown.copyFiles.destination": {
"/docSite/content/**/*": "${documentWorkspaceFolder}/docSite/assets/imgs/"
},
"[svg]": {
"editor.defaultFormatter": "jock.svg"
}
}

View File

@@ -132,15 +132,15 @@ services:
# fastgpt
sandbox:
container_name: sandbox
image: ghcr.io/labring/fastgpt-sandbox:v4.9.10 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.9.10 # 阿里云
image: ghcr.io/labring/fastgpt-sandbox:v4.9.10-fix2 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.9.10-fix2 # 阿里云
networks:
- fastgpt
restart: always
fastgpt-mcp-server:
container_name: fastgpt-mcp-server
image: ghcr.io/labring/fastgpt-mcp_server:v4.9.10 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-mcp_server:v4.9.10 # 阿里云
image: ghcr.io/labring/fastgpt-mcp_server:v4.9.10-fix2 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-mcp_server:v4.9.10-fix2 # 阿里云
ports:
- 3005:3000
networks:
@@ -150,8 +150,8 @@ services:
- FASTGPT_ENDPOINT=http://fastgpt:3000
fastgpt:
container_name: fastgpt
image: ghcr.io/labring/fastgpt:v4.9.10 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.9.10 # 阿里云
image: ghcr.io/labring/fastgpt:v4.9.10-fix2 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.9.10-fix2 # 阿里云
ports:
- 3000:3000
networks:

View File

@@ -109,15 +109,15 @@ services:
# fastgpt
sandbox:
container_name: sandbox
image: ghcr.io/labring/fastgpt-sandbox:v4.9.10 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.9.10 # 阿里云
image: ghcr.io/labring/fastgpt-sandbox:v4.9.10-fix2 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.9.10-fix2 # 阿里云
networks:
- fastgpt
restart: always
fastgpt-mcp-server:
container_name: fastgpt-mcp-server
image: ghcr.io/labring/fastgpt-mcp_server:v4.9.10 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-mcp_server:v4.9.10 # 阿里云
image: ghcr.io/labring/fastgpt-mcp_server:v4.9.10-fix2 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-mcp_server:v4.9.10-fix2 # 阿里云
ports:
- 3005:3000
networks:
@@ -127,8 +127,8 @@ services:
- FASTGPT_ENDPOINT=http://fastgpt:3000
fastgpt:
container_name: fastgpt
image: ghcr.io/labring/fastgpt:v4.9.10 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.9.10 # 阿里云
image: ghcr.io/labring/fastgpt:v4.9.10-fix2 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.9.10-fix2 # 阿里云
ports:
- 3000:3000
networks:

View File

@@ -96,15 +96,15 @@ services:
# fastgpt
sandbox:
container_name: sandbox
image: ghcr.io/labring/fastgpt-sandbox:v4.9.10 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.9.10 # 阿里云
image: ghcr.io/labring/fastgpt-sandbox:v4.9.10-fix2 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.9.10-fix2 # 阿里云
networks:
- fastgpt
restart: always
fastgpt-mcp-server:
container_name: fastgpt-mcp-server
image: ghcr.io/labring/fastgpt-mcp_server:v4.9.10 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-mcp_server:v4.9.10 # 阿里云
image: ghcr.io/labring/fastgpt-mcp_server:v4.9.10-fix2 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-mcp_server:v4.9.10-fix2 # 阿里云
ports:
- 3005:3000
networks:
@@ -114,8 +114,8 @@ services:
- FASTGPT_ENDPOINT=http://fastgpt:3000
fastgpt:
container_name: fastgpt
image: ghcr.io/labring/fastgpt:v4.9.10 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.9.10 # 阿里云
image: ghcr.io/labring/fastgpt:v4.9.10-fix2 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.9.10-fix2 # 阿里云
ports:
- 3000:3000
networks:

View File

@@ -72,15 +72,15 @@ services:
sandbox:
container_name: sandbox
image: ghcr.io/labring/fastgpt-sandbox:v4.9.10 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.9.10 # 阿里云
image: ghcr.io/labring/fastgpt-sandbox:v4.9.10-fix2 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.9.10-fix2 # 阿里云
networks:
- fastgpt
restart: always
fastgpt-mcp-server:
container_name: fastgpt-mcp-server
image: ghcr.io/labring/fastgpt-mcp_server:v4.9.10 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-mcp_server:v4.9.10 # 阿里云
image: ghcr.io/labring/fastgpt-mcp_server:v4.9.10-fix2 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-mcp_server:v4.9.10-fix2 # 阿里云
ports:
- 3005:3000
networks:
@@ -90,8 +90,8 @@ services:
- FASTGPT_ENDPOINT=http://fastgpt:3000
fastgpt:
container_name: fastgpt
image: ghcr.io/labring/fastgpt:v4.9.10 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.9.10 # 阿里云
image: ghcr.io/labring/fastgpt:v4.9.10-fix2 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.9.10-fix2 # 阿里云
ports:
- 3000:3000
networks:

View File

@@ -645,7 +645,7 @@ data 为集合的 ID。
{{< /tab >}}
{{< /tabs >}}
### 创建一个外部文件库集合(商业版
### 创建一个外部文件库集合(弃用
{{< tabs tabTotal="3" >}}
{{< tab tabName="请求示例" >}}

View File

@@ -1,5 +1,5 @@
---
title: 'V4.9.10(进行中)'
title: 'V4.9.10'
description: 'FastGPT V4.9.10 更新说明'
icon: 'upgrade'
draft: false
@@ -15,8 +15,8 @@ weight: 790
### 2. 更新镜像 tag
- 更新 FastGPT 镜像 tag: v4.9.10
- 更新 FastGPT 商业版镜像 tag: v4.9.10
- 更新 FastGPT 镜像 tag: v4.9.10-fix2
- 更新 FastGPT 商业版镜像 tag: v4.9.10-fix2
- mcp_server 无需更新
- Sandbox 无需更新
- AIProxy 无需更新

View File

@@ -0,0 +1,25 @@
---
title: 'V4.9.11(进行中)'
description: 'FastGPT V4.9.11 更新说明'
icon: 'upgrade'
draft: false
toc: true
weight: 789
---
## 🚀 新增内容
1. 工作流中增加节点搜索功能。
2. 工作流中,子流程版本控制,可选择“保持最新版本”,无需手动更新。
## ⚙️ 优化
1. 原文缓存改用 gridfs 存储,提高上限。
## 🐛 修复
1. 工作流中,管理员声明的全局系统工具,无法进行版本管理。
2. 工具调用节点前,有交互节点时,上下文异常。
3. 修复备份导入,小于 1000 字时,无法分块问题。
4. 自定义 PDF 解析,无法保存 base64 图片。

View File

@@ -4,6 +4,9 @@ import { type ErrType } from '../errorCode';
/* dataset: 507000 */
const startCode = 507000;
export enum CommonErrEnum {
methodNotAllowed = 'methodNotAllowed',
systemError = 'systemError',
unauthorized = 'unauthorized',
invalidParams = 'invalidParams',
invalidResource = 'invalidResource',
fileNotFound = 'fileNotFound',
@@ -35,6 +38,22 @@ const datasetErr = [
{
statusText: CommonErrEnum.inheritPermissionError,
message: 'error.inheritPermissionError'
},
{
statusText: CommonErrEnum.methodNotAllowed,
message: i18nT('common:code_error.error_message.405')
},
{
statusText: CommonErrEnum.systemError,
message: i18nT('common:code_error.error_message.500')
},
{
statusText: CommonErrEnum.unauthorized,
message: i18nT('common:code_error.error_message.403')
},
{
statusText: CommonErrEnum.invalidParams,
message: i18nT('common:code_error.error_message.422')
}
];
export default datasetErr.reduce((acc, cur, index) => {

View File

@@ -7,6 +7,7 @@ import {
} from './type';
export enum AppTypeEnum {
gate = 'gate',
folder = 'folder',
simple = 'simple',
workflow = 'advanced',

24
packages/global/core/app/tags.d.ts vendored Normal file
View File

@@ -0,0 +1,24 @@
import { TeamMemberStatusEnum } from 'support/user/team/constant';
import type { SourceMemberType } from 'support/user/type';
export type TagSchemaType = {
_id: string;
teamId: string;
name: string;
color: string;
createTime: Date;
};
export type TagWithCountType = TagSchemaType & {
count: number;
};
export type TagListItemType = {
_id: string;
teamId: string;
name: string;
color: string;
createTime: Date;
count?: number;
sourceMember?: SourceMemberType;
};

View File

@@ -65,6 +65,7 @@ export type AppListItemType = {
inheritPermission?: boolean;
private?: boolean;
sourceMember: SourceMemberType;
tags?: string[];
};
export type AppDetailType = AppSchema & {

View File

@@ -10,6 +10,8 @@ import { AppTypeEnum } from './constants';
import { AppErrEnum } from '../../common/error/code/app';
import { PluginErrEnum } from '../../common/error/code/plugin';
import { i18nT } from '../../../web/i18n/utils';
import appErrList from '../../common/error/code/app';
import pluginErrList from '../../common/error/code/plugin';
export const getDefaultAppForm = (): AppSimpleEditFormType => {
return {
@@ -44,7 +46,7 @@ export const appWorkflow2Form = ({
chatConfig
}: {
nodes: StoreNodeItemType[];
chatConfig: AppChatConfigType;
chatConfig?: AppChatConfigType;
}) => {
const defaultAppForm = getDefaultAppForm();
const findInputValueByKey = (inputs: FlowNodeInputItemType[], key: string) => {
@@ -170,6 +172,10 @@ export const appWorkflow2Form = ({
}
});
if (chatConfig) {
defaultAppForm.chatConfig = chatConfig;
}
return defaultAppForm;
};
@@ -190,17 +196,10 @@ export const getAppType = (config?: WorkflowTemplateBasicType | AppSimpleEditFor
return '';
};
export const formatToolError = (error?: string) => {
const unExistError: Array<string> = [
AppErrEnum.unAuthApp,
AppErrEnum.unExist,
PluginErrEnum.unAuth,
PluginErrEnum.unExist
];
export const formatToolError = (error?: any) => {
if (!error || typeof error !== 'string') return;
if (error && unExistError.includes(error)) {
return i18nT('app:un_auth');
} else {
return error;
}
const errorText = appErrList[error]?.message || pluginErrList[error]?.message;
return errorText || error;
};

View File

@@ -40,5 +40,6 @@ export function getSourceNameIcon({
export const predictDataLimitLength = (mode: TrainingModeEnum, data: any[]) => {
if (mode === TrainingModeEnum.qa) return data.length * 20;
if (mode === TrainingModeEnum.auto) return data.length * 5;
if (mode === TrainingModeEnum.image) return data.length * 2;
return data.length;
};

View File

@@ -59,7 +59,6 @@ export type FlowNodeCommonType = {
};
export type PluginDataType = {
version?: string;
diagram?: string;
userGuide?: string;
courseUrl?: string;
@@ -126,6 +125,7 @@ export type FlowNodeItemType = FlowNodeTemplateType & {
nodeId: string;
parentNodeId?: string;
isError?: boolean;
searchedText?: string;
debugResult?: {
status: 'running' | 'success' | 'skipped' | 'failed';
message?: string;

View File

@@ -2,7 +2,18 @@ import { NullPermission, PermissionKeyEnum, PermissionList } from '../constant';
import { type PermissionListType } from '../type';
import { i18nT } from '../../../../web/i18n/utils';
export enum AppPermissionKeyEnum {}
export const AppPermissionList: PermissionListType = {
export enum AppPermissionKeyEnum {
log = 'log',
quickGate = 'quickGate',
featuredGate = 'featuredGate'
}
export const AppLogPermission = 0b100000;
export const GateQuickAppPermission = 0b001100;
export const GateFeaturedAppPermission = 0b010100;
export const AppPermissionList: PermissionListType<AppPermissionKeyEnum> = {
[PermissionKeyEnum.read]: {
...PermissionList[PermissionKeyEnum.read],
description: i18nT('app:permission.des.read')
@@ -13,8 +24,28 @@ export const AppPermissionList: PermissionListType = {
},
[PermissionKeyEnum.manage]: {
...PermissionList[PermissionKeyEnum.manage],
value: 0b111111,
description: i18nT('app:permission.des.manage')
},
[AppPermissionKeyEnum.log]: {
name: i18nT('app:permission.name.log'),
value: AppLogPermission,
checkBoxType: 'multiple',
description: i18nT('app:permission.des.log')
},
[AppPermissionKeyEnum.quickGate]: {
name: '门户快捷应用权限',
description: '',
value: GateQuickAppPermission,
checkBoxType: 'hiden'
},
[AppPermissionKeyEnum.featuredGate]: {
name: '门户推荐应用权限',
description: '',
value: GateFeaturedAppPermission,
checkBoxType: 'hiden'
}
};
export const AppDefaultPermissionVal = NullPermission;
export const AppLogPermissionVal = AppPermissionList[AppPermissionKeyEnum.log].value;

View File

@@ -1,7 +1,8 @@
import { type PerConstructPros, Permission } from '../controller';
import { AppDefaultPermissionVal } from './constant';
import { AppDefaultPermissionVal, AppPermissionList } from './constant';
export class AppPermission extends Permission {
hasLogPer: boolean = false;
constructor(props?: PerConstructPros) {
if (!props) {
props = {
@@ -10,6 +11,13 @@ export class AppPermission extends Permission {
} else if (!props?.per) {
props.per = AppDefaultPermissionVal;
}
props.permissionList = AppPermissionList;
super(props);
this.setUpdatePermissionCallback(() => {
this.hasReadPer = this.checkPer(AppPermissionList.read.value);
this.hasWritePer = this.checkPer(AppPermissionList.write.value);
this.hasManagePer = this.checkPer(AppPermissionList.manage.value);
this.hasLogPer = this.checkPer(AppPermissionList.log.value);
});
}
}

View File

@@ -1,5 +1,10 @@
import { type PermissionListType, type PermissionValueType } from './type';
import { PermissionList, NullPermission, OwnerPermissionVal } from './constant';
import {
PermissionList,
NullPermission,
OwnerPermissionVal,
ManagePermissionVal
} from './constant';
export type PerConstructPros = {
per?: PermissionValueType;
@@ -63,6 +68,7 @@ export class Permission {
if (perm === OwnerPermissionVal) {
return this.value === OwnerPermissionVal;
}
return (this.value & perm) === perm;
}

View File

@@ -18,7 +18,7 @@ export type PermissionListType<T = {}> = Record<
name: string;
description: string;
value: PermissionValueType;
checkBoxType: 'single' | 'multiple';
checkBoxType: 'single' | 'multiple' | 'hiden';
}
>;

View File

@@ -19,7 +19,7 @@ export const TeamPermissionList: PermissionListType<TeamPermissionKeyEnum> = {
},
[PermissionKeyEnum.manage]: {
...PermissionList[PermissionKeyEnum.manage],
value: 0b000001
value: 0b000101
},
[TeamPermissionKeyEnum.appCreate]: {
checkBoxType: 'multiple',

View File

@@ -0,0 +1,31 @@
export type putUpdateGateConfigData = {
status?: boolean;
tools?: GateTool[];
slogan?: string;
placeholderText?: string;
};
export type putUpdateGateConfigResponse = {
status?: boolean;
tools?: string[];
slogan?: string;
placeholderText?: string;
};
export type putUpdateGateConfigCopyRightData = {
name?: string;
logo?: string;
banner?: string;
};
export type putUpdateGateConfigCopyRightResponse = {
name: string;
logo: string;
banner: string;
};
export type getGateConfigCopyRightResponse = {
name: string;
logo: string;
banner: string;
};

View File

@@ -0,0 +1,12 @@
export type GateSchemaType = {
teamId: string;
status: boolean;
tools: string[];
featuredApps: string[];
quickApps: string[];
slogan: string;
placeholderText: string;
name: string;
logo: string;
banner: string;
};

View File

@@ -13,6 +13,7 @@ const staticPluginList = [
'WeWorkWebhook',
'google',
'bing',
'bocha',
'delay'
];
// Run in worker thread (Have npm packages)

View File

@@ -1,6 +1,5 @@
{
"author": "",
"version": "4816",
"name": "钉钉 webhook",
"avatar": "plugins/dingding",
"intro": "向钉钉机器人发起 webhook 请求。",

View File

@@ -1,6 +1,5 @@
{
"author": "Menghuan1918",
"version": "488",
"name": "PDF识别",
"avatar": "plugins/doc2x",
"intro": "将PDF文件发送至Doc2X进行解析返回结构化的LaTeX公式的文本(markdown)支持传入String类型的URL或者流程输出中的文件链接变量",

View File

@@ -1,6 +1,5 @@
{
"author": "Menghuan1918",
"version": "488",
"name": "Doc2X服务",
"avatar": "plugins/doc2x",
"intro": "将传入的图片或PDF文件发送至Doc2X进行解析返回带LaTeX公式的markdown格式的文本。",

View File

@@ -1,6 +1,5 @@
{
"author": "",
"version": "4816",
"name": "企业微信 webhook",
"avatar": "plugins/qiwei",
"intro": "向企业微信机器人发起 webhook 请求。只能内部群使用。",

View File

@@ -1,6 +1,5 @@
{
"author": "",
"version": "4811",
"name": "Bing搜索",
"avatar": "core/workflow/template/bing",
"intro": "在Bing中搜索。",

View File

@@ -0,0 +1,604 @@
{
"author": "",
"name": "网络搜索",
"avatar": "common/searchLight",
"intro": "使用博查AI搜索引擎进行网络搜索。",
"showStatus": true,
"weight": 10,
"courseUrl": "",
"isTool": true,
"templateType": "search",
"workflow": {
"nodes": [
{
"nodeId": "pluginInput",
"name": "workflow:template.plugin_start",
"intro": "workflow:intro_plugin_input",
"avatar": "core/workflow/template/workflowStart",
"flowNodeType": "pluginInput",
"showStatus": false,
"position": {
"x": 636.3048409085379,
"y": -238.61714728578016
},
"version": "481",
"inputs": [
{
"renderTypeList": ["input"],
"selectedTypeIndex": 0,
"valueType": "string",
"canEdit": true,
"key": "apiKey",
"label": "apiKey",
"description": "博查API密钥",
"defaultValue": "",
"required": true
},
{
"renderTypeList": ["input", "reference"],
"selectedTypeIndex": 0,
"valueType": "string",
"canEdit": true,
"key": "query",
"label": "query",
"description": "搜索查询词",
"defaultValue": "",
"required": true,
"toolDescription": "搜索查询词"
},
{
"renderTypeList": ["input", "reference"],
"selectedTypeIndex": 0,
"valueType": "string",
"canEdit": true,
"key": "freshness",
"label": "freshness",
"description": "搜索指定时间范围内的网页。可填值oneDay(一天内)、oneWeek(一周内)、oneMonth(一个月内)、oneYear(一年内)、noLimit(不限,默认)、YYYY-MM-DD..YYYY-MM-DD(日期范围)、YYYY-MM-DD(指定日期)",
"defaultValue": "noLimit",
"required": false,
"toolDescription": "搜索时间范围"
},
{
"renderTypeList": ["input", "reference"],
"selectedTypeIndex": 0,
"valueType": "boolean",
"canEdit": true,
"key": "summary",
"label": "summary",
"description": "是否显示文本摘要。true显示false不显示(默认)",
"defaultValue": false,
"required": false,
"toolDescription": "是否显示文本摘要"
},
{
"renderTypeList": ["input", "reference"],
"selectedTypeIndex": 0,
"valueType": "string",
"canEdit": true,
"key": "include",
"label": "include",
"description": "指定搜索的site范围。多个域名使用|或,分隔最多20个。例如qq.com|m.163.com",
"defaultValue": "",
"required": false,
"toolDescription": "指定搜索的site范围"
},
{
"renderTypeList": ["input", "reference"],
"selectedTypeIndex": 0,
"valueType": "string",
"canEdit": true,
"key": "exclude",
"label": "exclude",
"description": "排除搜索的网站范围。多个域名使用|或,分隔最多20个。例如qq.com|m.163.com",
"defaultValue": "",
"required": false,
"toolDescription": "排除搜索的网站范围"
},
{
"renderTypeList": ["input", "reference"],
"selectedTypeIndex": 0,
"valueType": "number",
"canEdit": true,
"key": "count",
"label": "count",
"description": "返回结果的条数。可填范围1-50默认为10",
"defaultValue": 10,
"required": false,
"min": 1,
"max": 50,
"toolDescription": "返回结果条数"
}
],
"outputs": [
{
"id": "apiKey",
"valueType": "string",
"key": "apiKey",
"label": "apiKey",
"type": "hidden"
},
{
"id": "query",
"valueType": "string",
"key": "query",
"label": "query",
"type": "hidden"
},
{
"id": "freshness",
"valueType": "string",
"key": "freshness",
"label": "freshness",
"type": "hidden"
},
{
"id": "summary",
"valueType": "boolean",
"key": "summary",
"label": "summary",
"type": "hidden"
},
{
"id": "include",
"valueType": "string",
"key": "include",
"label": "include",
"type": "hidden"
},
{
"id": "exclude",
"valueType": "string",
"key": "exclude",
"label": "exclude",
"type": "hidden"
},
{
"id": "count",
"valueType": "number",
"key": "count",
"label": "count",
"type": "hidden"
}
]
},
{
"nodeId": "pluginOutput",
"name": "common:core.module.template.self_output",
"intro": "workflow:intro_custom_plugin_output",
"avatar": "core/workflow/template/pluginOutput",
"flowNodeType": "pluginOutput",
"showStatus": false,
"position": {
"x": 2764.1105686698083,
"y": -30.617147285780163
},
"version": "481",
"inputs": [
{
"renderTypeList": ["reference"],
"valueType": "object",
"canEdit": true,
"key": "result",
"label": "result",
"isToolOutput": true,
"description": "",
"value": ["nyA6oA8mF1iW", "httpRawResponse"]
}
],
"outputs": []
},
{
"nodeId": "pluginConfig",
"name": "common:core.module.template.system_config",
"intro": "",
"avatar": "core/workflow/template/systemConfig",
"flowNodeType": "pluginConfig",
"position": {
"x": 184.66337662472682,
"y": -216.05298493910115
},
"version": "4811",
"inputs": [],
"outputs": []
},
{
"nodeId": "nyA6oA8mF1iW",
"name": "HTTP 请求",
"intro": "调用博查搜索API",
"avatar": "core/workflow/template/httpRequest",
"flowNodeType": "httpRequest468",
"showStatus": true,
"position": {
"x": 1335.0647252518884,
"y": -455.9043948565971
},
"version": "481",
"inputs": [
{
"key": "system_addInputParam",
"renderTypeList": ["addInputParam"],
"valueType": "dynamic",
"label": "",
"required": false,
"description": "common:core.module.input.description.HTTP Dynamic Input",
"customInputConfig": {
"selectValueTypeList": [
"string",
"number",
"boolean",
"object",
"arrayString",
"arrayNumber",
"arrayBoolean",
"arrayObject",
"arrayAny",
"any",
"chatHistory",
"datasetQuote",
"dynamic",
"selectDataset",
"selectApp"
],
"showDescription": false,
"showDefaultValue": true
},
"debugLabel": "",
"toolDescription": ""
},
{
"key": "system_httpMethod",
"renderTypeList": ["custom"],
"valueType": "string",
"label": "",
"value": "POST",
"required": true,
"debugLabel": "",
"toolDescription": ""
},
{
"key": "system_httpTimeout",
"renderTypeList": ["custom"],
"valueType": "number",
"label": "",
"value": 30,
"min": 5,
"max": 600,
"required": true,
"debugLabel": "",
"toolDescription": ""
},
{
"key": "system_httpReqUrl",
"renderTypeList": ["hidden"],
"valueType": "string",
"label": "",
"description": "common:core.module.input.description.Http Request Url",
"placeholder": "https://api.ai.com/getInventory",
"required": false,
"value": "https://api.bochaai.com/v1/web-search",
"debugLabel": "",
"toolDescription": ""
},
{
"key": "system_httpHeader",
"renderTypeList": ["custom"],
"valueType": "any",
"value": [
{
"key": "Authorization",
"type": "string",
"value": "Bearer {{$pluginInput.apiKey$}}"
},
{
"key": "Content-Type",
"type": "string",
"value": "application/json"
}
],
"label": "",
"description": "common:core.module.input.description.Http Request Header",
"placeholder": "common:core.module.input.description.Http Request Header",
"required": false,
"debugLabel": "",
"toolDescription": ""
},
{
"key": "system_httpParams",
"renderTypeList": ["hidden"],
"valueType": "any",
"value": [],
"label": "",
"required": false,
"debugLabel": "",
"toolDescription": ""
},
{
"key": "system_httpJsonBody",
"renderTypeList": ["hidden"],
"valueType": "any",
"value": "{\n \"query\": \"{{query}}\",\n \"freshness\": \"{{freshness}}\",\n \"summary\": {{summary}},\n \"include\": \"{{include}}\",\n \"exclude\": \"{{exclude}}\",\n \"count\": {{count}}\n}",
"label": "",
"required": false,
"debugLabel": "",
"toolDescription": ""
},
{
"key": "system_httpFormBody",
"renderTypeList": ["hidden"],
"valueType": "any",
"value": [],
"label": "",
"required": false,
"debugLabel": "",
"toolDescription": ""
},
{
"key": "system_httpContentType",
"renderTypeList": ["hidden"],
"valueType": "string",
"value": "json",
"label": "",
"required": false,
"debugLabel": "",
"toolDescription": ""
},
{
"valueType": "string",
"renderTypeList": ["reference"],
"key": "query",
"label": "query",
"toolDescription": "博查搜索检索词",
"required": true,
"canEdit": true,
"editField": {
"key": true,
"description": true
},
"customInputConfig": {
"selectValueTypeList": [
"string",
"number",
"boolean",
"object",
"arrayString",
"arrayNumber",
"arrayBoolean",
"arrayObject",
"arrayAny",
"any",
"chatHistory",
"datasetQuote",
"dynamic",
"selectApp",
"selectDataset"
],
"showDescription": false,
"showDefaultValue": true
},
"value": ["pluginInput", "query"]
},
{
"valueType": "string",
"renderTypeList": ["reference"],
"key": "freshness",
"label": "freshness",
"toolDescription": "搜索时间范围",
"required": false,
"canEdit": true,
"editField": {
"key": true,
"description": true
},
"customInputConfig": {
"selectValueTypeList": [
"string",
"number",
"boolean",
"object",
"arrayString",
"arrayNumber",
"arrayBoolean",
"arrayObject",
"arrayAny",
"any",
"chatHistory",
"datasetQuote",
"dynamic",
"selectApp",
"selectDataset"
],
"showDescription": false,
"showDefaultValue": true
},
"value": ["pluginInput", "freshness"]
},
{
"valueType": "boolean",
"renderTypeList": ["reference"],
"key": "summary",
"label": "summary",
"toolDescription": "是否显示文本摘要",
"required": false,
"canEdit": true,
"editField": {
"key": true,
"description": true
},
"customInputConfig": {
"selectValueTypeList": [
"string",
"number",
"boolean",
"object",
"arrayString",
"arrayNumber",
"arrayBoolean",
"arrayObject",
"arrayAny",
"any",
"chatHistory",
"datasetQuote",
"dynamic",
"selectApp",
"selectDataset"
],
"showDescription": false,
"showDefaultValue": true
},
"value": ["pluginInput", "summary"]
},
{
"valueType": "string",
"renderTypeList": ["reference"],
"key": "include",
"label": "include",
"toolDescription": "指定搜索的site范围",
"required": false,
"canEdit": true,
"editField": {
"key": true,
"description": true
},
"customInputConfig": {
"selectValueTypeList": [
"string",
"number",
"boolean",
"object",
"arrayString",
"arrayNumber",
"arrayBoolean",
"arrayObject",
"arrayAny",
"any",
"chatHistory",
"datasetQuote",
"dynamic",
"selectApp",
"selectDataset"
],
"showDescription": false,
"showDefaultValue": true
},
"value": ["pluginInput", "include"]
},
{
"valueType": "string",
"renderTypeList": ["reference"],
"key": "exclude",
"label": "exclude",
"toolDescription": "排除搜索的网站范围",
"required": false,
"canEdit": true,
"editField": {
"key": true,
"description": true
},
"customInputConfig": {
"selectValueTypeList": [
"string",
"number",
"boolean",
"object",
"arrayString",
"arrayNumber",
"arrayBoolean",
"arrayObject",
"arrayAny",
"any",
"chatHistory",
"datasetQuote",
"dynamic",
"selectApp",
"selectDataset"
],
"showDescription": false,
"showDefaultValue": true
},
"value": ["pluginInput", "exclude"]
},
{
"valueType": "number",
"renderTypeList": ["reference"],
"key": "count",
"label": "count",
"toolDescription": "返回结果条数",
"required": false,
"canEdit": true,
"editField": {
"key": true,
"description": true
},
"customInputConfig": {
"selectValueTypeList": [
"string",
"number",
"boolean",
"object",
"arrayString",
"arrayNumber",
"arrayBoolean",
"arrayObject",
"arrayAny",
"any",
"chatHistory",
"datasetQuote",
"dynamic",
"selectApp",
"selectDataset"
],
"showDescription": false,
"showDefaultValue": true
},
"value": ["pluginInput", "count"]
}
],
"outputs": [
{
"id": "error",
"key": "error",
"label": "workflow:request_error",
"description": "HTTP请求错误信息成功时返回空",
"valueType": "object",
"type": "static"
},
{
"id": "httpRawResponse",
"key": "httpRawResponse",
"required": true,
"label": "workflow:raw_response",
"description": "HTTP请求的原始响应。只能接受字符串或JSON类型响应数据。",
"valueType": "any",
"type": "static"
},
{
"id": "system_addOutputParam",
"key": "system_addOutputParam",
"type": "dynamic",
"valueType": "dynamic",
"label": "",
"editField": {
"key": true,
"valueType": true
}
}
]
}
],
"edges": [
{
"source": "pluginInput",
"target": "nyA6oA8mF1iW",
"sourceHandle": "pluginInput-source-right",
"targetHandle": "nyA6oA8mF1iW-target-left"
},
{
"source": "nyA6oA8mF1iW",
"target": "pluginOutput",
"sourceHandle": "nyA6oA8mF1iW-source-right",
"targetHandle": "pluginOutput-target-left"
}
]
},
"chatConfig": {}
}

View File

@@ -1,6 +1,5 @@
{
"author": "silencezhang",
"version": "4811",
"name": "数据库连接",
"avatar": "core/workflow/template/datasource",
"intro": "可连接常用数据库并执行sql",

View File

@@ -1,6 +1,5 @@
{
"author": "collin",
"version": "4817",
"name": "流程等待",
"avatar": "core/workflow/template/sleep",
"intro": "让工作流等待指定时间后运行",

View File

@@ -1,6 +1,5 @@
{
"author": "silencezhang",
"version": "4817",
"name": "基础图表",
"avatar": "core/workflow/template/baseChart",
"intro": "根据数据生成图表可根据chartType生成柱状图折线图饼图",

View File

@@ -1,6 +1,5 @@
{
"author": "silencezhang",
"version": "486",
"name": "BI图表功能",
"avatar": "core/workflow/template/BI",
"intro": "BI图表功能可以生成一些常用的图表如饼图柱状图折线图等",

View File

@@ -1,6 +1,5 @@
{
"author": "",
"version": "486",
"name": "DuckDuckGo 网络搜索",
"avatar": "core/workflow/template/duckduckgo",
"intro": "使用 DuckDuckGo 进行网络搜索",

View File

@@ -1,6 +1,5 @@
{
"author": "",
"version": "486",
"name": "DuckDuckGo 图片搜索",
"avatar": "core/workflow/template/duckduckgo",
"intro": "使用 DuckDuckGo 进行图片搜索",

View File

@@ -1,6 +1,5 @@
{
"author": "",
"version": "486",
"name": "DuckDuckGo 新闻检索",
"avatar": "core/workflow/template/duckduckgo",
"intro": "使用 DuckDuckGo 进行新闻检索",

View File

@@ -1,6 +1,5 @@
{
"author": "",
"version": "486",
"name": "DuckDuckGo 视频搜索",
"avatar": "core/workflow/template/duckduckgo",
"intro": "使用 DuckDuckGo 进行视频搜索",

View File

@@ -1,6 +1,5 @@
{
"author": "",
"version": "486",
"name": "DuckDuckGo服务",
"avatar": "core/workflow/template/duckduckgo",
"intro": "DuckDuckGo 服务,包含网络搜索、图片搜索、新闻搜索等。",

View File

@@ -1,6 +1,5 @@
{
"author": "",
"version": "488",
"name": "飞书 webhook",
"avatar": "core/app/templates/plugin-feishu",
"intro": "向飞书机器人发起 webhook 请求。",

View File

@@ -1,6 +1,5 @@
{
"author": "",
"version": "486",
"name": "网页内容抓取",
"avatar": "core/workflow/template/fetchUrl",
"intro": "可获取一个网页链接内容,并以 Markdown 格式输出,仅支持获取静态网站。",

View File

@@ -1,6 +1,5 @@
{
"author": "",
"version": "481",
"templateType": "tools",
"name": "获取当前时间",
"avatar": "core/workflow/template/getTime",

View File

@@ -1,6 +1,5 @@
{
"author": "",
"version": "4811",
"name": "Google搜索",
"avatar": "core/workflow/template/google",
"intro": "在google中搜索。",

View File

@@ -1,6 +1,5 @@
{
"author": "",
"version": "486",
"name": "数学公式执行",
"avatar": "core/workflow/template/mathCall",
"intro": "用于执行数学表达式的工具,通过 js 的 expr-eval 库运行表达式并返回结果。",

View File

@@ -1,6 +1,5 @@
{
"author": "",
"version": "4816",
"name": "Search XNG 搜索",
"avatar": "core/workflow/template/searxng",
"intro": "使用 Search XNG 服务进行搜索。",

View File

@@ -1,6 +1,5 @@
{
"author": "cloudpense",
"version": "1.0.0",
"name": "Email 邮件发送",
"avatar": "plugins/email",
"intro": "通过SMTP协议发送电子邮件(nodemailer)",

View File

@@ -1,6 +1,5 @@
{
"author": "",
"version": "489",
"name": "文本加工",
"avatar": "/imgs/workflow/textEditor.svg",
"intro": "可对固定或传入的文本进行加工后输出,非字符串类型数据最终会转成字符串类型。",

View File

@@ -1,6 +1,5 @@
{
"author": "",
"version": "4811",
"name": "Wiki搜索",
"avatar": "core/workflow/template/wiki",
"intro": "在Wiki中查询释义。",

View File

@@ -0,0 +1,178 @@
import { retryFn } from '@fastgpt/global/common/system/utils';
import { connectionMongo } from '../../mongo';
import { MongoRawTextBufferSchema, bucketName } from './schema';
import { addLog } from '../../system/log';
import { setCron } from '../../system/cron';
import { checkTimerLock } from '../../system/timerLock/utils';
import { TimerIdEnum } from '../../system/timerLock/constants';
const getGridBucket = () => {
return new connectionMongo.mongo.GridFSBucket(connectionMongo.connection.db!, {
bucketName: bucketName
});
};
export const addRawTextBuffer = async ({
sourceId,
sourceName,
text,
expiredTime
}: {
sourceId: string;
sourceName: string;
text: string;
expiredTime: Date;
}) => {
const gridBucket = getGridBucket();
const metadata = {
sourceId,
sourceName,
expiredTime
};
const buffer = Buffer.from(text);
const fileSize = buffer.length;
// 单块大小:尽可能大,但不超过 14MB不小于128KB
const chunkSizeBytes = (() => {
// 计算理想块大小:文件大小 ÷ 目标块数(10)。 并且每个块需要小于 14MB
const idealChunkSize = Math.min(Math.ceil(fileSize / 10), 14 * 1024 * 1024);
// 确保块大小至少为128KB
const minChunkSize = 128 * 1024; // 128KB
// 取理想块大小和最小块大小中的较大值
let chunkSize = Math.max(idealChunkSize, minChunkSize);
// 将块大小向上取整到最接近的64KB的倍数使其更整齐
chunkSize = Math.ceil(chunkSize / (64 * 1024)) * (64 * 1024);
return chunkSize;
})();
const uploadStream = gridBucket.openUploadStream(sourceId, {
metadata,
chunkSizeBytes
});
return retryFn(async () => {
return new Promise((resolve, reject) => {
uploadStream.end(buffer);
uploadStream.on('finish', () => {
resolve(uploadStream.id);
});
uploadStream.on('error', (error) => {
addLog.error('addRawTextBuffer error', error);
resolve('');
});
});
});
};
export const getRawTextBuffer = async (sourceId: string) => {
const gridBucket = getGridBucket();
return retryFn(async () => {
const bufferData = await MongoRawTextBufferSchema.findOne(
{
'metadata.sourceId': sourceId
},
'_id metadata'
).lean();
if (!bufferData) {
return null;
}
// Read file content
const downloadStream = gridBucket.openDownloadStream(bufferData._id);
const chunks: Buffer[] = [];
return new Promise<{
text: string;
sourceName: string;
} | null>((resolve, reject) => {
downloadStream.on('data', (chunk) => {
chunks.push(chunk);
});
downloadStream.on('end', () => {
const buffer = Buffer.concat(chunks);
const text = buffer.toString('utf8');
resolve({
text,
sourceName: bufferData.metadata?.sourceName || ''
});
});
downloadStream.on('error', (error) => {
addLog.error('getRawTextBuffer error', error);
resolve(null);
});
});
});
};
export const deleteRawTextBuffer = async (sourceId: string): Promise<boolean> => {
const gridBucket = getGridBucket();
return retryFn(async () => {
const buffer = await MongoRawTextBufferSchema.findOne({ 'metadata.sourceId': sourceId });
if (!buffer) {
return false;
}
await gridBucket.delete(buffer._id);
return true;
});
};
export const updateRawTextBufferExpiredTime = async ({
sourceId,
expiredTime
}: {
sourceId: string;
expiredTime: Date;
}) => {
return retryFn(async () => {
return MongoRawTextBufferSchema.updateOne(
{ 'metadata.sourceId': sourceId },
{ $set: { 'metadata.expiredTime': expiredTime } }
);
});
};
export const clearExpiredRawTextBufferCron = async () => {
const clearExpiredRawTextBuffer = async () => {
addLog.debug('Clear expired raw text buffer start');
const gridBucket = getGridBucket();
return retryFn(async () => {
const data = await MongoRawTextBufferSchema.find(
{
'metadata.expiredTime': { $lt: new Date() }
},
'_id'
).lean();
for (const item of data) {
await gridBucket.delete(item._id);
}
addLog.debug('Clear expired raw text buffer end');
});
};
setCron('*/10 * * * *', async () => {
if (
await checkTimerLock({
timerId: TimerIdEnum.clearExpiredRawTextBuffer,
lockMinuted: 9
})
) {
try {
await clearExpiredRawTextBuffer();
} catch (error) {
addLog.error('clearExpiredRawTextBufferCron error', error);
}
}
});
};

View File

@@ -1,33 +1,22 @@
import { getMongoModel, Schema } from '../../mongo';
import { type RawTextBufferSchemaType } from './type';
import { getMongoModel, type Types, Schema } from '../../mongo';
export const collectionName = 'buffer_rawtexts';
export const bucketName = 'buffer_rawtext';
const RawTextBufferSchema = new Schema({
sourceId: {
type: String,
required: true
},
rawText: {
type: String,
default: ''
},
createTime: {
type: Date,
default: () => new Date()
},
metadata: Object
metadata: {
sourceId: { type: String, required: true },
sourceName: { type: String, required: true },
expiredTime: { type: Date, required: true }
}
});
RawTextBufferSchema.index({ 'metadata.sourceId': 'hashed' });
RawTextBufferSchema.index({ 'metadata.expiredTime': -1 });
try {
RawTextBufferSchema.index({ sourceId: 1 });
// 20 minutes
RawTextBufferSchema.index({ createTime: 1 }, { expireAfterSeconds: 20 * 60 });
} catch (error) {
console.log(error);
}
export const MongoRawTextBuffer = getMongoModel<RawTextBufferSchemaType>(
collectionName,
RawTextBufferSchema
);
export const MongoRawTextBufferSchema = getMongoModel<{
_id: Types.ObjectId;
metadata: {
sourceId: string;
sourceName: string;
expiredTime: Date;
};
}>(`${bucketName}.files`, RawTextBufferSchema);

View File

@@ -1,8 +0,0 @@
export type RawTextBufferSchemaType = {
sourceId: string;
rawText: string;
createTime: Date;
metadata?: {
filename: string;
};
};

View File

@@ -6,13 +6,13 @@ import { type DatasetFileSchema } from '@fastgpt/global/core/dataset/type';
import { MongoChatFileSchema, MongoDatasetFileSchema } from './schema';
import { detectFileEncoding, detectFileEncodingByPath } from '@fastgpt/global/common/file/tools';
import { CommonErrEnum } from '@fastgpt/global/common/error/code/common';
import { MongoRawTextBuffer } from '../../buffer/rawText/schema';
import { readRawContentByFileBuffer } from '../read/utils';
import { gridFsStream2Buffer, stream2Encoding } from './utils';
import { addLog } from '../../system/log';
import { readFromSecondary } from '../../mongo/utils';
import { parseFileExtensionFromUrl } from '@fastgpt/global/common/string/tools';
import { Readable } from 'stream';
import { addRawTextBuffer, getRawTextBuffer } from '../../buffer/rawText/controller';
import { addMinutes } from 'date-fns';
export function getGFSCollection(bucket: `${BucketNameEnum}`) {
MongoDatasetFileSchema;
@@ -223,15 +223,13 @@ export const readFileContentFromMongo = async ({
rawText: string;
filename: string;
}> => {
const bufferId = `${fileId}-${customPdfParse}`;
const bufferId = `${String(fileId)}-${customPdfParse}`;
// read buffer
const fileBuffer = await MongoRawTextBuffer.findOne({ sourceId: bufferId }, undefined, {
...readFromSecondary
}).lean();
const fileBuffer = await getRawTextBuffer(bufferId);
if (fileBuffer) {
return {
rawText: fileBuffer.rawText,
filename: fileBuffer.metadata?.filename || ''
rawText: fileBuffer.text,
filename: fileBuffer?.sourceName
};
}
@@ -265,16 +263,13 @@ export const readFileContentFromMongo = async ({
}
});
// < 14M
if (fileBuffers.length < 14 * 1024 * 1024 && rawText.trim()) {
MongoRawTextBuffer.create({
sourceId: bufferId,
rawText,
metadata: {
filename: file.filename
}
});
}
// Add buffer
addRawTextBuffer({
sourceId: bufferId,
sourceName: file.filename,
text: rawText,
expiredTime: addMinutes(new Date(), 20)
});
return {
rawText,

View File

@@ -1,16 +1,16 @@
import { Schema, getMongoModel } from '../../mongo';
const DatasetFileSchema = new Schema({});
const ChatFileSchema = new Schema({});
const DatasetFileSchema = new Schema({
metadata: Object
});
const ChatFileSchema = new Schema({
metadata: Object
});
try {
DatasetFileSchema.index({ uploadDate: -1 });
DatasetFileSchema.index({ uploadDate: -1 });
ChatFileSchema.index({ uploadDate: -1 });
ChatFileSchema.index({ 'metadata.chatId': 1 });
} catch (error) {
console.log(error);
}
ChatFileSchema.index({ uploadDate: -1 });
ChatFileSchema.index({ 'metadata.chatId': 1 });
export const MongoDatasetFileSchema = getMongoModel('dataset.files', DatasetFileSchema);
export const MongoChatFileSchema = getMongoModel('chat.files', ChatFileSchema);

View File

@@ -1,5 +1,57 @@
import { detectFileEncoding } from '@fastgpt/global/common/file/tools';
import { PassThrough } from 'stream';
import { getGridBucket } from './controller';
import { type BucketNameEnum } from '@fastgpt/global/common/file/constants';
import { retryFn } from '@fastgpt/global/common/system/utils';
export const createFileFromText = async ({
bucket,
filename,
text,
metadata
}: {
bucket: `${BucketNameEnum}`;
filename: string;
text: string;
metadata: Record<string, any>;
}) => {
const gridBucket = getGridBucket(bucket);
const buffer = Buffer.from(text);
const fileSize = buffer.length;
// 单块大小:尽可能大,但不超过 14MB不小于128KB
const chunkSizeBytes = (() => {
// 计算理想块大小:文件大小 ÷ 目标块数(10)。 并且每个块需要小于 14MB
const idealChunkSize = Math.min(Math.ceil(fileSize / 10), 14 * 1024 * 1024);
// 确保块大小至少为128KB
const minChunkSize = 128 * 1024; // 128KB
// 取理想块大小和最小块大小中的较大值
let chunkSize = Math.max(idealChunkSize, minChunkSize);
// 将块大小向上取整到最接近的64KB的倍数使其更整齐
chunkSize = Math.ceil(chunkSize / (64 * 1024)) * (64 * 1024);
return chunkSize;
})();
const uploadStream = gridBucket.openUploadStream(filename, {
metadata,
chunkSizeBytes
});
return retryFn(async () => {
return new Promise<{ fileId: string }>((resolve, reject) => {
uploadStream.end(buffer);
uploadStream.on('finish', () => {
resolve({ fileId: String(uploadStream.id) });
});
uploadStream.on('error', reject);
});
});
};
export const gridFsStream2Buffer = (stream: NodeJS.ReadableStream) => {
return new Promise<Buffer>((resolve, reject) => {

View File

@@ -110,7 +110,7 @@ export const readRawContentByFileBuffer = async ({
return {
rawText: text,
formatText: rawText,
formatText: text,
imageList
};
};

View File

@@ -5,7 +5,8 @@ export enum TimerIdEnum {
clearExpiredSubPlan = 'clearExpiredSubPlan',
updateStandardPlan = 'updateStandardPlan',
scheduleTriggerApp = 'scheduleTriggerApp',
notification = 'notification'
notification = 'notification',
clearExpiredRawTextBuffer = 'clearExpiredRawTextBuffer'
}
export enum LockNotificationEnum {

View File

@@ -30,8 +30,7 @@ import { Types } from 'mongoose';
community: community-id
commercial: commercial-id
*/
export async function splitCombinePluginId(id: string) {
export function splitCombineToolId(id: string) {
const splitRes = id.split('-');
if (splitRes.length === 1) {
// app id
@@ -42,7 +41,7 @@ export async function splitCombinePluginId(id: string) {
}
const [source, pluginId] = id.split('-') as [PluginSourceEnum, string];
if (!source || !pluginId) return Promise.reject('pluginId not found');
if (!source || !pluginId) throw new Error('pluginId not found');
return { source, pluginId: id };
}
@@ -54,7 +53,7 @@ const getSystemPluginTemplateById = async (
versionId?: string
): Promise<ChildAppType> => {
const item = getSystemPluginTemplates().find((plugin) => plugin.id === pluginId);
if (!item) return Promise.reject(PluginErrEnum.unAuth);
if (!item) return Promise.reject(PluginErrEnum.unExist);
const plugin = cloneDeep(item);
@@ -64,10 +63,10 @@ const getSystemPluginTemplateById = async (
{ pluginId: plugin.id, 'customConfig.associatedPluginId': plugin.associatedPluginId },
'associatedPluginId'
).lean();
if (!systemPlugin) return Promise.reject(PluginErrEnum.unAuth);
if (!systemPlugin) return Promise.reject(PluginErrEnum.unExist);
const app = await MongoApp.findById(plugin.associatedPluginId).lean();
if (!app) return Promise.reject(PluginErrEnum.unAuth);
if (!app) return Promise.reject(PluginErrEnum.unExist);
const version = versionId
? await getAppVersionById({
@@ -77,6 +76,12 @@ const getSystemPluginTemplateById = async (
})
: await getAppLatestVersion(plugin.associatedPluginId, app);
if (!version.versionId) return Promise.reject('App version not found');
const isLatest = version.versionId
? await checkIsLatestVersion({
appId: plugin.associatedPluginId,
versionId: version.versionId
})
: true;
return {
...plugin,
@@ -85,12 +90,19 @@ const getSystemPluginTemplateById = async (
edges: version.edges,
chatConfig: version.chatConfig
},
version: versionId || String(version.versionId),
version: versionId ? version?.versionId : '',
versionLabel: version?.versionName,
isLatestVersion: isLatest,
teamId: String(app.teamId),
tmbId: String(app.tmbId)
};
}
return plugin;
return {
...plugin,
version: undefined,
isLatestVersion: true
};
};
/* Format plugin to workflow preview node data */
@@ -102,11 +114,11 @@ export async function getChildAppPreviewNode({
versionId?: string;
}): Promise<FlowNodeTemplateType> {
const app: ChildAppType = await (async () => {
const { source, pluginId } = await splitCombinePluginId(appId);
const { source, pluginId } = splitCombineToolId(appId);
if (source === PluginSourceEnum.personal) {
const item = await MongoApp.findById(appId).lean();
if (!item) return Promise.reject('plugin not found');
if (!item) return Promise.reject(PluginErrEnum.unExist);
const version = await getAppVersionById({ appId, versionId, app: item });
@@ -132,8 +144,8 @@ export async function getChildAppPreviewNode({
},
templateType: FlowNodeTemplateTypeEnum.teamApp,
version: version.versionId,
versionLabel: version?.versionName || '',
version: versionId ? version?.versionId : '',
versionLabel: version?.versionName,
isLatestVersion: isLatest,
originCost: 0,
@@ -142,7 +154,7 @@ export async function getChildAppPreviewNode({
pluginOrder: 0
};
} else {
return getSystemPluginTemplateById(pluginId);
return getSystemPluginTemplateById(pluginId, versionId);
}
})();
@@ -216,12 +228,12 @@ export async function getChildAppRuntimeById(
id: string,
versionId?: string
): Promise<PluginRuntimeType> {
const app: ChildAppType = await (async () => {
const { source, pluginId } = await splitCombinePluginId(id);
const app = await (async () => {
const { source, pluginId } = splitCombineToolId(id);
if (source === PluginSourceEnum.personal) {
const item = await MongoApp.findById(id).lean();
if (!item) return Promise.reject('plugin not found');
if (!item) return Promise.reject(PluginErrEnum.unExist);
const version = await getAppVersionById({
appId: id,
@@ -244,8 +256,6 @@ export async function getChildAppRuntimeById(
},
templateType: FlowNodeTemplateTypeEnum.teamApp,
// 用不到
version: item?.pluginData?.nodeVersion,
originCost: 0,
currentCost: 0,
hasTokenFee: false,

View File

@@ -1,6 +1,6 @@
import { type ChatNodeUsageType } from '@fastgpt/global/support/wallet/bill/type';
import { type PluginRuntimeType } from '@fastgpt/global/core/plugin/type';
import { splitCombinePluginId } from './controller';
import { splitCombineToolId } from './controller';
import { PluginSourceEnum } from '@fastgpt/global/core/plugin/constants';
/*
@@ -20,7 +20,7 @@ export const computedPluginUsage = async ({
childrenUsage: ChatNodeUsageType[];
error?: boolean;
}) => {
const { source } = await splitCombinePluginId(plugin.id);
const { source } = splitCombineToolId(plugin.id);
const childrenUsages = childrenUsage.reduce((sum, item) => sum + (item.totalPoints || 0), 0);
if (source !== PluginSourceEnum.personal) {

View File

@@ -64,7 +64,12 @@ const AppSchema = new Schema({
type: Date,
default: () => new Date()
},
tags: [
{
type: Schema.Types.ObjectId,
ref: 'app_tags'
}
],
// role and auth
teamTags: {
type: [String]

View File

@@ -0,0 +1,242 @@
import { MongoTag } from './schema';
import { MongoApp } from '../schema';
import { Types } from '../../../common/mongo';
/**
* 创建新标签
*/
export const createTag = async ({
teamId,
name,
color
}: {
teamId: string;
name: string;
color?: string;
}) => {
const tag = await MongoTag.create({
teamId,
name,
color
});
return tag.toObject();
};
/**
* 获取团队所有标签
*/
export const getTeamTags = async (teamId: string) => {
const tags = await MongoTag.find({ teamId }).lean();
return tags;
};
/**
* 获取标签使用统计
*/
export const getTagsWithCount = async (teamId: string) => {
return MongoTag.aggregate([
{ $match: { teamId: new Types.ObjectId(teamId) } },
{
$lookup: {
from: 'apps',
localField: '_id',
foreignField: 'tags',
as: 'apps'
}
},
{
$addFields: {
count: { $size: '$apps' }
}
},
{
$project: {
apps: 0
}
}
]);
};
/**
* 更新标签
*/
export const updateTag = async ({
tagId,
teamId,
name,
color
}: {
tagId: string;
teamId: string;
name?: string;
color?: string;
}) => {
const updateData: Record<string, any> = {};
if (name !== undefined) updateData.name = name;
if (color !== undefined) updateData.color = color;
await MongoTag.updateOne({ _id: tagId, teamId }, { $set: updateData });
return MongoTag.findById(tagId).lean();
};
/**
* 删除标签
*/
export const deleteTag = async ({ tagId, teamId }: { tagId: string; teamId: string }) => {
// 先从所有 app 中移除该标签
await MongoApp.updateMany({ teamId, tags: tagId }, { $pull: { tags: tagId } });
// 然后删除标签
await MongoTag.deleteOne({ _id: tagId, teamId });
return true;
};
/**
* 为 app 添加标签
*/
export const addTagToApp = async ({
appId,
tagId,
teamId
}: {
appId: string;
tagId: string;
teamId: string;
}) => {
// 确认标签存在且属于该团队
const tag = await MongoTag.findOne({ _id: tagId, teamId });
if (!tag) {
throw new Error('Tag not found or not authorized');
}
await MongoApp.updateOne({ _id: appId, teamId }, { $addToSet: { tags: tagId } });
return true;
};
/**
* 从 app 移除标签
*/
export const removeTagFromApp = async ({
appId,
tagId,
teamId
}: {
appId: string;
tagId: string;
teamId: string;
}) => {
await MongoApp.updateOne({ _id: appId, teamId }, { $pull: { tags: tagId } });
return true;
};
/**
* 批量删除标签
*/
export const batchDeleteTags = async ({ tagIds, teamId }: { tagIds: string[]; teamId: string }) => {
if (!tagIds || tagIds.length === 0) {
return true;
}
// 先从所有 app 中移除这些标签
await MongoApp.updateMany(
{ teamId, tags: { $in: tagIds } },
{ $pull: { tags: { $in: tagIds } } }
);
// 然后删除标签
const result = await MongoTag.deleteMany({ _id: { $in: tagIds }, teamId });
return { deletedCount: result.deletedCount };
};
/**
* 批量为 app 添加标签
*/
export const batchAddTagsToApp = async ({
appId,
tagIds,
teamId
}: {
appId: string;
tagIds: string[];
teamId: string;
}) => {
if (!tagIds || tagIds.length === 0) {
return true;
}
// 确认标签存在且属于该团队
const tags = await MongoTag.find({ _id: { $in: tagIds }, teamId });
if (tags.length !== tagIds.length) {
throw new Error('Some tags not found or not authorized');
}
await MongoApp.updateOne({ _id: appId, teamId }, { $addToSet: { tags: { $each: tagIds } } });
return true;
};
/**
* 批量从 app 移除标签
*/
export const batchRemoveTagsFromApp = async ({
appId,
tagIds,
teamId
}: {
appId: string;
tagIds: string[];
teamId: string;
}) => {
if (!tagIds || tagIds.length === 0) {
return true;
}
await MongoApp.updateOne({ _id: appId, teamId }, { $pull: { tags: { $in: tagIds } } });
return true;
};
/**
* 批量为某一标签添加 app全量更新
*/
export const batchAddAppsToTag = async ({
tagId,
appIds,
teamId
}: {
tagId: string;
appIds: string[];
teamId: string;
}) => {
// 确认标签存在且属于该团队
const tag = await MongoTag.findOne({ _id: tagId, teamId });
if (!tag) {
throw new Error('Tag not found or not authorized');
}
// 如果 appIds 为空数组,则移除该标签的所有应用
if (!appIds || appIds.length === 0) {
await MongoApp.updateMany({ teamId, tags: tagId }, { $pull: { tags: tagId } });
return true;
}
// 确认所有 app 都存在且属于该团队
const apps = await MongoApp.find({ _id: { $in: appIds }, teamId });
if (apps.length !== appIds.length) {
throw new Error('Some apps not found or not authorized');
}
// 先从所有应用中移除该标签
await MongoApp.updateMany({ teamId, tags: tagId }, { $pull: { tags: tagId } });
// 然后为指定的应用添加该标签
await MongoApp.updateMany({ _id: { $in: appIds }, teamId }, { $addToSet: { tags: tagId } });
return true;
};

View File

@@ -0,0 +1,37 @@
import { TeamCollectionName } from '@fastgpt/global/support/user/team/constant';
import { getMongoModel, Schema } from '../../../common/mongo';
export const TagCollectionName = 'app_tags';
export type TagSchemaType = {
_id: string;
teamId: string;
name: string;
color: string;
createTime: Date;
};
const TagSchema = new Schema({
teamId: {
type: Schema.Types.ObjectId,
ref: TeamCollectionName,
required: true
},
name: {
type: String,
required: true
},
color: {
type: String,
default: '#3370ff'
},
createTime: {
type: Date,
default: () => new Date()
}
});
// 创建复合索引:按团队和名称确保唯一性
TagSchema.index({ teamId: 1, name: 1 }, { unique: true });
export const MongoTag = getMongoModel<TagSchemaType>(TagCollectionName, TagSchema);

View File

@@ -1,14 +1,13 @@
import { MongoDataset } from '../dataset/schema';
import { getEmbeddingModel } from '../ai/model';
import {
AppNodeFlowNodeTypeMap,
FlowNodeTypeEnum
} from '@fastgpt/global/core/workflow/node/constant';
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
import { NodeInputKeyEnum } from '@fastgpt/global/core/workflow/constants';
import type { StoreNodeItemType } from '@fastgpt/global/core/workflow/type/node';
import { MongoAppVersion } from './version/schema';
import { checkIsLatestVersion } from './version/controller';
import { Types } from '../../common/mongo';
import { getChildAppPreviewNode, splitCombineToolId } from './plugin/controller';
import { PluginSourceEnum } from '@fastgpt/global/core/plugin/constants';
import { authAppByTmbId } from '../../support/permission/app/auth';
import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant';
import { getErrText } from '@fastgpt/global/common/error/utils';
export async function listAppDatasetDataByTeamIdAndDatasetIds({
teamId,
@@ -33,53 +32,58 @@ export async function listAppDatasetDataByTeamIdAndDatasetIds({
export async function rewriteAppWorkflowToDetail({
nodes,
teamId,
isRoot
isRoot,
ownerTmbId
}: {
nodes: StoreNodeItemType[];
teamId: string;
isRoot: boolean;
ownerTmbId: string;
}) {
const datasetIdSet = new Set<string>();
// Add node(App Type) versionlabel and latest sign
const appNodes = nodes.filter((node) => AppNodeFlowNodeTypeMap[node.flowNodeType]);
const versionIds = appNodes
.filter((node) => node.version && Types.ObjectId.isValid(node.version))
.map((node) => node.version);
/* Add node(App Type) versionlabel and latest sign ==== */
await Promise.all(
nodes.map(async (node) => {
if (!node.pluginId) return;
const { source } = splitCombineToolId(node.pluginId);
if (versionIds.length > 0) {
const versionDataList = await MongoAppVersion.find(
{
_id: { $in: versionIds }
},
'_id versionName appId time'
).lean();
try {
const [preview] = await Promise.all([
getChildAppPreviewNode({
appId: node.pluginId,
versionId: node.version
}),
...(source === PluginSourceEnum.personal
? [
authAppByTmbId({
tmbId: ownerTmbId,
appId: node.pluginId,
per: ReadPermissionVal
})
]
: [])
]);
const versionMap: Record<string, any> = {};
const isLatestChecks = await Promise.all(
versionDataList.map(async (version) => {
const isLatest = await checkIsLatestVersion({
appId: version.appId,
versionId: version._id
});
return { versionId: String(version._id), isLatest };
})
);
const isLatestMap = new Map(isLatestChecks.map((item) => [item.versionId, item.isLatest]));
versionDataList.forEach((version) => {
versionMap[String(version._id)] = version;
});
appNodes.forEach((node) => {
if (!node.version) return;
const versionData = versionMap[String(node.version)];
if (versionData) {
node.versionLabel = versionData.versionName;
node.isLatestVersion = isLatestMap.get(String(node.version)) || false;
node.pluginData = {
diagram: preview.diagram,
userGuide: preview.userGuide,
courseUrl: preview.courseUrl,
name: preview.name,
avatar: preview.avatar
};
node.versionLabel = preview.versionLabel;
node.isLatestVersion = preview.isLatestVersion;
node.version = preview.version;
} catch (error) {
node.pluginData = {
error: getErrText(error)
};
}
});
}
})
);
/* Add node(App Type) versionlabel and latest sign ==== */
// Get all dataset ids from nodes
nodes.forEach((node) => {

View File

@@ -68,6 +68,9 @@ export const checkIsLatestVersion = async ({
appId: string;
versionId: string;
}) => {
if (!Types.ObjectId.isValid(versionId)) {
return false;
}
const version = await MongoAppVersion.findOne(
{
appId,

View File

@@ -77,7 +77,10 @@ export const createCollectionAndInsertData = async ({
const chunkSplitter = computeChunkSplitter(createCollectionParams);
const paragraphChunkDeep = computeParagraphChunkDeep(createCollectionParams);
if (trainingType === DatasetCollectionDataProcessModeEnum.qa) {
if (
trainingType === DatasetCollectionDataProcessModeEnum.qa ||
trainingType === DatasetCollectionDataProcessModeEnum.backup
) {
delete createCollectionParams.chunkTriggerType;
delete createCollectionParams.chunkTriggerMinSize;
delete createCollectionParams.dataEnhanceCollectionName;

View File

@@ -218,6 +218,10 @@ export const rawText2Chunks = ({
};
};
if (backupParse) {
return parseDatasetBackup2Chunks(rawText).chunks;
}
// Chunk condition
// 1. 选择最大值条件,只有超过了最大值(默认为模型的最大值*0.7),才会触发分块
if (chunkTriggerType === ChunkTriggerConfigTypeEnum.maxSize) {
@@ -240,10 +244,6 @@ export const rawText2Chunks = ({
}
}
if (backupParse) {
return parseDatasetBackup2Chunks(rawText).chunks;
}
const { chunks } = splitText2Chunks({
text: rawText,
chunkSize,

View File

@@ -86,7 +86,6 @@ export const dispatchRunTools = async (props: DispatchToolModuleProps): Promise<
});
// Check interactive entry
const interactiveResponse = lastInteractive;
props.node.isEntry = false;
const hasReadFilesTool = toolNodes.some(
(item) => item.flowNodeType === FlowNodeTypeEnum.readFiles
@@ -143,7 +142,7 @@ export const dispatchRunTools = async (props: DispatchToolModuleProps): Promise<
})
}
];
if (interactiveResponse) {
if (lastInteractive && isEntry) {
return value.slice(0, -2);
}
return value;
@@ -183,7 +182,7 @@ export const dispatchRunTools = async (props: DispatchToolModuleProps): Promise<
toolModel,
maxRunToolTimes: 30,
messages: adaptMessages,
interactiveEntryToolParams: interactiveResponse?.toolParams
interactiveEntryToolParams: lastInteractive?.toolParams
});
}
if (toolModel.functionCall) {
@@ -194,7 +193,7 @@ export const dispatchRunTools = async (props: DispatchToolModuleProps): Promise<
toolNodes,
toolModel,
messages: adaptMessages,
interactiveEntryToolParams: interactiveResponse?.toolParams
interactiveEntryToolParams: lastInteractive?.toolParams
});
}
@@ -224,7 +223,7 @@ export const dispatchRunTools = async (props: DispatchToolModuleProps): Promise<
toolNodes,
toolModel,
messages: adaptMessages,
interactiveEntryToolParams: interactiveResponse?.toolParams
interactiveEntryToolParams: lastInteractive?.toolParams
});
})();

View File

@@ -11,7 +11,6 @@ import type {
SystemVariablesType
} from '@fastgpt/global/core/workflow/runtime/type';
import type { RuntimeNodeItemType } from '@fastgpt/global/core/workflow/runtime/type.d';
import type { FlowNodeOutputItemType } from '@fastgpt/global/core/workflow/type/io.d';
import type {
AIChatItemValueItemType,
ChatHistoryItemResType,

View File

@@ -17,6 +17,7 @@ import { chatValue2RuntimePrompt } from '@fastgpt/global/core/chat/adapt';
import { getPluginRunUserQuery } from '@fastgpt/global/core/workflow/utils';
import { getPluginInputsFromStoreNodes } from '@fastgpt/global/core/app/plugin/utils';
import type { NodeInputKeyEnum } from '@fastgpt/global/core/workflow/constants';
import { getUserChatInfoAndAuthTeamPoints } from '../../../../support/permission/auth/team';
type RunPluginProps = ModuleDispatchProps<{
[NodeInputKeyEnum.forbidStream]?: boolean;
@@ -73,9 +74,11 @@ export const dispatchRunPlugin = async (props: RunPluginProps): Promise<RunPlugi
};
});
const { externalProvider } = await getUserChatInfoAndAuthTeamPoints(runningAppInfo.tmbId);
const runtimeVariables = {
...filterSystemVariables(props.variables),
appId: String(plugin.id)
appId: String(plugin.id),
...(externalProvider ? externalProvider.externalWorkflowVariables : {})
};
const { flowResponses, flowUsages, assistantResponses, runTimes } = await dispatchWorkFlow({
...props,

View File

@@ -20,6 +20,7 @@ import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant';
import { getAppVersionById } from '../../../app/version/controller';
import { parseUrlToFileType } from '@fastgpt/global/common/file/tools';
import { type ChildrenInteractive } from '@fastgpt/global/core/workflow/template/system/interactive/type';
import { getUserChatInfoAndAuthTeamPoints } from '../../../../support/permission/auth/team';
type Props = ModuleDispatchProps<{
[NodeInputKeyEnum.userChatInput]: string;
@@ -97,11 +98,13 @@ export const dispatchRunAppNode = async (props: Props): Promise<Response> => {
// Rewrite children app variables
const systemVariables = filterSystemVariables(variables);
const { externalProvider } = await getUserChatInfoAndAuthTeamPoints(appData.tmbId);
const childrenRunVariables = {
...systemVariables,
...childrenAppVariables,
histories: chatHistories,
appId: String(appData._id)
appId: String(appData._id),
...(externalProvider ? externalProvider.externalWorkflowVariables : {})
};
const childrenInteractive =

View File

@@ -5,8 +5,6 @@ import { NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants';
import { type DispatchNodeResultType } from '@fastgpt/global/core/workflow/runtime/type';
import axios from 'axios';
import { serverRequestBaseUrl } from '../../../../common/api/serverRequest';
import { MongoRawTextBuffer } from '../../../../common/buffer/rawText/schema';
import { readFromSecondary } from '../../../../common/mongo/utils';
import { getErrText } from '@fastgpt/global/common/error/utils';
import { detectFileEncoding, parseUrlToFileType } from '@fastgpt/global/common/file/tools';
import { readRawContentByFileBuffer } from '../../../../common/file/read/utils';
@@ -14,6 +12,8 @@ import { ChatRoleEnum } from '@fastgpt/global/core/chat/constants';
import { type ChatItemType, type UserChatItemValueItemType } from '@fastgpt/global/core/chat/type';
import { parseFileExtensionFromUrl } from '@fastgpt/global/common/string/tools';
import { addLog } from '../../../../common/system/log';
import { addRawTextBuffer, getRawTextBuffer } from '../../../../common/buffer/rawText/controller';
import { addMinutes } from 'date-fns';
type Props = ModuleDispatchProps<{
[NodeInputKeyEnum.fileUrlList]: string[];
@@ -158,14 +158,12 @@ export const getFileContentFromLinks = async ({
parseUrlList
.map(async (url) => {
// Get from buffer
const fileBuffer = await MongoRawTextBuffer.findOne({ sourceId: url }, undefined, {
...readFromSecondary
}).lean();
const fileBuffer = await getRawTextBuffer(url);
if (fileBuffer) {
return formatResponseObject({
filename: fileBuffer.metadata?.filename || url,
filename: fileBuffer.sourceName || url,
url,
content: fileBuffer.rawText
content: fileBuffer.text
});
}
@@ -220,17 +218,12 @@ export const getFileContentFromLinks = async ({
});
// Add to buffer
try {
if (buffer.length < 14 * 1024 * 1024 && rawText.trim()) {
MongoRawTextBuffer.create({
sourceId: url,
rawText,
metadata: {
filename: filename
}
});
}
} catch (error) {}
addRawTextBuffer({
sourceId: url,
sourceName: filename,
text: rawText,
expiredTime: addMinutes(new Date(), 20)
});
return formatResponseObject({ filename, url, content: rawText });
} catch (error) {

View File

@@ -10,7 +10,7 @@ import { AppPermission } from '@fastgpt/global/support/permission/app/controller
import { type PermissionValueType } from '@fastgpt/global/support/permission/type';
import { AppFolderTypeList } from '@fastgpt/global/core/app/constants';
import { type ParentIdType } from '@fastgpt/global/common/parentFolder/type';
import { splitCombinePluginId } from '../../../core/app/plugin/controller';
import { splitCombineToolId } from '../../../core/app/plugin/controller';
import { PluginSourceEnum } from '@fastgpt/global/core/plugin/constants';
import { type AuthModeType, type AuthResponseType } from '../type';
import { AppDefaultPermissionVal } from '@fastgpt/global/support/permission/app/constant';
@@ -24,7 +24,7 @@ export const authPluginByTmbId = async ({
appId: string;
per: PermissionValueType;
}) => {
const { source } = await splitCombinePluginId(appId);
const { source } = splitCombineToolId(appId);
if (source === PluginSourceEnum.personal) {
const { app } = await authAppByTmbId({
appId,
@@ -137,7 +137,7 @@ export const authApp = async ({
appId: ParentIdType;
per: PermissionValueType;
}): Promise<
AuthResponseType & {
AuthResponseType<AppPermission> & {
app: AppDetailType;
}
> => {

View File

@@ -0,0 +1,568 @@
import { MongoTeamGate } from './schema';
import { Types } from '../../../../common/mongo';
import type { ClientSession } from '../../../../common/mongo';
import { mongoSessionRun } from '../../../../common/mongo/sessionRun';
import {
GateFeaturedAppPermission,
GateQuickAppPermission
} from '@fastgpt/global/support/permission/app/constant';
import { PerResourceTypeEnum } from '@fastgpt/global/support/permission/constant';
import { DefaultGroupName } from '@fastgpt/global/support/user/team/group/constant';
import { MongoMemberGroupModel } from '../../../permission/memberGroup/memberGroupSchema';
import { MongoResourcePermission } from '../../../permission/schema';
export const addGatePermission = async ({
teamId,
appId,
per,
session
}: {
teamId: string;
appId: string;
per: number;
session?: ClientSession;
}) => {
// 1. 先找全员组
const teamGroup = await MongoMemberGroupModel.findOne({
teamId,
name: DefaultGroupName
});
if (!teamGroup) {
return Promise.reject('找不到全员组');
}
// 2. 加权限
await MongoResourcePermission.updateOne(
{
teamId,
groupId: teamGroup?._id,
resourceType: PerResourceTypeEnum.app,
resourceId: appId
},
{
permission: per
},
{
session,
upsert: true
}
);
};
export const removeGatePermission = async ({
teamId,
appId,
per,
session
}: {
teamId: string;
appId: string;
per: number;
session?: ClientSession;
}) => {
// 1. 先找全员组
const teamGroup = await MongoMemberGroupModel.findOne({
teamId,
name: DefaultGroupName
});
if (!teamGroup) {
return Promise.reject('找不到全员组');
}
await MongoResourcePermission.deleteOne(
{
teamId,
groupId: teamGroup?._id,
resourceType: PerResourceTypeEnum.app,
resourceId: appId,
permission: per
},
{
session
}
);
};
/**
* 创建团队门户配置
*/
export const createGateConfig = async ({ teamId }: { teamId: string }) => {
const gate = await MongoTeamGate.create({
teamId
});
return gate.toObject();
};
/**
* 获取团队门户配置
*/
export const getGateConfig = async (teamId: string) => {
const gate = await MongoTeamGate.findOne({ teamId }).lean();
return gate;
};
/**
* 更新团队门户配置
*/
export const updateGateConfig = async ({
teamId,
status,
name,
banner,
logo,
tools,
placeholderText
}: {
teamId: string;
status?: boolean;
name?: string;
banner?: string;
logo?: string;
tools?: string[];
placeholderText?: string;
}) => {
const updateData: Record<string, any> = {};
if (status !== undefined) updateData.status = status;
if (name !== undefined) updateData.name = name;
if (banner !== undefined) updateData.banner = banner;
if (logo !== undefined) updateData.logo = logo;
if (tools !== undefined) updateData.tools = tools;
if (placeholderText !== undefined) updateData.placeholderText = placeholderText;
// 使用 upsert 选项,如果不存在则创建
await MongoTeamGate.updateOne({ teamId }, { $set: updateData }, { upsert: true });
return MongoTeamGate.findOne({ teamId }).lean();
};
/**
* 删除团队门户配置
*/
export const deleteGateConfig = async (teamId: string) => {
await MongoTeamGate.deleteOne({ teamId });
return true;
};
/**
* 启用或禁用团队门户
*/
export const toggleGateStatus = async ({ teamId, status }: { teamId: string; status: boolean }) => {
await MongoTeamGate.updateOne({ teamId }, { $set: { status } }, { upsert: true });
return MongoTeamGate.findOne({ teamId }).lean();
};
/**
* 更新门户工具配置
*/
export const updateGateTools = async ({ teamId, tools }: { teamId: string; tools: string[] }) => {
await MongoTeamGate.updateOne({ teamId }, { $set: { tools } }, { upsert: true });
return MongoTeamGate.findOne({ teamId }).lean();
};
/**
* 添加门户工具
*/
export const addGateTool = async ({ teamId, tool }: { teamId: string; tool: string }) => {
await MongoTeamGate.updateOne({ teamId }, { $addToSet: { tools: tool } }, { upsert: true });
return MongoTeamGate.findOne({ teamId }).lean();
};
/**
* 移除门户工具
*/
export const removeGateTool = async ({ teamId, tool }: { teamId: string; tool: string }) => {
await MongoTeamGate.updateOne({ teamId }, { $pull: { tools: tool } });
return MongoTeamGate.findOne({ teamId }).lean();
};
/**
* 更新特色应用列表
*/
export const updateFeaturedApps = async ({
teamId,
featuredApps
}: {
teamId: string;
featuredApps: string[];
}) => {
// 将字符串数组转换为 ObjectId 数组
const objectIdArray = featuredApps.map((id) => new Types.ObjectId(id));
await MongoTeamGate.updateOne(
{ teamId },
{ $set: { featuredApps: objectIdArray } },
{ upsert: true }
);
return MongoTeamGate.findOne({ teamId }).lean();
};
/**
* 添加特色应用
*/
export const addFeaturedApp = async ({ teamId, appId }: { teamId: string; appId: string }) => {
await MongoTeamGate.updateOne(
{ teamId },
{ $addToSet: { featuredApps: new Types.ObjectId(appId) } },
{ upsert: true }
);
return MongoTeamGate.findOne({ teamId }).lean();
};
/**
* 删除特色应用
*/
export const removeFeaturedApp = async ({
teamId,
appId,
session
}: {
teamId: string;
appId: string;
session?: ClientSession;
}) => {
await MongoTeamGate.updateOne(
{ teamId },
{ $pull: { featuredApps: new Types.ObjectId(appId) } },
{ session }
);
return MongoTeamGate.findOne({ teamId }).lean();
};
/**
* 移动特色应用位置(原子操作)
* @param teamId 团队ID
* @param appId 要移动的应用ID
* @param toIndex 目标位置索引
*/
export const moveFeatureAppToPosition = async ({
teamId,
appId,
toIndex
}: {
teamId: string;
appId: string;
toIndex: number;
}) => {
const objectId = new Types.ObjectId(appId);
// 获取当前配置
const config = await MongoTeamGate.findOne({ teamId }).lean();
if (!config || !config.featuredApps) {
throw new Error('团队配置不存在');
}
const apps = [...config.featuredApps];
const currentIndex = apps.findIndex((id) => id.toString() === appId);
if (currentIndex === -1) {
throw new Error('应用不在特色应用列表中');
}
// 移动数组元素
const [movedApp] = apps.splice(currentIndex, 1);
apps.splice(toIndex, 0, movedApp);
// 一次性更新
await MongoTeamGate.updateOne({ teamId }, { $set: { featuredApps: apps } });
return MongoTeamGate.findOne({ teamId }).lean();
};
/**
* 更新工具排序
* @param teamId 团队ID
* @param orderedTools 按新顺序排列的工具数组
*/
export const reorderTools = async ({
teamId,
orderedTools
}: {
teamId: string;
orderedTools: string[];
}) => {
await MongoTeamGate.updateOne({ teamId }, { $set: { tools: orderedTools } });
return MongoTeamGate.findOne({ teamId }).lean();
};
/**
* 批量更新门户配置
*/
export const batchUpdateGateConfigs = async (
configs: {
teamId: string;
status?: boolean;
banner?: string;
logo?: string;
tools?: string[];
placeholderText?: string;
}[]
) => {
const operations = configs.map((config) => {
const { teamId, ...updateData } = config;
return {
updateOne: {
filter: { teamId },
update: { $set: updateData },
upsert: true
}
};
});
if (operations.length === 0) {
return true;
}
await MongoTeamGate.bulkWrite(operations);
return true;
};
/**
* 批量更新特色应用
*/
export const batchUpdateFeaturedApps = async (
updates: {
teamId: string;
featuredApps: string[];
}[]
) => {
const operations = updates.map((update) => {
const { teamId, featuredApps } = update;
// 将字符串数组转换为 ObjectId 数组
const objectIdArray = featuredApps.map((id) => new Types.ObjectId(id));
return {
updateOne: {
filter: { teamId },
update: { $set: { featuredApps: objectIdArray } },
upsert: true
}
};
});
if (operations.length === 0) {
return true;
}
const teamId = updates[0]?.teamId;
const gateConfig = await MongoTeamGate.findOne({ teamId });
if (!gateConfig) return Promise.reject('无 gate 配置');
const updatedAppId = updates[0].featuredApps;
const deleteAppId = gateConfig.featuredApps.filter((id) => !updatedAppId.includes(id));
await mongoSessionRun(async (session) => {
await MongoTeamGate.bulkWrite(operations, { session });
for (const id of deleteAppId) {
await removeGatePermission({
teamId,
appId: id,
per: GateFeaturedAppPermission,
session
});
}
for (const id of updatedAppId) {
await addGatePermission({
teamId,
appId: id,
per: GateFeaturedAppPermission,
session
});
}
});
return true;
};
/**
* 批量更新工具排序
*/
export const batchUpdateToolsOrder = async (
updates: {
teamId: string;
tools: string[];
}[]
) => {
const operations = updates.map((update) => {
const { teamId, tools } = update;
return {
updateOne: {
filter: { teamId },
update: { $set: { tools } },
upsert: true
}
};
});
if (operations.length === 0) {
return true;
}
await MongoTeamGate.bulkWrite(operations);
return true;
};
/**
* 批量删除特色应用
* @param teamId 团队ID
* @param appIds 要删除的应用ID数组
*/
export const batchDeleteFeaturedApps = async ({
teamId,
appIds,
session
}: {
teamId: string;
appIds: string[];
session?: ClientSession;
}) => {
if (!appIds || appIds.length === 0) {
return false;
}
await MongoTeamGate.updateOne(
{ teamId },
{ $pull: { featuredApps: { $in: appIds.map((id) => new Types.ObjectId(id)) } } },
{
session
}
);
return true;
};
/**
* 更新快速应用列表
*/
export const updateQuickApps = async ({
teamId,
quickApps
}: {
teamId: string;
quickApps: string[];
}) => {
await MongoTeamGate.updateOne({ teamId }, { $set: { quickApps } }, { upsert: true });
return MongoTeamGate.findOne({ teamId }).lean();
};
/**
* 添加快速应用
*/
export const addQuickApp = async ({ teamId, appId }: { teamId: string; appId: string }) => {
await MongoTeamGate.updateOne(
{ teamId },
{ $addToSet: { quickApps: new Types.ObjectId(appId) } },
{ upsert: true }
);
return MongoTeamGate.findOne({ teamId }).lean();
};
/**
* 删除快速应用
*/
export const removeQuickApp = async ({ teamId, appId }: { teamId: string; appId: string }) => {
await MongoTeamGate.updateOne({ teamId }, { $pull: { quickApps: new Types.ObjectId(appId) } });
return MongoTeamGate.findOne({ teamId }).lean();
};
/**
* 移动快速应用位置(原子操作)
* @param teamId 团队ID
* @param appId 要移动的应用ID
* @param toIndex 目标位置索引
*/
export const moveQuickAppToPosition = async ({
teamId,
appId,
toIndex
}: {
teamId: string;
appId: string;
toIndex: number;
}) => {
const objectId = new Types.ObjectId(appId);
// 获取当前配置
const config = await MongoTeamGate.findOne({ teamId }).lean();
if (!config || !config.quickApps) {
throw new Error('团队配置不存在');
}
const apps = [...config.quickApps];
const currentIndex = apps.findIndex((id) => id.toString() === appId);
if (currentIndex === -1) {
throw new Error('应用不在快速应用列表中');
}
// 移动数组元素
const [movedApp] = apps.splice(currentIndex, 1);
apps.splice(toIndex, 0, movedApp);
// 一次性更新
await MongoTeamGate.updateOne({ teamId }, { $set: { quickApps: apps } });
return MongoTeamGate.findOne({ teamId }).lean();
};
/**
* 批量更新快速应用
*/
export const batchUpdateQuickApps = async (teamId: string, quickApps: string[]) => {
const gateConfig = await MongoTeamGate.findOne({ teamId });
if (!gateConfig) {
return false;
}
// 计算删除的appId
const deleteAppIds = gateConfig.quickApps.filter((id) => !quickApps.includes(id.toString()));
return mongoSessionRun(async (session) => {
// 1. 删除权限
for (const id of deleteAppIds) {
await removeGatePermission({
teamId,
appId: id,
per: GateQuickAppPermission,
session
});
}
// 加权限
for (const id of quickApps) {
await addGatePermission({
teamId,
appId: id,
per: GateQuickAppPermission,
session
});
}
gateConfig.quickApps = quickApps;
await gateConfig.save({ session });
return true;
});
};
/**
* 批量删除快速应用
* @param teamId 团队ID
* @param appIds 要删除的应用ID数组
*/
export const batchDeleteQuickApps = async ({
teamId,
appIds
}: {
teamId: string;
appIds: string[];
}) => {
if (!appIds || appIds.length === 0) {
return false;
}
await MongoTeamGate.updateOne(
{ teamId },
{ $pull: { quickApps: { $in: appIds.map((id) => new Types.ObjectId(id)) } } }
);
return true;
};

View File

@@ -0,0 +1,45 @@
import { TeamCollectionName } from '@fastgpt/global/support/user/team/constant';
import { Schema, getMongoModel } from '../../../../common/mongo';
import type { GateSchemaType } from '@fastgpt/global/support/user/team/gate/type';
export const gateCollectionName = 'team_gate_config';
const GateConfigSchema = new Schema({
teamId: {
type: Schema.Types.ObjectId,
ref: TeamCollectionName
},
status: {
type: Boolean,
default: true
},
name: {
type: String
},
banner: {
type: String
},
logo: {
type: String
},
tools: {
type: [String]
},
placeholderText: {
type: String
},
featuredApps: [
{
type: Schema.Types.ObjectId,
ref: 'apps'
}
],
quickApps: [
{
type: Schema.Types.ObjectId,
ref: 'apps'
}
]
});
export const MongoTeamGate = getMongoModel<GateSchemaType>(gateCollectionName, GateConfigSchema);

View File

@@ -17,6 +17,11 @@ const TeamSchema = new Schema({
type: String,
default: '/icon/logo.svg'
},
// todo :banner
banner: {
type: String,
default: '/icon/banner.svg'
},
createTime: {
type: Date,
default: () => Date.now()

View File

@@ -287,6 +287,7 @@ export const iconPaths = {
'core/workflow/template/aiChat': () => import('./icons/core/workflow/template/aiChat.svg'),
'core/workflow/template/baseChart': () => import('./icons/core/workflow/template/baseChart.svg'),
'core/workflow/template/bing': () => import('./icons/core/workflow/template/bing.svg'),
'core/workflow/template/bocha': () => import('./icons/core/workflow/template/bocha.svg'),
'core/workflow/template/codeRun': () => import('./icons/core/workflow/template/codeRun.svg'),
'core/workflow/template/customFeedback': () =>
import('./icons/core/workflow/template/customFeedback.svg'),
@@ -473,6 +474,31 @@ export const iconPaths = {
'support/user/userLightSmall': () => import('./icons/support/user/userLightSmall.svg'),
'support/user/usersFill': () => import('./icons/support/user/usersFill.svg'),
'support/user/usersLight': () => import('./icons/support/user/usersLight.svg'),
'support/gate/gateLight': () => import('./icons/support/gate/gateLight.svg'),
'support/gate/chat/sidebar/chatGray': () =>
import('./icons/support/gate/chat/sidebar/chatGray.svg'),
'support/gate/chat/historySlider/new_chat': () =>
import('./icons/support/gate/chat/historySlider/new_chat.svg'),
'support/gate/chat/historySlider/clear-all': () =>
import('./icons/support/gate/chat/historySlider/clear-all.svg'),
'support/gate/chat/historySlider/chevron-right2': () =>
import('./icons/support/gate/chat/historySlider/chevron-right2.svg'),
'support/gate/chat/toolkitLine': () => import('./icons/support/gate/chat/toolkitLine.svg'),
'support/gate/chat/historySlider/chevron-left2': () =>
import('./icons/support/gate/chat/historySlider/chevron-left2.svg'),
'support/gate/chat/sidebar/appGray': () =>
import('./icons/support/gate/chat/sidebar/appGray.svg'),
'support/gate/chat/voiceGray': () => import('./icons/support/gate/chat/voiceGray.svg'),
'support/gate/chat/fileGray': () => import('./icons/support/gate/chat/fileGray.svg'),
'support/gate/chat/paperclip': () => import('./icons/support/gate/chat/paperclip.svg'),
'support/gate/chat/imageGray': () => import('./icons/support/gate/chat/imageGray.svg'),
'support/gate/chat/sidebar/CollapseButton': () =>
import('./icons/support/gate/chat/sidebar/CollapseButton.svg'),
'support/gate/home/savePrimary': () => import('./icons/support/gate/home/savePrimary.svg'),
'support/gate/home/shareLight': () => import('./icons/support/gate/home/shareLight.svg'),
'support/gate/home/sharePrimary': () => import('./icons/support/gate/home/sharePrimary.svg'),
'support/gate/home/upload': () => import('./icons/support/gate/home/upload.svg'),
'support/gate/home/add': () => import('./icons/support/gate/home/add.svg'),
text: () => import('./icons/text.svg'),
union: () => import('./icons/union.svg'),
user: () => import('./icons/user.svg'),

View File

@@ -0,0 +1,5 @@
<svg width="113" height="97" viewBox="0 0 113 97" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0 31.7259C1.80046 29.9255 3.82784 28.3872 5.96621 27.1988C8.10469 26.0103 10.3126 25.1947 12.4634 24.7992C14.6143 24.4037 16.6664 24.4361 18.5022 24.8938C20.2678 25.334 21.7994 26.1604 23.0183 27.3272L23.021 27.3245L47.189 51.4924L33.4778 65.2037L0 31.7259Z" fill="#C4DEFE"/>
<path d="M9.15662 11.5625C11.3617 10.2893 13.7181 9.32825 16.0912 8.73374C18.4645 8.13923 20.8082 7.92284 22.9882 8.09751C25.1681 8.27217 27.1419 8.83457 28.7966 9.75182C30.3881 10.6341 31.6537 11.8287 32.529 13.2712L32.5316 13.2697L32.6082 13.4025C32.6162 13.4162 32.6251 13.4297 32.633 13.4435L49.886 43.3286L33.0941 53.0234L9.15662 11.5625Z" fill="#A6CBFF"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M31.1377 0C33.6839 4.40811e-05 36.2052 0.345872 38.5576 1.01758C40.9099 1.68929 43.0472 2.67394 44.8477 3.91504C46.6482 5.15627 48.0773 6.63021 49.0518 8.25195C49.9888 9.81168 50.4867 11.4792 50.5234 13.166H50.5273V21.4072C56.6623 17.6586 63.874 15.498 71.5898 15.498C93.9304 15.4984 112.042 33.6087 112.042 55.9492C112.042 78.29 93.9305 96.401 71.5898 96.4014C49.3907 96.4014 31.3704 78.5193 31.1426 56.374H31.1377V0ZM71.9473 35.0439C60.1187 35.0441 50.5295 44.6334 50.5293 56.4619C50.5293 63.5338 53.9569 69.8057 59.2412 73.7061C66.4989 79.0625 76.5515 75.3841 85.3955 77.1592C92.613 78.608 97.2369 82.6827 98.3652 83.7686C97.3562 82.731 93.791 78.7138 92.2715 72.3291C89.8011 61.9479 94.8744 49.6043 87.5771 41.8184C83.6695 37.6493 78.1122 35.0441 71.9473 35.0439Z" fill="#006EFF"/>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -0,0 +1,3 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.6665 3.0263C10.5079 3.01307 10.2763 3.01095 9.84294 3.01095H7.16646C6.45348 3.01095 5.96581 3.01152 5.58819 3.04048C5.21887 3.06881 5.02616 3.12054 4.89105 3.18516C4.57275 3.3374 4.32387 3.57549 4.17134 3.85649C4.11194 3.96593 4.05991 4.12789 4.03072 4.4633C4.00053 4.81012 3.9998 5.26058 3.9998 5.93236V14.0677C3.9998 14.7394 4.00053 15.1899 4.03072 15.5367C4.05991 15.8721 4.11194 16.0341 4.17134 16.1435C4.32387 16.4245 4.57275 16.6626 4.89105 16.8149C5.02616 16.8795 5.21887 16.9312 5.58819 16.9595C5.96581 16.9885 6.45348 16.9891 7.16646 16.9891H12.4998C13.2128 16.9891 13.7005 16.9885 14.0781 16.9595C14.4474 16.9312 14.6401 16.8795 14.7752 16.8149C15.0935 16.6626 15.3424 16.4245 15.4949 16.1435C15.5543 16.0341 15.6063 15.8721 15.6355 15.5367C15.6657 15.1899 15.6665 14.7394 15.6665 14.0677V8.42632C15.6665 8.04949 15.6642 7.84193 15.654 7.70438L12.8076 7.70439C12.5963 7.70441 12.3934 7.70443 12.2221 7.69129C12.0356 7.67698 11.8167 7.64346 11.5952 7.53756C11.2863 7.38982 11.0253 7.14923 10.8582 6.84149C10.736 6.61626 10.6972 6.39184 10.681 6.2055C10.6664 6.03826 10.6664 5.84201 10.6665 5.64647C10.6665 5.63747 10.6665 5.62847 10.6665 5.61947V3.0263ZM11.8068 1.61342C11.6211 1.53551 11.4284 1.47397 11.2311 1.42951C10.8511 1.34388 10.4571 1.34404 9.92517 1.34426C9.89812 1.34427 9.87072 1.34428 9.84294 1.34428L7.13385 1.34428C6.4615 1.34427 5.90966 1.34427 5.46075 1.37869C4.99631 1.41431 4.57159 1.49047 4.17193 1.68162C3.54942 1.97936 3.03339 2.45928 2.70655 3.0614C2.49347 3.45396 2.40926 3.87156 2.37033 4.31878C2.33311 4.74646 2.33312 5.26987 2.33313 5.89642V14.1036C2.33312 14.7301 2.33311 15.2536 2.37033 15.6812C2.40926 16.1285 2.49347 16.5461 2.70655 16.9386C3.03339 17.5407 3.54942 18.0207 4.17193 18.3184C4.57159 18.5096 4.99631 18.5857 5.46075 18.6213C5.90965 18.6558 6.46147 18.6557 7.13382 18.6557H12.5324C13.2048 18.6557 13.7566 18.6558 14.2055 18.6213C14.67 18.5857 15.0947 18.5096 15.4943 18.3184C16.1168 18.0207 16.6329 17.5407 16.9597 16.9386C17.1728 16.5461 17.257 16.1285 17.2959 15.6812C17.3332 15.2536 17.3331 14.7301 17.3331 14.1036V8.42632C17.3331 8.39763 17.3331 8.3693 17.3332 8.3413C17.3335 7.85013 17.3337 7.46273 17.2381 7.08878C17.1885 6.89509 17.1202 6.70705 17.0344 6.52696C17.0287 6.51433 17.0227 6.50187 17.0163 6.4896C16.9608 6.37678 16.8983 6.26719 16.8292 6.16139C16.6188 5.83909 16.3328 5.57093 15.9611 5.22237C15.9405 5.20307 15.9197 5.18353 15.8986 5.16372L13.2417 2.66977C13.2216 2.65089 13.2018 2.63225 13.1822 2.61385C12.8076 2.26189 12.5241 1.99554 12.186 1.80108C12.0761 1.73786 11.9628 1.68091 11.8466 1.63043C11.8335 1.62442 11.8202 1.61875 11.8068 1.61342ZM12.3331 4.10281V5.61947C12.3331 5.82534 12.3337 5.94445 12.339 6.02864C12.3423 6.02893 12.3458 6.02922 12.3495 6.0295C12.4492 6.03715 12.5869 6.03772 12.8331 6.03772H14.3944L12.3331 4.10281Z" fill="#707070"/>
</svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@@ -0,0 +1,7 @@
<svg width="16" height="20" viewBox="0 0 16 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="icon/solid/chevron-right2">
<path id="Rectangle 3101"
d="M11.0474 14.2501C11.0474 15.735 9.25219 16.4786 8.20225 15.4286L3.95213 11.1785C3.30126 10.5276 3.30126 9.47236 3.95213 8.82149L8.20225 4.57138C9.25219 3.52143 11.0474 4.26505 11.0474 5.74989L11.0474 14.2501Z"
fill="currentColor" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 444 B

View File

@@ -0,0 +1,7 @@
<svg width="16" height="20" viewBox="0 0 16 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="icon/solid/chevron-right2">
<path id="Rectangle 3101"
d="M4.95255 5.74989C4.95255 4.26505 6.74778 3.52143 7.79772 4.57138L12.0478 8.82149C12.6987 9.47236 12.6987 10.5276 12.0478 11.1785L7.79772 15.4286C6.74778 16.4786 4.95255 15.735 4.95255 14.2501V5.74989Z"
fill="currentColor" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 436 B

View File

@@ -0,0 +1,7 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="icon/line/clear-all">
<path id="Union" fill-rule="evenodd" clip-rule="evenodd"
d="M7.33331 2.63809C7.33331 2.49081 7.4527 2.37142 7.59997 2.37142H8.39997C8.54725 2.37142 8.66664 2.49081 8.66664 2.63809V4.2C8.66664 4.9732 9.29344 5.6 10.0666 5.6L12.861 5.6C13.0083 5.6 13.1277 5.71939 13.1277 5.86666V6.66666C13.1277 6.81394 13.0083 6.93333 12.861 6.93333L3.13891 6.93333C2.99164 6.93333 2.87224 6.81394 2.87224 6.66666V5.86666C2.87224 5.71939 2.99164 5.6 3.13891 5.6L5.93331 5.6C6.70651 5.6 7.33331 4.97319 7.33331 4.2V2.63809ZM7.59997 1.03809C6.71632 1.03809 5.99997 1.75443 5.99997 2.63809V4.2C5.99997 4.23681 5.97012 4.26666 5.93331 4.26666L3.13891 4.26666C2.25526 4.26666 1.53891 4.98301 1.53891 5.86666V6.66666C1.53891 7.41125 2.04753 8.03705 2.73629 8.21558L1.47841 12.628C1.18709 13.6499 1.9545 14.6667 3.01711 14.6667H13.0318C14.0831 14.6667 14.8487 13.6701 14.5778 12.6543L13.384 8.17923C14.0109 7.96253 14.461 7.36717 14.461 6.66666V5.86666C14.461 4.98301 13.7447 4.26666 12.861 4.26666L10.0666 4.26666C10.0298 4.26666 9.99997 4.23681 9.99997 4.2V2.63809C9.99997 1.75443 9.28363 1.03809 8.39997 1.03809H7.59997ZM4.30936 8.26696H11.8226C11.9434 8.26696 12.0491 8.34817 12.0803 8.46489L13.2895 12.9979C13.3347 13.1672 13.207 13.3333 13.0318 13.3333H10.6269C10.627 13.3291 10.6271 13.3249 10.6271 13.3206L10.6409 11.4144C10.6436 11.0462 10.3472 10.7456 9.97907 10.7429C9.61089 10.7403 9.31026 11.0366 9.30759 11.4047L9.2938 13.311C9.29374 13.3184 9.29381 13.3259 9.294 13.3333H6.87303L6.87324 13.3206L6.88704 11.4144C6.88971 11.0462 6.5934 10.7456 6.22522 10.7429C5.85704 10.7403 5.55641 11.0366 5.55374 11.4047L5.53995 13.311C5.53989 13.3184 5.53996 13.3259 5.54015 13.3333H3.01711C2.84 13.3333 2.71211 13.1639 2.76066 12.9936L4.05291 8.46052C4.08557 8.34596 4.19024 8.26696 4.30936 8.26696Z"
fill="currentColor" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@@ -0,0 +1,8 @@
<svg width="19" height="18" viewBox="0 0 19 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M11.3834 2.86546C11.3834 3.27967 11.0476 3.61546 10.6334 3.61546H6.05212C4.39495 3.61546 3.05139 4.95892 3.05139 6.61546V10.3215C3.05158 11.5943 3.84602 12.6857 4.9696 13.1201L5.19885 13.1977L5.54646 13.3005C5.76638 13.3656 5.94445 13.5277 6.02982 13.7406L6.16492 14.0774C6.29685 14.4057 6.68973 14.54 6.99503 14.3611L8.59462 13.4243C8.70956 13.357 8.84034 13.3215 8.97353 13.3215H12.8014C14.4065 13.3215 15.7178 12.0607 15.7985 10.4761L15.8021 10.3215V7.97703C15.8021 7.56281 16.1379 7.22703 16.5521 7.22703C16.9663 7.22703 17.3021 7.56281 17.3021 7.97703V10.3215C17.3019 12.8066 15.2865 14.8215 12.8014 14.8215H9.38096C9.24764 14.8215 9.11674 14.857 9.00172 14.9245L6.57434 16.3471C6.10009 16.625 5.48906 16.4168 5.28381 15.907L4.90857 14.9729C4.82314 14.7602 4.64369 14.6019 4.42992 14.5193C2.74626 13.8685 1.55158 12.2347 1.55139 10.3215V6.61546C1.55139 4.13017 3.56684 2.11546 6.05212 2.11546H10.6334C11.0476 2.11546 11.3834 2.45124 11.3834 2.86546Z"
fill="#3370FF" />
<path
d="M14.9027 1.52901C15.2541 1.52912 15.5392 1.81403 15.5392 2.16549V3.43844H16.8121C17.1636 3.43851 17.4486 3.72344 17.4486 4.07491C17.4485 4.42632 17.1635 4.71132 16.8121 4.71139H15.5392V5.98434C15.5391 6.33571 15.2541 6.62071 14.9027 6.62081C14.5513 6.62081 14.2663 6.33577 14.2662 5.98434V4.71139H12.9933C12.6418 4.71139 12.3569 4.42637 12.3568 4.07491C12.3568 3.7234 12.6418 3.43844 12.9933 3.43844H14.2662V2.16549C14.2662 1.81397 14.5512 1.52901 14.9027 1.52901Z"
fill="#3370FF" />
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -0,0 +1,4 @@
<svg width="18" height="16" viewBox="0 0 18 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5.61372 6.52442C6.30407 6.52442 6.86372 5.96478 6.86372 5.27442C6.86372 4.58407 6.30407 4.02442 5.61372 4.02442C4.92336 4.02442 4.36371 4.58407 4.36371 5.27442C4.36371 5.96478 4.92336 6.52442 5.61372 6.52442Z" fill="#707070"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M0.666656 5.31489C0.666656 3.63474 0.666656 2.79466 0.993637 2.15292C1.28126 1.58843 1.7402 1.12949 2.30468 0.841873C2.94642 0.514893 3.7865 0.514893 5.46666 0.514893H12.5333C14.2135 0.514893 15.0536 0.514893 15.6953 0.841873C16.2598 1.12949 16.7187 1.58843 17.0063 2.15292C17.3333 2.79466 17.3333 3.63473 17.3333 5.31489V10.6852C17.3333 12.3654 17.3333 13.2054 17.0063 13.8472C16.7187 14.4117 16.2598 14.8706 15.6953 15.1582C15.0536 15.4852 14.2135 15.4852 12.5333 15.4852H5.46666C3.7865 15.4852 2.94642 15.4852 2.30468 15.1582C1.7402 14.8706 1.28126 14.4117 0.993637 13.8472C0.666656 13.2054 0.666656 12.3654 0.666656 10.6852V5.31489ZM5.46666 2.18156H12.5333C13.4009 2.18156 13.9514 2.18286 14.368 2.2169C14.7652 2.24935 14.8919 2.30306 14.9386 2.32688C15.1895 2.45472 15.3935 2.65869 15.5213 2.90957C15.5452 2.95633 15.5989 3.08303 15.6313 3.48022C15.6654 3.89686 15.6667 4.44731 15.6667 5.31489V10.6852C15.6667 10.6952 15.6667 10.7052 15.6667 10.7152L11.6244 6.67291C11.2989 6.34747 10.7713 6.34747 10.4459 6.67291L3.36552 13.7533C3.17216 13.7239 3.09546 13.6906 3.06134 13.6732C2.81045 13.5454 2.60648 13.3414 2.47865 13.0905C2.45483 13.0438 2.40111 12.9171 2.36866 12.5199C2.33462 12.1032 2.33332 11.5528 2.33332 10.6852V5.31489C2.33332 4.44731 2.33462 3.89686 2.36866 3.48022C2.40111 3.08303 2.45483 2.95633 2.47865 2.90957C2.60648 2.65869 2.81045 2.45472 3.06134 2.32688C3.10809 2.30306 3.23479 2.24935 3.63198 2.2169C4.04863 2.18286 4.59908 2.18156 5.46666 2.18156ZM11.0351 8.44068L5.65725 13.8185H12.5333C13.4009 13.8185 13.9514 13.8172 14.368 13.7832C14.7652 13.7508 14.8919 13.697 14.9386 13.6732C15.1895 13.5454 15.3935 13.3414 15.5213 13.0905C15.5316 13.0704 15.5474 13.0354 15.5647 12.9703L11.0351 8.44068Z" fill="#707070"/>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@@ -0,0 +1,7 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="icon/line/paperclip">
<path id="Icon (Stroke)" fill-rule="evenodd" clip-rule="evenodd"
d="M13.001 3.12035C12.4123 3.12035 11.8477 3.3542 11.4315 3.77046L4.63705 10.5649C3.94333 11.2586 3.55361 12.1995 3.55361 13.1805C3.55361 14.1616 3.94333 15.1025 4.63705 15.7962C5.33077 16.4899 6.27165 16.8796 7.25271 16.8796C8.23377 16.8796 9.17465 16.4899 9.86837 15.7962L16.6628 9.00179C16.9515 8.71306 17.4196 8.71306 17.7084 9.00179C17.9971 9.29051 17.9971 9.75863 17.7084 10.0474L10.9139 16.8418C9.94292 17.8128 8.62594 18.3583 7.25271 18.3583C5.87948 18.3583 4.5625 17.8128 3.59148 16.8418C2.62046 15.8708 2.07495 14.5538 2.07495 13.1805C2.07495 11.8073 2.62046 10.4903 3.59148 9.51931L10.3859 2.72489C11.0795 2.03133 12.0201 1.64169 13.001 1.64169C13.9818 1.64169 14.9225 2.03133 15.6161 2.72489C16.3096 3.41846 16.6993 4.35913 16.6993 5.33997C16.6993 6.32082 16.3096 7.26149 15.6161 7.95506L8.81425 14.7495C8.39814 15.1656 7.83378 15.3993 7.24532 15.3993C6.65685 15.3993 6.09249 15.1656 5.67638 14.7495C5.26028 14.3334 5.02651 13.769 5.02651 13.1805C5.02651 12.5921 5.26028 12.0277 5.67638 11.6116L11.9536 5.34181C12.2425 5.05325 12.7106 5.05353 12.9991 5.34242C13.2877 5.63132 13.2874 6.09943 12.9985 6.38799L6.72195 12.6572C6.58334 12.796 6.50517 12.9844 6.50517 13.1805C6.50517 13.3768 6.58315 13.5651 6.72195 13.7039C6.86076 13.8427 7.04902 13.9207 7.24532 13.9207C7.44162 13.9207 7.62988 13.8427 7.76868 13.7039L14.5705 6.90949C14.9866 6.49326 15.2206 5.92852 15.2206 5.33997C15.2206 4.75129 14.9868 4.18672 14.5705 3.77046C14.1542 3.3542 13.5897 3.12035 13.001 3.12035Z"
fill="currentColor" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@@ -0,0 +1,8 @@
<svg width="18" height="16" viewBox="0 0 18 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M9.91227 5.23185C10.2356 4.95244 10.724 4.98763 11.0036 5.31079C11.2829 5.63414 11.2479 6.12338 10.9246 6.40291L9.16113 7.92797L10.8774 9.51977C11.192 9.81221 11.2103 10.305 10.9181 10.62C10.6259 10.9347 10.1339 10.9531 9.81868 10.6615L7.54085 8.54972C7.37406 8.39504 7.29205 8.18359 7.29346 7.97273C7.2559 7.71942 7.34339 7.45278 7.55143 7.27286L9.91227 5.23185Z"
fill="currentcolor" />
<path fill-rule="evenodd" clip-rule="evenodd"
d="M13.0072 0.108149C15.6489 0.242083 17.7498 2.42669 17.75 5.10164V10.8984L17.7435 11.1555C17.614 13.7122 15.5638 15.7622 13.0072 15.8918L12.75 15.8984H5.25L4.99284 15.8918C2.43607 15.7623 0.385993 13.7123 0.25651 11.1555L0.25 10.8984V5.10164C0.250186 2.42663 2.35104 0.24199 4.99284 0.108149L5.25 0.101639H12.75L13.0072 0.108149ZM5.92546 14.2317H12.75C14.5909 14.2316 16.0833 12.7392 16.0833 10.8984V5.10164C16.0831 3.26092 14.5907 1.76842 12.75 1.76831H5.92546V14.2317ZM4.39225 1.8798C3.02015 2.24419 1.99539 3.4618 1.92074 4.92993L1.91667 5.10164V10.8984C1.91667 12.4427 2.96708 13.7408 4.39225 14.1194V1.8798Z"
fill="currentcolor" />
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -0,0 +1,14 @@
<svg width="19" height="18" viewBox="0 0 19 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd"
d="M0.10022 1.2C0.10022 0.537258 0.637478 0 1.30022 0H6.90022C7.56296 0 8.10022 0.537258 8.10022 1.2V6.8C8.10022 7.46274 7.56296 8 6.90022 8H1.30022C0.637478 8 0.10022 7.46274 0.10022 6.8V1.2ZM2.10022 6V2H6.10022V6H2.10022Z"
fill="#8A95A7" />
<path fill-rule="evenodd" clip-rule="evenodd"
d="M10.1002 1.2C10.1002 0.537258 10.6375 0 11.3002 0H16.9002C17.563 0 18.1002 0.537258 18.1002 1.2V6.8C18.1002 7.46274 17.563 8 16.9002 8H11.3002C10.6375 8 10.1002 7.46274 10.1002 6.8V1.2ZM12.1002 6V2H16.1002V6H12.1002Z"
fill="#8A95A7" />
<path fill-rule="evenodd" clip-rule="evenodd"
d="M11.3002 10C10.6375 10 10.1002 10.5373 10.1002 11.2V16.8C10.1002 17.4627 10.6375 18 11.3002 18H16.9002C17.563 18 18.1002 17.4627 18.1002 16.8V11.2C18.1002 10.5373 17.563 10 16.9002 10H11.3002ZM12.1002 12V16H16.1002V12H12.1002Z"
fill="#8A95A7" />
<path fill-rule="evenodd" clip-rule="evenodd"
d="M0.10022 11.2C0.10022 10.5373 0.637478 10 1.30022 10H6.90022C7.56296 10 8.10022 10.5373 8.10022 11.2V16.8C8.10022 17.4627 7.56296 18 6.90022 18H1.30022C0.637478 18 0.10022 17.4627 0.10022 16.8V11.2ZM2.10022 16V12H6.10022V16H2.10022Z"
fill="#8A95A7" />
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -0,0 +1,18 @@
<svg width="20" height="21" viewBox="0 0 20 21" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="icon/line/chat">
<g id="Union">
<path
d="M6.18697 8.52243C6.77251 8.52244 7.24715 8.99732 7.24735 9.58281C7.24735 10.1685 6.77263 10.6432 6.18697 10.6432C5.60129 10.6432 5.12659 10.1685 5.12659 9.58281C5.12679 8.99731 5.60142 8.52243 6.18697 8.52243Z"
fill="currentColor" />
<path
d="M10.0004 8.52243C10.586 8.52244 11.0606 8.99732 11.0608 9.58281C11.0608 10.1685 10.5861 10.6432 10.0004 10.6432C9.41477 10.6432 8.94006 10.1685 8.94006 9.58281C8.94027 8.99731 9.4149 8.52243 10.0004 8.52243Z"
fill="currentColor" />
<path
d="M13.8139 8.52243C14.3994 8.52249 14.8741 8.99735 14.8743 9.58281C14.8743 10.1684 14.3995 10.6431 13.8139 10.6432C13.2282 10.6432 12.7535 10.1685 12.7535 9.58281C12.7537 8.99731 13.2284 8.52243 13.8139 8.52243Z"
fill="currentColor" />
<path fill-rule="evenodd" clip-rule="evenodd"
d="M13.7504 2.5247C16.5117 2.52492 18.7504 4.76342 18.7504 7.5247V11.6425C18.7502 14.4037 16.5109 16.6425 13.7496 16.6425H9.94918C9.80105 16.6425 9.65542 16.6824 9.52763 16.7573L6.83069 18.3377C6.30374 18.6465 5.62482 18.4151 5.39677 17.8486L4.9801 16.811C4.88518 16.5747 4.6854 16.3983 4.44788 16.3064C2.57725 15.5833 1.24985 13.7682 1.24963 11.6425V7.5247C1.24963 4.76328 3.48902 2.5247 6.25045 2.5247H13.7504ZM6.25045 4.19137C4.40914 4.19137 2.9163 5.68411 2.9163 7.5247V11.6425C2.91651 13.0567 3.79923 14.2694 5.04765 14.7521L5.30237 14.8383L5.68892 14.9523C5.93318 15.0246 6.1312 15.2049 6.22603 15.4414L6.37577 15.8157C6.52236 16.1805 6.9586 16.3293 7.29781 16.1307L9.07515 15.0898C9.20286 15.015 9.34872 14.9759 9.4967 14.9759H13.7496C15.5331 14.9759 16.9901 13.5749 17.0797 11.8143L17.0838 11.6425V7.5247C17.0838 5.74165 15.6833 4.28501 13.9222 4.19544L13.7504 4.19137H6.25045Z"
fill="currentColor" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18" fill="none">
<path fill-rule="evenodd" clip-rule="evenodd"
d="M12.81 4.4538H12.8072C14.1971 4.45385 14.8993 4.45754 15.4374 4.73173C15.9173 4.97621 16.3074 5.36631 16.5518 5.84612C16.8298 6.3916 16.8298 7.10566 16.8298 8.5338V12.42C16.8298 13.8481 16.8298 14.5622 16.5518 15.1077C16.3074 15.5875 15.9173 15.9776 15.4374 16.2221C14.892 16.5 14.1779 16.5 12.7498 16.5H5.24992C3.82179 16.5 3.10772 16.5 2.56225 16.2221C2.08243 15.9776 1.69233 15.5875 1.44786 15.1077C1.16992 14.5622 1.16992 13.8481 1.16992 12.42V8.5338C1.16992 7.10566 1.16992 6.3916 1.44786 5.84612C1.69233 5.36631 2.08243 4.97621 2.56225 4.73173C3.10772 4.4538 3.82179 4.4538 5.24992 4.4538H5.36574V4.19994C5.36574 2.70877 6.57457 1.49994 8.06574 1.49994H10.11C11.6012 1.49994 12.81 2.70877 12.81 4.19994V4.4538ZM8.06574 2.99994H10.11C10.7727 2.99994 11.31 3.5372 11.31 4.19994V4.4538H6.86574V4.19994C6.86574 3.5372 7.403 2.99994 8.06574 2.99994ZM12.7498 5.9538H5.24992C4.5111 5.9538 4.0472 5.95496 3.69724 5.98356C3.36478 6.01072 3.26927 6.05497 3.24323 6.06824C3.04566 6.16891 2.88503 6.32954 2.78437 6.52711C2.7711 6.55314 2.72684 6.64865 2.69968 6.98111C2.68387 7.17461 2.67645 7.40293 2.67297 7.6933H15.3267C15.3232 7.40293 15.3158 7.17461 15.3 6.98111C15.2728 6.64865 15.2286 6.55314 15.2153 6.52711C15.1147 6.32954 14.954 6.16891 14.7565 6.06824C14.7304 6.05497 14.6349 6.01072 14.3025 5.98356C13.9525 5.95496 13.4886 5.9538 12.7498 5.9538ZM15.3298 9.0433H10.9765V11.392C10.9765 11.6405 10.775 11.842 10.5265 11.842H7.47313C7.2246 11.842 7.02313 11.6405 7.02313 11.392V9.0433H2.66992V12.42C2.66992 13.1588 2.67109 13.6227 2.69968 13.9727C2.72684 14.3052 2.7711 14.4007 2.78437 14.4267C2.88503 14.6243 3.04566 14.7849 3.24323 14.8856C3.26927 14.8988 3.36478 14.9431 3.69723 14.9702C4.0472 14.9988 4.5111 15 5.24992 15H12.7498C13.4886 15 13.9525 14.9988 14.3025 14.9702C14.6349 14.9431 14.7304 14.8988 14.7565 14.8856C14.954 14.7849 15.1147 14.6243 15.2153 14.4267C15.2286 14.4007 15.2728 14.3052 15.3 13.9727C15.3286 13.6227 15.3298 13.1588 15.3298 12.42V9.0433ZM9.62651 9.0433H8.37313V10.492H9.62651V9.0433Z"
fill="currentColor" />
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@@ -0,0 +1,5 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd"
d="M7.91635 2.02287C8.46897 1.47025 9.21848 1.15979 10 1.15979C10.7815 1.15979 11.531 1.47025 12.0837 2.02287C12.6363 2.57549 12.9467 3.325 12.9467 4.10652V9.99999C12.9467 10.7815 12.6363 11.531 12.0837 12.0836C11.531 12.6363 10.7815 12.9467 10 12.9467C9.21848 12.9467 8.46897 12.6363 7.91635 12.0836C7.36373 11.531 7.05327 10.7815 7.05327 9.99999V4.10652C7.05327 3.325 7.36373 2.57549 7.91635 2.02287ZM10 2.63316C9.60924 2.63316 9.23448 2.78839 8.95817 3.06469C8.68186 3.341 8.52663 3.71576 8.52663 4.10652V9.99999C8.52663 10.3907 8.68186 10.7655 8.95817 11.0418C9.23448 11.3181 9.60924 11.4734 10 11.4734C10.3908 11.4734 10.7655 11.3181 11.0418 11.0418C11.3181 10.7655 11.4734 10.3907 11.4734 9.99999V4.10652C11.4734 3.71576 11.3181 3.341 11.0418 3.06469C10.7655 2.78839 10.3908 2.63316 10 2.63316ZM4.84322 7.78994C5.25008 7.78994 5.5799 8.11976 5.5799 8.52662V9.99999C5.5799 11.1723 6.04559 12.2965 6.87452 13.1255C7.70345 13.9544 8.82772 14.4201 10 14.4201C11.1723 14.4201 12.2966 13.9544 13.1255 13.1255C13.9544 12.2965 14.4201 11.1723 14.4201 9.99999V8.52662C14.4201 8.11976 14.7499 7.78994 15.1568 7.78994C15.5636 7.78994 15.8935 8.11976 15.8935 8.52662V9.99999C15.8935 11.563 15.2725 13.0621 14.1673 14.1673C13.2372 15.0974 12.0281 15.6846 10.7367 15.8472V17.3668H12.9467C13.3536 17.3668 13.6834 17.6966 13.6834 18.1035C13.6834 18.5104 13.3536 18.8402 12.9467 18.8402H7.05327C6.64641 18.8402 6.31659 18.5104 6.31659 18.1035C6.31659 17.6966 6.64641 17.3668 7.05327 17.3668H9.26332V15.8472C7.97191 15.6846 6.76285 15.0974 5.83269 14.1673C4.72745 13.0621 4.10654 11.563 4.10654 9.99999V8.52662C4.10654 8.11976 4.43636 7.78994 4.84322 7.78994Z"
fill="currentColor" />
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
<!-- 保持原路径不变 -->
<path fill-rule="evenodd" clip-rule="evenodd"
d="M9.57021 1.39896C9.85172 1.32274 10.1484 1.32274 10.4299 1.39896C10.757 1.48751 11.0321 1.70357 11.2481 1.87322C11.2689 1.88955 11.2891 1.90544 11.3088 1.92075L16.8123 6.20128C16.8337 6.21793 16.855 6.23443 16.8761 6.25081C17.1809 6.48733 17.4525 6.6981 17.655 6.97191C17.8327 7.21212 17.9651 7.48272 18.0456 7.77043C18.1374 8.09838 18.137 8.4422 18.1366 8.82801C18.1365 8.85472 18.1365 8.88163 18.1365 8.90875V14.8115C18.1365 15.2387 18.1365 15.6085 18.1116 15.9134C18.0853 16.2355 18.0271 16.5578 17.8688 16.8684C17.6333 17.3306 17.2575 17.7064 16.7954 17.9418C16.4847 18.1001 16.1625 18.1584 15.8403 18.1847C15.5354 18.2096 15.1656 18.2096 14.7385 18.2096H5.26167C4.8345 18.2096 4.4647 18.2096 4.15985 18.1847C3.83769 18.1584 3.51541 18.1001 3.20478 17.9418C2.7426 17.7064 2.36685 17.3306 2.13136 16.8684C1.97308 16.5578 1.91484 16.2355 1.88852 15.9134C1.86362 15.6085 1.86363 15.2387 1.86364 14.8115L1.86364 8.90875C1.86364 8.88163 1.86361 8.85472 1.86358 8.82801C1.86315 8.4422 1.86276 8.09838 1.95456 7.77043C2.03509 7.48272 2.16744 7.21211 2.3451 6.97191C2.54761 6.6981 2.81925 6.48733 3.12405 6.25081C3.14515 6.23443 3.16642 6.21793 3.18782 6.20128L8.69136 1.92075C8.71105 1.90544 8.73129 1.88954 8.75208 1.87322C8.96808 1.70357 9.24317 1.48751 9.57021 1.39896ZM8.39904 16.5429H11.6011V11.3715C11.6011 11.1371 11.6005 11.0071 11.5934 10.9141C11.5005 10.9071 11.3705 10.9065 11.1361 10.9065H8.86404C8.62967 10.9065 8.49969 10.9071 8.40672 10.9141C8.39966 11.0071 8.39904 11.1371 8.39904 11.3715V16.5429ZM13.2678 16.5429L13.2678 11.3451C13.2678 11.1407 13.2678 10.9405 13.2539 10.7706C13.2387 10.5838 13.2026 10.3617 13.0885 10.1379C12.9308 9.82839 12.6792 9.57677 12.3697 9.41907C12.1459 9.30502 11.9238 9.26889 11.7369 9.25362C11.5671 9.23975 11.3668 9.23977 11.1624 9.2398H8.83772C8.63333 9.23977 8.43305 9.23975 8.26321 9.25362C8.07637 9.26889 7.85429 9.30502 7.63045 9.41907C7.32096 9.57676 7.06934 9.82839 6.91165 10.1379C6.79759 10.3617 6.76146 10.5838 6.7462 10.7706C6.73232 10.9405 6.73235 11.1407 6.73237 11.3451L6.73237 16.5429H5.29363C4.82543 16.5429 4.52439 16.5422 4.29557 16.5236C4.07648 16.5057 3.99795 16.4754 3.96143 16.4568C3.81286 16.3811 3.69207 16.2603 3.61637 16.1118C3.59776 16.0753 3.56756 15.9967 3.54965 15.7776C3.53096 15.5488 3.53031 15.2478 3.53031 14.7796V8.90875C3.53031 8.39549 3.53755 8.29823 3.55954 8.21968C3.58542 8.12719 3.62797 8.0402 3.68508 7.96299C3.73358 7.89741 3.80592 7.83198 4.21106 7.51687L9.71459 3.23634C9.86132 3.12222 9.94061 3.06115 10.0001 3.02088C10.0595 3.06115 10.1388 3.12222 10.2856 3.23634L15.7891 7.51687C16.1942 7.83198 16.2666 7.89741 16.3151 7.96299C16.3722 8.0402 16.4147 8.12719 16.4406 8.21968C16.4626 8.29823 16.4698 8.39549 16.4698 8.90875V14.7796C16.4698 15.2478 16.4692 15.5488 16.4505 15.7776C16.4326 15.9967 16.4024 16.0753 16.3838 16.1118C16.3081 16.2603 16.1873 16.3811 16.0387 16.4568C16.0022 16.4754 15.9237 16.5057 15.7046 16.5236C15.4758 16.5422 15.1747 16.5429 14.7065 16.5429H13.2678Z" />
</svg>

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@@ -0,0 +1,3 @@
<svg width="21" height="20" viewBox="0 0 21 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.5 3.33301C10.9603 3.33301 11.3334 3.7061 11.3334 4.16634V9.16634H16.3334C16.7936 9.16634 17.1667 9.53944 17.1667 9.99967C17.1667 10.4599 16.7936 10.833 16.3334 10.833H11.3334V15.833C11.3334 16.2932 10.9603 16.6663 10.5 16.6663C10.0398 16.6663 9.66671 16.2932 9.66671 15.833V10.833H4.66671C4.20647 10.833 3.83337 10.4599 3.83337 9.99967C3.83337 9.53944 4.20647 9.16634 4.66671 9.16634H9.66671V4.16634C9.66671 3.7061 10.0398 3.33301 10.5 3.33301Z" fill="#3370FF"/>
</svg>

After

Width:  |  Height:  |  Size: 619 B

View File

@@ -0,0 +1,12 @@
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_19520_1493)">
<path fill-rule="evenodd" clip-rule="evenodd"
d="M6.99991 2.05339C4.26775 2.05339 2.0529 4.26824 2.0529 7.0004C2.0529 9.73256 4.26775 11.9474 6.99991 11.9474C9.73207 11.9474 11.9469 9.73256 11.9469 7.0004C11.9469 4.26824 9.73207 2.05339 6.99991 2.05339ZM0.88623 7.0004C0.88623 3.62391 3.62342 0.886719 6.99991 0.886719C10.3764 0.886719 13.1136 3.62391 13.1136 7.0004C13.1136 10.3769 10.3764 13.1141 6.99991 13.1141C3.62342 13.1141 0.88623 10.3769 0.88623 7.0004ZM6.41657 4.78826C6.41657 4.46609 6.67774 4.20493 6.99991 4.20493H7.00544C7.3276 4.20493 7.58877 4.46609 7.58877 4.78826C7.58877 5.11042 7.3276 5.37159 7.00544 5.37159H6.99991C6.67774 5.37159 6.41657 5.11042 6.41657 4.78826ZM6.99991 6.41706C7.32207 6.41706 7.58324 6.67823 7.58324 7.0004V9.21253C7.58324 9.5347 7.32207 9.79587 6.99991 9.79587C6.67774 9.79587 6.41657 9.5347 6.41657 9.21253V7.0004C6.41657 6.67823 6.67774 6.41706 6.99991 6.41706Z"
fill="#3370FF" />
</g>
<defs>
<clipPath id="clip0_19520_1493">
<rect width="14" height="14" fill="white" />
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -0,0 +1,5 @@
<svg width="18" height="19" viewBox="0 0 18 19" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd"
d="M11.5538 3.5208C11.4875 3.5049 11.4085 3.50008 11.0059 3.50008H6V5.30008C6 5.52247 6.00058 5.64416 6.00773 5.7316C6.00801 5.73508 6.0083 5.73838 6.00859 5.74149C6.0117 5.74178 6.01499 5.74206 6.01848 5.74235C6.10592 5.74949 6.22761 5.75008 6.45 5.75008H11.55C11.7724 5.75008 11.8941 5.74949 11.9815 5.74235C11.985 5.74206 11.9883 5.74178 11.9914 5.74149C11.9917 5.73838 11.992 5.73508 11.9923 5.7316C11.9994 5.64416 12 5.52247 12 5.30008V3.81211C11.8588 3.67386 11.8143 3.63743 11.7706 3.6106C11.7035 3.56947 11.6303 3.53917 11.5538 3.5208ZM13.2802 2.96965L13.2333 2.92269C13.2184 2.90784 13.2037 2.8931 13.1891 2.87848C12.9735 2.66249 12.7835 2.47206 12.5543 2.33164C12.353 2.20827 12.1335 2.11736 11.9039 2.06224C11.6426 1.9995 11.3735 1.99975 11.0684 2.00004C11.0477 2.00006 11.0269 2.00008 11.0059 2.00008L5.81903 2.00008C5.63224 2.00007 5.45554 2.00007 5.28855 2.00105C5.27578 2.0004 5.26293 2.00008 5.25 2.00008C5.23345 2.00008 5.21702 2.00061 5.20074 2.00167C4.86462 2.00446 4.5692 2.01214 4.31113 2.03322C3.88956 2.06767 3.50203 2.14159 3.13803 2.32706C2.57354 2.61468 2.1146 3.07362 1.82698 3.6381C1.64151 4.00211 1.56759 4.38963 1.53315 4.81121C1.49998 5.2171 1.49999 5.71537 1.5 6.3191V12.681C1.49999 13.2848 1.49998 13.783 1.53315 14.1889C1.56759 14.6105 1.64151 14.998 1.82698 15.362C2.1146 15.9265 2.57354 16.3855 3.13803 16.6731C3.50203 16.8586 3.88956 16.9325 4.31113 16.9669C4.5692 16.988 4.86462 16.9957 5.20074 16.9985C5.21702 16.9995 5.23345 17.0001 5.25 17.0001C5.26293 17.0001 5.27578 16.9997 5.28855 16.9991C5.45553 17.0001 5.63222 17.0001 5.81901 17.0001H12.181C12.3678 17.0001 12.5445 17.0001 12.7115 16.9991C12.7242 16.9997 12.7371 17.0001 12.75 17.0001C12.7666 17.0001 12.783 16.9995 12.7993 16.9985C13.1354 16.9957 13.4308 16.988 13.6889 16.9669C14.1104 16.9325 14.498 16.8586 14.862 16.6731C15.4265 16.3855 15.8854 15.9265 16.173 15.362C16.3585 14.998 16.4324 14.6105 16.4669 14.1889C16.5 13.7831 16.5 13.2848 16.5 12.6811V7.49419C16.5 7.47319 16.5 7.45236 16.5 7.4317C16.5003 7.12653 16.5006 6.85748 16.4378 6.59614C16.3827 6.36656 16.2918 6.14709 16.1684 5.94577C16.028 5.71662 15.8376 5.52655 15.6216 5.31096C15.607 5.29637 15.5922 5.28165 15.5774 5.2668L13.2802 2.96965C13.2803 2.96971 13.2802 2.96958 13.2802 2.96965ZM13.5 5.31074V5.32399C13.5 5.51328 13.5 5.69763 13.4873 5.85375C13.4733 6.02519 13.4402 6.22749 13.3365 6.43106C13.1927 6.71331 12.9632 6.94278 12.681 7.08659C12.4774 7.19031 12.2751 7.22336 12.1037 7.23737C11.9476 7.25012 11.7632 7.2501 11.5739 7.25008L6.45 7.25008C6.44202 7.25008 6.43404 7.25008 6.42608 7.25008C6.23679 7.2501 6.05245 7.25012 5.89633 7.23737C5.72488 7.22336 5.52258 7.19031 5.31902 7.08659C5.03677 6.94278 4.8073 6.7133 4.66349 6.43106C4.55977 6.22749 4.52672 6.02519 4.51271 5.85375C4.49995 5.69763 4.49998 5.51329 4.5 5.324C4.5 5.31603 4.5 5.30806 4.5 5.30008V3.52322C4.47733 3.52479 4.4551 3.52646 4.43328 3.52824C4.10447 3.5551 3.93631 3.6038 3.81902 3.66357C3.53677 3.80738 3.3073 4.03685 3.16349 4.31909C3.10372 4.43639 3.05503 4.60454 3.02816 4.93335C3.00058 5.27092 3 5.70764 3 6.35008V12.6501C3 13.2925 3.00058 13.7292 3.02816 14.0668C3.05503 14.3956 3.10372 14.5638 3.16349 14.6811C3.3073 14.9633 3.53677 15.1928 3.81902 15.3366C3.93631 15.3964 4.10447 15.445 4.43328 15.4719C4.4551 15.4737 4.47733 15.4754 4.5 15.4769L4.5 11.4262C4.49998 11.2369 4.49995 11.0525 4.51271 10.8964C4.52672 10.725 4.55977 10.5227 4.66349 10.3191C4.8073 10.0368 5.03677 9.80738 5.31902 9.66357C5.52258 9.55984 5.72488 9.52679 5.89633 9.51278C6.05245 9.50003 6.2368 9.50005 6.42609 9.50007H11.5739C11.7632 9.50005 11.9475 9.50003 12.1037 9.51278C12.2751 9.52679 12.4774 9.55984 12.681 9.66357C12.9632 9.80738 13.1927 10.0368 13.3365 10.3191C13.4402 10.5227 13.4733 10.725 13.4873 10.8964C13.5 11.0525 13.5 11.2369 13.5 11.4262L13.5 15.4769C13.5227 15.4754 13.5449 15.4737 13.5667 15.4719C13.8955 15.445 14.0637 15.3964 14.181 15.3366C14.4632 15.1928 14.6927 14.9633 14.8365 14.6811C14.8963 14.5638 14.945 14.3956 14.9718 14.0668C14.9994 13.7292 15 13.2925 15 12.6501V7.49419C15 7.0916 14.9952 7.01255 14.9793 6.94631C14.9609 6.86979 14.9306 6.79663 14.8895 6.72952C14.8539 6.67144 14.8014 6.61213 14.5167 6.32746L13.5 5.31074ZM12 15.5001V11.4501C12 11.2277 11.9994 11.106 11.9923 11.0186C11.992 11.0151 11.9917 11.0118 11.9914 11.0087C11.9883 11.0084 11.985 11.0081 11.9815 11.0078C11.8941 11.0007 11.7724 11.0001 11.55 11.0001H6.45C6.22761 11.0001 6.10592 11.0007 6.01848 11.0078C6.01499 11.0081 6.0117 11.0084 6.00859 11.0087C6.0083 11.0118 6.00801 11.0151 6.00773 11.0186C6.00058 11.106 6 11.2277 6 11.4501V15.5001H12Z"
fill="#3370FF" />
</svg>

After

Width:  |  Height:  |  Size: 4.7 KiB

View File

@@ -0,0 +1,5 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd"
d="M6.72916 1.85097L6.69716 1.85085C5.98981 1.84827 5.41301 1.84617 4.94539 1.88309C4.46228 1.92122 4.0275 2.00464 3.62407 2.21292C3.01781 2.52591 2.5263 3.01564 2.21111 3.62075C2.00154 4.02311 1.91644 4.45656 1.8765 4.93842C1.83788 5.40451 1.83788 5.97916 1.83789 6.68347V11.3274C1.83788 12.0263 1.83788 12.5969 1.87635 13.0602C1.91616 13.5396 2.001 13.9708 2.20943 14.3721C2.52252 14.9748 3.01395 15.4662 3.61667 15.7793C4.01791 15.9877 4.44916 16.0726 4.92851 16.1124C5.39183 16.1509 5.96247 16.1508 6.66137 16.1508H11.3384C12.0373 16.1508 12.6079 16.1509 13.0712 16.1124C13.5506 16.0726 13.9818 15.9877 14.3831 15.7793C14.9858 15.4662 15.4772 14.9748 15.7903 14.3721C15.9987 13.9708 16.0836 13.5396 16.1234 13.0602C16.1619 12.5969 16.1619 12.0263 16.1619 11.3274V9.50554C16.1619 9.09133 15.8261 8.75554 15.4119 8.75554C14.9976 8.75554 14.6619 9.09133 14.6619 9.50554V11.2954C14.6619 12.0341 14.6613 12.5422 14.6285 12.9361C14.5966 13.321 14.5379 13.5291 14.4592 13.6806C14.2884 14.0094 14.0204 14.2774 13.6916 14.4482C13.5402 14.5269 13.332 14.5856 12.9471 14.6175C12.5532 14.6502 12.045 14.6508 11.3064 14.6508H6.69339C5.9547 14.6508 5.44655 14.6502 5.05265 14.6175C4.66773 14.5856 4.45958 14.5269 4.30814 14.4482C3.97938 14.2774 3.71132 14.0094 3.54055 13.6806C3.46188 13.5292 3.40317 13.321 3.37121 12.9361C3.33849 12.5422 3.33789 12.034 3.33789 11.2953V6.71554C3.33789 5.97142 3.33849 5.45912 3.37138 5.06231C3.40354 4.67419 3.46264 4.46502 3.54147 4.31369C3.7137 3.98302 3.98088 3.71681 4.31217 3.54578C4.46347 3.46767 4.67333 3.40923 5.06343 3.37843C5.46198 3.34697 5.97664 3.34825 6.72372 3.35096C7.34592 3.35322 7.97166 3.35468 8.54751 3.35468C8.96173 3.35468 9.29751 3.0189 9.29751 2.60468C9.29751 2.19047 8.96173 1.85468 8.54751 1.85468C7.97404 1.85468 7.35021 1.85322 6.72916 1.85097ZM8.26254 8.66553C7.96964 8.95843 7.96964 9.4333 8.26254 9.7262C8.55543 10.0191 9.0303 10.0191 9.3232 9.72619L14.6619 4.38754V6.5054C14.6619 6.91962 14.9976 7.2554 15.4119 7.2554C15.8261 7.2554 16.1619 6.91962 16.1619 6.5054V2.6698C16.1622 2.66102 16.1623 2.6522 16.1623 2.64334C16.1623 2.22913 15.8265 1.89334 15.4123 1.89334C15.4122 1.89334 15.4124 1.89334 15.4123 1.89334H11.5503C11.136 1.89334 10.8003 2.22913 10.8003 2.64334C10.8003 3.05756 11.136 3.39334 11.5503 3.39334H13.5347L8.26254 8.66553Z"
fill="white" />
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@@ -0,0 +1,5 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd"
d="M5.47674 0.0561965L5.44118 0.0560671C4.65524 0.053204 4.01435 0.0508692 3.49477 0.0918841C2.95798 0.134258 2.47489 0.226947 2.02664 0.458364C1.35301 0.806127 0.806891 1.35027 0.456684 2.02263C0.223824 2.46969 0.129267 2.9513 0.0848958 3.48669C0.0419755 4.00458 0.0419831 4.64308 0.0419925 5.42564V10.5855C0.041983 11.3621 0.0419753 11.9961 0.0847264 12.5109C0.128958 13.0435 0.223226 13.5227 0.454811 13.9685C0.802688 14.6382 1.34873 15.1842 2.01842 15.5321C2.46424 15.7637 2.9434 15.858 3.47601 15.9022C3.99081 15.945 4.62486 15.945 5.40142 15.9449H10.5981C11.3746 15.945 12.0087 15.945 12.5235 15.9022C13.0561 15.858 13.5353 15.7637 13.9811 15.5321C14.6508 15.1842 15.1968 14.6382 15.5447 13.9685C15.7763 13.5227 15.8705 13.0435 15.9148 12.5109C15.9575 11.9962 15.9575 11.3621 15.9575 10.5856V8.56128C15.9575 8.10104 15.5844 7.72795 15.1242 7.72795C14.6639 7.72795 14.2908 8.10104 14.2908 8.56128V10.55C14.2908 11.3708 14.2902 11.9354 14.2538 12.373C14.2183 12.8007 14.1531 13.032 14.0657 13.2002C13.8759 13.5655 13.5781 13.8634 13.2128 14.0531C13.0445 14.1405 12.8132 14.2057 12.3856 14.2413C11.9479 14.2776 11.3833 14.2783 10.5625 14.2783H5.43699C4.61622 14.2783 4.05161 14.2776 3.61395 14.2413C3.18626 14.2057 2.95498 14.1405 2.78671 14.0531C2.42142 13.8634 2.12358 13.5655 1.93383 13.2002C1.84642 13.032 1.78119 12.8007 1.74568 12.373C1.70933 11.9353 1.70866 11.3707 1.70866 10.5499V5.46128C1.70866 4.63448 1.70933 4.06526 1.74587 3.62435C1.78161 3.19311 1.84727 2.9607 1.93485 2.79256C2.12623 2.42514 2.42309 2.12936 2.79119 1.93932C2.95931 1.85253 3.19248 1.7876 3.62592 1.75338C4.06876 1.71843 4.6406 1.71984 5.47069 1.72285C6.16203 1.72536 6.85729 1.72699 7.49713 1.72699C7.95737 1.72699 8.33046 1.3539 8.33046 0.893659C8.33046 0.433422 7.95737 0.0603261 7.49713 0.0603261C6.85993 0.0603261 6.1668 0.0587019 5.47674 0.0561965ZM7.18049 7.62794C6.85505 7.95338 6.85505 8.48101 7.18049 8.80645C7.50592 9.13189 8.03356 9.13189 8.359 8.80645L14.2908 2.87461V5.22779C14.2908 5.68803 14.6639 6.06113 15.1242 6.06113C15.5844 6.06113 15.9575 5.68803 15.9575 5.22779V0.966006C15.9578 0.95625 15.958 0.946452 15.958 0.936614C15.958 0.476377 15.5849 0.103281 15.1247 0.103281C15.1246 0.103281 15.1248 0.103281 15.1247 0.103281H10.8335C10.3733 0.103281 10.0002 0.476377 10.0002 0.936614C10.0002 1.39685 10.3733 1.76995 10.8335 1.76995H13.0385L7.18049 7.62794Z"
fill="#3370FF" />
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@@ -0,0 +1,7 @@
<svg width="24" height="25" viewBox="0 0 24 25" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="icon/line/export">
<path id="Icon (Stroke)" fill-rule="evenodd" clip-rule="evenodd"
d="M11.2928 2.58684C11.6834 2.19631 12.3165 2.19631 12.707 2.58684L16.707 6.58684C17.0976 6.97736 17.0976 7.61053 16.707 8.00105C16.3165 8.39158 15.6834 8.39158 15.2928 8.00105L12.9999 5.70816V15.2939C12.9999 15.8462 12.5522 16.2939 11.9999 16.2939C11.4477 16.2939 10.9999 15.8462 10.9999 15.2939V5.70816L8.70705 8.00105C8.31652 8.39158 7.68336 8.39158 7.29283 8.00105C6.90231 7.61053 6.90231 6.97736 7.29283 6.58684L11.2928 2.58684ZM2.99994 11.2939C3.55222 11.2939 3.99994 11.7417 3.99994 12.2939V16.4939C3.99994 17.3505 4.00072 17.9328 4.03749 18.3829C4.07331 18.8213 4.13824 19.0455 4.21793 19.2019C4.40967 19.5783 4.71564 19.8842 5.09196 20.076C5.24836 20.1556 5.47256 20.2206 5.91098 20.2564C6.36107 20.2932 6.94336 20.2939 7.79994 20.2939H16.1999C17.0565 20.2939 17.6388 20.2932 18.0889 20.2564C18.5273 20.2206 18.7515 20.1556 18.9079 20.076C19.2842 19.8842 19.5902 19.5783 19.782 19.2019C19.8616 19.0455 19.9266 18.8213 19.9624 18.3829C19.9992 17.9328 19.9999 17.3505 19.9999 16.4939V12.2939C19.9999 11.7417 20.4477 11.2939 20.9999 11.2939C21.5522 11.2939 21.9999 11.7417 21.9999 12.2939V16.5353C22 17.3402 22 18.0046 21.9557 18.5458C21.9098 19.1079 21.8113 19.6246 21.564 20.1099C21.1805 20.8626 20.5686 21.4745 19.8159 21.858C19.3306 22.1053 18.8139 22.2038 18.2518 22.2498C17.7106 22.294 17.0462 22.294 16.2413 22.2939H7.75862C6.95366 22.294 6.2893 22.294 5.74811 22.2498C5.18602 22.2038 4.66931 22.1053 4.18398 21.858C3.43133 21.4745 2.81941 20.8626 2.43591 20.1099C2.18862 19.6246 2.09006 19.1079 2.04413 18.5458C1.99992 18.0046 1.99993 17.3402 1.99994 16.5352L1.99994 12.2939C1.99994 11.7417 2.44766 11.2939 2.99994 11.2939Z"
fill="#3370FF" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@@ -0,0 +1,289 @@
import type { ForwardedRef } from 'react';
import React, {
useRef,
forwardRef,
useMemo,
useEffect,
useImperativeHandle,
useState
} from 'react';
import {
Menu,
MenuList,
MenuItem,
Button,
useDisclosure,
MenuButton,
Box,
Flex,
Input,
Tag,
HStack
} from '@chakra-ui/react';
import type { ButtonProps, MenuItemProps } from '@chakra-ui/react';
import MyIcon from '../Icon';
import { useRequest2 } from '../../../hooks/useRequest';
import MyDivider from '../MyDivider';
import type { useScrollPagination } from '../../../hooks/useScrollPagination';
import Avatar from '../Avatar';
/** 选择组件 Props 类型
* value: 选中的值
* placeholder: 占位符
* list: 列表数据
* isLoading: 是否加载中
* ScrollData: 分页滚动数据控制器 [useScrollPagination] 的返回值
* */
export type GateSelectProps<T = any> = Omit<ButtonProps, 'onChange'> & {
value?: T;
placeholder?: string;
isSearch?: boolean;
list: {
alias?: string;
icon?: string;
iconSize?: string;
label: string | React.ReactNode;
description?: string;
value: T;
showBorder?: boolean;
tagColor?: string;
tagText?: string;
}[];
isLoading?: boolean;
onChange?: (val: T) => any | Promise<any>;
ScrollData?: ReturnType<typeof useScrollPagination>['ScrollData'];
};
const GateSelect = <T = any,>(
{
placeholder,
value,
isSearch = false,
width = '100%',
list = [],
onChange,
isLoading = false,
ScrollData,
...props
}: GateSelectProps<T>,
ref: ForwardedRef<{
focus: () => void;
}>
) => {
const ButtonRef = useRef<HTMLButtonElement>(null);
const MenuListRef = useRef<HTMLDivElement>(null);
const SelectedItemRef = useRef<HTMLDivElement>(null);
const SearchInputRef = useRef<HTMLInputElement>(null);
const menuItemStyles: MenuItemProps = {
borderRadius: 'sm',
py: 2,
display: 'flex',
alignItems: 'center',
_hover: {
backgroundColor: 'myGray.100'
},
_notLast: {
mb: 1
}
};
const { isOpen, onOpen, onClose } = useDisclosure();
const selectItem = useMemo(() => list.find((item) => item.value === value), [list, value]);
const [search, setSearch] = useState('');
const filterList = useMemo(() => {
if (!isSearch || !search) {
return list;
}
return list.filter((item) => {
const text = `${item.label?.toString()}${item.alias}${item.value}`;
const regx = new RegExp(search, 'gi');
return regx.test(text);
});
}, [list, search, isSearch]);
useImperativeHandle(ref, () => ({
focus() {
onOpen();
}
}));
useEffect(() => {
if (isOpen && MenuListRef.current && SelectedItemRef.current) {
const menu = MenuListRef.current;
const selectedItem = SelectedItemRef.current;
menu.scrollTop = selectedItem.offsetTop - menu.offsetTop - 100;
if (isSearch) {
setSearch('');
}
}
}, [isSearch, isOpen]);
const { runAsync: onclickChange, loading } = useRequest2((val: T) => onChange?.(val));
const ListRender = useMemo(() => {
return (
<>
{filterList.map((item, i) => (
<Box key={i}>
<MenuItem
{...menuItemStyles}
{...(value === item.value
? {
ref: SelectedItemRef,
color: 'primary.700',
bg: 'myGray.100',
fontWeight: '600'
}
: {
color: 'myGray.900'
})}
onClick={() => {
if (value !== item.value) {
onclickChange(item.value);
}
}}
whiteSpace={'pre-wrap'}
fontSize={'sm'}
display={'block'}
mb={0.5}
>
<Flex alignItems={'center'} justifyContent="space-between" width="100%">
<Flex alignItems={'center'}>
{item.icon && (
<Avatar mr={2} src={item.icon as any} w={item.iconSize ?? '1rem'} />
)}
{item.label}
</Flex>
{item.tagText && (
<Tag size="sm" colorScheme={item.tagColor || 'gray'} ml={2}>
{item.tagText}
</Tag>
)}
</Flex>
{item.description && (
<Box color={'myGray.500'} fontSize={'xs'}>
{item.description}
</Box>
)}
</MenuItem>
{item.showBorder && <MyDivider my={2} />}
</Box>
))}
</>
);
}, [filterList, value]);
const isSelecting = loading || isLoading;
return (
<Box>
<Menu
autoSelect={false}
isOpen={isOpen && !isSelecting}
onOpen={onOpen}
onClose={onClose}
strategy={'fixed'}
>
<MenuButton
as={Button}
ref={ButtonRef}
width={width}
px={3}
rightIcon={<MyIcon name={'core/chat/chevronDown'} w={4} color={'myGray.500'} />}
variant={'whitePrimaryOutline'}
size={'md'}
fontSize={'sm'}
textAlign={'left'}
h={'auto'}
whiteSpace={'pre-wrap'}
wordBreak={'break-word'}
_active={{
transform: 'none'
}}
_hover={{
borderRadius: '10px',
border: '0.5px solid var(--Gray-Iron-250, #E0E0E0)',
background: 'var(--Gray-Iron-150, #F3F3F3)'
}}
{...(isOpen
? {
borderColor: 'primary.600',
color: 'primary.700'
}
: {})}
{...props}
>
<Flex alignItems={'center'} justifyContent="space-between" width="100%">
<Flex alignItems={'center'}>
{isSelecting && <MyIcon mr={2} name={'common/loading'} w={'1rem'} />}
{isSearch && isOpen ? (
<Input
ref={SearchInputRef}
autoFocus
variant={'unstyled'}
value={search}
onChange={(e) => setSearch(e.target.value)}
placeholder={
selectItem?.alias ||
(typeof selectItem?.label === 'string' ? selectItem?.label : placeholder)
}
size={'sm'}
w={'100%'}
color={'myGray.700'}
onBlur={() => {
setTimeout(() => {
SearchInputRef?.current?.focus();
}, 0);
}}
/>
) : (
<Flex alignItems="center">
{selectItem?.icon && (
<Avatar mr={2} src={selectItem.icon as any} w={selectItem.iconSize ?? '1rem'} />
)}
{selectItem?.alias || selectItem?.label || placeholder}
</Flex>
)}
</Flex>
{selectItem?.tagText && (
<Tag size="sm" colorScheme={selectItem.tagColor || 'gray'} ml={2}>
{selectItem.tagText}
</Tag>
)}
</Flex>
</MenuButton>
<MenuList
ref={MenuListRef}
className={props.className}
w={(() => {
const w = ButtonRef.current?.clientWidth;
if (w) {
return `${w}px !important`;
}
return Array.isArray(width)
? width.map((item) => `${item} !important`)
: `${width} !important`;
})()}
px={'6px'}
py={'6px'}
border={'1px solid #fff'}
boxShadow={
'0px 2px 4px rgba(161, 167, 179, 0.25), 0px 0px 1px rgba(121, 141, 159, 0.25);'
}
zIndex={99}
maxH={'40vh'}
overflowY={'auto'}
>
{ScrollData ? <ScrollData>{ListRender}</ScrollData> : ListRender}
</MenuList>
</Menu>
</Box>
);
};
export default forwardRef(GateSelect) as <T>(
props: GateSelectProps<T> & { ref?: React.Ref<HTMLSelectElement> }
) => JSX.Element;

View File

@@ -50,6 +50,7 @@ export type SelectProps<T = any> = Omit<ButtonProps, 'onChange'> & {
showBorder?: boolean;
}[];
isLoading?: boolean;
showAvatar?: boolean;
onChange?: (val: T) => any | Promise<any>;
ScrollData?: ReturnType<typeof useScrollPagination>['ScrollData'];
customOnOpen?: () => void;
@@ -79,6 +80,7 @@ const MySelect = <T = any,>(
list = [],
onChange,
isLoading = false,
showAvatar = true,
ScrollData,
customOnOpen,
customOnClose,
@@ -255,7 +257,7 @@ const MySelect = <T = any,>(
/>
) : (
<>
{selectItem?.icon && (
{selectItem?.icon && showAvatar && (
<Avatar
mr={2}
src={selectItem.icon as any}

View File

@@ -1,17 +1,26 @@
import { Box } from '@chakra-ui/react';
import React from 'react';
import React, { useMemo } from 'react';
const HighlightText = ({
rawText,
matchText,
color = 'primary.600'
color = 'primary.600',
mode = 'text'
}: {
rawText: string;
matchText: string;
color?: string;
mode?: 'text' | 'bg';
}) => {
const regex = new RegExp(`(${matchText})`, 'gi');
const parts = rawText.split(regex);
const { parts } = useMemo(() => {
const regx = new RegExp(`(${matchText})`, 'gi');
const parts = rawText.split(regx);
return {
regx,
parts
};
}, [rawText, matchText]);
return (
<Box>
@@ -28,7 +37,17 @@ const HighlightText = ({
}
return (
<Box as="span" key={index} color={highLight ? color : 'inherit'}>
<Box
as="span"
key={index}
{...(mode === 'bg'
? {
bg: highLight ? color : 'transparent'
}
: {
color: highLight ? color : 'inherit'
})}
>
{part}
</Box>
);
@@ -37,4 +56,4 @@ const HighlightText = ({
);
};
export default HighlightText;
export default React.memo(HighlightText);

View File

@@ -1,4 +1,4 @@
import React, { forwardRef } from 'react';
import React, { forwardRef, useRef, useState, useEffect } from 'react';
import { Flex, Box, type BoxProps, HStack } from '@chakra-ui/react';
import MyIcon from '../Icon';
@@ -26,8 +26,41 @@ const FillRowTabs = ({
iconGap = 2,
...props
}: Props) => {
const tabsRef = useRef<HTMLDivElement>(null);
const itemsRef = useRef<Map<any, HTMLDivElement>>(new Map());
const [sliderStyle, setSliderStyle] = useState({
width: 0,
left: 0,
opacity: 0
});
useEffect(() => {
const updateSlider = () => {
const activeItem = itemsRef.current.get(value);
if (activeItem && tabsRef.current) {
const tabsRect = tabsRef.current.getBoundingClientRect();
const itemRect = activeItem.getBoundingClientRect();
setSliderStyle({
width: itemRect.width,
left: itemRect.left - tabsRect.left,
opacity: 1
});
}
};
updateSlider();
window.addEventListener('resize', updateSlider);
return () => {
window.removeEventListener('resize', updateSlider);
};
}, [value]);
return (
<Box
ref={tabsRef}
position="relative"
display={'inline-flex'}
px={'3px'}
py={'3px'}
@@ -40,9 +73,29 @@ const FillRowTabs = ({
fontWeight={'medium'}
{...props}
>
{/* 滑动背景元素 */}
<Box
position="absolute"
height="calc(100% - 6px)"
top="3px"
borderRadius={'xs'}
bg="white"
boxShadow="1.5"
transition="all 0.14s ease-in-out"
pointerEvents="none"
style={{
width: `${sliderStyle.width}px`,
left: `${sliderStyle.left}px`,
opacity: sliderStyle.opacity
}}
/>
{list.map((item) => (
<HStack
key={item.value}
ref={(el) => {
if (el) itemsRef.current.set(item.value, el);
}}
flex={'1 0 0'}
alignItems={'center'}
justifyContent={'center'}
@@ -53,19 +106,14 @@ const FillRowTabs = ({
userSelect={'none'}
whiteSpace={'noWrap'}
gap={iconGap}
{...(value === item.value
? {
bg: 'white',
boxShadow: '1.5',
color: 'primary.600'
}
: {
color: 'myGray.500',
_hover: {
color: 'primary.600'
},
onClick: () => onChange(item.value)
})}
zIndex={1}
position="relative"
transition="color 0.25s ease"
onClick={() => onChange(item.value)}
color={value === item.value ? 'primary.600' : 'myGray.500'}
_hover={{
color: 'primary.600'
}}
>
{item.icon && <MyIcon name={item.icon as any} w={iconSize} />}
<Box fontSize={labelSize}>{item.label}</Box>

View File

@@ -18,7 +18,7 @@ export default function Variable({ variableLabel }: { variableLabel: string }) {
: { bg: 'red.50', color: 'red.600' })}
>
{variableLabel ? (
<Flex alignItems={'center'}>{variableLabel}</Flex>
<Flex alignItems={'center'}>{t(variableLabel as any)}</Flex>
) : (
<Box>{t('common:invalid_variable')}</Box>
)}

View File

@@ -187,7 +187,7 @@ export function useScrollPagination<
scrollLoadType = 'bottom',
pageSize = 10,
params = {},
params,
EmptyTip,
showErrorToast = true,
disalbed = false,
@@ -196,7 +196,7 @@ export function useScrollPagination<
scrollLoadType?: 'top' | 'bottom';
pageSize?: number;
params?: Record<string, any>;
params?: Omit<TParams, 'offset' | 'pageSize'>;
EmptyTip?: React.JSX.Element;
showErrorToast?: boolean;
disalbed?: boolean;

Some files were not shown because too many files have changed in this diff Show More