V4.9.7 feature (#4669)

* update doc

* feat: Add coupon redemption feature for team subscriptions (#4595)

* feat: Add coupon redemption feature for team subscriptions

- Introduced `TeamCouponSub` and `TeamCouponSchema` types
- Added `redeemCoupon` API endpoint
- Updated UI to include a modal for coupon redemption
- Added new icon and translations for "Redeem coupon"

* perf: remove field teamId

* perf: use dynamic import

* refactor: move to page component

* perf: coupon code

* perf: mcp server

* perf: test

* auto layout (#4634)

* fix 4.9.6 (#4631)

* fix debug quote list

* delete next text node match

* fix extract default boolean value

* export latest 100 chat items

* fix quote item ui

* doc

* fix doc

* feat: auto layout

* perf: auto layout

* fix: auto layout null

* add start node

---------

Co-authored-by: heheer <heheer@sealos.io>

* fix: share link (#4644)

* Add workflow run duration;Get audio duration (#4645)

* add duration

* get audio duration

* Custom config path (#4649)

* feat: 通过环境变量DATA_PATH获取配置文件目录 (#4622)

通过环境变量DATA_PATH获取配置文件目录,以应对不同的部署方式的多样化需求

* feat: custom configjson path

* doc

---------

Co-authored-by: John Chen <sss1991@163.com>

* 程序api调用场景下,如果大量调用带有图片或视频,产生的聊天记录会导致后台mongo数据库异常。这个修改给api客户端一个禁止生成聊天记录的选项,避免这个后果。 (#3964)

* update special chatId

* perf: vector db rename

* update operationLog (#4647)

* update operationLog

* combine operationLogMap

* solve operationI18nLogMap bug

* remoce log

* feat: Rerank usage (#4654)

* refresh concat when update (#4655)

* fix: refresh code

* perf: timer lock

* Fix operationLog (#4657)

* perf: http streamable mcp

* add alipay (#4630)

* perf: subplan ui

* perf: pay code

* hiden bank tip

* Fix: pay error (#4665)

* fix quote number (#4666)

* remove log

---------

Co-authored-by: a.e. <49438478+I-Info@users.noreply.github.com>
Co-authored-by: heheer <heheer@sealos.io>
Co-authored-by: John Chen <sss1991@163.com>
Co-authored-by: gaord <bengao168@msn.com>
Co-authored-by: gggaaallleee <91131304+gggaaallleee@users.noreply.github.com>
This commit is contained in:
Archer
2025-04-26 16:17:21 +08:00
committed by GitHub
parent a669a60fe6
commit 0720bbe4da
143 changed files with 2067 additions and 1093 deletions

View File

@@ -20,13 +20,14 @@ import { useToast } from '@fastgpt/web/hooks/useToast';
import MyIcon from '@fastgpt/web/components/common/Icon';
import { useTranslation } from 'next-i18next';
import {
BillStatusEnum,
BillTypeEnum,
billPayWayMap,
billStatusMap,
billTypeMap
} from '@fastgpt/global/support/wallet/bill/constants';
import MyBox from '@fastgpt/web/components/common/MyBox';
import { useRequest } from '@fastgpt/web/hooks/useRequest';
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
import { standardSubLevelMap, subModeMap } from '@fastgpt/global/support/wallet/sub/constants';
import MySelect from '@fastgpt/web/components/common/MySelect';
import MyModal from '@fastgpt/web/components/common/MyModal';
@@ -68,39 +69,33 @@ const BillTable = () => {
defaultRequest: false
});
const { mutate: handleRefreshPayOrder, isLoading: isRefreshing } = useRequest({
mutationFn: async (payId: string) => {
try {
const data = await checkBalancePayResult(payId);
const { runAsync: handleRefreshPayOrder, loading: isRefreshing } = useRequest2(
async (payId: string) => {
const { status, description } = await checkBalancePayResult(payId);
if (status === BillStatusEnum.SUCCESS) {
toast({
title: data,
title: t('common:pay_success'),
status: 'success'
});
} catch (error: any) {
} else {
toast({
title: error?.message,
title: t(description as any),
status: 'warning'
});
console.log(error);
}
try {
if (status === BillStatusEnum.SUCCESS || status === BillStatusEnum.CLOSED) {
getData(1);
} catch (error) {}
}
}
});
);
useEffect(() => {
getData(1);
}, [billType]);
return (
<MyBox
isLoading={isLoading || isRefreshing}
position={'relative'}
h={'100%'}
minH={'50vh'}
overflow={'overlay'}
>
<MyBox isLoading={isLoading} position={'relative'} h={'100%'} minH={'50vh'}>
<TableContainer>
<Table>
<Thead>
@@ -135,7 +130,12 @@ const BillTable = () => {
<Td>{t(billStatusMap[item.status]?.label as any)}</Td>
<Td>
{item.status === 'NOTPAY' && (
<Button mr={4} onClick={() => handleRefreshPayOrder(item._id)} size={'sm'}>
<Button
isLoading={isRefreshing}
mr={4}
onClick={() => handleRefreshPayOrder(item._id)}
size={'sm'}
>
{t('account_bill:update')}
</Button>
)}
@@ -210,11 +210,13 @@ function BillDetailModal({ bill, onClose }: { bill: BillSchemaType; onClose: ()
<Box>{t(billPayWayMap[bill.metadata.payWay]?.label as any)}</Box>
</Flex>
)}
<Flex alignItems={'center'} pb={4}>
<FormLabel flex={'0 0 120px'}>{t('account_bill:support_wallet_amount')}:</FormLabel>
<Box>{t('account_bill:yuan', { amount: formatStorePrice2Read(bill.price) })}</Box>
</Flex>
{bill.metadata && (
{!!bill.price && (
<Flex alignItems={'center'} pb={4}>
<FormLabel flex={'0 0 120px'}>{t('account_bill:support_wallet_amount')}:</FormLabel>
<Box>{t('account_bill:yuan', { amount: formatStorePrice2Read(bill.price) })}</Box>
</Flex>
)}
{bill.metadata && !!bill.price && (
<Flex alignItems={'center'} pb={4}>
<FormLabel flex={'0 0 120px'}>{t('account_bill:has_invoice')}:</FormLabel>
{bill.metadata.payWay === 'balance' ? (
@@ -239,7 +241,7 @@ function BillDetailModal({ bill, onClose }: { bill: BillSchemaType; onClose: ()
{bill.metadata?.month !== undefined && (
<Flex alignItems={'center'} pb={4}>
<FormLabel flex={'0 0 120px'}>{t('account_bill:subscription_mode_month')}:</FormLabel>
<Box>{bill.metadata?.month}</Box>
<Box>{`${bill.metadata?.month} ${t('account_bill:month')}`}</Box>
</Flex>
)}
{bill.metadata?.datasetSize !== undefined && (

View File

@@ -221,7 +221,7 @@ const InvoiceHeaderForm = () => {
return (
<>
<MyBox isLoading={isLoading} pt={['1rem', '3.5rem']}>
<MyBox isLoading={isLoading} pt={'1rem'}>
<Flex w={'100%'} overflow={'auto'} justify={'center'} flexDir={'column'} align={'center'}>
<InvoiceHeaderSingleForm inputForm={inputForm} />
<Flex w={'100%'} justify={'center'} mt={'3rem'}>

View File

@@ -22,6 +22,7 @@ import MyIcon from '@fastgpt/web/components/common/Icon';
import dayjs from 'dayjs';
import { formatStorePrice2Read } from '@fastgpt/global/support/wallet/usage/tools';
import MyModal from '@fastgpt/web/components/common/MyModal';
const InvoiceTable = () => {
const { t } = useTranslation();
const [invoiceDetailData, setInvoiceDetailData] = useState<InvoiceSchemaType | ''>('');

View File

@@ -0,0 +1,57 @@
import { redeemCoupon } from '@/web/support/user/team/api';
import { Button, Input, VStack, Text, ModalBody, Box, ModalFooter } from '@chakra-ui/react';
import MyModal from '@fastgpt/web/components/common/MyModal';
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
import React from 'react';
import { useTranslation } from 'react-i18next';
const RedeemCouponModal = ({
onClose,
onSuccess
}: {
onClose: () => void;
onSuccess: () => void;
}) => {
const { t } = useTranslation();
const [couponCode, setCouponCode] = React.useState('');
const { runAsync: redeemCouponAsync, loading } = useRequest2(redeemCoupon, {
manual: true,
onSuccess: () => {
onSuccess();
onClose();
},
successToast: t('common:common.Success')
});
return (
<MyModal
isOpen
onClose={onClose}
iconSrc="support/account/coupon"
title={t('account_info:redeem_coupon')}
>
<ModalBody>
<Box fontWeight={500} color={'myGray.900'} mb={'1'}>
{t('account_info:redeem_coupon')}
</Box>
<Input
placeholder={t('account_info:redeem_coupon')}
value={couponCode}
onChange={(e) => setCouponCode(e.target.value)}
/>
</ModalBody>
<ModalFooter>
<Button variant={'whiteBase'} onClick={onClose}>
{t('account_info:cancel')}
</Button>
<Button ml={2} isLoading={loading} onClick={() => redeemCouponAsync(couponCode)}>
{t('account_info:confirm')}
</Button>
</ModalFooter>
</MyModal>
);
};
export default RedeemCouponModal;

View File

@@ -127,11 +127,11 @@ export const ModelEditModal = ({
);
const priceUnit = useMemo(() => {
if (isLLMModel || isEmbeddingModel) return '/ 1k Tokens';
if (isLLMModel || isEmbeddingModel || isRerankModel) return '/ 1k Tokens';
if (isTTSModel) return `/ 1k ${t('common:unit.character')}`;
if (isSTTModel) return `/ 60 ${t('common:unit.seconds')}`;
return '';
}, [isLLMModel, isEmbeddingModel, isTTSModel, t, isSTTModel]);
}, [isLLMModel, isEmbeddingModel, isTTSModel, t, isSTTModel, isRerankModel]);
const { runAsync: updateModel, loading: updatingModel } = useRequest2(
async (data: SystemModelItemType) => {

View File

@@ -141,6 +141,7 @@ const ModelTable = ({ Tab }: { Tab: React.ReactNode }) => {
typeLabel: t('common:model.type.embedding'),
priceLabel: (
<Flex color={'myGray.700'}>
{`${t('common:common.Input')}: `}
<Box fontWeight={'bold'} color={'myGray.900'} mr={0.5}>
{item.charsPointsPrice || 0}
</Box>
@@ -184,7 +185,17 @@ const ModelTable = ({ Tab }: { Tab: React.ReactNode }) => {
.map((item) => ({
...item,
typeLabel: t('common:model.type.reRank'),
priceLabel: <Flex color={'myGray.700'}>- </Flex>,
priceLabel: item.charsPointsPrice ? (
<Flex color={'myGray.700'}>
{`${t('common:common.Input')}: `}
<Box fontWeight={'bold'} color={'myGray.900'} mr={0.5}>
{item.charsPointsPrice}
</Box>
{` ${t('common:support.wallet.subscription.point')} / 1K Tokens`}
</Flex>
) : (
'-'
),
tagColor: 'red'
}));

View File

@@ -190,8 +190,9 @@ function EditModal({
/>
{isOpenContact && (
<UpdateContact
onClose={() => {
onCloseContact();
onClose={onCloseContact}
onSuccess={(val) => {
setValue('notificationAccount', val);
}}
mode="notification_account"
/>

View File

@@ -17,7 +17,7 @@ import MyBox from '@fastgpt/web/components/common/MyBox';
import { useScrollPagination } from '@fastgpt/web/hooks/useScrollPagination';
import { getOperationLogs } from '@/web/support/user/team/operantionLog/api';
import { TeamPermission } from '@fastgpt/global/support/permission/user/controller';
import { operationLogI18nMap } from '@fastgpt/service/support/operationLog/constants';
import { operationLogMap } from '@fastgpt/service/support/operationLog/constants';
import { OperationLogEventEnum } from '@fastgpt/global/support/operationLog/constants';
import { formatTime2YMDHMS } from '@fastgpt/global/common/string/time';
import UserBox from '@fastgpt/web/components/common/UserBox';
@@ -52,7 +52,7 @@ function OperationLogTable({ Tabs }: { Tabs: React.ReactNode }) {
const eventOptions = useMemo(
() =>
Object.values(OperationLogEventEnum).map((event) => ({
label: t(operationLogI18nMap[event].typeLabel),
label: t(operationLogMap[event].typeLabel),
value: event
})),
[t]
@@ -158,7 +158,7 @@ function OperationLogTable({ Tabs }: { Tabs: React.ReactNode }) {
</Thead>
<Tbody>
{operationLogs?.map((log) => {
const i18nData = operationLogI18nMap[log.event];
const i18nData = operationLogMap[log.event];
const metadata = { ...log.metadata };
if (log.event === OperationLogEventEnum.ASSIGN_PERMISSION) {