diff --git a/docSite/assets/imgs/dataset3.png b/docSite/assets/imgs/dataset3.png new file mode 100644 index 000000000000..c36b92e81db3 Binary files /dev/null and b/docSite/assets/imgs/dataset3.png differ diff --git a/docSite/assets/imgs/dataset4.png b/docSite/assets/imgs/dataset4.png new file mode 100644 index 000000000000..3230c015a3b5 Binary files /dev/null and b/docSite/assets/imgs/dataset4.png differ diff --git a/docSite/assets/imgs/faq1.png b/docSite/assets/imgs/faq1.png new file mode 100644 index 000000000000..6a5223c761bc Binary files /dev/null and b/docSite/assets/imgs/faq1.png differ diff --git a/docSite/assets/imgs/faq2.png b/docSite/assets/imgs/faq2.png new file mode 100644 index 000000000000..a05e9ebd21d6 Binary files /dev/null and b/docSite/assets/imgs/faq2.png differ diff --git a/docSite/assets/imgs/faq3.png b/docSite/assets/imgs/faq3.png new file mode 100644 index 000000000000..c9f7252b5232 Binary files /dev/null and b/docSite/assets/imgs/faq3.png differ diff --git a/docSite/assets/imgs/other1.png b/docSite/assets/imgs/other1.png new file mode 100644 index 000000000000..7b4ff777ae72 Binary files /dev/null and b/docSite/assets/imgs/other1.png differ diff --git a/docSite/assets/imgs/other2.png b/docSite/assets/imgs/other2.png new file mode 100644 index 000000000000..c89b7612af0e Binary files /dev/null and b/docSite/assets/imgs/other2.png differ diff --git a/docSite/assets/imgs/other3.png b/docSite/assets/imgs/other3.png new file mode 100644 index 000000000000..d06180a56f53 Binary files /dev/null and b/docSite/assets/imgs/other3.png differ diff --git a/docSite/assets/imgs/quizApp1.png b/docSite/assets/imgs/quizApp1.png new file mode 100644 index 000000000000..b8dd79e78962 Binary files /dev/null and b/docSite/assets/imgs/quizApp1.png differ diff --git a/docSite/assets/imgs/quizApp2.png b/docSite/assets/imgs/quizApp2.png new file mode 100644 index 000000000000..4d24f7356dfd Binary files /dev/null and b/docSite/assets/imgs/quizApp2.png differ diff --git a/docSite/content/zh-cn/docs/development/faq.md b/docSite/content/zh-cn/docs/development/faq.md index 91e606503bcf..dd42b9807b53 100644 --- a/docSite/content/zh-cn/docs/development/faq.md +++ b/docSite/content/zh-cn/docs/development/faq.md @@ -19,6 +19,53 @@ images: [] ## 二、通用问题 +### 通过sealos部署的话,是否没有本地部署的一些限制? +![](/imgs/faq1.png) +这是索引模型的长度限制,通过任何方式部署都一样的,但不同索引模型的配置不一样,可以在后台修改参数。 + +### sealos怎么挂载 小程序配置文件 + +新增配置文件:/app/projects/app/public/xxxx.txt +如图 +![](/imgs/faq2.png) + +### 数据库3306端口被占用了,启动服务失败 +![](/imgs/faq3.png) + +mysql 只有 oneAPI 用到,外面一般不需要调用,所以可以 +- 把 3306:3306 的映射去掉/或者直接改一个映射。 + +```yaml +# 在 docker-compose.yaml 文件内 +# ... + mysql: + image: mysql:8.0.36 + ports: + - 3306:3306 # 这个端口被占用了! + # - 3307:3306 # 直接改一个。。和外面的不冲突 + # *empty* 或者直接删了,反正外面用不到 + oneapi: + container_name: oneapi + image: ghcr.io/songquanpeng/one-api:latest + environment: + - SQL_DSN=root:oneapimmysql@tcp(mysql:3306)/oneapi # 这不用改,容器内外网络是隔离的 +``` +- 另一种做法是可以直接连现有的 mysql, 要改 oneAPI 的环境变量。 +```yaml +# 在 docker-compose.yaml 文件内 +# ... +# mysql: # 要连外面的,这个玩意用不到了 +# image: mysql:8.0.36 +# ports: +# - 3306:3306 # 这个端口被占用了! +oneapi: + container_name: oneapi + image: ghcr.io/songquanpeng/one-api:latest + environment: + - SQL_DSN=root:oneapimmysql@tcp(mysql:3306)/oneapi # 改成外面的链接字符串 +``` + + ### 本地部署的限制 具体内容参考https://fael3z0zfze.feishu.cn/wiki/OFpAw8XzAi36Guk8dfucrCKUnjg。 diff --git a/docSite/content/zh-cn/docs/development/openapi/auth.md b/docSite/content/zh-cn/docs/development/openapi/auth.md index 5bcd72a0e45f..aa7014e80db8 100644 --- a/docSite/content/zh-cn/docs/development/openapi/auth.md +++ b/docSite/content/zh-cn/docs/development/openapi/auth.md @@ -55,4 +55,28 @@ curl --location --request POST 'https://api.fastgpt.in/api/v1/chat/completions' } ] }' -``` \ No newline at end of file +``` + +## 自定义用户 ID + +`v4.8.13`后支持传入自定义的用户 ID, 并且存入历史记录中。 + +```sh +curl --location --request POST 'https://api.fastgpt.in/api/v1/chat/completions' \ +--header 'Authorization: Bearer fastgpt-xxxxxx' \ +--header 'Content-Type: application/json' \ +--data-raw '{ + "chatId": "111", + "stream": false, + "detail": false, + "messages": [ + { + "content": "导演是谁", + "role": "user" + } + ], + "customUid": "xxxxxx" +}' +``` + +在历史记录中,该条记录的使用者会显示为 `xxxxxx`。 diff --git a/docSite/content/zh-cn/docs/development/openapi/chat.md b/docSite/content/zh-cn/docs/development/openapi/chat.md index 25f2c835d0ae..808eccdeee1b 100644 --- a/docSite/content/zh-cn/docs/development/openapi/chat.md +++ b/docSite/content/zh-cn/docs/development/openapi/chat.md @@ -686,7 +686,7 @@ curl --location --request POST 'http://localhost:3000/api/core/chat/getHistories - appId - 应用 Id - offset - 偏移量,即从第几条数据开始取 - pageSize - 记录数量 -- source - 对话源 +- source - 对话源。source=api,表示获取通过 API 创建的对话(不会获取到页面上的对话记录) {{% /alert %}} {{< /markdownify >}} diff --git a/docSite/content/zh-cn/docs/development/openapi/dataset.md b/docSite/content/zh-cn/docs/development/openapi/dataset.md index 09d4004633a8..efe1d6397c6e 100644 --- a/docSite/content/zh-cn/docs/development/openapi/dataset.md +++ b/docSite/content/zh-cn/docs/development/openapi/dataset.md @@ -733,6 +733,21 @@ data 为集合的 ID。 {{< tab tabName="请求示例" >}} {{< markdownify >}} +**4.8.19+** +```bash +curl --location --request POST 'http://localhost:3000/api/core/dataset/collection/listv2' \ +--header 'Authorization: Bearer {{authorization}}' \ +--header 'Content-Type: application/json' \ +--data-raw '{ + "offset":0, + "pageSize": 10, + "datasetId":"6593e137231a2be9c5603ba7", + "parentId": null, + "searchText":"" +}' +``` + +**4.8.19-(不再维护)** ```bash curl --location --request POST 'http://localhost:3000/api/core/dataset/collection/list' \ --header 'Authorization: Bearer {{authorization}}' \ @@ -753,7 +768,7 @@ curl --location --request POST 'http://localhost:3000/api/core/dataset/collectio {{< markdownify >}} {{% alert icon=" " context="success" %}} -- pageNum: 页码(选填) +- offset: 偏移量 - pageSize: 每页数量,最大30(选填) - datasetId: 知识库的ID(必填) - parentId: 父级Id(选填) @@ -773,9 +788,7 @@ curl --location --request POST 'http://localhost:3000/api/core/dataset/collectio "statusText": "", "message": "", "data": { - "pageNum": 1, - "pageSize": 10, - "data": [ + "list": [ { "_id": "6593e137231a2be9c5603ba9", "parentId": null, diff --git a/docSite/content/zh-cn/docs/development/upgrading/4819.md b/docSite/content/zh-cn/docs/development/upgrading/4819.md new file mode 100644 index 000000000000..7f3ad25f66b2 --- /dev/null +++ b/docSite/content/zh-cn/docs/development/upgrading/4819.md @@ -0,0 +1,22 @@ +--- +title: 'V4.8.19(进行中)' +description: 'FastGPT V4.8.19 更新说明' +icon: 'upgrade' +draft: false +toc: true +weight: 806 +--- + + +## 完整更新内容 + +1. 新增 - 工作流知识库检索支持按知识库权限进行过滤。 +2. 新增 - 飞书/语雀知识库查看原文。 +3. 新增 - 流程等待插件,可以等待 n 毫秒后继续执行流程。 +4. 优化 - 成员列表分页加载。 +5. 优化 - 统一分页加载代码。 +6. 优化 - 对话页面加载时,可配置是否为独立页面。 +7. 修复 - 语雀文件库导入时,嵌套文件内容无法展开的问题。 +8. 修复 - 工作流编排中,LLM 参数无法关闭问题。 +9. 修复 - 工作流编排中,代码运行节点还原模板问题。 +10. 修复 - HTTP 接口适配对象字符串解析。 \ No newline at end of file diff --git a/docSite/content/zh-cn/docs/faq/app.md b/docSite/content/zh-cn/docs/faq/app.md index 5797a3f6090f..a38f5baba9af 100644 --- a/docSite/content/zh-cn/docs/faq/app.md +++ b/docSite/content/zh-cn/docs/faq/app.md @@ -21,6 +21,19 @@ weight: 908 定时执行会在应用发布后生效,会在后台生效。 +## V4.8.18-FIX2中提到“ 1. 修复 HTTP 节点, {{}} 格式引用变量兼容问题。建议尽快替换 / 模式取变量, {{}} 语法已弃用。”替换{{}}引用格式是仅仅只有在http节点,还是所有节点的都会有影响? + +只有 http 节点用到这个语法。 + +## 工作流类型的应用在运行预览可以正常提问返回,但是发布免登录窗口之后有问题。 + +一般是没正确发布,在工作流右上角点击【保存并发布】。 + +## 如何解决猜你想问使用中文回答显示 + +注意需要更新到V4.8.17及以上,把猜你想问的提示词改成中文。 +![](/imgs/quizApp2.png) + ## AI对话回答要求中的Markdown语法取消 修改知识库默认提示词, 默认用的是标准模板提示词,会要求按 Markdown 输出,可以去除该要求: @@ -38,3 +51,32 @@ A: 通常是由于上下文不一致导致,可以在对话日志中,找到 | | | | | --- | --- | --- | | ![](/imgs/image-85.png) | ![](/imgs/image-86.png) | ![](/imgs/image-87.png) | +在针对知识库的回答要求里有, 要给它配置提示词,不然他就是默认的,默认的里面就有该语法。 + +## 工作流操作:一个工作流,以一个问题分类节点开始,根据不同的分类导入到不同的分支,访问相应的知识库和AI对话,AI对话返回内容后,怎么样不进入问题分类节点,而是将问题到知识库搜索,然后把历史记录一起作为背景再次AI查询。 + +做个判断器,如果是初次开始对话也就是历史记录为0,就走问题分类;不为零直接走知识库和ai。 + +## 实时对话,设置 fastgpt 定时,比如每隔 3000MS 去拿一次 webhook发送过来的消息到AI页面 + +定时执行没有这么高频率的去拿信息的,想要实现在企微里面的实时对话的机器人, +目前通过低代码的工作流构建应该是不行的,只能自己写代码,然后去调用 FastGPT 的 APIKey 回复。企业微信似乎没有提供「自动监听」群聊消息的接口(或是通过 at 机器人这种触发消息推送)。应该只能发消息给应用,接收这个 https://developer.work.weixin.qq.com/document/path/90238 文档中的消息推送实现实时对话。或者是定时去拿群聊消息,通过这个文档所示的接口https://developer.work.weixin.qq.com/document/path/98914,然后用这个接口 https://developer.work.weixin.qq.com/document/path/90248 去推送消息。 + +## 工作流连接数据库 + +工作流提供该连接数据库功能,用这个数据库连接的 plugin 可以实现 text2SQL,但是相对危险,不建议做写入等操作。 + +![](/imgs/quizApp1.png) + +## 关于循环体,协助理解循环体的循环条件和终止条件、循环的方式,循环体内参数调用后、在循环体内属于是局部作用域的参数还是全局作用域的参数 + +可理解为 for 函数,传一个数组,每个数据都执行一次。 + +## 公式无法正常显示 + +添加相关提示词,引导模型按 Markdown 输出公式 + +```bash +Latex inline: \(x^2\) +Latex block: $$e=mc^2$$ +``` diff --git a/docSite/content/zh-cn/docs/faq/dataset.md b/docSite/content/zh-cn/docs/faq/dataset.md index 589b57160660..a5b90340ad4a 100644 --- a/docSite/content/zh-cn/docs/faq/dataset.md +++ b/docSite/content/zh-cn/docs/faq/dataset.md @@ -16,6 +16,25 @@ weight: 910 * **文件处理模型**:用于数据处理的【增强处理】和【问答拆分】。在【增强处理】中,生成相关问题和摘要,在【问答拆分】中执行问答对生成。 * **索引模型**:用于向量化,即通过对文本数据进行处理和组织,构建出一个能够快速查询的数据结构。 +## 知识库支持Excel类文件的导入 + +xlsx等都可以上传的,不止支持CSV。 + +## 知识库tokens的计算方式 + +统一按gpt3.5标准。 + +## 误删除重排模型后,重排模型怎么加入到fastgpt + +![](/imgs/dataset3.png) + +config.json文件里面配置后就可以勾选重排模型 + +## 线上平台上创建了应用和知识库,到期之后如果短期内不续费,数据是否会被清理。 + +免费版是三十天不登录后清空知识库,应用不会动。其他付费套餐到期后自动切免费版。 +![](/imgs/dataset4.png) + ## 基于知识库的查询,但是问题相关的答案过多。ai回答到一半就不继续回答。 FastGPT回复长度计算公式: @@ -37,7 +56,7 @@ FastGPT回复长度计算公式: ![](/imgs/dataset2.png) -1. 私有化部署的时候,后台配模型参数,可以在配置最大上文时候,预留一些空间,比如 128000 的模型,可以只配置 120000, 剩余的空间后续会被安排给输出 +另外私有化部署的时候,后台配模型参数,可以在配置最大上文时,预留一些空间,比如 128000 的模型,可以只配置 120000, 剩余的空间后续会被安排给输出 ## 受到模型上下文的限制,有时候达不到聊天记录的轮次,连续对话字数过多就会报上下文不够的错误。 @@ -61,4 +80,4 @@ FastGPT回复长度计算公式: ![](/imgs/dataset2.png) -1. 私有化部署的时候,后台配模型参数,可以在配置最大上文时候,预留一些空间,比如 128000 的模型,可以只配置 120000, 剩余的空间后续会被安排给输出 \ No newline at end of file +另外,私有化部署的时候,后台配模型参数,可以在配置最大上文时,预留一些空间,比如 128000 的模型,可以只配置 120000, 剩余的空间后续会被安排给输出。 \ No newline at end of file diff --git a/docSite/content/zh-cn/docs/faq/other.md b/docSite/content/zh-cn/docs/faq/other.md index b8adf64f2a18..09de91a1e59d 100644 --- a/docSite/content/zh-cn/docs/faq/other.md +++ b/docSite/content/zh-cn/docs/faq/other.md @@ -8,4 +8,14 @@ weight: 918 ## oneapi 官网是哪个 -只有开源的 README,没官网,GitHub: https://github.com/songquanpeng/one-api \ No newline at end of file +只有开源的 README,没官网,GitHub: https://github.com/songquanpeng/one-api + +## 想做多用户和多chat key + +![](/imgs/other1.png) +![](/imgs/other2.png) + +多用户问题:只能采取二开或者商业版 + +多chat key问题:1. 私有化部署情况下,oneapi解决。2. Saas或者商业版中,可以为每个团队设置单独的key。 +![](/imgs/other3.png) diff --git a/docSite/content/zh-cn/docs/guide/knowledge_base/lark_dataset.md b/docSite/content/zh-cn/docs/guide/knowledge_base/lark_dataset.md index 8c7a8ab55b2d..8631b0f0dbc8 100644 --- a/docSite/content/zh-cn/docs/guide/knowledge_base/lark_dataset.md +++ b/docSite/content/zh-cn/docs/guide/knowledge_base/lark_dataset.md @@ -22,10 +22,11 @@ FastGPT v4.8.16 版本开始,商业版用户支持飞书知识库导入,用 ## 2. 配置应用权限 -创建应用后,进入应用可以配置相关权限,这里需要增加两个权限: +创建应用后,进入应用可以配置相关权限,这里需要增加**3个权限**: 1. 获取云空间文件夹下的云文档清单 2. 查看新版文档 +3. 查看、评论、编辑和管理云空间中所有文件 ![alt text](/imgs/image-41.png) diff --git a/docSite/content/zh-cn/docs/guide/workbench/workflow/loop.md b/docSite/content/zh-cn/docs/guide/workbench/workflow/loop.md index 7b32a91cd6e5..7468e2f18cc8 100644 --- a/docSite/content/zh-cn/docs/guide/workbench/workflow/loop.md +++ b/docSite/content/zh-cn/docs/guide/workbench/workflow/loop.md @@ -1,6 +1,6 @@ --- -title: "循环执行" -description: "FastGPT 循环运行节点介绍和使用" +title: "批量运行" +description: "FastGPT 批量运行节点介绍和使用" icon: "input" draft: false toc: true @@ -9,15 +9,15 @@ weight: 260 ## 节点概述 -【**循环运行**】节点是 FastGPT V4.8.11 版本新增的一个重要功能模块。它允许工作流对数组类型的输入数据进行迭代处理,每次处理数组中的一个元素,并自动执行后续节点,直到完成整个数组的处理。 +【**批量运行**】节点是 FastGPT V4.8.11 版本新增的一个重要功能模块。它允许工作流对数组类型的输入数据进行迭代处理,每次处理数组中的一个元素,并自动执行后续节点,直到完成整个数组的处理。 这个节点的设计灵感来自编程语言中的循环结构,但以可视化的方式呈现。 -![循环运行节点](/imgs/fastgpt-loop-node.png) +![批量运行节点](/imgs/fastgpt-loop-node.png) > 在程序中,节点可以理解为一个个 Function 或者接口。可以理解为它就是一个**步骤**。将多个节点一个个拼接起来,即可一步步的去实现最终的 AI 输出。 -【**循环运行**】节点本质上也是一个 Function,它的主要职责是自动化地重复执行特定的工作流程。 +【**批量运行**】节点本质上也是一个 Function,它的主要职责是自动化地重复执行特定的工作流程。 ## 核心特性 @@ -41,9 +41,9 @@ weight: 260 ## 应用场景 -【**循环运行**】节点的主要作用是通过自动化的方式扩展工作流的处理能力,使 FastGPT 能够更好地处理批量任务和复杂的数据处理流程。特别是在处理大规模数据或需要多轮迭代的场景下,循环运行节点能显著提升工作流的效率和自动化程度。 +【**批量运行**】节点的主要作用是通过自动化的方式扩展工作流的处理能力,使 FastGPT 能够更好地处理批量任务和复杂的数据处理流程。特别是在处理大规模数据或需要多轮迭代的场景下,批量运行节点能显著提升工作流的效率和自动化程度。 -【**循环运行**】节点特别适合以下场景: +【**批量运行**】节点特别适合以下场景: 1. **批量数据处理** - 批量翻译文本 @@ -64,7 +64,7 @@ weight: 260 ### 输入参数设置 -【**循环运行**】节点需要配置两个核心输入参数: +【**批量运行**】节点需要配置两个核心输入参数: 1. **数组 (必填)**:接收一个数组类型的输入,可以是: - 字符串数组 (`Array`) @@ -95,7 +95,7 @@ weight: 260 ### 批量处理数组 -假设我们有一个包含多个文本的数组,需要对每个文本进行 AI 处理。这是循环运行节点最基础也最常见的应用场景。 +假设我们有一个包含多个文本的数组,需要对每个文本进行 AI 处理。这是批量运行节点最基础也最常见的应用场景。 #### 实现步骤 @@ -114,9 +114,9 @@ weight: 260 return { textArray: texts }; ``` -2. 配置循环运行节点 +2. 配置批量运行节点 - ![配置循环运行节点](/imgs/fastgpt-loop-node-example-2.png) + ![配置批量运行节点](/imgs/fastgpt-loop-node-example-2.png) - 数组输入:选择上一步代码运行节点的输出变量 `textArray`。 - 循环体内添加一个【AI 对话】节点,用于处理每个文本。这里我们输入的 prompt 为:`请将这段文本翻译成英文`。 @@ -128,7 +128,7 @@ weight: 260 ![运行流程](/imgs/fastgpt-loop-node-example-3.png) 1. 【代码运行】节点执行,生成测试数组 -2. 【循环运行】节点接收数组,开始遍历 +2. 【批量运行】节点接收数组,开始遍历 3. 对每个数组元素: - 【AI 对话】节点处理当前元素 - 【指定回复】节点输出翻译后的文本 @@ -144,7 +144,7 @@ weight: 260 - 需要维护上下文的连贯性 - 翻译质量需要多轮优化 -【**循环运行**】节点可以很好地解决这些问题。 +【**批量运行**】节点可以很好地解决这些问题。 #### 实现步骤 @@ -281,9 +281,9 @@ weight: 260 这里我们用到了 [Jina AI 开源的一个强大的正则表达式](https://x.com/JinaAI_/status/1823756993108304135),它能利用所有可能的边界线索和启发式方法来精确切分文本。 -2. 配置循环运行节点 +2. 配置批量运行节点 - ![配置循环运行节点](/imgs/fastgpt-loop-node-example-5.png) + ![配置批量运行节点](/imgs/fastgpt-loop-node-example-5.png) - 数组输入:选择上一步代码运行节点的输出变量 `chunks`。 - 循环体内添加一个【代码运行】节点,对源文本进行格式化。 diff --git a/docSite/content/zh-cn/docs/use-cases/app-cases/multi_turn_translation_bot.md b/docSite/content/zh-cn/docs/use-cases/app-cases/multi_turn_translation_bot.md index a3f51303bec5..b9b5363d93e6 100644 --- a/docSite/content/zh-cn/docs/use-cases/app-cases/multi_turn_translation_bot.md +++ b/docSite/content/zh-cn/docs/use-cases/app-cases/multi_turn_translation_bot.md @@ -212,7 +212,7 @@ export default async function (ctx: FunctionContext): Promise{ ![](/imgs/translate13.png) -## 循环执行 +## 批量运行 长文反思翻译比较关键的一个部分,就是对多个文本块进行循环反思翻译 diff --git a/docSite/content/zh-cn/docs/use-cases/app-cases/translate-subtitle-using-gpt.md b/docSite/content/zh-cn/docs/use-cases/app-cases/translate-subtitle-using-gpt.md index fa12d31cecf8..64703a7be214 100644 --- a/docSite/content/zh-cn/docs/use-cases/app-cases/translate-subtitle-using-gpt.md +++ b/docSite/content/zh-cn/docs/use-cases/app-cases/translate-subtitle-using-gpt.md @@ -91,9 +91,9 @@ weight: 604 这个过程不仅提高了效率,还最大限度地减少了人为错误的可能性。 -## 循环执行 +## 批量运行 -为了处理整个长字幕文件,我们需要一个循环执行机制。这是通过一个简单但有效的判断模块实现的: +为了处理整个长字幕文件,我们需要一个批量运行机制。这是通过一个简单但有效的判断模块实现的: 1. 检查当前翻译的文本块是否为最后一个。 2. 如果不是,则将工作流重定向到格式化原文本块节点。 diff --git a/packages/global/common/system/types/index.d.ts b/packages/global/common/system/types/index.d.ts index 90bcb7dfd108..64b7bd0a42ff 100644 --- a/packages/global/common/system/types/index.d.ts +++ b/packages/global/common/system/types/index.d.ts @@ -40,7 +40,7 @@ export type FastGPTConfigFileType = { export type FastGPTFeConfigsType = { show_emptyChat?: boolean; - register_method?: ['email' | 'phone']; + register_method?: ['email' | 'phone' | 'sync']; login_method?: ['email' | 'phone']; // Attention: login method is diffrent with oauth find_password_method?: ['email' | 'phone']; bind_notification_method?: ['email' | 'phone']; @@ -76,7 +76,6 @@ export type FastGPTFeConfigsType = { wecom?: { corpid?: string; agentid?: string; - secret?: string; }; microsoft?: { clientId?: string; diff --git a/packages/global/core/app/constants.ts b/packages/global/core/app/constants.ts index ba948503604d..a57a7a797633 100644 --- a/packages/global/core/app/constants.ts +++ b/packages/global/core/app/constants.ts @@ -33,7 +33,7 @@ export const defaultWhisperConfig: AppWhisperConfigType = { export const defaultQGConfig: AppQGConfigType = { open: false, model: 'gpt-4o-mini', - customPrompt: PROMPT_QUESTION_GUIDE + customPrompt: '' }; export const defaultChatInputGuideConfig = { diff --git a/packages/global/core/app/type.d.ts b/packages/global/core/app/type.d.ts index 86885c71e375..899628936ed9 100644 --- a/packages/global/core/app/type.d.ts +++ b/packages/global/core/app/type.d.ts @@ -12,8 +12,9 @@ import { TeamTagSchema as TeamTagsSchemaType } from '@fastgpt/global/support/use import { StoreEdgeItemType } from '../workflow/type/edge'; import { AppPermission } from '../../support/permission/app/controller'; import { ParentIdType } from '../../common/parentFolder/type'; -import { FlowNodeInputTypeEnum } from 'core/workflow/node/constant'; +import { FlowNodeInputTypeEnum } from '../../core/workflow/node/constant'; import { WorkflowTemplateBasicType } from '@fastgpt/global/core/workflow/type'; +import { SourceMemberType } from '../../support/user/type'; export type AppSchema = { _id: string; @@ -63,6 +64,7 @@ export type AppListItemType = { permission: AppPermission; inheritPermission?: boolean; private?: boolean; + sourceMember: SourceMemberType; }; export type AppDetailType = AppSchema & { diff --git a/packages/global/core/app/version.d.ts b/packages/global/core/app/version.d.ts index 178834ce4181..dbf0ae53f1f0 100644 --- a/packages/global/core/app/version.d.ts +++ b/packages/global/core/app/version.d.ts @@ -1,5 +1,7 @@ +import { TeamMemberStatusEnum } from 'support/user/team/constant'; import { StoreEdgeItemType } from '../workflow/type/edge'; import { AppChatConfigType, AppSchema } from './type'; +import { SourceMemberType } from 'support/user/type'; export type AppVersionSchemaType = { _id: string; @@ -20,4 +22,5 @@ export type VersionListItemType = { time: Date; isPublish: boolean | undefined; tmbId: string; + sourceMember: SourceMemberType; }; diff --git a/packages/global/core/dataset/apiDataset.d.ts b/packages/global/core/dataset/apiDataset.d.ts index 1d64a8a59e8b..94c036886559 100644 --- a/packages/global/core/dataset/apiDataset.d.ts +++ b/packages/global/core/dataset/apiDataset.d.ts @@ -5,6 +5,7 @@ export type APIFileItem = { type: 'file' | 'folder'; updateTime: Date; createTime: Date; + hasChild?: boolean; }; export type APIFileServer = { diff --git a/packages/global/core/dataset/type.d.ts b/packages/global/core/dataset/type.d.ts index 7a772f78d481..70d3211d1b8c 100644 --- a/packages/global/core/dataset/type.d.ts +++ b/packages/global/core/dataset/type.d.ts @@ -11,6 +11,7 @@ import { import { DatasetPermission } from '../../support/permission/dataset/controller'; import { Permission } from '../../support/permission/controller'; import { APIFileServer, FeishuServer, YuqueServer } from './apiDataset'; +import { SourceMemberType } from 'support/user/type'; export type DatasetSchemaType = { _id: string; @@ -165,6 +166,7 @@ export type DatasetListItemType = { vectorModel: VectorModelItemType; inheritPermission: boolean; private?: boolean; + sourceMember?: SourceMemberType; }; export type DatasetItemType = Omit & { diff --git a/packages/global/core/dataset/utils.ts b/packages/global/core/dataset/utils.ts index 5f25b230fc79..d8e6564ace2a 100644 --- a/packages/global/core/dataset/utils.ts +++ b/packages/global/core/dataset/utils.ts @@ -34,7 +34,7 @@ export function getSourceNameIcon({ } } catch (error) {} - return 'file/fill/manual'; + return 'file/fill/file'; } /* get dataset data default index */ diff --git a/packages/global/core/workflow/constants.ts b/packages/global/core/workflow/constants.ts index d93c7ada3a4f..175f0c19dd06 100644 --- a/packages/global/core/workflow/constants.ts +++ b/packages/global/core/workflow/constants.ts @@ -152,6 +152,7 @@ export enum NodeInputKeyEnum { datasetSearchExtensionModel = 'datasetSearchExtensionModel', datasetSearchExtensionBg = 'datasetSearchExtensionBg', collectionFilterMatch = 'collectionFilterMatch', + authTmbId = 'authTmbId', // concat dataset datasetQuoteList = 'system_datasetQuoteList', diff --git a/packages/global/core/workflow/runtime/type.d.ts b/packages/global/core/workflow/runtime/type.d.ts index 7084740b17c0..995302b65ed8 100644 --- a/packages/global/core/workflow/runtime/type.d.ts +++ b/packages/global/core/workflow/runtime/type.d.ts @@ -41,6 +41,10 @@ export type ChatDispatchProps = { teamId: string; tmbId: string; // App tmbId }; + runningUserInfo: { + teamId: string; + tmbId: string; + }; uid: string; // Who run this workflow chatId?: string; diff --git a/packages/global/core/workflow/template/system/datasetSearch.ts b/packages/global/core/workflow/template/system/datasetSearch.ts index 69e15a679843..2d117ada06eb 100644 --- a/packages/global/core/workflow/template/system/datasetSearch.ts +++ b/packages/global/core/workflow/template/system/datasetSearch.ts @@ -89,6 +89,13 @@ export const DatasetSearchModule: FlowNodeTemplateType = { valueType: WorkflowIOValueTypeEnum.string, value: '' }, + { + key: NodeInputKeyEnum.authTmbId, + renderTypeList: [FlowNodeInputTypeEnum.hidden], + label: '', + valueType: WorkflowIOValueTypeEnum.boolean, + value: false + }, { ...Input_Template_UserChatInput, toolDescription: i18nT('workflow:content_to_search') diff --git a/packages/global/core/workflow/type/node.d.ts b/packages/global/core/workflow/type/node.d.ts index d0bc5b9197fb..691fc96c9a89 100644 --- a/packages/global/core/workflow/type/node.d.ts +++ b/packages/global/core/workflow/type/node.d.ts @@ -89,6 +89,7 @@ export type NodeTemplateListItemType = { hasTokenFee?: boolean; // 是否配置积分 instructions?: string; // 使用说明 courseUrl?: string; // 教程链接 + sourceMember?: SourceMember; }; export type NodeTemplateListType = { diff --git a/packages/global/support/user/team/controller.d.ts b/packages/global/support/user/team/controller.d.ts index 98a64af30359..fb331d0de226 100644 --- a/packages/global/support/user/team/controller.d.ts +++ b/packages/global/support/user/team/controller.d.ts @@ -12,6 +12,7 @@ export type CreateTeamProps = { avatar?: string; defaultTeam?: boolean; memberName?: string; + memberAvatar?: string; }; export type UpdateTeamProps = Omit & { name?: string; diff --git a/packages/global/support/user/team/org/constant.ts b/packages/global/support/user/team/org/constant.ts index 370cdad82901..c5aea21abb34 100644 --- a/packages/global/support/user/team/org/constant.ts +++ b/packages/global/support/user/team/org/constant.ts @@ -5,8 +5,6 @@ export const OrgMemberCollectionName = 'team_org_members'; export const getOrgChildrenPath = (org: OrgSchemaType) => `${org.path}/${org.pathId}`; -// export enum OrgMemberRole { -// owner = 'owner', -// admin = 'admin', -// member = 'member' -// } +export enum SyncOrgSourceEnum { + wecom = 'wecom' +} diff --git a/packages/global/support/user/team/org/type.d.ts b/packages/global/support/user/team/org/type.d.ts index ca4a73292272..b742ece4858e 100644 --- a/packages/global/support/user/team/org/type.d.ts +++ b/packages/global/support/user/team/org/type.d.ts @@ -13,6 +13,7 @@ type OrgSchemaType = { }; type OrgMemberSchemaType = { + _id: string; teamId: string; orgId: string; tmbId: string; @@ -20,6 +21,6 @@ type OrgMemberSchemaType = { type OrgType = Omit & { avatar: string; - members: OrgMemberSchemaType[]; permission: TeamPermission; + members: OrgMemberSchemaType[]; }; diff --git a/packages/global/support/user/team/type.d.ts b/packages/global/support/user/team/type.d.ts index 9c7dc12b42ba..6f063576f8eb 100644 --- a/packages/global/support/user/team/type.d.ts +++ b/packages/global/support/user/team/type.d.ts @@ -44,6 +44,7 @@ export type TeamMemberSchema = { name: string; role: `${TeamMemberRoleEnum}`; status: `${TeamMemberStatusEnum}`; + avatar: string; defaultTeam: boolean; }; diff --git a/packages/global/support/user/type.d.ts b/packages/global/support/user/type.d.ts index 0f8729266d76..13f6dd52281a 100644 --- a/packages/global/support/user/type.d.ts +++ b/packages/global/support/user/type.d.ts @@ -1,12 +1,12 @@ import { TeamPermission } from '../permission/user/controller'; import { UserStatusEnum } from './constant'; +import { TeamMemberStatusEnum } from './team/constant'; import { TeamTmbItemType } from './team/type'; export type UserModelSchema = { _id: string; username: string; password: string; - avatar: string; promotionRate: number; inviterId?: string; openaiKey: string; @@ -22,7 +22,7 @@ export type UserModelSchema = { export type UserType = { _id: string; username: string; - avatar: string; + avatar: string; // it should be team member's avatar after 4.8.18 timezone: string; promotionRate: UserModelSchema['promotionRate']; team: TeamTmbItemType; @@ -30,3 +30,9 @@ export type UserType = { notificationAccount?: string; permission: TeamPermission; }; + +export type SourceMemberType = { + name: string; + avatar: string; + status: `${TeamMemberStatusEnum}`; +}; diff --git a/packages/plugins/register.ts b/packages/plugins/register.ts index 832d39628be4..3e00f18a5866 100644 --- a/packages/plugins/register.ts +++ b/packages/plugins/register.ts @@ -12,7 +12,8 @@ const staticPluginList = [ 'DingTalkWebhook', 'WeWorkWebhook', 'google', - 'bing' + 'bing', + 'delay' ]; // Run in worker thread (Have npm packages) const packagePluginList = [ @@ -28,8 +29,7 @@ const packagePluginList = [ 'databaseConnection', 'Doc2X', 'Doc2X/PDF2text', - 'searchXNG', - 'sleep' + 'searchXNG' ]; export const list = [...staticPluginList, ...packagePluginList]; diff --git a/packages/plugins/src/delay/index.ts b/packages/plugins/src/delay/index.ts new file mode 100644 index 000000000000..2e89a837d469 --- /dev/null +++ b/packages/plugins/src/delay/index.ts @@ -0,0 +1,18 @@ +import { delay } from '@fastgpt/global/common/system/utils'; + +type Props = { + ms: number; +}; +type Response = Promise; + +const main = async ({ ms }: Props): Response => { + if (typeof ms !== 'number' || ms <= 0 || ms > 300000) { + return ms; + } + + await delay(ms); + + return ms; +}; + +export default main; diff --git a/packages/plugins/src/sleep/template.json b/packages/plugins/src/delay/template.json similarity index 73% rename from packages/plugins/src/sleep/template.json rename to packages/plugins/src/delay/template.json index 45579f06c08e..2678d9480b28 100644 --- a/packages/plugins/src/sleep/template.json +++ b/packages/plugins/src/delay/template.json @@ -1,11 +1,11 @@ { "author": "collin", "version": "4817", - "name": "流程暂停", + "name": "流程等待", "avatar": "core/workflow/template/sleep", - "intro": "让工作流暂停指定时间后运行", + "intro": "让工作流等待指定时间后运行", "showStatus": true, - "weight": 10, + "weight": 1, "isTool": true, "templateType": "tools", @@ -20,22 +20,19 @@ "flowNodeType": "pluginInput", "showStatus": false, "position": { - "x": 616.4226348688949, - "y": -165.05298493910115 + "x": 627.6352390819724, + "y": -165.05298493910118 }, "version": "481", "inputs": [ { - "renderTypeList": [ - "numberInput", - "reference" - ], + "renderTypeList": ["numberInput", "reference"], "selectedTypeIndex": 0, "valueType": "number", "canEdit": true, - "key": "ms", - "label": "ms", - "description": "需要暂停的时间,毫秒", + "key": "延迟时长", + "label": "延迟时长", + "description": "需要暂停的时间,单位毫秒", "defaultValue": 1000, "list": [ { @@ -47,8 +44,8 @@ "canSelectFile": true, "canSelectImg": true, "required": true, - "toolDescription": "需要暂停的时间,毫秒", - "max": 999999999999, + "toolDescription": "需要暂停的时间,单位毫秒", + "max": 300000, "min": 1 } ], @@ -56,8 +53,8 @@ { "id": "ms", "valueType": "number", - "key": "ms", - "label": "ms", + "key": "延迟时长", + "label": "延迟时长", "type": "hidden" } ] @@ -70,15 +67,13 @@ "flowNodeType": "pluginOutput", "showStatus": false, "position": { - "x": 1925.5772573010433, - "y": -131.55298493910115 + "x": 1921.839722563351, + "y": -160.05298493910115 }, "version": "481", "inputs": [ { - "renderTypeList": [ - "reference" - ], + "renderTypeList": ["reference"], "valueType": "any", "canEdit": true, "key": "result", @@ -86,10 +81,7 @@ "isToolOutput": true, "description": "", "required": true, - "value": [ - "zCJC6zw7c14i", - "httpRawResponse" - ] + "value": ["zCJC6zw7c14i", "httpRawResponse"] } ], "outputs": [] @@ -116,16 +108,14 @@ "flowNodeType": "httpRequest468", "showStatus": true, "position": { - "x": 1152.535395637613, - "y": -433.21496011686054 + "x": 1154.4041630064592, + "y": -455.0529849391012 }, "version": "481", "inputs": [ { "key": "system_addInputParam", - "renderTypeList": [ - "addInputParam" - ], + "renderTypeList": ["addInputParam"], "valueType": "dynamic", "label": "", "required": false, @@ -157,9 +147,7 @@ }, { "key": "system_httpMethod", - "renderTypeList": [ - "custom" - ], + "renderTypeList": ["custom"], "valueType": "string", "label": "", "value": "POST", @@ -171,9 +159,7 @@ }, { "key": "system_httpTimeout", - "renderTypeList": [ - "custom" - ], + "renderTypeList": ["custom"], "valueType": "number", "label": "", "value": 30, @@ -187,24 +173,20 @@ }, { "key": "system_httpReqUrl", - "renderTypeList": [ - "hidden" - ], + "renderTypeList": ["hidden"], "valueType": "string", "label": "", "description": "common:core.module.input.description.Http Request Url", "placeholder": "https://api.ai.com/getInventory", "required": false, - "value": "sleep", + "value": "delay", "valueDesc": "", "debugLabel": "", "toolDescription": "" }, { "key": "system_httpHeader", - "renderTypeList": [ - "custom" - ], + "renderTypeList": ["custom"], "valueType": "any", "value": [], "label": "", @@ -217,9 +199,7 @@ }, { "key": "system_httpParams", - "renderTypeList": [ - "hidden" - ], + "renderTypeList": ["hidden"], "valueType": "any", "value": [], "label": "", @@ -231,9 +211,7 @@ }, { "key": "system_httpJsonBody", - "renderTypeList": [ - "hidden" - ], + "renderTypeList": ["hidden"], "valueType": "any", "value": "{\n\"ms\": {{$pluginInput.ms$}}\n}", "label": "", @@ -245,9 +223,7 @@ }, { "key": "system_httpFormBody", - "renderTypeList": [ - "hidden" - ], + "renderTypeList": ["hidden"], "valueType": "any", "value": [], "label": "", @@ -259,9 +235,7 @@ }, { "key": "system_httpContentType", - "renderTypeList": [ - "hidden" - ], + "renderTypeList": ["hidden"], "valueType": "string", "value": "json", "label": "", @@ -338,32 +312,7 @@ } ], "chatConfig": { - "welcomeText": "", - "variables": [], - "questionGuide": { - "open": false, - "model": "gpt-4o-mini", - "customPrompt": "You are an AI assistant tasked with predicting the user's next question based on the conversation history. Your goal is to generate 3 potential questions that will guide the user to continue the conversation. When generating these questions, adhere to the following rules:\n\n1. Use the same language as the user's last question in the conversation history.\n2. Keep each question under 20 characters in length.\n\nAnalyze the conversation history provided to you and use it as context to generate relevant and engaging follow-up questions. Your predictions should be logical extensions of the current topic or related areas that the user might be interested in exploring further.\n\nRemember to maintain consistency in tone and style with the existing conversation while providing diverse options for the user to choose from. Your goal is to keep the conversation flowing naturally and help the user delve deeper into the subject matter or explore related topics." - }, - "ttsConfig": { - "type": "web" - }, - "whisperConfig": { - "open": false, - "autoSend": false, - "autoTTSResponse": false - }, - "chatInputGuide": { - "open": false, - "textList": [], - "customUrl": "" - }, - "instruction": "", - "autoExecute": { - "open": false, - "defaultPrompt": "" - }, - "_id": "6784ba2c227f92f6c723ead0" + "welcomeText": "" } } -} \ No newline at end of file +} diff --git a/packages/plugins/src/sleep/index.ts b/packages/plugins/src/sleep/index.ts deleted file mode 100644 index 8f5e9de7eb13..000000000000 --- a/packages/plugins/src/sleep/index.ts +++ /dev/null @@ -1,17 +0,0 @@ -type Props = { - ms: number; -}; -type Response = Promise<{ - result: any; -}>; - -const main = async ({ ms }: Props): Response => { - await new Promise((resolve) => { - setTimeout(resolve, ms); - }); - return { - result: ms - }; -}; - -export default main; diff --git a/packages/service/common/api/pagination.ts b/packages/service/common/api/pagination.ts new file mode 100644 index 000000000000..052cd41f58ad --- /dev/null +++ b/packages/service/common/api/pagination.ts @@ -0,0 +1,21 @@ +import { CommonErrEnum } from '@fastgpt/global/common/error/code/common'; +import { ApiRequestProps } from '../../type/next'; + +export function parsePaginationRequest(req: ApiRequestProps) { + const { + pageSize = 10, + pageNum = 1, + offset = 0 + } = Object.keys(req.body).includes('pageSize') + ? req.body + : Object.keys(req.query).includes('pageSize') + ? req.query + : {}; + if (!pageSize || (pageNum === undefined && offset === undefined)) { + throw new Error(CommonErrEnum.missingParams); + } + return { + pageSize: Number(pageSize), + offset: offset ? Number(offset) : (Number(pageNum) - 1) * Number(pageSize) + }; +} diff --git a/packages/service/common/middle/cors.ts b/packages/service/common/middle/cors.ts index 73b294162603..294dbe5a4426 100644 --- a/packages/service/common/middle/cors.ts +++ b/packages/service/common/middle/cors.ts @@ -2,7 +2,7 @@ import type { NextApiResponse, NextApiRequest } from 'next'; import NextCors from 'nextjs-cors'; export async function withNextCors(req: NextApiRequest, res: NextApiResponse) { - const methods = ['GET', 'eHEAD', 'PUT', 'PATCH', 'POST', 'DELETE']; + const methods = ['GET', 'HEAD', 'PUT', 'PATCH', 'POST', 'DELETE']; const allowedOrigins = process.env.ALLOWED_ORIGINS?.split(','); const origin = req.headers.origin; diff --git a/packages/service/core/dataset/apiDataset/api.ts b/packages/service/core/dataset/apiDataset/api.ts index 19818b32fc71..162d685f59b0 100644 --- a/packages/service/core/dataset/apiDataset/api.ts +++ b/packages/service/core/dataset/apiDataset/api.ts @@ -99,7 +99,13 @@ export const useApiDatasetRequest = ({ apiServer }: { apiServer: APIFileServer } if (files.some((file) => !file.id || !file.name || typeof file.type === 'undefined')) { return Promise.reject('Invalid file data format'); } - return files; + + const formattedFiles = files.map((file) => ({ + ...file, + hasChild: file.type === 'folder' + })); + + return formattedFiles; }; const getFileContent = async ({ teamId, apiFileId }: { teamId: string; apiFileId: string }) => { diff --git a/packages/service/core/dataset/search/controller.ts b/packages/service/core/dataset/search/controller.ts index 0de4c2884d3b..7a3ea9a3f796 100644 --- a/packages/service/core/dataset/search/controller.ts +++ b/packages/service/core/dataset/search/controller.ts @@ -284,7 +284,7 @@ export async function searchDatasetData(props: SearchDatasetDataProps) { { _id: { $in: collectionIdList } }, - '_id name fileId rawLink externalFileId externalFileUrl', + '_id name fileId rawLink apiFileId externalFileId externalFileUrl', { ...readFromSecondary } ).lean() ]); @@ -525,7 +525,7 @@ export async function searchDatasetData(props: SearchDatasetDataProps) { { _id: { $in: searchResults.map((item) => item.collectionId) } }, - '_id name fileId rawLink externalFileId externalFileUrl', + '_id name fileId rawLink apiFileId externalFileId externalFileUrl', { ...readFromSecondary } ).lean() ]); diff --git a/packages/service/core/dataset/utils.ts b/packages/service/core/dataset/utils.ts new file mode 100644 index 000000000000..4f805ccb158f --- /dev/null +++ b/packages/service/core/dataset/utils.ts @@ -0,0 +1,39 @@ +import { getTmbInfoByTmbId } from '../../support/user/team/controller'; +import { getResourcePermission } from '../../support/permission/controller'; +import { PerResourceTypeEnum } from '@fastgpt/global/support/permission/constant'; +import { DatasetPermission } from '@fastgpt/global/support/permission/dataset/controller'; + +// TODO: 需要优化成批量获取权限 +export const filterDatasetsByTmbId = async ({ + datasetIds, + tmbId +}: { + datasetIds: string[]; + tmbId: string; +}) => { + const { teamId, permission: tmbPer } = await getTmbInfoByTmbId({ tmbId }); + + // First get all permissions + const permissions = await Promise.all( + datasetIds.map(async (datasetId) => { + const per = await getResourcePermission({ + teamId, + tmbId, + resourceId: datasetId, + resourceType: PerResourceTypeEnum.dataset + }); + + if (per === undefined) return false; + + const datasetPer = new DatasetPermission({ + per, + isOwner: tmbPer.isOwner + }); + + return datasetPer.hasReadPer; + }) + ); + + // Then filter datasetIds based on permissions + return datasetIds.filter((_, index) => permissions[index]); +}; diff --git a/packages/service/core/workflow/dispatch/dataset/search.ts b/packages/service/core/workflow/dispatch/dataset/search.ts index 8a40c8e28203..92067dacb3a3 100644 --- a/packages/service/core/workflow/dispatch/dataset/search.ts +++ b/packages/service/core/workflow/dispatch/dataset/search.ts @@ -17,6 +17,7 @@ import { ChatNodeUsageType } from '@fastgpt/global/support/wallet/bill/type'; import { checkTeamReRankPermission } from '../../../../support/permission/teamLimit'; import { MongoDataset } from '../../../dataset/schema'; import { i18nT } from '../../../../../web/i18n/utils'; +import { filterDatasetsByTmbId } from '../../../dataset/utils'; type DatasetSearchProps = ModuleDispatchProps<{ [NodeInputKeyEnum.datasetSelectList]: SelectedDatasetType; @@ -29,6 +30,7 @@ type DatasetSearchProps = ModuleDispatchProps<{ [NodeInputKeyEnum.datasetSearchExtensionModel]: string; [NodeInputKeyEnum.datasetSearchExtensionBg]: string; [NodeInputKeyEnum.collectionFilterMatch]: string; + [NodeInputKeyEnum.authTmbId]: boolean; }>; export type DatasetSearchResponse = DispatchNodeResultType<{ [NodeOutputKeyEnum.datasetQuoteQA]: SearchDataResponseItemType[]; @@ -39,6 +41,7 @@ export async function dispatchDatasetSearch( ): Promise { const { runningAppInfo: { teamId }, + runningUserInfo: { tmbId }, histories, node, params: { @@ -52,7 +55,8 @@ export async function dispatchDatasetSearch( datasetSearchUsingExtensionQuery, datasetSearchExtensionModel, datasetSearchExtensionBg, - collectionFilterMatch + collectionFilterMatch, + authTmbId = false } } = props as DatasetSearchProps; @@ -64,18 +68,20 @@ export async function dispatchDatasetSearch( return Promise.reject(i18nT('common:core.chat.error.Select dataset empty')); } + const emptyResult = { + quoteQA: [], + [DispatchNodeResponseKeyEnum.nodeResponse]: { + totalPoints: 0, + query: '', + limit, + searchMode + }, + nodeDispatchUsages: [], + [DispatchNodeResponseKeyEnum.toolResponses]: [] + }; + if (!userChatInput) { - return { - quoteQA: [], - [DispatchNodeResponseKeyEnum.nodeResponse]: { - totalPoints: 0, - query: '', - limit, - searchMode - }, - nodeDispatchUsages: [], - [DispatchNodeResponseKeyEnum.toolResponses]: [] - }; + return emptyResult; } // query extension @@ -83,13 +89,24 @@ export async function dispatchDatasetSearch( ? getLLMModel(datasetSearchExtensionModel) : undefined; - const { concatQueries, rewriteQuery, aiExtensionResult } = await datasetSearchQueryExtension({ - query: userChatInput, - extensionModel, - extensionBg: datasetSearchExtensionBg, - histories: getHistories(6, histories) - }); - + const [{ concatQueries, rewriteQuery, aiExtensionResult }, datasetIds] = await Promise.all([ + datasetSearchQueryExtension({ + query: userChatInput, + extensionModel, + extensionBg: datasetSearchExtensionBg, + histories: getHistories(6, histories) + }), + authTmbId + ? filterDatasetsByTmbId({ + datasetIds: datasets.map((item) => item.datasetId), + tmbId + }) + : Promise.resolve(datasets.map((item) => item.datasetId)) + ]); + + if (datasetIds.length === 0) { + return emptyResult; + } // console.log(concatQueries, rewriteQuery, aiExtensionResult); // get vector @@ -110,7 +127,7 @@ export async function dispatchDatasetSearch( model: vectorModel.model, similarity, limit, - datasetIds: datasets.map((item) => item.datasetId), + datasetIds, searchMode, usingReRank: usingReRank && (await checkTeamReRankPermission(teamId)), collectionFilterMatch diff --git a/packages/service/core/workflow/dispatch/plugin/run.ts b/packages/service/core/workflow/dispatch/plugin/run.ts index f7eb09af5e7b..a81a6a8677cc 100644 --- a/packages/service/core/workflow/dispatch/plugin/run.ts +++ b/packages/service/core/workflow/dispatch/plugin/run.ts @@ -72,6 +72,7 @@ export const dispatchRunPlugin = async (props: RunPluginProps): Promise({ + .populate<{ tmb: TeamMemberSchema }>({ path: 'tmb', - select: 'name userId role', - populate: { - path: 'user', - select: 'avatar' - } + select: 'name userId avatar' }) .lean(), MongoResourcePermission.find({ diff --git a/packages/service/support/permission/org/orgSchema.ts b/packages/service/support/permission/org/orgSchema.ts index 45030c4a664b..94fa531e466f 100644 --- a/packages/service/support/permission/org/orgSchema.ts +++ b/packages/service/support/permission/org/orgSchema.ts @@ -47,14 +47,11 @@ export const OrgSchema = new Schema( OrgSchema.virtual('members', { ref: OrgMemberCollectionName, localField: '_id', - foreignField: 'orgId' + foreignField: 'orgId', + match: function (this: OrgSchemaType) { + return { teamId: this.teamId }; + } }); -// OrgSchema.virtual('permission', { -// ref: ResourcePermissionCollectionName, -// localField: '_id', -// foreignField: 'orgId', -// justOne: true -// }); try { OrgSchema.index({ diff --git a/packages/service/support/user/controller.ts b/packages/service/support/user/controller.ts index cf432b214cc1..ab28aac72c63 100644 --- a/packages/service/support/user/controller.ts +++ b/packages/service/support/user/controller.ts @@ -41,7 +41,7 @@ export async function getUserDetail({ return { _id: user._id, username: user.username, - avatar: user.avatar, + avatar: tmb.avatar, timezone: user.timezone, promotionRate: user.promotionRate, team: tmb, diff --git a/packages/service/support/user/schema.ts b/packages/service/support/user/schema.ts index 10094ee6fbbb..78234fd34bb4 100644 --- a/packages/service/support/user/schema.ts +++ b/packages/service/support/user/schema.ts @@ -3,7 +3,6 @@ const { Schema } = connectionMongo; import { hashStr } from '@fastgpt/global/common/string/tools'; import type { UserModelSchema } from '@fastgpt/global/support/user/type'; import { UserStatusEnum, userStatusMap } from '@fastgpt/global/support/user/constant'; -import { getRandomUserAvatar } from '@fastgpt/global/support/user/utils'; export const userCollectionName = 'users'; @@ -33,11 +32,6 @@ const UserSchema = new Schema({ type: Date, default: () => new Date() }, - avatar: { - type: String, - default: () => getRandomUserAvatar() - }, - promotionRate: { type: Number, default: 15 @@ -62,7 +56,10 @@ const UserSchema = new Schema({ ref: userCollectionName }, fastgpt_sem: Object, - sourceDomain: String + sourceDomain: String, + + /** @deprecated */ + avatar: String }); try { diff --git a/packages/service/support/user/team/controller.ts b/packages/service/support/user/team/controller.ts index 451e02f5b680..d48d9bf0b2b5 100644 --- a/packages/service/support/user/team/controller.ts +++ b/packages/service/support/user/team/controller.ts @@ -37,7 +37,7 @@ async function getTeamMember(match: Record): Promise getRandomUserAvatar() + }, name: { type: String, default: 'Member' @@ -39,7 +44,6 @@ const TeamMemberSchema = new Schema({ // Abandoned role: { type: String - // enum: Object.keys(TeamMemberRoleMap) // disable enum validation for old data } }); diff --git a/packages/service/support/user/utils.ts b/packages/service/support/user/utils.ts index fc1fef631403..574ab7853dbb 100644 --- a/packages/service/support/user/utils.ts +++ b/packages/service/support/user/utils.ts @@ -1,4 +1,7 @@ +import { SourceMemberType } from '@fastgpt/global/support/user/type'; import { MongoTeam } from './team/teamSchema'; +import { MongoTeamMember } from './team/teamMemberSchema'; +import { ClientSession } from '../../common/mongo'; /* export dataset limit */ export const updateExportDatasetLimit = async (teamId: string) => { @@ -67,3 +70,41 @@ export const checkWebSyncLimit = async ({ return Promise.reject(`每个团队,每 ${limitMinutes} 分钟仅使用一次同步功能。`); } }; + +/** + * This function will add a property named sourceMember to the list passed in. + * @param list The list to add the sourceMember property to. [TmbId] property is required. + * @error If member is not found, this item will be skipped. + * @returns The list with the sourceMember property added. + */ +export async function addSourceMember({ + list, + session +}: { + list: T[]; + session?: ClientSession; +}): Promise> { + if (!Array.isArray(list)) return []; + + const tmbList = await MongoTeamMember.find( + { + _id: { $in: list.map((item) => String(item.tmbId)) } + }, + 'tmbId name avatar status', + { + session + } + ).lean(); + + return list + .map((item) => { + const tmb = tmbList.find((tmb) => String(tmb._id) === String(item.tmbId)); + if (!tmb) return; + + return { + ...item, + sourceMember: { name: tmb.name, avatar: tmb.avatar, status: tmb.status } + }; + }) + .filter(Boolean) as Array; +} diff --git a/packages/web/common/fetch/type.d.ts b/packages/web/common/fetch/type.d.ts index 0153db9561b8..7f2ea1fbf272 100644 --- a/packages/web/common/fetch/type.d.ts +++ b/packages/web/common/fetch/type.d.ts @@ -1,8 +1,13 @@ -export type PaginationProps = T & { - offset: number; - pageSize: number; -}; -export type PaginationResponse = { +import { RequireOnlyOne } from '@fastgpt/global/common/type/utils'; + +type PaginationProps = T & { + pageSize: number | string; +} & RequireOnlyOne<{ + offset: number | string; + pageNum: number | string; + }>; + +type PaginationResponse = { total: number; list: T[]; }; diff --git a/packages/web/components/common/Avatar/AvatarGroup.tsx b/packages/web/components/common/Avatar/AvatarGroup.tsx index 2c26b9a9d7af..199373f05a3f 100644 --- a/packages/web/components/common/Avatar/AvatarGroup.tsx +++ b/packages/web/components/common/Avatar/AvatarGroup.tsx @@ -23,7 +23,7 @@ function AvatarGroup({ {avatars.slice(0, max).map((avatar, index) => ( 0 ? 'absolute' : 'relative'} left={index > 0 ? `${index * 15}px` : 0} diff --git a/packages/web/components/common/MySelect/index.tsx b/packages/web/components/common/MySelect/index.tsx index 7d503d21ba57..c9eb6230dc45 100644 --- a/packages/web/components/common/MySelect/index.tsx +++ b/packages/web/components/common/MySelect/index.tsx @@ -21,7 +21,15 @@ import type { ButtonProps, MenuItemProps } from '@chakra-ui/react'; import MyIcon from '../Icon'; import { useRequest2 } from '../../../hooks/useRequest'; import MyDivider from '../MyDivider'; +import { useScrollPagination } from '../../../hooks/useScrollPagination'; +/** 选择组件 Props 类型 + * value: 选中的值 + * placeholder: 占位符 + * list: 列表数据 + * isLoading: 是否加载中 + * ScrollData: 分页滚动数据控制器 [useScrollPagination] 的返回值 + * */ export type SelectProps = ButtonProps & { value?: T; placeholder?: string; @@ -34,6 +42,7 @@ export type SelectProps = ButtonProps & { }[]; isLoading?: boolean; onchange?: (val: T) => any | Promise; + ScrollData?: ReturnType['ScrollData']; }; const MySelect = ( @@ -44,6 +53,7 @@ const MySelect = ( list = [], onchange, isLoading = false, + ScrollData, ...props }: SelectProps, ref: ForwardedRef<{ @@ -87,6 +97,46 @@ const MySelect = ( const isSelecting = loading || isLoading; + const ListRender = useMemo(() => { + return ( + <> + {list.map((item, i) => ( + + { + if (onChange && value !== item.value) { + onChange(item.value); + } + }} + whiteSpace={'pre-wrap'} + fontSize={'sm'} + display={'block'} + > + {item.label} + {item.description && ( + + {item.description} + + )} + + {item.showBorder && } + + ))} + + ); + }, [list, value]); + return ( ( maxH={'40vh'} overflowY={'auto'} > - {list.map((item, i) => ( - - { - if (onChange && value !== item.value) { - onChange(item.value); - } - }} - whiteSpace={'pre-wrap'} - fontSize={'sm'} - display={'block'} - > - {item.label} - {item.description && ( - - {item.description} - - )} - - {item.showBorder && } - - ))} + {ScrollData ? {ListRender} : ListRender} diff --git a/packages/web/components/common/UserBox/index.tsx b/packages/web/components/common/UserBox/index.tsx new file mode 100644 index 000000000000..8e659fa1ad33 --- /dev/null +++ b/packages/web/components/common/UserBox/index.tsx @@ -0,0 +1,23 @@ +import { Box, HStack, type StackProps } from '@chakra-ui/react'; +import { SourceMemberType } from '@fastgpt/global/support/user/type'; +import React from 'react'; +import Avatar from '../Avatar'; +import { useTranslation } from 'next-i18next'; +import Tag from '../Tag'; + +export type UserBoxProps = { + sourceMember: SourceMemberType; + avatarSize?: string; +} & StackProps; +function UserBox({ sourceMember, avatarSize = '1.25rem', ...props }: UserBoxProps) { + const { t } = useTranslation(); + return ( + + + {sourceMember.name} + {sourceMember.status === 'leave' && {t('common:user_leaved')}} + + ); +} + +export default React.memo(UserBox); diff --git a/packages/web/hooks/usePagination.tsx b/packages/web/hooks/usePagination.tsx index 477c3267237b..5e65d11f34e3 100644 --- a/packages/web/hooks/usePagination.tsx +++ b/packages/web/hooks/usePagination.tsx @@ -4,7 +4,6 @@ import { ArrowBackIcon, ArrowForwardIcon } from '@chakra-ui/icons'; import { useTranslation } from 'next-i18next'; import { useToast } from './useToast'; import { getErrText } from '@fastgpt/global/common/error/utils'; - import { useBoolean, useLockFn, @@ -14,37 +13,33 @@ import { useThrottleEffect } from 'ahooks'; -const thresholdVal = 200; +import { PaginationProps, PaginationResponse } from '../common/fetch/type'; -type PagingData = { - pageNum: number; - pageSize: number; - data: T[]; - total?: number; -}; +const thresholdVal = 200; -export function usePagination({ - api, - pageSize = 10, - params = {}, - defaultRequest = true, - type = 'button', - onChange, - refreshDeps, - scrollLoadType = 'bottom', - EmptyTip -}: { - api: (data: any) => Promise>; - pageSize?: number; - params?: Record; - defaultRequest?: boolean; - type?: 'button' | 'scroll'; - onChange?: (pageNum: number) => void; - refreshDeps?: any[]; - throttleWait?: number; - scrollLoadType?: 'top' | 'bottom'; - EmptyTip?: React.JSX.Element; -}) { +export function usePagination( + api: (data: PaginationProps) => Promise>, + { + pageSize = 10, + params, + defaultRequest = true, + type = 'button', + onChange, + refreshDeps, + scrollLoadType = 'bottom', + EmptyTip + }: { + pageSize?: number; + params?: DataT; + defaultRequest?: boolean; + type?: 'button' | 'scroll'; + onChange?: (pageNum: number) => void; + refreshDeps?: any[]; + throttleWait?: number; + scrollLoadType?: 'top' | 'bottom'; + EmptyTip?: React.JSX.Element; + } +) { const { toast } = useToast(); const { t } = useTranslation(); @@ -64,7 +59,7 @@ export function usePagination({ setTrue(); try { - const res: PagingData = await api({ + const res = await api({ pageNum: num, pageSize, ...params @@ -93,13 +88,13 @@ export function usePagination({ ); } - setData((prevData) => (num === 1 ? res.data : [...res.data, ...prevData])); + setData((prevData) => (num === 1 ? res.list : [...res.list, ...prevData])); adjustScrollPosition(); } else { - setData((prevData) => (num === 1 ? res.data : [...prevData, ...res.data])); + setData((prevData) => (num === 1 ? res.list : [...prevData, ...res.list])); } } else { - setData(res.data); + setData(res.list); } onChange?.(num); diff --git a/packages/web/hooks/useScrollPagination.tsx b/packages/web/hooks/useScrollPagination.tsx index 83a5ad11b350..4ecc77c44f14 100644 --- a/packages/web/hooks/useScrollPagination.tsx +++ b/packages/web/hooks/useScrollPagination.tsx @@ -16,7 +16,7 @@ import MyBox from '../components/common/MyBox'; import { useTranslation } from 'next-i18next'; type ItemHeight = (index: number, data: T) => number; -const thresholdVal = 200; +const thresholdVal = 100; export type ScrollListType = ({ children, @@ -269,8 +269,10 @@ export function useScrollPagination< ({ children, ScrollContainerRef, + isLoading, ...props }: { + isLoading?: boolean; children: ReactNode; ScrollContainerRef?: RefObject; } & BoxProps) => { @@ -302,7 +304,7 @@ export function useScrollPagination< ); return ( - + {scrollLoadType === 'top' && total > 0 && isLoading && ( {t('common:common.is_requesting')} @@ -325,7 +327,7 @@ export function useScrollPagination< )} {isEmpty && EmptyTip} - + ); } ); diff --git a/packages/web/i18n/en/common.json b/packages/web/i18n/en/common.json index 1feb36b2a3d0..9871c55280e7 100644 --- a/packages/web/i18n/en/common.json +++ b/packages/web/i18n/en/common.json @@ -39,6 +39,7 @@ "classification": "Classification", "click_to_resume": "Click to Resume", "code_editor": "Code Editor", + "code_error.account_error": "Incorrect account name or password", "code_error.app_error.invalid_app_type": "Invalid Application Type", "code_error.app_error.invalid_owner": "Unauthorized Application Owner", "code_error.app_error.not_exist": "Application Does Not Exist", @@ -95,7 +96,6 @@ "code_error.team_error.website_sync_not_enough": "Unauthorized to Use Website Sync", "code_error.token_error_code.403": "Invalid Login Status, Please Re-login", "code_error.user_error.balance_not_enough": "Insufficient Account Balance", - "code_error.account_error": "Incorrect account name or password", "code_error.user_error.bin_visitor_guest": "You Are Currently a Guest, Unauthorized to Operate", "code_error.user_error.un_auth_user": "User Not Found", "common.Action": "Action", @@ -1273,6 +1273,7 @@ "user.team.role.Visitor": "visitor", "user.team.role.writer": "writable member", "user.type": "Type", + "user_leaved": "Leaved", "verification": "Verification", "workflow.template.communication": "Communication", "xx_search_result": "{{key}} Search Results", diff --git a/packages/web/i18n/en/workflow.json b/packages/web/i18n/en/workflow.json index 2f2912a24405..223dda9b5c37 100644 --- a/packages/web/i18n/en/workflow.json +++ b/packages/web/i18n/en/workflow.json @@ -13,6 +13,8 @@ "append_application_reply_to_history_as_new_context": "Append the application's reply to the history as new context", "application_call": "Application Call", "assigned_reply": "Assigned Reply", + "auth_tmb_id": "Auth member", + "auth_tmb_id_tip": "After it is turned on, when the application is released to the outside world, the knowledge base will be filtered based on whether the user has permission to the knowledge base.\n\nIf it is not enabled, the configured knowledge base will be searched directly without permission filtering.", "can_not_loop": "This node can't loop.", "choose_another_application_to_call": "Select another application to call", "classification_result": "Classification Result", diff --git a/packages/web/i18n/zh-CN/account_team.json b/packages/web/i18n/zh-CN/account_team.json index e562ef7c9989..91587efc289d 100644 --- a/packages/web/i18n/zh-CN/account_team.json +++ b/packages/web/i18n/zh-CN/account_team.json @@ -35,5 +35,8 @@ "user_team_invite_member": "邀请成员", "user_team_leave_team": "离开团队", "user_team_leave_team_failed": "离开团队失败", - "waiting": "待接受" + "waiting": "待接受", + "sync_immediately": "立即同步", + "sync_member_failed": "同步成员失败", + "sync_member_success": "同步成员成功" } diff --git a/packages/web/i18n/zh-CN/common.json b/packages/web/i18n/zh-CN/common.json index bbbfcb4edfc0..fa46e013b3ab 100644 --- a/packages/web/i18n/zh-CN/common.json +++ b/packages/web/i18n/zh-CN/common.json @@ -43,6 +43,7 @@ "classification": "分类", "click_to_resume": "点击恢复", "code_editor": "代码编辑", + "code_error.account_error": "账号名或密码错误", "code_error.app_error.invalid_app_type": "错误的应用类型", "code_error.app_error.invalid_owner": "非法的应用所有者", "code_error.app_error.not_exist": "应用不存在", @@ -99,7 +100,6 @@ "code_error.team_error.website_sync_not_enough": "无权使用Web站点同步~", "code_error.token_error_code.403": "登录状态无效,请重新登录", "code_error.user_error.balance_not_enough": "账号余额不足~", - "code_error.account_error": "账号名或密码错误", "code_error.user_error.bin_visitor_guest": "您当前身份为游客,无权操作", "code_error.user_error.un_auth_user": "找不到该用户", "common.Action": "操作", @@ -1268,6 +1268,7 @@ "user.team.role.Visitor": "访客", "user.team.role.writer": "可写成员", "user.type": "类型", + "user_leaved": "已离开", "verification": "验证", "workflow.template.communication": "通信", "xx_search_result": "{{key}} 的搜索结果", diff --git a/packages/web/i18n/zh-CN/workflow.json b/packages/web/i18n/zh-CN/workflow.json index 42a62528d913..37c4dcabb2eb 100644 --- a/packages/web/i18n/zh-CN/workflow.json +++ b/packages/web/i18n/zh-CN/workflow.json @@ -13,6 +13,8 @@ "append_application_reply_to_history_as_new_context": "将该应用回复内容拼接到历史记录中,作为新的上下文返回", "application_call": "应用调用", "assigned_reply": "指定回复", + "auth_tmb_id": "使用者鉴权", + "auth_tmb_id_tip": "开启后,对外发布该应用时,还会根据用户是否有该知识库权限进行知识库过滤。\n若未开启,则直接按配置的知识库进行检索,不进行权限过滤。", "can_not_loop": "该节点不支持循环嵌套", "choose_another_application_to_call": "选择一个其他应用进行调用", "classification_result": "分类结果", diff --git a/packages/web/i18n/zh-Hant/common.json b/packages/web/i18n/zh-Hant/common.json index 2d3cfc13ac17..efa9bbf465fb 100644 --- a/packages/web/i18n/zh-Hant/common.json +++ b/packages/web/i18n/zh-Hant/common.json @@ -39,6 +39,7 @@ "classification": "分類", "click_to_resume": "點選繼續", "code_editor": "程式碼編輯器", + "code_error.account_error": "帳號名稱或密碼錯誤", "code_error.app_error.invalid_app_type": "無效的應用程式類型", "code_error.app_error.invalid_owner": "非法的應用程式擁有者", "code_error.app_error.not_exist": "應用程式不存在", @@ -95,7 +96,6 @@ "code_error.team_error.website_sync_not_enough": "無權使用網站同步", "code_error.token_error_code.403": "登入狀態無效,請重新登入", "code_error.user_error.balance_not_enough": "帳戶餘額不足", - "code_error.account_error": "帳號名稱或密碼錯誤", "code_error.user_error.bin_visitor_guest": "您目前身份為訪客,無權操作", "code_error.user_error.un_auth_user": "找不到此使用者", "common.Action": "操作", @@ -1273,6 +1273,7 @@ "user.team.role.Visitor": "訪客", "user.team.role.writer": "可寫入成員", "user.type": "類型", + "user_leaved": "已離開", "verification": "驗證", "workflow.template.communication": "通訊", "xx_search_result": "{{key}} 的搜尋結果", diff --git a/packages/web/i18n/zh-Hant/workflow.json b/packages/web/i18n/zh-Hant/workflow.json index 98603c05a75a..b022ab4cae2b 100644 --- a/packages/web/i18n/zh-Hant/workflow.json +++ b/packages/web/i18n/zh-Hant/workflow.json @@ -13,6 +13,8 @@ "append_application_reply_to_history_as_new_context": "將應用程式的回覆附加到歷史紀錄中,作為新的脈絡", "application_call": "應用程式呼叫", "assigned_reply": "指定回覆", + "auth_tmb_id": "使用者鑑權", + "auth_tmb_id_tip": "開啟後,對外發布應用程式時,也會根據使用者是否有該知識庫權限進行知識庫過濾。\n\n若未開啟,則直接按配置的知識庫進行檢索,不進行權限過濾。", "can_not_loop": "這個節點不能迴圈。", "choose_another_application_to_call": "選擇另一個應用程式來呼叫", "classification_result": "分類結果", diff --git a/projects/app/.env.template b/projects/app/.env.template index b8f4ca0a8e7f..71ed204df3ac 100644 --- a/projects/app/.env.template +++ b/projects/app/.env.template @@ -57,3 +57,5 @@ WORKFLOW_MAX_LOOP_TIMES=50 # CHAT_LOG_INTERVAL=10000 # # 日志来源ID前缀 # CHAT_LOG_SOURCE_ID_PREFIX=fastgpt- +# 自定义跨域,不配置时,默认都允许跨域(逗号分割) +ALLOWED_ORIGINS= \ No newline at end of file diff --git a/projects/app/package.json b/projects/app/package.json index c735c9f6af3a..86370fcda458 100644 --- a/projects/app/package.json +++ b/projects/app/package.json @@ -1,6 +1,6 @@ { "name": "app", - "version": "4.8.18", + "version": "4.8.19", "private": false, "scripts": { "dev": "next dev", diff --git a/projects/app/src/components/core/app/InputGuideConfig.tsx b/projects/app/src/components/core/app/InputGuideConfig.tsx index 13c3920ba2c0..91a83d24af2e 100644 --- a/projects/app/src/components/core/app/InputGuideConfig.tsx +++ b/projects/app/src/components/core/app/InputGuideConfig.tsx @@ -54,7 +54,7 @@ const InputGuideConfig = ({ onChange: (e: ChatInputGuideConfigType) => void; }) => { const { t } = useTranslation(); - const { chatT, commonT } = useI18n(); + const { chatT } = useI18n(); const { isOpen, onOpen, onClose } = useDisclosure(); const { isOpen: isOpenLexiconConfig, @@ -220,7 +220,7 @@ const LexiconConfigModal = ({ appId, onClose }: { appId: string; onClose: () => }); const { run: createNewData, loading: isCreating } = useRequest2( - (textList: string[]) => { + async (textList: string[]) => { if (textList.filter(Boolean).length === 0) { return Promise.resolve(); } diff --git a/projects/app/src/components/core/chat/ChatContainer/ChatBox/Provider.tsx b/projects/app/src/components/core/chat/ChatContainer/ChatBox/Provider.tsx index d7b4a470f280..3b1ff2f14ce1 100644 --- a/projects/app/src/components/core/chat/ChatContainer/ChatBox/Provider.tsx +++ b/projects/app/src/components/core/chat/ChatContainer/ChatBox/Provider.tsx @@ -29,8 +29,6 @@ export type ChatProviderProps = { outLinkAuthData?: OutLinkChatAuthProps; chatType: 'log' | 'chat' | 'share' | 'team'; - showRawSource: boolean; - showNodeStatus: boolean; }; type useChatStoreType = ChatProviderProps & { @@ -132,8 +130,6 @@ const Provider = ({ chatId, outLinkAuthData = {}, chatType = 'chat', - showRawSource, - showNodeStatus, children, ...props }: ChatProviderProps & { @@ -249,9 +245,7 @@ const Provider = ({ chatId, outLinkAuthData, getHistoryResponseData, - chatType, - showRawSource, - showNodeStatus + chatType }; return {children}; diff --git a/projects/app/src/components/core/chat/ChatContainer/ChatBox/components/ChatItem.tsx b/projects/app/src/components/core/chat/ChatContainer/ChatBox/components/ChatItem.tsx index 0dd714cd56bd..a603216d0220 100644 --- a/projects/app/src/components/core/chat/ChatContainer/ChatBox/components/ChatItem.tsx +++ b/projects/app/src/components/core/chat/ChatContainer/ChatBox/components/ChatItem.tsx @@ -25,6 +25,7 @@ import { isEqual } from 'lodash'; import { useSystem } from '@fastgpt/web/hooks/useSystem'; import { formatTimeToChatItemTime } from '@fastgpt/global/common/string/time'; import dayjs from 'dayjs'; +import { ChatItemContext } from '@/web/core/chat/context/chatItemContext'; const colorMap = { [ChatStatusEnum.loading]: { @@ -139,7 +140,7 @@ const ChatItem = (props: Props) => { const isChatting = useContextSelector(ChatBoxContext, (v) => v.isChatting); const chatType = useContextSelector(ChatBoxContext, (v) => v.chatType); - const showNodeStatus = useContextSelector(ChatBoxContext, (v) => v.showNodeStatus); + const showNodeStatus = useContextSelector(ChatItemContext, (v) => v.showNodeStatus); const isChatLog = chatType === 'log'; const { copyData } = useCopyData(); diff --git a/projects/app/src/components/core/chat/ChatContainer/ChatBox/components/QuoteModal.tsx b/projects/app/src/components/core/chat/ChatContainer/ChatBox/components/QuoteModal.tsx index 091125ad72d5..a8457d0b0f55 100644 --- a/projects/app/src/components/core/chat/ChatContainer/ChatBox/components/QuoteModal.tsx +++ b/projects/app/src/components/core/chat/ChatContainer/ChatBox/components/QuoteModal.tsx @@ -9,19 +9,16 @@ import RawSourceBox from '@/components/core/dataset/RawSourceBox'; import { getWebReqUrl } from '@fastgpt/web/common/system/utils'; import { useContextSelector } from 'use-context-selector'; import { ChatBoxContext } from '../Provider'; +import { ChatItemContext } from '@/web/core/chat/context/chatItemContext'; const QuoteModal = ({ rawSearch = [], onClose, - canEditDataset, - showRawSource, chatItemId, metadata }: { rawSearch: SearchDataResponseItemType[]; onClose: () => void; - canEditDataset: boolean; - showRawSource: boolean; chatItemId: string; metadata?: { collectionId: string; @@ -47,6 +44,11 @@ const QuoteModal = ({ chatItemId, ...(v.outLinkAuthData || {}) })); + const showRawSource = useContextSelector(ChatItemContext, (v) => v.isShowReadRawSource); + const showRouteToDatasetDetail = useContextSelector( + ChatItemContext, + (v) => v.showRouteToDatasetDetail + ); return ( <> @@ -71,12 +73,7 @@ const QuoteModal = ({ } > - + @@ -87,14 +84,10 @@ export default QuoteModal; export const QuoteList = React.memo(function QuoteList({ chatItemId, - rawSearch = [], - canEditDataset, - canViewSource + rawSearch = [] }: { chatItemId?: string; rawSearch: SearchDataResponseItemType[]; - canEditDataset: boolean; - canViewSource: boolean; }) { const theme = useTheme(); @@ -104,6 +97,11 @@ export const QuoteList = React.memo(function QuoteList({ chatId: v.chatId, ...(v.outLinkAuthData || {}) })); + const showRawSource = useContextSelector(ChatItemContext, (v) => v.isShowReadRawSource); + const showRouteToDatasetDetail = useContextSelector( + ChatItemContext, + (v) => v.showRouteToDatasetDetail + ); return ( <> @@ -120,8 +118,8 @@ export const QuoteList = React.memo(function QuoteList({ > diff --git a/projects/app/src/components/core/chat/ChatContainer/ChatBox/components/ResponseTags.tsx b/projects/app/src/components/core/chat/ChatContainer/ChatBox/components/ResponseTags.tsx index eda19e7e2ca3..7be7111e5b8b 100644 --- a/projects/app/src/components/core/chat/ChatContainer/ChatBox/components/ResponseTags.tsx +++ b/projects/app/src/components/core/chat/ChatContainer/ChatBox/components/ResponseTags.tsx @@ -49,7 +49,6 @@ const ResponseTags = ({ const [quoteFolded, setQuoteFolded] = useState(true); const chatType = useContextSelector(ChatBoxContext, (v) => v.chatType); - const showRawSource = useContextSelector(ChatBoxContext, (v) => v.showRawSource); const notSharePage = useMemo(() => chatType !== 'share', [chatType]); const { @@ -251,8 +250,6 @@ const ResponseTags = ({ setQuoteModalData(undefined)} /> )} diff --git a/projects/app/src/components/core/chat/ChatContainer/ChatBox/index.tsx b/projects/app/src/components/core/chat/ChatContainer/ChatBox/index.tsx index 939d152aac60..54534b622e73 100644 --- a/projects/app/src/components/core/chat/ChatContainer/ChatBox/index.tsx +++ b/projects/app/src/components/core/chat/ChatContainer/ChatBox/index.tsx @@ -18,7 +18,6 @@ import { Box, Checkbox } from '@chakra-ui/react'; import { EventNameEnum, eventBus } from '@/web/common/utils/eventbus'; import { chats2GPTMessages } from '@fastgpt/global/core/chat/adapt'; import { useForm } from 'react-hook-form'; -import { useRouter } from 'next/router'; import { useSystemStore } from '@/web/common/system/useSystemStore'; import { useTranslation } from 'next-i18next'; import { diff --git a/projects/app/src/components/core/chat/components/WholeResponseModal.tsx b/projects/app/src/components/core/chat/components/WholeResponseModal.tsx index 8dfc6a3204c4..d6078e193315 100644 --- a/projects/app/src/components/core/chat/components/WholeResponseModal.tsx +++ b/projects/app/src/components/core/chat/components/WholeResponseModal.tsx @@ -249,14 +249,7 @@ export const WholeResponseContent = ({ {activeModule.quoteList && activeModule.quoteList.length > 0 && ( - } + rawDom={} /> )} diff --git a/projects/app/src/components/core/dataset/RawSourceBox.tsx b/projects/app/src/components/core/dataset/RawSourceBox.tsx index 7957adfe0ee7..4b860d8dc0aa 100644 --- a/projects/app/src/components/core/dataset/RawSourceBox.tsx +++ b/projects/app/src/components/core/dataset/RawSourceBox.tsx @@ -5,7 +5,6 @@ import { useTranslation } from 'next-i18next'; import { getCollectionSourceAndOpen } from '@/web/core/dataset/hooks/readCollectionSource'; import { getSourceNameIcon } from '@fastgpt/global/core/dataset/utils'; import MyIcon from '@fastgpt/web/components/common/Icon'; -import { useI18n } from '@/web/context/I18n'; import type { readCollectionSourceBody } from '@/pages/api/core/dataset/collection/read'; type Props = BoxProps & @@ -33,7 +32,6 @@ const RawSourceBox = ({ ...props }: Props) => { const { t } = useTranslation(); - const { fileT } = useI18n(); const canPreview = !!sourceId && canView; @@ -51,7 +49,7 @@ const RawSourceBox = ({ return ( void }) { const { t } = useTranslation(); - const { loadAndGetTeamMembers } = useUserStore(); - const [inputValue, setInputValue] = React.useState(''); - const { data: teamMembers = [] } = useRequest2(loadAndGetTeamMembers, { - manual: false + const { data: teamMembers, ScrollData } = useScrollPagination(getTeamMembers, { + pageSize: 15 }); + const memberList = teamMembers.filter((item) => { return item.memberName.includes(inputValue); }); @@ -101,11 +101,6 @@ export function ChangeOwnerModal({ onOpenMemberListMenu(); setSelectedMember(null); }} - // onBlur={() => { - // setTimeout(() => { - // onCloseMemberListMenu(); - // }, 10); - // }} {...(selectedMember && { pl: '10' })} /> @@ -123,26 +118,28 @@ export function ChangeOwnerModal({ maxH={'300px'} overflow={'auto'} > - {memberList.map((item) => ( - { - setInputValue(item.memberName); - setSelectedMember(item); - onCloseMemberListMenu(); - }} - > - - - {item.memberName} - - - ))} + + {memberList.map((item) => ( + { + setInputValue(item.memberName); + setSelectedMember(item); + onCloseMemberListMenu(); + }} + > + + + {item.memberName} + + + ))} + )} diff --git a/projects/app/src/components/support/permission/MemberManager/MemberModal.tsx b/projects/app/src/components/support/permission/MemberManager/MemberModal.tsx index e51049b33a1a..4d77f9ef8a48 100644 --- a/projects/app/src/components/support/permission/MemberManager/MemberModal.tsx +++ b/projects/app/src/components/support/permission/MemberManager/MemberModal.tsx @@ -33,6 +33,10 @@ import { ParentTreePathItemType } from '@fastgpt/global/common/parentFolder/type import { OrgType } from '@fastgpt/global/support/user/team/org/type'; import { useContextSelector } from 'use-context-selector'; import { CollaboratorContext } from './context'; +import { getTeamMembers } from '@/web/support/user/team/api'; +import { getGroupList } from '@/web/support/user/team/group/api'; +import { getOrgList } from '@/web/support/user/team/org/api'; +import { useScrollPagination } from '@fastgpt/web/hooks/useScrollPagination'; const HoverBoxStyle = { bgColor: 'myGray.50', @@ -47,30 +51,27 @@ function MemberModal({ addPermissionOnly?: boolean; }) { const { t } = useTranslation(); - const { userInfo, loadAndGetTeamMembers, loadAndGetGroups, loadAndGetOrgs } = useUserStore(); - + const { userInfo } = useUserStore(); const collaboratorList = useContextSelector(CollaboratorContext, (v) => v.collaboratorList); - const [searchText, setSearchText] = useState(''); const [filterClass, setFilterClass] = useState<'member' | 'org' | 'group'>(); + const { data: members, ScrollData } = useScrollPagination(getTeamMembers, { + pageSize: 15 + }); - const { data: [members = [], groups = [], orgs = []] = [], loading: loadingMembersAndGroups } = - useRequest2( - async () => { - if (!userInfo?.team?.teamId) return [[], []]; - return Promise.all([ - loadAndGetTeamMembers(true), - loadAndGetGroups(true), - loadAndGetOrgs(true) - ]); - }, - { - manual: false, - refreshDeps: [userInfo?.team?.teamId] - } - ); + const { data: [groups = [], orgs = []] = [], loading: loadingGroupsAndOrgs } = useRequest2( + async () => { + if (!userInfo?.team?.teamId) return [[], []]; + return Promise.all([getGroupList(), getOrgList()]); + }, + { + manual: false, + refreshDeps: [userInfo?.team?.teamId] + } + ); const [parentPath, setParentPath] = useState(''); + const paths = useMemo(() => { const splitPath = parentPath.split('/').filter(Boolean); return splitPath @@ -212,7 +213,7 @@ function MemberModal({ h={'100%'} maxH={'90vh'} isCentered - isLoading={loadingMembersAndGroups} + isLoading={loadingGroupsAndOrgs} > + {/* Entry */} {!searchText && !filterClass && ( <> {entryList.current.map((item) => { @@ -298,142 +300,135 @@ function MemberModal({ )} - - {filterMembers.map((member) => { - const collaborator = collaboratorList?.find((v) => v.tmbId === member.tmbId); - const disabled = addOnly && collaborator !== undefined; - const onChange = () => { - if (disabled) return; - setSelectedMembers((state) => { - if (state.includes(member.tmbId)) { - return state.filter((v) => v !== member.tmbId); - } - return [...state, member.tmbId]; - }); - }; - return ( - - - - - {member.memberName} - - - - ); - })} - {filterOrgs.map((org) => { - const collaborator = collaboratorList?.find((v) => v.orgId === org._id); - const disabled = addOnly && collaborator !== undefined; - const onChange = () => { - if (disabled) return; - setSelectedOrgIdList((state) => { - if (state.includes(org._id)) { - return state.filter((v) => v !== org._id); - } - return [...state, org._id]; - }); - }; - return ( - - - - - {org.name} + {filterClass && ( + + {filterOrgs.map((org) => { + const onChange = () => { + setSelectedOrgIdList((state) => { + if (state.includes(org._id)) { + return state.filter((v) => v !== org._id); + } + return [...state, org._id]; + }); + }; + const collaborator = collaboratorList?.find((v) => v.orgId === org._id); + return ( + + + + + {org.name} + {org.count && ( + <> + + {org.count} + + + )} + + {org.count && ( - - {org.count} - + { + setParentPath(getOrgChildrenPath(org)); + }} + /> )} - - {org.count && ( - { - e.stopPropagation(); - setParentPath(getOrgChildrenPath(org)); - }} + ); + })} + {filterMembers.map((member) => { + const onChange = () => { + setSelectedMembers((state) => { + if (state.includes(member.tmbId)) { + return state.filter((v) => v !== member.tmbId); + } + return [...state, member.tmbId]; + }); + }; + const collaborator = collaboratorList?.find((v) => v.tmbId === member.tmbId); + return ( + + + + + {member.memberName} + + + + ); + })} + {filterGroups.map((group) => { + const onChange = () => { + setSelectedGroupIdList((state) => { + if (state.includes(group._id)) { + return state.filter((v) => v !== group._id); + } + return [...state, group._id]; + }); + }; + const collaborator = collaboratorList?.find((v) => v.groupId === group._id); + return ( + + - )} - - ); - })} - {filterGroups.map((group) => { - const collaborator = collaboratorList?.find((v) => v.groupId === group._id); - const disabled = addOnly && collaborator !== undefined; - const onChange = () => { - if (disabled) return; - setSelectedGroupIdList((state) => { - if (state.includes(group._id)) { - return state.filter((v) => v !== group._id); - } - return [...state, group._id]; - }); - }; - return ( - - - - - {group.name === DefaultGroupName ? userInfo?.team.teamName : group.name} - - - - ); - })} - + + + {group.name === DefaultGroupName ? userInfo?.team.teamName : group.name} + + + + ); + })} + + )} diff --git a/projects/app/src/global/core/api/appReq.d.ts b/projects/app/src/global/core/api/appReq.d.ts index 7668e5355fa8..59a039afc158 100644 --- a/projects/app/src/global/core/api/appReq.d.ts +++ b/projects/app/src/global/core/api/appReq.d.ts @@ -1,7 +1,7 @@ -import { RequestPaging } from '@/types'; +import { PaginationProps } from '@fastgpt/web/common/fetch/type'; -export type GetAppChatLogsParams = RequestPaging & { +export type GetAppChatLogsParams = PaginationProps<{ appId: string; dateStart: Date; dateEnd: Date; -}; +}>; diff --git a/projects/app/src/global/core/api/datasetReq.d.ts b/projects/app/src/global/core/api/datasetReq.d.ts index e896df0e2066..22c69146640f 100644 --- a/projects/app/src/global/core/api/datasetReq.d.ts +++ b/projects/app/src/global/core/api/datasetReq.d.ts @@ -3,24 +3,24 @@ import { DatasetCollectionTypeEnum, DatasetTypeEnum } from '@fastgpt/global/core/dataset/constants'; -import type { RequestPaging } from '@/types'; import { TrainingModeEnum } from '@fastgpt/global/core/dataset/constants'; import type { SearchTestItemType } from '@/types/core/dataset'; import { UploadChunkItemType } from '@fastgpt/global/core/dataset/type'; import { DatasetCollectionSchemaType } from '@fastgpt/global/core/dataset/type'; import { PermissionTypeEnum } from '@fastgpt/global/support/permission/constant'; import type { LLMModelItemType } from '@fastgpt/global/core/ai/model.d'; +import { PaginationProps } from '@fastgpt/web/common/fetch/type'; /* ===== dataset ===== */ /* ======= collections =========== */ -export type GetDatasetCollectionsProps = RequestPaging & { +export type GetDatasetCollectionsProps = PaginationProps<{ datasetId: string; parentId?: string; searchText?: string; filterTags?: string[]; simple?: boolean; selectFolder?: boolean; -}; +}>; /* ==== data ===== */ diff --git a/projects/app/src/pages/account/team/components/EditInfoModal.tsx b/projects/app/src/pageComponents/account/team/EditInfoModal.tsx similarity index 100% rename from projects/app/src/pages/account/team/components/EditInfoModal.tsx rename to projects/app/src/pageComponents/account/team/EditInfoModal.tsx diff --git a/projects/app/src/pages/account/team/components/GroupManage/GroupInfoModal.tsx b/projects/app/src/pageComponents/account/team/GroupManage/GroupInfoModal.tsx similarity index 96% rename from projects/app/src/pages/account/team/components/GroupManage/GroupInfoModal.tsx rename to projects/app/src/pageComponents/account/team/GroupManage/GroupInfoModal.tsx index c2fb98e065ee..9fd81a3105bf 100644 --- a/projects/app/src/pages/account/team/components/GroupManage/GroupInfoModal.tsx +++ b/projects/app/src/pageComponents/account/team/GroupManage/GroupInfoModal.tsx @@ -55,7 +55,7 @@ function GroupInfoModal({ onClose, editGroupId }: { onClose: () => void; editGro } ); - const { run: onCreate, loading: isLoadingCreate } = useRequest2( + const { runAsync: onCreate, loading: isLoadingCreate } = useRequest2( (data: GroupFormType) => { return postCreateGroup({ name: data.name, @@ -67,7 +67,7 @@ function GroupInfoModal({ onClose, editGroupId }: { onClose: () => void; editGro } ); - const { run: onUpdate, loading: isLoadingUpdate } = useRequest2( + const { runAsync: onUpdate, loading: isLoadingUpdate } = useRequest2( async (data: GroupFormType) => { if (!editGroupId) return; return putUpdateGroup({ diff --git a/projects/app/src/pages/account/team/components/GroupManage/GroupManageMember.tsx b/projects/app/src/pageComponents/account/team/GroupManage/GroupManageMember.tsx similarity index 90% rename from projects/app/src/pages/account/team/components/GroupManage/GroupManageMember.tsx rename to projects/app/src/pageComponents/account/team/GroupManage/GroupManageMember.tsx index 74e4be634dcf..95528e7ec800 100644 --- a/projects/app/src/pages/account/team/components/GroupManage/GroupManageMember.tsx +++ b/projects/app/src/pageComponents/account/team/GroupManage/GroupManageMember.tsx @@ -32,26 +32,26 @@ export type GroupFormType = { }[]; }; +// 1. Owner can not be deleted, toast +// 2. Owner/Admin can manage members +// 3. Owner can add/remove admins function GroupEditModal({ onClose, editGroupId }: { onClose: () => void; editGroupId?: string }) { - // 1. Owner can not be deleted, toast - // 2. Owner/Admin can manage members - // 3. Owner can add/remove admins const { t } = useTranslation(); const { userInfo } = useUserStore(); const { toast } = useToast(); - const [hoveredMemberId, setHoveredMemberId] = useState(undefined); - const { - members: allMembers, - refetchGroups, - groups, - refetchMembers - } = useContextSelector(TeamContext, (v) => v); + const groups = useContextSelector(TeamContext, (v) => v.groups); + const refetchGroups = useContextSelector(TeamContext, (v) => v.refetchGroups); const group = useMemo(() => { return groups.find((item) => item._id === editGroupId); }, [editGroupId, groups]); + const allMembers = useContextSelector(TeamContext, (v) => v.members); + const refetchMembers = useContextSelector(TeamContext, (v) => v.refetchMembers); + const MemberScrollData = useContextSelector(TeamContext, (v) => v.MemberScrollData); + const [hoveredMemberId, setHoveredMemberId] = useState(); const [members, setMembers] = useState(group?.members || []); + const [searchKey, setSearchKey] = useState(''); const filtered = useMemo(() => { return [ @@ -62,7 +62,7 @@ function GroupEditModal({ onClose, editGroupId }: { onClose: () => void; editGro ]; }, [searchKey, allMembers]); - const { run: onUpdate, loading: isLoadingUpdate } = useRequest2( + const { runAsync: onUpdate, loading: isLoadingUpdate } = useRequest2( async () => { if (!editGroupId || !members.length) return; return putUpdateGroup({ @@ -155,7 +155,7 @@ function GroupEditModal({ onClose, editGroupId }: { onClose: () => void; editGro setSearchKey(e.target.value); }} /> - + {filtered.map((member) => { return ( void; editGro ); })} - + {t('common:chosen') + ': ' + members.length} - + {members.map((member) => { return ( void; editGro ); })} - + - + + diff --git a/projects/app/src/pages/account/team/components/GroupManage/GroupTransferOwnerModal.tsx b/projects/app/src/pageComponents/account/team/GroupManage/GroupTransferOwnerModal.tsx similarity index 100% rename from projects/app/src/pages/account/team/components/GroupManage/GroupTransferOwnerModal.tsx rename to projects/app/src/pageComponents/account/team/GroupManage/GroupTransferOwnerModal.tsx diff --git a/projects/app/src/pages/account/team/components/GroupManage/index.tsx b/projects/app/src/pageComponents/account/team/GroupManage/index.tsx similarity index 89% rename from projects/app/src/pages/account/team/components/GroupManage/index.tsx rename to projects/app/src/pageComponents/account/team/GroupManage/index.tsx index ba25e90b673f..bd568d815431 100644 --- a/projects/app/src/pages/account/team/components/GroupManage/index.tsx +++ b/projects/app/src/pageComponents/account/team/GroupManage/index.tsx @@ -24,50 +24,43 @@ import { useUserStore } from '@/web/support/user/useUserStore'; import { useRequest2 } from '@fastgpt/web/hooks/useRequest'; import { deleteGroup } from '@/web/support/user/team/group/api'; import { DefaultGroupName } from '@fastgpt/global/support/user/team/group/constant'; -import MemberTag from '../../../../../components/support/user/team/Info/MemberTag'; +import MemberTag from '../../../../components/support/user/team/Info/MemberTag'; import MyTooltip from '@fastgpt/web/components/common/MyTooltip'; import dynamic from 'next/dynamic'; import { useState } from 'react'; import IconButton from '../OrgManage/IconButton'; +import { MemberGroupType } from '@fastgpt/global/support/permission/memberGroup/type'; const ChangeOwnerModal = dynamic(() => import('./GroupTransferOwnerModal')); const GroupInfoModal = dynamic(() => import('./GroupInfoModal')); -const ManageGroupMemberModal = dynamic(() => import('./GroupManageMember')); +const GroupManageMember = dynamic(() => import('./GroupManageMember')); function MemberTable({ Tabs }: { Tabs: React.ReactNode }) { const { t } = useTranslation(); const { userInfo } = useUserStore(); - const [editGroupId, setEditGroupId] = useState(); + const { groups, refetchGroups, members, refetchMembers } = useContextSelector( + TeamContext, + (v) => v + ); + + const [editGroup, setEditGroup] = useState(); const { isOpen: isOpenGroupInfo, onOpen: onOpenGroupInfo, onClose: onCloseGroupInfo } = useDisclosure(); - const { - isOpen: isOpenManageGroupMember, - onOpen: onOpenManageGroupMember, - onClose: onCloseManageGroupMember - } = useDisclosure(); - const onEditGroup = (groupId: string) => { - setEditGroupId(groupId); + + const onEditGroupInfo = (e: MemberGroupType) => { + setEditGroup(e); onOpenGroupInfo(); }; - const onManageMember = (groupId: string) => { - setEditGroupId(groupId); - onOpenManageGroupMember(); - }; const { ConfirmModal: ConfirmDeleteGroupModal, openConfirm: openDeleteGroupModal } = useConfirm({ type: 'delete', content: t('account_team:confirm_delete_group') }); - const { groups, refetchGroups, members, refetchMembers } = useContextSelector( - TeamContext, - (v) => v - ); - const { runAsync: delDeleteGroup } = useRequest2(deleteGroup, { onSuccess: () => { refetchGroups(); @@ -75,12 +68,21 @@ function MemberTable({ Tabs }: { Tabs: React.ReactNode }) { } }); + const { + isOpen: isOpenManageGroupMember, + onOpen: onOpenManageGroupMember, + onClose: onCloseManageGroupMember + } = useDisclosure(); + const onManageMember = (e: MemberGroupType) => { + setEditGroup(e); + onOpenManageGroupMember(); + }; + const hasGroupManagePer = (group: (typeof groups)[0]) => userInfo?.team.permission.hasManagePer || ['admin', 'owner'].includes( group.members.find((item) => item.tmbId === userInfo?.team.tmbId)?.role ?? '' ); - const isGroupOwner = (group: (typeof groups)[0]) => userInfo?.team.permission.hasManagePer || group.members.find((item) => item.role === 'owner')?.tmbId === userInfo?.team.tmbId; @@ -90,8 +92,8 @@ function MemberTable({ Tabs }: { Tabs: React.ReactNode }) { onOpen: onOpenChangeOwner, onClose: onCloseChangeOwner } = useDisclosure(); - const onChangeOwner = (groupId: string) => { - setEditGroupId(groupId); + const onChangeOwner = (e: MemberGroupType) => { + setEditGroup(e); onOpenChangeOwner(); }; @@ -173,7 +175,7 @@ function MemberTable({ Tabs }: { Tabs: React.ReactNode }) { v.avatar)} groupId={group._id} /> ) : hasGroupManagePer(group) ? ( - onManageMember(group._id)}> + onManageMember(group)}> members.find((m) => m.tmbId === v.tmbId)?.avatar ?? '' @@ -202,14 +204,14 @@ function MemberTable({ Tabs }: { Tabs: React.ReactNode }) { label: t('account_team:edit_info'), icon: 'edit', onClick: () => { - onEditGroup(group._id); + onEditGroupInfo(group); } }, { label: t('account_team:manage_member'), icon: 'support/team/group', onClick: () => { - onManageMember(group._id); + onManageMember(group); } }, ...(isGroupOwner(group) @@ -218,7 +220,7 @@ function MemberTable({ Tabs }: { Tabs: React.ReactNode }) { label: t('account_team:transfer_ownership'), icon: 'modal/changePer', onClick: () => { - onChangeOwner(group._id); + onChangeOwner(group); }, type: 'primary' as MenuItemType }, @@ -246,25 +248,25 @@ function MemberTable({ Tabs }: { Tabs: React.ReactNode }) { - {isOpenChangeOwner && editGroupId && ( - + {isOpenChangeOwner && editGroup && ( + )} {isOpenGroupInfo && ( { onCloseGroupInfo(); - setEditGroupId(undefined); + setEditGroup(undefined); }} - editGroupId={editGroupId} + editGroupId={editGroup?._id} /> )} - {isOpenManageGroupMember && ( - { onCloseManageGroupMember(); - setEditGroupId(undefined); + setEditGroup(undefined); }} - editGroupId={editGroupId} + editGroupId={editGroup._id} /> )} diff --git a/projects/app/src/pages/account/team/components/InviteModal.tsx b/projects/app/src/pageComponents/account/team/InviteModal.tsx similarity index 100% rename from projects/app/src/pages/account/team/components/InviteModal.tsx rename to projects/app/src/pageComponents/account/team/InviteModal.tsx diff --git a/projects/app/src/pages/account/team/components/MemberTable.tsx b/projects/app/src/pageComponents/account/team/MemberTable.tsx similarity index 53% rename from projects/app/src/pages/account/team/components/MemberTable.tsx rename to projects/app/src/pageComponents/account/team/MemberTable.tsx index 08b9af2bcfea..05eabd09bbf6 100644 --- a/projects/app/src/pages/account/team/components/MemberTable.tsx +++ b/projects/app/src/pageComponents/account/team/MemberTable.tsx @@ -13,7 +13,6 @@ import { Tr, useDisclosure } from '@chakra-ui/react'; -import { TeamMemberRoleEnum } from '@fastgpt/global/support/user/team/constant'; import { useTranslation } from 'next-i18next'; import { useUserStore } from '@/web/support/user/useUserStore'; import { useConfirm } from '@fastgpt/web/hooks/useConfirm'; @@ -30,6 +29,9 @@ import { useToast } from '@fastgpt/web/hooks/useToast'; import { TeamErrEnum } from '@fastgpt/global/common/error/code/team'; import { useRequest2 } from '@fastgpt/web/hooks/useRequest'; import { delLeaveTeam } from '@/web/support/user/team/api'; +import { postSyncMembers } from '@/web/support/user/api'; +import MyLoading from '@fastgpt/web/components/common/MyLoading'; +import { TeamMemberRoleEnum } from '@fastgpt/global/support/user/team/constant'; const InviteModal = dynamic(() => import('./InviteModal')); const TeamTagModal = dynamic(() => import('@/components/support/user/team/TeamTagModal')); @@ -40,8 +42,16 @@ function MemberTable({ Tabs }: { Tabs: React.ReactNode }) { const { userInfo, teamPlanStatus } = useUserStore(); const { feConfigs, setNotSufficientModalType } = useSystemStore(); - const { groups, refetchGroups, myTeams, refetchTeams, members, refetchMembers, onSwitchTeam } = - useContextSelector(TeamContext, (v) => v); + const { + groups, + refetchGroups, + myTeams, + refetchTeams, + members, + refetchMembers, + onSwitchTeam, + MemberScrollData + } = useContextSelector(TeamContext, (v) => v); const { isOpen: isOpenTeamTagsAsync, @@ -54,6 +64,8 @@ function MemberTable({ Tabs }: { Tabs: React.ReactNode }) { type: 'delete' }); + const isSyncMember = feConfigs.register_method?.includes('sync'); + const { runAsync: onLeaveTeam } = useRequest2( async () => { const defaultTeam = myTeams.find((item) => item.defaultTeam) || myTeams[0]; @@ -72,8 +84,17 @@ function MemberTable({ Tabs }: { Tabs: React.ReactNode }) { content: t('account_team:confirm_leave_team') }); + const { runAsync: onSyncMember, loading: isSyncing } = useRequest2(postSyncMembers, { + onSuccess() { + refetchMembers(); + }, + successToast: t('account_team:sync_member_success'), + errorToast: t('account_team:sync_member_failed') + }); + return ( <> + {isSyncing && } {Tabs} @@ -91,7 +112,21 @@ function MemberTable({ Tabs }: { Tabs: React.ReactNode }) { {t('account_team:label_sync')} )} - {userInfo?.team.permission.hasManagePer && ( + {userInfo?.team.permission.hasManagePer && isSyncMember && ( + + )} + {userInfo?.team.permission.hasManagePer && !isSyncMember && ( diff --git a/projects/app/src/pages/account/team/components/OrgManage/OrgMoveModal.tsx b/projects/app/src/pageComponents/account/team/OrgManage/OrgMoveModal.tsx similarity index 100% rename from projects/app/src/pages/account/team/components/OrgManage/OrgMoveModal.tsx rename to projects/app/src/pageComponents/account/team/OrgManage/OrgMoveModal.tsx diff --git a/projects/app/src/pages/account/team/components/OrgManage/OrgTree.tsx b/projects/app/src/pageComponents/account/team/OrgManage/OrgTree.tsx similarity index 100% rename from projects/app/src/pages/account/team/components/OrgManage/OrgTree.tsx rename to projects/app/src/pageComponents/account/team/OrgManage/OrgTree.tsx diff --git a/projects/app/src/pageComponents/account/team/OrgManage/index.tsx b/projects/app/src/pageComponents/account/team/OrgManage/index.tsx new file mode 100644 index 000000000000..ddc080ba5050 --- /dev/null +++ b/projects/app/src/pageComponents/account/team/OrgManage/index.tsx @@ -0,0 +1,369 @@ +import { useUserStore } from '@/web/support/user/useUserStore'; +import { + Box, + Divider, + Flex, + HStack, + Table, + TableContainer, + Tag, + Tbody, + Td, + Th, + Thead, + Tr, + VStack +} from '@chakra-ui/react'; +import type { OrgType } from '@fastgpt/global/support/user/team/org/type'; +import Avatar from '@fastgpt/web/components/common/Avatar'; +import MyIcon from '@fastgpt/web/components/common/Icon'; +import type { IconNameType } from '@fastgpt/web/components/common/Icon/type'; +import MyMenu from '@fastgpt/web/components/common/MyMenu'; +import { useConfirm } from '@fastgpt/web/hooks/useConfirm'; +import { useRequest2 } from '@fastgpt/web/hooks/useRequest'; +import { useTranslation } from 'next-i18next'; +import { useMemo, useState } from 'react'; +import { useContextSelector } from 'use-context-selector'; +import MemberTag from '@/components/support/user/team/Info/MemberTag'; +import { TeamContext } from '../context'; +import { deleteOrg, deleteOrgMember, getOrgList } from '@/web/support/user/team/org/api'; + +import IconButton from './IconButton'; +import { defaultOrgForm, type OrgFormType } from './OrgInfoModal'; + +import dynamic from 'next/dynamic'; +import MyBox from '@fastgpt/web/components/common/MyBox'; +import Path from '@/components/common/folder/Path'; +import { ParentTreePathItemType } from '@fastgpt/global/common/parentFolder/type'; +import { getOrgChildrenPath } from '@fastgpt/global/support/user/team/org/constant'; +import { useSystemStore } from '@/web/common/system/useSystemStore'; + +const OrgInfoModal = dynamic(() => import('./OrgInfoModal')); +const OrgMemberManageModal = dynamic(() => import('./OrgMemberManageModal')); +const OrgMoveModal = dynamic(() => import('./OrgMoveModal')); + +function ActionButton({ + icon, + text, + onClick +}: { + icon: IconNameType; + text: string; + onClick: () => void; +}) { + return ( + + + {text} + + ); +} + +function OrgTable({ Tabs }: { Tabs: React.ReactNode }) { + const { t } = useTranslation(); + const { userInfo, isTeamAdmin } = useUserStore(); + + const { members, MemberScrollData } = useContextSelector(TeamContext, (v) => v); + const { feConfigs } = useSystemStore(); + + const isSyncMember = feConfigs.register_method?.includes('sync'); + const [parentPath, setParentPath] = useState(''); + const { + data: orgs = [], + loading: isLoadingOrgs, + refresh: refetchOrgs + } = useRequest2(getOrgList, { + manual: false, + refreshDeps: [userInfo?.team?.teamId] + }); + const currentOrgs = useMemo(() => { + if (orgs.length === 0) return []; + // Auto select the first org(root org is team) + if (parentPath === '') { + setParentPath(getOrgChildrenPath(orgs[0])); + return []; + } + + return orgs + .filter((org) => org.path === parentPath) + .map((item) => { + return { + ...item, + // Member + org + count: + item.members.length + orgs.filter((org) => org.path === getOrgChildrenPath(item)).length + }; + }); + }, [orgs, parentPath]); + const currentOrg = useMemo(() => { + const splitPath = parentPath.split('/'); + const currentOrgId = splitPath[splitPath.length - 1]; + if (!currentOrgId) return; + + return orgs.find((org) => org.pathId === currentOrgId); + }, [orgs, parentPath]); + + const paths = useMemo(() => { + const splitPath = parentPath.split('/').filter(Boolean); + return splitPath + .map((id) => { + const org = orgs.find((org) => org.pathId === id)!; + + if (org.path === '') return; + + return { + parentId: getOrgChildrenPath(org), + parentName: org.name + }; + }) + .filter(Boolean) as ParentTreePathItemType[]; + }, [parentPath, orgs]); + + const [editOrg, setEditOrg] = useState(); + const [manageMemberOrg, setManageMemberOrg] = useState(); + const [movingOrg, setMovingOrg] = useState(); + + // Delete org + const { ConfirmModal: ConfirmDeleteOrgModal, openConfirm: openDeleteOrgModal } = useConfirm({ + type: 'delete', + content: t('account_team:confirm_delete_org') + }); + const deleteOrgHandler = (orgId: string) => openDeleteOrgModal(() => deleteOrgReq(orgId))(); + const { runAsync: deleteOrgReq } = useRequest2(deleteOrg, { + onSuccess: () => { + refetchOrgs(); + } + }); + + // Delete member + const { ConfirmModal: ConfirmDeleteMember, openConfirm: openDeleteMemberModal } = useConfirm({ + type: 'delete', + content: t('account_team:confirm_delete_member') + }); + const { runAsync: deleteMemberReq } = useRequest2(deleteOrgMember, { + onSuccess: () => { + refetchOrgs(); + } + }); + + return ( + <> + + {Tabs} + + + + + + + + {/* Table */} + + + + + + {!isSyncMember && ( + + )} + + + + {currentOrgs.map((org) => ( + + + {isTeamAdmin && !isSyncMember && ( + + )} + + ))} + {currentOrg?.members.map((member) => { + const memberInfo = members.find((m) => m.tmbId === member.tmbId); + if (!memberInfo) return null; + + return ( + + + + + ); + })} + +
+ {t('common:Name')} + + {t('common:common.Action')} +
+ setParentPath(getOrgChildrenPath(org))} + > + + {org.count} + + + + } + menuList={[ + { + children: [ + { + icon: 'edit', + label: t('account_team:edit_info'), + onClick: () => setEditOrg(org) + }, + { + icon: 'common/file/move', + label: t('common:Move'), + onClick: () => setMovingOrg(org) + }, + { + icon: 'delete', + label: t('account_team:delete'), + type: 'danger', + onClick: () => deleteOrgHandler(org._id) + } + ] + } + ]} + /> +
+ + + {isTeamAdmin && !isSyncMember && ( + } + menuList={[ + { + children: [ + { + icon: 'delete', + label: t('account_team:delete'), + type: 'danger', + onClick: () => + openDeleteMemberModal(() => + deleteMemberReq(currentOrg._id, member.tmbId) + )() + } + ] + } + ]} + /> + )} +
+
+
+ + {/* Slider */} + {!isSyncMember && ( + + + + + {currentOrg?.name} + + {currentOrg?.path !== '' && ( + setEditOrg(currentOrg)} /> + )} + + {currentOrg?.description || t('common:common.no_intro')} + + + + + {t('common:common.Action')} + + {currentOrg && isTeamAdmin && ( + + { + setEditOrg({ + ...defaultOrgForm, + parentId: currentOrg?._id + }); + }} + /> + setManageMemberOrg(currentOrg)} + /> + {currentOrg?.path !== '' && ( + <> + setMovingOrg(currentOrg)} + /> + deleteOrgHandler(currentOrg._id)} + /> + + )} + + )} + + )} +
+
+ + {!!editOrg && ( + setEditOrg(undefined)} + onSuccess={refetchOrgs} + /> + )} + {!!movingOrg && ( + setMovingOrg(undefined)} + onSuccess={refetchOrgs} + /> + )} + {!!manageMemberOrg && ( + setManageMemberOrg(undefined)} + /> + )} + + + + + ); +} + +export default OrgTable; diff --git a/projects/app/src/pages/account/team/components/PermissionManage/index.tsx b/projects/app/src/pageComponents/account/team/PermissionManage/index.tsx similarity index 99% rename from projects/app/src/pages/account/team/components/PermissionManage/index.tsx rename to projects/app/src/pageComponents/account/team/PermissionManage/index.tsx index 4cf9860d4f61..b2228a3c37ef 100644 --- a/projects/app/src/pages/account/team/components/PermissionManage/index.tsx +++ b/projects/app/src/pageComponents/account/team/PermissionManage/index.tsx @@ -24,7 +24,7 @@ import { import { useUserStore } from '@/web/support/user/useUserStore'; import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip'; import Avatar from '@fastgpt/web/components/common/Avatar'; -import MemberTag from '../../../../../components/support/user/team/Info/MemberTag'; +import MemberTag from '../../../../components/support/user/team/Info/MemberTag'; import { DefaultGroupName } from '@fastgpt/global/support/user/team/group/constant'; import { TeamManagePermissionVal, diff --git a/projects/app/src/pages/account/team/components/SelectMember.tsx b/projects/app/src/pageComponents/account/team/SelectMember.tsx similarity index 100% rename from projects/app/src/pages/account/team/components/SelectMember.tsx rename to projects/app/src/pageComponents/account/team/SelectMember.tsx diff --git a/projects/app/src/pages/account/team/components/context.tsx b/projects/app/src/pageComponents/account/team/context.tsx similarity index 86% rename from projects/app/src/pages/account/team/components/context.tsx rename to projects/app/src/pageComponents/account/team/context.tsx index b76ed99c3afa..c4fd1ad058d5 100644 --- a/projects/app/src/pages/account/team/components/context.tsx +++ b/projects/app/src/pageComponents/account/team/context.tsx @@ -2,7 +2,7 @@ import React, { ReactNode, useState } from 'react'; import { createContext } from 'use-context-selector'; import type { EditTeamFormDataType } from './EditInfoModal'; import dynamic from 'next/dynamic'; -import { getTeamList, putSwitchTeam } from '@/web/support/user/team/api'; +import { getTeamList, getTeamMembers, putSwitchTeam } from '@/web/support/user/team/api'; import { TeamMemberStatusEnum } from '@fastgpt/global/support/user/team/constant'; import { useUserStore } from '@/web/support/user/useUserStore'; import type { TeamTmbItemType, TeamMemberItemType } from '@fastgpt/global/support/user/team/type'; @@ -10,7 +10,7 @@ import { useRequest2 } from '@fastgpt/web/hooks/useRequest'; import { useTranslation } from 'next-i18next'; import { getGroupList } from '@/web/support/user/team/group/api'; import { MemberGroupListType } from '@fastgpt/global/support/permission/memberGroup/type'; -import { OrgType } from '@fastgpt/global/support/user/team/org/type'; +import { useScrollPagination } from '@fastgpt/web/hooks/useScrollPagination'; const EditInfoModal = dynamic(() => import('./EditInfoModal')); @@ -26,6 +26,7 @@ type TeamModalContextType = { refetchTeams: () => void; refetchGroups: () => void; teamSize: number; + MemberScrollData: ReturnType['ScrollData']; }; export const TeamContext = createContext({ @@ -49,13 +50,14 @@ export const TeamContext = createContext({ throw new Error('Function not implemented.'); }, - teamSize: 0 + teamSize: 0, + MemberScrollData: () => <> }); export const TeamModalContextProvider = ({ children }: { children: ReactNode }) => { const { t } = useTranslation(); const [editTeamData, setEditTeamData] = useState(); - const { userInfo, initUserInfo, loadAndGetTeamMembers } = useUserStore(); + const { userInfo, initUserInfo } = useUserStore(); const { data: myTeams = [], @@ -69,18 +71,11 @@ export const TeamModalContextProvider = ({ children }: { children: ReactNode }) // member action const { data: members = [], - runAsync: refetchMembers, - loading: loadingMembers - } = useRequest2( - () => { - if (!userInfo?.team?.teamId) return Promise.resolve([]); - return loadAndGetTeamMembers(true); - }, - { - manual: false, - refreshDeps: [userInfo?.team?.teamId] - } - ); + isLoading: loadingMembers, + refreshList: refetchMembers, + total: memberTotal, + ScrollData: MemberScrollData + } = useScrollPagination(getTeamMembers, {}); const { runAsync: onSwitchTeam, loading: isSwitchingTeam } = useRequest2( async (teamId: string) => { @@ -115,7 +110,8 @@ export const TeamModalContextProvider = ({ children }: { children: ReactNode }) refetchMembers, groups, refetchGroups, - teamSize: members.length + teamSize: memberTotal, + MemberScrollData }; return ( diff --git a/projects/app/src/pages/account/bill/components/BillTable.tsx b/projects/app/src/pages/account/bill/components/BillTable.tsx index 08c744692d81..228de85cb68d 100644 --- a/projects/app/src/pages/account/bill/components/BillTable.tsx +++ b/projects/app/src/pages/account/bill/components/BillTable.tsx @@ -1,4 +1,4 @@ -import React, { useState, useCallback, useMemo, useEffect } from 'react'; +import React, { useState, useMemo, useEffect } from 'react'; import { Button, Table, @@ -25,7 +25,6 @@ import { billStatusMap, billTypeMap } from '@fastgpt/global/support/wallet/bill/constants'; -// import { usePagination } from '@/web/common/hooks/usePagination'; import MyBox from '@fastgpt/web/components/common/MyBox'; import { useRequest } from '@fastgpt/web/hooks/useRequest'; import { standardSubLevelMap, subModeMap } from '@fastgpt/global/support/wallet/sub/constants'; @@ -33,25 +32,23 @@ import MySelect from '@fastgpt/web/components/common/MySelect'; import MyModal from '@fastgpt/web/components/common/MyModal'; import { usePagination } from '@fastgpt/web/hooks/usePagination'; import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel'; -import { useI18n } from '@/web/context/I18n'; const BillTable = () => { const { t } = useTranslation(); - const { commonT } = useI18n(); const { toast } = useToast(); - const [billType, setBillType] = useState(''); + const [billType, setBillType] = useState(undefined); const [billDetail, setBillDetail] = useState(); const billTypeList = useMemo( () => [ - { label: t('account_bill:all'), value: '' }, + { label: t('account_bill:all'), value: undefined }, ...Object.entries(billTypeMap).map(([key, value]) => ({ label: t(value.label as any), value: key })) ] as { label: string; - value: BillTypeEnum | ''; + value: BillTypeEnum | undefined; }[], [t] ); @@ -62,8 +59,7 @@ const BillTable = () => { Pagination, getData, total - } = usePagination({ - api: getBills, + } = usePagination(getBills, { pageSize: 20, params: { type: billType @@ -110,7 +106,7 @@ const BillTable = () => { # - + void }) { const { t } = useTranslation(); - const { commonT } = useI18n(); return ( { isLoading, Pagination, total - } = usePagination({ - api: getInvoiceRecords, + } = usePagination(getInvoiceRecords, { pageSize: 20 }); diff --git a/projects/app/src/pages/account/info/index.tsx b/projects/app/src/pages/account/info/index.tsx index 48c71cc31277..1adbff2167d6 100644 --- a/projects/app/src/pages/account/info/index.tsx +++ b/projects/app/src/pages/account/info/index.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useMemo, useRef } from 'react'; +import React, { useCallback, useMemo } from 'react'; import { Box, Flex, @@ -160,6 +160,7 @@ const MyInfo = ({ onOpenContact }: { onOpenContact: () => void }) => { color: 'myGray.900' }; + const isSyncMember = feConfigs.register_method?.includes('sync'); return ( {/* user info */} @@ -224,6 +225,7 @@ const MyInfo = ({ onOpenContact }: { onOpenContact: () => void }) => { {t('account_info:member_name')}:  void }) => { const { feConfigs } = useSystemStore(); const { t } = useTranslation(); const { isPc } = useSystem(); - const { userInfo, updateUserInfo } = useUserStore(); - - const { reset } = useForm({ - defaultValues: userInfo as UserType - }); return ( diff --git a/projects/app/src/pages/account/inform.tsx b/projects/app/src/pages/account/inform.tsx index 176abe602ced..d0612c3385f7 100644 --- a/projects/app/src/pages/account/inform.tsx +++ b/projects/app/src/pages/account/inform.tsx @@ -1,13 +1,12 @@ import React from 'react'; import { Box, Button, Flex, useTheme } from '@chakra-ui/react'; import { getInforms, readInform } from '@/web/support/user/inform/api'; -import type { UserInformSchema } from '@fastgpt/global/support/user/inform/type'; import { formatTimeToChatTime } from '@fastgpt/global/common/string/time'; import { usePagination } from '@fastgpt/web/hooks/usePagination'; import { useLoading } from '@fastgpt/web/hooks/useLoading'; import { useTranslation } from 'next-i18next'; import EmptyTip from '@fastgpt/web/components/common/EmptyTip'; -import AccountContainer, { TabEnum } from './components/AccountContainer'; +import AccountContainer from './components/AccountContainer'; import { serviceSideProps } from '@fastgpt/web/common/system/nextjs'; const InformTable = () => { @@ -23,8 +22,7 @@ const InformTable = () => { Pagination, getData, pageNum - } = usePagination({ - api: getInforms, + } = usePagination(getInforms, { pageSize: 20 }); diff --git a/projects/app/src/pages/account/promotion.tsx b/projects/app/src/pages/account/promotion.tsx index be9ed529d191..5da3e0b48815 100644 --- a/projects/app/src/pages/account/promotion.tsx +++ b/projects/app/src/pages/account/promotion.tsx @@ -25,7 +25,7 @@ import { usePagination } from '@fastgpt/web/hooks/usePagination'; import { useLoading } from '@fastgpt/web/hooks/useLoading'; import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip'; import EmptyTip from '@fastgpt/web/components/common/EmptyTip'; -import AccountContainer, { TabEnum } from './components/AccountContainer'; +import AccountContainer from './components/AccountContainer'; import { serviceSideProps } from '@fastgpt/web/common/system/nextjs'; const Promotion = () => { @@ -41,8 +41,7 @@ const Promotion = () => { total, pageSize, Pagination - } = usePagination({ - api: getPromotionRecords, + } = usePagination(getPromotionRecords, { pageSize: 20 }); diff --git a/projects/app/src/pages/account/team/components/OrgManage/index.tsx b/projects/app/src/pages/account/team/components/OrgManage/index.tsx deleted file mode 100644 index 241fc1aee2b7..000000000000 --- a/projects/app/src/pages/account/team/components/OrgManage/index.tsx +++ /dev/null @@ -1,354 +0,0 @@ -import { deleteOrg, deleteOrgMember } from '@/web/support/user/team/org/api'; -import { useUserStore } from '@/web/support/user/useUserStore'; -import { - Box, - Divider, - Flex, - HStack, - Table, - TableContainer, - Tag, - Tbody, - Td, - Th, - Thead, - Tr, - VStack -} from '@chakra-ui/react'; -import type { OrgType } from '@fastgpt/global/support/user/team/org/type'; -import Avatar from '@fastgpt/web/components/common/Avatar'; -import MyIcon from '@fastgpt/web/components/common/Icon'; -import type { IconNameType } from '@fastgpt/web/components/common/Icon/type'; -import MyMenu from '@fastgpt/web/components/common/MyMenu'; -import { useConfirm } from '@fastgpt/web/hooks/useConfirm'; -import { useRequest2 } from '@fastgpt/web/hooks/useRequest'; -import { useTranslation } from 'next-i18next'; -import { useMemo, useState } from 'react'; -import { useContextSelector } from 'use-context-selector'; -import MemberTag from '@/components/support/user/team/Info/MemberTag'; -import { TeamContext } from '../context'; -import { getOrgList } from '@/web/support/user/team/org/api'; - -import IconButton from './IconButton'; -import { defaultOrgForm, type OrgFormType } from './OrgInfoModal'; - -import dynamic from 'next/dynamic'; -import MyBox from '@fastgpt/web/components/common/MyBox'; -import Path from '@/components/common/folder/Path'; -import { ParentTreePathItemType } from '@fastgpt/global/common/parentFolder/type'; -import { getOrgChildrenPath } from '@fastgpt/global/support/user/team/org/constant'; - -const OrgInfoModal = dynamic(() => import('./OrgInfoModal')); -const OrgMemberManageModal = dynamic(() => import('./OrgMemberManageModal')); -const OrgMoveModal = dynamic(() => import('./OrgMoveModal')); - -function ActionButton({ - icon, - text, - onClick -}: { - icon: IconNameType; - text: string; - onClick: () => void; -}) { - return ( - - - {text} - - ); -} - -function OrgTable({ Tabs }: { Tabs: React.ReactNode }) { - const { t } = useTranslation(); - const { userInfo, isTeamAdmin } = useUserStore(); - - const { members } = useContextSelector(TeamContext, (v) => v); - - const [parentPath, setParentPath] = useState(''); - const { - data: orgs = [], - loading: isLoadingOrgs, - refresh: refetchOrgs - } = useRequest2(getOrgList, { - manual: false, - refreshDeps: [userInfo?.team?.teamId] - }); - const currentOrgs = useMemo(() => { - if (orgs.length === 0) return []; - // Auto select the first org(root org is team) - if (parentPath === '') { - setParentPath(getOrgChildrenPath(orgs[0])); - return []; - } - - return orgs - .filter((org) => org.path === parentPath) - .map((item) => { - return { - ...item, - // Member + org - count: - item.members.length + orgs.filter((org) => org.path === getOrgChildrenPath(item)).length - }; - }); - }, [orgs, parentPath]); - const currentOrg = useMemo(() => { - const splitPath = parentPath.split('/'); - const currentOrgId = splitPath[splitPath.length - 1]; - if (!currentOrgId) return; - - return orgs.find((org) => org.pathId === currentOrgId); - }, [orgs, parentPath]); - - const paths = useMemo(() => { - const splitPath = parentPath.split('/').filter(Boolean); - return splitPath - .map((id) => { - const org = orgs.find((org) => org.pathId === id)!; - - if (org.path === '') return; - - return { - parentId: getOrgChildrenPath(org), - parentName: org.name - }; - }) - .filter(Boolean) as ParentTreePathItemType[]; - }, [parentPath, orgs]); - - const [editOrg, setEditOrg] = useState(); - const [manageMemberOrg, setManageMemberOrg] = useState(); - const [movingOrg, setMovingOrg] = useState(); - - // Delete org - const { ConfirmModal: ConfirmDeleteOrgModal, openConfirm: openDeleteOrgModal } = useConfirm({ - type: 'delete', - content: t('account_team:confirm_delete_org') - }); - const deleteOrgHandler = (orgId: string) => openDeleteOrgModal(() => deleteOrgReq(orgId))(); - const { runAsync: deleteOrgReq } = useRequest2(deleteOrg, { - onSuccess: () => { - refetchOrgs(); - } - }); - - // Delete member - const { ConfirmModal: ConfirmDeleteMember, openConfirm: openDeleteMemberModal } = useConfirm({ - type: 'delete', - content: t('account_team:confirm_delete_member') - }); - const { runAsync: deleteMemberReq } = useRequest2(deleteOrgMember, { - onSuccess: () => { - refetchOrgs(); - } - }); - - return ( - <> - - {Tabs} - - - - - - - {/* Table */} - - - - - - - - - - {currentOrgs.map((org) => ( - - - - - ))} - {currentOrg?.members.map((member) => { - const memberInfo = members.find((m) => m.tmbId === member.tmbId); - if (!memberInfo) return null; - - return ( - - - - - ); - })} - -
- {t('common:Name')} - - {t('common:common.Action')} -
- setParentPath(getOrgChildrenPath(org))} - > - - {org.count} - - - - {isTeamAdmin && ( - } - menuList={[ - { - children: [ - { - icon: 'edit', - label: t('account_team:edit_info'), - onClick: () => setEditOrg(org) - }, - { - icon: 'common/file/move', - label: t('common:Move'), - onClick: () => setMovingOrg(org) - }, - { - icon: 'delete', - label: t('account_team:delete'), - type: 'danger', - onClick: () => deleteOrgHandler(org._id) - } - ] - } - ]} - /> - )} -
- - - {isTeamAdmin && ( - } - menuList={[ - { - children: [ - { - icon: 'delete', - label: t('account_team:delete'), - type: 'danger', - onClick: () => - openDeleteMemberModal(() => - deleteMemberReq(currentOrg._id, member.tmbId) - )() - } - ] - } - ]} - /> - )} -
-
- {/* Slider */} - - - - - {currentOrg?.name} - - {currentOrg?.path !== '' && ( - setEditOrg(currentOrg)} /> - )} - - {currentOrg?.description || t('common:common.no_intro')} - - - - - {t('common:common.Action')} - - {currentOrg && isTeamAdmin && ( - - { - setEditOrg({ - ...defaultOrgForm, - parentId: currentOrg?._id - }); - }} - /> - setManageMemberOrg(currentOrg)} - /> - {currentOrg?.path !== '' && ( - <> - setMovingOrg(currentOrg)} - /> - deleteOrgHandler(currentOrg._id)} - /> - - )} - - )} - -
- - {!!editOrg && ( - setEditOrg(undefined)} - onSuccess={refetchOrgs} - /> - )} - {!!movingOrg && ( - setMovingOrg(undefined)} - onSuccess={refetchOrgs} - /> - )} - {!!manageMemberOrg && ( - setManageMemberOrg(undefined)} - /> - )} - - - -
- - ); -} - -export default OrgTable; diff --git a/projects/app/src/pages/account/team/index.tsx b/projects/app/src/pages/account/team/index.tsx index f7ec577d6e68..f8e41ef21425 100644 --- a/projects/app/src/pages/account/team/index.tsx +++ b/projects/app/src/pages/account/team/index.tsx @@ -1,6 +1,6 @@ import { serviceSideProps } from '@fastgpt/web/common/system/nextjs'; import AccountContainer from '../components/AccountContainer'; -import { Box, Flex, useDisclosure } from '@chakra-ui/react'; +import { Box, Flex } from '@chakra-ui/react'; import Icon from '@fastgpt/web/components/common/Icon'; import { useTranslation } from 'next-i18next'; import TeamSelector from '../components/TeamSelector'; @@ -11,14 +11,15 @@ import { useRouter } from 'next/router'; import FillRowTabs from '@fastgpt/web/components/common/Tabs/FillRowTabs'; import MyIcon from '@fastgpt/web/components/common/Icon'; import { TeamMemberRoleEnum } from '@fastgpt/global/support/user/team/constant'; -import { TeamContext, TeamModalContextProvider } from './components/context'; +import { TeamContext, TeamModalContextProvider } from '@/pageComponents/account/team/context'; import dynamic from 'next/dynamic'; -import MemberTable from './components/MemberTable'; -const PermissionManage = dynamic(() => import('./components/PermissionManage/index')); -const GroupManage = dynamic(() => import('./components/GroupManage/index')); - -const OrgManage = dynamic(() => import('./components/OrgManage/index')); +const MemberTable = dynamic(() => import('@/pageComponents/account/team/MemberTable')); +const PermissionManage = dynamic( + () => import('@/pageComponents/account/team/PermissionManage/index') +); +const GroupManage = dynamic(() => import('@/pageComponents/account/team/GroupManage/index')); +const OrgManage = dynamic(() => import('@/pageComponents/account/team/OrgManage/index')); export enum TeamTabEnum { member = 'member', @@ -34,7 +35,7 @@ const Team = () => { const { t } = useTranslation(); const { userInfo } = useUserStore(); - const { setEditTeamData, teamSize, isLoading } = useContextSelector(TeamContext, (v) => v); + const { setEditTeamData, isLoading, teamSize } = useContextSelector(TeamContext, (v) => v); const Tabs = useMemo( () => ( @@ -62,72 +63,81 @@ const Team = () => { return ( - {/* header */} - - - - - - {t('account:team')} - - - - - - {userInfo?.team?.role === TeamMemberRoleEnum.owner && ( - - { - if (!userInfo?.team) return; - setEditTeamData({ - id: userInfo.team.teamId, - name: userInfo.team.teamName, - avatar: userInfo.team.avatar - }); - }} - /> + + {/* header */} + + + + + + {t('account:team')} + - )} + + + + {userInfo?.team?.role === TeamMemberRoleEnum.owner && ( + + { + if (!userInfo?.team) return; + setEditTeamData({ + id: userInfo.team.teamId, + name: userInfo.team.teamName, + avatar: userInfo.team.avatar + }); + }} + /> + + )} + + + + {t('account_team:total_team_members', { amount: teamSize })} + + {/* table */} - {t('account_team:total_team_members', { amount: teamSize })} + {teamTab === TeamTabEnum.member && } + {teamTab === TeamTabEnum.org && } + {teamTab === TeamTabEnum.group && } + {teamTab === TeamTabEnum.permission && } - - {/* table */} - - {teamTab === TeamTabEnum.member && } - {teamTab === TeamTabEnum.org && } - {teamTab === TeamTabEnum.group && } - {teamTab === TeamTabEnum.permission && } - ); }; diff --git a/projects/app/src/pages/account/usage/index.tsx b/projects/app/src/pages/account/usage/index.tsx index 649b4448aa93..8db661730fea 100644 --- a/projects/app/src/pages/account/usage/index.tsx +++ b/projects/app/src/pages/account/usage/index.tsx @@ -23,15 +23,16 @@ import DateRangePicker, { import { addDays } from 'date-fns'; import dynamic from 'next/dynamic'; import { useTranslation } from 'next-i18next'; -import { useQuery } from '@tanstack/react-query'; import { useUserStore } from '@/web/support/user/useUserStore'; import Avatar from '@fastgpt/web/components/common/Avatar'; import MySelect from '@fastgpt/web/components/common/MySelect'; import { formatNumber } from '@fastgpt/global/common/math/tools'; import EmptyTip from '@fastgpt/web/components/common/EmptyTip'; import { useSystem } from '@fastgpt/web/hooks/useSystem'; -import AccountContainer, { TabEnum } from '../components/AccountContainer'; +import AccountContainer from '../components/AccountContainer'; import { serviceSideProps } from '@fastgpt/web/common/system/nextjs'; +import { useScrollPagination } from '@fastgpt/web/hooks/useScrollPagination'; +import { getTeamMembers } from '@/web/support/user/team/api'; const UsageDetail = dynamic(() => import('./UsageDetail')); @@ -44,7 +45,7 @@ const UsageTable = () => { }); const [usageSource, setUsageSource] = useState(''); const { isPc } = useSystem(); - const { userInfo, loadAndGetTeamMembers } = useUserStore(); + const { userInfo } = useUserStore(); const [usageDetail, setUsageDetail] = useState(); const sourceList = useMemo( @@ -63,10 +64,7 @@ const UsageTable = () => { ); const [selectTmbId, setSelectTmbId] = useState(userInfo?.team?.tmbId); - const { data: members = [] } = useQuery(['getMembers', userInfo?.team?.teamId], () => { - if (!userInfo?.team?.teamId) return []; - return loadAndGetTeamMembers(); - }); + const { data: members, ScrollData } = useScrollPagination(getTeamMembers, {}); const tmbList = useMemo( () => members.map((item) => ({ @@ -86,14 +84,13 @@ const UsageTable = () => { isLoading, Pagination, getData - } = usePagination({ - api: getUserUsages, + } = usePagination(getUserUsages, { pageSize: isPc ? 20 : 10, params: { dateStart: dateRange.from || new Date(), dateEnd: addDays(dateRange.to || new Date(), 1), - source: usageSource, - teamMemberId: selectTmbId + source: usageSource as UsageSourceEnum, + teamMemberId: selectTmbId ?? '' }, defaultRequest: false }); @@ -120,6 +117,7 @@ const UsageTable = () => { { } }; -const batchUpdateFields = async (batchSize = 2000) => { - // Update in batches - await MongoDatasetData.updateMany( - { initFullText: { $exists: true } }, - { - $unset: { - initFullText: 1, - fullTextToken: 1 - } - } - ); -}; +// const batchUpdateFields = async (batchSize = 2000) => { +// // Find documents that still have these fields +// const documents = await MongoDatasetData.find({ initFullText: { $exists: true } }, '_id') +// .limit(batchSize) +// .lean(); + +// if (documents.length === 0) return; + +// // Update in batches +// await MongoDatasetData.updateMany( +// { _id: { $in: documents.map((doc) => doc._id) } }, +// { +// $unset: { +// initFullText: 1 +// // fullTextToken: 1 +// } +// } +// ); + +// success += documents.length; +// console.log('Delete success:', success); +// await batchUpdateFields(batchSize); +// }; diff --git a/projects/app/src/pages/api/admin/initv4819.ts b/projects/app/src/pages/api/admin/initv4819.ts new file mode 100644 index 000000000000..626fbdaa0e26 --- /dev/null +++ b/projects/app/src/pages/api/admin/initv4819.ts @@ -0,0 +1,55 @@ +import { NextAPI } from '@/service/middleware/entry'; +import { mongoSessionRun } from '@fastgpt/service/common/mongo/sessionRun'; +import { authCert } from '@fastgpt/service/support/permission/auth/common'; +import { MongoUser } from '@fastgpt/service/support/user/schema'; +import { MongoTeamMember } from '@fastgpt/service/support/user/team/teamMemberSchema'; +import { NextApiRequest, NextApiResponse } from 'next'; + +/* + 简单版迁移:直接升级到最新镜像,会去除 MongoDatasetData 里的索引。直接执行这个脚本。 + 无缝迁移: + 1. 移动 User 表中的 avatar 字段到 TeamMember 表中。 +*/ +async function handler(req: NextApiRequest, res: NextApiResponse) { + await authCert({ req, authRoot: true }); + await moveUserAvatar(); + return { success: true }; +} + +export default NextAPI(handler); + +const moveUserAvatar = async () => { + try { + const users = await MongoUser.find({}, '_id avatar'); + console.log('Total users:', users.length); + let success = 0; + for await (const user of users) { + // @ts-ignore + if (!user.avatar) continue; + try { + await mongoSessionRun(async (session) => { + await MongoTeamMember.updateOne( + { + userId: user._id + }, + { + $set: { + avatar: (user as any).avatar // 删除 avatar 字段, 因为 Type 改了,所以这里不能直接写 user.avatar + } + }, + { session } + ); + // @ts-ignore + user.avatar = undefined; + await user.save({ session }); + }); + success++; + console.log('Move avatar success:', success); + } catch (error) { + console.error(error); + } + } + } catch (error) { + console.error(error); + } +}; diff --git a/projects/app/src/pages/api/common/system/getInitData.ts b/projects/app/src/pages/api/common/system/getInitData.ts index 33cdb44371a0..85d6413c11e3 100644 --- a/projects/app/src/pages/api/common/system/getInitData.ts +++ b/projects/app/src/pages/api/common/system/getInitData.ts @@ -8,7 +8,8 @@ async function handler(req: ApiRequestProps<{}, { bufferId?: string }>, res: Nex // If bufferId is the same as the current bufferId, return directly if (bufferId && global.systemInitBufferId && global.systemInitBufferId === bufferId) { return { - bufferId: global.systemInitBufferId + bufferId: global.systemInitBufferId, + systemVersion: global.systemVersion || '0.0.0' }; } diff --git a/projects/app/src/pages/api/core/app/getChatLogs.ts b/projects/app/src/pages/api/core/app/getChatLogs.ts index e625e0262940..566c44a2b68b 100644 --- a/projects/app/src/pages/api/core/app/getChatLogs.ts +++ b/projects/app/src/pages/api/core/app/getChatLogs.ts @@ -1,6 +1,5 @@ import type { NextApiRequest, NextApiResponse } from 'next'; import { MongoChat } from '@fastgpt/service/core/chat/chatSchema'; -import type { PagingData } from '@/types'; import { AppLogsListItemType } from '@/types/app'; import { Types } from '@fastgpt/service/common/mongo'; import { addDays } from 'date-fns'; @@ -10,19 +9,22 @@ import { ChatItemCollectionName } from '@fastgpt/service/core/chat/chatItemSchem import { NextAPI } from '@/service/middleware/entry'; import { WritePermissionVal } from '@fastgpt/global/support/permission/constant'; import { readFromSecondary } from '@fastgpt/service/common/mongo/utils'; +import { parsePaginationRequest } from '@fastgpt/service/common/api/pagination'; +import { PaginationResponse } from '@fastgpt/web/common/fetch/type'; +import { addSourceMember } from '@fastgpt/service/support/user/utils'; async function handler( req: NextApiRequest, _res: NextApiResponse -): Promise> { +): Promise> { const { - pageNum = 1, - pageSize = 20, appId, dateStart = addDays(new Date(), -7), dateEnd = new Date() } = req.body as GetAppChatLogsParams; + const { pageSize = 20, offset } = parsePaginationRequest(req); + if (!appId) { throw new Error('缺少参数'); } @@ -39,7 +41,7 @@ async function handler( } }; - const [data, total] = await Promise.all([ + const [list, total] = await Promise.all([ MongoChat.aggregate( [ { $match: where }, @@ -51,7 +53,7 @@ async function handler( updateTime: -1 } }, - { $skip: (pageNum - 1) * pageSize }, + { $skip: offset }, { $limit: pageSize }, { $lookup: { @@ -144,10 +146,14 @@ async function handler( MongoChat.countDocuments(where, { ...readFromSecondary }) ]); + const listWithSourceMember = await addSourceMember({ + list: list + }); + + const listWithoutTmbId = list.filter((item) => !item.tmbId); + return { - pageNum, - pageSize, - data, + list: listWithSourceMember.concat(listWithoutTmbId), total }; } diff --git a/projects/app/src/pages/api/core/app/list.ts b/projects/app/src/pages/api/core/app/list.ts index ee2b605e2d4f..da515cd43da7 100644 --- a/projects/app/src/pages/api/core/app/list.ts +++ b/projects/app/src/pages/api/core/app/list.ts @@ -18,6 +18,7 @@ import { replaceRegChars } from '@fastgpt/global/common/string/tools'; import { concatPer } from '@fastgpt/service/support/permission/controller'; import { getGroupsByTmbId } from '@fastgpt/service/support/permission/memberGroup/controllers'; import { getOrgIdSetWithParentByTmbId } from '@fastgpt/service/support/permission/org/controllers'; +import { addSourceMember } from '@fastgpt/service/support/user/utils'; export type ListAppBody = { parentId?: ParentIdType; @@ -201,19 +202,9 @@ async function handler(req: ApiRequestProps): Promise app.permission.hasReadPer); - return formatApps.map((app) => ({ - _id: app._id, - tmbId: app.tmbId, - avatar: app.avatar, - type: app.type, - name: app.name, - intro: app.intro, - updateTime: app.updateTime, - permission: app.permission, - pluginData: app.pluginData, - inheritPermission: app.inheritPermission ?? true, - private: app.privateApp - })); + return addSourceMember({ + list: formatApps + }); } export default NextAPI(handler); diff --git a/projects/app/src/pages/api/core/app/version/list.ts b/projects/app/src/pages/api/core/app/version/list.ts index b0337c33eb7a..c03971d3071a 100644 --- a/projects/app/src/pages/api/core/app/version/list.ts +++ b/projects/app/src/pages/api/core/app/version/list.ts @@ -6,6 +6,8 @@ import { ApiRequestProps } from '@fastgpt/service/type/next'; import { authApp } from '@fastgpt/service/support/permission/app/auth'; import { WritePermissionVal } from '@fastgpt/global/support/permission/constant'; import { VersionListItemType } from '@fastgpt/global/core/app/version'; +import { parsePaginationRequest } from '@fastgpt/service/common/api/pagination'; +import { addSourceMember } from '@fastgpt/service/support/user/utils'; export type versionListBody = PaginationProps<{ appId: string; @@ -15,41 +17,40 @@ export type versionListResponse = PaginationResponse; async function handler( req: ApiRequestProps, - res: NextApiResponse + _res: NextApiResponse ): Promise { - const { offset, pageSize, appId } = req.body; + const { appId } = req.body; + const { offset, pageSize } = parsePaginationRequest(req); await authApp({ appId, req, per: WritePermissionVal, authToken: true }); const [result, total] = await Promise.all([ - MongoAppVersion.find( - { + (async () => { + const versions = await MongoAppVersion.find({ appId - }, - '_id appId versionName time isPublish tmbId' - ) - .sort({ - time: -1 }) - .skip(offset) - .limit(pageSize), + .sort({ + time: -1 + }) + .skip(offset) + .limit(pageSize) + .lean(); + + return addSourceMember({ + list: versions + }).then((list) => + list.map((item) => ({ + ...item, + isPublish: !!item.isPublish + })) + ); + })(), MongoAppVersion.countDocuments({ appId }) ]); - const versionList = result.map((item) => { - return { - _id: item._id, - appId: item.appId, - versionName: item.versionName, - time: item.time, - isPublish: item.isPublish, - tmbId: item.tmbId - }; - }); - return { total, - list: versionList + list: result }; } diff --git a/projects/app/src/pages/api/core/app/version/publish.test.ts b/projects/app/src/pages/api/core/app/version/publish.test.ts index a929fdde125e..314af025225c 100644 --- a/projects/app/src/pages/api/core/app/version/publish.test.ts +++ b/projects/app/src/pages/api/core/app/version/publish.test.ts @@ -12,7 +12,6 @@ describe('发布应用版本测试', () => { nodes: [], edges: [], chatConfig: {}, - type: AppTypeEnum.simple, isPublish: false, versionName: '1' }; diff --git a/projects/app/src/pages/api/core/chat/chatTest.ts b/projects/app/src/pages/api/core/chat/chatTest.ts index 3c0d64ddd5ef..26e9b044dec6 100644 --- a/projects/app/src/pages/api/core/chat/chatTest.ts +++ b/projects/app/src/pages/api/core/chat/chatTest.ts @@ -164,6 +164,10 @@ async function handler(req: NextApiRequest, res: NextApiResponse) { runningAppInfo: { id: appId, + teamId: app.teamId, + tmbId: app.tmbId + }, + runningUserInfo: { teamId, tmbId }, diff --git a/projects/app/src/pages/api/core/chat/getHistories.ts b/projects/app/src/pages/api/core/chat/getHistories.ts index ea38c84b8aae..2fafd8082361 100644 --- a/projects/app/src/pages/api/core/chat/getHistories.ts +++ b/projects/app/src/pages/api/core/chat/getHistories.ts @@ -7,6 +7,7 @@ import { NextAPI } from '@/service/middleware/entry'; import { ApiRequestProps, ApiResponseType } from '@fastgpt/service/type/next'; import { PaginationProps, PaginationResponse } from '@fastgpt/web/common/fetch/type'; import { GetHistoriesProps } from '@/global/core/chat/api'; +import { parsePaginationRequest } from '@fastgpt/service/common/api/pagination'; import { addMonths } from 'date-fns'; export type getHistoriesQuery = {}; @@ -17,9 +18,10 @@ export type getHistoriesResponse = {}; async function handler( req: ApiRequestProps, - res: ApiResponseType + _res: ApiResponseType ): Promise> { - const { appId, shareId, outLinkUid, teamId, teamToken, offset, pageSize, source } = req.body; + const { appId, shareId, outLinkUid, teamId, teamToken, source } = req.body; + const { offset, pageSize } = parsePaginationRequest(req); const match = await (async () => { if (shareId && outLinkUid) { diff --git a/projects/app/src/pages/api/core/chat/getPaginationRecords.ts b/projects/app/src/pages/api/core/chat/getPaginationRecords.ts index c779c11ecd30..82c11b81fcba 100644 --- a/projects/app/src/pages/api/core/chat/getPaginationRecords.ts +++ b/projects/app/src/pages/api/core/chat/getPaginationRecords.ts @@ -13,6 +13,7 @@ import { filterPublicNodeResponseData } from '@fastgpt/global/core/chat/utils'; import { GetChatTypeEnum } from '@/global/core/chat/constants'; import { PaginationProps, PaginationResponse } from '@fastgpt/web/common/fetch/type'; import { ChatItemType } from '@fastgpt/global/core/chat/type'; +import { parsePaginationRequest } from '@fastgpt/service/common/api/pagination'; export type getPaginationRecordsQuery = {}; @@ -22,16 +23,11 @@ export type getPaginationRecordsResponse = PaginationResponse; async function handler( req: ApiRequestProps, - res: ApiResponseType + _res: ApiResponseType ): Promise { - const { - appId, - chatId, - offset, - pageSize = 10, - loadCustomFeedbacks, - type = GetChatTypeEnum.normal - } = req.body; + const { appId, chatId, loadCustomFeedbacks, type = GetChatTypeEnum.normal } = req.body; + + const { offset, pageSize } = parsePaginationRequest(req); if (!appId || !chatId) { return { diff --git a/projects/app/src/pages/api/core/chat/inputGuide/list.ts b/projects/app/src/pages/api/core/chat/inputGuide/list.ts index b6173add01dd..5660f822eaa3 100644 --- a/projects/app/src/pages/api/core/chat/inputGuide/list.ts +++ b/projects/app/src/pages/api/core/chat/inputGuide/list.ts @@ -6,6 +6,7 @@ import { ApiRequestProps } from '@fastgpt/service/type/next'; import { ChatInputGuideSchemaType } from '@fastgpt/global/core/chat/inputGuide/type'; import { authApp } from '@fastgpt/service/support/permission/app/auth'; import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant'; +import { parsePaginationRequest } from '@fastgpt/service/common/api/pagination'; export type ChatInputGuideProps = PaginationProps<{ appId: string; @@ -17,7 +18,8 @@ async function handler( req: ApiRequestProps, res: NextApiResponse ): Promise { - const { appId, pageSize, offset, searchKey } = req.body; + const { appId, searchKey } = req.body; + const { offset, pageSize } = parsePaginationRequest(req); await authApp({ req, appId, authToken: true, per: ReadPermissionVal }); diff --git a/projects/app/src/pages/api/core/dataset/collection/list.ts b/projects/app/src/pages/api/core/dataset/collection/list.ts index 7c7410275f32..117e102684ab 100644 --- a/projects/app/src/pages/api/core/dataset/collection/list.ts +++ b/projects/app/src/pages/api/core/dataset/collection/list.ts @@ -2,7 +2,6 @@ import type { NextApiRequest } from 'next'; import { DatasetTrainingCollectionName } from '@fastgpt/service/core/dataset/training/schema'; import { Types } from '@fastgpt/service/common/mongo'; import type { DatasetCollectionsListItemType } from '@/global/core/dataset/type.d'; -import type { GetDatasetCollectionsProps } from '@/global/core/api/datasetReq'; import { MongoDatasetCollection } from '@fastgpt/service/core/dataset/collection/schema'; import { DatasetCollectionTypeEnum } from '@fastgpt/global/core/dataset/constants'; import { authDataset } from '@fastgpt/service/support/permission/dataset/auth'; @@ -10,11 +9,10 @@ import { DatasetDataCollectionName } from '@fastgpt/service/core/dataset/data/sc import { startTrainingQueue } from '@/service/core/dataset/training/utils'; import { NextAPI } from '@/service/middleware/entry'; import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant'; -import { PagingData } from '@/types'; import { readFromSecondary } from '@fastgpt/service/common/mongo/utils'; import { collectionTagsToTagLabel } from '@fastgpt/service/core/dataset/collection/utils'; -async function handler(req: NextApiRequest): Promise> { +async function handler(req: NextApiRequest) { let { pageNum = 1, pageSize = 10, @@ -24,7 +22,7 @@ async function handler(req: NextApiRequest): Promise> { + let { + datasetId, + parentId = null, + searchText = '', + selectFolder = false, + filterTags = [], + simple = false + } = req.body as GetDatasetCollectionsProps; + let { pageSize, offset } = parsePaginationRequest(req); + pageSize = Math.min(pageSize, 30); + searchText = searchText?.replace(/'/g, ''); + + // auth dataset and get my role + const { teamId, permission } = await authDataset({ + req, + authToken: true, + authApiKey: true, + datasetId, + per: ReadPermissionVal + }); + + const match = { + teamId: new Types.ObjectId(teamId), + datasetId: new Types.ObjectId(datasetId), + parentId: parentId ? new Types.ObjectId(parentId) : null, + ...(selectFolder ? { type: DatasetCollectionTypeEnum.folder } : {}), + ...(searchText + ? { + name: new RegExp(searchText, 'i') + } + : {}), + ...(filterTags.length ? { tags: { $in: filterTags } } : {}) + }; + + const selectField = { + _id: 1, + parentId: 1, + tmbId: 1, + name: 1, + type: 1, + forbid: 1, + createTime: 1, + updateTime: 1, + trainingType: 1, + fileId: 1, + rawLink: 1, + tags: 1, + externalFileId: 1 + }; + + // not count data amount + if (simple) { + const collections = await MongoDatasetCollection.find(match, undefined, { + ...readFromSecondary + }) + .select(selectField) + .sort({ + updateTime: -1 + }) + .lean(); + + return { + list: await Promise.all( + collections.map(async (item) => ({ + ...item, + tags: await collectionTagsToTagLabel({ + datasetId, + tags: item.tags + }), + dataAmount: 0, + trainingAmount: 0, + permission + })) + ), + total: await MongoDatasetCollection.countDocuments(match) + }; + } + + const [collections, total]: [DatasetCollectionsListItemType[], number] = await Promise.all([ + MongoDatasetCollection.aggregate([ + { + $match: match + }, + { + $sort: { updateTime: -1 } + }, + { + $skip: offset + }, + { + $limit: pageSize + }, + // count training data + { + $lookup: { + from: DatasetTrainingCollectionName, + let: { id: '$_id', team_id: match.teamId, dataset_id: match.datasetId }, + pipeline: [ + { + $match: { + $expr: { + $and: [{ $eq: ['$teamId', '$$team_id'] }, { $eq: ['$collectionId', '$$id'] }] + } + } + }, + { $count: 'count' } + ], + as: 'trainingCount' + } + }, + // count collection total data + { + $lookup: { + from: DatasetDataCollectionName, + let: { id: '$_id', team_id: match.teamId, dataset_id: match.datasetId }, + pipeline: [ + { + $match: { + $expr: { + $and: [ + { $eq: ['$teamId', '$$team_id'] }, + { $eq: ['$datasetId', '$$dataset_id'] }, + { $eq: ['$collectionId', '$$id'] } + ] + } + } + }, + { $count: 'count' } + ], + as: 'dataCount' + } + }, + { + $project: { + ...selectField, + dataAmount: { + $ifNull: [{ $arrayElemAt: ['$dataCount.count', 0] }, 0] + }, + trainingAmount: { + $ifNull: [{ $arrayElemAt: ['$trainingCount.count', 0] }, 0] + } + } + } + ]), + MongoDatasetCollection.countDocuments(match, { + ...readFromSecondary + }) + ]); + + const list = await Promise.all( + collections.map(async (item) => ({ + ...item, + tags: await collectionTagsToTagLabel({ + datasetId, + tags: item.tags + }), + permission + })) + ); + + if (list.find((item) => item.trainingAmount > 0)) { + startTrainingQueue(); + } + + // count collections + return { + list, + total + }; +} + +export default NextAPI(handler); diff --git a/projects/app/src/pages/api/core/dataset/collection/scrollList.ts b/projects/app/src/pages/api/core/dataset/collection/scrollList.ts index cf30cad3144a..2b2920975a0f 100644 --- a/projects/app/src/pages/api/core/dataset/collection/scrollList.ts +++ b/projects/app/src/pages/api/core/dataset/collection/scrollList.ts @@ -10,6 +10,7 @@ import { CommonErrEnum } from '@fastgpt/global/common/error/code/common'; import { ApiRequestProps } from '@fastgpt/service/type/next'; import { PaginationProps, PaginationResponse } from '@fastgpt/web/common/fetch/type'; import type { DatasetCollectionsListItemType } from '@/global/core/dataset/type.d'; +import { parsePaginationRequest } from '@fastgpt/service/common/api/pagination'; export type GetScrollCollectionsProps = PaginationProps<{ datasetId: string; @@ -25,8 +26,6 @@ async function handler( ): Promise> { let { datasetId, - pageSize = 10, - offset, parentId = null, searchText = '', selectFolder = false, @@ -36,6 +35,7 @@ async function handler( if (!datasetId) { return Promise.reject(CommonErrEnum.missingParams); } + let { offset, pageSize } = parsePaginationRequest(req); searchText = searchText?.replace(/'/g, ''); pageSize = Math.min(pageSize, 30); diff --git a/projects/app/src/pages/api/core/dataset/data/list.ts b/projects/app/src/pages/api/core/dataset/data/list.ts index 4e6990732227..a10f7e3ea549 100644 --- a/projects/app/src/pages/api/core/dataset/data/list.ts +++ b/projects/app/src/pages/api/core/dataset/data/list.ts @@ -3,19 +3,21 @@ import { MongoDatasetData } from '@fastgpt/service/core/dataset/data/schema'; import { replaceRegChars } from '@fastgpt/global/common/string/tools'; import { NextAPI } from '@/service/middleware/entry'; import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant'; -import { PagingData, RequestPaging } from '@/types'; import { ApiRequestProps } from '@fastgpt/service/type/next'; import { DatasetDataListItemType } from '@/global/core/dataset/type'; +import { parsePaginationRequest } from '@fastgpt/service/common/api/pagination'; +import { PaginationResponse } from '@fastgpt/web/common/fetch/type'; -export type GetDatasetDataListProps = RequestPaging & { +export type GetDatasetDataListProps = { searchText?: string; collectionId: string; }; async function handler( req: ApiRequestProps -): Promise> { - let { pageNum = 1, pageSize = 10, searchText = '', collectionId } = req.body; +): Promise> { + let { searchText = '', collectionId } = req.body; + let { offset, pageSize } = parsePaginationRequest(req); pageSize = Math.min(pageSize, 30); @@ -40,19 +42,17 @@ async function handler( : {}) }; - const [data, total] = await Promise.all([ + const [list, total] = await Promise.all([ MongoDatasetData.find(match, '_id datasetId collectionId q a chunkIndex') .sort({ chunkIndex: 1, updateTime: -1 }) - .skip((pageNum - 1) * pageSize) + .skip(offset) .limit(pageSize) .lean(), MongoDatasetData.countDocuments(match) ]); return { - pageNum, - pageSize, - data, + list, total }; } diff --git a/projects/app/src/pages/api/core/dataset/data/v2/list.ts b/projects/app/src/pages/api/core/dataset/data/v2/list.ts index 113fd70d4dff..0caa766d9d2a 100644 --- a/projects/app/src/pages/api/core/dataset/data/v2/list.ts +++ b/projects/app/src/pages/api/core/dataset/data/v2/list.ts @@ -6,6 +6,7 @@ import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant'; import { ApiRequestProps } from '@fastgpt/service/type/next'; import { DatasetDataListItemType } from '@/global/core/dataset/type'; import { PaginationProps, PaginationResponse } from '@fastgpt/web/common/fetch/type'; +import { parsePaginationRequest } from '@fastgpt/service/common/api/pagination'; export type GetDatasetDataListProps = PaginationProps & { searchText?: string; @@ -16,7 +17,8 @@ export type GetDatasetDataListRes = PaginationResponse; async function handler( req: ApiRequestProps ): Promise { - let { offset, pageSize = 10, searchText = '', collectionId } = req.body; + let { searchText = '', collectionId } = req.body; + let { offset, pageSize } = parsePaginationRequest(req); pageSize = Math.min(pageSize, 30); diff --git a/projects/app/src/pages/api/core/dataset/list.ts b/projects/app/src/pages/api/core/dataset/list.ts index 2a3e76c62361..ad9ffca5a4b0 100644 --- a/projects/app/src/pages/api/core/dataset/list.ts +++ b/projects/app/src/pages/api/core/dataset/list.ts @@ -1,8 +1,6 @@ -import type { DatasetListItemType } from '@fastgpt/global/core/dataset/type.d'; import { DatasetTypeEnum } from '@fastgpt/global/core/dataset/constants'; import { MongoDataset } from '@fastgpt/service/core/dataset/schema'; import { authUserPer } from '@fastgpt/service/support/permission/user/auth'; -import { getVectorModel } from '@fastgpt/service/core/ai/model'; import { NextAPI } from '@/service/middleware/entry'; import { DatasetPermission } from '@fastgpt/global/support/permission/dataset/controller'; import { @@ -19,6 +17,8 @@ import { replaceRegChars } from '@fastgpt/global/common/string/tools'; import { getGroupsByTmbId } from '@fastgpt/service/support/permission/memberGroup/controllers'; import { concatPer } from '@fastgpt/service/support/permission/controller'; import { getOrgIdSetWithParentByTmbId } from '@fastgpt/service/support/permission/org/controllers'; +import { addSourceMember } from '@fastgpt/service/support/user/utils'; +import { getVectorModel } from '@fastgpt/service/core/ai/model'; export type GetDatasetListBody = { parentId: ParentIdType; @@ -167,28 +167,24 @@ async function handler(req: ApiRequestProps) { })(); return { - ...dataset, + _id: dataset._id, + avatar: dataset.avatar, + name: dataset.name, + intro: dataset.intro, + type: dataset.type, + vectorModel: getVectorModel(dataset.vectorModel), + inheritPermission: dataset.inheritPermission, + tmbId: dataset.tmbId, + updateTime: dataset.updateTime, permission: Per, privateDataset }; }) .filter((app) => app.permission.hasReadPer); - const data = formatDatasets.map((item) => ({ - _id: item._id, - avatar: item.avatar, - name: item.name, - intro: item.intro, - type: item.type, - permission: item.permission, - vectorModel: getVectorModel(item.vectorModel), - inheritPermission: item.inheritPermission, - tmbId: item.tmbId, - updateTime: item.updateTime, - private: item.privateDataset - })); - - return data; + return addSourceMember({ + list: formatDatasets + }); } export default NextAPI(handler); diff --git a/projects/app/src/pages/api/core/workflow/debug.ts b/projects/app/src/pages/api/core/workflow/debug.ts index dfd7c72c5147..87d7ce439ec3 100644 --- a/projects/app/src/pages/api/core/workflow/debug.ts +++ b/projects/app/src/pages/api/core/workflow/debug.ts @@ -45,7 +45,11 @@ async function handler( requestOrigin: req.headers.origin, mode: 'debug', runningAppInfo: { - id: appId, + id: app._id, + teamId: app.teamId, + tmbId: app.tmbId + }, + runningUserInfo: { teamId, tmbId }, diff --git a/projects/app/src/pages/api/support/user/account/update.ts b/projects/app/src/pages/api/support/user/account/update.ts index 34bc06570675..192649081dd9 100644 --- a/projects/app/src/pages/api/support/user/account/update.ts +++ b/projects/app/src/pages/api/support/user/account/update.ts @@ -1,17 +1,18 @@ import { MongoUser } from '@fastgpt/service/support/user/schema'; import { authCert } from '@fastgpt/service/support/permission/auth/common'; import { UserUpdateParams } from '@/types/user'; -import { MongoTeamMember } from '@fastgpt/service/support/user/team/teamMemberSchema'; /* update user info */ import type { ApiRequestProps, ApiResponseType } from '@fastgpt/service/type/next'; import { NextAPI } from '@/service/middleware/entry'; import { mongoSessionRun } from '@fastgpt/service/common/mongo/sessionRun'; -import { getUserDetail } from '@fastgpt/service/support/user/controller'; import { refreshSourceAvatar } from '@fastgpt/service/common/file/image/controller'; +import { MongoTeamMember } from '@fastgpt/service/support/user/team/teamMemberSchema'; + export type UserAccountUpdateQuery = {}; export type UserAccountUpdateBody = UserUpdateParams; export type UserAccountUpdateResponse = {}; + async function handler( req: ApiRequestProps, _res: ApiResponseType @@ -19,21 +20,33 @@ async function handler( const { avatar, timezone } = req.body; const { tmbId } = await authCert({ req, authToken: true }); - const user = await getUserDetail({ tmbId }); + // const user = await getUserDetail({ tmbId }); // 更新对应的记录 await mongoSessionRun(async (session) => { - await MongoUser.updateOne( - { - _id: user._id - }, - { - ...(avatar && { avatar }), - ...(timezone && { timezone }) - } - ).session(session); - - await refreshSourceAvatar(avatar, user.avatar, session); + const tmb = await MongoTeamMember.findById(tmbId).session(session); + if (timezone) { + await MongoUser.updateOne( + { + _id: tmb?.userId + }, + { + timezone + } + ).session(session); + } + // if avatar, update team member avatar + if (avatar) { + await MongoTeamMember.updateOne( + { + _id: tmbId + }, + { + avatar + } + ).session(session); + await refreshSourceAvatar(avatar, tmb?.avatar, session); + } }); return {}; diff --git a/projects/app/src/pages/api/v1/chat/completions.ts b/projects/app/src/pages/api/v1/chat/completions.ts index 23c245df36cf..52d48341875e 100644 --- a/projects/app/src/pages/api/v1/chat/completions.ts +++ b/projects/app/src/pages/api/v1/chat/completions.ts @@ -280,6 +280,10 @@ async function handler(req: NextApiRequest, res: NextApiResponse) { teamId: String(app.teamId), tmbId: String(app.tmbId) }, + runningUserInfo: { + teamId, + tmbId + }, uid: String(outLinkUserId || tmbId), chatId, diff --git a/projects/app/src/pages/app/detail/components/Logs/DetailLogsModal.tsx b/projects/app/src/pages/app/detail/components/Logs/DetailLogsModal.tsx index d034f21247c5..0098e8cec3b0 100644 --- a/projects/app/src/pages/app/detail/components/Logs/DetailLogsModal.tsx +++ b/projects/app/src/pages/app/detail/components/Logs/DetailLogsModal.tsx @@ -164,8 +164,6 @@ const DetailLogsModal = ({ appId, chatId, onClose }: Props) => { showMarkIcon showVoiceIcon={false} chatType="log" - showRawSource - showNodeStatus /> )}
@@ -187,7 +185,12 @@ const Render = (props: Props) => { }, [appId, chatId]); return ( - + diff --git a/projects/app/src/pages/app/detail/components/Logs/index.tsx b/projects/app/src/pages/app/detail/components/Logs/index.tsx index 263a4c710eee..b104da20be10 100644 --- a/projects/app/src/pages/app/detail/components/Logs/index.tsx +++ b/projects/app/src/pages/app/detail/components/Logs/index.tsx @@ -13,7 +13,7 @@ import { ModalBody, HStack } from '@chakra-ui/react'; -import Avatar from '@fastgpt/web/components/common/Avatar'; +import UserBox from '@fastgpt/web/components/common/UserBox'; import MyIcon from '@fastgpt/web/components/common/Icon'; import { useTranslation } from 'next-i18next'; import { getAppChatLogs } from '@/web/core/app/api'; @@ -30,8 +30,6 @@ import { cardStyles } from '../constants'; import dynamic from 'next/dynamic'; import { useSystem } from '@fastgpt/web/hooks/useSystem'; -import { useUserStore } from '@/web/support/user/useUserStore'; -import { useMount } from 'ahooks'; const DetailLogsModal = dynamic(() => import('./DetailLogsModal')); @@ -40,17 +38,11 @@ const Logs = () => { const { isPc } = useSystem(); const appId = useContextSelector(AppContext, (v) => v.appId); - const { teamMembers, loadAndGetTeamMembers } = useUserStore(); - - useMount(() => { - loadAndGetTeamMembers(); - }); const [dateRange, setDateRange] = useState({ from: addDays(new Date(), -7), to: new Date() }); - const { isOpen: isOpenMarkDesc, onOpen: onOpenMarkDesc, @@ -63,8 +55,7 @@ const Logs = () => { Pagination, getData, pageNum - } = usePagination({ - api: getAppChatLogs, + } = usePagination(getAppChatLogs, { pageSize: 20, params: { appId, @@ -139,15 +130,7 @@ const Logs = () => { {!!item.outLinkUid ? ( item.outLinkUid ) : ( - - v.tmbId === item.tmbId)?.avatar} - w="1.25rem" - /> - - {teamMembers?.find((v) => v.tmbId === item.tmbId)?.memberName} - - + )}
diff --git a/projects/app/src/pages/app/detail/components/PublishHistoriesSlider.tsx b/projects/app/src/pages/app/detail/components/PublishHistoriesSlider.tsx index 50b327d325d4..e367e196376f 100644 --- a/projects/app/src/pages/app/detail/components/PublishHistoriesSlider.tsx +++ b/projects/app/src/pages/app/detail/components/PublishHistoriesSlider.tsx @@ -4,7 +4,7 @@ import { getWorkflowVersionList, updateAppVersion } from '@/web/core/app/api/version'; -import { useVirtualScrollPagination } from '@fastgpt/web/hooks/useScrollPagination'; +import { useScrollPagination } from '@fastgpt/web/hooks/useScrollPagination'; import CustomRightDrawer from '@fastgpt/web/components/common/MyDrawer/CustomRightDrawer'; import { useTranslation } from 'next-i18next'; import { Box, BoxProps, Button, Flex, Input } from '@chakra-ui/react'; @@ -18,9 +18,7 @@ import Tag from '@fastgpt/web/components/common/Tag'; import MyIcon from '@fastgpt/web/components/common/Icon'; import MyPopover from '@fastgpt/web/components/common/MyPopover'; import MyBox from '@fastgpt/web/components/common/MyBox'; -import { useUserStore } from '@/web/support/user/useUserStore'; import { useRequest2 } from '@fastgpt/web/hooks/useRequest'; -import { useSystemStore } from '@/web/common/system/useSystemStore'; import { useToast } from '@fastgpt/web/hooks/useToast'; import type { AppVersionSchemaType, VersionListItemType } from '@fastgpt/global/core/app/version'; import type { SimpleAppSnapshotType } from './SimpleApp/useSnapshots'; @@ -183,23 +181,18 @@ const TeamCloud = ({ }) => { const { t } = useTranslation(); const { appDetail } = useContextSelector(AppContext, (v) => v); - const { loadAndGetTeamMembers } = useUserStore(); - const { feConfigs } = useSystemStore(); - const { scrollDataList, ScrollList, isLoading, fetchData, setData } = useVirtualScrollPagination( - getWorkflowVersionList, - { - itemHeight: 40, - overscan: 20, - - pageSize: 30, - defaultParams: { - appId: appDetail._id - } - } - ); - const { data: members = [] } = useRequest2(loadAndGetTeamMembers, { - manual: !feConfigs.isPlus + const { + ScrollData, + data: scrollDataList, + setData, + isLoading + } = useScrollPagination(getWorkflowVersionList, { + pageSize: 30, + params: { + appId: appDetail._id + }, + refreshDeps: [appDetail._id] }); const [editIndex, setEditIndex] = useState(undefined); const [hoveredIndex, setHoveredIndex] = useState(undefined); @@ -237,15 +230,13 @@ const TeamCloud = ({ ); return ( - - {scrollDataList.map((data, index) => { - const item = data.data; - const firstPublishedIndex = scrollDataList.findIndex((data) => data.data.isPublish); - const tmb = members.find((member) => member.tmbId === item.tmbId); + + {scrollDataList.map((item, index) => { + const firstPublishedIndex = scrollDataList.findIndex((data) => data.isPublish); return ( - +
} > - {({ onClose }) => ( - + {() => ( + - + - - {tmb?.memberName} - - + + {item.sourceMember.name} + {item.sourceMember.status === 'leave' && ( + {t('common:user_leaved')} + )} + + {formatTime2YMDHMS(item.time)} @@ -349,6 +353,6 @@ const TeamCloud = ({ ); })} - + ); }; diff --git a/projects/app/src/pages/app/detail/components/SimpleApp/ChatTest.tsx b/projects/app/src/pages/app/detail/components/SimpleApp/ChatTest.tsx index ac63c0e26da3..0ef02db7e8cf 100644 --- a/projects/app/src/pages/app/detail/components/SimpleApp/ChatTest.tsx +++ b/projects/app/src/pages/app/detail/components/SimpleApp/ChatTest.tsx @@ -91,7 +91,12 @@ const Render = ({ appForm }: Props) => { ); return ( - + diff --git a/projects/app/src/pages/app/detail/components/WorkflowComponents/Flow/ChatTest.tsx b/projects/app/src/pages/app/detail/components/WorkflowComponents/Flow/ChatTest.tsx index 87e27c03c42e..4834f09706b1 100644 --- a/projects/app/src/pages/app/detail/components/WorkflowComponents/Flow/ChatTest.tsx +++ b/projects/app/src/pages/app/detail/components/WorkflowComponents/Flow/ChatTest.tsx @@ -158,7 +158,12 @@ const Render = (Props: Props) => { ); return ( - + diff --git a/projects/app/src/pages/app/detail/components/WorkflowComponents/Flow/NodeTemplatesModal.tsx b/projects/app/src/pages/app/detail/components/WorkflowComponents/Flow/NodeTemplatesModal.tsx index 7a07ada846b3..b48d715dacb4 100644 --- a/projects/app/src/pages/app/detail/components/WorkflowComponents/Flow/NodeTemplatesModal.tsx +++ b/projects/app/src/pages/app/detail/components/WorkflowComponents/Flow/NodeTemplatesModal.tsx @@ -50,7 +50,6 @@ import { useWorkflowUtils } from './hooks/useUtils'; import { moduleTemplatesFlat } from '@fastgpt/global/core/workflow/template/constants'; import { cloneDeep } from 'lodash'; import { useSystem } from '@fastgpt/web/hooks/useSystem'; -import { useUserStore } from '@/web/support/user/useUserStore'; import { LoopStartNode } from '@fastgpt/global/core/workflow/template/system/loop/loopStart'; import { LoopEndNode } from '@fastgpt/global/core/workflow/template/system/loop/loopEnd'; import { NodeInputKeyEnum, NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants'; @@ -89,8 +88,6 @@ enum TemplateTypeEnum { const sliderWidth = 460; const NodeTemplatesModal = ({ isOpen, onClose }: ModuleTemplateListProps) => { - const { loadAndGetTeamMembers } = useUserStore(); - const [parentId, setParentId] = useState(''); const [searchKey, setSearchKey] = useState(''); const { feConfigs } = useSystemStore(); @@ -99,10 +96,6 @@ const NodeTemplatesModal = ({ isOpen, onClose }: ModuleTemplateListProps) => { const nodeList = useContextSelector(WorkflowContext, (v) => v.nodeList); const appId = useContextSelector(WorkflowContext, (v) => v.appId); - const { data: members = [] } = useRequest2(loadAndGetTeamMembers, { - manual: !feConfigs.isPlus - }); - const [templateType, setTemplateType] = useState(TemplateTypeEnum.basic); const { data: basicNodes } = useRequest2( @@ -162,19 +155,10 @@ const NodeTemplatesModal = ({ isOpen, onClose }: ModuleTemplateListProps) => { searchVal?: string; }) => { if (type === TemplateTypeEnum.teamPlugin) { - const teamApps = await getTeamPlugTemplates({ + return getTeamPlugTemplates({ parentId, searchKey: searchVal }).then((res) => res.filter((app) => app.id !== appId)); - - return teamApps.map((app) => { - const member = members.find((member) => member.tmbId === app.tmbId); - return { - ...app, - author: member?.memberName, - authorAvatar: member?.avatar - }; - }); } if (type === TemplateTypeEnum.systemPlugin) { return getSystemPlugTemplates({ @@ -188,7 +172,7 @@ const NodeTemplatesModal = ({ isOpen, onClose }: ModuleTemplateListProps) => { setParentId(parentId); setTemplateType(type); }, - refreshDeps: [members, searchKey, templateType] + refreshDeps: [searchKey, templateType] } ); @@ -420,7 +404,6 @@ const RenderList = React.memo(function RenderList({ templates, type, onClose, - parentId, setParentId }: RenderListProps) { const { t } = useTranslation(); diff --git a/projects/app/src/pages/app/detail/components/WorkflowComponents/Flow/nodes/NodeCode.tsx b/projects/app/src/pages/app/detail/components/WorkflowComponents/Flow/nodes/NodeCode.tsx index 3a58f36658cd..3c048ff4db44 100644 --- a/projects/app/src/pages/app/detail/components/WorkflowComponents/Flow/nodes/NodeCode.tsx +++ b/projects/app/src/pages/app/detail/components/WorkflowComponents/Flow/nodes/NodeCode.tsx @@ -14,22 +14,18 @@ import RenderToolInput from './render/RenderToolInput'; import RenderOutput from './render/RenderOutput'; import CodeEditor from '@fastgpt/web/components/common/Textarea/CodeEditor'; import { Box, Flex } from '@chakra-ui/react'; -import { useI18n } from '@/web/context/I18n'; import { useConfirm } from '@fastgpt/web/hooks/useConfirm'; -import { getLatestNodeTemplate } from '@/web/core/workflow/utils'; -import { CodeNode } from '@fastgpt/global/core/workflow/template/system/sandbox'; +import { JS_TEMPLATE } from '@fastgpt/global/core/workflow/template/system/sandbox/constants'; const NodeCode = ({ data, selected }: NodeProps) => { const { t } = useTranslation(); - const { workflowT } = useI18n(); const { nodeId, inputs, outputs } = data; - const { splitToolInputs, onChangeNode, onResetNode } = useContextSelector( - WorkflowContext, - (ctx) => ctx - ); + + const splitToolInputs = useContextSelector(WorkflowContext, (ctx) => ctx.splitToolInputs); + const onChangeNode = useContextSelector(WorkflowContext, (ctx) => ctx.onChangeNode); const { ConfirmModal, openConfirm } = useConfirm({ - content: workflowT('code.Reset template confirm') + content: t('workflow:code.Reset template confirm') }); const CustomComponent = useMemo(() => { @@ -38,19 +34,24 @@ const NodeCode = ({ data, selected }: NodeProps) => { return ( - {'Javascript ' + workflowT('Code')} + {'Javascript ' + t('workflow:Code')} { - onResetNode({ - id: nodeId, - node: getLatestNodeTemplate(data, CodeNode) + onChangeNode({ + nodeId, + type: 'updateInput', + key: item.key, + value: { + ...item, + value: JS_TEMPLATE + } }); })} > - {workflowT('code.Reset template')} + {t('workflow:code.Reset template')} ) => { ); } }; - }, [data, nodeId, onChangeNode, onResetNode, openConfirm, workflowT]); + }, [nodeId, onChangeNode, openConfirm, t]); - const Render = useMemo(() => { - const { isTool, commonInputs } = splitToolInputs(inputs, nodeId); + const { isTool, commonInputs } = splitToolInputs(inputs, nodeId); - return ( - - {isTool && ( - <> - - - - - )} - - - - - - - - - - - ); - }, [ConfirmModal, CustomComponent, data, inputs, nodeId, outputs, selected, splitToolInputs, t]); - - return Render; + return ( + + {isTool && ( + <> + + + + + )} + + + + + + + + + + + ); }; export default React.memo(NodeCode); diff --git a/projects/app/src/pages/app/detail/components/WorkflowComponents/Flow/nodes/render/RenderInput/Label.tsx b/projects/app/src/pages/app/detail/components/WorkflowComponents/Flow/nodes/render/RenderInput/Label.tsx index d8fb9eb499b3..b372f2655186 100644 --- a/projects/app/src/pages/app/detail/components/WorkflowComponents/Flow/nodes/render/RenderInput/Label.tsx +++ b/projects/app/src/pages/app/detail/components/WorkflowComponents/Flow/nodes/render/RenderInput/Label.tsx @@ -1,5 +1,5 @@ import { FlowNodeInputItemType } from '@fastgpt/global/core/workflow/type/io.d'; -import React, { useCallback, useMemo } from 'react'; +import React, { useCallback } from 'react'; import { useTranslation } from 'next-i18next'; import { Box, Flex } from '@chakra-ui/react'; @@ -10,14 +10,14 @@ import { useContextSelector } from 'use-context-selector'; import { WorkflowContext } from '@/pages/app/detail/components/WorkflowComponents/context'; import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip'; import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel'; -import VariableTip from '@/components/common/Textarea/MyTextarea/VariableTip'; type Props = { nodeId: string; input: FlowNodeInputItemType; + RightComponent?: React.JSX.Element; }; -const InputLabel = ({ nodeId, input }: Props) => { +const InputLabel = ({ nodeId, input, RightComponent }: Props) => { const { t } = useTranslation(); const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode); @@ -68,11 +68,11 @@ const InputLabel = ({ nodeId, input }: Props) => { )} - {/* Variable picker tip */} - {input.renderTypeList[input.selectedTypeIndex ?? 0] === FlowNodeInputTypeEnum.textarea && ( + {/* Right Component */} + {RightComponent && ( <> - - + + {RightComponent} )} diff --git a/projects/app/src/pages/app/detail/components/WorkflowComponents/Flow/nodes/render/RenderInput/index.tsx b/projects/app/src/pages/app/detail/components/WorkflowComponents/Flow/nodes/render/RenderInput/index.tsx index f798ff805aba..d4c4e90173bd 100644 --- a/projects/app/src/pages/app/detail/components/WorkflowComponents/Flow/nodes/render/RenderInput/index.tsx +++ b/projects/app/src/pages/app/detail/components/WorkflowComponents/Flow/nodes/render/RenderInput/index.tsx @@ -8,71 +8,72 @@ import InputLabel from './Label'; import type { RenderInputProps } from './type'; import { useSystemStore } from '@/web/common/system/useSystemStore'; -const RenderList: { - types: FlowNodeInputTypeEnum[]; - Component: React.ComponentType; -}[] = [ - { - types: [FlowNodeInputTypeEnum.reference], +const RenderList: Record< + FlowNodeInputTypeEnum, + | { + Component: React.ComponentType; + LableRightComponent?: React.ComponentType; + } + | undefined +> = { + [FlowNodeInputTypeEnum.reference]: { Component: dynamic(() => import('./templates/Reference')) }, - { - types: [FlowNodeInputTypeEnum.fileSelect], + [FlowNodeInputTypeEnum.fileSelect]: { Component: dynamic(() => import('./templates/Reference')) }, - { - types: [FlowNodeInputTypeEnum.select], + [FlowNodeInputTypeEnum.select]: { Component: dynamic(() => import('./templates/Select')) }, - { - types: [FlowNodeInputTypeEnum.numberInput], + [FlowNodeInputTypeEnum.numberInput]: { Component: dynamic(() => import('./templates/NumberInput')) }, - { - types: [FlowNodeInputTypeEnum.switch], + [FlowNodeInputTypeEnum.switch]: { Component: dynamic(() => import('./templates/Switch')) }, - { - types: [FlowNodeInputTypeEnum.selectApp], + [FlowNodeInputTypeEnum.selectApp]: { Component: dynamic(() => import('./templates/SelectApp')) }, - { - types: [FlowNodeInputTypeEnum.selectLLMModel], + [FlowNodeInputTypeEnum.selectLLMModel]: { Component: dynamic(() => import('./templates/SelectLLMModel')) }, - { - types: [FlowNodeInputTypeEnum.settingLLMModel], + [FlowNodeInputTypeEnum.settingLLMModel]: { Component: dynamic(() => import('./templates/SettingLLMModel')) }, - { - types: [FlowNodeInputTypeEnum.selectDataset], - Component: dynamic(() => import('./templates/SelectDataset')) + [FlowNodeInputTypeEnum.selectDataset]: { + Component: dynamic(() => + import('./templates/SelectDataset').then((mod) => mod.SelectDatasetRender) + ), + LableRightComponent: dynamic(() => + import('./templates/SelectDataset').then((mod) => mod.SwitchAuthTmb) + ) }, - { - types: [FlowNodeInputTypeEnum.selectDatasetParamsModal], + [FlowNodeInputTypeEnum.selectDatasetParamsModal]: { Component: dynamic(() => import('./templates/SelectDatasetParams')) }, - { - types: [FlowNodeInputTypeEnum.addInputParam], + [FlowNodeInputTypeEnum.addInputParam]: { Component: dynamic(() => import('./templates/DynamicInputs/index')) }, - { - types: [FlowNodeInputTypeEnum.JSONEditor], + [FlowNodeInputTypeEnum.JSONEditor]: { Component: dynamic(() => import('./templates/JsonEditor')) }, - { - types: [FlowNodeInputTypeEnum.settingDatasetQuotePrompt], + [FlowNodeInputTypeEnum.settingDatasetQuotePrompt]: { Component: dynamic(() => import('./templates/SettingQuotePrompt')) }, - { - types: [FlowNodeInputTypeEnum.input], + [FlowNodeInputTypeEnum.input]: { Component: dynamic(() => import('./templates/TextInput')) }, - { - types: [FlowNodeInputTypeEnum.textarea], - Component: dynamic(() => import('./templates/Textarea')) - } -]; + [FlowNodeInputTypeEnum.textarea]: { + Component: dynamic(() => import('./templates/Textarea')), + LableRightComponent: dynamic(() => + import('./templates/Textarea').then((mod) => mod.TextareaRightComponent) + ) + }, + + [FlowNodeInputTypeEnum.customVariable]: undefined, + [FlowNodeInputTypeEnum.hidden]: undefined, + [FlowNodeInputTypeEnum.custom]: undefined +}; const hideLabelTypeList = [FlowNodeInputTypeEnum.addInputParam]; @@ -101,7 +102,7 @@ const RenderInput = ({ flowInputList, nodeId, CustomComponent, mb = 5 }: Props) return true; }); - }, [feConfigs?.isPlus, flowInputList]); + }, [filterProInputs]); return ( <> @@ -110,23 +111,41 @@ const RenderInput = ({ flowInputList, nodeId, CustomComponent, mb = 5 }: Props) const RenderComponent = (() => { if (renderType === FlowNodeInputTypeEnum.custom && CustomComponent?.[input.key]) { - return <>{CustomComponent?.[input.key]({ ...input })}; + return { + Component: <>{CustomComponent?.[input.key]({ ...input })} + }; } - const Component = RenderList.find((item) => item.types.includes(renderType))?.Component; + const RenderItem = RenderList[renderType]; + + if (!RenderItem) return null; - if (!Component) return null; - return ; + return { + Component: ( + + ), + LableRightComponent: RenderItem.LableRightComponent ? ( + + ) : undefined + }; })(); return ( {!!input.label && !hideLabelTypeList.includes(renderType) && ( - + )} {!!RenderComponent && ( - {RenderComponent} + {RenderComponent.Component} )} diff --git a/projects/app/src/pages/app/detail/components/WorkflowComponents/Flow/nodes/render/RenderInput/templates/SelectDataset.tsx b/projects/app/src/pages/app/detail/components/WorkflowComponents/Flow/nodes/render/RenderInput/templates/SelectDataset.tsx index 162208986da4..b4bdb70e8888 100644 --- a/projects/app/src/pages/app/detail/components/WorkflowComponents/Flow/nodes/render/RenderInput/templates/SelectDataset.tsx +++ b/projects/app/src/pages/app/detail/components/WorkflowComponents/Flow/nodes/render/RenderInput/templates/SelectDataset.tsx @@ -1,6 +1,6 @@ import React, { useEffect, useMemo, useState } from 'react'; import type { RenderInputProps } from '../type'; -import { Box, Button, Flex, Grid, useDisclosure, useTheme } from '@chakra-ui/react'; +import { Box, Button, Flex, Grid, Switch, useDisclosure, useTheme } from '@chakra-ui/react'; import { useDatasetStore } from '@/web/core/dataset/store/dataset'; import { SelectedDatasetType } from '@fastgpt/global/core/workflow/api'; import Avatar from '@fastgpt/web/components/common/Avatar'; @@ -12,12 +12,17 @@ import dynamic from 'next/dynamic'; import MyIcon from '@fastgpt/web/components/common/Icon'; import { useContextSelector } from 'use-context-selector'; import { WorkflowContext } from '@/pages/app/detail/components/WorkflowComponents/context'; +import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip'; +import { NodeInputKeyEnum } from '@fastgpt/global/core/workflow/constants'; const DatasetSelectModal = dynamic(() => import('@/components/core/app/DatasetSelectModal')); -const SelectDatasetRender = ({ inputs = [], item, nodeId }: RenderInputProps) => { +export const SelectDatasetRender = React.memo(function SelectDatasetRender({ + inputs = [], + item, + nodeId +}: RenderInputProps) { const { t } = useTranslation(); - const theme = useTheme(); const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode); const [data, setData] = useState({ @@ -80,8 +85,9 @@ const SelectDatasetRender = ({ inputs = [], item, nodeId }: RenderInputProps) => key={item._id} alignItems={'center'} h={10} - border={theme.borders.base} - borderColor={'myGray.200'} + boxShadow={'sm'} + bg={'white'} + border={'base'} px={2} borderRadius={'md'} > @@ -128,11 +134,47 @@ const SelectDatasetRender = ({ inputs = [], item, nodeId }: RenderInputProps) => onOpenDatasetSelect, selectedDatasets, selectedDatasetsValue, - t, - theme.borders.base + t ]); return Render; -}; +}); -export default React.memo(SelectDatasetRender); +export const SwitchAuthTmb = React.memo(function SwitchAuthTmb({ + inputs = [], + item, + nodeId +}: RenderInputProps) { + const { t } = useTranslation(); + const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode); + + const authTmbIdInput = useMemo( + () => inputs.find((v) => v.key === NodeInputKeyEnum.authTmbId), + [inputs] + ); + + return authTmbIdInput ? ( + + {t('workflow:auth_tmb_id')} + + { + onChangeNode({ + nodeId, + key: NodeInputKeyEnum.authTmbId, + type: 'updateInput', + value: { + ...authTmbIdInput, + value: e.target.checked + } + }); + }} + /> + + ) : null; +}); + +export default SelectDatasetRender; diff --git a/projects/app/src/pages/app/detail/components/WorkflowComponents/Flow/nodes/render/RenderInput/templates/Textarea.tsx b/projects/app/src/pages/app/detail/components/WorkflowComponents/Flow/nodes/render/RenderInput/templates/Textarea.tsx index ef5d355cad3b..d190e6fb2839 100644 --- a/projects/app/src/pages/app/detail/components/WorkflowComponents/Flow/nodes/render/RenderInput/templates/Textarea.tsx +++ b/projects/app/src/pages/app/detail/components/WorkflowComponents/Flow/nodes/render/RenderInput/templates/Textarea.tsx @@ -9,6 +9,7 @@ import { AppContext } from '@/pages/app/detail/components/context'; import { getEditorVariables } from '../../../../../utils'; import { WorkflowNodeEdgeContext } from '../../../../../context/workflowInitContext'; import { useSystemStore } from '@/web/common/system/useSystemStore'; +import VariableTip from '@/components/common/Textarea/MyTextarea/VariableTip'; const TextareaRender = ({ item, nodeId }: RenderInputProps) => { const { t } = useTranslation(); @@ -84,3 +85,10 @@ const TextareaRender = ({ item, nodeId }: RenderInputProps) => { }; export default React.memo(TextareaRender); + +export const TextareaRightComponent = React.memo(function TextareaRightComponent({ + item, + nodeId +}: RenderInputProps) { + return ; +}); diff --git a/projects/app/src/pages/app/detail/components/useChatTest.tsx b/projects/app/src/pages/app/detail/components/useChatTest.tsx index 7a204c309a25..22a54dfeb8c2 100644 --- a/projects/app/src/pages/app/detail/components/useChatTest.tsx +++ b/projects/app/src/pages/app/detail/components/useChatTest.tsx @@ -141,8 +141,6 @@ export const useChatTest = ({ chatId={chatId} showMarkIcon chatType="chat" - showRawSource - showNodeStatus onStartChat={startChat} /> ) diff --git a/projects/app/src/pages/app/list/components/List.tsx b/projects/app/src/pages/app/list/components/List.tsx index ff31441640b3..66af89fbe16a 100644 --- a/projects/app/src/pages/app/list/components/List.tsx +++ b/projects/app/src/pages/app/list/components/List.tsx @@ -34,8 +34,8 @@ import { postCopyApp } from '@/web/core/app/api/app'; import { formatTimeToChatTime } from '@fastgpt/global/common/string/time'; import { useSystem } from '@fastgpt/web/hooks/useSystem'; import { useChatStore } from '@/web/core/chat/context/useChatStore'; -import { useUserStore } from '@/web/support/user/useUserStore'; import { RequireOnlyOne } from '@fastgpt/global/common/type/utils'; +import UserBox from '@fastgpt/web/components/common/UserBox'; const HttpEditModal = dynamic(() => import('./HttpPluginEditModal')); const ListItem = () => { @@ -44,8 +44,6 @@ const ListItem = () => { const { parentId = null } = router.query; const { isPc } = useSystem(); - const { loadAndGetTeamMembers } = useUserStore(); - const { openConfirm: openMoveConfirm, ConfirmModal: MoveConfirmModal } = useConfirm({ type: 'common', title: t('common:move.confirm'), @@ -115,10 +113,6 @@ const ListItem = () => { successToast: t('app:create_copy_success') }); - const { data: members = [] } = useRequest2(loadAndGetTeamMembers, { - manual: false - }); - const { runAsync: onResumeInheritPermission } = useRequest2( () => { return resumeInheritPer(editPerApp!._id); @@ -145,7 +139,6 @@ const ListItem = () => { alignItems={'stretch'} > {myApps.map((app, index) => { - const owner = members.find((v) => v.tmbId === app.tmbId); return ( { color={'myGray.500'} > - {owner && ( - - - - {owner.memberName} - - - )} - + void; totalRecordsCount: number; }) => { const { t } = useTranslation(); @@ -71,7 +69,7 @@ const ChatHeader = ({ )} {/* control */} - {!isPlugin && } + {!isPlugin && } ); }; diff --git a/projects/app/src/pages/chat/components/ChatHistorySlider.tsx b/projects/app/src/pages/chat/components/ChatHistorySlider.tsx index 8b025916897f..96fa849264a0 100644 --- a/projects/app/src/pages/chat/components/ChatHistorySlider.tsx +++ b/projects/app/src/pages/chat/components/ChatHistorySlider.tsx @@ -28,7 +28,6 @@ type HistoryItemType = { const ChatHistorySlider = ({ confirmClearText }: { confirmClearText: string }) => { const theme = useTheme(); const router = useRouter(); - const isUserChatPage = router.pathname === '/chat'; const { t } = useTranslation(); @@ -46,6 +45,7 @@ const ChatHistorySlider = ({ confirmClearText }: { confirmClearText: string }) = const appName = useContextSelector(ChatItemContext, (v) => v.chatBoxData?.app.name); const appAvatar = useContextSelector(ChatItemContext, (v) => v.chatBoxData?.app.avatar); + const showRouteToAppDetail = useContextSelector(ChatItemContext, (v) => v.showRouteToAppDetail); const concatHistory = useMemo(() => { const formatHistories: HistoryItemType[] = histories.map((item) => { @@ -77,8 +77,8 @@ const ChatHistorySlider = ({ confirmClearText }: { confirmClearText: string }) = }); const canRouteToDetail = useMemo( - () => appId && userInfo?.team.permission.hasWritePer, - [appId, userInfo?.team.permission.hasWritePer] + () => appId && userInfo?.team.permission.hasWritePer && showRouteToAppDetail, + [appId, userInfo?.team.permission.hasWritePer, showRouteToAppDetail] ); return ( @@ -287,7 +287,7 @@ const ChatHistorySlider = ({ confirmClearText }: { confirmClearText: string }) = {/* exec */} - {!isPc && isUserChatPage && ( + {!isPc && !!canRouteToDetail && ( import('@/components/common/folder/SelectOneResource')); @@ -22,6 +24,8 @@ const SliderApps = ({ apps, activeAppId }: { apps: AppListItemType[]; activeAppI const router = useRouter(); const isTeamChat = router.pathname === '/chat/team'; + const showRouteToAppDetail = useContextSelector(ChatItemContext, (v) => v.showRouteToAppDetail); + const getAppList = useCallback(async ({ parentId }: GetResourceFolderListProps) => { return getMyApps({ parentId, @@ -50,34 +54,36 @@ const SliderApps = ({ apps, activeAppId }: { apps: AppListItemType[]; activeAppI return ( - - {!isTeamChat && ( - router.push('/app/list')} - > - } - bg={'white'} - boxShadow={'1px 1px 9px rgba(0,0,0,0.15)'} - size={'smSquare'} - borderRadius={'50%'} - aria-label={''} - /> - {t('common:core.chat.Exit Chat')} - - )} - + {showRouteToAppDetail && ( + <> + + router.push('/app/list')} + > + } + bg={'white'} + boxShadow={'1px 1px 9px rgba(0,0,0,0.15)'} + size={'smSquare'} + borderRadius={'50%'} + aria-label={''} + /> + {t('common:core.chat.Exit Chat')} + + + + + )} {!isTeamChat && ( <> - void; -}) => { +const ToolMenu = ({ history }: { history: ChatItemType[] }) => { + const router = useRouter(); const { t } = useTranslation(); const { onExportChat } = useChatBox(); const onChangeChatId = useContextSelector(ChatContext, (v) => v.onChangeChatId); + const chatData = useContextSelector(ChatItemContext, (v) => v.chatBoxData); + const showRouteToAppDetail = useContextSelector(ChatItemContext, (v) => v.showRouteToAppDetail); - return history.length > 0 ? ( + return ( router.push(`/app/detail?appId=${chatData.appId}`) } ] } @@ -76,8 +75,6 @@ const ToolMenu = ({ : []) ]} /> - ) : ( - ); }; diff --git a/projects/app/src/pages/chat/index.tsx b/projects/app/src/pages/chat/index.tsx index e80019041444..e574d0ff0dab 100644 --- a/projects/app/src/pages/chat/index.tsx +++ b/projects/app/src/pages/chat/index.tsx @@ -44,6 +44,7 @@ const Chat = ({ myApps }: { myApps: AppListItemType[] }) => { const theme = useTheme(); const { t } = useTranslation(); const { isPc } = useSystem(); + const { userInfo } = useUserStore(); const { setLastChatAppId, chatId, appId, outLinkAuthData } = useChatStore(); @@ -186,7 +187,6 @@ const Chat = ({ myApps }: { myApps: AppListItemType[] }) => { apps={myApps} history={chatRecords} showHistory - onRouteToAppDetail={() => router.push(`/app/detail?appId=${appId}`)} /> {/* chat box */} @@ -208,8 +208,6 @@ const Chat = ({ myApps }: { myApps: AppListItemType[] }) => { feedbackType={'user'} onStartChat={onStartChat} chatType={'chat'} - showRawSource - showNodeStatus isReady={!loading} /> )} @@ -221,8 +219,8 @@ const Chat = ({ myApps }: { myApps: AppListItemType[] }) => { ); }; -const Render = (props: { appId: string }) => { - const { appId } = props; +const Render = (props: { appId: string; isStandalone?: string }) => { + const { appId, isStandalone } = props; const { t } = useTranslation(); const { toast } = useToast(); const router = useRouter(); @@ -276,7 +274,12 @@ const Render = (props: { appId: string }) => { return source === ChatSourceEnum.online ? ( - + @@ -289,6 +292,7 @@ export async function getServerSideProps(context: any) { return { props: { appId: context?.query?.appId || '', + isStandalone: context?.query?.isStandalone || '', ...(await serviceSideProps(context, ['file', 'app', 'chat', 'workflow'])) } }; diff --git a/projects/app/src/pages/chat/share.tsx b/projects/app/src/pages/chat/share.tsx index 74f898be417a..2dd85c879525 100644 --- a/projects/app/src/pages/chat/share.tsx +++ b/projects/app/src/pages/chat/share.tsx @@ -55,7 +55,6 @@ type Props = { const OutLink = (props: Props) => { const { t } = useTranslation(); const router = useRouter(); - const { showRawSource, showNodeStatus } = props; const { shareId = '', showHistory = '1', @@ -287,8 +286,6 @@ const OutLink = (props: Props) => { feedbackType={'user'} onStartChat={startChat} chatType="share" - showRawSource={showRawSource} - showNodeStatus={showNodeStatus} /> )} @@ -340,7 +337,12 @@ const Render = (props: Props) => { return source === ChatSourceEnum.share ? ( - + diff --git a/projects/app/src/pages/chat/team.tsx b/projects/app/src/pages/chat/team.tsx index 5bb080de15af..db0c5af31b1b 100644 --- a/projects/app/src/pages/chat/team.tsx +++ b/projects/app/src/pages/chat/team.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useEffect, useMemo, useState } from 'react'; +import React, { useCallback, useEffect, useMemo } from 'react'; import NextHead from '@/components/common/NextHead'; import { getTeamChatInfo } from '@/web/core/chat/api'; import { useRouter } from 'next/router'; @@ -20,8 +20,7 @@ import { useRequest2 } from '@fastgpt/web/hooks/useRequest'; import ChatContextProvider, { ChatContext } from '@/web/core/chat/context/chatContext'; import { AppListItemType } from '@fastgpt/global/core/app/type'; import { useContextSelector } from 'use-context-selector'; -import { InitChatResponse } from '@/global/core/chat/api'; -import { defaultChatData, GetChatTypeEnum } from '@/global/core/chat/constants'; +import { GetChatTypeEnum } from '@/global/core/chat/constants'; import { AppTypeEnum } from '@fastgpt/global/core/app/constants'; import { getNanoid } from '@fastgpt/global/common/string/tools'; @@ -226,8 +225,6 @@ const Chat = ({ myApps }: { myApps: AppListItemType[] }) => { feedbackType={'user'} onStartChat={startChat} chatType="team" - showRawSource - showNodeStatus /> )}
@@ -299,7 +296,12 @@ const Render = (props: Props) => { return source === ChatSourceEnum.team ? ( - + diff --git a/projects/app/src/pages/dataset/detail/components/CollectionCard/Context.tsx b/projects/app/src/pages/dataset/detail/components/CollectionCard/Context.tsx index 3ac4ea160d84..f43bf12bb497 100644 --- a/projects/app/src/pages/dataset/detail/components/CollectionCard/Context.tsx +++ b/projects/app/src/pages/dataset/detail/components/CollectionCard/Context.tsx @@ -111,8 +111,7 @@ const CollectionPageContextProvider = ({ children }: { children: ReactNode }) => isLoading: isGetting, pageNum, pageSize - } = usePagination({ - api: getDatasetCollections, + } = usePagination(getDatasetCollections, { pageSize: 20, params: { datasetId, diff --git a/projects/app/src/pages/dataset/detail/components/Import/diffSource/APIDataset.tsx b/projects/app/src/pages/dataset/detail/components/Import/diffSource/APIDataset.tsx index e261a959a0c5..e0cdff95ca54 100644 --- a/projects/app/src/pages/dataset/detail/components/Import/diffSource/APIDataset.tsx +++ b/projects/app/src/pages/dataset/detail/components/Import/diffSource/APIDataset.tsx @@ -49,7 +49,6 @@ const CustomAPIFileInput = () => { parentId: '', parentName: '' }); - const [parentUuid, setParentUuid] = useState(''); const [paths, setPaths] = useState([]); const [searchKey, setSearchKey] = useState(''); @@ -128,7 +127,7 @@ const CustomAPIFileInput = () => { const handleItemClick = useCallback( (item: APIFileItem) => { - if (item.type === 'folder') { + if (item.hasChild) { setPaths((state) => [...state, { parentId: item.id, parentName: item.name }]); return setParent({ parentId: item.id, @@ -251,6 +250,7 @@ const CustomAPIFileInput = () => { {item.name} + {item.hasChild && } ); })} diff --git a/projects/app/src/pages/dataset/list/component/List.tsx b/projects/app/src/pages/dataset/list/component/List.tsx index 709990a9971f..db711e176b87 100644 --- a/projects/app/src/pages/dataset/list/component/List.tsx +++ b/projects/app/src/pages/dataset/list/component/List.tsx @@ -28,10 +28,10 @@ import EmptyTip from '@fastgpt/web/components/common/EmptyTip'; import { useFolderDrag } from '@/components/common/folder/useFolderDrag'; import MyBox from '@fastgpt/web/components/common/MyBox'; import { useTranslation } from 'next-i18next'; -import { useUserStore } from '@/web/support/user/useUserStore'; import { useSystem } from '@fastgpt/web/hooks/useSystem'; import SideTag from './SideTag'; import { getModelProvider } from '@fastgpt/global/core/ai/provider'; +import UserBox from '@fastgpt/web/components/common/UserBox'; const EditResourceModal = dynamic(() => import('@/components/common/Modal/EditResourceModal')); @@ -39,7 +39,6 @@ function List() { const { setLoading } = useSystemStore(); const { isPc } = useSystem(); const { t } = useTranslation(); - const { loadAndGetTeamMembers } = useUserStore(); const { loadMyDatasets, setMoveDatasetId, @@ -81,10 +80,6 @@ function List() { } }); - const { data: members = [] } = useRequest2(loadAndGetTeamMembers, { - manual: false - }); - const editPerDataset = useMemo( () => (editPerDatasetIndex !== undefined ? myDatasets[editPerDatasetIndex] : undefined), [editPerDatasetIndex, myDatasets] @@ -156,7 +151,6 @@ function List() { alignItems={'stretch'} > {formatDatasets.map((dataset, index) => { - const owner = members.find((v) => v.tmbId === dataset.tmbId); const vectorModelAvatar = getModelProvider(dataset.vectorModel.provider)?.avatar; return ( @@ -265,14 +259,12 @@ function List() { color={'myGray.500'} > - {owner && ( - - - - {owner.memberName} - - - )} + { teamId: String(app.teamId), tmbId: String(app.tmbId) }, + runningUserInfo: { + teamId: String(app.teamId), + tmbId: String(app.tmbId) + }, uid: String(app.tmbId), runtimeNodes: storeNodes2RuntimeNodes(nodes, getWorkflowEntryNodeIds(nodes)), runtimeEdges: initWorkflowEdgeStatus(edges), diff --git a/projects/app/src/types/app.d.ts b/projects/app/src/types/app.d.ts index fffc4e194362..cbc9dafeeacf 100644 --- a/projects/app/src/types/app.d.ts +++ b/projects/app/src/types/app.d.ts @@ -13,6 +13,8 @@ import type { FlowNodeTemplateType } from '@fastgpt/global/core/workflow/type/no import type { ChatSchema } from '@fastgpt/global/core/chat/type'; import type { AppSchema } from '@fastgpt/global/core/app/type'; import { ChatModelType } from '@/constants/model'; +import { TeamMemberStatusEnum } from '@fastgpt/global/support/user/team/constant'; +import { SourceMember } from '@fastgpt/global/support/user/type'; export interface ShareAppItem { _id: string; @@ -45,4 +47,5 @@ export type AppLogsListItemType = { markCount: number; outLinkUid?: string; tmbId: string; + sourceMember: SourceMember; }; diff --git a/projects/app/src/types/index.d.ts b/projects/app/src/types/index.d.ts index 228ee1df8164..84dfaf634d23 100644 --- a/projects/app/src/types/index.d.ts +++ b/projects/app/src/types/index.d.ts @@ -10,15 +10,6 @@ import { import { TrackEventName } from '@/web/common/system/constants'; import { SubPlanType } from '@fastgpt/global/support/wallet/sub/type'; -export type PagingData = { - pageNum: number; - pageSize: number; - data: T[]; - total?: number; -}; - -export type RequestPaging = { pageNum: number; pageSize: number; [key]: any }; - declare global { var qaQueueLen: number; var vectorQueueLen: number; diff --git a/projects/app/src/web/core/app/api.ts b/projects/app/src/web/core/app/api.ts index 26675705b940..d1908a48ee4e 100644 --- a/projects/app/src/web/core/app/api.ts +++ b/projects/app/src/web/core/app/api.ts @@ -5,7 +5,7 @@ import { AppUpdateParams, AppChangeOwnerBody } from '@/global/core/app/api'; import type { CreateAppBody } from '@/pages/api/core/app/create'; import type { ListAppBody } from '@/pages/api/core/app/list'; import { AppLogsListItemType } from '@/types/app'; -import { PagingData } from '@/types'; +import { PaginationResponse } from '@fastgpt/web/common/fetch/type'; /** * 获取应用列表 @@ -39,7 +39,7 @@ export const putAppById = (id: string, data: AppUpdateParams) => // =================== chat logs export const getAppChatLogs = (data: GetAppChatLogsParams) => - POST>(`/core/app/getChatLogs`, data); + POST>(`/core/app/getChatLogs`, data); export const resumeInheritPer = (appId: string) => GET(`/core/app/resumeInheritPermission`, { appId }); diff --git a/projects/app/src/web/core/app/api/plugin.ts b/projects/app/src/web/core/app/api/plugin.ts index ed13b625ef40..a2353e66679a 100644 --- a/projects/app/src/web/core/app/api/plugin.ts +++ b/projects/app/src/web/core/app/api/plugin.ts @@ -35,7 +35,8 @@ export const getTeamPlugTemplates = (data?: ListAppBody) => intro: app.intro, showStatus: false, version: app.pluginData?.nodeVersion || defaultNodeVersion, - isTool: true + isTool: true, + sourceMember: app.sourceMember })) ); diff --git a/projects/app/src/web/core/app/api/version.ts b/projects/app/src/web/core/app/api/version.ts index 967d55bfea91..daa0adb68848 100644 --- a/projects/app/src/web/core/app/api/version.ts +++ b/projects/app/src/web/core/app/api/version.ts @@ -1,5 +1,5 @@ import { PostPublishAppProps } from '@/global/core/app/api'; -import { GET, POST, DELETE, PUT } from '@/web/common/api/request'; +import { GET, POST } from '@/web/common/api/request'; import type { AppVersionSchemaType } from '@fastgpt/global/core/app/version'; import { PaginationProps } from '@fastgpt/web/common/fetch/type'; import type { diff --git a/projects/app/src/web/core/chat/context/chatItemContext.tsx b/projects/app/src/web/core/chat/context/chatItemContext.tsx index 991e2e90fd78..633fae4d2a4e 100644 --- a/projects/app/src/web/core/chat/context/chatItemContext.tsx +++ b/projects/app/src/web/core/chat/context/chatItemContext.tsx @@ -9,6 +9,12 @@ import { AppTypeEnum } from '@fastgpt/global/core/app/constants'; import { AppChatConfigType, VariableItemType } from '@fastgpt/global/core/app/type'; import { FlowNodeInputItemType } from '@fastgpt/global/core/workflow/type/io'; +type ContextProps = { + showRouteToAppDetail: boolean; + showRouteToDatasetDetail: boolean; + isShowReadRawSource: boolean; + showNodeStatus: boolean; +}; type ChatBoxDataType = { appId: string; title?: string; @@ -37,7 +43,7 @@ type ChatItemContextType = { chatBoxData: ChatBoxDataType; setChatBoxData: React.Dispatch>; isPlugin: boolean; -}; +} & ContextProps; export const ChatItemContext = createContext({ ChatBoxRef: null, @@ -61,7 +67,15 @@ export const ChatItemContext = createContext({ /* Chat 对象的上下文 */ -const ChatItemContextProvider = ({ children }: { children: ReactNode }) => { +const ChatItemContextProvider = ({ + children, + showRouteToAppDetail, + showRouteToDatasetDetail, + isShowReadRawSource, + showNodeStatus +}: { + children: ReactNode; +} & ContextProps) => { const ChatBoxRef = useRef(null); const variablesForm = useForm(); @@ -113,16 +127,23 @@ const ChatItemContextProvider = ({ children }: { children: ReactNode }) => { pluginRunTab, setPluginRunTab, resetVariables, - clearChatRecords + clearChatRecords, + showRouteToAppDetail, + showRouteToDatasetDetail, + isShowReadRawSource, + showNodeStatus }; }, [ chatBoxData, - setChatBoxData, - clearChatRecords, isPlugin, + variablesForm, pluginRunTab, resetVariables, - variablesForm + clearChatRecords, + showRouteToAppDetail, + showRouteToDatasetDetail, + isShowReadRawSource, + showNodeStatus ]); return {children}; diff --git a/projects/app/src/web/core/chat/context/chatRecordContext.tsx b/projects/app/src/web/core/chat/context/chatRecordContext.tsx index 16adcd193395..a01c1712d4ff 100644 --- a/projects/app/src/web/core/chat/context/chatRecordContext.tsx +++ b/projects/app/src/web/core/chat/context/chatRecordContext.tsx @@ -2,7 +2,7 @@ import { getPaginationRecordsBody } from '@/pages/api/core/chat/getPaginationRec import { ChatSiteItemType } from '@fastgpt/global/core/chat/type'; import { PaginationResponse } from '@fastgpt/web/common/fetch/type'; import { useScrollPagination } from '@fastgpt/web/hooks/useScrollPagination'; -import React, { ReactNode, useEffect, useMemo, useState } from 'react'; +import React, { ReactNode, useMemo, useState } from 'react'; import { createContext, useContextSelector } from 'use-context-selector'; import { ChatItemContext } from './chatItemContext'; import { getChatRecords } from '../api'; @@ -68,7 +68,7 @@ const ChatRecordContextProvider = ({ const res = await getChatRecords(data); // First load scroll to bottom - if (data.offset === 0) { + if (Number(data.offset) === 0) { function scrollToBottom() { requestAnimationFrame( ChatBoxRef?.current ? () => ChatBoxRef?.current?.scrollToBottom?.() : scrollToBottom diff --git a/projects/app/src/web/core/dataset/api.ts b/projects/app/src/web/core/dataset/api.ts index 5a17b7fce889..ac40e1a02ecd 100644 --- a/projects/app/src/web/core/dataset/api.ts +++ b/projects/app/src/web/core/dataset/api.ts @@ -37,7 +37,6 @@ import type { DatasetCollectionItemType } from '@fastgpt/global/core/dataset/typ import { DatasetCollectionSyncResultEnum } from '@fastgpt/global/core/dataset/constants'; import type { DatasetDataItemType } from '@fastgpt/global/core/dataset/type'; import type { DatasetCollectionsListItemType } from '@/global/core/dataset/type.d'; -import { PagingData } from '@/types'; import type { getDatasetTrainingQueueResponse } from '@/pages/api/core/dataset/training/getDatasetTrainingQueue'; import type { rebuildEmbeddingBody } from '@/pages/api/core/dataset/training/rebuildEmbedding'; import type { @@ -66,8 +65,6 @@ import type { listExistIdQuery, listExistIdResponse } from '@/pages/api/core/dataset/apiDataset/listExistId'; -import { FeishuServer, YuqueServer } from '@fastgpt/global/core/dataset/apiDataset'; -import { RequireOnlyOne } from '@fastgpt/global/common/type/utils'; /* ======================== dataset ======================= */ export const getDatasets = (data: GetDatasetListBody) => @@ -110,7 +107,7 @@ export const postSearchText = (data: SearchTestProps) => /* ============================= collections ==================================== */ export const getDatasetCollections = (data: GetDatasetCollectionsProps) => - POST>(`/core/dataset/collection/list`, data); + POST>(`/core/dataset/collection/listV2`, data); export const getDatasetCollectionPathById = (parentId: string) => GET(`/core/dataset/collection/paths`, { parentId }); export const getDatasetCollectionById = (id: string) => diff --git a/projects/app/src/web/core/dataset/components/SelectCollections.tsx b/projects/app/src/web/core/dataset/components/SelectCollections.tsx index 7b09e0e7a8b3..5ca8b139e771 100644 --- a/projects/app/src/web/core/dataset/components/SelectCollections.tsx +++ b/projects/app/src/web/core/dataset/components/SelectCollections.tsx @@ -1,7 +1,7 @@ import MyIcon from '@fastgpt/web/components/common/Icon'; import MyModal from '@fastgpt/web/components/common/MyModal'; import ParentPaths from '@/components/common/ParentPaths'; -import { useRequest } from '@fastgpt/web/hooks/useRequest'; +import { useRequest, useRequest2 } from '@fastgpt/web/hooks/useRequest'; import { getDatasetCollectionPathById, getDatasetCollections } from '@/web/core/dataset/api'; import { Box, Flex, ModalFooter, Button, useTheme, Grid, Card, ModalBody } from '@chakra-ui/react'; import { DatasetCollectionTypeEnum } from '@fastgpt/global/core/dataset/constants'; @@ -48,20 +48,24 @@ const SelectCollections = ({ useQuery(['loadDatasetDetail', datasetId], () => loadDatasetDetail(datasetId)); - const { data, isLoading } = useQuery(['getDatasetCollections', parentId], () => - getDatasetCollections({ - datasetId, - parentId, - selectFolder: type === 'folder', - simple: true, - pageNum: 1, - pageSize: 50 - }) + const { data, loading: isLoading } = useRequest2( + () => + getDatasetCollections({ + datasetId, + parentId, + selectFolder: type === 'folder', + simple: true, + pageNum: 1, + pageSize: 50 + }), + { + manual: false, + refreshDeps: [datasetId, parentId, type] + } ); - const formatCollections = useMemo( () => - data?.data.map((collection) => { + data?.list.map((collection) => { const icon = getCollectionIcon(collection.type, collection.name); return { @@ -111,7 +115,7 @@ const SelectCollections = ({ title={ ({ + paths={paths.map((path) => ({ parentId: path.parentId, parentName: path.parentName }))} diff --git a/projects/app/src/web/core/workflow/utils.ts b/projects/app/src/web/core/workflow/utils.ts index 1d5c6f99854b..209b284f872d 100644 --- a/projects/app/src/web/core/workflow/utils.ts +++ b/projects/app/src/web/core/workflow/utils.ts @@ -118,7 +118,7 @@ export const storeNode2FlowNode = ({ toolDescription: t(templateInput.toolDescription ?? (storeInput.toolDescription as any)), selectedTypeIndex: storeInput.selectedTypeIndex ?? templateInput.selectedTypeIndex, - value: storeInput.value ?? templateInput.value, + value: storeInput.value, valueType: storeInput.valueType ?? templateInput.valueType, label: storeInput.label ?? templateInput.label }; diff --git a/projects/app/src/web/support/activity/promotion/api.ts b/projects/app/src/web/support/activity/promotion/api.ts index b44cf67cbaf2..abccbee88c73 100644 --- a/projects/app/src/web/support/activity/promotion/api.ts +++ b/projects/app/src/web/support/activity/promotion/api.ts @@ -1,6 +1,6 @@ -import { GET, POST, PUT } from '@/web/common/api/request'; +import { GET, POST } from '@/web/common/api/request'; import type { PromotionRecordType } from '@/global/support/api/userRes.d'; -import { PagingData, type RequestPaging } from '@/types'; +import { PaginationProps, PaginationResponse } from '@fastgpt/web/common/fetch/type'; /* get promotion init data */ export const getPromotionInitData = () => @@ -10,5 +10,8 @@ export const getPromotionInitData = () => }>('/proApi/support/activity/promotion/getPromotionData'); /* promotion records */ -export const getPromotionRecords = (data: RequestPaging) => - POST>(`/proApi/support/activity/promotion/getPromotions`, data); +export const getPromotionRecords = (data: PaginationProps) => + POST>( + `/proApi/support/activity/promotion/getPromotions`, + data + ); diff --git a/projects/app/src/web/support/user/api.ts b/projects/app/src/web/support/user/api.ts index 6c6e17022632..b1d076d374bc 100644 --- a/projects/app/src/web/support/user/api.ts +++ b/projects/app/src/web/support/user/api.ts @@ -90,3 +90,5 @@ export const getCaptchaPic = (username: string) => GET<{ captchaImage: string; }>('/proApi/support/user/account/captcha/getImgCaptcha', { username }); + +export const postSyncMembers = () => POST('/proApi/support/user/team/org/sync'); diff --git a/projects/app/src/web/support/user/inform/api.ts b/projects/app/src/web/support/user/inform/api.ts index fb5d4434dcbe..206ad2ab1e4b 100644 --- a/projects/app/src/web/support/user/inform/api.ts +++ b/projects/app/src/web/support/user/inform/api.ts @@ -1,10 +1,10 @@ -import { GET, POST, PUT } from '@/web/common/api/request'; -import type { PagingData, RequestPaging } from '@/types'; +import { GET, POST } from '@/web/common/api/request'; import type { UserInformSchema } from '@fastgpt/global/support/user/inform/type'; import { SystemMsgModalValueType } from '@fastgpt/service/support/user/inform/type'; +import { PaginationProps, PaginationResponse } from '@fastgpt/web/common/fetch/type'; -export const getInforms = (data: RequestPaging) => - POST>(`/proApi/support/user/inform/list`, data); +export const getInforms = (data: PaginationProps) => + POST>(`/proApi/support/user/inform/list`, data); export const getUnreadCount = () => GET<{ diff --git a/projects/app/src/web/support/user/team/api.ts b/projects/app/src/web/support/user/team/api.ts index 34e5759f973a..af49e4022bd5 100644 --- a/projects/app/src/web/support/user/team/api.ts +++ b/projects/app/src/web/support/user/team/api.ts @@ -19,6 +19,7 @@ import { } from '@fastgpt/global/support/user/team/type.d'; import { FeTeamPlanStatusType, TeamSubSchema } from '@fastgpt/global/support/wallet/sub/type'; import { TeamInvoiceHeaderType } from '@fastgpt/global/support/user/team/type'; +import { PaginationProps, PaginationResponse } from '@fastgpt/web/common/fetch/type'; /* --------------- team ---------------- */ export const getTeamList = (status: `${TeamMemberSchema['status']}`) => @@ -30,8 +31,8 @@ export const putSwitchTeam = (teamId: string) => PUT(`/proApi/support/user/team/switch`, { teamId }); /* --------------- team member ---------------- */ -export const getTeamMembers = () => - GET(`/proApi/support/user/team/member/list`); +export const getTeamMembers = (props: PaginationProps) => + GET>(`/proApi/support/user/team/member/list`, props); export const postInviteTeamMember = (data: InviteMemberProps) => POST(`/proApi/support/user/team/member/invite`, data); export const putUpdateMemberName = (name: string) => diff --git a/projects/app/src/web/support/user/useUserStore.ts b/projects/app/src/web/support/user/useUserStore.ts index 040e17556958..6bb30d1353b5 100644 --- a/projects/app/src/web/support/user/useUserStore.ts +++ b/projects/app/src/web/support/user/useUserStore.ts @@ -30,9 +30,6 @@ type State = { teamPlanStatus: FeTeamPlanStatusType | null; initTeamPlanStatus: () => Promise; - teamMembers: TeamMemberItemType[]; - loadAndGetTeamMembers: (init?: boolean) => Promise; - teamMemberGroups: MemberGroupListType; myGroups: MemberGroupListType; loadAndGetGroups: (init?: boolean) => Promise; @@ -102,7 +99,7 @@ export const useUserStore = create()( }, // team teamPlanStatus: null, - initTeamPlanStatus() { + async initTeamPlanStatus() { return getTeamPlanStatus().then((res) => { set((state) => { state.teamPlanStatus = res; @@ -110,21 +107,6 @@ export const useUserStore = create()( return res; }); }, - teamMembers: [], - loadAndGetTeamMembers: async (init = false) => { - if (!useSystemStore.getState()?.feConfigs?.isPlus) return []; - - const randomRefresh = Math.random() > 0.7; - if (!randomRefresh && !init && get().teamMembers?.length) - return Promise.resolve(get().teamMembers); - - const res = await getTeamMembers(); - set((state) => { - state.teamMembers = res; - }); - - return res; - }, teamMemberGroups: [], teamOrgs: [], myGroups: [], diff --git a/projects/app/src/web/support/wallet/bill/api.ts b/projects/app/src/web/support/wallet/bill/api.ts index a04de528c238..3058340b3a27 100644 --- a/projects/app/src/web/support/wallet/bill/api.ts +++ b/projects/app/src/web/support/wallet/bill/api.ts @@ -1,14 +1,14 @@ -import { PagingData, RequestPaging } from '@/types'; import { GET, POST } from '@/web/common/api/request'; import { CreateBillProps, CreateBillResponse } from '@fastgpt/global/support/wallet/bill/api'; import { BillTypeEnum } from '@fastgpt/global/support/wallet/bill/constants'; import type { BillSchemaType } from '@fastgpt/global/support/wallet/bill/type.d'; +import { PaginationProps, PaginationResponse } from '@fastgpt/web/common/fetch/type'; export const getBills = ( - data: RequestPaging & { + data: PaginationProps<{ type?: BillTypeEnum; - } -) => POST>(`/proApi/support/wallet/bill/list`, data); + }> +) => POST>(`/proApi/support/wallet/bill/list`, data); export const getWxPayQRCode = (data: CreateBillProps) => POST(`/proApi/support/wallet/bill/create`, data); diff --git a/projects/app/src/web/support/wallet/bill/invoice/api.ts b/projects/app/src/web/support/wallet/bill/invoice/api.ts index 7ec7772e8427..c5523e8d8fbd 100644 --- a/projects/app/src/web/support/wallet/bill/invoice/api.ts +++ b/projects/app/src/web/support/wallet/bill/invoice/api.ts @@ -1,8 +1,8 @@ -import { PagingData, RequestPaging } from '@/types'; import { GET, POST } from '@/web/common/api/request'; import { BillTypeEnum } from '@fastgpt/global/support/wallet/bill/constants'; import { InvoiceType } from '@fastgpt/global/support/wallet/bill/type'; import { InvoiceSchemaType } from '@fastgpt/global/support/wallet/bill/type'; +import { PaginationProps, PaginationResponse } from '@fastgpt/web/common/fetch/type'; export type invoiceBillDataType = { type: BillTypeEnum; price: number; @@ -16,5 +16,5 @@ export const getInvoiceBillsList = () => export const submitInvoice = (data: InvoiceType) => POST(`/proApi/support/wallet/bill/invoice/submit`, data); -export const getInvoiceRecords = (data: RequestPaging) => - POST>(`/proApi/support/wallet/bill/invoice/records`, data); +export const getInvoiceRecords = (data: PaginationProps) => + POST>(`/proApi/support/wallet/bill/invoice/records`, data); diff --git a/projects/app/src/web/support/wallet/usage/api.ts b/projects/app/src/web/support/wallet/usage/api.ts index 41b32be3d208..c4ce4171ac55 100644 --- a/projects/app/src/web/support/wallet/usage/api.ts +++ b/projects/app/src/web/support/wallet/usage/api.ts @@ -1,10 +1,17 @@ -import { GET, POST } from '@/web/common/api/request'; +import { POST } from '@/web/common/api/request'; import { CreateTrainingUsageProps } from '@fastgpt/global/support/wallet/usage/api.d'; -import type { PagingData, RequestPaging } from '@/types'; +import { UsageSourceEnum } from '@fastgpt/global/support/wallet/usage/constants'; import type { UsageItemType } from '@fastgpt/global/support/wallet/usage/type'; +import { PaginationProps, PaginationResponse } from '@fastgpt/web/common/fetch/type'; -export const getUserUsages = (data: RequestPaging) => - POST>(`/proApi/support/wallet/usage/getUsage`, data); +export const getUserUsages = ( + data: PaginationProps<{ + dateStart: Date; + dateEnd: Date; + source?: UsageSourceEnum; + teamMemberId: string; + }> +) => POST>(`/proApi/support/wallet/usage/getUsage`, data); export const postCreateTrainingUsage = (data: CreateTrainingUsageProps) => POST(`/support/wallet/usage/createTrainingUsage`, data);