Invoice (#2435)
* feat: invoice (#2293) * feat: default voice header * add i18n * refactor: 优化代码 * feat: 用户开票 * refactor: 代码优化&&样式联调 (#2384) * Feat: invoice upload (#2424) * refactor: 验收问题&&样式调整 * feat: 文件上传 * 小调整 * perf: invoice ui --------- Co-authored-by: papapatrick <109422393+Patrickill@users.noreply.github.com>
This commit is contained in:
6
projects/app/public/imgs/modal/invoice.svg
Normal file
6
projects/app/public/imgs/modal/invoice.svg
Normal file
@@ -0,0 +1,6 @@
|
||||
<svg width="16" height="18" viewBox="0 0 16 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M3.66401 5.66669C3.66401 5.20645 4.03711 4.83335 4.49735 4.83335H11.6695C12.1297 4.83335 12.5028 5.20645 12.5028 5.66669C12.5028 6.12692 12.1297 6.50002 11.6695 6.50002H4.49735C4.03711 6.50002 3.66401 6.12692 3.66401 5.66669Z" fill="#3370FF"/>
|
||||
<path d="M3.66401 9.00002C3.66401 8.53978 4.03711 8.16669 4.49735 8.16669H11.6695C12.1297 8.16669 12.5028 8.53978 12.5028 9.00002C12.5028 9.46026 12.1297 9.83335 11.6695 9.83335H4.49735C4.03711 9.83335 3.66401 9.46026 3.66401 9.00002Z" fill="#3370FF"/>
|
||||
<path d="M4.49735 11.5C4.03711 11.5 3.66401 11.8731 3.66401 12.3334C3.66401 12.7936 4.03711 13.1667 4.49735 13.1667H11.6695C12.1297 13.1667 12.5028 12.7936 12.5028 12.3334C12.5028 11.8731 12.1297 11.5 11.6695 11.5H4.49735Z" fill="#3370FF"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.4691 1.62233C11.6249 1.77591 11.8751 1.77592 12.0308 1.62235L12.7192 0.943613C12.875 0.790047 13.1252 0.790059 13.2809 0.943642L13.9691 1.62231C14.1249 1.7759 14.3751 1.77591 14.5308 1.62233L15.1596 1.00236C15.286 0.877733 15.5 0.967269 15.5 1.14477L15.5 16.8551C15.5 17.0326 15.286 17.1221 15.1596 16.9975L14.5308 16.3775C14.3751 16.2239 14.1249 16.2239 13.9691 16.3775L13.2808 17.0562C13.1251 17.2098 12.8748 17.2098 12.7191 17.0562L12.0308 16.3775C11.8751 16.2239 11.6248 16.2239 11.4691 16.3775L10.7808 17.0562C10.6251 17.2098 10.3748 17.2098 10.2191 17.0562L9.53077 16.3774C9.37502 16.2239 9.12479 16.2239 8.96904 16.3774L8.2807 17.0562C8.12495 17.2098 7.87472 17.2098 7.71897 17.0562L7.03067 16.3775C6.87493 16.2239 6.62469 16.2239 6.46894 16.3775L5.78067 17.0562C5.62492 17.2098 5.37468 17.2098 5.21894 17.0562L4.53068 16.3775C4.37493 16.2239 4.12468 16.2239 3.96893 16.3775L3.28066 17.0563C3.1249 17.2099 2.87463 17.2099 2.71888 17.0563L2.03078 16.3776C1.87503 16.2239 1.62474 16.2239 1.46898 16.3776L0.84045 16.9975C0.714067 17.1222 0.5 17.0327 0.5 16.8552V1.14489C0.5 0.967371 0.714067 0.877842 0.84045 1.00251L1.469 1.62251C1.62475 1.77614 1.87502 1.77614 2.03078 1.62253L2.71905 0.943698C2.8748 0.790091 3.12505 0.790085 3.2808 0.943684L3.96896 1.62233C4.1247 1.77591 4.37491 1.77592 4.53067 1.62235L5.21906 0.943613C5.37481 0.790047 5.62503 0.79006 5.78077 0.943642L6.46898 1.62234C6.62471 1.77591 6.87492 1.77593 7.03067 1.62237L7.71916 0.943592C7.87492 0.790036 8.12513 0.790053 8.28086 0.943631L8.96908 1.62233C9.12481 1.77591 9.37502 1.77593 9.53077 1.62236L10.2192 0.943603C10.375 0.790041 10.6252 0.790056 10.7809 0.943636L11.4691 1.62233ZM14.0753 3.53086C14.0753 3.19949 13.8067 2.93086 13.4753 2.93086H2.52471C2.19334 2.93086 1.92471 3.19949 1.92471 3.53086V14.4691C1.92471 14.8005 2.19334 15.0691 2.52471 15.0691H13.4753C13.8067 15.0691 14.0753 14.8005 14.0753 14.4691V3.53086Z" fill="#3370FF"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.8 KiB |
@@ -2,7 +2,7 @@ import React, { useEffect, useMemo } from 'react';
|
||||
import { Controller } from 'react-hook-form';
|
||||
import RenderPluginInput from './renderPluginInput';
|
||||
import { Button, Flex } from '@chakra-ui/react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useContextSelector } from 'use-context-selector';
|
||||
import { PluginRunContext } from '../context';
|
||||
import { WorkflowIOValueTypeEnum } from '@fastgpt/global/core/workflow/constants';
|
||||
|
||||
@@ -5,7 +5,7 @@ import type { PermissionValueType } from '@fastgpt/global/support/permission/typ
|
||||
import { ReadPermissionVal, WritePermissionVal } from '@fastgpt/global/support/permission/constant';
|
||||
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
||||
import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
|
||||
export enum defaultPermissionEnum {
|
||||
private = 'private',
|
||||
|
||||
@@ -83,7 +83,7 @@ const Account = () => {
|
||||
) : (
|
||||
<>
|
||||
<MyInfo />
|
||||
{!!standardPlan && <PlanUsage />}
|
||||
{standardPlan && <PlanUsage />}
|
||||
<Other />
|
||||
</>
|
||||
)}
|
||||
|
||||
@@ -0,0 +1,300 @@
|
||||
import {
|
||||
getInvoiceBillsList,
|
||||
invoiceBillDataType,
|
||||
submitInvoice
|
||||
} from '@/web/support/wallet/bill/invoice/api';
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Checkbox,
|
||||
Flex,
|
||||
Table,
|
||||
TableContainer,
|
||||
Tbody,
|
||||
Td,
|
||||
Th,
|
||||
Thead,
|
||||
Tr,
|
||||
useDisclosure
|
||||
} from '@chakra-ui/react';
|
||||
import { billTypeMap } from '@fastgpt/global/support/wallet/bill/constants';
|
||||
import { formatStorePrice2Read } from '@fastgpt/global/support/wallet/usage/tools';
|
||||
import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
||||
import dayjs from 'dayjs';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useCallback, useState } from 'react';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import Divider from '@/pages/app/detail/components/WorkflowComponents/Flow/components/Divider';
|
||||
import { TeamInvoiceHeaderType } from '@fastgpt/global/support/user/team/type';
|
||||
import { InvoiceHeaderSingleForm } from './InvoiceHeaderForm';
|
||||
import MyBox from '@fastgpt/web/components/common/MyBox';
|
||||
import { getTeamInvoiceHeader } from '@/web/support/user/team/api';
|
||||
import { useToast } from '@fastgpt/web/hooks/useToast';
|
||||
type chosenBillDataType = {
|
||||
_id: string;
|
||||
price: number;
|
||||
};
|
||||
const ApplyInvoiceModal = ({ onClose }: { onClose: () => void }) => {
|
||||
const { t } = useTranslation();
|
||||
const { toast } = useToast();
|
||||
const [chosenBillDataList, setChosenBillDataList] = useState<chosenBillDataType[]>([]);
|
||||
const [totalPrice, setTotalPrice] = useState(0);
|
||||
const [formData, setFormData] = useState<TeamInvoiceHeaderType>({
|
||||
teamName: '',
|
||||
unifiedCreditCode: '',
|
||||
companyAddress: '',
|
||||
companyPhone: '',
|
||||
bankName: '',
|
||||
bankAccount: '',
|
||||
needSpecialInvoice: undefined,
|
||||
emailAddress: ''
|
||||
});
|
||||
const {
|
||||
isOpen: isOpenSettleModal,
|
||||
onOpen: onOpenSettleModal,
|
||||
onClose: onCloseSettleModal
|
||||
} = useDisclosure();
|
||||
|
||||
const handleChange = useCallback((e: any) => {
|
||||
const { name, value } = e.target;
|
||||
setFormData((prev) => ({ ...prev, [name]: value }));
|
||||
}, []);
|
||||
|
||||
const handleRatiosChange = useCallback((v: string) => {
|
||||
setFormData((prev) => ({ ...prev, needSpecialInvoice: v === 'true' }));
|
||||
}, []);
|
||||
|
||||
const isHeaderValid = useCallback((v: TeamInvoiceHeaderType) => {
|
||||
const emailRegex = /\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/;
|
||||
for (const [key, value] of Object.entries(v)) {
|
||||
if (typeof value === 'string' && value.trim() === '') {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return emailRegex.test(v.emailAddress);
|
||||
}, []);
|
||||
|
||||
const {
|
||||
loading: isLoading,
|
||||
data: billsList,
|
||||
run: getInvoiceBills
|
||||
} = useRequest2(() => getInvoiceBillsList(), {
|
||||
manual: false
|
||||
});
|
||||
|
||||
const { run: handleSubmitInvoice, loading: isSubmitting } = useRequest2(
|
||||
() =>
|
||||
submitInvoice({
|
||||
amount: totalPrice,
|
||||
billIdList: chosenBillDataList.map((item) => item._id),
|
||||
...formData
|
||||
}),
|
||||
{
|
||||
manual: true,
|
||||
successToast: t('common:common.submit_success'),
|
||||
errorToast: t('common:common.Submit failed'),
|
||||
onSuccess: () => onClose()
|
||||
}
|
||||
);
|
||||
|
||||
const { loading: isLoadingHeader } = useRequest2(() => getTeamInvoiceHeader(), {
|
||||
manual: false,
|
||||
onSuccess: (res) => setFormData(res)
|
||||
});
|
||||
|
||||
const handleSubmit = useCallback(async () => {
|
||||
if (!isHeaderValid(formData)) {
|
||||
toast({
|
||||
title: t('common:support.wallet.invoice_data.in_valid'),
|
||||
status: 'info'
|
||||
});
|
||||
return;
|
||||
}
|
||||
handleSubmitInvoice();
|
||||
}, [formData, handleSubmitInvoice, isHeaderValid, t, toast]);
|
||||
|
||||
const handleBack = useCallback(() => {
|
||||
setChosenBillDataList([]);
|
||||
getInvoiceBills();
|
||||
onCloseSettleModal();
|
||||
}, [getInvoiceBills, onCloseSettleModal]);
|
||||
|
||||
const handleSingleCheck = useCallback(
|
||||
(item: invoiceBillDataType) => {
|
||||
if (chosenBillDataList.find((bill) => bill._id === item._id)) {
|
||||
setChosenBillDataList(chosenBillDataList.filter((bill) => bill._id !== item._id));
|
||||
} else {
|
||||
setChosenBillDataList([...chosenBillDataList, { _id: item._id, price: item.price }]);
|
||||
}
|
||||
},
|
||||
[chosenBillDataList]
|
||||
);
|
||||
|
||||
return (
|
||||
<MyModal
|
||||
isOpen={true}
|
||||
isCentered
|
||||
iconSrc="/imgs/modal/invoice.svg"
|
||||
minHeight={'42.25rem'}
|
||||
w={'43rem'}
|
||||
onClose={onClose}
|
||||
isLoading={isLoading}
|
||||
title={t('common:support.wallet.apply_invoice')}
|
||||
>
|
||||
{!isOpenSettleModal ? (
|
||||
<Box px={['1.6rem', '3.25rem']} py={['1rem', '2rem']}>
|
||||
<Box fontWeight={500} fontSize={'1rem'} pb={'0.75rem'}>
|
||||
{t('common:support.wallet.billable_invoice')}
|
||||
</Box>
|
||||
<Box h={'27.9rem'} overflow={'auto'}>
|
||||
<TableContainer>
|
||||
<Table>
|
||||
<Thead>
|
||||
<Tr>
|
||||
<Th>
|
||||
<Checkbox
|
||||
isChecked={
|
||||
chosenBillDataList.length === billsList?.length && billsList?.length !== 0
|
||||
}
|
||||
onChange={(e) => {
|
||||
!e.target.checked
|
||||
? setChosenBillDataList([])
|
||||
: setChosenBillDataList(
|
||||
billsList?.map((item) => ({
|
||||
_id: item._id,
|
||||
price: item.price
|
||||
})) || []
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</Th>
|
||||
<Th>{t('common:user.type')}</Th>
|
||||
<Th>{t('common:user.Time')}</Th>
|
||||
<Th>{t('common:support.wallet.Amount')}</Th>
|
||||
</Tr>
|
||||
</Thead>
|
||||
<Tbody fontSize={'0.875rem'}>
|
||||
{billsList?.map((item) => (
|
||||
<Tr
|
||||
cursor={'pointer'}
|
||||
key={item._id}
|
||||
onClick={(e: any) => {
|
||||
if (e.target?.name && e.target.name === 'check') return;
|
||||
handleSingleCheck(item);
|
||||
}}
|
||||
_hover={{
|
||||
bg: 'blue.50'
|
||||
}}
|
||||
>
|
||||
<Td>
|
||||
<Checkbox
|
||||
name="check"
|
||||
isChecked={chosenBillDataList.some((i) => i._id === item._id)}
|
||||
/>
|
||||
</Td>
|
||||
<Td>{t(billTypeMap[item.type]?.label as any)}</Td>
|
||||
<Td>
|
||||
{item.createTime
|
||||
? dayjs(item.createTime).format('YYYY/MM/DD HH:mm:ss')
|
||||
: '-'}
|
||||
</Td>
|
||||
<Td>{t('common:pay.yuan', { amount: formatStorePrice2Read(item.price) })}</Td>
|
||||
</Tr>
|
||||
))}
|
||||
</Tbody>
|
||||
</Table>
|
||||
{!isLoading && billsList && billsList.length === 0 && (
|
||||
<Flex
|
||||
mt={'20vh'}
|
||||
flexDirection={'column'}
|
||||
alignItems={'center'}
|
||||
justifyContent={'center'}
|
||||
>
|
||||
<MyIcon name="empty" w={'48px'} h={'48px'} color={'transparent'} />
|
||||
<Box mt={2} color={'myGray.500'}>
|
||||
{t('common:support.wallet.noBill')}
|
||||
</Box>
|
||||
</Flex>
|
||||
)}
|
||||
</TableContainer>
|
||||
</Box>
|
||||
<Flex pt={'2.5rem'} justify={'flex-end'}>
|
||||
<Button
|
||||
variant={'primary'}
|
||||
px="0"
|
||||
isDisabled={!chosenBillDataList.length}
|
||||
onClick={() => {
|
||||
let total = chosenBillDataList.reduce((acc, cur) => acc + Number(cur.price), 0);
|
||||
if (!total) return;
|
||||
setTotalPrice(total);
|
||||
onOpenSettleModal();
|
||||
}}
|
||||
>
|
||||
<Flex alignItems={'center'}>
|
||||
<Box px={'1.25rem'} py={'0.5rem'}>
|
||||
{t('common:common.Confirm')}
|
||||
</Box>
|
||||
</Flex>
|
||||
</Button>
|
||||
</Flex>
|
||||
</Box>
|
||||
) : (
|
||||
<Box px={['1.6rem', '3.25rem']} py={['1rem', '2rem']}>
|
||||
<Box w={'100%'} fontSize={'0.875rem'}>
|
||||
<Flex w={'100%'} justifyContent={'space-between'}>
|
||||
<Box>{t('common:support.wallet.invoice_amount')}</Box>
|
||||
<Box>{t('common:pay.yuan', { amount: formatStorePrice2Read(totalPrice) })}</Box>
|
||||
</Flex>
|
||||
<Box w={'100%'} py={4}>
|
||||
<Divider showBorderBottom={false} />
|
||||
</Box>
|
||||
</Box>
|
||||
<MyBox isLoading={isLoadingHeader}>
|
||||
<Flex justify={'center'}>
|
||||
<InvoiceHeaderSingleForm
|
||||
formData={formData}
|
||||
handleChange={handleChange}
|
||||
handleRatiosChange={handleRatiosChange}
|
||||
/>
|
||||
</Flex>
|
||||
</MyBox>
|
||||
<Flex
|
||||
align={'center'}
|
||||
w={'19.8rem'}
|
||||
h={'1.75rem'}
|
||||
mt={4}
|
||||
px={'0.75rem'}
|
||||
py={'0.38rem'}
|
||||
bg={'blue.50'}
|
||||
borderRadius={'sm'}
|
||||
color={'blue.600'}
|
||||
>
|
||||
<MyIcon name="infoRounded" w={'14px'} h={'14px'} />
|
||||
<Box ml={2} fontSize={'0.6875rem'}>
|
||||
{t('common:support.wallet.invoice_info')}
|
||||
</Box>
|
||||
</Flex>
|
||||
<Flex justify={'flex-end'} w={'100%'} pt={[3, 7]}>
|
||||
<Button variant={'outline'} mr={'0.75rem'} px="0" onClick={handleBack}>
|
||||
<Flex alignItems={'center'}>
|
||||
<Box px={'1.25rem'} py={'0.5rem'}>
|
||||
{t('common:back')}
|
||||
</Box>
|
||||
</Flex>
|
||||
</Button>
|
||||
<Button isLoading={isSubmitting} px="0" onClick={handleSubmit}>
|
||||
<Flex alignItems={'center'}>
|
||||
<Box px={'1.25rem'} py={'0.5rem'}>
|
||||
{t('common:common.Confirm')}
|
||||
</Box>
|
||||
</Flex>
|
||||
</Button>
|
||||
</Flex>
|
||||
</Box>
|
||||
)}
|
||||
</MyModal>
|
||||
);
|
||||
};
|
||||
|
||||
export default ApplyInvoiceModal;
|
||||
@@ -0,0 +1,58 @@
|
||||
import { Box, Button, Flex } from '@chakra-ui/react';
|
||||
import FillRowTabs from '@fastgpt/web/components/common/Tabs/FillRowTabs';
|
||||
import dynamic from 'next/dynamic';
|
||||
import { useState } from 'react';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import ApplyInvoiceModal from './ApplyInvoiceModal';
|
||||
|
||||
const TabEnum = {
|
||||
bill: 'bill',
|
||||
invoice: 'invoice',
|
||||
invoiceHeader: 'voiceHeader'
|
||||
};
|
||||
const BillTable = dynamic(() => import('./BillTable'));
|
||||
const InvoiceHeaderForm = dynamic(() => import('./InvoiceHeaderForm'));
|
||||
const InvoiceTable = dynamic(() => import('./InvoiceTable'));
|
||||
const BillAndInvoice = () => {
|
||||
const [currentTab, setCurrentTab] = useState(TabEnum.bill);
|
||||
const [isOpenInvoiceModal, setIsOpenInvoiceModal] = useState(false);
|
||||
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<>
|
||||
<Box p={['1rem', '2rem']}>
|
||||
<Flex justifyContent={'space-between'} alignItems={'center'} pb={'0.75rem'}>
|
||||
<FillRowTabs
|
||||
list={[
|
||||
{ label: t('common:support.wallet.bill_tag.bill'), value: TabEnum.bill },
|
||||
{ label: t('common:support.wallet.bill_tag.invoice'), value: TabEnum.invoice },
|
||||
{
|
||||
label: t('common:support.wallet.bill_tag.default_header'),
|
||||
value: TabEnum.invoiceHeader
|
||||
}
|
||||
]}
|
||||
value={currentTab}
|
||||
onChange={setCurrentTab}
|
||||
></FillRowTabs>
|
||||
{currentTab !== TabEnum.invoiceHeader && (
|
||||
<Button variant={'primary'} px="0" onClick={() => setIsOpenInvoiceModal(true)}>
|
||||
<Flex alignItems={'center'} px={'20px'}>
|
||||
<Box px={'1.25rem'} py={'0.5rem'}>
|
||||
{t('common:support.wallet.invoicing')}
|
||||
</Box>
|
||||
</Flex>
|
||||
</Button>
|
||||
)}
|
||||
</Flex>
|
||||
<Box h={'100%'}>
|
||||
{currentTab === TabEnum.bill && <BillTable />}
|
||||
{currentTab === TabEnum.invoice && <InvoiceTable />}
|
||||
{currentTab === TabEnum.invoiceHeader && <InvoiceHeaderForm />}
|
||||
</Box>
|
||||
{isOpenInvoiceModal && <ApplyInvoiceModal onClose={() => setIsOpenInvoiceModal(false)} />}
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default BillAndInvoice;
|
||||
@@ -102,8 +102,6 @@ const BillTable = () => {
|
||||
position={'relative'}
|
||||
h={'100%'}
|
||||
overflow={'overlay'}
|
||||
py={[0, 5]}
|
||||
px={[3, 8]}
|
||||
>
|
||||
<TableContainer>
|
||||
<Table>
|
||||
@@ -188,7 +186,7 @@ function BillDetailModal({ bill, onClose }: { bill: BillSchemaType; onClose: ()
|
||||
isOpen={true}
|
||||
onClose={onClose}
|
||||
iconSrc="/imgs/modal/bill.svg"
|
||||
title={t('common:support.wallet.usage.Usage Detail')}
|
||||
title={t('common:support.wallet.bill_detail')}
|
||||
maxW={['90vw', '700px']}
|
||||
>
|
||||
<ModalBody>
|
||||
@@ -218,6 +216,10 @@ function BillDetailModal({ bill, onClose }: { bill: BillSchemaType; onClose: ()
|
||||
<FormLabel flex={'0 0 120px'}>{t('common:support.wallet.bill.Type')}:</FormLabel>
|
||||
<Box>{t(billTypeMap[bill.type]?.label as any)}</Box>
|
||||
</Flex>
|
||||
<Flex alignItems={'center'} pb={4}>
|
||||
<FormLabel flex={'0 0 120px'}>{t('common:support.wallet.has_invoice')}:</FormLabel>
|
||||
<Box>{bill.hasInvoice ? t('common:yes') : t('common:no')}</Box>
|
||||
</Flex>
|
||||
{!!bill.metadata?.subMode && (
|
||||
<Flex alignItems={'center'} pb={4}>
|
||||
<FormLabel flex={'0 0 120px'}>
|
||||
@@ -0,0 +1,209 @@
|
||||
import Divider from '@/pages/app/detail/components/WorkflowComponents/Flow/components/Divider';
|
||||
import { getTeamInvoiceHeader, updateTeamInvoiceHeader } from '@/web/support/user/team/api';
|
||||
import { Box, Button, Flex, Input, Radio, RadioGroup, Stack } from '@chakra-ui/react';
|
||||
import { TeamInvoiceHeaderType } from '@fastgpt/global/support/user/team/type';
|
||||
import MyBox from '@fastgpt/web/components/common/MyBox';
|
||||
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
||||
import { useCallback, useState } from 'react';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useToast } from '@fastgpt/web/hooks/useToast';
|
||||
|
||||
const InputItem = ({
|
||||
label,
|
||||
value,
|
||||
onChange,
|
||||
name
|
||||
}: {
|
||||
label: string;
|
||||
value: string;
|
||||
onChange: (e: any) => void;
|
||||
name: string;
|
||||
}) => {
|
||||
return (
|
||||
<>
|
||||
<Flex justify={'space-between'} flexDir={['column', 'row']}>
|
||||
<Box fontSize={'14px'} lineHeight={'2rem'}>
|
||||
{label}
|
||||
</Box>
|
||||
<Input
|
||||
bg={'myGray.50'}
|
||||
border={'1px solid'}
|
||||
borderColor={'myGray.200'}
|
||||
w={'21.25rem'}
|
||||
focusBorderColor="myGray.200"
|
||||
placeholder={label}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
name={name}
|
||||
/>
|
||||
</Flex>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export const InvoiceHeaderSingleForm = ({
|
||||
formData,
|
||||
handleChange,
|
||||
handleRatiosChange
|
||||
}: {
|
||||
formData: TeamInvoiceHeaderType;
|
||||
handleChange: (e: any) => void;
|
||||
handleRatiosChange: (v: string) => void;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<>
|
||||
<Flex
|
||||
w={['auto', '36rem']}
|
||||
flexDir={'column'}
|
||||
gap={'1rem'}
|
||||
fontWeight={'500'}
|
||||
color={'myGray.900'}
|
||||
fontSize={'14px'}
|
||||
>
|
||||
<InputItem
|
||||
label={t('common:support.wallet.invoice_data.organization_name')}
|
||||
value={formData.teamName}
|
||||
onChange={handleChange}
|
||||
name="teamName"
|
||||
/>
|
||||
<InputItem
|
||||
label={t('common:support.wallet.invoice_data.unit_code')}
|
||||
value={formData.unifiedCreditCode}
|
||||
onChange={handleChange}
|
||||
name="unifiedCreditCode"
|
||||
/>
|
||||
<InputItem
|
||||
label={t('common:support.wallet.invoice_data.company_address')}
|
||||
value={formData.companyAddress}
|
||||
onChange={handleChange}
|
||||
name="companyAddress"
|
||||
/>
|
||||
<InputItem
|
||||
label={t('common:support.wallet.invoice_data.company_phone')}
|
||||
value={formData.companyPhone}
|
||||
onChange={handleChange}
|
||||
name="companyPhone"
|
||||
/>
|
||||
<InputItem
|
||||
label={t('common:support.wallet.invoice_data.bank')}
|
||||
value={formData.bankName}
|
||||
onChange={handleChange}
|
||||
name="bankName"
|
||||
/>
|
||||
<InputItem
|
||||
label={t('common:support.wallet.invoice_data.bank_account')}
|
||||
value={formData.bankAccount}
|
||||
onChange={handleChange}
|
||||
name="bankAccount"
|
||||
/>
|
||||
<Flex justify={'space-between'} flexDir={['column', 'row']}>
|
||||
<Box fontSize={'14px'} lineHeight={'2rem'}>
|
||||
{t('common:support.wallet.invoice_data.need_special_invoice')}
|
||||
</Box>
|
||||
<RadioGroup
|
||||
value={
|
||||
formData.needSpecialInvoice === undefined
|
||||
? ''
|
||||
: formData.needSpecialInvoice.toString()
|
||||
}
|
||||
onChange={handleRatiosChange}
|
||||
w={'21.25rem'}
|
||||
>
|
||||
<Stack direction="row" h={'2rem'}>
|
||||
<Radio value="true" pr={'1rem'}>
|
||||
<Box fontSize={'14px'}>{t('common:yes')}</Box>
|
||||
</Radio>
|
||||
<Radio value="false">
|
||||
<Box fontSize={'14px'}>{t('common:no')}</Box>
|
||||
</Radio>
|
||||
</Stack>
|
||||
</RadioGroup>
|
||||
</Flex>
|
||||
<Box w={'100%'}>
|
||||
<Divider showBorderBottom={false} />
|
||||
</Box>
|
||||
<InputItem
|
||||
label={t('common:support.wallet.invoice_data.email')}
|
||||
value={formData.emailAddress}
|
||||
onChange={handleChange}
|
||||
name="emailAddress"
|
||||
/>
|
||||
</Flex>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const InvoiceHeaderForm = () => {
|
||||
const [formData, setFormData] = useState<TeamInvoiceHeaderType>({
|
||||
teamName: '',
|
||||
unifiedCreditCode: '',
|
||||
companyAddress: '',
|
||||
companyPhone: '',
|
||||
bankName: '',
|
||||
bankAccount: '',
|
||||
needSpecialInvoice: undefined,
|
||||
emailAddress: ''
|
||||
});
|
||||
const { loading: isLoading } = useRequest2(() => getTeamInvoiceHeader(), {
|
||||
manual: false,
|
||||
onSuccess: (data) => {
|
||||
setFormData(data);
|
||||
}
|
||||
});
|
||||
const { t } = useTranslation();
|
||||
const { toast } = useToast();
|
||||
const handleChange = useCallback((e: any) => {
|
||||
const { name, value } = e.target;
|
||||
setFormData((prev) => ({ ...prev, [name]: value }));
|
||||
}, []);
|
||||
const handleRatiosChange = useCallback((v: string) => {
|
||||
setFormData((prev) => ({ ...prev, needSpecialInvoice: v === 'true' }));
|
||||
}, []);
|
||||
const isHeaderValid = useCallback((v: TeamInvoiceHeaderType) => {
|
||||
const emailRegex = /\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/;
|
||||
return emailRegex.test(v.emailAddress);
|
||||
}, []);
|
||||
const { loading: isSubmitting, run: handleSubmit } = useRequest2(
|
||||
() => updateTeamInvoiceHeader(formData),
|
||||
{
|
||||
manual: true,
|
||||
successToast: t('common:common.Save Success'),
|
||||
errorToast: t('common:common.Save Failed')
|
||||
}
|
||||
);
|
||||
const onSubmit = useCallback(() => {
|
||||
if (!isHeaderValid(formData)) {
|
||||
toast({
|
||||
title: t('common:support.wallet.invoice_data.in_valid'),
|
||||
status: 'info'
|
||||
});
|
||||
return;
|
||||
}
|
||||
handleSubmit();
|
||||
}, [handleSubmit, formData, isHeaderValid, toast, t]);
|
||||
return (
|
||||
<>
|
||||
<MyBox isLoading={isLoading} pt={['1rem', '3.5rem']}>
|
||||
<Flex w={'100%'} overflow={'auto'} justify={'center'} flexDir={'column'} align={'center'}>
|
||||
<InvoiceHeaderSingleForm
|
||||
formData={formData}
|
||||
handleChange={handleChange}
|
||||
handleRatiosChange={handleRatiosChange}
|
||||
/>
|
||||
<Flex w={'100%'} justify={'center'} mt={'3rem'}>
|
||||
<Button variant={'primary'} px="0" onClick={onSubmit} isLoading={isSubmitting}>
|
||||
<Flex alignItems={'center'} px={'20px'}>
|
||||
<Box px={'1.25rem'} py={'0.5rem'}>
|
||||
{t('common:common.Save')}
|
||||
</Box>
|
||||
</Flex>
|
||||
</Button>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</MyBox>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default InvoiceHeaderForm;
|
||||
207
projects/app/src/pages/account/components/bill/InvoiceTable.tsx
Normal file
207
projects/app/src/pages/account/components/bill/InvoiceTable.tsx
Normal file
@@ -0,0 +1,207 @@
|
||||
import { getInvoiceRecords } from '@/web/support/wallet/bill/invoice/api';
|
||||
import MyBox from '@fastgpt/web/components/common/MyBox';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useEffect, useState } from 'react';
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Flex,
|
||||
FormLabel,
|
||||
ModalBody,
|
||||
Table,
|
||||
TableContainer,
|
||||
Tbody,
|
||||
Td,
|
||||
Th,
|
||||
Thead,
|
||||
Tr
|
||||
} from '@chakra-ui/react';
|
||||
import { usePagination } from '@fastgpt/web/hooks/usePagination';
|
||||
import { InvoiceSchemaType } from '@fastgpt/global/support/wallet/bill/type';
|
||||
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 | ''>('');
|
||||
const {
|
||||
data: invoices,
|
||||
isLoading,
|
||||
Pagination,
|
||||
getData,
|
||||
total
|
||||
} = usePagination<InvoiceSchemaType>({
|
||||
api: getInvoiceRecords,
|
||||
pageSize: 20,
|
||||
defaultRequest: false
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
getData(1);
|
||||
}, [getData]);
|
||||
|
||||
return (
|
||||
<MyBox isLoading={isLoading} position={'relative'} h={'100%'} overflow={'overlay'}>
|
||||
<TableContainer minH={'50vh'}>
|
||||
<Table>
|
||||
<Thead h="3rem">
|
||||
<Tr>
|
||||
<Th w={'20%'}>#</Th>
|
||||
<Th w={'20%'}>{t('common:user.Time')}</Th>
|
||||
<Th w={'20%'}>{t('common:support.wallet.Amount')}</Th>
|
||||
<Th w={'20%'}>{t('common:support.wallet.bill.Status')}</Th>
|
||||
<Th w={'20%'}></Th>
|
||||
</Tr>
|
||||
</Thead>
|
||||
<Tbody fontSize={'sm'}>
|
||||
{invoices.map((item, i) => (
|
||||
<Tr key={item._id}>
|
||||
<Td>{i + 1}</Td>
|
||||
<Td>
|
||||
{item.createTime ? dayjs(item.createTime).format('YYYY/MM/DD HH:mm:ss') : '-'}
|
||||
</Td>
|
||||
<Td>{t('common:pay.yuan', { amount: formatStorePrice2Read(item.amount) })}</Td>
|
||||
<Td>
|
||||
<Flex
|
||||
px={'0.75rem'}
|
||||
py={'0.38rem'}
|
||||
w={'4.25rem'}
|
||||
h={'1.75rem'}
|
||||
bg={item.status === 1 ? 'blue.50' : 'green.50'}
|
||||
rounded={'md'}
|
||||
justify={'center'}
|
||||
align={'center'}
|
||||
color={item.status === 1 ? 'blue.600' : 'green.600'}
|
||||
>
|
||||
<MyIcon name="point" w={'6px'} h={'6px'} />
|
||||
<Box ml={'0.25rem'}>
|
||||
{item.status === 1
|
||||
? t('common:common.submitted')
|
||||
: t('common:common.have_done')}
|
||||
</Box>
|
||||
</Flex>
|
||||
</Td>
|
||||
<Td>
|
||||
<Button
|
||||
onClick={() => setInvoiceDetailData(item)}
|
||||
h={'2rem'}
|
||||
w={'4.5rem'}
|
||||
variant={'whiteBase'}
|
||||
size={'sm'}
|
||||
py={'0.5rem'}
|
||||
px={'0.75rem'}
|
||||
_hover={{
|
||||
color: 'blue.600'
|
||||
}}
|
||||
>
|
||||
<Flex>
|
||||
<MyIcon name="paragraph" w={'16px'} h={'16px'} />
|
||||
<Box ml={'0.38rem'}>{t('common:common.Detail')}</Box>
|
||||
</Flex>
|
||||
</Button>
|
||||
</Td>
|
||||
</Tr>
|
||||
))}
|
||||
</Tbody>
|
||||
</Table>
|
||||
{total >= 20 && (
|
||||
<Flex mt={3} justifyContent={'flex-end'}>
|
||||
<Pagination />
|
||||
</Flex>
|
||||
)}
|
||||
{!isLoading && invoices.length === 0 && (
|
||||
<Flex
|
||||
mt={'20vh'}
|
||||
flexDirection={'column'}
|
||||
alignItems={'center'}
|
||||
justifyContent={'center'}
|
||||
>
|
||||
<MyIcon name="empty" w={'48px'} h={'48px'} color={'transparent'} />
|
||||
<Box mt={2} color={'myGray.500'}>
|
||||
{t('common:support.wallet.no_invoice')}
|
||||
</Box>
|
||||
</Flex>
|
||||
)}
|
||||
</TableContainer>
|
||||
{!!invoiceDetailData && (
|
||||
<InvoiceDetailModal invoice={invoiceDetailData} onClose={() => setInvoiceDetailData('')} />
|
||||
)}
|
||||
</MyBox>
|
||||
);
|
||||
};
|
||||
|
||||
export default InvoiceTable;
|
||||
|
||||
function InvoiceDetailModal({
|
||||
invoice,
|
||||
onClose
|
||||
}: {
|
||||
invoice: InvoiceSchemaType;
|
||||
onClose: () => void;
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<MyModal
|
||||
maxW={['90vw', '700px']}
|
||||
isOpen={true}
|
||||
onClose={onClose}
|
||||
title={
|
||||
<Flex align={'center'}>
|
||||
<MyIcon name="paragraph" w={'20px'} h={'20px'} color={'blue.600'} />
|
||||
<Box ml={'0.62rem'}>{t('common:support.wallet.invoice_detail')}</Box>
|
||||
</Flex>
|
||||
}
|
||||
>
|
||||
<ModalBody px={'3.25rem'} py={'2rem'}>
|
||||
<Flex w={'100%'} h={'100%'} flexDir={'column'} gap={'1rem'}>
|
||||
<LabelItem
|
||||
label={t('common:support.wallet.invoice_amount')}
|
||||
value={t('common:pay.yuan', { amount: formatStorePrice2Read(invoice.amount) })}
|
||||
/>
|
||||
<LabelItem
|
||||
label={t('common:support.wallet.invoice_data.organization_name')}
|
||||
value={invoice.teamName}
|
||||
/>
|
||||
<LabelItem
|
||||
label={t('common:support.wallet.invoice_data.unit_code')}
|
||||
value={invoice.unifiedCreditCode}
|
||||
/>
|
||||
<LabelItem
|
||||
label={t('common:support.wallet.invoice_data.company_address')}
|
||||
value={invoice.companyAddress}
|
||||
/>
|
||||
<LabelItem
|
||||
label={t('common:support.wallet.invoice_data.company_phone')}
|
||||
value={invoice.companyPhone}
|
||||
/>
|
||||
<LabelItem
|
||||
label={t('common:support.wallet.invoice_data.bank')}
|
||||
value={invoice.bankName}
|
||||
/>
|
||||
<LabelItem
|
||||
label={t('common:support.wallet.invoice_data.bank_account')}
|
||||
value={invoice.bankAccount}
|
||||
/>
|
||||
<LabelItem
|
||||
label={t('common:support.wallet.invoice_data.need_special_invoice')}
|
||||
value={invoice.needSpecialInvoice ? t('common:yes') : t('common:no')}
|
||||
/>
|
||||
<LabelItem
|
||||
label={t('common:support.wallet.invoice_data.email')}
|
||||
value={invoice.emailAddress}
|
||||
/>
|
||||
</Flex>
|
||||
</ModalBody>
|
||||
</MyModal>
|
||||
);
|
||||
}
|
||||
|
||||
function LabelItem({ label, value }: { label: string; value: string }) {
|
||||
return (
|
||||
<Flex alignItems={'center'} justify={'space-between'}>
|
||||
<FormLabel flex={'0 0 120px'}>{label}</FormLabel>
|
||||
<Box>{value}</Box>
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
@@ -16,7 +16,7 @@ import { useSystem } from '@fastgpt/web/hooks/useSystem';
|
||||
|
||||
const Promotion = dynamic(() => import('./components/Promotion'));
|
||||
const UsageTable = dynamic(() => import('./components/UsageTable'));
|
||||
const BillTable = dynamic(() => import('./components/BillTable'));
|
||||
const BillAndInvoice = dynamic(() => import('./components/bill/BillAndInvoice'));
|
||||
const InformTable = dynamic(() => import('./components/InformTable'));
|
||||
const ApiKeyTable = dynamic(() => import('./components/ApiKeyTable'));
|
||||
const Individuation = dynamic(() => import('./components/Individuation'));
|
||||
@@ -53,7 +53,8 @@ const Account = ({ currentTab }: { currentTab: TabEnum }) => {
|
||||
}
|
||||
]
|
||||
: []),
|
||||
...(feConfigs?.show_pay && userInfo?.team?.permission.hasWritePer
|
||||
// ...(feConfigs?.show_pay && userInfo?.team?.permission.hasWritePer
|
||||
...(feConfigs?.show_pay || userInfo?.team?.permission.hasWritePer
|
||||
? [
|
||||
{
|
||||
icon: 'support/bill/payRecordLight',
|
||||
@@ -176,7 +177,7 @@ const Account = ({ currentTab }: { currentTab: TabEnum }) => {
|
||||
{currentTab === TabEnum.info && <UserInfo />}
|
||||
{currentTab === TabEnum.promotion && <Promotion />}
|
||||
{currentTab === TabEnum.usage && <UsageTable />}
|
||||
{currentTab === TabEnum.bill && <BillTable />}
|
||||
{currentTab === TabEnum.bill && <BillAndInvoice />}
|
||||
{currentTab === TabEnum.individuation && <Individuation />}
|
||||
{currentTab === TabEnum.inform && <InformTable />}
|
||||
{currentTab === TabEnum.apikey && <ApiKeyTable />}
|
||||
|
||||
@@ -29,7 +29,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
maxSize: (global.feConfigs?.uploadFileMaxSize || 500) * 1024 * 1024
|
||||
});
|
||||
const { file, bucketName, metadata } = await upload.doUpload(req, res);
|
||||
|
||||
filePaths.push(file.path);
|
||||
const { teamId, tmbId, outLinkUid } = await authChatCert({ req, authToken: true });
|
||||
|
||||
await authUploadLimit(outLinkUid || tmbId);
|
||||
|
||||
@@ -2,7 +2,7 @@ import React from 'react';
|
||||
import { Box, Flex, Input } from '@chakra-ui/react';
|
||||
import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
|
||||
import dayjs from 'dayjs';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { UseFormRegister, UseFormSetValue } from 'react-hook-form';
|
||||
import { OutLinkEditType } from '@fastgpt/global/support/outLink/type';
|
||||
import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useCopyData } from '@/web/common/hooks/useCopyData';
|
||||
import { Box, Image, Flex, ModalBody } from '@chakra-ui/react';
|
||||
import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
|
||||
export type ShowShareLinkModalProps = {
|
||||
shareLink: string;
|
||||
|
||||
@@ -5,7 +5,7 @@ import MyBox from '@fastgpt/web/components/common/MyBox';
|
||||
import { postCreateDatasetCollectionTag } from '@/web/core/dataset/api';
|
||||
import { useContextSelector } from 'use-context-selector';
|
||||
import { DatasetPageContext } from '@/web/core/dataset/context/datasetPageContext';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { useRequest } from '@fastgpt/web/hooks/useRequest';
|
||||
import { CollectionPageContext } from './Context';
|
||||
|
||||
@@ -5,7 +5,7 @@ import MyBox from '@fastgpt/web/components/common/MyBox';
|
||||
import { postCreateDatasetCollectionTag, putDatasetCollectionById } from '@/web/core/dataset/api';
|
||||
import { useContextSelector } from 'use-context-selector';
|
||||
import { DatasetPageContext } from '@/web/core/dataset/context/datasetPageContext';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { useRequest } from '@fastgpt/web/hooks/useRequest';
|
||||
import { useDeepCompareEffect } from 'ahooks';
|
||||
|
||||
@@ -34,7 +34,7 @@ import EmptyTip from '@fastgpt/web/components/common/EmptyTip';
|
||||
import { useFolderDrag } from '@/components/common/folder/useFolderDrag';
|
||||
import MyBox from '@fastgpt/web/components/common/MyBox';
|
||||
import { useI18n } from '@/web/context/I18n';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
|
||||
function List() {
|
||||
const { setLoading } = useSystemStore();
|
||||
|
||||
@@ -20,7 +20,7 @@ import dynamic from 'next/dynamic';
|
||||
import { DatasetTypeEnum } from '@fastgpt/global/core/dataset/constants';
|
||||
import { DatasetItemType, DatasetListItemType } from '@fastgpt/global/core/dataset/type';
|
||||
import { EditResourceInfoFormType } from '@/components/common/Modal/EditResourceModal';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
|
||||
const MoveModal = dynamic(() => import('@/components/common/folder/MoveModal'));
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
TeamMemberSchema
|
||||
} from '@fastgpt/global/support/user/team/type.d';
|
||||
import { FeTeamPlanStatusType, TeamSubSchema } from '@fastgpt/global/support/wallet/sub/type';
|
||||
import { TeamInvoiceHeaderType } from '@fastgpt/global/support/user/team/type';
|
||||
|
||||
/* --------------- team ---------------- */
|
||||
export const getTeamList = (status: `${TeamMemberSchema['status']}`) =>
|
||||
@@ -61,3 +62,9 @@ export const getTeamPlanStatus = () =>
|
||||
GET<FeTeamPlanStatusType>(`/support/user/team/plan/getTeamPlanStatus`, { maxQuantity: 1 });
|
||||
export const getTeamPlans = () =>
|
||||
GET<TeamSubSchema[]>(`/proApi/support/user/team/plan/getTeamPlans`);
|
||||
|
||||
export const getTeamInvoiceHeader = () =>
|
||||
GET<TeamInvoiceHeaderType>(`/proApi/support/user/team/invoiceAccount/getTeamInvoiceHeader`);
|
||||
|
||||
export const updateTeamInvoiceHeader = (data: TeamInvoiceHeaderType) =>
|
||||
POST(`/proApi/support/user/team/invoiceAccount/update`, data);
|
||||
|
||||
20
projects/app/src/web/support/wallet/bill/invoice/api.ts
Normal file
20
projects/app/src/web/support/wallet/bill/invoice/api.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { RequestPaging } from '@/types';
|
||||
import { GET, POST } from '@/web/common/api/request';
|
||||
import { BillTypeEnum } from '@fastgpt/global/support/wallet/bill/constants';
|
||||
import { InvoiceType } from '@fastgpt/global/support/wallet/bill/type';
|
||||
import { InvoiceSchemaType } from '../../../../../../../../packages/global/support/wallet/bill/type';
|
||||
export type invoiceBillDataType = {
|
||||
type: BillTypeEnum;
|
||||
price: number;
|
||||
createTime: Date;
|
||||
_id: string;
|
||||
};
|
||||
|
||||
export const getInvoiceBillsList = () =>
|
||||
GET<invoiceBillDataType[]>(`/proApi/support/wallet/bill/invoice/unInvoiceList`);
|
||||
|
||||
export const submitInvoice = (data: InvoiceType) =>
|
||||
POST(`/proApi/support/wallet/bill/invoice/submit`, data);
|
||||
|
||||
export const getInvoiceRecords = (data: RequestPaging) =>
|
||||
POST<InvoiceSchemaType[]>(`/proApi/support/wallet/bill/invoice/records`, data);
|
||||
Reference in New Issue
Block a user