Add developer profile page
This commit is contained in:
@@ -132,5 +132,13 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.no-tool {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 1.4em;
|
||||||
|
font-weight: bolder;
|
||||||
|
color: constants.$font-secondary-color;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
188
src/assets/css/pages/tools/user.scss
Normal file
188
src/assets/css/pages/tools/user.scss
Normal file
@@ -0,0 +1,188 @@
|
|||||||
|
@use '@/assets/css/constants' as constants;
|
||||||
|
|
||||||
|
[data-component=tools-store-user] .root-content {
|
||||||
|
padding: {
|
||||||
|
top: 80px;
|
||||||
|
left: 30px;
|
||||||
|
right: 30px;
|
||||||
|
bottom: 30px;
|
||||||
|
};
|
||||||
|
|
||||||
|
.root-box {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
overflow: visible;
|
||||||
|
align-items: center;
|
||||||
|
min-width: 900px;
|
||||||
|
padding-bottom: 20px;
|
||||||
|
|
||||||
|
> .info {
|
||||||
|
margin-left: 40px;
|
||||||
|
transform: translateY(-40px);
|
||||||
|
|
||||||
|
> * {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar-box {
|
||||||
|
background-color: white;
|
||||||
|
padding: 4px;
|
||||||
|
border-radius: 50%;
|
||||||
|
box-shadow: 5px 5px 15px 0 rgba(0, 0, 0, 0.1);
|
||||||
|
|
||||||
|
.avatar {
|
||||||
|
background-color: transparent !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-name {
|
||||||
|
margin: {
|
||||||
|
top: 20px;
|
||||||
|
left: 24px;
|
||||||
|
};
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
> * {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nickname {
|
||||||
|
font-size: 2.4em;
|
||||||
|
font-weight: bolder;
|
||||||
|
color: constants.$production-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.url {
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
> span {
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tools {
|
||||||
|
padding: 20px;
|
||||||
|
gap: 20px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
> .card-box {
|
||||||
|
width: 180px;
|
||||||
|
height: 290px;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
.common-card {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
text-align: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
> * {
|
||||||
|
display: block;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
display: flex;
|
||||||
|
padding-top: 40px;
|
||||||
|
padding-bottom: 20px;
|
||||||
|
color: constants.$production-color;
|
||||||
|
font-size: constants.$SIZE_ICON_XL;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
img {
|
||||||
|
width: constants.$SIZE_ICON_XL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.version {
|
||||||
|
position: absolute;
|
||||||
|
left: 10px;
|
||||||
|
top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info {
|
||||||
|
padding-top: 20px;
|
||||||
|
|
||||||
|
.tool-name {
|
||||||
|
font-weight: bolder;
|
||||||
|
font-size: 1.6em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tool-desc {
|
||||||
|
margin-top: 10px;
|
||||||
|
color: constants.$font-secondary-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.author {
|
||||||
|
display: flex;
|
||||||
|
margin-top: auto;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: end;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
gap: 10px;
|
||||||
|
|
||||||
|
.avatar {
|
||||||
|
> * {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.author-name {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.operation {
|
||||||
|
position: absolute;
|
||||||
|
top: 6px;
|
||||||
|
right: 12px;
|
||||||
|
font-size: 1.6em;
|
||||||
|
|
||||||
|
> *:hover {
|
||||||
|
color: constants.$font-secondary-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.load-more-card {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
text-align: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
display: flex;
|
||||||
|
font-size: constants.$SIZE_ICON_XXL;
|
||||||
|
color: constants.$production-color;
|
||||||
|
align-items: center;
|
||||||
|
transform: translateY(-20px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.text {
|
||||||
|
position: absolute;
|
||||||
|
top: 60%;
|
||||||
|
font-size: 1.2em;
|
||||||
|
font-weight: bolder;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-tool {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
font-size: 1.2em;
|
||||||
|
font-weight: bolder;
|
||||||
|
color: constants.$font-secondary-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1
src/assets/svg/copy.svg
Normal file
1
src/assets/svg/copy.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024"><path d="M672 832 224 832c-52.928 0-96-43.072-96-96L128 160c0-52.928 43.072-96 96-96l448 0c52.928 0 96 43.072 96 96l0 576C768 788.928 724.928 832 672 832zM224 128C206.368 128 192 142.368 192 160l0 576c0 17.664 14.368 32 32 32l448 0c17.664 0 32-14.336 32-32L704 160c0-17.632-14.336-32-32-32L224 128z" /><path d="M800 960 320 960c-17.664 0-32-14.304-32-32s14.336-32 32-32l480 0c17.664 0 32-14.336 32-32L832 256c0-17.664 14.304-32 32-32s32 14.336 32 32l0 608C896 916.928 852.928 960 800 960z" /><path d="M544 320 288 320c-17.664 0-32-14.336-32-32s14.336-32 32-32l256 0c17.696 0 32 14.336 32 32S561.696 320 544 320z" /><path d="M608 480 288.032 480c-17.664 0-32-14.336-32-32s14.336-32 32-32L608 416c17.696 0 32 14.336 32 32S625.696 480 608 480z" /><path d="M608 640 288 640c-17.664 0-32-14.304-32-32s14.336-32 32-32l320 0c17.696 0 32 14.304 32 32S625.696 640 608 640z" /></svg>
|
||||||
|
After Width: | Height: | Size: 937 B |
@@ -1,12 +1,12 @@
|
|||||||
import FitFullscreen from '@/components/common/FitFullscreen.tsx'
|
|
||||||
import '@/assets/css/pages/system/tools/execute.scss'
|
import '@/assets/css/pages/system/tools/execute.scss'
|
||||||
import { base64ToFiles, base64ToStr, IMPORT_MAP_FILE_NAME } from '@/components/Playground/files.ts'
|
import { DATABASE_NO_RECORD_FOUND, DATABASE_SELECT_SUCCESS } from '@/constants/common.constants'
|
||||||
import { IImportMap } from '@/components/Playground/shared.ts'
|
import { r_sys_tool_get_one } from '@/services/system'
|
||||||
import compiler from '@/components/Playground/compiler.ts'
|
import FitFullscreen from '@/components/common/FitFullscreen'
|
||||||
import { DATABASE_NO_RECORD_FOUND, DATABASE_SELECT_SUCCESS } from '@/constants/common.constants.ts'
|
import Card from '@/components/common/Card'
|
||||||
import { r_sys_tool_get_one } from '@/services/system.tsx'
|
|
||||||
import Card from '@/components/common/Card.tsx'
|
|
||||||
import Playground from '@/components/Playground'
|
import Playground from '@/components/Playground'
|
||||||
|
import compiler from '@/components/Playground/compiler'
|
||||||
|
import { IImportMap } from '@/components/Playground/shared'
|
||||||
|
import { base64ToFiles, base64ToStr, IMPORT_MAP_FILE_NAME } from '@/components/Playground/files'
|
||||||
|
|
||||||
const Execute = () => {
|
const Execute = () => {
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { ChangeEvent, KeyboardEvent } from 'react'
|
import { ChangeEvent, KeyboardEvent } from 'react'
|
||||||
|
import Icon from '@ant-design/icons'
|
||||||
import {
|
import {
|
||||||
r_sys_tool_delete,
|
r_sys_tool_delete,
|
||||||
r_sys_tool_get,
|
r_sys_tool_get,
|
||||||
@@ -15,16 +16,15 @@ import {
|
|||||||
DATABASE_SELECT_SUCCESS,
|
DATABASE_SELECT_SUCCESS,
|
||||||
DATABASE_UPDATE_SUCCESS,
|
DATABASE_UPDATE_SUCCESS,
|
||||||
TOOL_NOT_UNDER_REVIEW
|
TOOL_NOT_UNDER_REVIEW
|
||||||
} from '@/constants/common.constants.ts'
|
} from '@/constants/common.constants'
|
||||||
import FlexBox from '@/components/common/FlexBox.tsx'
|
import FlexBox from '@/components/common/FlexBox'
|
||||||
import Card from '@/components/common/Card.tsx'
|
import Card from '@/components/common/Card'
|
||||||
import FitFullscreen from '@/components/common/FitFullscreen.tsx'
|
import FitFullscreen from '@/components/common/FitFullscreen'
|
||||||
import HideScrollbar from '@/components/common/HideScrollbar.tsx'
|
import HideScrollbar from '@/components/common/HideScrollbar'
|
||||||
import Icon from '@ant-design/icons'
|
import compiler from '@/components/Playground/compiler'
|
||||||
import compiler from '@/components/Playground/compiler.ts'
|
import { IImportMap } from '@/components/Playground/shared'
|
||||||
import { base64ToFiles, IMPORT_MAP_FILE_NAME, strToBase64 } from '@/components/Playground/files.ts'
|
import { base64ToFiles, IMPORT_MAP_FILE_NAME, strToBase64 } from '@/components/Playground/files'
|
||||||
import { IImportMap } from '@/components/Playground/shared.ts'
|
import Permission from '@/components/common/Permission'
|
||||||
import Permission from '@/components/common/Permission.tsx'
|
|
||||||
|
|
||||||
const Tools = () => {
|
const Tools = () => {
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { base64ToFiles } from '@/components/Playground/files'
|
|||||||
import Playground from '@/components/Playground'
|
import Playground from '@/components/Playground'
|
||||||
import FitFullscreen from '@/components/common/FitFullscreen'
|
import FitFullscreen from '@/components/common/FitFullscreen'
|
||||||
import Card from '@/components/common/Card'
|
import Card from '@/components/common/Card'
|
||||||
import { getLoginStatus } from '@/util/auth.tsx'
|
import { getLoginStatus } from '@/util/auth'
|
||||||
|
|
||||||
const Source = () => {
|
const Source = () => {
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { DetailedHTMLProps, HTMLAttributes, MouseEvent, ReactNode, UIEvent, useState } from 'react'
|
import { DetailedHTMLProps, HTMLAttributes, MouseEvent, ReactNode, UIEvent } from 'react'
|
||||||
import VanillaTilt, { TiltOptions } from 'vanilla-tilt'
|
import VanillaTilt, { TiltOptions } from 'vanilla-tilt'
|
||||||
import Icon from '@ant-design/icons'
|
import Icon from '@ant-design/icons'
|
||||||
import '@/assets/css/pages/tools/store.scss'
|
import '@/assets/css/pages/tools/store.scss'
|
||||||
@@ -56,6 +56,11 @@ const CommonCard = ({
|
|||||||
url && navigate(url)
|
url && navigate(url)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleOnClickAuthor = (e: MouseEvent<HTMLDivElement>) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
navigate(authorUsername)
|
||||||
|
}
|
||||||
|
|
||||||
const handleOnSourceBtnClick = (e: MouseEvent<HTMLDivElement>) => {
|
const handleOnSourceBtnClick = (e: MouseEvent<HTMLDivElement>) => {
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
navigate(`/source/${authorUsername}/${toolId}`)
|
navigate(`/source/${authorUsername}/${toolId}`)
|
||||||
@@ -78,12 +83,12 @@ const CommonCard = ({
|
|||||||
<div className={'tool-id'}>{`ID: ${toolId}`}</div>
|
<div className={'tool-id'}>{`ID: ${toolId}`}</div>
|
||||||
{toolDesc && <div className={'tool-desc'}>{`简介:${toolDesc}`}</div>}
|
{toolDesc && <div className={'tool-desc'}>{`简介:${toolDesc}`}</div>}
|
||||||
</div>
|
</div>
|
||||||
<div className={'author'}>
|
<div className={'author'} onClick={handleOnClickAuthor}>
|
||||||
<div className={'avatar'}>
|
<div className={'avatar'}>
|
||||||
<AntdAvatar
|
<AntdAvatar
|
||||||
src={
|
src={
|
||||||
<AntdImage
|
<AntdImage
|
||||||
preview={{ mask: <Icon component={IconOxygenEye}></Icon> }}
|
preview={false}
|
||||||
src={`data:image/png;base64,${authorAvatar}`}
|
src={`data:image/png;base64,${authorAvatar}`}
|
||||||
alt={'Avatar'}
|
alt={'Avatar'}
|
||||||
/>
|
/>
|
||||||
@@ -139,11 +144,13 @@ const Store = () => {
|
|||||||
const scrollTopRef = useRef(0)
|
const scrollTopRef = useRef(0)
|
||||||
const [isLoading, setIsLoading] = useState(false)
|
const [isLoading, setIsLoading] = useState(false)
|
||||||
const [currentPage, setCurrentPage] = useState(0)
|
const [currentPage, setCurrentPage] = useState(0)
|
||||||
const [hasNextPage, setHasNextPage] = useState(true)
|
const [hasNextPage, setHasNextPage] = useState(false)
|
||||||
const [toolData, setToolData] = useState<ToolVo[]>([])
|
const [toolData, setToolData] = useState<ToolVo[]>([])
|
||||||
const [hideSearch, setHideSearch] = useState(false)
|
const [hideSearch, setHideSearch] = useState(false)
|
||||||
|
const [searchValue, setSearchValue] = useState('')
|
||||||
|
|
||||||
const handleOnSearch = (value: string) => {
|
const handleOnSearch = (value: string) => {
|
||||||
|
setSearchValue(value)
|
||||||
getTool(1, value)
|
getTool(1, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -160,7 +167,7 @@ const Store = () => {
|
|||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
getTool(currentPage + 1, '')
|
getTool(currentPage + 1, searchValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
const getTool = (page: number, searchValue: string) => {
|
const getTool = (page: number, searchValue: string) => {
|
||||||
@@ -177,7 +184,10 @@ const Store = () => {
|
|||||||
switch (response.code) {
|
switch (response.code) {
|
||||||
case DATABASE_SELECT_SUCCESS:
|
case DATABASE_SELECT_SUCCESS:
|
||||||
setCurrentPage(response.data!.current)
|
setCurrentPage(response.data!.current)
|
||||||
if (response.data!.current === response.data!.pages) {
|
if (
|
||||||
|
response.data!.current === response.data!.pages ||
|
||||||
|
response.data!.total === 0
|
||||||
|
) {
|
||||||
setHasNextPage(false)
|
setHasNextPage(false)
|
||||||
} else {
|
} else {
|
||||||
setHasNextPage(true)
|
setHasNextPage(true)
|
||||||
@@ -220,6 +230,7 @@ const Store = () => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<FlexBox direction={'horizontal'} className={'root-content'}>
|
<FlexBox direction={'horizontal'} className={'root-content'}>
|
||||||
|
{!toolData.length && <div className={'no-tool'}>未找到任何工具</div>}
|
||||||
{toolData?.map((value) => (
|
{toolData?.map((value) => (
|
||||||
<CommonCard
|
<CommonCard
|
||||||
key={value.id}
|
key={value.id}
|
||||||
|
|||||||
295
src/pages/Tools/User.tsx
Normal file
295
src/pages/Tools/User.tsx
Normal file
@@ -0,0 +1,295 @@
|
|||||||
|
import { DetailedHTMLProps, HTMLAttributes, MouseEvent, ReactNode } from 'react'
|
||||||
|
import VanillaTilt, { TiltOptions } from 'vanilla-tilt'
|
||||||
|
import Icon from '@ant-design/icons'
|
||||||
|
import '@/assets/css/pages/tools/user.scss'
|
||||||
|
import {
|
||||||
|
COLOR_BACKGROUND,
|
||||||
|
DATABASE_NO_RECORD_FOUND,
|
||||||
|
DATABASE_SELECT_SUCCESS
|
||||||
|
} from '@/constants/common.constants'
|
||||||
|
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
|
||||||
|
url: string
|
||||||
|
authorUsername: string
|
||||||
|
ver: string
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
},
|
||||||
|
url,
|
||||||
|
authorUsername,
|
||||||
|
ver,
|
||||||
|
...props
|
||||||
|
}: CommonCardProps) => {
|
||||||
|
const navigate = useNavigate()
|
||||||
|
const cardRef = useRef<HTMLDivElement>(null)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
cardRef.current && VanillaTilt.init(cardRef.current, options)
|
||||||
|
}, [options])
|
||||||
|
|
||||||
|
const handleCardOnClick = () => {
|
||||||
|
url && navigate(url)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleOnSourceBtnClick = (e: MouseEvent<HTMLDivElement>) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
navigate(`/source/${authorUsername}/${toolId}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card
|
||||||
|
style={{ overflow: 'visible', ...style }}
|
||||||
|
ref={cardRef}
|
||||||
|
{...props}
|
||||||
|
onClick={handleCardOnClick}
|
||||||
|
>
|
||||||
|
<FlexBox className={'common-card'}>
|
||||||
|
<div className={'icon'}>{icon}</div>
|
||||||
|
<div className={'version'}>
|
||||||
|
<AntdTag>V{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'}>
|
||||||
|
<AntdTooltip title={'源码'}>
|
||||||
|
<Icon component={IconOxygenCode} onClick={handleOnSourceBtnClick} />
|
||||||
|
</AntdTooltip>
|
||||||
|
</div>
|
||||||
|
</FlexBox>
|
||||||
|
</Card>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
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>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const User = () => {
|
||||||
|
const { username } = useParams()
|
||||||
|
const navigate = useNavigate()
|
||||||
|
const [isLoading, setIsLoading] = useState(false)
|
||||||
|
const [userWithInfoVo, setUserWithInfoVo] = useState<UserWithInfoVo>()
|
||||||
|
const [isLoadingTools, setIsLoadingTools] = useState(false)
|
||||||
|
const [currentPage, setCurrentPage] = useState(0)
|
||||||
|
const [hasNextPage, setHasNextPage] = useState(false)
|
||||||
|
const [toolData, setToolData] = useState<ToolVo[]>([])
|
||||||
|
|
||||||
|
const handleOnCopyToClipboard = (username?: string) => {
|
||||||
|
return username
|
||||||
|
? () => {
|
||||||
|
void navigator.clipboard
|
||||||
|
.writeText(new URL(`/store/${username}`, location.href).href)
|
||||||
|
.then(() => {
|
||||||
|
void message.success('已复制到剪切板')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
const getProfile = () => {
|
||||||
|
if (isLoading) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
setIsLoading(true)
|
||||||
|
void message.loading({ content: '加载中', key: 'LOADING', duration: 0 })
|
||||||
|
|
||||||
|
void r_sys_user_info_get_basic(username!)
|
||||||
|
.then((res) => {
|
||||||
|
const response = res.data
|
||||||
|
switch (response.code) {
|
||||||
|
case DATABASE_SELECT_SUCCESS:
|
||||||
|
setUserWithInfoVo(response.data!)
|
||||||
|
getTool(1)
|
||||||
|
break
|
||||||
|
case DATABASE_NO_RECORD_FOUND:
|
||||||
|
void message.warning('用户不存在')
|
||||||
|
setTimeout(() => {
|
||||||
|
navigate(-1)
|
||||||
|
}, 3000)
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
void message.error('获取失败请稍后重试')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
setIsLoading(false)
|
||||||
|
void message.destroy('LOADING')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleOnLoadMore = () => {
|
||||||
|
if (isLoading) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
getTool(currentPage + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
const getTool = (page: number) => {
|
||||||
|
if (isLoadingTools) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
setIsLoadingTools(true)
|
||||||
|
void message.loading({ content: '加载工具列表中', key: 'LOADING', duration: 0 })
|
||||||
|
|
||||||
|
void r_tool_store_get_by_username(username!, { currentPage: page })
|
||||||
|
.then((res) => {
|
||||||
|
const response = res.data
|
||||||
|
|
||||||
|
switch (response.code) {
|
||||||
|
case DATABASE_SELECT_SUCCESS:
|
||||||
|
setCurrentPage(response.data!.current)
|
||||||
|
if (
|
||||||
|
response.data!.current === response.data!.pages ||
|
||||||
|
response.data!.total === 0
|
||||||
|
) {
|
||||||
|
setHasNextPage(false)
|
||||||
|
} else {
|
||||||
|
setHasNextPage(true)
|
||||||
|
}
|
||||||
|
if (response.data!.current === 1) {
|
||||||
|
setToolData(response.data!.records)
|
||||||
|
} else {
|
||||||
|
setToolData([...toolData, ...response.data!.records])
|
||||||
|
}
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
void message.error('加载失败,请稍后重试')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
setIsLoadingTools(false)
|
||||||
|
message.destroy('LOADING')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
getProfile()
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<FitFullscreen data-component={'tools-store-user'}>
|
||||||
|
<HideScrollbar
|
||||||
|
isShowVerticalScrollbar
|
||||||
|
autoHideWaitingTime={1000}
|
||||||
|
className={'root-content'}
|
||||||
|
>
|
||||||
|
<Card className={'root-box'}>
|
||||||
|
<FlexBox className={'info'} direction={'horizontal'}>
|
||||||
|
<div className={'avatar-box'}>
|
||||||
|
<AntdAvatar
|
||||||
|
src={
|
||||||
|
<img
|
||||||
|
src={`data:image/png;base64,${userWithInfoVo?.userInfo.avatar}`}
|
||||||
|
alt={'Avatar'}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
size={144}
|
||||||
|
style={{
|
||||||
|
background: COLOR_BACKGROUND,
|
||||||
|
cursor: 'pointer'
|
||||||
|
}}
|
||||||
|
className={'avatar'}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<FlexBox className={'info-name'}>
|
||||||
|
<div className={'nickname'}>
|
||||||
|
{userWithInfoVo?.userInfo.nickname}
|
||||||
|
</div>
|
||||||
|
<a
|
||||||
|
className={'url'}
|
||||||
|
onClick={handleOnCopyToClipboard(userWithInfoVo?.username)}
|
||||||
|
>
|
||||||
|
{userWithInfoVo?.username &&
|
||||||
|
new URL(`/store/${userWithInfoVo.username}`, location.href)
|
||||||
|
.href}
|
||||||
|
<Icon component={IconOxygenCopy} />
|
||||||
|
</a>
|
||||||
|
</FlexBox>
|
||||||
|
</FlexBox>
|
||||||
|
<FlexBox direction={'horizontal'} className={'tools'}>
|
||||||
|
{!toolData.length && (
|
||||||
|
<div className={'no-tool'}>该开发者暂未发布任何工具</div>
|
||||||
|
)}
|
||||||
|
{toolData?.map((value) => (
|
||||||
|
<CommonCard
|
||||||
|
key={value.id}
|
||||||
|
icon={
|
||||||
|
<img
|
||||||
|
src={`data:image/svg+xml;base64,${value.icon}`}
|
||||||
|
alt={'Icon'}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
toolName={value.name}
|
||||||
|
toolId={value.toolId}
|
||||||
|
toolDesc={value.description}
|
||||||
|
url={`/view/${value.author.username}/${value.toolId}`}
|
||||||
|
authorUsername={value.author.username}
|
||||||
|
ver={value.ver}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
{hasNextPage && <LoadMoreCard onClick={handleOnLoadMore} />}
|
||||||
|
</FlexBox>
|
||||||
|
</Card>
|
||||||
|
</HideScrollbar>
|
||||||
|
</FitFullscreen>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default User
|
||||||
@@ -266,14 +266,14 @@ const User = () => {
|
|||||||
href={
|
href={
|
||||||
userWithPowerInfoVo?.username &&
|
userWithPowerInfoVo?.username &&
|
||||||
new URL(
|
new URL(
|
||||||
`/view/${userWithPowerInfoVo.username}`,
|
`/store/${userWithPowerInfoVo.username}`,
|
||||||
location.href
|
location.href
|
||||||
).href
|
).href
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{userWithPowerInfoVo?.username &&
|
{userWithPowerInfoVo?.username &&
|
||||||
new URL(
|
new URL(
|
||||||
`/view/${userWithPowerInfoVo.username}`,
|
`/store/${userWithPowerInfoVo.username}`,
|
||||||
location.href
|
location.href
|
||||||
).href}
|
).href}
|
||||||
<Icon component={IconOxygenShare} />
|
<Icon component={IconOxygenShare} />
|
||||||
|
|||||||
@@ -19,6 +19,13 @@ export const tools: RouteJsonObject[] = [
|
|||||||
icon: lazy(() => import('~icons/oxygen/store')),
|
icon: lazy(() => import('~icons/oxygen/store')),
|
||||||
menu: true
|
menu: true
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'store/:username',
|
||||||
|
absolutePath: '/store',
|
||||||
|
id: 'tools-view-user',
|
||||||
|
component: lazy(() => import('@/pages/Tools/User')),
|
||||||
|
name: '开发者'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: 'create',
|
path: 'create',
|
||||||
absolutePath: '/create',
|
absolutePath: '/create',
|
||||||
|
|||||||
@@ -26,6 +26,9 @@ import request from '@/services/index'
|
|||||||
|
|
||||||
export const r_sys_user_info_get = () => request.get<UserWithPowerInfoVo>(URL_SYS_USER_INFO)
|
export const r_sys_user_info_get = () => request.get<UserWithPowerInfoVo>(URL_SYS_USER_INFO)
|
||||||
|
|
||||||
|
export const r_sys_user_info_get_basic = (username: string) =>
|
||||||
|
request.get<UserWithInfoVo>(`${URL_SYS_USER_INFO}/${username}`)
|
||||||
|
|
||||||
export const r_sys_user_info_update = (param: UserInfoUpdateParam) =>
|
export const r_sys_user_info_update = (param: UserInfoUpdateParam) =>
|
||||||
request.patch(URL_SYS_USER_INFO, param)
|
request.patch(URL_SYS_USER_INFO, param)
|
||||||
|
|
||||||
|
|||||||
@@ -33,3 +33,6 @@ export const r_tool_delete = (id: string) => request.delete(`${URL_TOOL}/${id}`)
|
|||||||
|
|
||||||
export const r_tool_store_get = (param: ToolStoreGetParam) =>
|
export const r_tool_store_get = (param: ToolStoreGetParam) =>
|
||||||
request.get<PageVo<ToolVo>>(URL_TOOL_STORE, param)
|
request.get<PageVo<ToolVo>>(URL_TOOL_STORE, param)
|
||||||
|
|
||||||
|
export const r_tool_store_get_by_username = (username: string, param: ToolStoreGetParam) =>
|
||||||
|
request.get<PageVo<ToolVo>>(`${URL_TOOL_STORE}/${username}`, param)
|
||||||
|
|||||||
Reference in New Issue
Block a user