diff --git a/frontend/desktop/src/components/account/AccountCenter/index.tsx b/frontend/desktop/src/components/account/AccountCenter/index.tsx index 43a29c05a5e..8d70dd22dda 100644 --- a/frontend/desktop/src/components/account/AccountCenter/index.tsx +++ b/frontend/desktop/src/components/account/AccountCenter/index.tsx @@ -263,7 +263,7 @@ export default function Index(props: Omit) { } /> )} - {conf.authConfig?.idp.sms.enabled && ( + {conf.authConfig?.idp.sms.enabled && conf.authConfig.idp.sms.ali.enabled && ( {t('common:phone')}} RightElement={ @@ -299,40 +299,42 @@ export default function Index(props: Omit) { } /> )} - {t('common:email')}} - RightElement={ - <> - - {providerState.EMAIL.isBinding - ? providerState.EMAIL.id.replace(/(\d{3})\d+(\d{4})/, '$1****$2') - : t('common:unbound')} - - - { - providerState.EMAIL.isBinding - ? setPageState(PageState.EMAIL_CHANGE_BIND) - : setPageState(PageState.EMAIL_BIND); - }} - /> - {providerState.EMAIL.isBinding && providerState.total > 1 && ( + {conf.authConfig?.idp.sms.enabled && conf.authConfig.idp.sms.email.enabled && ( + {t('common:email')}} + RightElement={ + <> + + {providerState.EMAIL.isBinding + ? providerState.EMAIL.id.replace(/(\d{3})\d+(\d{4})/, '$1****$2') + : t('common:unbound')} + + { - setPageState(PageState.EMAIL_UNBIND); + providerState.EMAIL.isBinding + ? setPageState(PageState.EMAIL_CHANGE_BIND) + : setPageState(PageState.EMAIL_BIND); }} /> - )} - - - } - /> + {providerState.EMAIL.isBinding && providerState.total > 1 && ( + { + setPageState(PageState.EMAIL_UNBIND); + }} + /> + )} + + + } + /> + )} (LoginType.NONE); @@ -113,8 +110,8 @@ export default function SigninComponent() { ]); useEffect(() => { - setTabIndex(needSms ? LoginType.SMS : needPassword ? LoginType.PASSWORD : LoginType.NONE); - }, [needPassword, needSms]); + setTabIndex(needPhone ? LoginType.SMS : needPassword ? LoginType.PASSWORD : LoginType.NONE); + }, [needPassword, needPhone]); const LoginComponent = useMemo( () => (tabIndex !== LoginType.NONE ? loginConfig[tabIndex].component : null), diff --git a/frontend/desktop/src/pages/api/platform/getAuthConfig.ts b/frontend/desktop/src/pages/api/platform/getAuthConfig.ts index fea71d05a62..8aeebb90fe3 100644 --- a/frontend/desktop/src/pages/api/platform/getAuthConfig.ts +++ b/frontend/desktop/src/pages/api/platform/getAuthConfig.ts @@ -29,7 +29,13 @@ function genResAuthClientConfig(conf: AuthConfigType) { enabled: !!conf.idp.password?.enabled }, sms: { - enabled: !!conf.idp.sms?.ali?.enabled + enabled: !!conf.idp.sms?.enabled, + ali: { + enabled: !!conf.idp.sms?.ali?.enabled + }, + email: { + enabled: !!conf.idp.sms?.email?.enabled + } }, github: { enabled: !!conf.idp.github?.enabled, diff --git a/frontend/desktop/src/types/system.ts b/frontend/desktop/src/types/system.ts index 2f25e2f4f7d..dac507ced71 100644 --- a/frontend/desktop/src/types/system.ts +++ b/frontend/desktop/src/types/system.ts @@ -165,7 +165,19 @@ export type AuthClientConfigType = DeepRequired< 'cloudVitrualMachineUrl' ] > ->; +> & { + idp: { + sms: { + enabled: boolean; + ali: { + enabled: boolean; + }; + email: { + enabled: boolean; + }; + }; + }; +}; export type JwtConfigType = { internal?: string; @@ -277,7 +289,13 @@ export const DefaultAuthClientConfig: AuthClientConfigType = { proxyAddress: '' }, sms: { - enabled: false + enabled: false, + ali: { + enabled: false + }, + email: { + enabled: false + } }, oauth2: { enabled: false, diff --git a/frontend/providers/costcenter/public/locales/en/common.json b/frontend/providers/costcenter/public/locales/en/common.json index b602075aa52..d7d9220483d 100644 --- a/frontend/providers/costcenter/public/locales/en/common.json +++ b/frontend/providers/costcenter/public/locales/en/common.json @@ -24,6 +24,7 @@ "Select Amount": "Select Amount", "Port Amount": "Port Amount", "CPU Amount": "CPU Amount", + "GPU Amount": "GPU Amount", "Storage Amount": "Storage Amount", "Memory Amount": "Memory Amount", "Network Amount": "Network Amount", @@ -49,6 +50,7 @@ "Network": "Network", "Storage": "Storage", "cpu": "CPU", + "gpu": "GPU", "memory": "Memory", "network": "Network", "storage": "Storage", diff --git a/frontend/providers/costcenter/public/locales/zh/common.json b/frontend/providers/costcenter/public/locales/zh/common.json index 1a2f8b7e39d..0bca7f2394f 100644 --- a/frontend/providers/costcenter/public/locales/zh/common.json +++ b/frontend/providers/costcenter/public/locales/zh/common.json @@ -43,11 +43,13 @@ "Storage": "存储卷", "Network": "网络", "CPU Amount": "CPU金额", + "GPU Amount": "GPU金额", "Port Amount": "端口金额", "Storage Amount": "存储金额", "Memory Amount": "内存金额", "Network Amount": "网络金额", "cpu": "CPU", + "gpu": "GPU", "memory": "内存", "storage": "存储卷", "network": "网络", diff --git a/frontend/providers/costcenter/src/components/RechargeModal.tsx b/frontend/providers/costcenter/src/components/RechargeModal.tsx index efb889f053a..003a048b30b 100644 --- a/frontend/providers/costcenter/src/components/RechargeModal.tsx +++ b/frontend/providers/costcenter/src/components/RechargeModal.tsx @@ -77,7 +77,7 @@ function WechatPayment(props: { complete: number; codeURL?: string; tradeNO?: st px="37px" justify={'center'} align={'center'} - mt={'135px'} + m={'auto'} display={'flex'} justifyContent={'center'} alignItems={'center'} @@ -411,7 +411,12 @@ const RechargeModal = forwardRef( return ( - + {!detail ? ( complete === 0 ? ( <> @@ -447,35 +452,37 @@ const RechargeModal = forwardRef( {formatMoney(balance).toFixed(2)} - + {t('Select Amount')} - - - - {t('first_recharge_title')} - - - {t('first_recharge_tips')} - - } - > - - - + {specialBonus && specialBonus.length > 0 && ( + + + + {t('first_recharge_title')} + + + {t('first_recharge_tips')} + + } + > + + + + )} {steps.map((amount, index) => ( diff --git a/frontend/providers/costcenter/src/components/billing/AmountDisplay.tsx b/frontend/providers/costcenter/src/components/billing/AmountDisplay.tsx index b33ff9479bd..eaad7c56735 100644 --- a/frontend/providers/costcenter/src/components/billing/AmountDisplay.tsx +++ b/frontend/providers/costcenter/src/components/billing/AmountDisplay.tsx @@ -1,15 +1,17 @@ -import { useQuery } from '@tanstack/react-query'; +import CurrencySymbol from '@/components/CurrencySymbol'; import request from '@/service/request'; +import useBillingStore from '@/stores/billing'; +import useEnvStore from '@/stores/env'; +import useOverviewStore from '@/stores/overview'; import { formatMoney } from '@/utils/format'; +import { Box, Flex, Text } from '@chakra-ui/react'; +import { useQuery } from '@tanstack/react-query'; import { useTranslation } from 'next-i18next'; -import { Text, Box, Flex } from '@chakra-ui/react'; -import CurrencySymbol from '@/components/CurrencySymbol'; -import useOverviewStore from '@/stores/overview'; -import useBillingStore from '@/stores/billing'; import { useMemo } from 'react'; export default function AmountDisplay({ onlyOut = false }: { onlyOut?: boolean }) { const { startTime, endTime } = useOverviewStore(); + const { currency } = useEnvStore(); const { getNamespace, getAppType, getRegion, getAppName } = useBillingStore(); const rechargeQueryBody = { startTime, @@ -60,7 +62,7 @@ export default function AmountDisplay({ onlyOut = false }: { onlyOut?: boolean } {t(item.title)}: - + {formatMoney(item.value).toFixed(2)} ))} diff --git a/frontend/providers/costcenter/src/components/cost_overview/components/pieChart.tsx b/frontend/providers/costcenter/src/components/cost_overview/components/pieChart.tsx index 47face06b72..65f11b02577 100644 --- a/frontend/providers/costcenter/src/components/cost_overview/components/pieChart.tsx +++ b/frontend/providers/costcenter/src/components/cost_overview/components/pieChart.tsx @@ -1,4 +1,5 @@ import { resourceType } from '@/constants/billing'; +import useEnvStore from '@/stores/env'; import { formatMoney } from '@/utils/format'; import ReactEChartsCore from 'echarts-for-react/lib/core'; import { PieChart } from 'echarts/charts'; @@ -20,9 +21,10 @@ echarts.use([ export default function CostChart({ data }: { data: number[]; appName: string }) { const { t } = useTranslation(); - + const { gpuEnabled } = useEnvStore(); const radius = ['50%', '90%']; - const result = [0, 1, 2, 3, 4].map((_, i) => { + + const result = (gpuEnabled ? [0, 1, 2, 3, 4, 5] : [0, 1, 2, 3, 4]).map((_, i) => { return [t(resourceType[i]), formatMoney(data[i]).toFixed(2)]; }); const title = t('All APP', { ns: 'applist' }) + '\n' + t('Cost Form'); diff --git a/frontend/providers/costcenter/src/components/cost_overview/cost.tsx b/frontend/providers/costcenter/src/components/cost_overview/cost.tsx index b115de9f1fb..6fc31516d04 100644 --- a/frontend/providers/costcenter/src/components/cost_overview/cost.tsx +++ b/frontend/providers/costcenter/src/components/cost_overview/cost.tsx @@ -1,15 +1,15 @@ -import { Box, Flex, HStack, Text } from '@chakra-ui/react'; -import { useTranslation } from 'next-i18next'; -import dynamic from 'next/dynamic'; import Notfound from '@/components/notFound'; -import { useQuery } from '@tanstack/react-query'; -import useOverviewStore from '@/stores/overview'; import request from '@/service/request'; -import { ApiResp, PropertiesCost } from '@/types'; import useBillingStore from '@/stores/billing'; +import useOverviewStore from '@/stores/overview'; +import { PropertiesCost } from '@/types'; +import { Box, Flex, HStack, Text } from '@chakra-ui/react'; +import { useQuery } from '@tanstack/react-query'; +import { useTranslation } from 'next-i18next'; +import dynamic from 'next/dynamic'; +import SelectRange from '../billing/selectDateRange'; import AppNameMenu from '../menu/AppNameMenu'; import AppTypeMenu from '../menu/AppTypeMenu'; -import SelectRange from '../billing/selectDateRange'; const Chart = dynamic(() => import('./components/pieChart'), { ssr: false }); @@ -18,7 +18,6 @@ export const Cost = function Cost() { const startTime = useOverviewStore((state) => state.startTime); const endTime = useOverviewStore((state) => state.endTime); const { getNamespace, getAppName, getAppType, getRegion } = useBillingStore(); - const query = { namespace: getNamespace()?.[0] || '', appType: getAppType(), @@ -30,22 +29,11 @@ export const Cost = function Cost() { const { data, isInitialLoading, isFetching } = useQuery({ queryKey: ['billing', 'properties', 'costs', query], queryFn: () => { - return request.post>('/api/billing/costDistrube', query); + return request.post('/api/billing/costDistrube', query); }, select(data) { const _data = data.data; - return [ - // @ts-ignore - _data['0'], - // @ts-ignore - _data['1'], - // @ts-ignore - _data['2'], - // @ts-ignore - _data['3'], - // @ts-ignore - _data['4'] - ]; + return [_data['0'], _data['1'], _data['2'], _data['3'], _data['4'], _data['5']]; } }); return ( @@ -83,7 +71,7 @@ export const Cost = function Cost() { ) : ( - + )} ); diff --git a/frontend/providers/costcenter/src/components/table/billingTable.tsx b/frontend/providers/costcenter/src/components/table/billingTable.tsx index b734a8face1..d8e56bb2737 100644 --- a/frontend/providers/costcenter/src/components/table/billingTable.tsx +++ b/frontend/providers/costcenter/src/components/table/billingTable.tsx @@ -35,8 +35,7 @@ export function BillingDetailsTable({ ...styles }: { data: APPBillingItem[] } & TableContainerProps) { const { t } = useTranslation(); - const currency = useEnvStore((s) => s.currency); - + const { currency, gpuEnabled } = useEnvStore((s) => s); const columns = useMemo(() => { const columnHelper = createColumnHelper(); const customTh = (needCurrency?: boolean) => @@ -56,12 +55,14 @@ export function BillingDetailsTable({ function CustomCell(props: CellContext) { return ; }; - const getUnit = (x: string) => { + const getUnit = ( + x: 'cpu' | 'memory' | 'storage' | 'gpu' | 'network' | 'services.nodeports' + ) => { return function CustomCell(props: CellContext) { const resourceEntity = valuationMap.get(x); if (!resourceEntity) return '0'; const unit = resourceEntity.unit; - return props.cell.getValue() / resourceEntity.scale + ' ' + unit; + return props.cell.getValue() / resourceEntity.scale + ' ' + t(unit, { ns: 'common' }); }; }; return [ @@ -138,6 +139,20 @@ export function BillingDetailsTable({ header: customTh(), cell: customCell() }), + ...(gpuEnabled + ? [ + columnHelper.accessor((row) => row.used[5], { + id: TableHeaderID.GPU, + header: customTh(), + cell: getUnit('gpu') + }), + columnHelper.accessor((row) => row.used_amount[5], { + id: TableHeaderID.GPUAmount, + header: customTh(), + cell: customCell() + }) + ] + : []), columnHelper.accessor((row) => row.time, { id: TableHeaderID.TransactionTime, header: customTh(), diff --git a/frontend/providers/costcenter/src/components/valuation/CalculatorPanel/index.tsx b/frontend/providers/costcenter/src/components/valuation/CalculatorPanel/index.tsx index f23cc10285c..4a4f4029ef7 100644 --- a/frontend/providers/costcenter/src/components/valuation/CalculatorPanel/index.tsx +++ b/frontend/providers/costcenter/src/components/valuation/CalculatorPanel/index.tsx @@ -173,7 +173,7 @@ export default function CalculatorPanel({ }); } - const gpuData = priceData.filter((x) => x.title.startsWith('gpu-')); + const gpuData = priceData.filter((x) => x.isGpu); const totalAmount = useMemo(() => { const cpuPrice = priceData.find((x) => x.title === 'cpu')?.price || 0; const cpuAmount = config.resources.cpu.val * cpuPrice; @@ -335,54 +335,56 @@ export default function CalculatorPanel({ {/*gpu*/} - - - - - {t('GPU')} - - - - x.title)} - triggerRender={({ text, idx }) => { - const Icon = gpuData[idx].icon; - return ( - - - {text} - - ); - }} - itemRender={({ text, idx }) => { - const Icon = gpuData[idx].icon; - return ( - - - {text} - - ); - }} - > - { - if (count < 0) return; - updateGpuCount(count); - }} - min={0} - /> - - + {gpuEnabled && ( + + + + + {t('GPU')} + + + + x.title)} + triggerRender={({ text, idx }) => { + const Icon = gpuData[idx].icon; + return ( + + + {text} + + ); + }} + itemRender={({ text, idx }) => { + const Icon = gpuData[idx].icon; + return ( + + + {text} + + ); + }} + > + { + if (count < 0) return; + updateGpuCount(count); + }} + min={0} + /> + + + )} diff --git a/frontend/providers/costcenter/src/components/valuation/PriceTablePanel.tsx b/frontend/providers/costcenter/src/components/valuation/PriceTablePanel.tsx index 041ace5a4d6..94308de4b74 100644 --- a/frontend/providers/costcenter/src/components/valuation/PriceTablePanel.tsx +++ b/frontend/providers/costcenter/src/components/valuation/PriceTablePanel.tsx @@ -7,9 +7,11 @@ import { PricePayload, PriceTable } from '../table/PriceTable'; export function PriceTablePanel({ priceData }: { priceData: PricePayload[] }) { const { t } = useTranslation(); const gpuEnabled = useEnvStore((state) => state.gpuEnabled); - const baseData = priceData.filter((x) => x.title !== 'network' && !x.title.startsWith('gpu-')); - const networkData = priceData.filter((x) => x.title === 'network'); const gpuData = priceData.filter((x) => x.isGpu); + const otherData = priceData.filter((x) => !x.isGpu); + const networkData = otherData.filter((x) => x.title === 'network'); + const baseData = otherData.filter((x) => x.title !== 'network'); + return ( diff --git a/frontend/providers/costcenter/src/components/valuation/quota.tsx b/frontend/providers/costcenter/src/components/valuation/quota.tsx index 13648840f83..445f70897d1 100644 --- a/frontend/providers/costcenter/src/components/valuation/quota.tsx +++ b/frontend/providers/costcenter/src/components/valuation/quota.tsx @@ -1,12 +1,14 @@ import { valuationMap } from '@/constants/payment'; import { UserQuotaItemType } from '@/pages/api/getQuota'; import request from '@/service/request'; +import useEnvStore from '@/stores/env'; import { ApiResp } from '@/types'; import { Box, Divider, HStack, Stack, StackProps, Text } from '@chakra-ui/react'; import { useQuery } from '@tanstack/react-query'; import dynamic from 'next/dynamic'; import { useTranslation } from 'react-i18next'; import CpuIcon from '../icons/CpuIcon'; +import GpuIcon from '../icons/GpuIcon'; import { MemoryIcon } from '../icons/MemoryIcon'; import { StorageIcon } from '../icons/StorageIcon'; const QuotaPie = dynamic(() => import('../cost_overview/components/quotaPieChart'), { ssr: false }); @@ -15,20 +17,27 @@ export default function Quota(props: StackProps) { const { data } = useQuery(['quota'], () => request>('/api/getQuota') ); + const { gpuEnabled } = useEnvStore(); const quota = (data?.data?.quota || []) - .filter((d) => d.type !== 'gpu') - .map((d) => { + .filter((d) => gpuEnabled || d.type !== 'gpu') + .flatMap((d) => { + const entity = valuationMap.get(d.type); + if (!entity) { + return []; + } const _limit = Number.parseInt(d.limit * 1000 + ''); const _used = Number.parseInt(d.used * 1000 + ''); - return { - ...d, - limit: _limit / 1000, - used: _used / 1000, - remain: (_limit - _used) / 1000, - title: t(d.type), - unit: valuationMap.get(d.type)?.unit, - bg: valuationMap.get(d.type)?.bg - }; + return [ + { + ...d, + limit: _limit / 1000, + used: _used / 1000, + remain: (_limit - _used) / 1000, + title: t(d.type), + unit: t(entity.unit), + bg: entity.bg + } + ]; }); return ( @@ -43,6 +52,8 @@ export default function Quota(props: StackProps) { ) : item.type === 'storage' ? ( + ) : item.type === 'gpu' ? ( + ) : ( <> )} diff --git a/frontend/providers/costcenter/src/constants/billing.ts b/frontend/providers/costcenter/src/constants/billing.ts index 84b81276f8c..474d76816b5 100644 --- a/frontend/providers/costcenter/src/constants/billing.ts +++ b/frontend/providers/costcenter/src/constants/billing.ts @@ -49,7 +49,8 @@ export enum TableHeaderID { 'TraderID' = 'Trader ID', 'Status' = 'Invoice Status', 'InvoiceCreateTime' = 'Invoice Create Time', - 'InvoiceUpdateTime' = 'Invoice Update Time' + 'InvoiceUpdateTime' = 'Invoice Update Time', + 'GPUAmount' = 'GPU Amount' } -export const resourceType = ['cpu', 'memory', 'storage', 'network', 'nodeports'] as const; +export const resourceType = ['cpu', 'memory', 'storage', 'network', 'nodeports', 'gpu'] as const; diff --git a/frontend/providers/costcenter/src/constants/payment.ts b/frontend/providers/costcenter/src/constants/payment.ts index 3b27de63f8d..316c30034ba 100644 --- a/frontend/providers/costcenter/src/constants/payment.ts +++ b/frontend/providers/costcenter/src/constants/payment.ts @@ -94,7 +94,7 @@ export const valuationMap = new Map([ ['gpu', { unit: 'GPU Unit', scale: 1000, bg: '#6FCA88', idx: 3 }], ['network', { unit: 'M', scale: 1, bg: '#F182AA', idx: 4 }], ['services.nodeports', { unit: 'port_unit', scale: 1000, bg: '#F182AA', idx: 5 }] -]); +] as const); // export const BillingUnitMap = new Map([ // ['cpu', { unit: ''}] // ['port', { unit: ''}] diff --git a/frontend/providers/costcenter/src/pages/resource_analysis/index.tsx b/frontend/providers/costcenter/src/pages/resource_analysis/index.tsx index 7eb5c8bf524..240e7e73e24 100644 --- a/frontend/providers/costcenter/src/pages/resource_analysis/index.tsx +++ b/frontend/providers/costcenter/src/pages/resource_analysis/index.tsx @@ -12,7 +12,7 @@ import { Box, Flex, Heading, HStack, Img, Stack, Text, VStack } from '@chakra-ui import { useQuery, useQueryClient } from '@tanstack/react-query'; import { useTranslation } from 'next-i18next'; import { serverSideTranslations } from 'next-i18next/serverSideTranslations'; -import { useMemo, useState } from 'react'; +import { useState } from 'react'; export default function Resource() { const { t } = useTranslation(); @@ -68,7 +68,6 @@ export default function Resource() { keepPreviousData: true, queryKey: ['appOverviewBilling', queryBody, page, pageSize] }); - const appOverviews = useMemo(() => data?.data?.overviews || [], [data?.data?.overviews]); return ( diff --git a/frontend/providers/costcenter/src/pages/valuation/index.tsx b/frontend/providers/costcenter/src/pages/valuation/index.tsx index a1b71aa1d1b..e8b283e6716 100644 --- a/frontend/providers/costcenter/src/pages/valuation/index.tsx +++ b/frontend/providers/costcenter/src/pages/valuation/index.tsx @@ -50,7 +50,7 @@ function Valuation() { _data?.data?.properties // ?.filter((x) => !x.name.startsWith('gpu-')) ?.flatMap((x) => { - let props = valuationMap.get(x.name); + let props = valuationMap.get(x.name as any); if (!props) { if (!x.name.startsWith('gpu-')) return []; const gpuprops = valuationMap.get('gpu'); diff --git a/frontend/providers/costcenter/src/types/billing.ts b/frontend/providers/costcenter/src/types/billing.ts index b720674e7e8..0ba83d7a029 100644 --- a/frontend/providers/costcenter/src/types/billing.ts +++ b/frontend/providers/costcenter/src/types/billing.ts @@ -86,6 +86,7 @@ export type PropertiesCost = { 2: number; 3: number; 4: number; + 5: number; }; export type RechargeBillingItem = { ID: string;