Refactor(Card): Component all cards

Make all cards into components
This commit is contained in:
2024-04-28 11:37:57 +08:00
parent a5b26d9680
commit 497fb28b5d
34 changed files with 908 additions and 1206 deletions

View File

@@ -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>()

View File

@@ -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()

View File

@@ -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[]>()

View File

@@ -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>()

View File

@@ -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 (

View File

@@ -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>
)
}

View File

@@ -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>
</>
)
}

View File

@@ -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>
)
}

View File

@@ -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>
)
}

View File

@@ -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>
)
}

View File

@@ -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>
</>
)
}

View File

@@ -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 (
<>

View File

@@ -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>

View File

@@ -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

View File

@@ -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}
/>
)
})}

View File

@@ -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'}