4.8.5 test (#1819)
This commit is contained in:
parent
5cc01b8509
commit
24596a6e21
4
.github/workflows/build-sandbox-image.yml
vendored
4
.github/workflows/build-sandbox-image.yml
vendored
@ -10,6 +10,7 @@ jobs:
|
|||||||
build-fastgpt-sandbox-images:
|
build-fastgpt-sandbox-images:
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-20.04
|
||||||
steps:
|
steps:
|
||||||
|
# install env
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
@ -31,6 +32,7 @@ jobs:
|
|||||||
restore-keys: |
|
restore-keys: |
|
||||||
${{ runner.os }}-buildx-
|
${{ runner.os }}-buildx-
|
||||||
|
|
||||||
|
# login docker
|
||||||
- name: Login to GitHub Container Registry
|
- name: Login to GitHub Container Registry
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v2
|
||||||
with:
|
with:
|
||||||
@ -49,6 +51,7 @@ jobs:
|
|||||||
username: ${{ secrets.DOCKER_HUB_NAME }}
|
username: ${{ secrets.DOCKER_HUB_NAME }}
|
||||||
password: ${{ secrets.DOCKER_HUB_PASSWORD }}
|
password: ${{ secrets.DOCKER_HUB_PASSWORD }}
|
||||||
|
|
||||||
|
# Set tag
|
||||||
- name: Set image name and tag
|
- name: Set image name and tag
|
||||||
run: |
|
run: |
|
||||||
if [[ "${{ github.ref_name }}" == "main" ]]; then
|
if [[ "${{ github.ref_name }}" == "main" ]]; then
|
||||||
@ -66,6 +69,7 @@ jobs:
|
|||||||
echo "Docker_Hub_Tag=${{ secrets.DOCKER_IMAGE_NAME }}/fastgpt-sandbox:${{ github.ref_name }}" >> $GITHUB_ENV
|
echo "Docker_Hub_Tag=${{ secrets.DOCKER_IMAGE_NAME }}/fastgpt-sandbox:${{ github.ref_name }}" >> $GITHUB_ENV
|
||||||
echo "Docker_Hub_Latest=${{ secrets.DOCKER_IMAGE_NAME }}/fastgpt-sandbox:latest" >> $GITHUB_ENV
|
echo "Docker_Hub_Latest=${{ secrets.DOCKER_IMAGE_NAME }}/fastgpt-sandbox:latest" >> $GITHUB_ENV
|
||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Build and publish image for main branch or tag push event
|
- name: Build and publish image for main branch or tag push event
|
||||||
env:
|
env:
|
||||||
Git_Tag: ${{ env.Git_Tag }}
|
Git_Tag: ${{ env.Git_Tag }}
|
||||||
|
|||||||
91
.github/workflows/fastgpt-image.yml
vendored
91
.github/workflows/fastgpt-image.yml
vendored
@ -11,6 +11,7 @@ jobs:
|
|||||||
build-fastgpt-images:
|
build-fastgpt-images:
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-20.04
|
||||||
steps:
|
steps:
|
||||||
|
# install env
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
@ -31,19 +32,45 @@ jobs:
|
|||||||
key: ${{ runner.os }}-buildx-${{ github.sha }}
|
key: ${{ runner.os }}-buildx-${{ github.sha }}
|
||||||
restore-keys: |
|
restore-keys: |
|
||||||
${{ runner.os }}-buildx-
|
${{ runner.os }}-buildx-
|
||||||
|
|
||||||
|
# login docker
|
||||||
- name: Login to GitHub Container Registry
|
- name: Login to GitHub Container Registry
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v2
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.repository_owner }}
|
username: ${{ github.repository_owner }}
|
||||||
password: ${{ secrets.GH_PAT }}
|
password: ${{ secrets.GH_PAT }}
|
||||||
- name: Set DOCKER_REPO_TAGGED based on branch or tag
|
- name: Login to Ali Hub
|
||||||
|
uses: docker/login-action@v2
|
||||||
|
with:
|
||||||
|
registry: registry.cn-hangzhou.aliyuncs.com
|
||||||
|
username: ${{ secrets.ALI_HUB_USERNAME }}
|
||||||
|
password: ${{ secrets.ALI_HUB_PASSWORD }}
|
||||||
|
- name: Login to Docker Hub
|
||||||
|
uses: docker/login-action@v2
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKER_HUB_NAME }}
|
||||||
|
password: ${{ secrets.DOCKER_HUB_PASSWORD }}
|
||||||
|
|
||||||
|
# Set tag
|
||||||
|
- name: Set image name and tag
|
||||||
run: |
|
run: |
|
||||||
if [[ "${{ github.ref_name }}" == "main" ]]; then
|
if [[ "${{ github.ref_name }}" == "main" ]]; then
|
||||||
echo "DOCKER_REPO_TAGGED=ghcr.io/${{ github.repository_owner }}/fastgpt:latest" >> $GITHUB_ENV
|
echo "Git_Tag=ghcr.io/${{ github.repository_owner }}/fastgpt:latest" >> $GITHUB_ENV
|
||||||
|
echo "Git_Latest=ghcr.io/${{ github.repository_owner }}/fastgpt:latest" >> $GITHUB_ENV
|
||||||
|
echo "Ali_Tag=${{ secrets.ALI_IMAGE_NAME }}/fastgpt:latest" >> $GITHUB_ENV
|
||||||
|
echo "Ali_Latest=${{ secrets.ALI_IMAGE_NAME }}/fastgpt:latest" >> $GITHUB_ENV
|
||||||
|
echo "Docker_Hub_Tag=${{ secrets.DOCKER_IMAGE_NAME }}/fastgpt:latest" >> $GITHUB_ENV
|
||||||
|
echo "Docker_Hub_Latest=${{ secrets.DOCKER_IMAGE_NAME }}/fastgpt:latest" >> $GITHUB_ENV
|
||||||
else
|
else
|
||||||
echo "DOCKER_REPO_TAGGED=ghcr.io/${{ github.repository_owner }}/fastgpt:${{ github.ref_name }}" >> $GITHUB_ENV
|
echo "Git_Tag=ghcr.io/${{ github.repository_owner }}/fastgpt:${{ github.ref_name }}" >> $GITHUB_ENV
|
||||||
|
echo "Git_Latest=ghcr.io/${{ github.repository_owner }}/fastgpt:latest" >> $GITHUB_ENV
|
||||||
|
echo "Ali_Tag=${{ secrets.ALI_IMAGE_NAME }}/fastgpt:${{ github.ref_name }}" >> $GITHUB_ENV
|
||||||
|
echo "Ali_Latest=${{ secrets.ALI_IMAGE_NAME }}/fastgpt:latest" >> $GITHUB_ENV
|
||||||
|
echo "Docker_Hub_Tag=${{ secrets.DOCKER_IMAGE_NAME }}/fastgpt:${{ github.ref_name }}" >> $GITHUB_ENV
|
||||||
|
echo "Docker_Hub_Latest=${{ secrets.DOCKER_IMAGE_NAME }}/fastgpt:latest" >> $GITHUB_ENV
|
||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Build and publish image for main branch or tag push event
|
- name: Build and publish image for main branch or tag push event
|
||||||
env:
|
env:
|
||||||
DOCKER_REPO_TAGGED: ${{ env.DOCKER_REPO_TAGGED }}
|
DOCKER_REPO_TAGGED: ${{ env.DOCKER_REPO_TAGGED }}
|
||||||
@ -56,56 +83,10 @@ jobs:
|
|||||||
--push \
|
--push \
|
||||||
--cache-from=type=local,src=/tmp/.buildx-cache \
|
--cache-from=type=local,src=/tmp/.buildx-cache \
|
||||||
--cache-to=type=local,dest=/tmp/.buildx-cache \
|
--cache-to=type=local,dest=/tmp/.buildx-cache \
|
||||||
-t ${DOCKER_REPO_TAGGED} \
|
-t ${Git_Tag} \
|
||||||
|
-t ${Git_Latest} \
|
||||||
|
-t ${Ali_Tag} \
|
||||||
|
-t ${Ali_Latest} \
|
||||||
|
-t ${Docker_Hub_Tag} \
|
||||||
|
-t ${Docker_Hub_Latest} \
|
||||||
.
|
.
|
||||||
push-to-docker-hub:
|
|
||||||
needs: build-fastgpt-images
|
|
||||||
runs-on: ubuntu-20.04
|
|
||||||
if: github.repository == 'labring/FastGPT'
|
|
||||||
steps:
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
- name: Login to Docker Hub
|
|
||||||
uses: docker/login-action@v2
|
|
||||||
with:
|
|
||||||
username: ${{ secrets.DOCKER_HUB_NAME }}
|
|
||||||
password: ${{ secrets.DOCKER_HUB_PASSWORD }}
|
|
||||||
- name: Set DOCKER_REPO_TAGGED based on branch or tag
|
|
||||||
run: |
|
|
||||||
if [[ "${{ github.ref_name }}" == "main" ]]; then
|
|
||||||
echo "IMAGE_TAG=latest" >> $GITHUB_ENV
|
|
||||||
else
|
|
||||||
echo "IMAGE_TAG=${{ github.ref_name }}" >> $GITHUB_ENV
|
|
||||||
fi
|
|
||||||
- name: Pull image from GitHub Container Registry
|
|
||||||
run: docker pull ghcr.io/${{ github.repository_owner }}/fastgpt:${{env.IMAGE_TAG}}
|
|
||||||
- name: Tag image with Docker Hub repository name and version tag
|
|
||||||
run: docker tag ghcr.io/${{ github.repository_owner }}/fastgpt:${{env.IMAGE_TAG}} ${{ secrets.DOCKER_IMAGE_NAME }}/fastgpt:${{env.IMAGE_TAG}}
|
|
||||||
- name: Push image to Docker Hub
|
|
||||||
run: docker push ${{ secrets.DOCKER_IMAGE_NAME }}/fastgpt:${{env.IMAGE_TAG}}
|
|
||||||
push-to-ali-hub:
|
|
||||||
needs: build-fastgpt-images
|
|
||||||
if: github.repository == 'labring/FastGPT'
|
|
||||||
runs-on: ubuntu-20.04
|
|
||||||
steps:
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
- name: Login to Ali Hub
|
|
||||||
uses: docker/login-action@v2
|
|
||||||
with:
|
|
||||||
registry: registry.cn-hangzhou.aliyuncs.com
|
|
||||||
username: ${{ secrets.ALI_HUB_USERNAME }}
|
|
||||||
password: ${{ secrets.ALI_HUB_PASSWORD }}
|
|
||||||
- name: Set DOCKER_REPO_TAGGED based on branch or tag
|
|
||||||
run: |
|
|
||||||
if [[ "${{ github.ref_name }}" == "main" ]]; then
|
|
||||||
echo "IMAGE_TAG=latest" >> $GITHUB_ENV
|
|
||||||
else
|
|
||||||
echo "IMAGE_TAG=${{ github.ref_name }}" >> $GITHUB_ENV
|
|
||||||
fi
|
|
||||||
- name: Pull image from GitHub Container Registry
|
|
||||||
run: docker pull ghcr.io/${{ github.repository_owner }}/fastgpt:${{env.IMAGE_TAG}}
|
|
||||||
- name: Tag image with Docker Hub repository name and version tag
|
|
||||||
run: docker tag ghcr.io/${{ github.repository_owner }}/fastgpt:${{env.IMAGE_TAG}} ${{ secrets.ALI_IMAGE_NAME }}/fastgpt:${{env.IMAGE_TAG}}
|
|
||||||
- name: Push image to Docker Hub
|
|
||||||
run: docker push ${{ secrets.ALI_IMAGE_NAME }}/fastgpt:${{env.IMAGE_TAG}}
|
|
||||||
|
|||||||
@ -31,7 +31,9 @@ images: []
|
|||||||
### 页面崩溃
|
### 页面崩溃
|
||||||
|
|
||||||
1. 关闭翻译
|
1. 关闭翻译
|
||||||
2. 检查配置文件是否正常加载,如果没有正常加载会导致缺失系统信息,在某些操作下会导致空指针。(95%情况是配置文件不对,可以F12打开控制台,看具体的空指针情况)
|
2. 检查配置文件是否正常加载,如果没有正常加载会导致缺失系统信息,在某些操作下会导致空指针。
|
||||||
|
* 95%情况是配置文件不对。会提示 xxx undefined
|
||||||
|
* 提示`URI malformed`,请 Issue 反馈具体操作和页面,这是由于特殊字符串编码解析报错。
|
||||||
3. 某些api不兼容问题(较少)
|
3. 某些api不兼容问题(较少)
|
||||||
|
|
||||||
### 开启内容补全后,响应速度变慢
|
### 开启内容补全后,响应速度变慢
|
||||||
|
|||||||
@ -36,6 +36,8 @@ curl --location --request POST 'https://{{host}}/api/admin/initv485' \
|
|||||||
3. 优化 - 原文件编码存取
|
3. 优化 - 原文件编码存取
|
||||||
4. 优化 - 文件夹读取,支持单个文件夹超出 100 个文件
|
4. 优化 - 文件夹读取,支持单个文件夹超出 100 个文件
|
||||||
5. 优化 - 问答拆分/手动录入,当有`a`字段时,自动将`q`作为补充索引。
|
5. 优化 - 问答拆分/手动录入,当有`a`字段时,自动将`q`作为补充索引。
|
||||||
6. 修复 - SSR渲染
|
6. 优化 - 对话框页面代码
|
||||||
7. 修复 - 定时任务无法实际关闭
|
7. 修复 - SSR渲染
|
||||||
8. 修复 - 输入引导特殊字符导致正则报错
|
8. 修复 - 定时任务无法实际关闭
|
||||||
|
9. 修复 - 输入引导特殊字符导致正则报错
|
||||||
|
10. 修复 - 文件包含特殊字符`%`,且为转义时会导致页面崩溃
|
||||||
@ -24,13 +24,15 @@ export function getSourceNameIcon({
|
|||||||
sourceName: string;
|
sourceName: string;
|
||||||
sourceId?: string;
|
sourceId?: string;
|
||||||
}) {
|
}) {
|
||||||
const fileIcon = getFileIcon(decodeURIComponent(sourceName), '');
|
try {
|
||||||
|
const fileIcon = getFileIcon(decodeURIComponent(sourceName.replace(/%/g, '%25')), '');
|
||||||
if (fileIcon) {
|
if (fileIcon) {
|
||||||
return fileIcon;
|
return fileIcon;
|
||||||
}
|
}
|
||||||
if (strIsLink(sourceId)) {
|
if (strIsLink(sourceId)) {
|
||||||
return 'common/linkBlue';
|
return 'common/linkBlue';
|
||||||
}
|
}
|
||||||
|
} catch (error) {}
|
||||||
|
|
||||||
return 'file/fill/manual';
|
return 'file/fill/manual';
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,7 +12,7 @@ type Props = Omit<BoxProps, 'onChange'> & {
|
|||||||
onChange: (e: string) => void;
|
onChange: (e: string) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const RowTabs = ({ list, value, onChange, py = '7px', px = '12px', ...props }: Props) => {
|
const FillRowTabs = ({ list, value, onChange, py = '7px', px = '12px', ...props }: Props) => {
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
display={'inline-flex'}
|
display={'inline-flex'}
|
||||||
@ -55,4 +55,4 @@ const RowTabs = ({ list, value, onChange, py = '7px', px = '12px', ...props }: P
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default RowTabs;
|
export default FillRowTabs;
|
||||||
@ -2,18 +2,24 @@ import React, { useMemo } from 'react';
|
|||||||
import { Box, Flex, Grid, Image } from '@chakra-ui/react';
|
import { Box, Flex, Grid, Image } from '@chakra-ui/react';
|
||||||
import type { FlexProps, GridProps } from '@chakra-ui/react';
|
import type { FlexProps, GridProps } from '@chakra-ui/react';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
import MyIcon from '../Icon';
|
||||||
|
|
||||||
// @ts-ignore
|
type Props<ValueType = string> = Omit<GridProps, 'onChange'> & {
|
||||||
interface Props extends GridProps {
|
list: { icon?: string; label: string | React.ReactNode; value: ValueType }[];
|
||||||
list: { id: string; icon?: string; label: string | React.ReactNode }[];
|
value: ValueType;
|
||||||
activeId: string;
|
|
||||||
size?: 'sm' | 'md' | 'lg';
|
size?: 'sm' | 'md' | 'lg';
|
||||||
inlineStyles?: FlexProps;
|
inlineStyles?: FlexProps;
|
||||||
onChange: (id: string) => void;
|
onChange: (value: ValueType) => void;
|
||||||
}
|
};
|
||||||
|
|
||||||
const Tabs = ({ list, size = 'md', activeId, onChange, inlineStyles, ...props }: Props) => {
|
const LightRowTabs = <ValueType = string,>({
|
||||||
|
list,
|
||||||
|
size = 'md',
|
||||||
|
value,
|
||||||
|
onChange,
|
||||||
|
inlineStyles,
|
||||||
|
...props
|
||||||
|
}: Props<ValueType>) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const sizeMap = useMemo(() => {
|
const sizeMap = useMemo(() => {
|
||||||
switch (size) {
|
switch (size) {
|
||||||
@ -49,7 +55,7 @@ const Tabs = ({ list, size = 'md', activeId, onChange, inlineStyles, ...props }:
|
|||||||
>
|
>
|
||||||
{list.map((item) => (
|
{list.map((item) => (
|
||||||
<Flex
|
<Flex
|
||||||
key={item.id}
|
key={item.value as string}
|
||||||
py={sizeMap.inlineP}
|
py={sizeMap.inlineP}
|
||||||
alignItems={'center'}
|
alignItems={'center'}
|
||||||
justifyContent={'center'}
|
justifyContent={'center'}
|
||||||
@ -57,7 +63,7 @@ const Tabs = ({ list, size = 'md', activeId, onChange, inlineStyles, ...props }:
|
|||||||
px={3}
|
px={3}
|
||||||
whiteSpace={'nowrap'}
|
whiteSpace={'nowrap'}
|
||||||
{...inlineStyles}
|
{...inlineStyles}
|
||||||
{...(activeId === item.id
|
{...(value === item.value
|
||||||
? {
|
? {
|
||||||
color: 'primary.600',
|
color: 'primary.600',
|
||||||
cursor: 'default',
|
cursor: 'default',
|
||||||
@ -68,8 +74,8 @@ const Tabs = ({ list, size = 'md', activeId, onChange, inlineStyles, ...props }:
|
|||||||
cursor: 'pointer'
|
cursor: 'pointer'
|
||||||
})}
|
})}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (activeId === item.id) return;
|
if (value === item.value) return;
|
||||||
onChange(item.id);
|
onChange(item.value);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{item.icon && (
|
{item.icon && (
|
||||||
@ -88,4 +94,4 @@ const Tabs = ({ list, size = 'md', activeId, onChange, inlineStyles, ...props }:
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Tabs;
|
export default LightRowTabs;
|
||||||
@ -551,7 +551,8 @@ export const theme = extendTheme({
|
|||||||
color: 'myGray.600',
|
color: 'myGray.600',
|
||||||
fontWeight: 'normal',
|
fontWeight: 'normal',
|
||||||
height: '100%',
|
height: '100%',
|
||||||
overflow: 'hidden'
|
overflow: 'hidden',
|
||||||
|
fontSize: '16px'
|
||||||
},
|
},
|
||||||
a: {
|
a: {
|
||||||
color: 'primary.600'
|
color: 'primary.600'
|
||||||
|
|||||||
@ -364,6 +364,7 @@ const ChatInput = ({
|
|||||||
color={'myGray.900'}
|
color={'myGray.900'}
|
||||||
isDisabled={isSpeaking}
|
isDisabled={isSpeaking}
|
||||||
value={inputValue}
|
value={inputValue}
|
||||||
|
fontSize={['md', 'sm']}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
const textarea = e.target;
|
const textarea = e.target;
|
||||||
textarea.style.height = textareaMinH;
|
textarea.style.height = textareaMinH;
|
||||||
|
|||||||
@ -4,9 +4,8 @@ import type { ChatHistoryItemResType } from '@fastgpt/global/core/chat/type.d';
|
|||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
import { moduleTemplatesFlat } from '@fastgpt/global/core/workflow/template/constants';
|
import { moduleTemplatesFlat } from '@fastgpt/global/core/workflow/template/constants';
|
||||||
|
|
||||||
import Tabs from '../../Tabs';
|
import LightRowTabs from '@fastgpt/web/components/common/Tabs/LightRowTabs';
|
||||||
import MyModal from '@fastgpt/web/components/common/MyModal';
|
import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||||
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
|
|
||||||
import Markdown from '../../Markdown';
|
import Markdown from '../../Markdown';
|
||||||
import { QuoteList } from './QuoteModal';
|
import { QuoteList } from './QuoteModal';
|
||||||
import { DatasetSearchModeMap } from '@fastgpt/global/core/dataset/constants';
|
import { DatasetSearchModeMap } from '@fastgpt/global/core/dataset/constants';
|
||||||
@ -142,7 +141,7 @@ export const ResponseBox = React.memo(function ResponseBox({
|
|||||||
{t(item.moduleName)}
|
{t(item.moduleName)}
|
||||||
</Flex>
|
</Flex>
|
||||||
),
|
),
|
||||||
id: `${i}`
|
value: `${i}`
|
||||||
})),
|
})),
|
||||||
[response, t]
|
[response, t]
|
||||||
);
|
);
|
||||||
@ -155,7 +154,7 @@ export const ResponseBox = React.memo(function ResponseBox({
|
|||||||
<>
|
<>
|
||||||
{!hideTabs && (
|
{!hideTabs && (
|
||||||
<Box>
|
<Box>
|
||||||
<Tabs list={list} activeId={currentTab} onChange={setCurrentTab} />
|
<LightRowTabs list={list} value={currentTab} onChange={setCurrentTab} />
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
<Box py={2} px={4} flex={'1 0 0'} overflow={'auto'}>
|
<Box py={2} px={4} flex={'1 0 0'} overflow={'auto'}>
|
||||||
|
|||||||
@ -997,7 +997,7 @@ const ChatBox = (
|
|||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
{/* message input */}
|
{/* message input */}
|
||||||
{onStartChat && (chatStarted || filterVariableNodes.length === 0) && active && (
|
{onStartChat && (chatStarted || filterVariableNodes.length === 0) && active && appId && (
|
||||||
<ChatInput
|
<ChatInput
|
||||||
onSendMessage={sendPrompt}
|
onSendMessage={sendPrompt}
|
||||||
onStop={() => chatController.current?.abort('stop')}
|
onStop={() => chatController.current?.abort('stop')}
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import React, { useMemo } from 'react';
|
|||||||
import { Box, BoxProps, Flex, Link, LinkProps } from '@chakra-ui/react';
|
import { Box, BoxProps, Flex, Link, LinkProps } from '@chakra-ui/react';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import { useUserStore } from '@/web/support/user/useUserStore';
|
import { useUserStore } from '@/web/support/user/useUserStore';
|
||||||
import { useChatStore } from '@/web/core/chat/storeChat';
|
import { useChatStore } from '@/web/core/chat/context/storeChat';
|
||||||
import { HUMAN_ICON } from '@fastgpt/global/common/system/constants';
|
import { HUMAN_ICON } from '@fastgpt/global/common/system/constants';
|
||||||
import NextLink from 'next/link';
|
import NextLink from 'next/link';
|
||||||
import Badge from '../Badge';
|
import Badge from '../Badge';
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import React, { useMemo } from 'react';
|
import React, { useMemo } from 'react';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import { Flex, Box } from '@chakra-ui/react';
|
import { Flex, Box } from '@chakra-ui/react';
|
||||||
import { useChatStore } from '@/web/core/chat/storeChat';
|
import { useChatStore } from '@/web/core/chat/context/storeChat';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
import Badge from '../Badge';
|
import Badge from '../Badge';
|
||||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||||
|
|||||||
@ -4,15 +4,20 @@ import type { GridProps } from '@chakra-ui/react';
|
|||||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||||
import type { IconNameType } from '@fastgpt/web/components/common/Icon/type.d';
|
import type { IconNameType } from '@fastgpt/web/components/common/Icon/type.d';
|
||||||
|
|
||||||
// @ts-ignore
|
export type Props<ValueType = string> = Omit<GridProps, 'onChange'> & {
|
||||||
export interface Props extends GridProps {
|
list: { value: ValueType; label: string; icon: string }[];
|
||||||
list: { id: string; label: string; icon: string }[];
|
value: ValueType;
|
||||||
activeId: string;
|
|
||||||
size?: 'sm' | 'md' | 'lg';
|
size?: 'sm' | 'md' | 'lg';
|
||||||
onChange: (id: string) => void;
|
onChange: (value: ValueType) => void;
|
||||||
}
|
};
|
||||||
|
|
||||||
const SideTabs = ({ list, size = 'md', activeId, onChange, ...props }: Props) => {
|
const SideTabs = <ValueType = string,>({
|
||||||
|
list,
|
||||||
|
size = 'md',
|
||||||
|
value,
|
||||||
|
onChange,
|
||||||
|
...props
|
||||||
|
}: Props<ValueType>) => {
|
||||||
const sizeMap = useMemo(() => {
|
const sizeMap = useMemo(() => {
|
||||||
switch (size) {
|
switch (size) {
|
||||||
case 'sm':
|
case 'sm':
|
||||||
@ -37,14 +42,14 @@ const SideTabs = ({ list, size = 'md', activeId, onChange, ...props }: Props) =>
|
|||||||
<Box fontSize={sizeMap.fontSize} {...props}>
|
<Box fontSize={sizeMap.fontSize} {...props}>
|
||||||
{list.map((item) => (
|
{list.map((item) => (
|
||||||
<Flex
|
<Flex
|
||||||
key={item.id}
|
key={item.value as string}
|
||||||
py={sizeMap.inlineP}
|
py={sizeMap.inlineP}
|
||||||
borderRadius={'md'}
|
borderRadius={'md'}
|
||||||
px={3}
|
px={3}
|
||||||
mb={2}
|
mb={2}
|
||||||
fontWeight={'medium'}
|
fontWeight={'medium'}
|
||||||
alignItems={'center'}
|
alignItems={'center'}
|
||||||
{...(activeId === item.id
|
{...(value === item.value
|
||||||
? {
|
? {
|
||||||
bg: ' primary.100 !important',
|
bg: ' primary.100 !important',
|
||||||
color: 'primary.600 ',
|
color: 'primary.600 ',
|
||||||
@ -59,8 +64,8 @@ const SideTabs = ({ list, size = 'md', activeId, onChange, ...props }: Props) =>
|
|||||||
bg: 'myGray.100'
|
bg: 'myGray.100'
|
||||||
}}
|
}}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (activeId === item.id) return;
|
if (value === item.value) return;
|
||||||
onChange(item.id);
|
onChange(item.value);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<MyIcon mr={2} name={item.icon as IconNameType} w={'20px'} />
|
<MyIcon mr={2} name={item.icon as IconNameType} w={'20px'} />
|
||||||
@ -71,4 +76,4 @@ const SideTabs = ({ list, size = 'md', activeId, onChange, ...props }: Props) =>
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default React.memo(SideTabs);
|
export default SideTabs;
|
||||||
|
|||||||
@ -136,7 +136,7 @@ const SelectOneResource = ({
|
|||||||
</Flex>
|
</Flex>
|
||||||
)}
|
)}
|
||||||
<Avatar ml={index !== 0 ? '0.5rem' : 0} src={item.avatar} w={'1.25rem'} />
|
<Avatar ml={index !== 0 ? '0.5rem' : 0} src={item.avatar} w={'1.25rem'} />
|
||||||
<Box fontSize={'sm'} ml={2}>
|
<Box fontSize={['md', 'sm']} ml={2}>
|
||||||
{item.name}
|
{item.name}
|
||||||
</Box>
|
</Box>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|||||||
@ -23,7 +23,7 @@ import { NodeInputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
|||||||
import { DatasetSearchModeMap } from '@fastgpt/global/core/dataset/constants';
|
import { DatasetSearchModeMap } from '@fastgpt/global/core/dataset/constants';
|
||||||
import MyRadio from '@/components/common/MyRadio';
|
import MyRadio from '@/components/common/MyRadio';
|
||||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||||
import Tabs from '@/components/Tabs';
|
import LightRowTabs from '@fastgpt/web/components/common/Tabs/LightRowTabs';
|
||||||
import PromptEditor from '@fastgpt/web/components/common/Textarea/PromptEditor';
|
import PromptEditor from '@fastgpt/web/components/common/Textarea/PromptEditor';
|
||||||
import { useUserStore } from '@/web/support/user/useUserStore';
|
import { useUserStore } from '@/web/support/user/useUserStore';
|
||||||
import { useToast } from '@fastgpt/web/hooks/useToast';
|
import { useToast } from '@fastgpt/web/hooks/useToast';
|
||||||
@ -127,27 +127,27 @@ const DatasetParamsModal = ({
|
|||||||
w={['90vw', '550px']}
|
w={['90vw', '550px']}
|
||||||
>
|
>
|
||||||
<ModalBody flex={'auto'} overflow={'auto'}>
|
<ModalBody flex={'auto'} overflow={'auto'}>
|
||||||
<Tabs
|
<LightRowTabs<SearchSettingTabEnum>
|
||||||
mb={3}
|
mb={3}
|
||||||
list={[
|
list={[
|
||||||
{
|
{
|
||||||
icon: 'modal/setting',
|
icon: 'modal/setting',
|
||||||
label: t('core.dataset.search.search mode'),
|
label: t('core.dataset.search.search mode'),
|
||||||
id: SearchSettingTabEnum.searchMode
|
value: SearchSettingTabEnum.searchMode
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: 'support/outlink/apikeyFill',
|
icon: 'support/outlink/apikeyFill',
|
||||||
label: t('core.dataset.search.Filter'),
|
label: t('core.dataset.search.Filter'),
|
||||||
id: SearchSettingTabEnum.limit
|
value: SearchSettingTabEnum.limit
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: t('core.module.template.Query extension'),
|
label: t('core.module.template.Query extension'),
|
||||||
id: SearchSettingTabEnum.queryExtension,
|
value: SearchSettingTabEnum.queryExtension,
|
||||||
icon: '/imgs/workflow/cfr.svg'
|
icon: '/imgs/workflow/cfr.svg'
|
||||||
}
|
}
|
||||||
]}
|
]}
|
||||||
activeId={currentTabType}
|
value={currentTabType}
|
||||||
onChange={(e) => setCurrentTabType(e as any)}
|
onChange={setCurrentTabType}
|
||||||
/>
|
/>
|
||||||
{currentTabType === SearchSettingTabEnum.searchMode && (
|
{currentTabType === SearchSettingTabEnum.searchMode && (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@ -3,7 +3,6 @@ import MyIcon from '@fastgpt/web/components/common/Icon';
|
|||||||
import { useUserStore } from '@/web/support/user/useUserStore';
|
import { useUserStore } from '@/web/support/user/useUserStore';
|
||||||
import { useContextSelector } from 'use-context-selector';
|
import { useContextSelector } from 'use-context-selector';
|
||||||
import { TeamMemberRoleEnum } from '@fastgpt/global/support/user/team/constant';
|
import { TeamMemberRoleEnum } from '@fastgpt/global/support/user/team/constant';
|
||||||
import RowTabs from '@fastgpt/web/components/common/Tabs/RowTabs';
|
|
||||||
import { useMemo, useState } from 'react';
|
import { useMemo, useState } from 'react';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||||
@ -15,6 +14,7 @@ import { TeamModalContext } from './context';
|
|||||||
import { useRequest } from '@fastgpt/web/hooks/useRequest';
|
import { useRequest } from '@fastgpt/web/hooks/useRequest';
|
||||||
import { delLeaveTeam } from '@/web/support/user/team/api';
|
import { delLeaveTeam } from '@/web/support/user/team/api';
|
||||||
import dynamic from 'next/dynamic';
|
import dynamic from 'next/dynamic';
|
||||||
|
import LightRowTabs from '@fastgpt/web/components/common/Tabs/LightRowTabs';
|
||||||
|
|
||||||
enum TabListEnum {
|
enum TabListEnum {
|
||||||
member = 'member',
|
member = 'member',
|
||||||
@ -124,14 +124,12 @@ function TeamCard() {
|
|||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
<Flex px={5} alignItems={'center'} justifyContent={'space-between'}>
|
<Flex px={5} alignItems={'center'} justifyContent={'space-between'}>
|
||||||
<RowTabs
|
<LightRowTabs<TabListEnum>
|
||||||
overflow={'auto'}
|
overflow={'auto'}
|
||||||
list={Tablist}
|
list={Tablist}
|
||||||
value={tab}
|
value={tab}
|
||||||
onChange={(v) => {
|
onChange={setTab}
|
||||||
setTab(v as TabListEnum);
|
></LightRowTabs>
|
||||||
}}
|
|
||||||
></RowTabs>
|
|
||||||
{/* ctrl buttons */}
|
{/* ctrl buttons */}
|
||||||
<Flex alignItems={'center'}>
|
<Flex alignItems={'center'}>
|
||||||
{tab === TabListEnum.member && userInfo?.team.permission.hasManagePer && (
|
{tab === TabListEnum.member && userInfo?.team.permission.hasManagePer && (
|
||||||
|
|||||||
1
projects/app/src/global/core/chat/api.d.ts
vendored
1
projects/app/src/global/core/chat/api.d.ts
vendored
@ -51,6 +51,7 @@ export type GetHistoriesProps = OutLinkChatAuthProps & {
|
|||||||
export type UpdateHistoryProps = OutLinkChatAuthProps & {
|
export type UpdateHistoryProps = OutLinkChatAuthProps & {
|
||||||
appId: string;
|
appId: string;
|
||||||
chatId: string;
|
chatId: string;
|
||||||
|
title?: string;
|
||||||
customTitle?: string;
|
customTitle?: string;
|
||||||
top?: boolean;
|
top?: boolean;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import React, { useCallback } from 'react';
|
import React, { useCallback } from 'react';
|
||||||
import { Box, Flex, useDisclosure, useTheme } from '@chakra-ui/react';
|
import { Box, Flex, useTheme } from '@chakra-ui/react';
|
||||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import dynamic from 'next/dynamic';
|
import dynamic from 'next/dynamic';
|
||||||
@ -7,7 +7,7 @@ import { useUserStore } from '@/web/support/user/useUserStore';
|
|||||||
import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
|
import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
|
||||||
import PageContainer from '@/components/PageContainer';
|
import PageContainer from '@/components/PageContainer';
|
||||||
import SideTabs from '@/components/SideTabs';
|
import SideTabs from '@/components/SideTabs';
|
||||||
import Tabs from '@/components/Tabs';
|
import LightRowTabs from '@fastgpt/web/components/common/Tabs/LightRowTabs';
|
||||||
import UserInfo from './components/Info';
|
import UserInfo from './components/Info';
|
||||||
import { serviceSideProps } from '@/web/common/utils/i18n';
|
import { serviceSideProps } from '@/web/common/utils/i18n';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
@ -31,7 +31,7 @@ enum TabEnum {
|
|||||||
'loginout' = 'loginout'
|
'loginout' = 'loginout'
|
||||||
}
|
}
|
||||||
|
|
||||||
const Account = ({ currentTab }: { currentTab: `${TabEnum}` }) => {
|
const Account = ({ currentTab }: { currentTab: TabEnum }) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { userInfo, setUserInfo } = useUserStore();
|
const { userInfo, setUserInfo } = useUserStore();
|
||||||
const { feConfigs, isPc, systemVersion } = useSystemStore();
|
const { feConfigs, isPc, systemVersion } = useSystemStore();
|
||||||
@ -40,14 +40,14 @@ const Account = ({ currentTab }: { currentTab: `${TabEnum}` }) => {
|
|||||||
{
|
{
|
||||||
icon: 'support/user/userLight',
|
icon: 'support/user/userLight',
|
||||||
label: t('user.Personal Information'),
|
label: t('user.Personal Information'),
|
||||||
id: TabEnum.info
|
value: TabEnum.info
|
||||||
},
|
},
|
||||||
...(feConfigs?.isPlus
|
...(feConfigs?.isPlus
|
||||||
? [
|
? [
|
||||||
{
|
{
|
||||||
icon: 'support/usage/usageRecordLight',
|
icon: 'support/usage/usageRecordLight',
|
||||||
label: t('user.Usage Record'),
|
label: t('user.Usage Record'),
|
||||||
id: TabEnum.usage
|
value: TabEnum.usage
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
: []),
|
: []),
|
||||||
@ -56,7 +56,7 @@ const Account = ({ currentTab }: { currentTab: `${TabEnum}` }) => {
|
|||||||
{
|
{
|
||||||
icon: 'support/bill/payRecordLight',
|
icon: 'support/bill/payRecordLight',
|
||||||
label: t('support.wallet.Bills'),
|
label: t('support.wallet.Bills'),
|
||||||
id: TabEnum.bill
|
value: TabEnum.bill
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
: []),
|
: []),
|
||||||
@ -66,7 +66,7 @@ const Account = ({ currentTab }: { currentTab: `${TabEnum}` }) => {
|
|||||||
{
|
{
|
||||||
icon: 'support/account/promotionLight',
|
icon: 'support/account/promotionLight',
|
||||||
label: t('user.Promotion Record'),
|
label: t('user.Promotion Record'),
|
||||||
id: TabEnum.promotion
|
value: TabEnum.promotion
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
: []),
|
: []),
|
||||||
@ -75,21 +75,21 @@ const Account = ({ currentTab }: { currentTab: `${TabEnum}` }) => {
|
|||||||
{
|
{
|
||||||
icon: 'support/outlink/apikeyLight',
|
icon: 'support/outlink/apikeyLight',
|
||||||
label: t('user.apikey.key'),
|
label: t('user.apikey.key'),
|
||||||
id: TabEnum.apikey
|
value: TabEnum.apikey
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
: []),
|
: []),
|
||||||
{
|
{
|
||||||
icon: 'support/user/individuation',
|
icon: 'support/user/individuation',
|
||||||
label: t('support.account.Individuation'),
|
label: t('support.account.Individuation'),
|
||||||
id: TabEnum.individuation
|
value: TabEnum.individuation
|
||||||
},
|
},
|
||||||
...(feConfigs.isPlus
|
...(feConfigs.isPlus
|
||||||
? [
|
? [
|
||||||
{
|
{
|
||||||
icon: 'support/user/informLight',
|
icon: 'support/user/informLight',
|
||||||
label: t('user.Notice'),
|
label: t('user.Notice'),
|
||||||
id: TabEnum.inform
|
value: TabEnum.inform
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
: []),
|
: []),
|
||||||
@ -97,7 +97,7 @@ const Account = ({ currentTab }: { currentTab: `${TabEnum}` }) => {
|
|||||||
{
|
{
|
||||||
icon: 'support/account/loginoutLight',
|
icon: 'support/account/loginoutLight',
|
||||||
label: t('user.Sign Out'),
|
label: t('user.Sign Out'),
|
||||||
id: TabEnum.loginout
|
value: TabEnum.loginout
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -139,13 +139,13 @@ const Account = ({ currentTab }: { currentTab: `${TabEnum}` }) => {
|
|||||||
flex={'0 0 200px'}
|
flex={'0 0 200px'}
|
||||||
borderRight={theme.borders.base}
|
borderRight={theme.borders.base}
|
||||||
>
|
>
|
||||||
<SideTabs
|
<SideTabs<TabEnum>
|
||||||
flex={1}
|
flex={1}
|
||||||
mx={'auto'}
|
mx={'auto'}
|
||||||
mt={2}
|
mt={2}
|
||||||
w={'100%'}
|
w={'100%'}
|
||||||
list={tabList}
|
list={tabList}
|
||||||
activeId={currentTab}
|
value={currentTab}
|
||||||
onChange={setCurrentTab}
|
onChange={setCurrentTab}
|
||||||
/>
|
/>
|
||||||
<Flex alignItems={'center'}>
|
<Flex alignItems={'center'}>
|
||||||
@ -157,14 +157,14 @@ const Account = ({ currentTab }: { currentTab: `${TabEnum}` }) => {
|
|||||||
</Flex>
|
</Flex>
|
||||||
) : (
|
) : (
|
||||||
<Box mb={3}>
|
<Box mb={3}>
|
||||||
<Tabs
|
<LightRowTabs<TabEnum>
|
||||||
m={'auto'}
|
m={'auto'}
|
||||||
size={isPc ? 'md' : 'sm'}
|
size={isPc ? 'md' : 'sm'}
|
||||||
list={tabList.map((item) => ({
|
list={tabList.map((item) => ({
|
||||||
id: item.id,
|
value: item.value,
|
||||||
label: item.label
|
label: item.label
|
||||||
}))}
|
}))}
|
||||||
activeId={currentTab}
|
value={currentTab}
|
||||||
onChange={setCurrentTab}
|
onChange={setCurrentTab}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@ -8,7 +8,7 @@ import { ApiRequestProps } from '@fastgpt/service/type/next';
|
|||||||
|
|
||||||
/* update chat top, custom title */
|
/* update chat top, custom title */
|
||||||
async function handler(req: ApiRequestProps<UpdateHistoryProps>, res: NextApiResponse) {
|
async function handler(req: ApiRequestProps<UpdateHistoryProps>, res: NextApiResponse) {
|
||||||
const { appId, chatId, customTitle, top } = req.body;
|
const { appId, chatId, title, customTitle, top } = req.body;
|
||||||
await autChatCrud({
|
await autChatCrud({
|
||||||
req,
|
req,
|
||||||
authToken: true,
|
authToken: true,
|
||||||
@ -20,6 +20,7 @@ async function handler(req: ApiRequestProps<UpdateHistoryProps>, res: NextApiRes
|
|||||||
{ appId, chatId },
|
{ appId, chatId },
|
||||||
{
|
{
|
||||||
updateTime: new Date(),
|
updateTime: new Date(),
|
||||||
|
...(title !== undefined && { title }),
|
||||||
...(customTitle !== undefined && { customTitle }),
|
...(customTitle !== undefined && { customTitle }),
|
||||||
...(top !== undefined && { top })
|
...(top !== undefined && { top })
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,7 +19,7 @@ import {
|
|||||||
Switch,
|
Switch,
|
||||||
Textarea
|
Textarea
|
||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
import RowTabs from '@fastgpt/web/components/common/Tabs/RowTabs';
|
import FillRowTabs from '@fastgpt/web/components/common/Tabs/FillRowTabs';
|
||||||
import { useRequest, useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
import { useRequest, useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
||||||
import EmptyTip from '@fastgpt/web/components/common/EmptyTip';
|
import EmptyTip from '@fastgpt/web/components/common/EmptyTip';
|
||||||
import { FlowNodeTemplateType } from '@fastgpt/global/core/workflow/type/index.d';
|
import { FlowNodeTemplateType } from '@fastgpt/global/core/workflow/type/index.d';
|
||||||
@ -97,7 +97,7 @@ const ToolSelectModal = ({ onClose, ...props }: Props & { onClose: () => void })
|
|||||||
>
|
>
|
||||||
{/* Header: row and search */}
|
{/* Header: row and search */}
|
||||||
<Box px={[3, 6]} pt={4} display={'flex'} justifyContent={'space-between'} w={'full'}>
|
<Box px={[3, 6]} pt={4} display={'flex'} justifyContent={'space-between'} w={'full'}>
|
||||||
<RowTabs
|
<FillRowTabs
|
||||||
list={[
|
list={[
|
||||||
{
|
{
|
||||||
icon: 'core/modules/teamPlugin',
|
icon: 'core/modules/teamPlugin',
|
||||||
|
|||||||
@ -15,7 +15,7 @@ import { getPreviewPluginNode, getSystemPlugTemplates } from '@/web/core/app/api
|
|||||||
import { useToast } from '@fastgpt/web/hooks/useToast';
|
import { useToast } from '@fastgpt/web/hooks/useToast';
|
||||||
import { getErrText } from '@fastgpt/global/common/error/utils';
|
import { getErrText } from '@fastgpt/global/common/error/utils';
|
||||||
import { workflowNodeTemplateList } from '@fastgpt/web/core/workflow/constants';
|
import { workflowNodeTemplateList } from '@fastgpt/web/core/workflow/constants';
|
||||||
import RowTabs from '@fastgpt/web/components/common/Tabs/RowTabs';
|
import FillRowTabs from '@fastgpt/web/components/common/Tabs/FillRowTabs';
|
||||||
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
||||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
@ -143,7 +143,7 @@ const NodeTemplatesModal = ({ isOpen, onClose }: ModuleTemplateListProps) => {
|
|||||||
>
|
>
|
||||||
<Box pl={'20px'} mb={3} pr={'10px'} whiteSpace={'nowrap'} overflow={'hidden'}>
|
<Box pl={'20px'} mb={3} pr={'10px'} whiteSpace={'nowrap'} overflow={'hidden'}>
|
||||||
<Flex flex={'1 0 0'} alignItems={'center'} gap={3}>
|
<Flex flex={'1 0 0'} alignItems={'center'} gap={3}>
|
||||||
<RowTabs
|
<FillRowTabs
|
||||||
list={[
|
list={[
|
||||||
{
|
{
|
||||||
icon: 'core/modules/basicNode',
|
icon: 'core/modules/basicNode',
|
||||||
|
|||||||
@ -21,11 +21,10 @@ import {
|
|||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
import { NodeInputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
import { NodeInputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
import Tabs from '@/components/Tabs';
|
import LightRowTabs from '@fastgpt/web/components/common/Tabs/LightRowTabs';
|
||||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||||
import { FlowNodeInputItemType } from '@fastgpt/global/core/workflow/type/io.d';
|
import { FlowNodeInputItemType } from '@fastgpt/global/core/workflow/type/io.d';
|
||||||
import { useToast } from '@fastgpt/web/hooks/useToast';
|
import { useToast } from '@fastgpt/web/hooks/useToast';
|
||||||
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
|
|
||||||
import JSONEditor from '@fastgpt/web/components/common/Textarea/JsonEditor';
|
import JSONEditor from '@fastgpt/web/components/common/Textarea/JsonEditor';
|
||||||
import { formatEditorVariablePickerIcon } from '@fastgpt/global/core/workflow/utils';
|
import { formatEditorVariablePickerIcon } from '@fastgpt/global/core/workflow/utils';
|
||||||
import { EditorVariablePickerType } from '@fastgpt/web/components/common/Textarea/PromptEditor/type';
|
import { EditorVariablePickerType } from '@fastgpt/web/components/common/Textarea/PromptEditor/type';
|
||||||
@ -310,9 +309,9 @@ export function RenderHttpProps({
|
|||||||
label={t('core.module.http.Props tip', { variable: variableText })}
|
label={t('core.module.http.Props tip', { variable: variableText })}
|
||||||
></QuestionTip>
|
></QuestionTip>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Tabs
|
<LightRowTabs<TabEnum>
|
||||||
list={[
|
list={[
|
||||||
{ label: <RenderPropsItem text="Params" num={paramsLength} />, id: TabEnum.params },
|
{ label: <RenderPropsItem text="Params" num={paramsLength} />, value: TabEnum.params },
|
||||||
...(!['GET', 'DELETE'].includes(requestMethods)
|
...(!['GET', 'DELETE'].includes(requestMethods)
|
||||||
? [
|
? [
|
||||||
{
|
{
|
||||||
@ -322,14 +321,17 @@ export function RenderHttpProps({
|
|||||||
{jsonBody?.value && <Box ml={1}>✅</Box>}
|
{jsonBody?.value && <Box ml={1}>✅</Box>}
|
||||||
</Flex>
|
</Flex>
|
||||||
),
|
),
|
||||||
id: TabEnum.body
|
value: TabEnum.body
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
: []),
|
: []),
|
||||||
{ label: <RenderPropsItem text="Headers" num={headersLength} />, id: TabEnum.headers }
|
{
|
||||||
|
label: <RenderPropsItem text="Headers" num={headersLength} />,
|
||||||
|
value: TabEnum.headers
|
||||||
|
}
|
||||||
]}
|
]}
|
||||||
activeId={selectedTab}
|
value={selectedTab}
|
||||||
onChange={(e) => setSelectedTab(e as any)}
|
onChange={setSelectedTab}
|
||||||
/>
|
/>
|
||||||
<Box bg={'white'} borderRadius={'md'}>
|
<Box bg={'white'} borderRadius={'md'}>
|
||||||
{params &&
|
{params &&
|
||||||
|
|||||||
@ -1,8 +1,7 @@
|
|||||||
import React, { useCallback, useState } from 'react';
|
import React, { useCallback, useState } from 'react';
|
||||||
import { Box, Flex, Button, useDisclosure, HStack } from '@chakra-ui/react';
|
import { Box, Flex, Button, useDisclosure } from '@chakra-ui/react';
|
||||||
import { AddIcon } from '@chakra-ui/icons';
|
import { AddIcon } from '@chakra-ui/icons';
|
||||||
import { serviceSideProps } from '@/web/common/utils/i18n';
|
import { serviceSideProps } from '@/web/common/utils/i18n';
|
||||||
import PageContainer from '@/components/PageContainer';
|
|
||||||
import { useUserStore } from '@/web/support/user/useUserStore';
|
import { useUserStore } from '@/web/support/user/useUserStore';
|
||||||
import { useI18n } from '@/web/context/I18n';
|
import { useI18n } from '@/web/context/I18n';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
@ -32,8 +31,8 @@ import {
|
|||||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||||
import type { CreateAppType } from './components/CreateModal';
|
import type { CreateAppType } from './components/CreateModal';
|
||||||
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
|
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
|
||||||
import Tabs from '@/components/Tabs';
|
|
||||||
import MyBox from '@fastgpt/web/components/common/MyBox';
|
import MyBox from '@fastgpt/web/components/common/MyBox';
|
||||||
|
import LightRowTabs from '@fastgpt/web/components/common/Tabs/LightRowTabs';
|
||||||
|
|
||||||
const CreateModal = dynamic(() => import('./components/CreateModal'));
|
const CreateModal = dynamic(() => import('./components/CreateModal'));
|
||||||
const EditFolderModal = dynamic(
|
const EditFolderModal = dynamic(
|
||||||
@ -121,26 +120,26 @@ const MyApps = () => {
|
|||||||
alignItems={'center'}
|
alignItems={'center'}
|
||||||
justifyContent={'space-between'}
|
justifyContent={'space-between'}
|
||||||
>
|
>
|
||||||
<Tabs
|
<LightRowTabs
|
||||||
list={[
|
list={[
|
||||||
{
|
{
|
||||||
label: appT('type.All'),
|
label: appT('type.All'),
|
||||||
id: 'ALL'
|
value: 'ALL'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: appT('type.Simple bot'),
|
label: appT('type.Simple bot'),
|
||||||
id: AppTypeEnum.simple
|
value: AppTypeEnum.simple
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: appT('type.Workflow bot'),
|
label: appT('type.Workflow bot'),
|
||||||
id: AppTypeEnum.workflow
|
value: AppTypeEnum.workflow
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: appT('type.Plugin'),
|
label: appT('type.Plugin'),
|
||||||
id: AppTypeEnum.plugin
|
value: AppTypeEnum.plugin
|
||||||
}
|
}
|
||||||
]}
|
]}
|
||||||
activeId={appType}
|
value={appType}
|
||||||
inlineStyles={{ px: 0.5 }}
|
inlineStyles={{ px: 0.5 }}
|
||||||
gap={5}
|
gap={5}
|
||||||
display={'flex'}
|
display={'flex'}
|
||||||
|
|||||||
@ -9,6 +9,8 @@ import { useRouter } from 'next/router';
|
|||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
import { getChatTitleFromChatMessage } from '@fastgpt/global/core/chat/utils';
|
import { getChatTitleFromChatMessage } from '@fastgpt/global/core/chat/utils';
|
||||||
import MyTag from '@fastgpt/web/components/common/Tag/index';
|
import MyTag from '@fastgpt/web/components/common/Tag/index';
|
||||||
|
import { useContextSelector } from 'use-context-selector';
|
||||||
|
import { ChatContext } from '@/web/core/chat/context/chatContext';
|
||||||
|
|
||||||
const ChatHeader = ({
|
const ChatHeader = ({
|
||||||
history,
|
history,
|
||||||
@ -16,8 +18,7 @@ const ChatHeader = ({
|
|||||||
appAvatar,
|
appAvatar,
|
||||||
chatModels,
|
chatModels,
|
||||||
showHistory,
|
showHistory,
|
||||||
onRoute2AppDetail,
|
onRoute2AppDetail
|
||||||
onOpenSlider
|
|
||||||
}: {
|
}: {
|
||||||
history: ChatItemType[];
|
history: ChatItemType[];
|
||||||
appName: string;
|
appName: string;
|
||||||
@ -25,7 +26,6 @@ const ChatHeader = ({
|
|||||||
chatModels?: string[];
|
chatModels?: string[];
|
||||||
showHistory?: boolean;
|
showHistory?: boolean;
|
||||||
onRoute2AppDetail?: () => void;
|
onRoute2AppDetail?: () => void;
|
||||||
onOpenSlider: () => void;
|
|
||||||
}) => {
|
}) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@ -36,6 +36,8 @@ const ChatHeader = ({
|
|||||||
[appName, history, t]
|
[appName, history, t]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const onOpenSlider = useContextSelector(ChatContext, (v) => v.onOpenSlider);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex
|
<Flex
|
||||||
alignItems={'center'}
|
alignItems={'center'}
|
||||||
|
|||||||
@ -8,7 +8,7 @@ import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
|
|||||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
|
import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
|
||||||
import Tabs from '@/components/Tabs';
|
import LightRowTabs from '@fastgpt/web/components/common/Tabs/LightRowTabs';
|
||||||
import { useUserStore } from '@/web/support/user/useUserStore';
|
import { useUserStore } from '@/web/support/user/useUserStore';
|
||||||
import { AppListItemType } from '@fastgpt/global/core/app/type';
|
import { AppListItemType } from '@fastgpt/global/core/app/type';
|
||||||
import { useI18n } from '@/web/context/I18n';
|
import { useI18n } from '@/web/context/I18n';
|
||||||
@ -20,6 +20,9 @@ import {
|
|||||||
} from '@fastgpt/global/common/parentFolder/type';
|
} from '@fastgpt/global/common/parentFolder/type';
|
||||||
import { getMyApps } from '@/web/core/app/api';
|
import { getMyApps } from '@/web/core/app/api';
|
||||||
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
|
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
|
||||||
|
import { useContextSelector } from 'use-context-selector';
|
||||||
|
import { ChatContext } from '@/web/core/chat/context/chatContext';
|
||||||
|
import MyBox from '@fastgpt/web/components/common/MyBox';
|
||||||
|
|
||||||
type HistoryItemType = {
|
type HistoryItemType = {
|
||||||
id: string;
|
id: string;
|
||||||
@ -38,30 +41,22 @@ const ChatHistorySlider = ({
|
|||||||
appId,
|
appId,
|
||||||
appName,
|
appName,
|
||||||
appAvatar,
|
appAvatar,
|
||||||
history,
|
|
||||||
apps = [],
|
apps = [],
|
||||||
confirmClearText,
|
confirmClearText,
|
||||||
activeChatId,
|
|
||||||
onChangeChat,
|
|
||||||
onDelHistory,
|
onDelHistory,
|
||||||
onClearHistory,
|
onClearHistory,
|
||||||
onSetHistoryTop,
|
onSetHistoryTop,
|
||||||
onSetCustomTitle,
|
onSetCustomTitle
|
||||||
onClose
|
|
||||||
}: {
|
}: {
|
||||||
appId?: string;
|
appId?: string;
|
||||||
appName: string;
|
appName: string;
|
||||||
appAvatar: string;
|
appAvatar: string;
|
||||||
history: HistoryItemType[];
|
|
||||||
activeChatId: string;
|
|
||||||
apps?: AppListItemType[];
|
apps?: AppListItemType[];
|
||||||
confirmClearText: string;
|
confirmClearText: string;
|
||||||
onChangeChat: (chatId?: string) => void;
|
|
||||||
onDelHistory: (e: { chatId: string }) => void;
|
onDelHistory: (e: { chatId: string }) => void;
|
||||||
onClearHistory: () => void;
|
onClearHistory: () => void;
|
||||||
onSetHistoryTop?: (e: { chatId: string; top: boolean }) => void;
|
onSetHistoryTop?: (e: { chatId: string; top: boolean }) => void;
|
||||||
onSetCustomTitle?: (e: { chatId: string; title: string }) => void;
|
onSetCustomTitle?: (e: { chatId: string; title: string }) => void;
|
||||||
onClose: () => void;
|
|
||||||
}) => {
|
}) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
@ -73,7 +68,28 @@ const ChatHistorySlider = ({
|
|||||||
const { isPc } = useSystemStore();
|
const { isPc } = useSystemStore();
|
||||||
const { userInfo } = useUserStore();
|
const { userInfo } = useUserStore();
|
||||||
|
|
||||||
const [currentTab, setCurrentTab] = useState<`${TabEnum}`>(TabEnum.history);
|
const [currentTab, setCurrentTab] = useState<TabEnum>(TabEnum.history);
|
||||||
|
|
||||||
|
const {
|
||||||
|
histories,
|
||||||
|
onChangeChatId,
|
||||||
|
onChangeAppId,
|
||||||
|
chatId: activeChatId,
|
||||||
|
isLoading
|
||||||
|
} = useContextSelector(ChatContext, (v) => v);
|
||||||
|
|
||||||
|
const concatHistory = useMemo(() => {
|
||||||
|
const formatHistories: HistoryItemType[] = histories.map((item) => ({
|
||||||
|
id: item.chatId,
|
||||||
|
title: item.title,
|
||||||
|
customTitle: item.customTitle,
|
||||||
|
top: item.top
|
||||||
|
}));
|
||||||
|
const newChat: HistoryItemType = { id: activeChatId, title: t('core.chat.New Chat') };
|
||||||
|
const activeChat = histories.find((item) => item.chatId === activeChatId);
|
||||||
|
|
||||||
|
return !activeChat ? [newChat].concat(formatHistories) : formatHistories;
|
||||||
|
}, [activeChatId, histories, t]);
|
||||||
|
|
||||||
const showApps = apps?.length > 0;
|
const showApps = apps?.length > 0;
|
||||||
|
|
||||||
@ -86,15 +102,6 @@ const ChatHistorySlider = ({
|
|||||||
content: confirmClearText
|
content: confirmClearText
|
||||||
});
|
});
|
||||||
|
|
||||||
const concatHistory = useMemo<HistoryItemType[]>(
|
|
||||||
() =>
|
|
||||||
!activeChatId
|
|
||||||
? //@ts-ignore
|
|
||||||
[{ id: activeChatId, title: t('core.chat.New Chat') }].concat(history)
|
|
||||||
: history,
|
|
||||||
[activeChatId, history, t]
|
|
||||||
);
|
|
||||||
|
|
||||||
const canRouteToDetail = useMemo(
|
const canRouteToDetail = useMemo(
|
||||||
() => appId && userInfo?.team.permission.hasWritePer,
|
() => appId && userInfo?.team.permission.hasWritePer,
|
||||||
[appId, userInfo?.team.permission.hasWritePer]
|
[appId, userInfo?.team.permission.hasWritePer]
|
||||||
@ -111,22 +118,10 @@ const ChatHistorySlider = ({
|
|||||||
);
|
);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const onChangeApp = useCallback(
|
|
||||||
(appId: string) => {
|
|
||||||
router.replace({
|
|
||||||
query: {
|
|
||||||
...router.query,
|
|
||||||
chatId: '',
|
|
||||||
appId
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[router]
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex
|
<MyBox
|
||||||
position={'relative'}
|
isLoading={isLoading}
|
||||||
|
display={'flex'}
|
||||||
flexDirection={'column'}
|
flexDirection={'column'}
|
||||||
w={'100%'}
|
w={'100%'}
|
||||||
h={'100%'}
|
h={'100%'}
|
||||||
@ -162,27 +157,27 @@ const ChatHistorySlider = ({
|
|||||||
{/* menu */}
|
{/* menu */}
|
||||||
<Flex w={'100%'} px={[2, 5]} h={'36px'} my={5} alignItems={'center'}>
|
<Flex w={'100%'} px={[2, 5]} h={'36px'} my={5} alignItems={'center'}>
|
||||||
{!isPc && appId && (
|
{!isPc && appId && (
|
||||||
<Tabs
|
<LightRowTabs<TabEnum>
|
||||||
flex={'1 0 0'}
|
flex={'1 0 0'}
|
||||||
mr={2}
|
mr={2}
|
||||||
list={[
|
list={[
|
||||||
{ label: t('core.chat.Recent use'), id: TabEnum.recently },
|
{ label: t('core.chat.Recent use'), value: TabEnum.recently },
|
||||||
{ label: t('App'), id: TabEnum.app },
|
{ label: t('App'), value: TabEnum.app },
|
||||||
{ label: t('core.chat.History'), id: TabEnum.history }
|
{ label: t('core.chat.History'), value: TabEnum.history }
|
||||||
]}
|
]}
|
||||||
activeId={currentTab}
|
value={currentTab}
|
||||||
onChange={(e) => setCurrentTab(e as `${TabEnum}`)}
|
onChange={setCurrentTab}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<Button
|
<Button
|
||||||
variant={'whitePrimary'}
|
variant={'whitePrimary'}
|
||||||
flex={['0', 1]}
|
flex={['0 0 auto', 1]}
|
||||||
h={'100%'}
|
h={'100%'}
|
||||||
color={'primary.600'}
|
color={'primary.600'}
|
||||||
borderRadius={'xl'}
|
borderRadius={'xl'}
|
||||||
leftIcon={<MyIcon name={'core/chat/chatLight'} w={'16px'} />}
|
leftIcon={<MyIcon name={'core/chat/chatLight'} w={'16px'} />}
|
||||||
overflow={'hidden'}
|
overflow={'hidden'}
|
||||||
onClick={() => onChangeChat()}
|
onClick={() => onChangeChatId()}
|
||||||
>
|
>
|
||||||
{t('core.chat.New Chat')}
|
{t('core.chat.New Chat')}
|
||||||
</Button>
|
</Button>
|
||||||
@ -195,7 +190,11 @@ const ChatHistorySlider = ({
|
|||||||
size={'mdSquare'}
|
size={'mdSquare'}
|
||||||
aria-label={''}
|
aria-label={''}
|
||||||
borderRadius={'50%'}
|
borderRadius={'50%'}
|
||||||
onClick={openConfirm(onClearHistory)}
|
onClick={() =>
|
||||||
|
openConfirm(() => {
|
||||||
|
onClearHistory();
|
||||||
|
})()
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<MyIcon name={'common/clearLight'} w={'16px'} />
|
<MyIcon name={'common/clearLight'} w={'16px'} />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
@ -232,7 +231,7 @@ const ChatHistorySlider = ({
|
|||||||
}
|
}
|
||||||
: {
|
: {
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
onChangeChat(item.id);
|
onChangeChatId(item.id);
|
||||||
}
|
}
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
@ -292,7 +291,7 @@ const ChatHistorySlider = ({
|
|||||||
onClick: () => {
|
onClick: () => {
|
||||||
onDelHistory({ chatId: item.id });
|
onDelHistory({ chatId: item.id });
|
||||||
if (item.id === activeChatId) {
|
if (item.id === activeChatId) {
|
||||||
onChangeChat();
|
onChangeChatId();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
type: 'danger'
|
type: 'danger'
|
||||||
@ -324,10 +323,7 @@ const ChatHistorySlider = ({
|
|||||||
color: 'primary.600'
|
color: 'primary.600'
|
||||||
}
|
}
|
||||||
: {
|
: {
|
||||||
onClick: () => {
|
onClick: () => onChangeAppId(item._id)
|
||||||
onChangeApp(item._id);
|
|
||||||
onClose();
|
|
||||||
}
|
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<Avatar src={item.avatar} w={'24px'} />
|
<Avatar src={item.avatar} w={'24px'} />
|
||||||
@ -344,8 +340,7 @@ const ChatHistorySlider = ({
|
|||||||
value={appId}
|
value={appId}
|
||||||
onSelect={(id) => {
|
onSelect={(id) => {
|
||||||
if (!id) return;
|
if (!id) return;
|
||||||
onChangeApp(id);
|
onChangeAppId(id);
|
||||||
onClose();
|
|
||||||
}}
|
}}
|
||||||
server={getAppList}
|
server={getAppList}
|
||||||
/>
|
/>
|
||||||
@ -377,7 +372,7 @@ const ChatHistorySlider = ({
|
|||||||
)}
|
)}
|
||||||
<EditTitleModal />
|
<EditTitleModal />
|
||||||
<ConfirmModal />
|
<ConfirmModal />
|
||||||
</Flex>
|
</MyBox>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -7,13 +7,15 @@ import Avatar from '@/components/Avatar';
|
|||||||
import { AppListItemType } from '@fastgpt/global/core/app/type';
|
import { AppListItemType } from '@fastgpt/global/core/app/type';
|
||||||
import MyDivider from '@fastgpt/web/components/common/MyDivider';
|
import MyDivider from '@fastgpt/web/components/common/MyDivider';
|
||||||
import MyPopover from '@fastgpt/web/components/common/MyPopover/index';
|
import MyPopover from '@fastgpt/web/components/common/MyPopover/index';
|
||||||
import SelectOneResource from '@/components/common/folder/SelectOneResource';
|
|
||||||
import { getMyApps } from '@/web/core/app/api';
|
import { getMyApps } from '@/web/core/app/api';
|
||||||
import {
|
import {
|
||||||
GetResourceFolderListProps,
|
GetResourceFolderListProps,
|
||||||
GetResourceListItemResponse
|
GetResourceListItemResponse
|
||||||
} from '@fastgpt/global/common/parentFolder/type';
|
} from '@fastgpt/global/common/parentFolder/type';
|
||||||
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
|
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
|
||||||
|
import dynamic from 'next/dynamic';
|
||||||
|
|
||||||
|
const SelectOneResource = dynamic(() => import('@/components/common/folder/SelectOneResource'));
|
||||||
|
|
||||||
const SliderApps = ({ apps, activeAppId }: { apps: AppListItemType[]; activeAppId: string }) => {
|
const SliderApps = ({ apps, activeAppId }: { apps: AppListItemType[]; activeAppId: string }) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@ -74,11 +76,6 @@ const SliderApps = ({ apps, activeAppId }: { apps: AppListItemType[]; activeAppI
|
|||||||
{!isTeamChat && (
|
{!isTeamChat && (
|
||||||
<>
|
<>
|
||||||
<MyDivider h={2} my={1} />
|
<MyDivider h={2} my={1} />
|
||||||
<MyPopover
|
|
||||||
placement="right-start"
|
|
||||||
offset={[30, -65]}
|
|
||||||
trigger="hover"
|
|
||||||
Trigger={
|
|
||||||
<HStack
|
<HStack
|
||||||
px={4}
|
px={4}
|
||||||
my={2}
|
my={2}
|
||||||
@ -87,6 +84,11 @@ const SliderApps = ({ apps, activeAppId }: { apps: AppListItemType[]; activeAppI
|
|||||||
justifyContent={'space-between'}
|
justifyContent={'space-between'}
|
||||||
>
|
>
|
||||||
<Box>{t('core.chat.Recent use')}</Box>
|
<Box>{t('core.chat.Recent use')}</Box>
|
||||||
|
<MyPopover
|
||||||
|
placement="bottom-end"
|
||||||
|
offset={[20, 10]}
|
||||||
|
trigger="hover"
|
||||||
|
Trigger={
|
||||||
<HStack
|
<HStack
|
||||||
spacing={0.5}
|
spacing={0.5}
|
||||||
cursor={'pointer'}
|
cursor={'pointer'}
|
||||||
@ -102,7 +104,6 @@ const SliderApps = ({ apps, activeAppId }: { apps: AppListItemType[]; activeAppI
|
|||||||
<Box>{t('common.More')}</Box>
|
<Box>{t('common.More')}</Box>
|
||||||
<MyIcon name={'common/select'} w={'1rem'} />
|
<MyIcon name={'common/select'} w={'1rem'} />
|
||||||
</HStack>
|
</HStack>
|
||||||
</HStack>
|
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{({ onClose }) => (
|
{({ onClose }) => (
|
||||||
@ -119,6 +120,7 @@ const SliderApps = ({ apps, activeAppId }: { apps: AppListItemType[]; activeAppI
|
|||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
</MyPopover>
|
</MyPopover>
|
||||||
|
</HStack>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
@ -1,25 +1,12 @@
|
|||||||
import React, { useCallback, useRef } from 'react';
|
import React, { useCallback, useRef, useState } from 'react';
|
||||||
import NextHead from '@/components/common/NextHead';
|
import NextHead from '@/components/common/NextHead';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import { getInitChatInfo } from '@/web/core/chat/api';
|
import { delChatRecordById, getChatHistories, getInitChatInfo } from '@/web/core/chat/api';
|
||||||
import {
|
import { Box, Flex, Drawer, DrawerOverlay, DrawerContent, useTheme } from '@chakra-ui/react';
|
||||||
Box,
|
|
||||||
Flex,
|
|
||||||
useDisclosure,
|
|
||||||
Drawer,
|
|
||||||
DrawerOverlay,
|
|
||||||
DrawerContent,
|
|
||||||
useTheme
|
|
||||||
} from '@chakra-ui/react';
|
|
||||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||||
import { useQuery } from '@tanstack/react-query';
|
|
||||||
import { streamFetch } from '@/web/common/api/fetch';
|
import { streamFetch } from '@/web/common/api/fetch';
|
||||||
import { useChatStore } from '@/web/core/chat/storeChat';
|
import { useChatStore } from '@/web/core/chat/context/storeChat';
|
||||||
import { useLoading } from '@fastgpt/web/hooks/useLoading';
|
|
||||||
import { useToast } from '@fastgpt/web/hooks/useToast';
|
import { useToast } from '@fastgpt/web/hooks/useToast';
|
||||||
import { customAlphabet } from 'nanoid';
|
|
||||||
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 12);
|
|
||||||
import type { ChatHistoryItemType } from '@fastgpt/global/core/chat/type.d';
|
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
|
|
||||||
import ChatBox from '@/components/ChatBox';
|
import ChatBox from '@/components/ChatBox';
|
||||||
@ -29,7 +16,6 @@ import SideBar from '@/components/SideBar';
|
|||||||
import ChatHistorySlider from './components/ChatHistorySlider';
|
import ChatHistorySlider from './components/ChatHistorySlider';
|
||||||
import SliderApps from './components/SliderApps';
|
import SliderApps from './components/SliderApps';
|
||||||
import ChatHeader from './components/ChatHeader';
|
import ChatHeader from './components/ChatHeader';
|
||||||
import { getErrText } from '@fastgpt/global/common/error/utils';
|
|
||||||
import { useUserStore } from '@/web/support/user/useUserStore';
|
import { useUserStore } from '@/web/support/user/useUserStore';
|
||||||
import { serviceSideProps } from '@/web/common/utils/i18n';
|
import { serviceSideProps } from '@/web/common/utils/i18n';
|
||||||
import { checkChatSupportSelectFileByChatModels } from '@/web/core/chat/utils';
|
import { checkChatSupportSelectFileByChatModels } from '@/web/core/chat/utils';
|
||||||
@ -39,40 +25,48 @@ import { GPTMessages2Chats } from '@fastgpt/global/core/chat/adapt';
|
|||||||
import { getMyApps } from '@/web/core/app/api';
|
import { getMyApps } from '@/web/core/app/api';
|
||||||
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
||||||
|
|
||||||
const Chat = ({ appId, chatId }: { appId: string; chatId: string }) => {
|
import { useMount } from 'ahooks';
|
||||||
|
import { getNanoid } from '@fastgpt/global/common/string/tools';
|
||||||
|
import { InitChatResponse } from '@/global/core/chat/api';
|
||||||
|
import { defaultChatData } from '@/global/core/chat/constants';
|
||||||
|
import ChatContextProvider, { ChatContext } from '@/web/core/chat/context/chatContext';
|
||||||
|
import { AppListItemType } from '@fastgpt/global/core/app/type';
|
||||||
|
import { useContextSelector } from 'use-context-selector';
|
||||||
|
|
||||||
|
type Props = { appId: string; chatId: string };
|
||||||
|
|
||||||
|
const Chat = ({
|
||||||
|
appId,
|
||||||
|
chatId,
|
||||||
|
myApps
|
||||||
|
}: Props & {
|
||||||
|
myApps: AppListItemType[];
|
||||||
|
}) => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { toast } = useToast();
|
|
||||||
|
|
||||||
const ChatBoxRef = useRef<ComponentRef>(null);
|
const ChatBoxRef = useRef<ComponentRef>(null);
|
||||||
const forbidRefresh = useRef(false);
|
|
||||||
|
|
||||||
|
const { setLastChatAppId } = useChatStore();
|
||||||
const {
|
const {
|
||||||
lastChatAppId,
|
|
||||||
setLastChatAppId,
|
|
||||||
lastChatId,
|
|
||||||
setLastChatId,
|
|
||||||
histories,
|
|
||||||
loadHistories,
|
loadHistories,
|
||||||
pushHistory,
|
onUpdateHistory,
|
||||||
updateHistory,
|
onClearHistories,
|
||||||
delOneHistory,
|
onDelHistory,
|
||||||
clearHistories,
|
isOpenSlider,
|
||||||
chatData,
|
onCloseSlider,
|
||||||
setChatData,
|
forbidLoadChat,
|
||||||
delOneHistoryItem
|
onChangeChatId
|
||||||
} = useChatStore();
|
} = useContextSelector(ChatContext, (v) => v);
|
||||||
const { userInfo } = useUserStore();
|
|
||||||
|
|
||||||
|
const { userInfo } = useUserStore();
|
||||||
const { isPc } = useSystemStore();
|
const { isPc } = useSystemStore();
|
||||||
const { Loading, setIsLoading } = useLoading();
|
|
||||||
const { isOpen: isOpenSlider, onClose: onCloseSlider, onOpen: onOpenSlider } = useDisclosure();
|
|
||||||
|
|
||||||
const startChat = useCallback(
|
const startChat = useCallback(
|
||||||
async ({ messages, controller, generatingMessage, variables }: StartChatFnProps) => {
|
async ({ messages, controller, generatingMessage, variables }: StartChatFnProps) => {
|
||||||
const prompts = messages.slice(-2);
|
const prompts = messages.slice(-2);
|
||||||
const completionChatId = chatId ? chatId : nanoid();
|
const completionChatId = chatId ? chatId : getNanoid();
|
||||||
|
|
||||||
const { responseText, responseData } = await streamFetch({
|
const { responseText, responseData } = await streamFetch({
|
||||||
data: {
|
data: {
|
||||||
@ -89,170 +83,82 @@ const Chat = ({ appId, chatId }: { appId: string; chatId: string }) => {
|
|||||||
|
|
||||||
// new chat
|
// new chat
|
||||||
if (completionChatId !== chatId) {
|
if (completionChatId !== chatId) {
|
||||||
const newHistory: ChatHistoryItemType = {
|
|
||||||
chatId: completionChatId,
|
|
||||||
updateTime: new Date(),
|
|
||||||
title: newTitle,
|
|
||||||
appId,
|
|
||||||
top: false
|
|
||||||
};
|
|
||||||
pushHistory(newHistory);
|
|
||||||
if (controller.signal.reason !== 'leave') {
|
if (controller.signal.reason !== 'leave') {
|
||||||
forbidRefresh.current = true;
|
onChangeChatId(completionChatId, true);
|
||||||
router.replace({
|
loadHistories();
|
||||||
query: {
|
|
||||||
chatId: completionChatId,
|
|
||||||
appId
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// update chat
|
// update chat
|
||||||
const currentChat = histories.find((item) => item.chatId === chatId);
|
onUpdateHistory({
|
||||||
currentChat &&
|
appId,
|
||||||
updateHistory({
|
chatId: completionChatId,
|
||||||
...currentChat,
|
|
||||||
updateTime: new Date(),
|
|
||||||
title: newTitle
|
title: newTitle
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// update chat window
|
// update chat window
|
||||||
setChatData((state) => ({
|
setChatData((state) => ({
|
||||||
...state,
|
...state,
|
||||||
title: newTitle,
|
title: newTitle
|
||||||
history: ChatBoxRef.current?.getChatHistories() || state.history
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
return { responseText, responseData, isNewChat: forbidRefresh.current };
|
return { responseText, responseData, isNewChat: forbidLoadChat.current };
|
||||||
},
|
},
|
||||||
[appId, chatId, histories, pushHistory, router, setChatData, updateHistory]
|
[appId, chatId, forbidLoadChat, loadHistories, onChangeChatId, onUpdateHistory]
|
||||||
);
|
|
||||||
|
|
||||||
const { data: myApps = [], runAsync: loadMyApps } = useRequest2(
|
|
||||||
() => getMyApps({ getRecentlyChat: true }),
|
|
||||||
{
|
|
||||||
manual: false
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// get chat app info
|
// get chat app info
|
||||||
const loadChatInfo = useCallback(
|
const [chatData, setChatData] = useState<InitChatResponse>(defaultChatData);
|
||||||
async ({
|
const { loading } = useRequest2(
|
||||||
appId,
|
async () => {
|
||||||
chatId,
|
if (!appId || forbidLoadChat.current) return;
|
||||||
loading = false
|
|
||||||
}: {
|
|
||||||
appId: string;
|
|
||||||
chatId: string;
|
|
||||||
loading?: boolean;
|
|
||||||
}) => {
|
|
||||||
try {
|
|
||||||
loading && setIsLoading(true);
|
|
||||||
const res = await getInitChatInfo({ appId, chatId });
|
const res = await getInitChatInfo({ appId, chatId });
|
||||||
const history = res.history.map((item) => ({
|
const history = res.history.map((item) => ({
|
||||||
...item,
|
...item,
|
||||||
dataId: item.dataId || nanoid(),
|
dataId: item.dataId || getNanoid(),
|
||||||
status: ChatStatusEnum.finish
|
status: ChatStatusEnum.finish
|
||||||
}));
|
}));
|
||||||
|
|
||||||
setChatData({
|
const result: InitChatResponse = {
|
||||||
...res,
|
...res,
|
||||||
history
|
history
|
||||||
});
|
};
|
||||||
|
|
||||||
// have records.
|
// reset chat box
|
||||||
ChatBoxRef.current?.resetHistory(history);
|
ChatBoxRef.current?.resetHistory(history);
|
||||||
ChatBoxRef.current?.resetVariables(res.variables);
|
ChatBoxRef.current?.resetVariables(res.variables);
|
||||||
if (res.history.length > 0) {
|
if (history.length > 0) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
ChatBoxRef.current?.scrollToBottom('auto');
|
ChatBoxRef.current?.scrollToBottom('auto');
|
||||||
}, 500);
|
}, 500);
|
||||||
}
|
}
|
||||||
} catch (e: any) {
|
|
||||||
// reset all chat tore
|
setLastChatAppId(appId);
|
||||||
|
setChatData(result);
|
||||||
|
},
|
||||||
|
{
|
||||||
|
manual: false,
|
||||||
|
refreshDeps: [appId, chatId],
|
||||||
|
onError(e: any) {
|
||||||
setLastChatAppId('');
|
setLastChatAppId('');
|
||||||
setLastChatId('');
|
|
||||||
toast({
|
// reset all chat tore
|
||||||
title: getErrText(e, t('core.chat.Failed to initialize chat')),
|
|
||||||
status: 'error'
|
|
||||||
});
|
|
||||||
if (e?.code === 501) {
|
if (e?.code === 501) {
|
||||||
router.replace('/app/list');
|
router.replace('/app/list');
|
||||||
} else if (chatId) {
|
} else if (chatId) {
|
||||||
router.replace({
|
onChangeChatId('');
|
||||||
query: {
|
|
||||||
...router.query,
|
|
||||||
chatId: ''
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setIsLoading(false);
|
|
||||||
return null;
|
|
||||||
},
|
},
|
||||||
[setIsLoading, setChatData, setLastChatAppId, setLastChatId, toast, t, router]
|
onFinally() {
|
||||||
|
forbidLoadChat.current = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
);
|
);
|
||||||
// 初始化聊天框
|
|
||||||
useQuery(['init', { appId, chatId }], () => {
|
|
||||||
// pc: redirect to latest model chat
|
|
||||||
if (!appId && lastChatAppId) {
|
|
||||||
return router.replace({
|
|
||||||
query: {
|
|
||||||
appId: lastChatAppId,
|
|
||||||
chatId: lastChatId
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (!appId && myApps[0]) {
|
|
||||||
return router.replace({
|
|
||||||
query: {
|
|
||||||
appId: myApps[0]._id,
|
|
||||||
chatId: lastChatId
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (!appId) {
|
|
||||||
(async () => {
|
|
||||||
const apps = await loadMyApps();
|
|
||||||
if (apps.length === 0) {
|
|
||||||
toast({
|
|
||||||
status: 'error',
|
|
||||||
title: t('core.chat.You need to a chat app')
|
|
||||||
});
|
|
||||||
router.replace('/app/list');
|
|
||||||
} else {
|
|
||||||
router.replace({
|
|
||||||
query: {
|
|
||||||
appId: apps[0]._id,
|
|
||||||
chatId: lastChatId
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// store id
|
|
||||||
appId && setLastChatAppId(appId);
|
|
||||||
setLastChatId(chatId);
|
|
||||||
|
|
||||||
if (forbidRefresh.current) {
|
|
||||||
forbidRefresh.current = false;
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return loadChatInfo({
|
|
||||||
appId,
|
|
||||||
chatId,
|
|
||||||
loading: appId !== chatData.appId
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
useQuery(['loadHistories', appId], () => (appId ? loadHistories({ appId }) : null));
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex h={'100%'}>
|
<Flex h={'100%'}>
|
||||||
<NextHead title={chatData.app.name}></NextHead>
|
<NextHead title={chatData.app.name} icon={chatData.app.avatar}></NextHead>
|
||||||
{/* pc show myself apps */}
|
{/* pc show myself apps */}
|
||||||
{isPc && (
|
{isPc && (
|
||||||
<Box borderRight={theme.borders.base} w={'220px'} flexShrink={0}>
|
<Box borderRight={theme.borders.base} w={'220px'} flexShrink={0}>
|
||||||
@ -260,7 +166,7 @@ const Chat = ({ appId, chatId }: { appId: string; chatId: string }) => {
|
|||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<PageContainer flex={'1 0 0'} w={0} p={[0, '16px']} position={'relative'}>
|
<PageContainer isLoading={loading} flex={'1 0 0'} w={0} p={[0, '16px']} position={'relative'}>
|
||||||
<Flex h={'100%'} flexDirection={['column', 'row']} bg={'white'}>
|
<Flex h={'100%'} flexDirection={['column', 'row']} bg={'white'}>
|
||||||
{/* pc always show history. */}
|
{/* pc always show history. */}
|
||||||
{((children: React.ReactNode) => {
|
{((children: React.ReactNode) => {
|
||||||
@ -285,42 +191,17 @@ const Chat = ({ appId, chatId }: { appId: string; chatId: string }) => {
|
|||||||
appId={appId}
|
appId={appId}
|
||||||
appName={chatData.app.name}
|
appName={chatData.app.name}
|
||||||
appAvatar={chatData.app.avatar}
|
appAvatar={chatData.app.avatar}
|
||||||
activeChatId={chatId}
|
onDelHistory={(e) => onDelHistory({ ...e, appId })}
|
||||||
onClose={onCloseSlider}
|
|
||||||
history={histories.map((item, i) => ({
|
|
||||||
id: item.chatId,
|
|
||||||
title: item.title,
|
|
||||||
customTitle: item.customTitle,
|
|
||||||
top: item.top
|
|
||||||
}))}
|
|
||||||
onChangeChat={(chatId) => {
|
|
||||||
router.replace({
|
|
||||||
query: {
|
|
||||||
chatId: chatId || '',
|
|
||||||
appId
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (!isPc) {
|
|
||||||
onCloseSlider();
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
onDelHistory={(e) => delOneHistory({ ...e, appId })}
|
|
||||||
onClearHistory={() => {
|
onClearHistory={() => {
|
||||||
clearHistories({ appId });
|
onClearHistories({ appId });
|
||||||
router.replace({
|
|
||||||
query: {
|
|
||||||
appId
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}}
|
}}
|
||||||
onSetHistoryTop={(e) => {
|
onSetHistoryTop={(e) => {
|
||||||
updateHistory({ ...e, appId });
|
onUpdateHistory({ ...e, appId });
|
||||||
}}
|
}}
|
||||||
onSetCustomTitle={async (e) => {
|
onSetCustomTitle={async (e) => {
|
||||||
updateHistory({
|
onUpdateHistory({
|
||||||
appId,
|
appId,
|
||||||
chatId: e.chatId,
|
chatId: e.chatId,
|
||||||
title: e.title,
|
|
||||||
customTitle: e.title
|
customTitle: e.title
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
@ -340,7 +221,6 @@ const Chat = ({ appId, chatId }: { appId: string; chatId: string }) => {
|
|||||||
appName={chatData.app.name}
|
appName={chatData.app.name}
|
||||||
history={chatData.history}
|
history={chatData.history}
|
||||||
chatModels={chatData.app.chatModels}
|
chatModels={chatData.app.chatModels}
|
||||||
onOpenSlider={onOpenSlider}
|
|
||||||
onRoute2AppDetail={() => router.push(`/app/detail?appId=${appId}`)}
|
onRoute2AppDetail={() => router.push(`/app/detail?appId=${appId}`)}
|
||||||
showHistory
|
showHistory
|
||||||
/>
|
/>
|
||||||
@ -356,19 +236,81 @@ const Chat = ({ appId, chatId }: { appId: string; chatId: string }) => {
|
|||||||
showFileSelector={checkChatSupportSelectFileByChatModels(chatData.app.chatModels)}
|
showFileSelector={checkChatSupportSelectFileByChatModels(chatData.app.chatModels)}
|
||||||
feedbackType={'user'}
|
feedbackType={'user'}
|
||||||
onStartChat={startChat}
|
onStartChat={startChat}
|
||||||
onDelMessage={(e) => delOneHistoryItem({ ...e, appId, chatId })}
|
onDelMessage={({ contentId }) => delChatRecordById({ contentId, appId, chatId })}
|
||||||
appId={appId}
|
appId={appId}
|
||||||
chatId={chatId}
|
chatId={chatId}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Loading fixed={false} />
|
|
||||||
</PageContainer>
|
</PageContainer>
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const Render = (props: Props) => {
|
||||||
|
const { appId } = props;
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const { toast } = useToast();
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const { lastChatAppId, lastChatId } = useChatStore();
|
||||||
|
|
||||||
|
const { data: myApps = [], runAsync: loadMyApps } = useRequest2(
|
||||||
|
() => getMyApps({ getRecentlyChat: true }),
|
||||||
|
{
|
||||||
|
manual: false
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const { data: histories = [], runAsync: loadHistories } = useRequest2(
|
||||||
|
() => (appId ? getChatHistories({ appId }) : Promise.resolve([])),
|
||||||
|
{
|
||||||
|
manual: false,
|
||||||
|
refreshDeps: [appId]
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// 初始化聊天框
|
||||||
|
useMount(async () => {
|
||||||
|
// pc: redirect to latest model chat
|
||||||
|
if (!appId) {
|
||||||
|
if (lastChatAppId) {
|
||||||
|
return router.replace({
|
||||||
|
query: {
|
||||||
|
...router.query,
|
||||||
|
appId: lastChatAppId,
|
||||||
|
chatId: lastChatId
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const apps = await loadMyApps();
|
||||||
|
if (apps.length === 0) {
|
||||||
|
toast({
|
||||||
|
status: 'error',
|
||||||
|
title: t('core.chat.You need to a chat app')
|
||||||
|
});
|
||||||
|
router.replace('/app/list');
|
||||||
|
} else {
|
||||||
|
router.replace({
|
||||||
|
query: {
|
||||||
|
...router.query,
|
||||||
|
appId: apps[0]._id,
|
||||||
|
chatId: ''
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ChatContextProvider histories={histories} loadHistories={loadHistories}>
|
||||||
|
<Chat {...props} myApps={myApps} />
|
||||||
|
</ChatContextProvider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export async function getServerSideProps(context: any) {
|
export async function getServerSideProps(context: any) {
|
||||||
return {
|
return {
|
||||||
props: {
|
props: {
|
||||||
@ -379,4 +321,4 @@ export async function getServerSideProps(context: any) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Chat;
|
export default Render;
|
||||||
|
|||||||
@ -1,15 +1,13 @@
|
|||||||
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
import React, { useCallback, useRef, useState } from 'react';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import { Box, Flex, useDisclosure, Drawer, DrawerOverlay, DrawerContent } from '@chakra-ui/react';
|
import { Box, Flex, Drawer, DrawerOverlay, DrawerContent } from '@chakra-ui/react';
|
||||||
import { useToast } from '@fastgpt/web/hooks/useToast';
|
import { useToast } from '@fastgpt/web/hooks/useToast';
|
||||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||||
import { useQuery } from '@tanstack/react-query';
|
|
||||||
import { streamFetch } from '@/web/common/api/fetch';
|
import { streamFetch } from '@/web/common/api/fetch';
|
||||||
import { useShareChatStore } from '@/web/core/chat/storeShareChat';
|
import { useShareChatStore } from '@/web/core/chat/storeShareChat';
|
||||||
import SideBar from '@/components/SideBar';
|
import SideBar from '@/components/SideBar';
|
||||||
import { GPTMessages2Chats } from '@fastgpt/global/core/chat/adapt';
|
import { GPTMessages2Chats } from '@fastgpt/global/core/chat/adapt';
|
||||||
import { getErrText } from '@fastgpt/global/common/error/utils';
|
import { getErrText } from '@fastgpt/global/common/error/utils';
|
||||||
import type { ChatHistoryItemType, ChatSiteItemType } from '@fastgpt/global/core/chat/type.d';
|
|
||||||
import { customAlphabet } from 'nanoid';
|
import { customAlphabet } from 'nanoid';
|
||||||
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 12);
|
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 12);
|
||||||
|
|
||||||
@ -21,27 +19,30 @@ import ChatHistorySlider from './components/ChatHistorySlider';
|
|||||||
import { serviceSideProps } from '@/web/common/utils/i18n';
|
import { serviceSideProps } from '@/web/common/utils/i18n';
|
||||||
import { checkChatSupportSelectFileByChatModels } from '@/web/core/chat/utils';
|
import { checkChatSupportSelectFileByChatModels } from '@/web/core/chat/utils';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
import { getInitOutLinkChatInfo } from '@/web/core/chat/api';
|
import { delChatRecordById, getChatHistories, getInitOutLinkChatInfo } from '@/web/core/chat/api';
|
||||||
import { getChatTitleFromChatMessage } from '@fastgpt/global/core/chat/utils';
|
import { getChatTitleFromChatMessage } from '@fastgpt/global/core/chat/utils';
|
||||||
import { useChatStore } from '@/web/core/chat/storeChat';
|
|
||||||
import { ChatStatusEnum } from '@fastgpt/global/core/chat/constants';
|
import { ChatStatusEnum } from '@fastgpt/global/core/chat/constants';
|
||||||
import MyBox from '@fastgpt/web/components/common/MyBox';
|
|
||||||
import { MongoOutLink } from '@fastgpt/service/support/outLink/schema';
|
import { MongoOutLink } from '@fastgpt/service/support/outLink/schema';
|
||||||
import { OutLinkWithAppType } from '@fastgpt/global/support/outLink/type';
|
import { OutLinkWithAppType } from '@fastgpt/global/support/outLink/type';
|
||||||
import { addLog } from '@fastgpt/service/common/system/log';
|
import { addLog } from '@fastgpt/service/common/system/log';
|
||||||
import { connectToDatabase } from '@/service/mongo';
|
import { connectToDatabase } from '@/service/mongo';
|
||||||
import NextHead from '@/components/common/NextHead';
|
import NextHead from '@/components/common/NextHead';
|
||||||
import Head from 'next/head';
|
import { useContextSelector } from 'use-context-selector';
|
||||||
|
import ChatContextProvider, { ChatContext } from '@/web/core/chat/context/chatContext';
|
||||||
|
import { InitChatResponse } from '@/global/core/chat/api';
|
||||||
|
import { defaultChatData } from '@/global/core/chat/constants';
|
||||||
|
import { useMount } from 'ahooks';
|
||||||
|
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
||||||
|
|
||||||
const OutLink = ({
|
type Props = {
|
||||||
appName,
|
|
||||||
appIntro,
|
|
||||||
appAvatar
|
|
||||||
}: {
|
|
||||||
appName: string;
|
appName: string;
|
||||||
appIntro: string;
|
appIntro: string;
|
||||||
appAvatar: string;
|
appAvatar: string;
|
||||||
}) => {
|
shareId: string;
|
||||||
|
authToken: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const OutLink = ({ appName, appIntro, appAvatar }: Props) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const {
|
const {
|
||||||
@ -58,28 +59,28 @@ const OutLink = ({
|
|||||||
[key: string]: string;
|
[key: string]: string;
|
||||||
};
|
};
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
const { isOpen: isOpenSlider, onClose: onCloseSlider, onOpen: onOpenSlider } = useDisclosure();
|
|
||||||
const { isPc } = useSystemStore();
|
const { isPc } = useSystemStore();
|
||||||
const ChatBoxRef = useRef<ComponentRef>(null);
|
const ChatBoxRef = useRef<ComponentRef>(null);
|
||||||
const forbidRefresh = useRef(false);
|
|
||||||
const initSign = useRef(false);
|
const initSign = useRef(false);
|
||||||
const [isEmbed, setIdEmbed] = useState(true);
|
const [isEmbed, setIdEmbed] = useState(true);
|
||||||
|
|
||||||
const { localUId } = useShareChatStore();
|
const [chatData, setChatData] = useState<InitChatResponse>(defaultChatData);
|
||||||
const {
|
|
||||||
histories,
|
|
||||||
loadHistories,
|
|
||||||
pushHistory,
|
|
||||||
updateHistory,
|
|
||||||
delOneHistory,
|
|
||||||
chatData,
|
|
||||||
setChatData,
|
|
||||||
delOneHistoryItem,
|
|
||||||
clearHistories
|
|
||||||
} = useChatStore();
|
|
||||||
const appId = chatData.appId;
|
const appId = chatData.appId;
|
||||||
|
|
||||||
|
const { localUId } = useShareChatStore();
|
||||||
const outLinkUid: string = authToken || localUId;
|
const outLinkUid: string = authToken || localUId;
|
||||||
|
|
||||||
|
const {
|
||||||
|
loadHistories,
|
||||||
|
onUpdateHistory,
|
||||||
|
onClearHistories,
|
||||||
|
onDelHistory,
|
||||||
|
isOpenSlider,
|
||||||
|
onCloseSlider,
|
||||||
|
forbidLoadChat,
|
||||||
|
onChangeChatId
|
||||||
|
} = useContextSelector(ChatContext, (v) => v);
|
||||||
|
|
||||||
const startChat = useCallback(
|
const startChat = useCallback(
|
||||||
async ({ messages, controller, generatingMessage, variables }: StartChatFnProps) => {
|
async ({ messages, controller, generatingMessage, variables }: StartChatFnProps) => {
|
||||||
const prompts = messages.slice(-2);
|
const prompts = messages.slice(-2);
|
||||||
@ -115,30 +116,13 @@ const OutLink = ({
|
|||||||
|
|
||||||
// new chat
|
// new chat
|
||||||
if (completionChatId !== chatId) {
|
if (completionChatId !== chatId) {
|
||||||
const newHistory: ChatHistoryItemType = {
|
onChangeChatId(completionChatId, true);
|
||||||
chatId: completionChatId,
|
loadHistories();
|
||||||
updateTime: new Date(),
|
|
||||||
title: newTitle,
|
|
||||||
appId,
|
|
||||||
top: false
|
|
||||||
};
|
|
||||||
pushHistory(newHistory);
|
|
||||||
if (controller.signal.reason !== 'leave') {
|
|
||||||
forbidRefresh.current = true;
|
|
||||||
router.replace({
|
|
||||||
query: {
|
|
||||||
...router.query,
|
|
||||||
chatId: completionChatId
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// update chat
|
// update chat
|
||||||
const currentChat = histories.find((item) => item.chatId === chatId);
|
onUpdateHistory({
|
||||||
currentChat &&
|
appId,
|
||||||
updateHistory({
|
chatId: completionChatId,
|
||||||
...currentChat,
|
|
||||||
updateTime: new Date(),
|
|
||||||
title: newTitle,
|
title: newTitle,
|
||||||
shareId,
|
shareId,
|
||||||
outLinkUid
|
outLinkUid
|
||||||
@ -148,49 +132,40 @@ const OutLink = ({
|
|||||||
// update chat window
|
// update chat window
|
||||||
setChatData((state) => ({
|
setChatData((state) => ({
|
||||||
...state,
|
...state,
|
||||||
title: newTitle,
|
title: newTitle
|
||||||
history: ChatBoxRef.current?.getChatHistories() || state.history
|
|
||||||
}));
|
|
||||||
|
|
||||||
/* post message to report result */
|
|
||||||
const result: ChatSiteItemType[] = GPTMessages2Chats(prompts).map((item) => ({
|
|
||||||
...item,
|
|
||||||
dataId: item.dataId || nanoid(),
|
|
||||||
status: 'finish'
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
// hook message
|
||||||
window.top?.postMessage(
|
window.top?.postMessage(
|
||||||
{
|
{
|
||||||
type: 'shareChatFinish',
|
type: 'shareChatFinish',
|
||||||
data: {
|
data: {
|
||||||
question: result[0]?.value,
|
question: prompts[0]?.content,
|
||||||
answer: responseText
|
answer: responseText
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'*'
|
'*'
|
||||||
);
|
);
|
||||||
|
|
||||||
return { responseText, responseData, isNewChat: forbidRefresh.current };
|
return { responseText, responseData, isNewChat: forbidLoadChat.current };
|
||||||
},
|
},
|
||||||
[
|
[
|
||||||
chatId,
|
chatId,
|
||||||
customVariables,
|
customVariables,
|
||||||
shareId,
|
shareId,
|
||||||
outLinkUid,
|
outLinkUid,
|
||||||
setChatData,
|
forbidLoadChat,
|
||||||
appId,
|
onChangeChatId,
|
||||||
pushHistory,
|
loadHistories,
|
||||||
router,
|
onUpdateHistory,
|
||||||
histories,
|
appId
|
||||||
updateHistory
|
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
const loadChatInfo = useCallback(
|
const { loading } = useRequest2(
|
||||||
async (shareId: string, chatId: string) => {
|
async () => {
|
||||||
if (!shareId) return null;
|
if (!shareId || !outLinkUid || forbidLoadChat.current) return;
|
||||||
|
|
||||||
try {
|
|
||||||
const res = await getInitOutLinkChatInfo({
|
const res = await getInitOutLinkChatInfo({
|
||||||
chatId,
|
chatId,
|
||||||
shareId,
|
shareId,
|
||||||
@ -201,15 +176,26 @@ const OutLink = ({
|
|||||||
dataId: item.dataId || nanoid(),
|
dataId: item.dataId || nanoid(),
|
||||||
status: ChatStatusEnum.finish
|
status: ChatStatusEnum.finish
|
||||||
}));
|
}));
|
||||||
|
const result: InitChatResponse = {
|
||||||
setChatData({
|
|
||||||
...res,
|
...res,
|
||||||
history
|
history
|
||||||
});
|
};
|
||||||
|
|
||||||
|
// reset chat box
|
||||||
ChatBoxRef.current?.resetHistory(history);
|
ChatBoxRef.current?.resetHistory(history);
|
||||||
ChatBoxRef.current?.resetVariables(res.variables);
|
ChatBoxRef.current?.resetVariables(res.variables);
|
||||||
|
if (history.length > 0) {
|
||||||
|
setTimeout(() => {
|
||||||
|
ChatBoxRef.current?.scrollToBottom('auto');
|
||||||
|
}, 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
setChatData(result);
|
||||||
|
},
|
||||||
|
{
|
||||||
|
manual: false,
|
||||||
|
refreshDeps: [shareId, outLinkUid, chatId],
|
||||||
|
onSuccess() {
|
||||||
// send init message
|
// send init message
|
||||||
if (!initSign.current) {
|
if (!initSign.current) {
|
||||||
initSign.current = true;
|
initSign.current = true;
|
||||||
@ -217,76 +203,41 @@ const OutLink = ({
|
|||||||
window.top?.postMessage({ type: 'shareChatReady' }, '*');
|
window.top?.postMessage({ type: 'shareChatReady' }, '*');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
if (chatId && res.history.length > 0) {
|
onError(e: any) {
|
||||||
setTimeout(() => {
|
|
||||||
ChatBoxRef.current?.scrollToBottom('auto');
|
|
||||||
}, 500);
|
|
||||||
}
|
|
||||||
} catch (e: any) {
|
|
||||||
console.log(e);
|
console.log(e);
|
||||||
toast({
|
toast({
|
||||||
status: 'error',
|
status: 'error',
|
||||||
title: getErrText(e, t('core.shareChat.Init Error'))
|
title: getErrText(e, t('core.shareChat.Init Error'))
|
||||||
});
|
});
|
||||||
if (chatId) {
|
if (chatId) {
|
||||||
router.replace({
|
onChangeChatId('');
|
||||||
query: {
|
|
||||||
...router.query,
|
|
||||||
chatId: ''
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
},
|
},
|
||||||
[outLinkUid, router, setChatData, t, toast]
|
onFinally() {
|
||||||
|
forbidLoadChat.current = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const { isFetching } = useQuery(['init', shareId, chatId], () => {
|
|
||||||
if (forbidRefresh.current) {
|
|
||||||
forbidRefresh.current = false;
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return loadChatInfo(shareId, chatId);
|
|
||||||
});
|
|
||||||
|
|
||||||
// load histories
|
|
||||||
useQuery(['loadHistories', outLinkUid, shareId], () => {
|
|
||||||
if (shareId && outLinkUid) {
|
|
||||||
return loadHistories({
|
|
||||||
shareId,
|
|
||||||
outLinkUid
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
|
|
||||||
// window init
|
// window init
|
||||||
useEffect(() => {
|
useMount(() => {
|
||||||
setIdEmbed(window !== top);
|
setIdEmbed(window !== top);
|
||||||
}, []);
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<NextHead title={appName} desc={appIntro} icon={appAvatar} />
|
<NextHead title={appName} desc={appIntro} icon={appAvatar} />
|
||||||
|
|
||||||
<PageContainer
|
<PageContainer
|
||||||
|
isLoading={loading}
|
||||||
{...(isEmbed
|
{...(isEmbed
|
||||||
? { p: '0 !important', insertProps: { borderRadius: '0', boxShadow: 'none' } }
|
? { p: '0 !important', insertProps: { borderRadius: '0', boxShadow: 'none' } }
|
||||||
: { p: [0, 5] })}
|
: { p: [0, 5] })}
|
||||||
>
|
>
|
||||||
<MyBox
|
<Flex h={'100%'} flexDirection={['column', 'row']} bg={'white'}>
|
||||||
isLoading={isFetching}
|
{showHistory === '1' &&
|
||||||
h={'100%'}
|
((children: React.ReactNode) => {
|
||||||
display={'flex'}
|
|
||||||
flexDirection={['column', 'row']}
|
|
||||||
bg={'white'}
|
|
||||||
>
|
|
||||||
{showHistory === '1'
|
|
||||||
? ((children: React.ReactNode) => {
|
|
||||||
return isPc ? (
|
return isPc ? (
|
||||||
<SideBar>{children}</SideBar>
|
<SideBar>{children}</SideBar>
|
||||||
) : (
|
) : (
|
||||||
@ -308,58 +259,31 @@ const OutLink = ({
|
|||||||
appName={chatData.app.name}
|
appName={chatData.app.name}
|
||||||
appAvatar={chatData.app.avatar}
|
appAvatar={chatData.app.avatar}
|
||||||
confirmClearText={t('core.chat.Confirm to clear share chat history')}
|
confirmClearText={t('core.chat.Confirm to clear share chat history')}
|
||||||
activeChatId={chatId}
|
|
||||||
history={histories.map((item) => ({
|
|
||||||
id: item.chatId,
|
|
||||||
title: item.title,
|
|
||||||
customTitle: item.customTitle,
|
|
||||||
top: item.top
|
|
||||||
}))}
|
|
||||||
onClose={onCloseSlider}
|
|
||||||
onChangeChat={(chatId) => {
|
|
||||||
router.replace({
|
|
||||||
query: {
|
|
||||||
...router.query,
|
|
||||||
chatId: chatId || ''
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (!isPc) {
|
|
||||||
onCloseSlider();
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
onDelHistory={({ chatId }) =>
|
onDelHistory={({ chatId }) =>
|
||||||
delOneHistory({ appId: chatData.appId, chatId, shareId, outLinkUid })
|
onDelHistory({ appId: chatData.appId, chatId, shareId, outLinkUid })
|
||||||
}
|
}
|
||||||
onClearHistory={() => {
|
onClearHistory={() => {
|
||||||
clearHistories({ shareId, outLinkUid });
|
onClearHistories({ shareId, outLinkUid });
|
||||||
router.replace({
|
|
||||||
query: {
|
|
||||||
...router.query,
|
|
||||||
chatId: ''
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}}
|
}}
|
||||||
onSetHistoryTop={(e) => {
|
onSetHistoryTop={(e) => {
|
||||||
updateHistory({
|
onUpdateHistory({
|
||||||
...e,
|
...e,
|
||||||
appId: chatData.appId,
|
appId: chatData.appId,
|
||||||
shareId,
|
shareId,
|
||||||
outLinkUid
|
outLinkUid
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
onSetCustomTitle={async (e) => {
|
onSetCustomTitle={(e) => {
|
||||||
updateHistory({
|
onUpdateHistory({
|
||||||
appId: chatData.appId,
|
appId: chatData.appId,
|
||||||
chatId: e.chatId,
|
chatId: e.chatId,
|
||||||
title: e.title,
|
|
||||||
customTitle: e.title,
|
customTitle: e.title,
|
||||||
shareId,
|
shareId,
|
||||||
outLinkUid
|
outLinkUid
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)
|
)}
|
||||||
: null}
|
|
||||||
|
|
||||||
{/* chat container */}
|
{/* chat container */}
|
||||||
<Flex
|
<Flex
|
||||||
@ -375,12 +299,10 @@ const OutLink = ({
|
|||||||
appName={chatData.app.name}
|
appName={chatData.app.name}
|
||||||
history={chatData.history}
|
history={chatData.history}
|
||||||
showHistory={showHistory === '1'}
|
showHistory={showHistory === '1'}
|
||||||
onOpenSlider={onOpenSlider}
|
|
||||||
/>
|
/>
|
||||||
{/* chat box */}
|
{/* chat box */}
|
||||||
<Box flex={1}>
|
<Box flex={1}>
|
||||||
<ChatBox
|
<ChatBox
|
||||||
active={!!chatData.app.name}
|
|
||||||
ref={ChatBoxRef}
|
ref={ChatBoxRef}
|
||||||
appAvatar={chatData.app.avatar}
|
appAvatar={chatData.app.avatar}
|
||||||
userAvatar={chatData.userAvatar}
|
userAvatar={chatData.userAvatar}
|
||||||
@ -389,8 +311,14 @@ const OutLink = ({
|
|||||||
feedbackType={'user'}
|
feedbackType={'user'}
|
||||||
onUpdateVariable={(e) => {}}
|
onUpdateVariable={(e) => {}}
|
||||||
onStartChat={startChat}
|
onStartChat={startChat}
|
||||||
onDelMessage={(e) =>
|
onDelMessage={({ contentId }) =>
|
||||||
delOneHistoryItem({ ...e, appId: chatData.appId, chatId, shareId, outLinkUid })
|
delChatRecordById({
|
||||||
|
contentId,
|
||||||
|
appId: chatData.appId,
|
||||||
|
chatId,
|
||||||
|
shareId,
|
||||||
|
outLinkUid
|
||||||
|
})
|
||||||
}
|
}
|
||||||
appId={chatData.appId}
|
appId={chatData.appId}
|
||||||
chatId={chatId}
|
chatId={chatId}
|
||||||
@ -399,16 +327,37 @@ const OutLink = ({
|
|||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
</Flex>
|
</Flex>
|
||||||
</MyBox>
|
</Flex>
|
||||||
</PageContainer>
|
</PageContainer>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default OutLink;
|
const Render = (props: Props) => {
|
||||||
|
const { shareId, authToken } = props;
|
||||||
|
const { localUId } = useShareChatStore();
|
||||||
|
const outLinkUid: string = authToken || localUId;
|
||||||
|
|
||||||
|
const { data: histories = [], runAsync: loadHistories } = useRequest2(
|
||||||
|
() => (shareId && outLinkUid ? getChatHistories({ shareId, outLinkUid }) : Promise.resolve([])),
|
||||||
|
{
|
||||||
|
manual: false,
|
||||||
|
refreshDeps: [shareId, outLinkUid]
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ChatContextProvider histories={histories} loadHistories={loadHistories}>
|
||||||
|
<OutLink {...props} />;
|
||||||
|
</ChatContextProvider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Render;
|
||||||
|
|
||||||
export async function getServerSideProps(context: any) {
|
export async function getServerSideProps(context: any) {
|
||||||
const shareId = context?.query?.shareId || '';
|
const shareId = context?.query?.shareId || '';
|
||||||
|
const authToken = context?.query?.authToken || '';
|
||||||
|
|
||||||
const app = await (async () => {
|
const app = await (async () => {
|
||||||
try {
|
try {
|
||||||
@ -433,6 +382,8 @@ export async function getServerSideProps(context: any) {
|
|||||||
appName: app?.appId?.name ?? 'name',
|
appName: app?.appId?.name ?? 'name',
|
||||||
appAvatar: app?.appId?.avatar ?? '',
|
appAvatar: app?.appId?.avatar ?? '',
|
||||||
appIntro: app?.appId?.intro ?? 'intro',
|
appIntro: app?.appId?.intro ?? 'intro',
|
||||||
|
shareId: shareId ?? '',
|
||||||
|
authToken: authToken ?? '',
|
||||||
...(await serviceSideProps(context, ['file']))
|
...(await serviceSideProps(context, ['file']))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,18 +1,9 @@
|
|||||||
import React, { useCallback, useEffect, useRef } from 'react';
|
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||||
import NextHead from '@/components/common/NextHead';
|
import NextHead from '@/components/common/NextHead';
|
||||||
import { getTeamChatInfo } from '@/web/core/chat/api';
|
import { delChatRecordById, getChatHistories, getTeamChatInfo } from '@/web/core/chat/api';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import {
|
import { Box, Flex, Drawer, DrawerOverlay, DrawerContent, useTheme } from '@chakra-ui/react';
|
||||||
Box,
|
|
||||||
Flex,
|
|
||||||
useDisclosure,
|
|
||||||
Drawer,
|
|
||||||
DrawerOverlay,
|
|
||||||
DrawerContent,
|
|
||||||
useTheme
|
|
||||||
} from '@chakra-ui/react';
|
|
||||||
import { useToast } from '@fastgpt/web/hooks/useToast';
|
import { useToast } from '@fastgpt/web/hooks/useToast';
|
||||||
import { useQuery } from '@tanstack/react-query';
|
|
||||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||||
import SideBar from '@/components/SideBar';
|
import SideBar from '@/components/SideBar';
|
||||||
import PageContainer from '@/components/PageContainer';
|
import PageContainer from '@/components/PageContainer';
|
||||||
@ -22,21 +13,26 @@ import ChatHeader from './components/ChatHeader';
|
|||||||
import { serviceSideProps } from '@/web/common/utils/i18n';
|
import { serviceSideProps } from '@/web/common/utils/i18n';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
import { checkChatSupportSelectFileByChatModels } from '@/web/core/chat/utils';
|
import { checkChatSupportSelectFileByChatModels } from '@/web/core/chat/utils';
|
||||||
import { useChatStore } from '@/web/core/chat/storeChat';
|
|
||||||
import { customAlphabet } from 'nanoid';
|
import { customAlphabet } from 'nanoid';
|
||||||
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 12);
|
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 12);
|
||||||
import ChatBox from '@/components/ChatBox';
|
import ChatBox from '@/components/ChatBox';
|
||||||
import type { ComponentRef, StartChatFnProps } from '@/components/ChatBox/type.d';
|
import type { ComponentRef, StartChatFnProps } from '@/components/ChatBox/type.d';
|
||||||
import { streamFetch } from '@/web/common/api/fetch';
|
import { streamFetch } from '@/web/common/api/fetch';
|
||||||
import type { ChatHistoryItemType } from '@fastgpt/global/core/chat/type.d';
|
|
||||||
import { getChatTitleFromChatMessage } from '@fastgpt/global/core/chat/utils';
|
import { getChatTitleFromChatMessage } from '@fastgpt/global/core/chat/utils';
|
||||||
import { ChatStatusEnum } from '@fastgpt/global/core/chat/constants';
|
import { ChatStatusEnum } from '@fastgpt/global/core/chat/constants';
|
||||||
import { getErrText } from '@fastgpt/global/common/error/utils';
|
import { getErrText } from '@fastgpt/global/common/error/utils';
|
||||||
import MyBox from '@fastgpt/web/components/common/MyBox';
|
|
||||||
import SliderApps from './components/SliderApps';
|
import SliderApps from './components/SliderApps';
|
||||||
import { GPTMessages2Chats } from '@fastgpt/global/core/chat/adapt';
|
import { GPTMessages2Chats } from '@fastgpt/global/core/chat/adapt';
|
||||||
|
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 } from '@/global/core/chat/constants';
|
||||||
|
|
||||||
const OutLink = () => {
|
type Props = { appId: string; chatId: string; teamId: string; teamToken: string };
|
||||||
|
|
||||||
|
const Chat = ({ myApps }: { myApps: AppListItemType[] }) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const {
|
const {
|
||||||
@ -45,11 +41,7 @@ const OutLink = () => {
|
|||||||
chatId = '',
|
chatId = '',
|
||||||
teamToken,
|
teamToken,
|
||||||
...customVariables
|
...customVariables
|
||||||
} = router.query as {
|
} = router.query as Props & {
|
||||||
teamId: string;
|
|
||||||
appId: string;
|
|
||||||
chatId: string;
|
|
||||||
teamToken: string;
|
|
||||||
[key: string]: string;
|
[key: string]: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -57,22 +49,20 @@ const OutLink = () => {
|
|||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const { isPc } = useSystemStore();
|
const { isPc } = useSystemStore();
|
||||||
const ChatBoxRef = useRef<ComponentRef>(null);
|
const ChatBoxRef = useRef<ComponentRef>(null);
|
||||||
const forbidRefresh = useRef(false);
|
|
||||||
|
|
||||||
const { isOpen: isOpenSlider, onClose: onCloseSlider, onOpen: onOpenSlider } = useDisclosure();
|
const [chatData, setChatData] = useState<InitChatResponse>(defaultChatData);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
chatData,
|
|
||||||
setChatData,
|
|
||||||
histories,
|
|
||||||
loadHistories,
|
loadHistories,
|
||||||
lastChatAppId,
|
onUpdateHistory,
|
||||||
lastChatId,
|
onClearHistories,
|
||||||
pushHistory,
|
onDelHistory,
|
||||||
updateHistory,
|
isOpenSlider,
|
||||||
delOneHistory,
|
onCloseSlider,
|
||||||
delOneHistoryItem,
|
forbidLoadChat,
|
||||||
clearHistories
|
onChangeChatId,
|
||||||
} = useChatStore();
|
onChangeAppId
|
||||||
|
} = useContextSelector(ChatContext, (v) => v);
|
||||||
|
|
||||||
const startChat = useCallback(
|
const startChat = useCallback(
|
||||||
async ({ messages, controller, generatingMessage, variables }: StartChatFnProps) => {
|
async ({ messages, controller, generatingMessage, variables }: StartChatFnProps) => {
|
||||||
@ -99,30 +89,12 @@ const OutLink = () => {
|
|||||||
|
|
||||||
// new chat
|
// new chat
|
||||||
if (completionChatId !== chatId) {
|
if (completionChatId !== chatId) {
|
||||||
const newHistory: ChatHistoryItemType = {
|
onChangeChatId(completionChatId, true);
|
||||||
chatId: completionChatId,
|
loadHistories();
|
||||||
updateTime: new Date(),
|
|
||||||
title: newTitle,
|
|
||||||
appId,
|
|
||||||
top: false
|
|
||||||
};
|
|
||||||
pushHistory(newHistory);
|
|
||||||
if (controller.signal.reason !== 'leave') {
|
|
||||||
forbidRefresh.current = true;
|
|
||||||
router.replace({
|
|
||||||
query: {
|
|
||||||
...router.query,
|
|
||||||
chatId: completionChatId
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// update chat
|
onUpdateHistory({
|
||||||
const currentChat = histories.find((item) => item.chatId === chatId);
|
appId: chatData.appId,
|
||||||
currentChat &&
|
chatId: completionChatId,
|
||||||
updateHistory({
|
|
||||||
...currentChat,
|
|
||||||
updateTime: new Date(),
|
|
||||||
title: newTitle,
|
title: newTitle,
|
||||||
teamId,
|
teamId,
|
||||||
teamToken
|
teamToken
|
||||||
@ -135,7 +107,7 @@ const OutLink = () => {
|
|||||||
history: ChatBoxRef.current?.getChatHistories() || state.history
|
history: ChatBoxRef.current?.getChatHistories() || state.history
|
||||||
}));
|
}));
|
||||||
|
|
||||||
return { responseText, responseData, isNewChat: forbidRefresh.current };
|
return { responseText, responseData, isNewChat: forbidLoadChat.current };
|
||||||
},
|
},
|
||||||
[
|
[
|
||||||
chatId,
|
chatId,
|
||||||
@ -143,132 +115,63 @@ const OutLink = () => {
|
|||||||
appId,
|
appId,
|
||||||
teamId,
|
teamId,
|
||||||
teamToken,
|
teamToken,
|
||||||
setChatData,
|
forbidLoadChat,
|
||||||
pushHistory,
|
onChangeChatId,
|
||||||
router,
|
loadHistories,
|
||||||
histories,
|
onUpdateHistory,
|
||||||
updateHistory
|
chatData.appId
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
/* replace router query to last chat */
|
|
||||||
useEffect(() => {
|
|
||||||
if ((!chatId || !appId) && (lastChatId || lastChatAppId)) {
|
|
||||||
router.replace({
|
|
||||||
query: {
|
|
||||||
...router.query,
|
|
||||||
chatId: chatId || lastChatId,
|
|
||||||
appId: appId || lastChatAppId
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
// get chat app list
|
|
||||||
const loadApps = useCallback(async () => {
|
|
||||||
try {
|
|
||||||
const apps = await getMyTokensApps({ teamId, teamToken });
|
|
||||||
|
|
||||||
if (apps.length <= 0) {
|
|
||||||
toast({
|
|
||||||
status: 'error',
|
|
||||||
title: t('core.chat.You need to a chat app')
|
|
||||||
});
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
// if app id not exist, redirect to first app
|
|
||||||
if (!appId || !apps.find((item) => item._id === appId)) {
|
|
||||||
router.replace({
|
|
||||||
query: {
|
|
||||||
...router.query,
|
|
||||||
appId: apps[0]?._id
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return apps;
|
|
||||||
} catch (error: any) {
|
|
||||||
toast({
|
|
||||||
status: 'warning',
|
|
||||||
title: getErrText(error)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return [];
|
|
||||||
}, [appId, teamToken, router, teamId, t, toast]);
|
|
||||||
const { data: myApps = [], isLoading: isLoadingApps } = useQuery(['initApps', teamId], () => {
|
|
||||||
if (!teamId) {
|
|
||||||
toast({
|
|
||||||
status: 'error',
|
|
||||||
title: t('support.user.team.tag.Have not opened')
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
return loadApps();
|
|
||||||
});
|
|
||||||
|
|
||||||
// load histories
|
|
||||||
useQuery(['loadHistories', appId], () => {
|
|
||||||
if (teamId && appId) {
|
|
||||||
return loadHistories({ teamId, appId, teamToken: teamToken });
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
});
|
|
||||||
|
|
||||||
// get chat app info
|
// get chat app info
|
||||||
const loadChatInfo = useCallback(async () => {
|
const { loading } = useRequest2(
|
||||||
try {
|
async () => {
|
||||||
const res = await getTeamChatInfo({ teamId, appId, chatId, teamToken: teamToken });
|
if (!appId || forbidLoadChat.current) return;
|
||||||
|
|
||||||
|
const res = await getTeamChatInfo({ teamId, appId, chatId, teamToken });
|
||||||
const history = res.history.map((item) => ({
|
const history = res.history.map((item) => ({
|
||||||
...item,
|
...item,
|
||||||
dataId: item.dataId || nanoid(),
|
dataId: item.dataId || nanoid(),
|
||||||
status: ChatStatusEnum.finish
|
status: ChatStatusEnum.finish
|
||||||
}));
|
}));
|
||||||
|
|
||||||
setChatData({
|
const result: InitChatResponse = {
|
||||||
...res,
|
...res,
|
||||||
history
|
history
|
||||||
});
|
};
|
||||||
|
|
||||||
// have records.
|
// have records.
|
||||||
ChatBoxRef.current?.resetHistory(history);
|
ChatBoxRef.current?.resetHistory(history);
|
||||||
ChatBoxRef.current?.resetVariables(res.variables);
|
ChatBoxRef.current?.resetVariables(res.variables);
|
||||||
|
|
||||||
if (res.history.length > 0) {
|
if (res.history.length > 0) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
ChatBoxRef.current?.scrollToBottom('auto');
|
ChatBoxRef.current?.scrollToBottom('auto');
|
||||||
}, 500);
|
}, 500);
|
||||||
}
|
}
|
||||||
} catch (e: any) {
|
|
||||||
|
setChatData(result);
|
||||||
|
},
|
||||||
|
{
|
||||||
|
manual: false,
|
||||||
|
refreshDeps: [teamId, teamToken, appId, chatId],
|
||||||
|
onError(e: any) {
|
||||||
toast({
|
toast({
|
||||||
title: t('core.chat.Failed to initialize chat'),
|
title: getErrText(e, t('core.chat.Failed to initialize chat')),
|
||||||
status: 'error'
|
status: 'error'
|
||||||
});
|
});
|
||||||
if (chatId) {
|
if (chatId) {
|
||||||
router.replace({
|
onChangeChatId('');
|
||||||
query: {
|
|
||||||
...router.query,
|
|
||||||
chatId: ''
|
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
|
onFinally() {
|
||||||
|
forbidLoadChat.current = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
);
|
||||||
}, [teamId, appId, chatId, teamToken, setChatData, toast, t, router]);
|
|
||||||
const { isFetching } = useQuery(['init', teamId, appId, chatId], () => {
|
|
||||||
if (forbidRefresh.current) {
|
|
||||||
forbidRefresh.current = false;
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (teamId && appId) {
|
|
||||||
return loadChatInfo();
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MyBox display={'flex'} h={'100%'} isLoading={isLoadingApps || isFetching}>
|
<Flex h={'100%'}>
|
||||||
<NextHead title={chatData.app.name}></NextHead>
|
<NextHead title={chatData.app.name} icon={chatData.app.avatar}></NextHead>
|
||||||
{/* pc show myself apps */}
|
{/* pc show myself apps */}
|
||||||
{isPc && (
|
{isPc && (
|
||||||
<Box borderRight={theme.borders.base} w={'220px'} flexShrink={0}>
|
<Box borderRight={theme.borders.base} w={'220px'} flexShrink={0}>
|
||||||
@ -276,7 +179,7 @@ const OutLink = () => {
|
|||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<PageContainer flex={'1 0 0'} w={0} p={[0, '16px']} position={'relative'}>
|
<PageContainer isLoading={loading} flex={'1 0 0'} w={0} p={[0, '16px']} position={'relative'}>
|
||||||
<Flex h={'100%'} flexDirection={['column', 'row']} bg={'white'}>
|
<Flex h={'100%'} flexDirection={['column', 'row']} bg={'white'}>
|
||||||
{((children: React.ReactNode) => {
|
{((children: React.ReactNode) => {
|
||||||
return isPc || !appId ? (
|
return isPc || !appId ? (
|
||||||
@ -299,44 +202,18 @@ const OutLink = () => {
|
|||||||
apps={myApps}
|
apps={myApps}
|
||||||
appName={chatData.app.name}
|
appName={chatData.app.name}
|
||||||
appAvatar={chatData.app.avatar}
|
appAvatar={chatData.app.avatar}
|
||||||
activeChatId={chatId}
|
|
||||||
confirmClearText={t('core.chat.Confirm to clear history')}
|
confirmClearText={t('core.chat.Confirm to clear history')}
|
||||||
onClose={onCloseSlider}
|
onDelHistory={(e) => onDelHistory({ ...e, appId, teamId, teamToken })}
|
||||||
history={histories.map((item, i) => ({
|
|
||||||
id: item.chatId,
|
|
||||||
title: item.title,
|
|
||||||
customTitle: item.customTitle,
|
|
||||||
top: item.top
|
|
||||||
}))}
|
|
||||||
onChangeChat={(chatId) => {
|
|
||||||
router.replace({
|
|
||||||
query: {
|
|
||||||
...router.query,
|
|
||||||
chatId: chatId || ''
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (!isPc) {
|
|
||||||
onCloseSlider();
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
onDelHistory={(e) => delOneHistory({ ...e, appId, teamId, teamToken })}
|
|
||||||
onClearHistory={() => {
|
onClearHistory={() => {
|
||||||
clearHistories({ appId, teamId, teamToken });
|
onClearHistories({ appId, teamId, teamToken });
|
||||||
router.replace({
|
|
||||||
query: {
|
|
||||||
...router.query,
|
|
||||||
chatId: ''
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}}
|
}}
|
||||||
onSetHistoryTop={(e) => {
|
onSetHistoryTop={(e) => {
|
||||||
updateHistory({ ...e, teamId, teamToken, appId });
|
onUpdateHistory({ ...e, teamId, teamToken, appId });
|
||||||
}}
|
}}
|
||||||
onSetCustomTitle={async (e) => {
|
onSetCustomTitle={async (e) => {
|
||||||
updateHistory({
|
onUpdateHistory({
|
||||||
appId,
|
appId,
|
||||||
chatId: e.chatId,
|
chatId: e.chatId,
|
||||||
title: e.title,
|
|
||||||
customTitle: e.title,
|
customTitle: e.title,
|
||||||
teamId,
|
teamId,
|
||||||
teamToken
|
teamToken
|
||||||
@ -357,13 +234,11 @@ const OutLink = () => {
|
|||||||
appAvatar={chatData.app.avatar}
|
appAvatar={chatData.app.avatar}
|
||||||
appName={chatData.app.name}
|
appName={chatData.app.name}
|
||||||
history={chatData.history}
|
history={chatData.history}
|
||||||
onOpenSlider={onOpenSlider}
|
|
||||||
showHistory
|
showHistory
|
||||||
/>
|
/>
|
||||||
{/* chat box */}
|
{/* chat box */}
|
||||||
<Box flex={1}>
|
<Box flex={1}>
|
||||||
<ChatBox
|
<ChatBox
|
||||||
active={!!chatData.app.name}
|
|
||||||
ref={ChatBoxRef}
|
ref={ChatBoxRef}
|
||||||
appAvatar={chatData.app.avatar}
|
appAvatar={chatData.app.avatar}
|
||||||
userAvatar={chatData.userAvatar}
|
userAvatar={chatData.userAvatar}
|
||||||
@ -372,8 +247,8 @@ const OutLink = () => {
|
|||||||
feedbackType={'user'}
|
feedbackType={'user'}
|
||||||
onUpdateVariable={(e) => {}}
|
onUpdateVariable={(e) => {}}
|
||||||
onStartChat={startChat}
|
onStartChat={startChat}
|
||||||
onDelMessage={(e) =>
|
onDelMessage={({ contentId }) =>
|
||||||
delOneHistoryItem({ ...e, appId: chatData.appId, chatId, teamId, teamToken })
|
delChatRecordById({ contentId, appId: chatData.appId, chatId, teamId, teamToken })
|
||||||
}
|
}
|
||||||
appId={chatData.appId}
|
appId={chatData.appId}
|
||||||
chatId={chatId}
|
chatId={chatId}
|
||||||
@ -384,16 +259,73 @@ const OutLink = () => {
|
|||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
</PageContainer>
|
</PageContainer>
|
||||||
</MyBox>
|
</Flex>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const Render = (props: Props) => {
|
||||||
|
const { teamId, appId, teamToken } = props;
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const { toast } = useToast();
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const { data: myApps = [], runAsync: loadMyApps } = useRequest2(
|
||||||
|
async () => {
|
||||||
|
if (teamId && teamToken) {
|
||||||
|
return getMyTokensApps({ teamId, teamToken });
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
},
|
||||||
|
{
|
||||||
|
manual: false
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const { data: histories = [], runAsync: loadHistories } = useRequest2(
|
||||||
|
async () => {
|
||||||
|
if (teamId && appId && teamToken) {
|
||||||
|
return getChatHistories({ teamId, appId, teamToken: teamToken });
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
},
|
||||||
|
{
|
||||||
|
manual: false,
|
||||||
|
refreshDeps: [appId, teamId, teamToken]
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// 初始化聊天框
|
||||||
|
useEffect(() => {
|
||||||
|
(async () => {
|
||||||
|
if (appId || myApps.length === 0) return;
|
||||||
|
|
||||||
|
router.replace({
|
||||||
|
query: {
|
||||||
|
...router.query,
|
||||||
|
appId: myApps[0]._id,
|
||||||
|
chatId: ''
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
}, [appId, loadMyApps, myApps, router, t, toast]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ChatContextProvider histories={histories} loadHistories={loadHistories}>
|
||||||
|
<Chat {...props} myApps={myApps} />
|
||||||
|
</ChatContextProvider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export async function getServerSideProps(context: any) {
|
export async function getServerSideProps(context: any) {
|
||||||
return {
|
return {
|
||||||
props: {
|
props: {
|
||||||
|
appId: context?.query?.appId || '',
|
||||||
|
chatId: context?.query?.chatId || '',
|
||||||
|
teamId: context?.query?.teamId || '',
|
||||||
|
teamToken: context?.query?.teamToken || '',
|
||||||
...(await serviceSideProps(context, ['file']))
|
...(await serviceSideProps(context, ['file']))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export default OutLink;
|
export default Render;
|
||||||
|
|||||||
@ -75,16 +75,16 @@ const InputDataModal = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const tabList = [
|
const tabList = [
|
||||||
{ label: t('dataset.data.edit.Content'), id: TabEnum.content, icon: 'common/overviewLight' },
|
{ label: t('dataset.data.edit.Content'), value: TabEnum.content, icon: 'common/overviewLight' },
|
||||||
{
|
{
|
||||||
label: t('dataset.data.edit.Index', { amount: indexes.length }),
|
label: t('dataset.data.edit.Index', { amount: indexes.length }),
|
||||||
id: TabEnum.index,
|
value: TabEnum.index,
|
||||||
icon: 'kbTest'
|
icon: 'kbTest'
|
||||||
},
|
},
|
||||||
...(dataId
|
...(dataId
|
||||||
? [{ label: t('dataset.data.edit.Delete'), id: TabEnum.delete, icon: 'delete' }]
|
? [{ label: t('dataset.data.edit.Delete'), value: TabEnum.delete, icon: 'delete' }]
|
||||||
: []),
|
: []),
|
||||||
{ label: t('dataset.data.edit.Course'), id: TabEnum.doc, icon: 'common/courseLight' }
|
{ label: t('dataset.data.edit.Course'), value: TabEnum.doc, icon: 'common/courseLight' }
|
||||||
];
|
];
|
||||||
|
|
||||||
const { ConfirmModal, openConfirm } = useConfirm({
|
const { ConfirmModal, openConfirm } = useConfirm({
|
||||||
@ -243,10 +243,10 @@ const InputDataModal = ({
|
|||||||
mb={6}
|
mb={6}
|
||||||
fontSize={'sm'}
|
fontSize={'sm'}
|
||||||
/>
|
/>
|
||||||
<SideTabs
|
<SideTabs<TabEnum>
|
||||||
list={tabList}
|
list={tabList}
|
||||||
activeId={currentTab}
|
value={currentTab}
|
||||||
onChange={async (e: any) => {
|
onChange={async (e) => {
|
||||||
if (e === TabEnum.delete) {
|
if (e === TabEnum.delete) {
|
||||||
return openConfirm(onDeleteData)();
|
return openConfirm(onDeleteData)();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,9 +8,9 @@ import DatasetTypeTag from '@/components/core/dataset/DatasetTypeTag';
|
|||||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||||
import SideTabs from '@/components/SideTabs';
|
import SideTabs from '@/components/SideTabs';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import Tabs from '@/components/Tabs';
|
|
||||||
import { useContextSelector } from 'use-context-selector';
|
import { useContextSelector } from 'use-context-selector';
|
||||||
import { DatasetPageContext } from '@/web/core/dataset/context/datasetPageContext';
|
import { DatasetPageContext } from '@/web/core/dataset/context/datasetPageContext';
|
||||||
|
import LightRowTabs from '@fastgpt/web/components/common/Tabs/LightRowTabs';
|
||||||
import { useI18n } from '@/web/context/I18n';
|
import { useI18n } from '@/web/context/I18n';
|
||||||
|
|
||||||
export enum TabEnum {
|
export enum TabEnum {
|
||||||
@ -34,12 +34,12 @@ const Slider = ({ currentTab }: { currentTab: TabEnum }) => {
|
|||||||
const tabList = [
|
const tabList = [
|
||||||
{
|
{
|
||||||
label: t('core.dataset.Collection'),
|
label: t('core.dataset.Collection'),
|
||||||
id: TabEnum.collectionCard,
|
value: TabEnum.collectionCard,
|
||||||
icon: 'common/overviewLight'
|
icon: 'common/overviewLight'
|
||||||
},
|
},
|
||||||
{ label: t('core.dataset.test.Search Test'), id: TabEnum.test, icon: 'kbTest' },
|
{ label: t('core.dataset.test.Search Test'), value: TabEnum.test, icon: 'kbTest' },
|
||||||
...(datasetDetail.permission.hasManagePer
|
...(datasetDetail.permission.hasManagePer
|
||||||
? [{ label: t('common.Config'), id: TabEnum.info, icon: 'common/settingLight' }]
|
? [{ label: t('common.Config'), value: TabEnum.info, icon: 'common/settingLight' }]
|
||||||
: [])
|
: [])
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -78,16 +78,14 @@ const Slider = ({ currentTab }: { currentTab: TabEnum }) => {
|
|||||||
</Flex>
|
</Flex>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
<SideTabs
|
<SideTabs<TabEnum>
|
||||||
px={4}
|
px={4}
|
||||||
flex={1}
|
flex={1}
|
||||||
mx={'auto'}
|
mx={'auto'}
|
||||||
w={'100%'}
|
w={'100%'}
|
||||||
list={tabList}
|
list={tabList}
|
||||||
activeId={currentTab}
|
value={currentTab}
|
||||||
onChange={(e: any) => {
|
onChange={setCurrentTab}
|
||||||
setCurrentTab(e);
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
<Box px={4}>
|
<Box px={4}>
|
||||||
{rebuildingCount > 0 && (
|
{rebuildingCount > 0 && (
|
||||||
@ -149,16 +147,13 @@ const Slider = ({ currentTab }: { currentTab: TabEnum }) => {
|
|||||||
</Flex>
|
</Flex>
|
||||||
) : (
|
) : (
|
||||||
<Box mb={3}>
|
<Box mb={3}>
|
||||||
<Tabs
|
<LightRowTabs<TabEnum>
|
||||||
m={'auto'}
|
m={'auto'}
|
||||||
w={'260px'}
|
w={'260px'}
|
||||||
size={isPc ? 'md' : 'sm'}
|
size={isPc ? 'md' : 'sm'}
|
||||||
list={tabList.map((item) => ({
|
list={tabList}
|
||||||
id: item.id,
|
value={currentTab}
|
||||||
label: item.label
|
onChange={setCurrentTab}
|
||||||
}))}
|
|
||||||
activeId={currentTab}
|
|
||||||
onChange={(e: any) => setCurrentTab(e)}
|
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import React, { useCallback, useEffect } from 'react';
|
import React, { useCallback, useEffect } from 'react';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import type { ResLogin } from '@/global/support/api/userRes.d';
|
import type { ResLogin } from '@/global/support/api/userRes.d';
|
||||||
import { useChatStore } from '@/web/core/chat/storeChat';
|
import { useChatStore } from '@/web/core/chat/context/storeChat';
|
||||||
import { useUserStore } from '@/web/support/user/useUserStore';
|
import { useUserStore } from '@/web/support/user/useUserStore';
|
||||||
import { clearToken, setToken } from '@/web/support/user/auth';
|
import { clearToken, setToken } from '@/web/support/user/auth';
|
||||||
import { postFastLogin } from '@/web/support/user/api';
|
import { postFastLogin } from '@/web/support/user/api';
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import { useSystemStore } from '@/web/common/system/useSystemStore';
|
|||||||
import type { ResLogin } from '@/global/support/api/userRes.d';
|
import type { ResLogin } from '@/global/support/api/userRes.d';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import { useUserStore } from '@/web/support/user/useUserStore';
|
import { useUserStore } from '@/web/support/user/useUserStore';
|
||||||
import { useChatStore } from '@/web/core/chat/storeChat';
|
import { useChatStore } from '@/web/core/chat/context/storeChat';
|
||||||
import LoginForm from './components/LoginForm/LoginForm';
|
import LoginForm from './components/LoginForm/LoginForm';
|
||||||
import dynamic from 'next/dynamic';
|
import dynamic from 'next/dynamic';
|
||||||
import { serviceSideProps } from '@/web/common/utils/i18n';
|
import { serviceSideProps } from '@/web/common/utils/i18n';
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import React, { useCallback, useEffect } from 'react';
|
|||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||||
import type { ResLogin } from '@/global/support/api/userRes.d';
|
import type { ResLogin } from '@/global/support/api/userRes.d';
|
||||||
import { useChatStore } from '@/web/core/chat/storeChat';
|
import { useChatStore } from '@/web/core/chat/context/storeChat';
|
||||||
import { useUserStore } from '@/web/support/user/useUserStore';
|
import { useUserStore } from '@/web/support/user/useUserStore';
|
||||||
import { clearToken, setToken } from '@/web/support/user/auth';
|
import { clearToken, setToken } from '@/web/support/user/auth';
|
||||||
import { oauthLogin } from '@/web/support/user/api';
|
import { oauthLogin } from '@/web/support/user/api';
|
||||||
|
|||||||
@ -43,7 +43,7 @@ export const delChatHistoryById = (data: DelHistoryProps) => DELETE(`/core/chat/
|
|||||||
/**
|
/**
|
||||||
* clear all history by appid
|
* clear all history by appid
|
||||||
*/
|
*/
|
||||||
export const clearChatHistoryByAppId = (data: ClearHistoriesProps) =>
|
export const delClearChatHistories = (data: ClearHistoriesProps) =>
|
||||||
DELETE(`/core/chat/clearHistories`, data);
|
DELETE(`/core/chat/clearHistories`, data);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
151
projects/app/src/web/core/chat/context/chatContext.tsx
Normal file
151
projects/app/src/web/core/chat/context/chatContext.tsx
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
||||||
|
import { useRouter } from 'next/router';
|
||||||
|
import React, { ReactNode, useCallback, useEffect, useRef } from 'react';
|
||||||
|
import { createContext } from 'use-context-selector';
|
||||||
|
import { delClearChatHistories, delChatHistoryById, putChatHistory } from '../api';
|
||||||
|
import { ChatHistoryItemType } from '@fastgpt/global/core/chat/type';
|
||||||
|
import { ClearHistoriesProps, DelHistoryProps, UpdateHistoryProps } from '@/global/core/chat/api';
|
||||||
|
import { useDisclosure } from '@chakra-ui/react';
|
||||||
|
import { useChatStore } from './storeChat';
|
||||||
|
|
||||||
|
type ChatContextValueType = {
|
||||||
|
histories: ChatHistoryItemType[];
|
||||||
|
loadHistories: () => Promise<ChatHistoryItemType[]>;
|
||||||
|
};
|
||||||
|
type ChatContextType = ChatContextValueType & {
|
||||||
|
chatId: string;
|
||||||
|
onUpdateHistory: (data: UpdateHistoryProps) => void;
|
||||||
|
onDelHistory: (data: DelHistoryProps) => Promise<undefined>;
|
||||||
|
onClearHistories: (data: ClearHistoriesProps) => Promise<undefined>;
|
||||||
|
isOpenSlider: boolean;
|
||||||
|
onCloseSlider: () => void;
|
||||||
|
onOpenSlider: () => void;
|
||||||
|
forbidLoadChat: React.MutableRefObject<boolean>;
|
||||||
|
onChangeChatId: (chatId?: string, forbid?: boolean) => void;
|
||||||
|
onChangeAppId: (appId: string) => void;
|
||||||
|
isLoading: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ChatContext = createContext<ChatContextType>({
|
||||||
|
chatId: '',
|
||||||
|
// forbidLoadChat: undefined,
|
||||||
|
histories: [],
|
||||||
|
loadHistories: function (): Promise<ChatHistoryItemType[]> {
|
||||||
|
throw new Error('Function not implemented.');
|
||||||
|
},
|
||||||
|
onUpdateHistory: function (data: UpdateHistoryProps): void {
|
||||||
|
throw new Error('Function not implemented.');
|
||||||
|
},
|
||||||
|
onDelHistory: function (data: DelHistoryProps): Promise<undefined> {
|
||||||
|
throw new Error('Function not implemented.');
|
||||||
|
},
|
||||||
|
onClearHistories: function (data: ClearHistoriesProps): Promise<undefined> {
|
||||||
|
throw new Error('Function not implemented.');
|
||||||
|
},
|
||||||
|
isOpenSlider: false,
|
||||||
|
onCloseSlider: function (): void {
|
||||||
|
throw new Error('Function not implemented.');
|
||||||
|
},
|
||||||
|
onOpenSlider: function (): void {
|
||||||
|
throw new Error('Function not implemented.');
|
||||||
|
},
|
||||||
|
forbidLoadChat: { current: false },
|
||||||
|
onChangeChatId: function (chatId?: string | undefined, forbid?: boolean | undefined): void {
|
||||||
|
throw new Error('Function not implemented.');
|
||||||
|
},
|
||||||
|
onChangeAppId: function (appId: string): void {
|
||||||
|
throw new Error('Function not implemented.');
|
||||||
|
},
|
||||||
|
isLoading: false
|
||||||
|
});
|
||||||
|
|
||||||
|
const ChatContextProvider = ({
|
||||||
|
children,
|
||||||
|
histories,
|
||||||
|
loadHistories
|
||||||
|
}: ChatContextValueType & { children: ReactNode }) => {
|
||||||
|
const router = useRouter();
|
||||||
|
const { chatId = '' } = router.query as { chatId: string };
|
||||||
|
const isSystemChat = router.pathname === '/chat';
|
||||||
|
|
||||||
|
const forbidLoadChat = useRef(false);
|
||||||
|
|
||||||
|
const { isOpen: isOpenSlider, onClose: onCloseSlider, onOpen: onOpenSlider } = useDisclosure();
|
||||||
|
|
||||||
|
const { setLastChatId } = useChatStore();
|
||||||
|
const onChangeChatId = useCallback(
|
||||||
|
(changeChatId = '', forbid = false) => {
|
||||||
|
if (chatId !== changeChatId) {
|
||||||
|
forbidLoadChat.current = forbid;
|
||||||
|
setLastChatId(changeChatId);
|
||||||
|
router.replace({
|
||||||
|
query: {
|
||||||
|
...router.query,
|
||||||
|
chatId: changeChatId || ''
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
onCloseSlider();
|
||||||
|
},
|
||||||
|
[chatId, onCloseSlider, router, setLastChatId]
|
||||||
|
);
|
||||||
|
useEffect(() => {
|
||||||
|
setLastChatId(chatId);
|
||||||
|
}, [chatId, setLastChatId]);
|
||||||
|
|
||||||
|
const onChangeAppId = useCallback(
|
||||||
|
(appId: string) => {
|
||||||
|
router.replace({
|
||||||
|
query: {
|
||||||
|
...router.query,
|
||||||
|
chatId: '',
|
||||||
|
appId
|
||||||
|
}
|
||||||
|
});
|
||||||
|
onCloseSlider();
|
||||||
|
},
|
||||||
|
[onCloseSlider, router]
|
||||||
|
);
|
||||||
|
|
||||||
|
const { runAsync: onUpdateHistory, loading: isUpdatingHistory } = useRequest2(putChatHistory, {
|
||||||
|
onSuccess() {
|
||||||
|
loadHistories();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const { runAsync: onDelHistory, loading: isDeletingHistory } = useRequest2(delChatHistoryById, {
|
||||||
|
onSuccess() {
|
||||||
|
loadHistories();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const { runAsync: onClearHistories, loading: isClearingHistory } = useRequest2(
|
||||||
|
delClearChatHistories,
|
||||||
|
{
|
||||||
|
onSuccess() {
|
||||||
|
loadHistories();
|
||||||
|
},
|
||||||
|
onFinally() {
|
||||||
|
onChangeChatId('');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
const isLoading = isUpdatingHistory || isDeletingHistory || isClearingHistory;
|
||||||
|
|
||||||
|
const contextValue = {
|
||||||
|
chatId,
|
||||||
|
histories,
|
||||||
|
loadHistories,
|
||||||
|
onUpdateHistory,
|
||||||
|
onDelHistory,
|
||||||
|
onClearHistories,
|
||||||
|
isOpenSlider,
|
||||||
|
onCloseSlider,
|
||||||
|
onOpenSlider,
|
||||||
|
forbidLoadChat,
|
||||||
|
onChangeChatId,
|
||||||
|
onChangeAppId,
|
||||||
|
isLoading
|
||||||
|
};
|
||||||
|
return <ChatContext.Provider value={contextValue}>{children}</ChatContext.Provider>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ChatContextProvider;
|
||||||
39
projects/app/src/web/core/chat/context/storeChat.ts
Normal file
39
projects/app/src/web/core/chat/context/storeChat.ts
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import { create } from 'zustand';
|
||||||
|
import { devtools, persist } from 'zustand/middleware';
|
||||||
|
import { immer } from 'zustand/middleware/immer';
|
||||||
|
import type { DeleteChatItemProps } from '@/global/core/chat/api';
|
||||||
|
|
||||||
|
type State = {
|
||||||
|
lastChatAppId: string;
|
||||||
|
setLastChatAppId: (id: string) => void;
|
||||||
|
lastChatId: string;
|
||||||
|
setLastChatId: (id: string) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useChatStore = create<State>()(
|
||||||
|
devtools(
|
||||||
|
persist(
|
||||||
|
immer((set, get) => ({
|
||||||
|
lastChatAppId: '',
|
||||||
|
setLastChatAppId(id: string) {
|
||||||
|
set((state) => {
|
||||||
|
state.lastChatAppId = id;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
lastChatId: '',
|
||||||
|
setLastChatId(id: string) {
|
||||||
|
set((state) => {
|
||||||
|
state.lastChatId = id;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})),
|
||||||
|
{
|
||||||
|
name: 'chatStore',
|
||||||
|
partialize: (state) => ({
|
||||||
|
lastChatAppId: state.lastChatAppId,
|
||||||
|
lastChatId: state.lastChatId
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
@ -1,147 +0,0 @@
|
|||||||
import { create } from 'zustand';
|
|
||||||
import { devtools, persist } from 'zustand/middleware';
|
|
||||||
import { immer } from 'zustand/middleware/immer';
|
|
||||||
import type { ChatHistoryItemType } from '@fastgpt/global/core/chat/type.d';
|
|
||||||
import type {
|
|
||||||
InitChatResponse,
|
|
||||||
GetHistoriesProps,
|
|
||||||
ClearHistoriesProps,
|
|
||||||
DelHistoryProps,
|
|
||||||
UpdateHistoryProps,
|
|
||||||
DeleteChatItemProps
|
|
||||||
} from '@/global/core/chat/api';
|
|
||||||
import {
|
|
||||||
delChatHistoryById,
|
|
||||||
getChatHistories,
|
|
||||||
clearChatHistoryByAppId,
|
|
||||||
delChatRecordById,
|
|
||||||
putChatHistory
|
|
||||||
} from '@/web/core/chat/api';
|
|
||||||
import { defaultChatData } from '@/global/core/chat/constants';
|
|
||||||
|
|
||||||
type State = {
|
|
||||||
histories: ChatHistoryItemType[];
|
|
||||||
loadHistories: (data: GetHistoriesProps) => Promise<null>;
|
|
||||||
delOneHistory(data: DelHistoryProps): Promise<void>;
|
|
||||||
clearHistories(data: ClearHistoriesProps): Promise<void>;
|
|
||||||
pushHistory: (history: ChatHistoryItemType) => void;
|
|
||||||
updateHistory: (e: UpdateHistoryProps & { updateTime?: Date; title?: string }) => Promise<any>;
|
|
||||||
chatData: InitChatResponse;
|
|
||||||
setChatData: (e: InitChatResponse | ((e: InitChatResponse) => InitChatResponse)) => void;
|
|
||||||
lastChatAppId: string;
|
|
||||||
setLastChatAppId: (id: string) => void;
|
|
||||||
lastChatId: string;
|
|
||||||
setLastChatId: (id: string) => void;
|
|
||||||
delOneHistoryItem: (e: DeleteChatItemProps) => Promise<any>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useChatStore = create<State>()(
|
|
||||||
devtools(
|
|
||||||
persist(
|
|
||||||
immer((set, get) => ({
|
|
||||||
lastChatAppId: '',
|
|
||||||
setLastChatAppId(id: string) {
|
|
||||||
set((state) => {
|
|
||||||
state.lastChatAppId = id;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
lastChatId: '',
|
|
||||||
setLastChatId(id: string) {
|
|
||||||
set((state) => {
|
|
||||||
state.lastChatId = id;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
histories: [],
|
|
||||||
async loadHistories(e) {
|
|
||||||
const data = await getChatHistories(e);
|
|
||||||
set((state) => {
|
|
||||||
state.histories = data;
|
|
||||||
});
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
async delOneHistory(props) {
|
|
||||||
set((state) => {
|
|
||||||
state.histories = state.histories.filter((item) => item.chatId !== props.chatId);
|
|
||||||
});
|
|
||||||
await delChatHistoryById(props);
|
|
||||||
},
|
|
||||||
async clearHistories(data) {
|
|
||||||
set((state) => {
|
|
||||||
state.histories = [];
|
|
||||||
});
|
|
||||||
await clearChatHistoryByAppId(data);
|
|
||||||
},
|
|
||||||
pushHistory(history) {
|
|
||||||
set((state) => {
|
|
||||||
state.histories = [history, ...state.histories];
|
|
||||||
});
|
|
||||||
},
|
|
||||||
async updateHistory(props) {
|
|
||||||
const { chatId, customTitle, top, title, updateTime } = props;
|
|
||||||
const index = get().histories.findIndex((item) => item.chatId === chatId);
|
|
||||||
|
|
||||||
if (index > -1) {
|
|
||||||
const newHistory = {
|
|
||||||
...get().histories[index],
|
|
||||||
...(title && { title }),
|
|
||||||
...(updateTime && { updateTime }),
|
|
||||||
...(customTitle !== undefined && { customTitle }),
|
|
||||||
...(top !== undefined && { top })
|
|
||||||
};
|
|
||||||
|
|
||||||
if (customTitle !== undefined || top !== undefined) {
|
|
||||||
try {
|
|
||||||
putChatHistory(props);
|
|
||||||
} catch (error) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
set((state) => {
|
|
||||||
const newHistories = (() => {
|
|
||||||
return [
|
|
||||||
newHistory,
|
|
||||||
...get().histories.slice(0, index),
|
|
||||||
...get().histories.slice(index + 1)
|
|
||||||
];
|
|
||||||
})();
|
|
||||||
|
|
||||||
state.histories = newHistories;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
chatData: defaultChatData,
|
|
||||||
setChatData(e = defaultChatData) {
|
|
||||||
if (typeof e === 'function') {
|
|
||||||
set((state) => {
|
|
||||||
state.chatData = e(state.chatData);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
set((state) => {
|
|
||||||
state.chatData = e;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async delOneHistoryItem(props) {
|
|
||||||
const { chatId, contentId } = props;
|
|
||||||
if (!chatId || !contentId) return;
|
|
||||||
|
|
||||||
try {
|
|
||||||
get().setChatData((state) => ({
|
|
||||||
...state,
|
|
||||||
history: state.history.filter((item) => item.dataId !== contentId)
|
|
||||||
}));
|
|
||||||
await delChatRecordById(props);
|
|
||||||
} catch (err) {
|
|
||||||
console.log(err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})),
|
|
||||||
{
|
|
||||||
name: 'chatStore',
|
|
||||||
partialize: (state) => ({
|
|
||||||
lastChatAppId: state.lastChatAppId,
|
|
||||||
lastChatId: state.lastChatId
|
|
||||||
})
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
@ -10,32 +10,16 @@ const nanoid = customAlphabet(
|
|||||||
|
|
||||||
type State = {
|
type State = {
|
||||||
localUId: string;
|
localUId: string;
|
||||||
shareChatHistory: (ChatHistoryItemType & { delete?: boolean })[];
|
|
||||||
clearLocalHistory: (shareId?: string) => void;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useShareChatStore = create<State>()(
|
export const useShareChatStore = create<State>()(
|
||||||
devtools(
|
devtools(
|
||||||
persist(
|
persist(
|
||||||
immer((set, get) => ({
|
immer((set, get) => ({
|
||||||
localUId: `shareChat-${Date.now()}-${nanoid()}`,
|
localUId: `shareChat-${Date.now()}-${nanoid()}`
|
||||||
shareChatHistory: [], // old version field
|
|
||||||
clearLocalHistory() {
|
|
||||||
// abandon
|
|
||||||
set((state) => {
|
|
||||||
state.shareChatHistory = state.shareChatHistory.map((item) => ({
|
|
||||||
...item,
|
|
||||||
delete: true
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})),
|
})),
|
||||||
{
|
{
|
||||||
name: 'shareChatStore',
|
name: 'shareChatStore'
|
||||||
partialize: (state) => ({
|
|
||||||
localUId: state.localUId,
|
|
||||||
shareChatHistory: state.shareChatHistory
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user