Refactor(Card): Component all cards
Make all cards into components
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { hasPermission } from '@/util/auth'
|
||||
import { r_sys_settings_base_get, r_sys_settings_base_update } from '@/services/system'
|
||||
import { SettingsCard } from '@/pages/System/Settings'
|
||||
import SettingsCard from '@/components/system/SettingCard'
|
||||
|
||||
const Base = () => {
|
||||
const [baseForm] = AntdForm.useForm<BaseSettingsParam>()
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
r_sys_settings_mail_send,
|
||||
r_sys_settings_mail_update
|
||||
} from '@/services/system'
|
||||
import { SettingsCard } from '@/pages/System/Settings'
|
||||
import SettingsCard from '@/components/system/SettingCard'
|
||||
|
||||
const Mail = () => {
|
||||
const [modal, contextHolder] = AntdModal.useModal()
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
r_sys_settings_sensitive_get,
|
||||
r_sys_settings_sensitive_update
|
||||
} from '@/services/system'
|
||||
import { SettingsCard } from '@/pages/System/Settings'
|
||||
import SettingsCard from '@/components/system/SettingCard'
|
||||
|
||||
const SensitiveWord = () => {
|
||||
const [dataSource, setDataSource] = useState<SensitiveWordVo[]>()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { hasPermission } from '@/util/auth'
|
||||
import { r_sys_settings_two_factor_get, r_sys_settings_two_factor_update } from '@/services/system'
|
||||
import { SettingsCard } from '@/pages/System/Settings'
|
||||
import SettingsCard from '@/components/system/SettingCard'
|
||||
|
||||
const TwoFactor = () => {
|
||||
const [twoFactorForm] = AntdForm.useForm<TwoFactorSettingsParam>()
|
||||
|
||||
@@ -1,55 +1,12 @@
|
||||
import { PropsWithChildren, ReactNode } from 'react'
|
||||
import Icon from '@ant-design/icons'
|
||||
import '@/assets/css/pages/system/settings.scss'
|
||||
import FitFullscreen from '@/components/common/FitFullscreen'
|
||||
import HideScrollbar from '@/components/common/HideScrollbar'
|
||||
import Card from '@/components/common/Card'
|
||||
import FlexBox from '@/components/common/FlexBox'
|
||||
import LoadingMask from '@/components/common/LoadingMask'
|
||||
import Permission from '@/components/common/Permission'
|
||||
import Base from '@/pages/System/Settings/Base'
|
||||
import Mail from '@/pages/System/Settings/Mail'
|
||||
import SensitiveWord from '@/pages/System/Settings/SensitiveWord'
|
||||
import TwoFactor from '@/pages/System/Settings/TwoFactor.tsx'
|
||||
|
||||
interface SettingsCardProps extends PropsWithChildren {
|
||||
icon: IconComponent
|
||||
title: string
|
||||
loading?: boolean
|
||||
modifyOperationCode?: string[]
|
||||
expand?: ReactNode
|
||||
onReset?: () => void
|
||||
onSave?: () => void
|
||||
}
|
||||
export const SettingsCard = (props: SettingsCardProps) => {
|
||||
return (
|
||||
<Card>
|
||||
<FlexBox className={'settings-card'}>
|
||||
<FlexBox direction={'horizontal'} className={'head'}>
|
||||
<Icon component={props.icon} className={'icon'} />
|
||||
<div className={'title'}>{props.title}</div>
|
||||
{!props.loading && (
|
||||
<Permission operationCode={props.modifyOperationCode}>
|
||||
{props.expand}
|
||||
<AntdButton onClick={props.onReset} title={'重置'}>
|
||||
<Icon component={IconOxygenBack} />
|
||||
</AntdButton>
|
||||
<AntdButton className={'bt-save'} onClick={props.onSave} title={'保存'}>
|
||||
<Icon component={IconOxygenSave} />
|
||||
</AntdButton>
|
||||
</Permission>
|
||||
)}
|
||||
</FlexBox>
|
||||
<LoadingMask
|
||||
maskContent={<AntdSkeleton active paragraph={{ rows: 6 }} />}
|
||||
hidden={!props.loading}
|
||||
>
|
||||
{props.children}
|
||||
</LoadingMask>
|
||||
</FlexBox>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
import TwoFactor from '@/pages/System/Settings/TwoFactor'
|
||||
|
||||
const Settings = () => {
|
||||
return (
|
||||
|
||||
@@ -4,7 +4,7 @@ import { getTimesBetweenTwoTimes } from '@/util/datetime'
|
||||
import { r_sys_statistics_active } from '@/services/system'
|
||||
import FlexBox from '@/components/common/FlexBox'
|
||||
import { getTooltipTimeFormatter, lineEChartsBaseOption } from '@/pages/System/Statistics/shared'
|
||||
import { CommonCard } from '@/pages/System/Statistics'
|
||||
import StatisticsCard from '@/components/system/StatisticsCard'
|
||||
|
||||
const ActiveInfo = () => {
|
||||
const activeInfoDivRef = useRef<HTMLDivElement>(null)
|
||||
@@ -147,7 +147,7 @@ const ActiveInfo = () => {
|
||||
}
|
||||
|
||||
return (
|
||||
<CommonCard
|
||||
<StatisticsCard
|
||||
icon={IconOxygenAnalysis}
|
||||
title={
|
||||
<>
|
||||
@@ -183,7 +183,7 @@ const ActiveInfo = () => {
|
||||
<FlexBox className={'card-content'} direction={'horizontal'}>
|
||||
<div className={'big-chart'} ref={activeInfoDivRef} />
|
||||
</FlexBox>
|
||||
</CommonCard>
|
||||
</StatisticsCard>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
barEChartsBaseOption,
|
||||
EChartsOption
|
||||
} from '@/pages/System/Statistics/shared'
|
||||
import { CommonCard } from '@/pages/System/Statistics'
|
||||
import StatisticsCard from '@/components/system/StatisticsCard'
|
||||
|
||||
const CPUInfo = () => {
|
||||
const keyDivRef = useRef<HTMLDivElement>(null)
|
||||
@@ -142,7 +142,7 @@ const CPUInfo = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<CommonCard
|
||||
<StatisticsCard
|
||||
icon={IconOxygenCpu}
|
||||
title={'CPU 信息'}
|
||||
loading={isLoading}
|
||||
@@ -172,7 +172,7 @@ const CPUInfo = () => {
|
||||
<FlexBox className={'value-chart'} ref={cpuInfoDivRef} />
|
||||
<FlexBox className={'value-percent'} ref={percentDivRef} />
|
||||
</FlexBox>
|
||||
</CommonCard>
|
||||
</StatisticsCard>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { r_sys_statistics_hardware } from '@/services/system'
|
||||
import FlexBox from '@/components/common/FlexBox'
|
||||
import { CommonCard } from '@/pages/System/Statistics'
|
||||
import StatisticsCard from '@/components/system/StatisticsCard'
|
||||
|
||||
const HardwareInfo = () => {
|
||||
const [hardwareInfoData, setHardwareInfoData] = useState<HardwareInfoVo>()
|
||||
@@ -17,7 +17,7 @@ const HardwareInfo = () => {
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<CommonCard
|
||||
<StatisticsCard
|
||||
icon={IconOxygenHardware}
|
||||
title={'硬件信息'}
|
||||
loading={hardwareInfoData === undefined}
|
||||
@@ -56,7 +56,7 @@ const HardwareInfo = () => {
|
||||
<div title={hardwareInfoData?.disks}>{hardwareInfoData?.disks}</div>
|
||||
</FlexBox>
|
||||
</FlexBox>
|
||||
</CommonCard>
|
||||
</StatisticsCard>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import { getTimesBetweenTwoTimes } from '@/util/datetime'
|
||||
import { r_sys_statistics_online } from '@/services/system'
|
||||
import FlexBox from '@/components/common/FlexBox'
|
||||
import { getTooltipTimeFormatter, lineEChartsBaseOption } from '@/pages/System/Statistics/shared'
|
||||
import { CommonCard } from '@/pages/System/Statistics'
|
||||
import StatisticsCard from '@/components/system/StatisticsCard'
|
||||
|
||||
const OnlineInfo = () => {
|
||||
const onlineInfoDivRef = useRef<HTMLDivElement>(null)
|
||||
@@ -148,7 +148,7 @@ const OnlineInfo = () => {
|
||||
}
|
||||
|
||||
return (
|
||||
<CommonCard
|
||||
<StatisticsCard
|
||||
icon={IconOxygenOnline}
|
||||
title={
|
||||
<>
|
||||
@@ -188,7 +188,7 @@ const OnlineInfo = () => {
|
||||
<FlexBox className={'card-content'} direction={'horizontal'}>
|
||||
<div className={'big-chart'} ref={onlineInfoDivRef} />
|
||||
</FlexBox>
|
||||
</CommonCard>
|
||||
</StatisticsCard>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { utcToLocalTime } from '@/util/datetime'
|
||||
import { r_sys_statistics_software } from '@/services/system'
|
||||
import FlexBox from '@/components/common/FlexBox'
|
||||
import { CommonCard } from '@/pages/System/Statistics'
|
||||
import StatisticsCard from '@/components/system/StatisticsCard'
|
||||
|
||||
const SoftwareInfo = () => {
|
||||
const [softwareInfoData, setSoftwareInfoData] = useState<SoftwareInfoVo>()
|
||||
@@ -18,7 +18,7 @@ const SoftwareInfo = () => {
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<CommonCard
|
||||
<StatisticsCard
|
||||
icon={IconOxygenSoftware}
|
||||
title={'软件信息'}
|
||||
loading={softwareInfoData === undefined}
|
||||
@@ -61,7 +61,7 @@ const SoftwareInfo = () => {
|
||||
</div>
|
||||
</FlexBox>
|
||||
</FlexBox>
|
||||
</CommonCard>
|
||||
</StatisticsCard>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
barEChartsBaseOption,
|
||||
EChartsOption
|
||||
} from '@/pages/System/Statistics/shared'
|
||||
import { CommonCard } from '@/pages/System/Statistics'
|
||||
import StatisticsCard from '@/components/system/StatisticsCard'
|
||||
|
||||
const StorageInfo = () => {
|
||||
const keyDivRef = useRef<HTMLDivElement>(null)
|
||||
@@ -161,7 +161,7 @@ const StorageInfo = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<CommonCard
|
||||
<StatisticsCard
|
||||
icon={IconOxygenMemory}
|
||||
title={'内存信息'}
|
||||
loading={isLoading}
|
||||
@@ -191,7 +191,7 @@ const StorageInfo = () => {
|
||||
<FlexBox className={'value-chart'} ref={storageInfoDivRef} />
|
||||
<FlexBox className={'value-percent'} ref={percentDivRef} />
|
||||
</FlexBox>
|
||||
</CommonCard>
|
||||
</StatisticsCard>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
import { PropsWithChildren, ReactNode } from 'react'
|
||||
import Icon from '@ant-design/icons'
|
||||
import '@/assets/css/pages/system/statistics.scss'
|
||||
import Card from '@/components/common/Card'
|
||||
import FlexBox from '@/components/common/FlexBox'
|
||||
import FitFullscreen from '@/components/common/FitFullscreen'
|
||||
import HideScrollbar from '@/components/common/HideScrollbar'
|
||||
import LoadingMask from '@/components/common/LoadingMask'
|
||||
import Permission from '@/components/common/Permission'
|
||||
import OnlineInfo from '@/pages/System/Statistics/OnlineInfo'
|
||||
import ActiveInfo from '@/pages/System/Statistics/ActiveInfo'
|
||||
@@ -14,33 +10,6 @@ import HardwareInfo from '@/pages/System/Statistics/HardwareInfo'
|
||||
import CPUInfo from '@/pages/System/Statistics/CPUInfo'
|
||||
import StorageInfo from '@/pages/System/Statistics/StorageInfo'
|
||||
|
||||
interface CommonCardProps extends PropsWithChildren {
|
||||
icon: IconComponent
|
||||
title: ReactNode
|
||||
loading?: boolean
|
||||
expand?: ReactNode
|
||||
}
|
||||
|
||||
export const CommonCard = (props: CommonCardProps) => {
|
||||
return (
|
||||
<Card style={{ overflow: 'visible' }}>
|
||||
<FlexBox className={'common-card'}>
|
||||
<FlexBox direction={'horizontal'} className={'head'}>
|
||||
<Icon component={props.icon} className={'icon'} />
|
||||
<div className={'title'}>{props.title}</div>
|
||||
{props.expand}
|
||||
</FlexBox>
|
||||
<LoadingMask
|
||||
hidden={!props.loading}
|
||||
maskContent={<AntdSkeleton active paragraph={{ rows: 6 }} />}
|
||||
>
|
||||
{props.children}
|
||||
</LoadingMask>
|
||||
</FlexBox>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
const Statistics = () => {
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -1,65 +1,9 @@
|
||||
import { DetailedHTMLProps, HTMLAttributes, ReactNode } from 'react'
|
||||
import Icon from '@ant-design/icons'
|
||||
import VanillaTilt, { TiltOptions } from 'vanilla-tilt'
|
||||
import '@/assets/css/pages/system/index.scss'
|
||||
import HideScrollbar from '@/components/common/HideScrollbar'
|
||||
import FitFullscreen from '@/components/common/FitFullscreen'
|
||||
import FlexBox from '@/components/common/FlexBox'
|
||||
import Card from '@/components/common/Card'
|
||||
import Permission from '@/components/common/Permission'
|
||||
|
||||
interface CommonCardProps
|
||||
extends DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement> {
|
||||
icon: IconComponent
|
||||
description?: ReactNode
|
||||
options?: TiltOptions
|
||||
url?: string
|
||||
}
|
||||
|
||||
const CommonCard = forwardRef<HTMLDivElement, CommonCardProps>(
|
||||
({
|
||||
style,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
ref,
|
||||
icon,
|
||||
description,
|
||||
options = {
|
||||
reverse: true,
|
||||
max: 8,
|
||||
glare: true,
|
||||
scale: 1.03
|
||||
},
|
||||
url,
|
||||
children,
|
||||
...props
|
||||
}) => {
|
||||
const navigate = useNavigate()
|
||||
const cardRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
useEffect(() => {
|
||||
cardRef.current && VanillaTilt.init(cardRef.current, options)
|
||||
}, [options])
|
||||
|
||||
const handleCardOnClick = () => {
|
||||
url && navigate(url)
|
||||
}
|
||||
|
||||
return (
|
||||
<Card
|
||||
style={{ overflow: 'visible', ...style }}
|
||||
ref={cardRef}
|
||||
{...props}
|
||||
onClick={handleCardOnClick}
|
||||
>
|
||||
<FlexBox className={'common-card'}>
|
||||
<Icon component={icon} className={'icon'} />
|
||||
<div className={'text'}>{children}</div>
|
||||
<div className={'description'}>{description}</div>
|
||||
</FlexBox>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
)
|
||||
import SystemCard from '@/components/system/SystemCard.tsx'
|
||||
|
||||
const System = () => {
|
||||
return (
|
||||
@@ -68,54 +12,54 @@ const System = () => {
|
||||
<HideScrollbar isShowVerticalScrollbar autoHideWaitingTime={1000}>
|
||||
<FlexBox direction={'horizontal'} className={'root-content'}>
|
||||
<Permission path={'/system/statistics'}>
|
||||
<CommonCard icon={IconOxygenAnalysis} url={'statistics'}>
|
||||
<SystemCard icon={IconOxygenAnalysis} url={'statistics'}>
|
||||
系统概况
|
||||
</CommonCard>
|
||||
</SystemCard>
|
||||
</Permission>
|
||||
<Permission path={'/system/settings'}>
|
||||
<CommonCard icon={IconOxygenOption} url={'settings'}>
|
||||
<SystemCard icon={IconOxygenOption} url={'settings'}>
|
||||
系统设置
|
||||
</CommonCard>
|
||||
</SystemCard>
|
||||
</Permission>
|
||||
<Permission operationCode={['system:tool:query:tool']}>
|
||||
<CommonCard icon={IconOxygenTool} url={'tools'}>
|
||||
<SystemCard icon={IconOxygenTool} url={'tools'}>
|
||||
工具管理
|
||||
</CommonCard>
|
||||
</SystemCard>
|
||||
</Permission>
|
||||
<Permission operationCode={['system:tool:query:template']}>
|
||||
<CommonCard icon={IconOxygenTemplate} url={'tools/template'}>
|
||||
<SystemCard icon={IconOxygenTemplate} url={'tools/template'}>
|
||||
模板管理
|
||||
</CommonCard>
|
||||
</SystemCard>
|
||||
</Permission>
|
||||
<Permission operationCode={['system:tool:query:base']}>
|
||||
<CommonCard icon={IconOxygenBase} url={'tools/base'}>
|
||||
<SystemCard icon={IconOxygenBase} url={'tools/base'}>
|
||||
基板管理
|
||||
</CommonCard>
|
||||
</SystemCard>
|
||||
</Permission>
|
||||
<Permission operationCode={['system:tool:query:category']}>
|
||||
<CommonCard icon={IconOxygenCategory} url={'tools/category'}>
|
||||
<SystemCard icon={IconOxygenCategory} url={'tools/category'}>
|
||||
类别管理
|
||||
</CommonCard>
|
||||
</SystemCard>
|
||||
</Permission>
|
||||
<Permission path={'/system/user'}>
|
||||
<CommonCard icon={IconOxygenUser} url={'user'}>
|
||||
<SystemCard icon={IconOxygenUser} url={'user'}>
|
||||
用户管理
|
||||
</CommonCard>
|
||||
</SystemCard>
|
||||
</Permission>
|
||||
<Permission path={'/system/role'}>
|
||||
<CommonCard icon={IconOxygenRole} url={'role'}>
|
||||
<SystemCard icon={IconOxygenRole} url={'role'}>
|
||||
角色管理
|
||||
</CommonCard>
|
||||
</SystemCard>
|
||||
</Permission>
|
||||
<Permission path={'/system/group'}>
|
||||
<CommonCard icon={IconOxygenGroup} url={'group'}>
|
||||
<SystemCard icon={IconOxygenGroup} url={'group'}>
|
||||
群组管理
|
||||
</CommonCard>
|
||||
</SystemCard>
|
||||
</Permission>
|
||||
<Permission path={'/system/log'}>
|
||||
<CommonCard icon={IconOxygenLog} url={'log'}>
|
||||
<SystemCard icon={IconOxygenLog} url={'log'}>
|
||||
系统日志
|
||||
</CommonCard>
|
||||
</SystemCard>
|
||||
</Permission>
|
||||
</FlexBox>
|
||||
</HideScrollbar>
|
||||
|
||||
@@ -1,292 +1,13 @@
|
||||
import { DetailedHTMLProps, HTMLAttributes, MouseEvent, ReactNode, UIEvent } from 'react'
|
||||
import VanillaTilt, { TiltOptions } from 'vanilla-tilt'
|
||||
import protocolCheck from 'custom-protocol-check'
|
||||
import Icon from '@ant-design/icons'
|
||||
import { UIEvent } from 'react'
|
||||
import '@/assets/css/pages/tools/store.scss'
|
||||
import {
|
||||
COLOR_BACKGROUND,
|
||||
COLOR_MAIN,
|
||||
COLOR_PRODUCTION,
|
||||
DATABASE_SELECT_SUCCESS
|
||||
} from '@/constants/common.constants'
|
||||
import { DATABASE_SELECT_SUCCESS } from '@/constants/common.constants'
|
||||
import { checkDesktop } from '@/util/common'
|
||||
import { navigateToSource, navigateToStore, navigateToView } from '@/util/navigation'
|
||||
import { r_tool_add_favorite, r_tool_remove_favorite, r_tool_store_get } from '@/services/tool'
|
||||
import Card from '@/components/common/Card'
|
||||
import { r_tool_store_get } from '@/services/tool'
|
||||
import FlexBox from '@/components/common/FlexBox'
|
||||
import FitFullscreen from '@/components/common/FitFullscreen'
|
||||
import HideScrollbar from '@/components/common/HideScrollbar'
|
||||
|
||||
interface CommonCardProps
|
||||
extends DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement> {
|
||||
icon: ReactNode
|
||||
toolName: string
|
||||
toolId: string
|
||||
toolDesc: string
|
||||
options?: TiltOptions
|
||||
authorName: string
|
||||
authorAvatar: string
|
||||
authorUsername: string
|
||||
ver: string
|
||||
platform: Platform
|
||||
supportPlatform: Platform[]
|
||||
favorite: boolean
|
||||
}
|
||||
|
||||
const CommonCard = ({
|
||||
style,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
ref,
|
||||
icon,
|
||||
toolName,
|
||||
toolId,
|
||||
toolDesc,
|
||||
options = {
|
||||
reverse: true,
|
||||
max: 8,
|
||||
glare: true,
|
||||
['max-glare']: 0.3,
|
||||
scale: 1.03
|
||||
},
|
||||
authorName,
|
||||
authorAvatar,
|
||||
authorUsername,
|
||||
ver,
|
||||
platform,
|
||||
supportPlatform,
|
||||
favorite,
|
||||
...props
|
||||
}: CommonCardProps) => {
|
||||
const navigate = useNavigate()
|
||||
const [modal, contextHolder] = AntdModal.useModal()
|
||||
const cardRef = useRef<HTMLDivElement>(null)
|
||||
const [favorite_, setFavorite_] = useState<boolean>(favorite)
|
||||
|
||||
useEffect(() => {
|
||||
cardRef.current && VanillaTilt.init(cardRef.current, options)
|
||||
}, [options])
|
||||
|
||||
const handleCardOnClick = () => {
|
||||
if (!checkDesktop() && platform === 'DESKTOP') {
|
||||
void message.warning('此应用需要桌面端环境,请在桌面端打开')
|
||||
return
|
||||
}
|
||||
if (platform === 'ANDROID') {
|
||||
void modal.info({
|
||||
icon: <Icon style={{ color: COLOR_MAIN }} component={IconOxygenInfo} />,
|
||||
title: 'Android 端',
|
||||
centered: true,
|
||||
maskClosable: true,
|
||||
content: (
|
||||
<FlexBox className={'android-qrcode'}>
|
||||
<AntdQRCode
|
||||
value={`oxygen://openurl/view/${authorUsername}/${toolId}`}
|
||||
size={300}
|
||||
/>
|
||||
<AntdTag className={'tag'}>请使用手机端扫描上方二维码</AntdTag>
|
||||
</FlexBox>
|
||||
)
|
||||
})
|
||||
return
|
||||
}
|
||||
navigateToView(navigate, authorUsername, toolId, platform)
|
||||
}
|
||||
|
||||
const handleOnClickAuthor = (e: MouseEvent<HTMLDivElement>) => {
|
||||
e.stopPropagation()
|
||||
navigateToStore(navigate, authorUsername)
|
||||
}
|
||||
|
||||
const handleOnSourceBtnClick = (e: MouseEvent<HTMLDivElement>) => {
|
||||
e.stopPropagation()
|
||||
navigateToSource(navigate, authorUsername, toolId, platform)
|
||||
}
|
||||
|
||||
const handleOnStarBtnClick = (e: MouseEvent<HTMLDivElement>) => {
|
||||
e.stopPropagation()
|
||||
if (favorite_) {
|
||||
void r_tool_remove_favorite({
|
||||
username: authorUsername,
|
||||
toolId: toolId,
|
||||
platform: platform
|
||||
}).then((res) => {
|
||||
const response = res.data
|
||||
if (response.success) {
|
||||
setFavorite_(false)
|
||||
} else {
|
||||
void message.error('取消收藏失败,请稍后重试')
|
||||
}
|
||||
})
|
||||
} else {
|
||||
void r_tool_add_favorite({
|
||||
username: authorUsername,
|
||||
toolId: toolId,
|
||||
platform: platform
|
||||
}).then((res) => {
|
||||
const response = res.data
|
||||
if (response.success) {
|
||||
setFavorite_(true)
|
||||
} else {
|
||||
void message.error('收藏失败,请稍后重试')
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const handleOnAndroidBtnClick = (e: MouseEvent<HTMLDivElement>) => {
|
||||
e.stopPropagation()
|
||||
void modal.info({
|
||||
icon: <Icon style={{ color: COLOR_MAIN }} component={IconOxygenInfo} />,
|
||||
title: 'Android 端',
|
||||
centered: true,
|
||||
maskClosable: true,
|
||||
content: (
|
||||
<FlexBox className={'android-qrcode'}>
|
||||
<AntdQRCode
|
||||
value={`oxygen://openurl/view/${authorUsername}/${toolId}`}
|
||||
size={300}
|
||||
/>
|
||||
<AntdTag className={'tag'}>请使用手机端扫描上方二维码</AntdTag>
|
||||
</FlexBox>
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
const handleOnDesktopBtnClick = (e: MouseEvent<HTMLDivElement>) => {
|
||||
e.stopPropagation()
|
||||
if (!checkDesktop()) {
|
||||
void message.loading({ content: '启动桌面端中……', key: 'LOADING', duration: 0 })
|
||||
protocolCheck(
|
||||
`oxygen://openurl/view/${authorUsername}/${toolId}`,
|
||||
() => {
|
||||
void message.warning('打开失败,此应用需要桌面端环境,请安装桌面端后重试')
|
||||
void message.destroy('LOADING')
|
||||
},
|
||||
() => {
|
||||
void message.destroy('LOADING')
|
||||
},
|
||||
2000,
|
||||
() => {
|
||||
void message.warning('打开失败,此应用需要桌面端环境,请安装桌面端后重试')
|
||||
void message.destroy('LOADING')
|
||||
}
|
||||
)
|
||||
return
|
||||
}
|
||||
navigateToView(navigate, authorUsername, toolId, platform)
|
||||
}
|
||||
|
||||
const handleOnWebBtnClick = (e: MouseEvent<HTMLDivElement>) => {
|
||||
e.stopPropagation()
|
||||
navigateToView(navigate, authorUsername, toolId, platform)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Card
|
||||
style={{ overflow: 'visible', ...style }}
|
||||
ref={cardRef}
|
||||
{...props}
|
||||
onClick={handleCardOnClick}
|
||||
>
|
||||
<FlexBox className={'common-card'}>
|
||||
<div className={'icon'}>{icon}</div>
|
||||
<div className={'version'}>
|
||||
<AntdTag>
|
||||
{platform.slice(0, 1)}-{ver}
|
||||
</AntdTag>
|
||||
</div>
|
||||
<div className={'info'}>
|
||||
<div className={'tool-name'}>{toolName}</div>
|
||||
<div className={'tool-id'}>{`ID: ${toolId}`}</div>
|
||||
{toolDesc && <div className={'tool-desc'}>{`简介:${toolDesc}`}</div>}
|
||||
</div>
|
||||
<div className={'author'} onClick={handleOnClickAuthor}>
|
||||
<div className={'avatar'}>
|
||||
<AntdAvatar
|
||||
src={
|
||||
<AntdImage
|
||||
preview={false}
|
||||
src={`data:image/png;base64,${authorAvatar}`}
|
||||
alt={'Avatar'}
|
||||
/>
|
||||
}
|
||||
style={{ background: COLOR_BACKGROUND }}
|
||||
/>
|
||||
</div>
|
||||
<AntdTooltip title={authorUsername}>
|
||||
<div className={'author-name'}>{authorName}</div>
|
||||
</AntdTooltip>
|
||||
</div>
|
||||
<div className={'operation'}>
|
||||
{platform !== 'ANDROID' && supportPlatform.includes('ANDROID') && (
|
||||
<AntdTooltip title={'Android 端'}>
|
||||
<Icon
|
||||
component={IconOxygenMobile}
|
||||
onClick={handleOnAndroidBtnClick}
|
||||
/>
|
||||
</AntdTooltip>
|
||||
)}
|
||||
{platform === 'DESKTOP' && supportPlatform.includes('WEB') && (
|
||||
<AntdTooltip title={'Web 端'}>
|
||||
<Icon component={IconOxygenBrowser} onClick={handleOnWebBtnClick} />
|
||||
</AntdTooltip>
|
||||
)}
|
||||
{platform === 'WEB' && supportPlatform.includes('DESKTOP') && (
|
||||
<AntdTooltip title={'桌面端'}>
|
||||
<Icon
|
||||
component={IconOxygenDesktop}
|
||||
onClick={handleOnDesktopBtnClick}
|
||||
/>
|
||||
</AntdTooltip>
|
||||
)}
|
||||
<AntdTooltip title={'源码'}>
|
||||
<Icon component={IconOxygenCode} onClick={handleOnSourceBtnClick} />
|
||||
</AntdTooltip>
|
||||
<AntdTooltip title={favorite_ ? '取消收藏' : '收藏'}>
|
||||
<Icon
|
||||
component={favorite_ ? IconOxygenStarFilled : IconOxygenStar}
|
||||
style={{ color: favorite_ ? COLOR_PRODUCTION : undefined }}
|
||||
onClick={handleOnStarBtnClick}
|
||||
/>
|
||||
</AntdTooltip>
|
||||
</div>
|
||||
</FlexBox>
|
||||
</Card>
|
||||
{contextHolder}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
interface LoadMoreCardProps {
|
||||
onClick: () => void
|
||||
}
|
||||
|
||||
const LoadMoreCard = ({ onClick }: LoadMoreCardProps) => {
|
||||
const cardRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
useEffect(() => {
|
||||
cardRef.current &&
|
||||
VanillaTilt.init(cardRef.current, {
|
||||
reverse: true,
|
||||
max: 8,
|
||||
glare: true,
|
||||
['max-glare']: 0.3,
|
||||
scale: 1.03
|
||||
})
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<Card style={{ overflow: 'visible' }} ref={cardRef} onClick={onClick}>
|
||||
<FlexBox className={'load-more-card'}>
|
||||
<div className={'icon'}>
|
||||
<Icon component={IconOxygenMore} />{' '}
|
||||
</div>
|
||||
<div className={'text'}>加载更多</div>
|
||||
</FlexBox>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
import StoreCard from '@/components/tools/StoreCard'
|
||||
import LoadMoreCard from '@/components/tools/LoadMoreCard'
|
||||
|
||||
const Store = () => {
|
||||
const scrollTopRef = useRef(0)
|
||||
@@ -411,7 +132,7 @@ const Store = () => {
|
||||
: webTool || desktopTool) || androidTool
|
||||
|
||||
return (
|
||||
<CommonCard
|
||||
<StoreCard
|
||||
key={firstTool!.id}
|
||||
icon={
|
||||
<img
|
||||
|
||||
@@ -1,226 +1,20 @@
|
||||
import { DetailedHTMLProps, HTMLAttributes, MouseEvent, ReactNode } from 'react'
|
||||
import VanillaTilt, { TiltOptions } from 'vanilla-tilt'
|
||||
import protocolCheck from 'custom-protocol-check'
|
||||
import Icon from '@ant-design/icons'
|
||||
import '@/assets/css/pages/tools/user.scss'
|
||||
import {
|
||||
COLOR_BACKGROUND,
|
||||
COLOR_MAIN,
|
||||
DATABASE_NO_RECORD_FOUND,
|
||||
DATABASE_SELECT_SUCCESS
|
||||
} from '@/constants/common.constants'
|
||||
import { checkDesktop } from '@/util/common'
|
||||
import { navigateToRoot, navigateToSource, navigateToView } from '@/util/navigation'
|
||||
import { navigateToRoot } from '@/util/navigation'
|
||||
import { r_sys_user_info_get_basic } from '@/services/system'
|
||||
import { r_tool_store_get_by_username } from '@/services/tool'
|
||||
import FitFullscreen from '@/components/common/FitFullscreen'
|
||||
import HideScrollbar from '@/components/common/HideScrollbar'
|
||||
import Card from '@/components/common/Card'
|
||||
import FlexBox from '@/components/common/FlexBox'
|
||||
|
||||
interface CommonCardProps
|
||||
extends DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement> {
|
||||
icon: ReactNode
|
||||
toolName: string
|
||||
toolId: string
|
||||
toolDesc: string
|
||||
options?: TiltOptions
|
||||
authorUsername: string
|
||||
ver: string
|
||||
platform: Platform
|
||||
supportPlatform: Platform[]
|
||||
}
|
||||
|
||||
const CommonCard = ({
|
||||
style,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
ref,
|
||||
icon,
|
||||
toolName,
|
||||
toolId,
|
||||
toolDesc,
|
||||
options = {
|
||||
reverse: true,
|
||||
max: 8,
|
||||
glare: true,
|
||||
['max-glare']: 0.3,
|
||||
scale: 1.03
|
||||
},
|
||||
authorUsername,
|
||||
ver,
|
||||
platform,
|
||||
supportPlatform,
|
||||
...props
|
||||
}: CommonCardProps) => {
|
||||
const navigate = useNavigate()
|
||||
const [modal, contextHolder] = AntdModal.useModal()
|
||||
const cardRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
useEffect(() => {
|
||||
cardRef.current && VanillaTilt.init(cardRef.current, options)
|
||||
}, [options])
|
||||
|
||||
const handleCardOnClick = () => {
|
||||
if (!checkDesktop() && platform === 'DESKTOP') {
|
||||
void message.warning('此应用需要桌面端环境,请在桌面端打开')
|
||||
return
|
||||
}
|
||||
if (platform === 'ANDROID') {
|
||||
void modal.info({
|
||||
icon: <Icon style={{ color: COLOR_MAIN }} component={IconOxygenInfo} />,
|
||||
title: 'Android 端',
|
||||
centered: true,
|
||||
maskClosable: true,
|
||||
content: (
|
||||
<FlexBox className={'android-qrcode'}>
|
||||
<AntdQRCode
|
||||
value={`oxygen://openurl/view/${authorUsername}/${toolId}`}
|
||||
size={300}
|
||||
/>
|
||||
<AntdTag className={'tag'}>请使用手机端扫描上方二维码</AntdTag>
|
||||
</FlexBox>
|
||||
)
|
||||
})
|
||||
return
|
||||
}
|
||||
navigateToView(navigate, authorUsername, toolId, platform)
|
||||
}
|
||||
|
||||
const handleOnSourceBtnClick = (e: MouseEvent<HTMLDivElement>) => {
|
||||
e.stopPropagation()
|
||||
navigateToSource(navigate, authorUsername, toolId, platform)
|
||||
}
|
||||
|
||||
const handleOnAndroidBtnClick = (e: MouseEvent<HTMLDivElement>) => {
|
||||
e.stopPropagation()
|
||||
void modal.info({
|
||||
icon: <Icon style={{ color: COLOR_MAIN }} component={IconOxygenInfo} />,
|
||||
title: 'Android 端',
|
||||
centered: true,
|
||||
maskClosable: true,
|
||||
content: (
|
||||
<FlexBox className={'android-qrcode'}>
|
||||
<AntdQRCode
|
||||
value={`oxygen://openurl/view/${authorUsername}/${toolId}`}
|
||||
size={300}
|
||||
/>
|
||||
<AntdTag className={'tag'}>请使用手机端扫描上方二维码</AntdTag>
|
||||
</FlexBox>
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
const handleOnDesktopBtnClick = (e: MouseEvent<HTMLDivElement>) => {
|
||||
e.stopPropagation()
|
||||
if (!checkDesktop()) {
|
||||
void message.loading({ content: '启动桌面端中……', key: 'LOADING', duration: 0 })
|
||||
protocolCheck(
|
||||
`oxygen://openurl/view/${authorUsername}/${toolId}`,
|
||||
() => {
|
||||
void message.warning('打开失败,此应用需要桌面端环境,请安装桌面端后重试')
|
||||
void message.destroy('LOADING')
|
||||
},
|
||||
() => {
|
||||
void message.destroy('LOADING')
|
||||
},
|
||||
2000,
|
||||
() => {
|
||||
void message.warning('打开失败,此应用需要桌面端环境,请安装桌面端后重试')
|
||||
void message.destroy('LOADING')
|
||||
}
|
||||
)
|
||||
return
|
||||
}
|
||||
navigateToView(navigate, authorUsername, toolId, platform)
|
||||
}
|
||||
|
||||
const handleOnWebBtnClick = (e: MouseEvent<HTMLDivElement>) => {
|
||||
e.stopPropagation()
|
||||
navigateToView(navigate, authorUsername, toolId, platform)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Card
|
||||
style={{ overflow: 'visible', ...style }}
|
||||
ref={cardRef}
|
||||
{...props}
|
||||
onClick={handleCardOnClick}
|
||||
>
|
||||
<FlexBox className={'common-card'}>
|
||||
<div className={'icon'}>{icon}</div>
|
||||
<div className={'version'}>
|
||||
<AntdTag>
|
||||
{platform.slice(0, 1)}-{ver}
|
||||
</AntdTag>
|
||||
</div>
|
||||
<div className={'info'}>
|
||||
<div className={'tool-name'}>{toolName}</div>
|
||||
<div className={'tool-id'}>{`ID: ${toolId}`}</div>
|
||||
{toolDesc && <div className={'tool-desc'}>{`简介:${toolDesc}`}</div>}
|
||||
</div>
|
||||
<div className={'operation'}>
|
||||
{platform !== 'ANDROID' && supportPlatform.includes('ANDROID') && (
|
||||
<AntdTooltip title={'Android 端'}>
|
||||
<Icon
|
||||
component={IconOxygenMobile}
|
||||
onClick={handleOnAndroidBtnClick}
|
||||
/>
|
||||
</AntdTooltip>
|
||||
)}
|
||||
{platform === 'DESKTOP' && supportPlatform.includes('WEB') && (
|
||||
<AntdTooltip title={'Web 端'}>
|
||||
<Icon component={IconOxygenBrowser} onClick={handleOnWebBtnClick} />
|
||||
</AntdTooltip>
|
||||
)}
|
||||
{platform === 'WEB' && supportPlatform.includes('DESKTOP') && (
|
||||
<AntdTooltip title={'桌面端'}>
|
||||
<Icon
|
||||
component={IconOxygenDesktop}
|
||||
onClick={handleOnDesktopBtnClick}
|
||||
/>
|
||||
</AntdTooltip>
|
||||
)}
|
||||
<AntdTooltip title={'源码'}>
|
||||
<Icon component={IconOxygenCode} onClick={handleOnSourceBtnClick} />
|
||||
</AntdTooltip>
|
||||
</div>
|
||||
</FlexBox>
|
||||
</Card>
|
||||
{contextHolder}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
interface LoadMoreCardProps {
|
||||
onClick: () => void
|
||||
}
|
||||
|
||||
const LoadMoreCard = ({ onClick }: LoadMoreCardProps) => {
|
||||
const cardRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
useEffect(() => {
|
||||
cardRef.current &&
|
||||
VanillaTilt.init(cardRef.current, {
|
||||
reverse: true,
|
||||
max: 8,
|
||||
glare: true,
|
||||
['max-glare']: 0.3,
|
||||
scale: 1.03
|
||||
})
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<Card style={{ overflow: 'visible' }} ref={cardRef} onClick={onClick}>
|
||||
<FlexBox className={'load-more-card'}>
|
||||
<div className={'icon'}>
|
||||
<Icon component={IconOxygenMore} />{' '}
|
||||
</div>
|
||||
<div className={'text'}>加载更多</div>
|
||||
</FlexBox>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
import StoreCard from '@/components/tools/StoreCard'
|
||||
import LoadMoreCard from '@/components/tools/LoadMoreCard'
|
||||
|
||||
const User = () => {
|
||||
const { username } = useParams()
|
||||
@@ -401,7 +195,7 @@ const User = () => {
|
||||
: webTool || desktopTool) || androidTool
|
||||
|
||||
return (
|
||||
<CommonCard
|
||||
<StoreCard
|
||||
key={firstTool!.id}
|
||||
icon={
|
||||
<img
|
||||
@@ -416,6 +210,7 @@ const User = () => {
|
||||
ver={firstTool!.ver}
|
||||
platform={firstTool!.platform}
|
||||
supportPlatform={tools.map((value) => value.platform)}
|
||||
favorite={firstTool!.favorite}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import { DetailedHTMLProps, HTMLAttributes, ReactNode } from 'react'
|
||||
import Icon from '@ant-design/icons'
|
||||
import VanillaTilt, { TiltOptions } from 'vanilla-tilt'
|
||||
import '@/assets/css/pages/tools/index.scss'
|
||||
import {
|
||||
DATABASE_DELETE_SUCCESS,
|
||||
@@ -27,106 +25,8 @@ import {
|
||||
import FitFullscreen from '@/components/common/FitFullscreen'
|
||||
import HideScrollbar from '@/components/common/HideScrollbar'
|
||||
import FlexBox from '@/components/common/FlexBox'
|
||||
import Card from '@/components/common/Card'
|
||||
|
||||
interface CommonCardProps
|
||||
extends DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement> {
|
||||
icon: ReactNode
|
||||
toolName?: string
|
||||
toolId?: string
|
||||
options?: TiltOptions
|
||||
url?: string
|
||||
onOpen?: () => void
|
||||
onEdit?: () => void
|
||||
onSource?: () => void
|
||||
onPublish?: () => void
|
||||
onCancelReview?: () => void
|
||||
onDelete?: () => void
|
||||
}
|
||||
|
||||
const CommonCard = ({
|
||||
style,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
ref,
|
||||
icon,
|
||||
toolName,
|
||||
toolId,
|
||||
options = {
|
||||
reverse: true,
|
||||
max: 8,
|
||||
glare: true,
|
||||
['max-glare']: 0.3,
|
||||
scale: 1.03
|
||||
},
|
||||
url,
|
||||
onOpen,
|
||||
onEdit,
|
||||
onSource,
|
||||
onPublish,
|
||||
onCancelReview,
|
||||
onDelete,
|
||||
children,
|
||||
...props
|
||||
}: CommonCardProps) => {
|
||||
const navigate = useNavigate()
|
||||
const cardRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
useEffect(() => {
|
||||
cardRef.current && VanillaTilt.init(cardRef.current, options)
|
||||
}, [options])
|
||||
|
||||
const handleCardOnClick = () => {
|
||||
url && navigate(url)
|
||||
}
|
||||
|
||||
return (
|
||||
<Card
|
||||
style={{ overflow: 'visible', ...style }}
|
||||
ref={cardRef}
|
||||
{...props}
|
||||
onClick={handleCardOnClick}
|
||||
>
|
||||
<FlexBox className={'common-card'}>
|
||||
<div className={'icon'}>{icon}</div>
|
||||
<div className={'info'}>
|
||||
{toolName && <div className={'tool-name'}>{toolName}</div>}
|
||||
{toolId && <div className={'tool-id'}>{`ID: ${toolId}`}</div>}
|
||||
</div>
|
||||
<div className={'operation'}>
|
||||
{onOpen && (
|
||||
<AntdButton onClick={onOpen} size={'small'} type={'primary'}>
|
||||
打开
|
||||
</AntdButton>
|
||||
)}
|
||||
{onEdit && onPublish && (
|
||||
<div className={'edit'}>
|
||||
<AntdButton.Group size={'small'}>
|
||||
<AntdButton onClick={onEdit}>编辑</AntdButton>
|
||||
<AntdButton onClick={onPublish}>发布</AntdButton>
|
||||
</AntdButton.Group>
|
||||
</div>
|
||||
)}
|
||||
{onSource && (
|
||||
<AntdButton size={'small'} onClick={onSource}>
|
||||
源码
|
||||
</AntdButton>
|
||||
)}
|
||||
{onCancelReview && (
|
||||
<AntdButton size={'small'} onClick={onCancelReview}>
|
||||
取消审核
|
||||
</AntdButton>
|
||||
)}
|
||||
{onDelete && (
|
||||
<AntdButton size={'small'} danger onClick={onDelete}>
|
||||
删除
|
||||
</AntdButton>
|
||||
)}
|
||||
</div>
|
||||
{children}
|
||||
</FlexBox>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
import RepositoryCard from '@/components/tools/RepositoryCard'
|
||||
import LoadMoreCard from '@/components/tools/LoadMoreCard'
|
||||
|
||||
interface ToolCardProps {
|
||||
tools: ToolVo[]
|
||||
@@ -262,7 +162,7 @@ const ToolCard = ({ tools, onDelete, onUpgrade, onSubmit, onCancel }: ToolCardPr
|
||||
}
|
||||
|
||||
return (
|
||||
<CommonCard
|
||||
<RepositoryCard
|
||||
icon={<img src={`data:image/svg+xml;base64,${selectedTool.icon}`} alt={'Icon'} />}
|
||||
toolName={selectedTool.name}
|
||||
toolId={selectedTool.toolId}
|
||||
@@ -296,37 +196,7 @@ const ToolCard = ({ tools, onDelete, onUpgrade, onSubmit, onCancel }: ToolCardPr
|
||||
/>
|
||||
</AntdTooltip>
|
||||
)}
|
||||
</CommonCard>
|
||||
)
|
||||
}
|
||||
|
||||
interface LoadMoreCardProps {
|
||||
onClick: () => void
|
||||
}
|
||||
|
||||
const LoadMoreCard = ({ onClick }: LoadMoreCardProps) => {
|
||||
const cardRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
useEffect(() => {
|
||||
cardRef.current &&
|
||||
VanillaTilt.init(cardRef.current, {
|
||||
reverse: true,
|
||||
max: 8,
|
||||
glare: true,
|
||||
['max-glare']: 0.3,
|
||||
scale: 1.03
|
||||
})
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<Card style={{ overflow: 'visible' }} ref={cardRef} onClick={onClick}>
|
||||
<FlexBox className={'load-more-card'}>
|
||||
<div className={'icon'}>
|
||||
<Icon component={IconOxygenMore} />{' '}
|
||||
</div>
|
||||
<div className={'text'}>加载更多</div>
|
||||
</FlexBox>
|
||||
</Card>
|
||||
</RepositoryCard>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -616,7 +486,7 @@ const Tools = () => {
|
||||
<FitFullscreen data-component={'tools'}>
|
||||
<HideScrollbar isShowVerticalScrollbar autoHideWaitingTime={1000}>
|
||||
<FlexBox direction={'horizontal'} className={'root-content'}>
|
||||
<CommonCard
|
||||
<RepositoryCard
|
||||
icon={<Icon component={IconOxygenNewProject} />}
|
||||
toolName={'创建工具'}
|
||||
url={'/create'}
|
||||
|
||||
Reference in New Issue
Block a user