mirror of
https://github.com/labring/FastGPT.git
synced 2025-07-22 04:06:18 +00:00
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:
16
packages/global/support/user/team/type.d.ts
vendored
16
packages/global/support/user/team/type.d.ts
vendored
@@ -90,3 +90,19 @@ export type LafAccountType = {
|
||||
appid: string;
|
||||
pat: string;
|
||||
};
|
||||
|
||||
export type TeamInvoiceHeaderType = {
|
||||
teamName: string;
|
||||
unifiedCreditCode: string;
|
||||
companyAddress: string;
|
||||
companyPhone: string;
|
||||
bankName: string;
|
||||
bankAccount: string;
|
||||
needSpecialInvoice?: boolean;
|
||||
emailAddress: string;
|
||||
};
|
||||
|
||||
export type TeamInvoiceHeaderInfoSchemaType = TeamInvoiceHeaderType & {
|
||||
_id: string;
|
||||
teamId: string;
|
||||
};
|
||||
|
@@ -44,6 +44,7 @@ export enum BillPayWayEnum {
|
||||
balance = 'balance',
|
||||
wx = 'wx'
|
||||
}
|
||||
|
||||
export const billPayWayMap = {
|
||||
[BillPayWayEnum.balance]: {
|
||||
label: 'support.wallet.bill.payWay.balance'
|
||||
|
4
packages/global/support/wallet/bill/invoice/constants.ts
Normal file
4
packages/global/support/wallet/bill/invoice/constants.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export enum InvoiceStatusEnum {
|
||||
submitted = 1,
|
||||
completed = 2
|
||||
}
|
15
packages/global/support/wallet/bill/type.d.ts
vendored
15
packages/global/support/wallet/bill/type.d.ts
vendored
@@ -1,6 +1,6 @@
|
||||
import { StandardSubLevelEnum, SubModeEnum, SubTypeEnum } from '../sub/constants';
|
||||
import { BillPayWayEnum, BillTypeEnum } from './constants';
|
||||
|
||||
import { TeamInvoiceHeaderType } from '../../user/team/type';
|
||||
export type BillSchemaType = {
|
||||
_id: string;
|
||||
userId: string;
|
||||
@@ -29,3 +29,16 @@ export type ChatNodeUsageType = {
|
||||
moduleName: string;
|
||||
model?: string;
|
||||
};
|
||||
|
||||
export type InvoiceType = {
|
||||
amount: number;
|
||||
billIdList: string[];
|
||||
} & TeamInvoiceHeaderType;
|
||||
|
||||
export type InvoiceSchemaType = {
|
||||
teamId: string;
|
||||
_id: string;
|
||||
status: 1 | 2;
|
||||
createTime: Date;
|
||||
finishTime?: Date;
|
||||
} & InvoiceType;
|
||||
|
56
packages/web/common/file/hooks/useSelectFile.tsx
Normal file
56
packages/web/common/file/hooks/useSelectFile.tsx
Normal file
@@ -0,0 +1,56 @@
|
||||
import React, { useRef, useCallback } from 'react';
|
||||
import { Box } from '@chakra-ui/react';
|
||||
import { useToast } from '../../../hooks/useToast';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
export const useSelectFile = (props?: {
|
||||
fileType?: string;
|
||||
multiple?: boolean;
|
||||
maxCount?: number;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const { fileType = '*', multiple = false, maxCount = 10 } = props || {};
|
||||
const { toast } = useToast();
|
||||
const SelectFileDom = useRef<HTMLInputElement>(null);
|
||||
const openSign = useRef<any>();
|
||||
|
||||
const File = useCallback(
|
||||
({ onSelect }: { onSelect: (e: File[], sign?: any) => void }) => (
|
||||
<Box position={'absolute'} w={0} h={0} overflow={'hidden'}>
|
||||
<input
|
||||
ref={SelectFileDom}
|
||||
type="file"
|
||||
accept={fileType}
|
||||
multiple={multiple}
|
||||
onChange={(e) => {
|
||||
const files = e.target.files;
|
||||
|
||||
if (!files || files?.length === 0) return;
|
||||
|
||||
let fileList = Array.from(files);
|
||||
if (fileList.length > maxCount) {
|
||||
toast({
|
||||
status: 'warning',
|
||||
title: t('file:select_file_amount_limit', { max: maxCount })
|
||||
});
|
||||
fileList = fileList.slice(0, maxCount);
|
||||
}
|
||||
onSelect(fileList, openSign.current);
|
||||
|
||||
e.target.value = '';
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
),
|
||||
[fileType, maxCount, multiple, toast]
|
||||
);
|
||||
|
||||
const onOpen = useCallback((sign?: any) => {
|
||||
openSign.current = sign;
|
||||
SelectFileDom.current && SelectFileDom.current.click();
|
||||
}, []);
|
||||
|
||||
return {
|
||||
File,
|
||||
onOpen
|
||||
};
|
||||
};
|
@@ -226,7 +226,10 @@ export const iconPaths = {
|
||||
delete: () => import('./icons/delete.svg'),
|
||||
edit: () => import('./icons/edit.svg'),
|
||||
empty: () => import('./icons/empty.svg'),
|
||||
paragraph: () => import('./icons/paragraph.svg'),
|
||||
export: () => import('./icons/export.svg'),
|
||||
point: () => import('./icons/point.svg'),
|
||||
infoRounded: () => import('./icons/infoRounded.svg'),
|
||||
'file/csv': () => import('./icons/file/csv.svg'),
|
||||
'file/fill/csv': () => import('./icons/file/fill/csv.svg'),
|
||||
'file/fill/doc': () => import('./icons/file/fill/doc.svg'),
|
||||
|
10
packages/web/components/common/Icon/icons/infoRounded.svg
Normal file
10
packages/web/components/common/Icon/icons/infoRounded.svg
Normal file
@@ -0,0 +1,10 @@
|
||||
<svg viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="icon/line/info-rounded" clip-path="url(#clip0_9300_11)">
|
||||
<path id="Icon (Stroke)" fill-rule="evenodd" clip-rule="evenodd" d="M6.99991 2.05302C4.26775 2.05302 2.0529 4.26787 2.0529 7.00003C2.0529 9.73219 4.26775 11.947 6.99991 11.947C9.73207 11.947 11.9469 9.73219 11.9469 7.00003C11.9469 4.26787 9.73207 2.05302 6.99991 2.05302ZM0.88623 7.00003C0.88623 3.62354 3.62342 0.886353 6.99991 0.886353C10.3764 0.886353 13.1136 3.62354 13.1136 7.00003C13.1136 10.3765 10.3764 13.1137 6.99991 13.1137C3.62342 13.1137 0.88623 10.3765 0.88623 7.00003ZM6.41657 4.78789C6.41657 4.46573 6.67774 4.20456 6.99991 4.20456H7.00544C7.3276 4.20456 7.58877 4.46573 7.58877 4.78789C7.58877 5.11006 7.3276 5.37123 7.00544 5.37123H6.99991C6.67774 5.37123 6.41657 5.11006 6.41657 4.78789ZM6.99991 6.4167C7.32207 6.4167 7.58324 6.67786 7.58324 7.00003V9.21217C7.58324 9.53433 7.32207 9.7955 6.99991 9.7955C6.67774 9.7955 6.41657 9.53433 6.41657 9.21217V7.00003C6.41657 6.67786 6.67774 6.4167 6.99991 6.4167Z" />
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_9300_11">
|
||||
<rect width="14" height="14" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
After Width: | Height: | Size: 1.2 KiB |
3
packages/web/components/common/Icon/icons/paragraph.svg
Normal file
3
packages/web/components/common/Icon/icons/paragraph.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M1.19885 4.05802C1.19885 3.68983 1.51614 3.39136 1.90752 3.39136H14.0924C14.4838 3.39136 14.8011 3.68983 14.8011 4.05802C14.8011 4.42621 14.4838 4.72469 14.0924 4.72469H1.90752C1.51614 4.72469 1.19885 4.42621 1.19885 4.05802ZM1.19885 7.95852C1.19885 7.59033 1.51614 7.29185 1.90752 7.29185H14.0924C14.4838 7.29185 14.8011 7.59033 14.8011 7.95852C14.8011 8.32671 14.4838 8.62518 14.0924 8.62518H1.90752C1.51614 8.62518 1.19885 8.32671 1.19885 7.95852ZM1.19885 11.942C1.19885 11.5738 1.51614 11.2753 1.90752 11.2753H9.71358C10.105 11.2753 10.4222 11.5738 10.4222 11.942C10.4222 12.3102 10.105 12.6087 9.71358 12.6087H1.90752C1.51614 12.6087 1.19885 12.3102 1.19885 11.942Z" />
|
||||
</svg>
|
After Width: | Height: | Size: 805 B |
3
packages/web/components/common/Icon/icons/point.svg
Normal file
3
packages/web/components/common/Icon/icons/point.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 6 6" fill="none">
|
||||
<circle cx="3" cy="3" r="3" />
|
||||
</svg>
|
After Width: | Height: | Size: 114 B |
@@ -21,7 +21,9 @@ const PopoverConfirm = ({
|
||||
Trigger,
|
||||
placement = 'bottom-start',
|
||||
offset,
|
||||
onConfirm
|
||||
onConfirm,
|
||||
confirmText,
|
||||
cancelText
|
||||
}: {
|
||||
content: string;
|
||||
showCancel?: boolean;
|
||||
@@ -30,6 +32,8 @@ const PopoverConfirm = ({
|
||||
placement?: PlacementWithLogical;
|
||||
offset?: [number, number];
|
||||
onConfirm: () => any;
|
||||
confirmText?: string;
|
||||
cancelText?: string;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -82,11 +86,11 @@ const PopoverConfirm = ({
|
||||
<HStack mt={1} justifyContent={'flex-end'}>
|
||||
{showCancel && (
|
||||
<Button variant={'whiteBase'} size="sm" onClick={onClose}>
|
||||
{t('common:common.Cancel')}
|
||||
{cancelText || t('common:common.Cancel')}
|
||||
</Button>
|
||||
)}
|
||||
<Button isLoading={loading} variant={map.variant} size="sm" onClick={onclickConfirm}>
|
||||
{t('common:common.Confirm')}
|
||||
{confirmText || t('common:common.Confirm')}
|
||||
</Button>
|
||||
</HStack>
|
||||
</PopoverContent>
|
||||
|
@@ -8,7 +8,7 @@ import { Box, Flex } from '@chakra-ui/react';
|
||||
import { useBasicTypeaheadTriggerMatch } from '../../utils';
|
||||
import { EditorVariableLabelPickerType } from '../../type';
|
||||
import { WorkflowIOValueTypeEnum } from '@fastgpt/global/core/workflow/constants';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import Avatar from '../../../../Avatar';
|
||||
|
||||
interface EditorVariableItemType {
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import { ChevronRightIcon } from '@chakra-ui/icons';
|
||||
import { Box } from '@chakra-ui/react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import Avatar from '../../../../../../../components/common/Avatar';
|
||||
|
||||
export default function VariableLabel({
|
||||
|
@@ -2,7 +2,7 @@ import { useRef, useState, useCallback, useMemo, useEffect } from 'react';
|
||||
import { IconButton, Flex, Box, Input } from '@chakra-ui/react';
|
||||
import { ArrowBackIcon, ArrowForwardIcon } from '@chakra-ui/icons';
|
||||
import { useMutation } from '@tanstack/react-query';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { throttle } from 'lodash';
|
||||
import { useToast } from './useToast';
|
||||
import { getErrText } from '@fastgpt/global/common/error/utils';
|
||||
|
@@ -75,6 +75,7 @@
|
||||
"unit": "Number"
|
||||
},
|
||||
"move_app": "Move app",
|
||||
"no": "no",
|
||||
"paste_config": "Paste Config",
|
||||
"plugin_cost_per_times": "{{cost}}/per time",
|
||||
"plugin_dispatch": "Plugins",
|
||||
@@ -84,6 +85,27 @@
|
||||
"search_app": "Search app",
|
||||
"setting_app": "Settings",
|
||||
"setting_plugin": "Setting plugin",
|
||||
"support": {
|
||||
"wallet": {
|
||||
"bill_tag": {
|
||||
"bill": "billing records",
|
||||
"default_header": "Default header",
|
||||
"invoice": "Invoicing records"
|
||||
},
|
||||
"invoice_data": {
|
||||
"bank": "Bank of deposit",
|
||||
"bank_account": "Account opening account",
|
||||
"company_address": "company address",
|
||||
"company_phone": "company phone",
|
||||
"email": "email address",
|
||||
"in_valid": "There are empty fields or incorrect email formats",
|
||||
"need_special_invoice": "Do you need a special ticket?",
|
||||
"organization_name": "name of association",
|
||||
"unit_code": "same credit code"
|
||||
},
|
||||
"invoicing": "Invoicing"
|
||||
}
|
||||
},
|
||||
"template": {
|
||||
"simple_robot": "Simple Robot"
|
||||
},
|
||||
@@ -95,6 +117,7 @@
|
||||
"templateTags": {
|
||||
"Image_generation": "Image generation",
|
||||
"Office_services": "Office searvices",
|
||||
"Recommendation": "recommend",
|
||||
"Roleplay": "Roleplay",
|
||||
"Web_search": "Web search",
|
||||
"Writing": "Writing"
|
||||
@@ -115,6 +138,7 @@
|
||||
"Create plugin bot": "Create plugin bot",
|
||||
"Create simple bot": "Create simple bot",
|
||||
"Create simple bot tip": "Create simple AI applications in form form",
|
||||
"Create template tip": "Explore more ways to play in the template market to help you understand and use various applications",
|
||||
"Create workflow bot": "Create workflow bot",
|
||||
"Create workflow tip": "Through the way of low code, build a logically complex multi-round dialogue AI application, recommended for advanced players",
|
||||
"Http plugin": "Http plugin",
|
||||
@@ -160,5 +184,6 @@
|
||||
"user_file_input_desc": "Links to documents and images uploaded by users",
|
||||
"user_select": "User select",
|
||||
"user_select_tip": "The module can have multiple options that lead to different workflow branches"
|
||||
}
|
||||
},
|
||||
"yes": "yes"
|
||||
}
|
||||
|
@@ -29,6 +29,7 @@
|
||||
"UnKnow": "Unknown",
|
||||
"Warning": "Warning",
|
||||
"add_new": "Add new",
|
||||
"back": "return",
|
||||
"chose_condition": "Selection criteria",
|
||||
"chosen": "selected",
|
||||
"classification": "Classification",
|
||||
@@ -36,6 +37,8 @@
|
||||
"code_editor": "Code edit",
|
||||
"code_error": {
|
||||
"app_error": {
|
||||
"invalid_app_type": "Wrong application type",
|
||||
"invalid_owner": "Illegal app owner",
|
||||
"not_exist": "App does not exist",
|
||||
"un_auth_app": "No permission to operate this application"
|
||||
},
|
||||
@@ -237,6 +240,7 @@
|
||||
"empty": "This directory has nothing selectable~",
|
||||
"open_dataset": "Open knowledge base"
|
||||
},
|
||||
"have_done": "Completed",
|
||||
"input": {
|
||||
"Repeat Value": "Duplicate value"
|
||||
},
|
||||
@@ -258,6 +262,9 @@
|
||||
"error tip": "Speech to text failed",
|
||||
"not support": "Your browser does not support speech input"
|
||||
},
|
||||
"submit_success": "Submitted successfully",
|
||||
"submitted": "Submitted",
|
||||
"submitting": "Submitting",
|
||||
"support": "support",
|
||||
"system": {
|
||||
"Commercial version function": "Commercial version feature",
|
||||
@@ -358,6 +365,14 @@
|
||||
"logs": {
|
||||
"Source And Time": "Source & Time"
|
||||
},
|
||||
"more": "View more",
|
||||
"navbar": {
|
||||
"External": "external use",
|
||||
"Flow mode": "Advanced orchestration",
|
||||
"Publish": "release",
|
||||
"Publish app": "Publish application",
|
||||
"Simple mode": "Easy configuration"
|
||||
},
|
||||
"no_app": "There is no application yet, go and create one!",
|
||||
"not_published": "Unpublished",
|
||||
"outLink": {
|
||||
@@ -600,7 +615,8 @@
|
||||
"success": "Start syncing"
|
||||
}
|
||||
},
|
||||
"training": {}
|
||||
"training": {
|
||||
}
|
||||
},
|
||||
"data": {
|
||||
"Auxiliary Data": "Auxiliary data",
|
||||
@@ -1061,6 +1077,7 @@
|
||||
"Tools": "Tools"
|
||||
},
|
||||
"new_create": "Create New",
|
||||
"no": "no",
|
||||
"no_data": "No data",
|
||||
"no_laf_env": "The system is not configured with Laf environment",
|
||||
"not_yet_introduced": "No introduction yet",
|
||||
@@ -1097,7 +1114,12 @@
|
||||
"Resume InheritPermission Confirm": "Whether to resume to inherit the parent folder's permission?",
|
||||
"Resume InheritPermission Failed": "Resume Failed",
|
||||
"Resume InheritPermission Success": "Resume Success",
|
||||
"change_owner_tip": "Your permissions will not be retained after transfer"
|
||||
"change_owner": "transfer ownership",
|
||||
"change_owner_failed": "Transfer ownership failed",
|
||||
"change_owner_placeholder": "Enter username to find account",
|
||||
"change_owner_success": "Successfully transferred ownership",
|
||||
"change_owner_tip": "Your administrator rights will be retained after the transfer",
|
||||
"change_owner_to": "transferred to"
|
||||
},
|
||||
"plugin": {
|
||||
"App": "Select app",
|
||||
@@ -1225,6 +1247,7 @@
|
||||
"Standard Plan Detail": "Plan details",
|
||||
"To read plan": "View plan",
|
||||
"amount_0": "The purchase quantity cannot be 0",
|
||||
"apply_invoice": "Apply for making an invoice",
|
||||
"bill": {
|
||||
"Number": "Order number",
|
||||
"Status": "Status",
|
||||
@@ -1241,12 +1264,36 @@
|
||||
"success": "Payment successful"
|
||||
}
|
||||
},
|
||||
"bill_detail": "Bill details",
|
||||
"bill_tag": {
|
||||
"bill": "billing records",
|
||||
"default_header": "Default header",
|
||||
"invoice": "Invoicing records"
|
||||
},
|
||||
"billable_invoice": "Billable bills",
|
||||
"buy_resource": "Purchase resource pack",
|
||||
"has_invoice": "Whether the invoice has been issued",
|
||||
"invoice_amount": "Invoice amount",
|
||||
"invoice_data": {
|
||||
"bank": "Bank of deposit",
|
||||
"bank_account": "Account opening account",
|
||||
"company_address": "Company address",
|
||||
"company_phone": "Company phone number",
|
||||
"email": "Email address",
|
||||
"in_valid": "There are empty fields or incorrect email formats",
|
||||
"need_special_invoice": "Do you need a special ticket?",
|
||||
"organization_name": "Organization name",
|
||||
"unit_code": "same credit code"
|
||||
},
|
||||
"invoice_detail": "Invoice details",
|
||||
"invoice_info": "The invoice will be sent to your mailbox within 3-7 working days, please be patient.",
|
||||
"invoicing": "Invoicing",
|
||||
"moduleName": {
|
||||
"index": "Index generation",
|
||||
"qa": "QA split"
|
||||
},
|
||||
"noBill": "No bill records~",
|
||||
"no_invoice": "No invoicing record yet",
|
||||
"subscription": {
|
||||
"AI points": "AI points",
|
||||
"AI points click to read tip": "Each call to the AI model will consume a certain amount of AI points (similar to Tokens). Click to view detailed calculation rules.",
|
||||
@@ -1322,7 +1369,8 @@
|
||||
"Total points": "AI points consumed",
|
||||
"Usage Detail": "Usage details",
|
||||
"Whisper": "Voice input"
|
||||
}
|
||||
},
|
||||
"use_default": "Use default header"
|
||||
}
|
||||
},
|
||||
"sync_link": "Sync Link",
|
||||
@@ -1442,5 +1490,6 @@
|
||||
"type": "type"
|
||||
},
|
||||
"verification": "verify",
|
||||
"xx_search_result": "{{key}} Search results"
|
||||
"xx_search_result": "{{key}} Search results",
|
||||
"yes": "yes"
|
||||
}
|
||||
|
@@ -1,10 +1,16 @@
|
||||
{
|
||||
"app_key_tips": "These keys have the current application identification, refer to the document for specific use ",
|
||||
"basic_info": "Basic information",
|
||||
"copy_link_hint": "Copy the link below to the specified location",
|
||||
"create_api_key": "Create new Key",
|
||||
"create_link": "Create link",
|
||||
"default_response": "Default Response",
|
||||
"edit_api_key": "Edit Key information",
|
||||
"edit_feishu_bot": "Edit Feishu Robot",
|
||||
"edit_link": "Edit",
|
||||
"feishu_api": "Feishu interface",
|
||||
"feishu_bot": "Feishu Robot",
|
||||
"feishu_bot_desc": "Directly access Feishu Robot through API",
|
||||
"feishu_name": "Lark",
|
||||
"key_alias": "key alias, for display only ",
|
||||
"key_tips": "You can use the API Key to access certain interfaces (you can't access the app, you need to use the API key within the app to access the app)",
|
||||
@@ -12,9 +18,21 @@
|
||||
"official_account": {
|
||||
"params": "Wechat params"
|
||||
},
|
||||
"new_feishu_bot": "Added Feishu robot",
|
||||
"publish_name": "name",
|
||||
"qpm_is_empty": "QPM cannot be empty",
|
||||
"qpm_tips": "How many times per minute can each IP ask at most",
|
||||
"request_address": "Request address",
|
||||
"show_share_link_modal_title": "Get started",
|
||||
"token_auth": "Token authentication",
|
||||
"token_auth_tips": "Identity verification server address, if this value is filled, a request will be sent to the specified server before each conversation to perform identity verification",
|
||||
"token_auth_use_cases": "View usage instructions for identity verification"
|
||||
"token_auth_use_cases": "View usage instructions for identity verification",
|
||||
"wecom": {
|
||||
"api": "Qiwei API",
|
||||
"bot": "Enterprise WeChat robot",
|
||||
"bot_desc": "Directly access enterprise WeChat robots through API",
|
||||
"create_modal_title": "Create a Qiwei robot",
|
||||
"edit_modal_title": "Edit Qiwei Robot",
|
||||
"title": "Publish to enterprise WeChat robot"
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,12 @@
|
||||
{
|
||||
"bind_inform_account_error": "Abnormal binding notification account",
|
||||
"bind_inform_account_success": "Binding notification account successful",
|
||||
"code_error": {
|
||||
"app_error": {
|
||||
"invalid_app_type": "Wrong application type",
|
||||
"invalid_owner": "Illegal app owner"
|
||||
}
|
||||
},
|
||||
"delete": {
|
||||
"admin_failed": "Failed to delete administrator",
|
||||
"admin_success": "Administrator deleted successfully"
|
||||
@@ -17,7 +23,8 @@
|
||||
},
|
||||
"name": "name",
|
||||
"notification": {
|
||||
"Bind Notification Pipe Hint": "Bind the email address or mobile phone number for receiving notifications to ensure that you receive important system notifications in a timely manner."
|
||||
"Bind Notification Pipe Hint": "Bind the email address or mobile phone number for receiving notifications to ensure that you receive important system notifications in a timely manner.",
|
||||
"remind_owner_bind": "Please remind the creator to bind the notification account"
|
||||
},
|
||||
"operations": "operate",
|
||||
"password": {
|
||||
@@ -48,6 +55,12 @@
|
||||
"Read desc": "Members can only read related resources and cannot create new resources.",
|
||||
"Write": "Write",
|
||||
"Write tip": "In addition to readable resources, you can also create new resources",
|
||||
"change_owner": "transfer ownership",
|
||||
"change_owner_failed": "Transfer ownership failed",
|
||||
"change_owner_placeholder": "Enter username to find account",
|
||||
"change_owner_success": "Successfully transferred ownership",
|
||||
"change_owner_tip": "Your administrator rights will be retained after the transfer",
|
||||
"change_owner_to": "transferred to",
|
||||
"only_collaborators": "Collaborator access only",
|
||||
"team_read": "Team accessible",
|
||||
"team_write": "Team editable"
|
||||
|
@@ -19,6 +19,9 @@
|
||||
"package_overlay_a": "可以的。每次购买的资源包都是独立的,在其有效期内将会叠加使用。AI积分会优先扣除最先过期的资源包。",
|
||||
"package_overlay_q": "额外资源包可以叠加么?"
|
||||
},
|
||||
"yes": "是",
|
||||
"no": "否",
|
||||
"back": "返回",
|
||||
"Folder": "文件夹",
|
||||
"Login": "登录",
|
||||
"Move": "移动",
|
||||
@@ -135,6 +138,7 @@
|
||||
"Detail": "详情",
|
||||
"Documents": "文档",
|
||||
"Done": "完成",
|
||||
"have_done": "已完成",
|
||||
"Edit": "编辑",
|
||||
"Exit": "退出",
|
||||
"Expired Time": "过期时间",
|
||||
@@ -190,7 +194,11 @@
|
||||
"Set Name": "取个名字",
|
||||
"Setting": "设置",
|
||||
"Status": "状态",
|
||||
"submitting": "提交中",
|
||||
"submit_success": "提交成功",
|
||||
"Submit failed": "提交失败",
|
||||
"submitted": "已提交",
|
||||
|
||||
"Success": "成功",
|
||||
"Sync success": "同步成功",
|
||||
"Team": "团队",
|
||||
@@ -1233,8 +1241,17 @@
|
||||
"wallet": {
|
||||
"Ai point every thousand tokens": "{{points}} 积分/1K tokens",
|
||||
"Amount": "金额",
|
||||
"Bills": "账单",
|
||||
"Bills": "账单与开票",
|
||||
"invoicing": "开票",
|
||||
"invoice_amount": "开票金额",
|
||||
"bill_detail": "账单详情",
|
||||
"billable_invoice": "可开票账单",
|
||||
"apply_invoice": "申请开票",
|
||||
"Buy": "购买",
|
||||
"invoice_detail": "发票详情",
|
||||
"invoice_info": "发票将在 3-7 个工作日内发送至邮箱,请耐心等待",
|
||||
"no_invoice": "暂无开票记录",
|
||||
"has_invoice": "是否已开票",
|
||||
"Confirm pay": "支付确认",
|
||||
"Not sufficient": "您的 AI 积分不足,请先升级套餐或购买额外 AI 积分后继续使用。",
|
||||
"Plan expired time": "套餐到期时间",
|
||||
@@ -1242,6 +1259,23 @@
|
||||
"Standard Plan Detail": "套餐详情",
|
||||
"To read plan": "查看套餐",
|
||||
"amount_0": "购买数量不能为0",
|
||||
"use_default": "使用默认抬头",
|
||||
"bill_tag": {
|
||||
"bill": "账单记录",
|
||||
"invoice": "开票记录",
|
||||
"default_header": "默认抬头"
|
||||
},
|
||||
"invoice_data": {
|
||||
"organization_name": "组织名称",
|
||||
"unit_code": "统一信用代码",
|
||||
"company_address": "公司地址",
|
||||
"company_phone": "公司电话",
|
||||
"bank": "开户银行",
|
||||
"bank_account": "开户账号",
|
||||
"need_special_invoice": "是否需要专票",
|
||||
"email": "邮箱地址",
|
||||
"in_valid": "存在空字段或邮箱格式错误"
|
||||
},
|
||||
"bill": {
|
||||
"Number": "订单号",
|
||||
"Status": "状态",
|
||||
|
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