Commit 941549ff by archer

perf: user info framwork

parent 1f170e1c
...@@ -62,9 +62,22 @@ jobs: ...@@ -62,9 +62,22 @@ jobs:
with: with:
username: ${{ secrets.DOCKER_HUB_NAME }} username: ${{ secrets.DOCKER_HUB_NAME }}
password: ${{ secrets.DOCKER_HUB_PASSWORD }} 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 - name: Pull image from GitHub Container Registry
run: docker pull ghcr.io/${{ github.repository_owner }}/fastgpt:${{ github.ref_name }} env:
IMAGE_TAG: ${{ env.IMAGE_TAG }}
run: docker pull ghcr.io/${{ github.repository_owner }}/fastgpt:${ IMAGE_TAG }
- name: Tag image with Docker Hub repository name and version tag - name: Tag image with Docker Hub repository name and version tag
run: docker tag ghcr.io/${{ github.repository_owner }}/fastgpt:${{ github.ref_name }} ${{ secrets.DOCKER_IMAGE_NAME }}:${{ github.ref_name }} env:
IMAGE_TAG: ${{ env.IMAGE_TAG }}
run: docker tag ghcr.io/${{ github.repository_owner }}/fastgpt:${ IMAGE_TAG } ${{ secrets.DOCKER_IMAGE_NAME }}:${ IMAGE_TAG }
- name: Push image to Docker Hub - name: Push image to Docker Hub
run: docker push ${{ secrets.DOCKER_IMAGE_NAME }}:${{ github.ref_name }} env:
IMAGE_TAG: ${{ env.IMAGE_TAG }}
run: docker push ${{ secrets.DOCKER_IMAGE_NAME }}:${ IMAGE_TAG }
...@@ -19,6 +19,7 @@ services: ...@@ -19,6 +19,7 @@ services:
- /etc/localtime:/etc/localtime:ro - /etc/localtime:/etc/localtime:ro
mongodb: mongodb:
image: mongo:5.0.18 image: mongo:5.0.18
# image : registry.cn-hangzhou.aliyuncs.com/fastgpt/mongo:5.0.18 # 阿里云
container_name: mongo container_name: mongo
restart: always restart: always
ports: ports:
......
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1683254589346" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1074" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><path d="M446.5084032 361.0189472m-220.5069312 0a215.4848 215.4848 0 1 0 441.01386347 0 215.4848 215.4848 0 1 0-441.01386347 0ZM620.04694507 784.5116256c0-68.92772267 33.63539307-129.9888128 85.37702826-167.69996587 0.03602027-0.02619627 0.03820373-0.08077333 0.00218347-0.1080608-26.91486293-21.45832213-56.54544427-39.6551616-88.3045024-53.96834986-0.02401387-0.0109152-0.0523936-0.00654933-0.07204053 0.009824-46.01985387 38.94457813-105.5320672 62.44187093-170.5412096 62.44187093s-124.52026453-23.4962016-170.5412096-62.44187093c-0.019648-0.01746453-0.04802667-0.0207392-0.07204054-0.009824C143.28449387 622.50178773 47.66130453 749.79343893 32.841648 900.79959573c-0.00436587 0.04038613 0.02837973 0.0753152 0.06876587 0.0753152l622.71265706 0c0.054576 0 0.0873216-0.06003413 0.05566827-0.1058784C633.19001707 867.60954667 620.04694507 827.59745493 620.04694507 784.5116256zM827.43700053 620.7826336c-90.42533867 0-163.728992 73.30365227-163.728992 163.728992s73.30365227 163.728992 163.728992 163.728992 163.728992-73.30365227 163.728992-163.728992S917.86234027 620.7826336 827.43700053 620.7826336zM911.96263893 808.52521067L851.51935253 808.52521067c-0.03820373 0-0.06876587 0.0305632-0.06876586 0.06876586l0 60.4432864c0 12.48597333-9.2812512 23.34448-21.71155627 24.51896214-14.23023253 1.34476053-26.31561493-9.93725867-26.31561493-23.90443307l0-61.05672427c0-0.03820373-0.0305632-0.06876587-0.06876587-0.06876586L742.9124544 808.52630187c-12.48597333 0-23.34448-9.2812512-24.51896213-21.71155627-1.34476053-14.23023253 9.93725867-26.31561493 23.90443306-26.31561493l61.05672427 0c0.03820373 0 0.06876587-0.0305632 0.06876587-0.06876587l0-60.4432864c0-12.48597333 9.2812512-23.34448 21.71155626-24.51896213 14.23023253-1.34476053 26.31561493 9.93725867 26.31561494 23.90443306l0 61.05672427c0 0.03820373 0.0305632 0.06876587 0.06876586 0.06876587l61.05672427 0c13.9671744 0 25.2491936 12.0853824 23.90443307 26.31561493C935.30602667 799.24396053 924.44752 808.52521067 911.96263893 808.52521067z" p-id="1075"></path></svg>
\ No newline at end of file
...@@ -8,7 +8,6 @@ const map = { ...@@ -8,7 +8,6 @@ const map = {
chatSend: require('./icons/chatSend.svg').default, chatSend: require('./icons/chatSend.svg').default,
develop: require('./icons/develop.svg').default, develop: require('./icons/develop.svg').default,
user: require('./icons/user.svg').default, user: require('./icons/user.svg').default,
promotion: require('./icons/promotion.svg').default,
delete: require('./icons/delete.svg').default, delete: require('./icons/delete.svg').default,
withdraw: require('./icons/withdraw.svg').default, withdraw: require('./icons/withdraw.svg').default,
stop: require('./icons/stop.svg').default, stop: require('./icons/stop.svg').default,
......
...@@ -43,12 +43,6 @@ const Navbar = () => { ...@@ -43,12 +43,6 @@ const Navbar = () => {
activeLink: ['/model/share'] activeLink: ['/model/share']
}, },
{ {
label: '邀请',
icon: 'promotion',
link: '/promotion',
activeLink: ['/promotion']
},
{
label: '开发', label: '开发',
icon: 'develop', icon: 'develop',
link: '/openapi', link: '/openapi',
......
...@@ -9,7 +9,6 @@ import { ...@@ -9,7 +9,6 @@ import {
ModalFooter, ModalFooter,
ModalBody, ModalBody,
ModalCloseButton, ModalCloseButton,
useColorModeValue,
Image Image
} from '@chakra-ui/react'; } from '@chakra-ui/react';
...@@ -18,7 +17,7 @@ const WxConcat = ({ onClose }: { onClose: () => void }) => { ...@@ -18,7 +17,7 @@ const WxConcat = ({ onClose }: { onClose: () => void }) => {
<Modal isOpen={true} onClose={onClose}> <Modal isOpen={true} onClose={onClose}>
<ModalOverlay /> <ModalOverlay />
<ModalContent> <ModalContent>
<ModalHeader>wx交流群</ModalHeader> <ModalHeader>联系方式-wx</ModalHeader>
<ModalCloseButton /> <ModalCloseButton />
<ModalBody textAlign={'center'}> <ModalBody textAlign={'center'}>
<Image <Image
...@@ -28,12 +27,6 @@ const WxConcat = ({ onClose }: { onClose: () => void }) => { ...@@ -28,12 +27,6 @@ const WxConcat = ({ onClose }: { onClose: () => void }) => {
height={'200px'} height={'200px'}
alt="" alt=""
/> />
<Box mt={2}>
微信号:
<Box as={'span'} userSelect={'all'}>
YNyiqi
</Box>
</Box>
</ModalBody> </ModalBody>
<ModalFooter> <ModalFooter>
......
...@@ -43,6 +43,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< ...@@ -43,6 +43,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
PgClient.select<KbDataItemType>('modelData', { PgClient.select<KbDataItemType>('modelData', {
fields: ['id', 'q', 'a', 'source'], fields: ['id', 'q', 'a', 'source'],
where, where,
order: [{ field: 'id', mode: 'DESC' }],
limit: pageSize, limit: pageSize,
offset: pageSize * (pageNum - 1) offset: pageSize * (pageNum - 1)
}), }),
......
...@@ -17,7 +17,7 @@ const Login = () => { ...@@ -17,7 +17,7 @@ const Login = () => {
const { lastRoute = '' } = router.query as { lastRoute: string }; const { lastRoute = '' } = router.query as { lastRoute: string };
const { isPc } = useGlobalStore(); const { isPc } = useGlobalStore();
const [pageType, setPageType] = useState<`${PageTypeEnum}`>(PageTypeEnum.login); const [pageType, setPageType] = useState<`${PageTypeEnum}`>(PageTypeEnum.login);
const { setUserInfo, setLastModelId, loadMyModels } = useUserStore(); const { setUserInfo, setLastModelId, loadMyModels, loadKbList, setLastKbId } = useUserStore();
const { setLastChatId, setLastChatModelId, loadHistory } = useChatStore(); const { setLastChatId, setLastChatModelId, loadHistory } = useChatStore();
const loginSuccess = useCallback( const loginSuccess = useCallback(
...@@ -26,7 +26,9 @@ const Login = () => { ...@@ -26,7 +26,9 @@ const Login = () => {
setLastChatId(''); setLastChatId('');
setLastModelId(''); setLastModelId('');
setLastChatModelId(''); setLastChatModelId('');
setLastKbId('');
loadMyModels(true); loadMyModels(true);
loadKbList(true);
loadHistory({ pageNum: 1, init: true }); loadHistory({ pageNum: 1, init: true });
setUserInfo(res.user); setUserInfo(res.user);
...@@ -37,10 +39,12 @@ const Login = () => { ...@@ -37,10 +39,12 @@ const Login = () => {
[ [
lastRoute, lastRoute,
loadHistory, loadHistory,
loadKbList,
loadMyModels, loadMyModels,
router, router,
setLastChatId, setLastChatId,
setLastChatModelId, setLastChatModelId,
setLastKbId,
setLastModelId, setLastModelId,
setUserInfo setUserInfo
] ]
......
import React from 'react'; import React from 'react';
import { Card, Box, Table, Thead, Tbody, Tr, Th, Td, TableContainer, Flex } from '@chakra-ui/react'; import { Table, Thead, Tbody, Tr, Th, Td, TableContainer, Flex } from '@chakra-ui/react';
import { BillTypeMap } from '@/constants/user'; import { BillTypeMap } from '@/constants/user';
import { getUserBills } from '@/api/user'; import { getUserBills } from '@/api/user';
import type { UserBillType } from '@/types/user'; import type { UserBillType } from '@/types/user';
...@@ -19,11 +19,8 @@ const BillTable = () => { ...@@ -19,11 +19,8 @@ const BillTable = () => {
}); });
return ( return (
<Card mt={6} py={4}> <>
<Box fontSize={'xl'} fontWeight={'bold'} px={6} mb={1}> <TableContainer position={'relative'} minH={'200px'}>
使用记录
</Box>
<TableContainer position={'relative'}>
<Table> <Table>
<Thead> <Thead>
<Tr> <Tr>
...@@ -51,10 +48,10 @@ const BillTable = () => { ...@@ -51,10 +48,10 @@ const BillTable = () => {
<Loading loading={isLoading} fixed={false} /> <Loading loading={isLoading} fixed={false} />
</TableContainer> </TableContainer>
<Flex mt={4} px={4} justifyContent={'flex-end'}> <Flex w={'100%'} mt={4} justifyContent={'flex-end'}>
<Pagination /> <Pagination />
</Flex> </Flex>
</Card> </>
); );
}; };
......
import React, { useState, useCallback } from 'react'; import React, { useState, useCallback } from 'react';
import { import { Button, Table, Thead, Tbody, Tr, Th, Td, TableContainer } from '@chakra-ui/react';
Card,
Box,
Flex,
Button,
Table,
Thead,
Tbody,
Tr,
Th,
Td,
TableContainer,
useDisclosure
} from '@chakra-ui/react';
import { getPayOrders, checkPayResult } from '@/api/user'; import { getPayOrders, checkPayResult } from '@/api/user';
import { PaySchema } from '@/types/mongoSchema'; import { PaySchema } from '@/types/mongoSchema';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import { useQuery } from '@tanstack/react-query'; import { useQuery } from '@tanstack/react-query';
import { formatPrice } from '@/utils/user'; import { formatPrice } from '@/utils/user';
import WxConcat from '@/components/WxConcat';
import { useGlobalStore } from '@/store/global'; import { useGlobalStore } from '@/store/global';
import { useToast } from '@/hooks/useToast'; import { useToast } from '@/hooks/useToast';
const PayRecordTable = () => { const PayRecordTable = () => {
const { isOpen: isOpenWx, onOpen: onOpenWx, onClose: onCloseWx } = useDisclosure();
const [payOrders, setPayOrders] = useState<PaySchema[]>([]); const [payOrders, setPayOrders] = useState<PaySchema[]>([]);
const { setLoading } = useGlobalStore(); const { setLoading } = useGlobalStore();
const { toast } = useToast(); const { toast } = useToast();
...@@ -60,52 +45,38 @@ const PayRecordTable = () => { ...@@ -60,52 +45,38 @@ const PayRecordTable = () => {
}); });
return ( return (
<> <TableContainer>
<Card mt={6} py={4}> <Table>
<Flex alignItems={'flex-end'} px={6} mb={1}> <Thead>
<Box fontSize={'xl'} fontWeight={'bold'}> <Tr>
充值记录 <Th>订单号</Th>
</Box> <Th>时间</Th>
<Button onClick={onOpenWx} size={'xs'} ml={4} variant={'outline'}> <Th>金额</Th>
异常问题,wx联系 <Th>状态</Th>
</Button> <Th></Th>
</Flex> </Tr>
<TableContainer px={6}> </Thead>
<Table> <Tbody fontSize={'sm'}>
<Thead> {payOrders.map((item) => (
<Tr> <Tr key={item._id}>
<Th>订单号</Th> <Td>{item.orderId}</Td>
<Th>时间</Th> <Td>
<Th>金额</Th> {item.createTime ? dayjs(item.createTime).format('YYYY/MM/DD HH:mm:ss') : '-'}
<Th>状态</Th> </Td>
<Th></Th> <Td>{formatPrice(item.price)}元</Td>
</Tr> <Td>{item.status}</Td>
</Thead> <Td>
<Tbody fontSize={'sm'}> {item.status === 'NOTPAY' && (
{payOrders.map((item) => ( <Button onClick={() => handleRefreshPayOrder(item._id)} size={'sm'}>
<Tr key={item._id}> 更新
<Td>{item.orderId}</Td> </Button>
<Td> )}
{item.createTime ? dayjs(item.createTime).format('YYYY/MM/DD HH:mm:ss') : '-'} </Td>
</Td> </Tr>
<Td>{formatPrice(item.price)}元</Td> ))}
<Td>{item.status}</Td> </Tbody>
<Td> </Table>
{item.status === 'NOTPAY' && ( </TableContainer>
<Button onClick={() => handleRefreshPayOrder(item._id)} size={'sm'}>
更新
</Button>
)}
</Td>
</Tr>
))}
</Tbody>
</Table>
</TableContainer>
</Card>
{/* wx 联系 */}
{isOpenWx && <WxConcat onClose={onCloseWx} />}
</>
); );
}; };
......
import React from 'react';
import { Flex, Table, Thead, Tbody, Tr, Th, Td, TableContainer } from '@chakra-ui/react';
import { useLoading } from '@/hooks/useLoading';
import dayjs from 'dayjs';
import { getPromotionRecords } from '@/api/user';
import { usePagination } from '@/hooks/usePagination';
import { PromotionRecordType } from '@/api/response/user';
import { PromotionTypeMap } from '@/constants/user';
const OpenApi = () => {
const { Loading } = useLoading();
const {
data: promotionRecords,
isLoading,
Pagination
} = usePagination<PromotionRecordType>({
api: getPromotionRecords
});
return (
<>
<TableContainer position={'relative'} overflow={'hidden'} minH={'200px'}>
<Table>
<Thead>
<Tr>
<Th>时间</Th>
<Th>类型</Th>
<Th>金额</Th>
</Tr>
</Thead>
<Tbody fontSize={'sm'}>
{promotionRecords.map((item) => (
<Tr key={item._id}>
<Td>
{item.createTime ? dayjs(item.createTime).format('YYYY/MM/DD HH:mm:ss') : '-'}
</Td>
<Td>{PromotionTypeMap[item.type]}</Td>
<Td>{item.amount}</Td>
</Tr>
))}
</Tbody>
</Table>
<Loading loading={isLoading} fixed={false} />
</TableContainer>
<Flex mt={4} justifyContent={'flex-end'}>
<Pagination />
</Flex>
</>
);
};
export default OpenApi;
import React, { useCallback, useState } from 'react'; import React, { useCallback, useRef, useState } from 'react';
import { Card, Box, Flex, Button, Input } from '@chakra-ui/react'; import {
Card,
Box,
Flex,
Button,
Input,
Grid,
useDisclosure,
Tabs,
TabList,
TabPanels,
Tab,
TabPanel
} from '@chakra-ui/react';
import { useForm } from 'react-hook-form'; import { useForm } from 'react-hook-form';
import { UserUpdateParams } from '@/types/user'; import { UserUpdateParams } from '@/types/user';
import { putUserInfo } from '@/api/user'; import { putUserInfo, getPromotionInitData } from '@/api/user';
import { useToast } from '@/hooks/useToast'; import { useToast } from '@/hooks/useToast';
import { useGlobalStore } from '@/store/global'; import { useGlobalStore } from '@/store/global';
import { useUserStore } from '@/store/user'; import { useUserStore } from '@/store/user';
...@@ -13,8 +26,10 @@ import { useQuery } from '@tanstack/react-query'; ...@@ -13,8 +26,10 @@ import { useQuery } from '@tanstack/react-query';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import { useSelectFile } from '@/hooks/useSelectFile'; import { useSelectFile } from '@/hooks/useSelectFile';
import { compressImg } from '@/utils/file'; import { compressImg } from '@/utils/file';
import { useCopyData } from '@/utils/tools';
import Loading from '@/components/Loading'; import Loading from '@/components/Loading';
import Avatar from '@/components/Avatar'; import Avatar from '@/components/Avatar';
import MyIcon from '@/components/Icon';
const PayRecordTable = dynamic(() => import('./components/PayRecordTable'), { const PayRecordTable = dynamic(() => import('./components/PayRecordTable'), {
loading: () => <Loading fixed={false} />, loading: () => <Loading fixed={false} />,
...@@ -24,20 +39,49 @@ const BilTable = dynamic(() => import('./components/BillTable'), { ...@@ -24,20 +39,49 @@ const BilTable = dynamic(() => import('./components/BillTable'), {
loading: () => <Loading fixed={false} />, loading: () => <Loading fixed={false} />,
ssr: false ssr: false
}); });
const PromotionTable = dynamic(() => import('./components/PromotionTable'), {
loading: () => <Loading fixed={false} />,
ssr: false
});
const PayModal = dynamic(() => import('./components/PayModal'), { const PayModal = dynamic(() => import('./components/PayModal'), {
loading: () => <Loading fixed={false} />, loading: () => <Loading fixed={false} />,
ssr: false ssr: false
}); });
const WxConcat = dynamic(() => import('@/components/WxConcat'), {
loading: () => <Loading fixed={false} />,
ssr: false
});
enum TableEnum {
'bill' = 'bill',
'pay' = 'pay',
'promotion' = 'promotion'
}
const NumberSetting = () => { const NumberSetting = () => {
const tableType = useRef([
{ label: '账单详情', value: TableEnum.bill, Component: BilTable },
{ label: '充值记录', value: TableEnum.pay, Component: PayRecordTable },
{ label: '佣金记录', value: TableEnum.pay, Component: PromotionTable }
]);
const router = useRouter(); const router = useRouter();
const { copyData } = useCopyData();
const { userInfo, updateUserInfo, initUserInfo, setUserInfo } = useUserStore(); const { userInfo, updateUserInfo, initUserInfo, setUserInfo } = useUserStore();
const { setLoading } = useGlobalStore(); const { setLoading } = useGlobalStore();
const { register, handleSubmit, reset } = useForm<UserUpdateParams>({ const { register, handleSubmit, reset } = useForm<UserUpdateParams>({
defaultValues: userInfo as UserType defaultValues: userInfo as UserType
}); });
const [showPay, setShowPay] = useState(false);
const { toast } = useToast(); const { toast } = useToast();
const {
isOpen: isOpenPayModal,
onClose: onClosePayModal,
onOpen: onOpenPayModal
} = useDisclosure();
const {
isOpen: isOpenWxConcat,
onClose: onCloseWxConcat,
onOpen: onOpenWxConcat
} = useDisclosure();
const { File, onOpen: onOpenSelectFile } = useSelectFile({ const { File, onOpen: onOpenSelectFile } = useSelectFile({
fileType: '.jpg,.png', fileType: '.jpg,.png',
...@@ -92,76 +136,137 @@ const NumberSetting = () => { ...@@ -92,76 +136,137 @@ const NumberSetting = () => {
}, [router, setUserInfo]); }, [router, setUserInfo]);
useQuery(['init'], initUserInfo); useQuery(['init'], initUserInfo);
const { data: { invitedAmount = 0, historyAmount = 0, residueAmount = 0 } = {} } = useQuery(
['getPromotionInitData'],
getPromotionInitData
);
return ( return (
<Box py={[5, 10]} px={'5vw'}> <Box py={[5, 10]} px={'5vw'}>
{/* 核心信息 */} <Grid gridTemplateColumns={['1fr', '3fr 300px']} gridGap={4}>
<Card px={6} py={4}> <Card px={6} py={4}>
<Flex justifyContent={'space-between'}> <Flex justifyContent={'space-between'}>
<Box fontSize={'xl'} fontWeight={'bold'}> <Box fontSize={'xl'} fontWeight={'bold'}>
账号信息 账号信息
</Box>
<Button variant={'outline'} size={'xs'} onClick={onclickLogOut}>
退出登录
</Button>
</Flex>
<Flex mt={6} alignItems={'center'}>
<Box flex={'0 0 50px'}>头像:</Box>
<Avatar
src={userInfo?.avatar}
w={['28px', '36px']}
h={['28px', '36px']}
cursor={'pointer'}
title={'点击切换头像'}
onClick={onOpenSelectFile}
/>
</Flex>
<Flex mt={6} alignItems={'center'}>
<Box flex={'0 0 50px'}>账号:</Box>
<Box>{userInfo?.username}</Box>
</Flex>
<Box mt={6}>
<Flex alignItems={'center'}>
<Box flex={'0 0 50px'}>余额:</Box>
<Box>
<strong>{userInfo?.balance}</strong> 元
</Box> </Box>
<Button <Button variant={'outline'} size={'xs'} onClick={onclickLogOut}>
size={['xs', 'sm']} 退出登录
w={['70px', '80px']}
ml={5}
onClick={() => setShowPay(true)}
>
充值
</Button> </Button>
</Flex> </Flex>
<Box fontSize={'xs'} color={'blackAlpha.500'}> <Flex mt={6} alignItems={'center'}>
如果填写了自己的 openai 账号,网页上 openai 模型对话不会计费。 <Box flex={'0 0 50px'}>头像:</Box>
<Avatar
src={userInfo?.avatar}
w={['28px', '36px']}
h={['28px', '36px']}
cursor={'pointer'}
title={'点击切换头像'}
onClick={onOpenSelectFile}
/>
</Flex>
<Flex mt={6} alignItems={'center'}>
<Box flex={'0 0 50px'}>账号:</Box>
<Box>{userInfo?.username}</Box>
</Flex>
<Box mt={6}>
<Flex alignItems={'center'}>
<Box flex={'0 0 50px'}>余额:</Box>
<Box>
<strong>{userInfo?.balance}</strong> 元
</Box>
<Button size={['xs', 'sm']} w={['70px', '80px']} ml={5} onClick={onOpenPayModal}>
充值
</Button>
</Flex>
<Box fontSize={'xs'} color={'blackAlpha.500'}>
如果填写了自己的 openai 账号,网页上 openai 模型对话不会计费。
</Box>
</Box>
<Flex mt={6} alignItems={'center'}>
<Box flex={'0 0 85px'}>openaiKey:</Box>
<Input
{...register(`openaiKey`)}
maxW={'300px'}
placeholder={'openai账号。回车或失去焦点保存'}
size={'sm'}
onBlur={handleSubmit(onclickSave)}
onKeyDown={(e) => {
if (e.keyCode === 13) {
handleSubmit(onclickSave)();
}
}}
></Input>
</Flex>
</Card>
<Card px={6} py={4}>
<Box fontSize={'xl'} fontWeight={'bold'}>
我的邀请
</Box> </Box>
</Box> {[
<Flex mt={6} alignItems={'center'}> { label: '佣金比例', value: `${userInfo?.promotion.rate || 15}%` },
<Box flex={'0 0 85px'}>openaiKey:</Box> { label: '已注册用户数', value: `${invitedAmount}人` },
<Input { label: '累计佣金', value: `¥${historyAmount}` }
{...register(`openaiKey`)} ].map((item) => (
maxW={'300px'} <Flex key={item.label} alignItems={'center'} mt={4} justifyContent={'space-between'}>
placeholder={'openai账号。回车或失去焦点保存'} <Box w={'120px'}>{item.label}</Box>
size={'sm'} <Box fontWeight={'bold'}>{item.value}</Box>
onBlur={handleSubmit(onclickSave)} </Flex>
onKeyDown={(e) => { ))}
if (e.keyCode === 13) { <Button
handleSubmit(onclickSave)(); mt={4}
} variant={'outline'}
}} w={'100%'}
></Input> onClick={() =>
</Flex> copyData(`${location.origin}/?inviterId=${userInfo?._id}`, '已复制邀请链接')
}
>
复制邀请链接
</Button>
<Button
mt={4}
leftIcon={<MyIcon name="withdraw" w={'22px'} />}
px={4}
title={residueAmount < 50 ? '最低提现额度为50元' : ''}
isDisabled={residueAmount < 50}
variant={'outline'}
colorScheme={'myBlue'}
onClick={onOpenWxConcat}
>
提现
</Button>
</Card>
</Grid>
<Card mt={4} px={[3, 6]} py={4}>
<Tabs variant="unstyled" isLazy>
<TabList whiteSpace={'nowrap'}>
{tableType.current.map((item) => (
<Tab
key={item.value}
py={1}
px={3}
borderRadius={'sm'}
mr={1}
transition={'none'}
_selected={{ color: 'white', bg: 'myBlue.600' }}
>
{item.label}
</Tab>
))}
</TabList>
<TabPanels>
{tableType.current.map((Item) => (
<TabPanel minH={'550px'} key={Item.value}>
<Item.Component />
</TabPanel>
))}
</TabPanels>
</Tabs>
</Card> </Card>
{isOpenPayModal && <PayModal onClose={onClosePayModal} />}
{isOpenWxConcat && <WxConcat onClose={onCloseWxConcat} />}
<File onSelect={onSelectFile} /> <File onSelect={onSelectFile} />
{/* 充值记录 */}
<PayRecordTable />
{/* 账单表 */}
<BilTable />
{showPay && <PayModal onClose={() => setShowPay(false)} />}
</Box> </Box>
); );
}; };
......
...@@ -61,6 +61,7 @@ const OpenApi = () => { ...@@ -61,6 +61,7 @@ const OpenApi = () => {
的形式接入到自己的应用中,例如:飞书、企业微信、客服助手。请注意保管你的 Api 的形式接入到自己的应用中,例如:飞书、企业微信、客服助手。请注意保管你的 Api
Key,不要泄露! Key,不要泄露!
</Box> </Box>
<Box>使用 Fast Api 功能仅能使用平台余额。</Box>
<Box <Box
my={1} my={1}
as="a" as="a"
......
import React, { useState } from 'react';
import Link from 'next/link';
import {
Card,
Box,
Button,
Flex,
Table,
Thead,
Tbody,
Tr,
Th,
Td,
TableContainer,
Modal,
ModalOverlay,
ModalContent,
ModalHeader,
ModalBody,
ModalCloseButton,
useColorModeValue,
ModalFooter,
useDisclosure
} from '@chakra-ui/react';
import { useQuery } from '@tanstack/react-query';
import { useLoading } from '@/hooks/useLoading';
import dayjs from 'dayjs';
import { useCopyData } from '@/utils/tools';
import { useUserStore } from '@/store/user';
import MyIcon from '@/components/Icon';
import { getPromotionRecords } from '@/api/user';
import { usePagination } from '@/hooks/usePagination';
import { PromotionRecordType } from '@/api/response/user';
import { PromotionTypeMap } from '@/constants/user';
import { getPromotionInitData } from '@/api/user';
import Image from 'next/image';
const OpenApi = () => {
const { Loading } = useLoading();
const { userInfo, initUserInfo } = useUserStore();
const { copyData } = useCopyData();
const {
isOpen: isOpenWithdraw,
onClose: onCloseWithdraw,
onOpen: onOpenWithdraw
} = useDisclosure();
useQuery(['init'], initUserInfo);
const { data: { invitedAmount = 0, historyAmount = 0, residueAmount = 0 } = {} } = useQuery(
['getPromotionInitData'],
getPromotionInitData
);
const {
data: promotionRecords,
isLoading,
Pagination,
total
} = usePagination<PromotionRecordType>({
api: getPromotionRecords
});
return (
<Box py={[5, 10]} px={'5vw'}>
<Card px={6} py={4} position={'relative'}>
<Box fontSize={'xl'} fontWeight={'bold'}>
我的邀请
</Box>
<Box my={2} color={'blackAlpha.600'} fontSize={'sm'}>
你可以通过邀请链接邀请好友注册 FastGpt 账号。好友在 FastGpt
平台的每次充值,你都会获得一定比例的佣金。
</Box>
<Flex my={2} alignItems={'center'}>
<Box>当前剩余佣金: ¥</Box>
<Box mx={2} fontSize={'xl'} lineHeight={1} fontWeight={'bold'}>
{residueAmount}
</Box>
</Flex>
<Flex>
<Button
mr={4}
variant={'outline'}
onClick={() => {
copyData(`${location.origin}/?inviterId=${userInfo?._id}`, '已复制邀请链接');
}}
>
复制邀请链接
</Button>
<Button
leftIcon={<MyIcon name="withdraw" w={'22px'} />}
px={4}
title={residueAmount < 50 ? '最低提现额度为50元' : ''}
isDisabled={residueAmount < 50}
onClick={onOpenWithdraw}
>
提现
</Button>
</Flex>
</Card>
<Card mt={4} px={6} py={4} position={'relative'}>
<Flex alignItems={'center'} mb={3} justifyContent={['space-between', 'flex-start']}>
<Box w={'120px'}>佣金比例</Box>
<Box fontWeight={'bold'}>{userInfo?.promotion.rate || 15}%</Box>
</Flex>
<Flex alignItems={'center'} mb={3} justifyContent={['space-between', 'flex-start']}>
<Box w={'120px'}>已注册用户数</Box>
<Box fontWeight={'bold'}>{invitedAmount}人</Box>
</Flex>
<Flex alignItems={'center'} justifyContent={['space-between', 'flex-start']}>
<Box w={'120px'}>累计佣金</Box>
<Box fontWeight={'bold'}>¥ {historyAmount}</Box>
</Flex>
</Card>
<Card mt={4} px={6} py={4} position={'relative'}>
<Box fontSize={'xl'} fontWeight={'bold'}>
佣金记录 ({total})
</Box>
<TableContainer position={'relative'}>
<Table>
<Thead>
<Tr>
<Th>时间</Th>
<Th>类型</Th>
<Th>金额</Th>
</Tr>
</Thead>
<Tbody fontSize={'sm'}>
{promotionRecords.map((item) => (
<Tr key={item._id}>
<Td>
{item.createTime ? dayjs(item.createTime).format('YYYY/MM/DD HH:mm:ss') : '-'}
</Td>
<Td>{PromotionTypeMap[item.type]}</Td>
<Td>{item.amount}</Td>
</Tr>
))}
</Tbody>
</Table>
<Loading loading={isLoading} fixed={false} />
</TableContainer>
<Flex mt={4} px={4} justifyContent={'flex-end'}>
<Pagination />
</Flex>
</Card>
<Modal isOpen={isOpenWithdraw} onClose={onCloseWithdraw}>
<ModalOverlay />
<ModalContent color={useColorModeValue('blackAlpha.700', 'white')}>
<ModalHeader>提现联系</ModalHeader>
<ModalCloseButton />
<ModalBody textAlign={'center'}>
<Image
style={{ margin: 'auto' }}
src={'/imgs/wx300-2.jpg'}
width={200}
height={200}
alt=""
/>
<Box mt={2}>
微信号:
<Box as={'span'} userSelect={'all'}>
YNyiqi
</Box>
</Box>
<Box>发送你的邀请链接和需要提现的金额</Box>
</ModalBody>
<ModalFooter>
<Button variant={'outline'} onClick={onCloseWithdraw}>
关闭
</Button>
</ModalFooter>
</ModalContent>
</Modal>
</Box>
);
};
export default OpenApi;
...@@ -16,11 +16,6 @@ const list = [ ...@@ -16,11 +16,6 @@ const list = [
link: '/model/share' link: '/model/share'
}, },
{ {
icon: 'promotion',
label: '邀请好友',
link: '/promotion'
},
{
icon: 'develop', icon: 'develop',
label: '开发', label: '开发',
link: '/openapi' link: '/openapi'
......
...@@ -108,7 +108,6 @@ class Pg { ...@@ -108,7 +108,6 @@ class Pg {
} }
LIMIT ${props.limit || 10} OFFSET ${props.offset || 0} LIMIT ${props.limit || 10} OFFSET ${props.offset || 0}
`; `;
const pg = await connectPg(); const pg = await connectPg();
return pg.query<T>(sql); return pg.query<T>(sql);
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment