mirror of
https://github.com/labring/FastGPT.git
synced 2025-07-30 18:48:55 +00:00
4.6.7 first pr (#726)
This commit is contained in:
@@ -40,8 +40,8 @@ const FeedbackModal = ({
|
||||
onSuccess() {
|
||||
onSuccess(ref.current?.value || t('core.chat.feedback.No Content'));
|
||||
},
|
||||
successToast: t('chat.Feedback Success'),
|
||||
errorToast: t('chat.Feedback Failed')
|
||||
successToast: t('core.chat.Feedback Success'),
|
||||
errorToast: t('core.chat.Feedback Failed')
|
||||
});
|
||||
|
||||
return (
|
||||
@@ -49,17 +49,17 @@ const FeedbackModal = ({
|
||||
isOpen={true}
|
||||
onClose={onClose}
|
||||
iconSrc="/imgs/modal/badAnswer.svg"
|
||||
title={t('chat.Feedback Modal')}
|
||||
title={t('core.chat.Feedback Modal')}
|
||||
>
|
||||
<ModalBody>
|
||||
<Textarea ref={ref} rows={10} placeholder={t('chat.Feedback Modal Tip')} />
|
||||
<Textarea ref={ref} rows={10} placeholder={t('core.chat.Feedback Modal Tip')} />
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button variant={'whiteBase'} mr={2} onClick={onClose}>
|
||||
{t('Cancel')}
|
||||
</Button>
|
||||
<Button isLoading={isLoading} onClick={mutate}>
|
||||
{t('chat.Feedback Submit')}
|
||||
{t('core.chat.Feedback Submit')}
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</MyModal>
|
||||
|
@@ -8,10 +8,11 @@ import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useSelectFile } from '@/web/common/file/hooks/useSelectFile';
|
||||
import { compressImgFileAndUpload } from '@/web/common/file/controller';
|
||||
import { useToast } from '@/web/common/hooks/useToast';
|
||||
import { customAlphabet } from 'nanoid';
|
||||
import { IMG_BLOCK_KEY } from '@fastgpt/global/core/chat/constants';
|
||||
import { addDays } from 'date-fns';
|
||||
import { useRequest } from '@/web/common/hooks/useRequest';
|
||||
import { MongoImageTypeEnum } from '@fastgpt/global/common/file/image/constants';
|
||||
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 6);
|
||||
|
||||
enum FileTypeEnum {
|
||||
@@ -45,7 +46,6 @@ const MessageInput = ({
|
||||
resetInputVal: (val: string) => void;
|
||||
}) => {
|
||||
const { shareId } = useRouter().query as { shareId?: string };
|
||||
const { toast } = useToast();
|
||||
const {
|
||||
isSpeaking,
|
||||
isTransCription,
|
||||
@@ -68,17 +68,18 @@ const MessageInput = ({
|
||||
maxCount: 10
|
||||
});
|
||||
|
||||
const uploadFile = useCallback(
|
||||
async (file: FileItemType) => {
|
||||
const { mutate: uploadFile } = useRequest({
|
||||
mutationFn: async (file: FileItemType) => {
|
||||
if (file.type === FileTypeEnum.image) {
|
||||
try {
|
||||
const src = await compressImgFileAndUpload({
|
||||
type: MongoImageTypeEnum.chatImage,
|
||||
file: file.rawFile,
|
||||
maxW: 4329,
|
||||
maxH: 4329,
|
||||
maxSize: 1024 * 1024 * 5,
|
||||
// 30 day expired.
|
||||
expiredTime: addDays(new Date(), 30),
|
||||
expiredTime: addDays(new Date(), 7),
|
||||
shareId
|
||||
});
|
||||
setFileList((state) =>
|
||||
@@ -94,16 +95,13 @@ const MessageInput = ({
|
||||
} catch (error) {
|
||||
setFileList((state) => state.filter((item) => item.id !== file.id));
|
||||
console.log(error);
|
||||
|
||||
toast({
|
||||
status: 'error',
|
||||
title: t('common.Upload File Failed')
|
||||
});
|
||||
return Promise.reject(error);
|
||||
}
|
||||
}
|
||||
},
|
||||
[shareId, t, toast]
|
||||
);
|
||||
errorToast: t('common.Upload File Failed')
|
||||
});
|
||||
|
||||
const onSelectFile = useCallback(
|
||||
async (files: File[]) => {
|
||||
if (!files || files.length === 0) {
|
||||
@@ -219,7 +217,7 @@ ${images.map((img) => JSON.stringify({ src: img.src })).join('\n')}
|
||||
visibility={isSpeaking && isTransCription ? 'visible' : 'hidden'}
|
||||
>
|
||||
<Spinner size={'sm'} mr={4} />
|
||||
{t('chat.Converting to text')}
|
||||
{t('core.chat.Converting to text')}
|
||||
</Flex>
|
||||
|
||||
{/* file preview */}
|
||||
|
@@ -5,7 +5,7 @@ import MyModal from '../MyModal';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import type { SearchDataResponseItemType } from '@fastgpt/global/core/dataset/type';
|
||||
import QuoteItem from '../core/dataset/QuoteItem';
|
||||
import { RawSourceText } from '@/pages/dataset/detail/components/InputDataModal';
|
||||
import RawSourceBox from '../core/dataset/RawSourceBox';
|
||||
|
||||
const QuoteModal = ({
|
||||
rawSearch = [],
|
||||
@@ -46,7 +46,7 @@ const QuoteModal = ({
|
||||
title={
|
||||
<Box>
|
||||
{metadata ? (
|
||||
<RawSourceText {...metadata} canView={false} />
|
||||
<RawSourceBox {...metadata} canView={false} />
|
||||
) : (
|
||||
<>{t('core.chat.Quote Amount', { amount: rawSearch.length })}</>
|
||||
)}
|
||||
|
@@ -19,7 +19,7 @@ const ReadFeedbackModal = ({
|
||||
isOpen={true}
|
||||
onClose={onClose}
|
||||
iconSrc="/imgs/modal/readFeedback.svg"
|
||||
title={t('chat.Feedback Modal')}
|
||||
title={t('core.chat.Feedback Modal')}
|
||||
>
|
||||
<ModalBody>{content}</ModalBody>
|
||||
<ModalFooter>
|
||||
|
@@ -92,7 +92,7 @@ const ResponseTags = ({
|
||||
<>
|
||||
{sourceList.length > 0 && (
|
||||
<>
|
||||
<ChatBoxDivider icon="core/chat/quoteFill" text={t('chat.Quote')} />
|
||||
<ChatBoxDivider icon="core/chat/quoteFill" text={t('core.chat.Quote')} />
|
||||
<Flex alignItems={'center'} flexWrap={'wrap'} gap={2}>
|
||||
{sourceList.map((item) => (
|
||||
<MyTooltip key={item.collectionId} label={t('core.chat.quote.Read Quote')}>
|
||||
|
@@ -46,7 +46,7 @@ const SelectMarkCollection = ({
|
||||
paths={paths}
|
||||
onClose={onClose}
|
||||
setParentId={setParentId}
|
||||
tips={t('chat.Select Mark Kb Desc')}
|
||||
tips={t('core.chat.Select Mark Kb Desc')}
|
||||
>
|
||||
<ModalBody flex={'1 0 0'} overflowY={'auto'}>
|
||||
<Grid
|
||||
@@ -164,19 +164,23 @@ const SelectMarkCollection = ({
|
||||
<InputDataModal
|
||||
onClose={onClose}
|
||||
collectionId={adminMarkData.collectionId}
|
||||
dataId={adminMarkData.dataId}
|
||||
defaultValue={{
|
||||
id: adminMarkData.dataId,
|
||||
q: adminMarkData.q,
|
||||
a: adminMarkData.a,
|
||||
indexes: [getDefaultIndex({ dataId: `${Date.now()}` })]
|
||||
a: adminMarkData.a
|
||||
}}
|
||||
onSuccess={(data) => {
|
||||
if (!data.q || !adminMarkData.datasetId || !adminMarkData.collectionId || !data.id) {
|
||||
if (
|
||||
!data.q ||
|
||||
!adminMarkData.datasetId ||
|
||||
!adminMarkData.collectionId ||
|
||||
!data.dataId
|
||||
) {
|
||||
return onClose();
|
||||
}
|
||||
|
||||
onSuccess({
|
||||
dataId: data.id,
|
||||
dataId: data.dataId,
|
||||
datasetId: adminMarkData.datasetId,
|
||||
collectionId: adminMarkData.collectionId,
|
||||
q: data.q,
|
||||
|
@@ -910,14 +910,15 @@ const ChatBox = (
|
||||
)}
|
||||
{/* admin mark content */}
|
||||
{showMarkIcon && item.adminFeedback && (
|
||||
<Box>
|
||||
<Box fontSize={'sm'}>
|
||||
<ChatBoxDivider
|
||||
icon="core/app/markLight"
|
||||
text={t('chat.Admin Mark Content')}
|
||||
text={t('core.chat.Admin Mark Content')}
|
||||
/>
|
||||
<Box whiteSpace={'pre'}>{`${item.adminFeedback.q || ''}${
|
||||
item.adminFeedback.a ? `\n${item.adminFeedback.a}` : ''
|
||||
}`}</Box>
|
||||
<Box whiteSpace={'pre'}>
|
||||
<Box color={'black'}>{item.adminFeedback.q}</Box>
|
||||
<Box color={'myGray.600'}>{item.adminFeedback.a}</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
)}
|
||||
</Card>
|
||||
@@ -996,6 +997,7 @@ const ChatBox = (
|
||||
setAdminMarkData={(e) => setAdminMarkData({ ...e, chatItemId: adminMarkData.chatItemId })}
|
||||
onClose={() => setAdminMarkData(undefined)}
|
||||
onSuccess={(adminFeedback) => {
|
||||
console.log(adminMarkData);
|
||||
if (!appId || !chatId || !adminMarkData.chatItemId) return;
|
||||
updateChatAdminFeedback({
|
||||
appId,
|
||||
@@ -1003,6 +1005,7 @@ const ChatBox = (
|
||||
chatItemId: adminMarkData.chatItemId,
|
||||
...adminFeedback
|
||||
});
|
||||
|
||||
// update dom
|
||||
setChatHistory((state) =>
|
||||
state.map((chatItem) =>
|
||||
@@ -1234,7 +1237,7 @@ function ChatController({
|
||||
{!!onDelete && (
|
||||
<>
|
||||
{onRetry && (
|
||||
<MyTooltip label={t('chat.retry')}>
|
||||
<MyTooltip label={t('core.chat.retry')}>
|
||||
<MyIcon
|
||||
{...controlIconStyle}
|
||||
name={'common/retryLight'}
|
||||
@@ -1301,7 +1304,7 @@ function ChatController({
|
||||
</MyTooltip>
|
||||
))}
|
||||
{!!onMark && (
|
||||
<MyTooltip label={t('chat.Mark')}>
|
||||
<MyTooltip label={t('core.chat.Mark')}>
|
||||
<MyIcon
|
||||
{...controlIconStyle}
|
||||
name={'core/app/markLight'}
|
||||
|
@@ -30,7 +30,7 @@ const Navbar = ({ unread }: { unread: number }) => {
|
||||
{
|
||||
label: t('navbar.Chat'),
|
||||
icon: 'core/chat/chatLight',
|
||||
activeIcon: 'chatcore/dataset/chatFillFill',
|
||||
activeIcon: 'core/chat/chatFill',
|
||||
link: `/chat?appId=${lastChatAppId}&chatId=${lastChatId}`,
|
||||
activeLink: ['/chat']
|
||||
},
|
||||
@@ -77,6 +77,12 @@ const Navbar = ({ unread }: { unread: number }) => {
|
||||
h: '58px',
|
||||
borderRadius: 'md'
|
||||
};
|
||||
const hoverStyle: LinkProps = {
|
||||
_hover: {
|
||||
bg: 'myGray.05',
|
||||
color: 'primary.600'
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Flex
|
||||
@@ -146,10 +152,11 @@ const Navbar = ({ unread }: { unread: number }) => {
|
||||
<Link
|
||||
as={NextLink}
|
||||
{...itemStyles}
|
||||
{...hoverStyle}
|
||||
prefetch
|
||||
href={`/account?currentTab=inform`}
|
||||
mb={0}
|
||||
color={'#9096a5'}
|
||||
color={'myGray.500'}
|
||||
>
|
||||
<Badge count={unread}>
|
||||
<MyIcon name={'inform'} width={'22px'} height={'22px'} />
|
||||
@@ -161,10 +168,11 @@ const Navbar = ({ unread }: { unread: number }) => {
|
||||
<MyTooltip label={t('common.system.Use Helper')} placement={'right-end'}>
|
||||
<Link
|
||||
{...itemStyles}
|
||||
{...hoverStyle}
|
||||
href={feConfigs?.chatbotUrl || getDocPath('/docs/intro')}
|
||||
target="_blank"
|
||||
mb={0}
|
||||
color={'#9096a5'}
|
||||
color={'myGray.500'}
|
||||
>
|
||||
<MyIcon name={'common/courseLight'} width={'26px'} height={'26px'} />
|
||||
</Link>
|
||||
@@ -177,8 +185,9 @@ const Navbar = ({ unread }: { unread: number }) => {
|
||||
href="https://github.com/labring/FastGPT"
|
||||
target={'_blank'}
|
||||
{...itemStyles}
|
||||
{...hoverStyle}
|
||||
mt={0}
|
||||
color={'#9096a5'}
|
||||
color={'myGray.500'}
|
||||
>
|
||||
<MyIcon name={'common/gitLight'} width={'22px'} height={'22px'} />
|
||||
</Link>
|
||||
|
@@ -24,7 +24,7 @@ const QuestionGuide = ({ text }: { text: string }) => {
|
||||
|
||||
return questionGuides.length > 0 ? (
|
||||
<Box mt={2}>
|
||||
<ChatBoxDivider icon="core/chat/QGFill" text={t('chat.Question Guide Tips')} />
|
||||
<ChatBoxDivider icon="core/chat/QGFill" text={t('core.chat.Question Guide Tips')} />
|
||||
<Flex alignItems={'center'} flexWrap={'wrap'} gap={2}>
|
||||
{questionGuides.map((text) => (
|
||||
<Flex
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import React, { useState } from 'react';
|
||||
import React, { WheelEventHandler, useState } from 'react';
|
||||
import {
|
||||
Box,
|
||||
Image,
|
||||
@@ -14,6 +14,17 @@ const MdImage = ({ src }: { src?: string }) => {
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [succeed, setSucceed] = useState(false);
|
||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||
const [scale, setScale] = useState(1);
|
||||
|
||||
const handleWheel: WheelEventHandler<HTMLImageElement> = (e) => {
|
||||
setScale((prevScale) => {
|
||||
const newScale = prevScale + e.deltaY * 0.5 * -0.01;
|
||||
if (newScale < 0.5) return 0.5;
|
||||
if (newScale > 10) return 10;
|
||||
return newScale;
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Skeleton
|
||||
minH="100px"
|
||||
@@ -48,6 +59,7 @@ const MdImage = ({ src }: { src?: string }) => {
|
||||
<ModalOverlay />
|
||||
<ModalContent boxShadow={'none'} maxW={'auto'} w="auto" bg={'transparent'}>
|
||||
<Image
|
||||
transform={`scale(${scale})`}
|
||||
borderRadius={'md'}
|
||||
src={src}
|
||||
alt={''}
|
||||
@@ -57,6 +69,7 @@ const MdImage = ({ src }: { src?: string }) => {
|
||||
fallbackSrc={'/imgs/errImg.png'}
|
||||
fallbackStrategy={'onError'}
|
||||
objectFit={'contain'}
|
||||
onWheel={handleWheel}
|
||||
/>
|
||||
</ModalContent>
|
||||
<ModalCloseButton bg={'myWhite.500'} zIndex={999999} />
|
||||
|
@@ -343,7 +343,6 @@
|
||||
margin: 10px 0;
|
||||
}
|
||||
.markdown {
|
||||
text-align: justify;
|
||||
tab-size: 4;
|
||||
word-spacing: normal;
|
||||
width: 100%;
|
||||
|
@@ -112,6 +112,17 @@ function A({ children, ...props }: any) {
|
||||
}
|
||||
|
||||
const Markdown = ({ source, isChatting = false }: { source: string; isChatting?: boolean }) => {
|
||||
const components = useMemo<any>(
|
||||
() => ({
|
||||
img: Image,
|
||||
pre: 'div',
|
||||
p: (pProps: any) => <p {...pProps} dir="auto" />,
|
||||
code: Code,
|
||||
a: A
|
||||
}),
|
||||
[]
|
||||
);
|
||||
|
||||
const formatSource = source
|
||||
.replace(/\\n/g, '\n ')
|
||||
.replace(/(http[s]?:\/\/[^\s,。]+)([。,])/g, '$1 $2')
|
||||
@@ -124,13 +135,7 @@ const Markdown = ({ source, isChatting = false }: { source: string; isChatting?:
|
||||
`}
|
||||
remarkPlugins={[RemarkMath, RemarkGfm, RemarkBreaks]}
|
||||
rehypePlugins={[RehypeKatex]}
|
||||
components={{
|
||||
img: Image,
|
||||
pre: 'div',
|
||||
p: (pProps) => <p {...pProps} dir="auto" />,
|
||||
code: Code,
|
||||
a: A
|
||||
}}
|
||||
components={components}
|
||||
linkTarget={'_blank'}
|
||||
>
|
||||
{formatSource}
|
||||
|
@@ -4,18 +4,19 @@ import MySelect, { type SelectProps } from './index';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import dynamic from 'next/dynamic';
|
||||
import { useDisclosure } from '@chakra-ui/react';
|
||||
import { feConfigs } from '@/web/common/system/staticData';
|
||||
const PriceBox = dynamic(() => import('@/components/support/wallet/Price'));
|
||||
|
||||
const SelectAiModel = ({ list, ...props }: SelectProps) => {
|
||||
const { t } = useTranslation();
|
||||
const expandList = useMemo(
|
||||
() =>
|
||||
list.concat({
|
||||
label: t('support.user.Price'),
|
||||
value: 'price'
|
||||
}),
|
||||
[list, t]
|
||||
);
|
||||
const expandList = useMemo(() => {
|
||||
return feConfigs.show_pay
|
||||
? list.concat({
|
||||
label: t('support.user.Price'),
|
||||
value: 'price'
|
||||
})
|
||||
: list;
|
||||
}, [list, t]);
|
||||
|
||||
const {
|
||||
isOpen: isOpenPriceBox,
|
||||
|
30
projects/app/src/components/core/dataset/DatasetTypeTag.tsx
Normal file
30
projects/app/src/components/core/dataset/DatasetTypeTag.tsx
Normal file
@@ -0,0 +1,30 @@
|
||||
import { Box, Flex, FlexProps } from '@chakra-ui/react';
|
||||
import { DatasetTypeEnum } from '@fastgpt/global/core/dataset/constant';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import React from 'react';
|
||||
import { DatasetTypeMap } from '@fastgpt/global/core/dataset/constant';
|
||||
|
||||
const DatasetTypeTag = ({ type, ...props }: { type: `${DatasetTypeEnum}` } & FlexProps) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const item = DatasetTypeMap[type];
|
||||
|
||||
return (
|
||||
<Flex
|
||||
bg={'myGray.100'}
|
||||
borderWidth={'1px'}
|
||||
borderColor={'myGray.200'}
|
||||
px={4}
|
||||
py={'6px'}
|
||||
borderRadius={'md'}
|
||||
fontSize={'xs'}
|
||||
{...props}
|
||||
>
|
||||
<MyIcon name={item.icon as any} w={'16px'} mr={2} color={'myGray.400'} />
|
||||
<Box>{t(item.label)}</Box>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export default DatasetTypeTag;
|
@@ -1,9 +1,6 @@
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import { Box, Flex, Link, Progress } from '@chakra-ui/react';
|
||||
import {
|
||||
type InputDataType,
|
||||
RawSourceText
|
||||
} from '@/pages/dataset/detail/components/InputDataModal';
|
||||
import RawSourceBox from '@/components/core/dataset/RawSourceBox';
|
||||
import type { SearchDataResponseItemType } from '@fastgpt/global/core/dataset/type.d';
|
||||
import NextLink from 'next/link';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
@@ -11,9 +8,6 @@ import { useTranslation } from 'next-i18next';
|
||||
import MyTooltip from '@/components/MyTooltip';
|
||||
import dynamic from 'next/dynamic';
|
||||
import MyBox from '@/components/common/MyBox';
|
||||
import { getDatasetDataItemById } from '@/web/core/dataset/api';
|
||||
import { useRequest } from '@/web/common/hooks/useRequest';
|
||||
import { DatasetDataItemType } from '@fastgpt/global/core/dataset/type';
|
||||
import { SearchScoreTypeEnum, SearchScoreTypeMap } from '@fastgpt/global/core/dataset/constant';
|
||||
|
||||
const InputDataModal = dynamic(() => import('@/pages/dataset/detail/components/InputDataModal'));
|
||||
@@ -58,17 +52,7 @@ const QuoteItem = ({
|
||||
linkToDataset?: boolean;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const [editInputData, setEditInputData] = useState<InputDataType & { collectionId: string }>();
|
||||
|
||||
const { mutate: onclickEdit, isLoading } = useRequest({
|
||||
mutationFn: async (id: string) => {
|
||||
return getDatasetDataItemById(id);
|
||||
},
|
||||
onSuccess(data: DatasetDataItemType) {
|
||||
setEditInputData(data);
|
||||
},
|
||||
errorToast: t('core.dataset.data.get data error')
|
||||
});
|
||||
const [editInputData, setEditInputData] = useState<{ dataId: string; collectionId: string }>();
|
||||
|
||||
const score = useMemo(() => {
|
||||
if (!Array.isArray(quoteItem.score)) {
|
||||
@@ -114,7 +98,6 @@ const QuoteItem = ({
|
||||
return (
|
||||
<>
|
||||
<MyBox
|
||||
isLoading={isLoading}
|
||||
position={'relative'}
|
||||
overflow={'hidden'}
|
||||
fontSize={'sm'}
|
||||
@@ -124,7 +107,7 @@ const QuoteItem = ({
|
||||
display={'flex'}
|
||||
flexDirection={'column'}
|
||||
>
|
||||
<Flex alignItems={'center'} mb={3}>
|
||||
<Flex alignItems={'center'} mb={3} flexWrap={'wrap'} gap={3}>
|
||||
{score?.primaryScore && (
|
||||
<>
|
||||
{canViewSource ? (
|
||||
@@ -132,7 +115,6 @@ const QuoteItem = ({
|
||||
<Flex
|
||||
px={'12px'}
|
||||
py={'5px'}
|
||||
mr={4}
|
||||
borderRadius={'md'}
|
||||
color={'primary.700'}
|
||||
bg={'primary.50'}
|
||||
@@ -177,13 +159,13 @@ const QuoteItem = ({
|
||||
{canViewSource &&
|
||||
score.secondaryScore.map((item, i) => (
|
||||
<MyTooltip key={item.type} label={t(SearchScoreTypeMap[item.type]?.desc)}>
|
||||
<Box fontSize={'xs'} mr={3}>
|
||||
<Box fontSize={'xs'}>
|
||||
<Flex alignItems={'flex-start'} lineHeight={1.2} mb={1}>
|
||||
<Box
|
||||
px={'5px'}
|
||||
borderWidth={'1px'}
|
||||
borderRadius={'sm'}
|
||||
mr={1}
|
||||
mr={'2px'}
|
||||
{...(scoreTheme[i] && scoreTheme[i])}
|
||||
>
|
||||
<Box transform={'scale(0.9)'}>#{item.index + 1}</Box>
|
||||
@@ -223,7 +205,7 @@ const QuoteItem = ({
|
||||
{quoteItem.q.length + (quoteItem.a?.length || 0)}
|
||||
</Flex>
|
||||
</MyTooltip>
|
||||
<RawSourceText
|
||||
<RawSourceBox
|
||||
fontWeight={'bold'}
|
||||
color={'black'}
|
||||
sourceName={quoteItem.sourceName}
|
||||
@@ -249,7 +231,12 @@ const QuoteItem = ({
|
||||
_hover={{
|
||||
color: 'primary.600'
|
||||
}}
|
||||
onClick={() => onclickEdit(quoteItem.id)}
|
||||
onClick={() =>
|
||||
setEditInputData({
|
||||
dataId: quoteItem.id,
|
||||
collectionId: quoteItem.collectionId
|
||||
})
|
||||
}
|
||||
/>
|
||||
</Box>
|
||||
</MyTooltip>
|
||||
@@ -271,7 +258,7 @@ const QuoteItem = ({
|
||||
)}
|
||||
</MyBox>
|
||||
|
||||
{editInputData && editInputData.id && (
|
||||
{editInputData && (
|
||||
<InputDataModal
|
||||
onClose={() => setEditInputData(undefined)}
|
||||
onSuccess={() => {
|
||||
@@ -280,7 +267,7 @@ const QuoteItem = ({
|
||||
onDelete={() => {
|
||||
console.log('删除引用成功');
|
||||
}}
|
||||
defaultValue={editInputData}
|
||||
dataId={editInputData.dataId}
|
||||
collectionId={editInputData.collectionId}
|
||||
/>
|
||||
)}
|
||||
|
68
projects/app/src/components/core/dataset/RawSourceBox.tsx
Normal file
68
projects/app/src/components/core/dataset/RawSourceBox.tsx
Normal file
@@ -0,0 +1,68 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import { Box, BoxProps, Image } from '@chakra-ui/react';
|
||||
import { useToast } from '@/web/common/hooks/useToast';
|
||||
import { getErrText } from '@fastgpt/global/common/error/utils';
|
||||
import MyTooltip from '@/components/MyTooltip';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { getFileAndOpen } from '@/web/core/dataset/utils';
|
||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||
import { getSourceNameIcon } from '@fastgpt/global/core/dataset/utils';
|
||||
|
||||
type Props = BoxProps & {
|
||||
sourceName?: string;
|
||||
sourceId?: string;
|
||||
canView?: boolean;
|
||||
};
|
||||
|
||||
const RawSourceBox = ({ sourceId, sourceName = '', canView = true, ...props }: Props) => {
|
||||
const { t } = useTranslation();
|
||||
const { toast } = useToast();
|
||||
const { setLoading } = useSystemStore();
|
||||
|
||||
const canPreview = useMemo(() => !!sourceId && canView, [canView, sourceId]);
|
||||
|
||||
const icon = useMemo(() => getSourceNameIcon({ sourceId, sourceName }), [sourceId, sourceName]);
|
||||
|
||||
return (
|
||||
<MyTooltip
|
||||
label={canPreview ? t('file.Click to view file') || '' : ''}
|
||||
shouldWrapChildren={false}
|
||||
>
|
||||
<Box
|
||||
color={'myGray.600'}
|
||||
display={'inline-flex'}
|
||||
whiteSpace={'nowrap'}
|
||||
{...(canPreview
|
||||
? {
|
||||
cursor: 'pointer',
|
||||
textDecoration: 'underline',
|
||||
onClick: async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
await getFileAndOpen(sourceId as string);
|
||||
} catch (error) {
|
||||
toast({
|
||||
title: t(getErrText(error, 'error.fileNotFound')),
|
||||
status: 'error'
|
||||
});
|
||||
}
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
: {})}
|
||||
{...props}
|
||||
>
|
||||
<Image src={icon} alt="" w={['14px', '16px']} mr={2} />
|
||||
<Box
|
||||
maxW={['200px', '300px']}
|
||||
className={props.className ?? 'textEllipsis'}
|
||||
wordBreak={'break-all'}
|
||||
>
|
||||
{sourceName || t('common.UnKnow Source')}
|
||||
</Box>
|
||||
</Box>
|
||||
</MyTooltip>
|
||||
);
|
||||
};
|
||||
|
||||
export default RawSourceBox;
|
@@ -38,7 +38,7 @@ const DatasetSelectContainer = ({
|
||||
parentId: path.parentId,
|
||||
parentName: path.parentName
|
||||
}))}
|
||||
FirstPathDom={t('chat.Select Mark Kb')}
|
||||
FirstPathDom={t('core.chat.Select Mark Kb')}
|
||||
onClick={(e) => {
|
||||
setParentId(e);
|
||||
}}
|
||||
|
@@ -12,6 +12,7 @@ import MyTooltip from '@/components/MyTooltip';
|
||||
import Avatar from '@/components/Avatar';
|
||||
import { postCreateTeam, putUpdateTeam } from '@/web/support/user/team/api';
|
||||
import { CreateTeamProps } from '@fastgpt/global/support/user/team/controller.d';
|
||||
import { MongoImageTypeEnum } from '@fastgpt/global/common/file/image/constants';
|
||||
|
||||
export type FormDataType = CreateTeamProps & {
|
||||
id?: string;
|
||||
@@ -50,6 +51,7 @@ function EditModal({
|
||||
if (!file) return;
|
||||
try {
|
||||
const src = await compressImgFileAndUpload({
|
||||
type: MongoImageTypeEnum.teamAvatar,
|
||||
file,
|
||||
maxW: 300,
|
||||
maxH: 300
|
||||
|
@@ -17,14 +17,6 @@ import Markdown from '@/components/Markdown';
|
||||
|
||||
const Price = ({ onClose }: { onClose: () => void }) => {
|
||||
const list = [
|
||||
{
|
||||
title: '知识库存储',
|
||||
describe: '',
|
||||
md: `
|
||||
| 计费项 | 价格(¥) |
|
||||
| --- | --- |
|
||||
| 知识库索引数量 | 0/1000条/天 |`
|
||||
},
|
||||
{
|
||||
title: '对话模型',
|
||||
describe: '',
|
||||
|
171
projects/app/src/components/support/wallet/SubDatasetModal.tsx
Normal file
171
projects/app/src/components/support/wallet/SubDatasetModal.tsx
Normal file
@@ -0,0 +1,171 @@
|
||||
import React, { useState } from 'react';
|
||||
|
||||
import MyModal from '@/components/MyModal';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import {
|
||||
Box,
|
||||
Flex,
|
||||
ModalBody,
|
||||
NumberInput,
|
||||
NumberInputField,
|
||||
NumberInputStepper,
|
||||
NumberIncrementStepper,
|
||||
NumberDecrementStepper,
|
||||
ModalFooter,
|
||||
Button
|
||||
} from '@chakra-ui/react';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { getTeamDatasetValidSub, postExpandTeamDatasetSub } from '@/web/support/wallet/sub/api';
|
||||
import Markdown from '@/components/Markdown';
|
||||
import MyTooltip from '@/components/MyTooltip';
|
||||
import { QuestionOutlineIcon } from '@chakra-ui/icons';
|
||||
import { useConfirm } from '@/web/common/hooks/useConfirm';
|
||||
import { getMonthRemainingDays } from '@fastgpt/global/common/math/date';
|
||||
import { useRequest } from '@/web/common/hooks/useRequest';
|
||||
import { useRouter } from 'next/router';
|
||||
import { feConfigs } from '@/web/common/system/staticData';
|
||||
import { useToast } from '@/web/common/hooks/useToast';
|
||||
import { formatTime2YMDHM } from '@fastgpt/global/common/string/time';
|
||||
import MySelect from '@/components/Select';
|
||||
|
||||
const SubDatasetModal = ({ onClose }: { onClose: () => void }) => {
|
||||
const datasetStoreFreeSize = feConfigs?.subscription?.datasetStoreFreeSize || 0;
|
||||
const datasetStorePrice = feConfigs?.subscription?.datasetStorePrice || 0;
|
||||
|
||||
const { t } = useTranslation();
|
||||
const { toast } = useToast();
|
||||
const router = useRouter();
|
||||
const { ConfirmModal, openConfirm } = useConfirm({});
|
||||
const [datasetSize, setDatasetSize] = useState(0);
|
||||
const [isRenew, setIsRenew] = useState('false');
|
||||
|
||||
const isExpand = datasetSize > 0;
|
||||
|
||||
const { data: datasetSub } = useQuery(['getTeamDatasetValidSub'], getTeamDatasetValidSub, {
|
||||
onSuccess(res) {
|
||||
setIsRenew(`${res?.sub?.renew}`);
|
||||
}
|
||||
});
|
||||
|
||||
const { mutate, isLoading } = useRequest({
|
||||
mutationFn: () => postExpandTeamDatasetSub({ size: datasetSize, renew: isRenew === 'true' }),
|
||||
onSuccess(res) {
|
||||
if (isExpand) {
|
||||
router.reload();
|
||||
} else {
|
||||
onClose();
|
||||
}
|
||||
},
|
||||
successToast: isExpand ? t('support.wallet.Pay success') : t('common.Update success'),
|
||||
errorToast: isExpand ? t('support.wallet.Pay error') : t('common.error.Update error')
|
||||
});
|
||||
|
||||
return (
|
||||
<MyModal
|
||||
isOpen
|
||||
iconSrc="/imgs/module/db.png"
|
||||
title={t('support.wallet.subscription.Dataset store')}
|
||||
>
|
||||
<ModalBody>
|
||||
<>
|
||||
<Flex alignItems={'center'}>
|
||||
{t('support.user.Price')}
|
||||
<MyTooltip label={t('support.wallet.subscription.Dataset store price tip')}>
|
||||
<QuestionOutlineIcon ml={1} />
|
||||
</MyTooltip>
|
||||
</Flex>
|
||||
<Markdown
|
||||
source={`
|
||||
| 免费知识库 | ${datasetStoreFreeSize}条 |
|
||||
| --- | --- |
|
||||
| 额外知识库 | ${datasetStorePrice}元/1000条/月 |
|
||||
`}
|
||||
/>
|
||||
</>
|
||||
<Flex mt={4}>
|
||||
<Box w={'100px'}>{t('support.wallet.subscription.Current dataset store')}: </Box>
|
||||
<Box ml={2} fontWeight={'bold'} flex={1}>
|
||||
{datasetSub?.sub?.datasetStoreAmount || 0}
|
||||
{t('core.dataset.data.unit')}
|
||||
</Box>
|
||||
</Flex>
|
||||
{datasetSub?.sub?.expiredTime && (
|
||||
<Flex mt={3}>
|
||||
<Box w={'100px'}>到期时间: </Box>
|
||||
<Box ml={2}>{formatTime2YMDHM(datasetSub?.sub?.expiredTime)}</Box>
|
||||
</Flex>
|
||||
)}
|
||||
|
||||
<Flex mt={3} alignItems={'center'}>
|
||||
<Box w={'100px'}>是否续订: </Box>
|
||||
<MySelect
|
||||
ml={2}
|
||||
value={isRenew}
|
||||
size={'sm'}
|
||||
w={'150px'}
|
||||
list={[
|
||||
{ label: '自动续费', value: 'true' },
|
||||
{ label: '不自动续费', value: 'false' }
|
||||
]}
|
||||
onchange={setIsRenew}
|
||||
/>
|
||||
</Flex>
|
||||
<Box mt={4}>
|
||||
<Box>{t('support.wallet.subscription.Expand size')}</Box>
|
||||
<Flex alignItems={'center'} mt={1}>
|
||||
<NumberInput
|
||||
flex={1}
|
||||
min={0}
|
||||
step={1}
|
||||
value={datasetSize}
|
||||
position={'relative'}
|
||||
onChange={(e) => {
|
||||
setDatasetSize(Number(e));
|
||||
}}
|
||||
>
|
||||
<NumberInputField value={datasetSize} step={1} min={0} />
|
||||
<NumberInputStepper>
|
||||
<NumberIncrementStepper />
|
||||
<NumberDecrementStepper />
|
||||
</NumberInputStepper>
|
||||
</NumberInput>
|
||||
<Box ml={2}>000{t('core.dataset.data.unit')}</Box>
|
||||
</Flex>
|
||||
</Box>
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button mr={3} variant={'whiteBase'} onClick={onClose}>
|
||||
{t('common.Close')}
|
||||
</Button>
|
||||
<Button
|
||||
isLoading={isLoading}
|
||||
onClick={() => {
|
||||
if (isExpand) {
|
||||
const currentMonthPrice = (
|
||||
datasetSize *
|
||||
datasetStorePrice *
|
||||
(getMonthRemainingDays() / 30)
|
||||
).toFixed(2);
|
||||
const totalSize = (datasetSub?.sub?.datasetStoreAmount || 0) / 1000 + datasetSize;
|
||||
openConfirm(
|
||||
mutate,
|
||||
undefined,
|
||||
`本次扩容预估扣除 ${currentMonthPrice} 元。次月起,每月 1 号将会扣除 ${
|
||||
totalSize * datasetStorePrice
|
||||
} 元(共${totalSize * 1000}条)。请确保账号余额充足。`
|
||||
)();
|
||||
} else {
|
||||
mutate('');
|
||||
}
|
||||
}}
|
||||
>
|
||||
{t('common.Confirm')}
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
|
||||
<ConfirmModal />
|
||||
</MyModal>
|
||||
);
|
||||
};
|
||||
|
||||
export default SubDatasetModal;
|
Reference in New Issue
Block a user