diff --git a/package-lock.json b/package-lock.json
index 97ec8d1..2688f85 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -15,6 +15,9 @@
},
"devDependencies": {
"@ant-design/icons": "^5.3.1",
+ "@dnd-kit/core": "^6.1.0",
+ "@dnd-kit/sortable": "^8.0.0",
+ "@dnd-kit/utilities": "^3.2.2",
"@electron-toolkit/eslint-config-prettier": "^2.0.0",
"@electron-toolkit/eslint-config-ts": "^1.0.1",
"@electron-toolkit/tsconfig": "^1.0.1",
@@ -602,6 +605,59 @@
"url": "https://opencollective.com/webpack"
}
},
+ "node_modules/@dnd-kit/accessibility": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmmirror.com/@dnd-kit/accessibility/-/accessibility-3.1.0.tgz",
+ "integrity": "sha512-ea7IkhKvlJUv9iSHJOnxinBcoOI3ppGnnL+VDJ75O45Nss6HtZd8IdN8touXPDtASfeI2T2LImb8VOZcL47wjQ==",
+ "dev": true,
+ "dependencies": {
+ "tslib": "^2.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.8.0"
+ }
+ },
+ "node_modules/@dnd-kit/core": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmmirror.com/@dnd-kit/core/-/core-6.1.0.tgz",
+ "integrity": "sha512-J3cQBClB4TVxwGo3KEjssGEXNJqGVWx17aRTZ1ob0FliR5IjYgTxl5YJbKTzA6IzrtelotH19v6y7uoIRUZPSg==",
+ "dev": true,
+ "dependencies": {
+ "@dnd-kit/accessibility": "^3.1.0",
+ "@dnd-kit/utilities": "^3.2.2",
+ "tslib": "^2.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.8.0",
+ "react-dom": ">=16.8.0"
+ }
+ },
+ "node_modules/@dnd-kit/sortable": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmmirror.com/@dnd-kit/sortable/-/sortable-8.0.0.tgz",
+ "integrity": "sha512-U3jk5ebVXe1Lr7c2wU7SBZjcWdQP+j7peHJfCspnA81enlu88Mgd7CC8Q+pub9ubP7eKVETzJW+IBAhsqbSu/g==",
+ "dev": true,
+ "dependencies": {
+ "@dnd-kit/utilities": "^3.2.2",
+ "tslib": "^2.0.0"
+ },
+ "peerDependencies": {
+ "@dnd-kit/core": "^6.1.0",
+ "react": ">=16.8.0"
+ }
+ },
+ "node_modules/@dnd-kit/utilities": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmmirror.com/@dnd-kit/utilities/-/utilities-3.2.2.tgz",
+ "integrity": "sha512-+MKAJEOfaBe5SmV6t34p80MMKhjvUz0vRrvVJbPT0WElzaOJ/1xs+D+KDv+tD/NE5ujfrChEcshd4fLn0wpiqg==",
+ "dev": true,
+ "dependencies": {
+ "tslib": "^2.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.8.0"
+ }
+ },
"node_modules/@electron-toolkit/eslint-config-prettier": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@electron-toolkit/eslint-config-prettier/-/eslint-config-prettier-2.0.0.tgz",
diff --git a/package.json b/package.json
index ee5530b..1f6e452 100644
--- a/package.json
+++ b/package.json
@@ -30,6 +30,9 @@
},
"devDependencies": {
"@ant-design/icons": "^5.3.1",
+ "@dnd-kit/core": "^6.1.0",
+ "@dnd-kit/sortable": "^8.0.0",
+ "@dnd-kit/utilities": "^3.2.2",
"@electron-toolkit/eslint-config-prettier": "^2.0.0",
"@electron-toolkit/eslint-config-ts": "^1.0.1",
"@electron-toolkit/tsconfig": "^1.0.1",
diff --git a/src/renderer/src/assets/css/components/common/sidebar.scss b/src/renderer/src/assets/css/components/common/sidebar.scss
index 42190cd..6e3def4 100644
--- a/src/renderer/src/assets/css/components/common/sidebar.scss
+++ b/src/renderer/src/assets/css/components/common/sidebar.scss
@@ -55,13 +55,14 @@
.scroll {
min-height: 0;
flex: 1;
+ width: 100%;
}
ul {
- > li {
+ > li, > div > li {
+ padding: 2px 14px;
&.item {
position: relative;
- margin: 4px 14px;
font-size: 1.4em;
>.menu-bt {
@@ -78,6 +79,10 @@
height: 40px;
font-size: constants.$SIZE_ICON_SM;
cursor: pointer;
+
+ img{
+ width: 100%;
+ }
}
a {
@@ -86,6 +91,7 @@
height: 100%;
width: 100%;
transition: all 0.2s;
+ background-color: constants.$origin-color;
.text {
flex: 1;
@@ -94,7 +100,12 @@
&.active {
color: constants.$origin-color;
- background-color: constants.$main-color !important;
+ background-color: constants.$main-color;
+
+ img {
+ filter: drop-shadow(1000px 0 0 constants.$origin-color);
+ transform: translate(-1000px);
+ }
}
}
}
@@ -136,11 +147,11 @@
&.active {
color: constants.$origin-color;
- background-color: constants.$main-color !important;
+ background-color: constants.$main-color;
}
}
- &:hover a {
+ &:hover a:not(.active) {
background-color: constants.$background-color;
}
}
@@ -149,7 +160,7 @@
&:hover {
>.menu-bt {
- a {
+ a:not(.active) {
background-color: constants.$background-color;
}
}
@@ -171,6 +182,22 @@
}
}
}
+
+ .delete {
+ .menu-bt {
+ border: {
+ width: 1px;
+ color: constants.$error-secondary-color;
+ style: dashed;
+ };
+ filter: drop-shadow(1000px 0 0 constants.$error-secondary-color);
+ transform: translate(-1000px);
+
+ > a {
+ background-color: transparent !important;
+ }
+ }
+ }
}
}
@@ -257,7 +284,7 @@
}
.menu-bt {
- .text {
+ .text, .extend {
display: none;
}
}
diff --git a/src/renderer/src/assets/css/components/dnd/drag-handle.scss b/src/renderer/src/assets/css/components/dnd/drag-handle.scss
new file mode 100644
index 0000000..22b1088
--- /dev/null
+++ b/src/renderer/src/assets/css/components/dnd/drag-handle.scss
@@ -0,0 +1,5 @@
+[data-component=component-drag-handle] {
+ background-color: transparent;
+ color: unset;
+ cursor: grab;
+}
diff --git a/src/renderer/src/assets/css/components/tools/repository-card.scss b/src/renderer/src/assets/css/components/tools/repository-card.scss
index 06d360e..b563c6f 100644
--- a/src/renderer/src/assets/css/components/tools/repository-card.scss
+++ b/src/renderer/src/assets/css/components/tools/repository-card.scss
@@ -1,6 +1,8 @@
@use '@/assets/css/constants' as constants;
[data-component=component-repository-card] {
+ height: 100%;
+
.repository-card {
width: 100%;
height: 100%;
@@ -12,23 +14,25 @@
flex: 0 0 auto;
}
- .version-select {
- position: absolute;
- top: 10px;
- left: 10px;
- width: 9em;
- }
+ .header {
+ display: flex;
+ width: 100%;
+ align-items: center;
+ padding: 10px;
- .upgrade-bt {
- position: absolute;
- top: 10px;
- right: 10px;
- font-size: 1.8em;
+ .version-select {
+ width: 9em;
+ margin-right: auto;
+ }
+
+ >:not(.version-select) {
+ font-size: 1.6em;
+ }
}
.icon {
display: flex;
- padding-top: 50px;
+ padding-top: 10px;
padding-bottom: 20px;
color: constants.$production-color;
font-size: constants.$SIZE_ICON_XL;
diff --git a/src/renderer/src/assets/css/components/tools/store-card.scss b/src/renderer/src/assets/css/components/tools/store-card.scss
index 15d0570..1652173 100644
--- a/src/renderer/src/assets/css/components/tools/store-card.scss
+++ b/src/renderer/src/assets/css/components/tools/store-card.scss
@@ -1,6 +1,7 @@
@use '@/assets/css/constants' as constants;
[data-component=component-store-card] {
+ height: 100%;
cursor: pointer;
.store-card {
@@ -14,10 +15,31 @@
flex: 0 0 auto;
}
+ .header {
+ display: flex;
+ width: 100%;
+ padding: 10px;
+ justify-content: space-between;
+
+ .version {
+ width: 0;
+ }
+
+ .operation {
+ display: flex;
+ font-size: 1.6em;
+ gap: 4px;
+ opacity: 0;
+
+ > *:hover {
+ color: constants.$font-secondary-color;
+ }
+ }
+ }
.icon {
display: flex;
- padding-top: 40px;
+ padding-top: 10px;
padding-bottom: 20px;
color: constants.$production-color;
font-size: constants.$SIZE_ICON_XL;
@@ -28,12 +50,6 @@
}
}
- .version {
- position: absolute;
- left: 10px;
- top: 10px;
- }
-
.info {
padding-top: 20px;
@@ -43,8 +59,16 @@
}
.tool-desc {
- margin-top: 10px;
+ margin: {
+ top: 10px;
+ left: auto;
+ right: auto;
+ };
color: constants.$font-secondary-color;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ max-height: 40px;
+ width: 80%;
}
}
@@ -68,17 +92,16 @@
align-items: center;
}
}
+ }
- .operation {
- display: flex;
- position: absolute;
- top: 10px;
- right: 12px;
- font-size: 1.6em;
- gap: 4px;
+ :hover {
+ .header {
+ .version {
+ opacity: 0;
+ }
- > *:hover {
- color: constants.$font-secondary-color;
+ .operation {
+ opacity: 1;
}
}
}
diff --git a/src/renderer/src/assets/css/pages/tools-framework.scss b/src/renderer/src/assets/css/pages/tools-framework.scss
index 79d055f..137c254 100644
--- a/src/renderer/src/assets/css/pages/tools-framework.scss
+++ b/src/renderer/src/assets/css/pages/tools-framework.scss
@@ -4,6 +4,13 @@
[data-component=tools-framework] {
.left-panel {
background-color: constants.$origin-color;
+
+ .menu-droppable {
+ display: flex;
+ min-height: 0;
+ flex: 1;
+ width: 100%;
+ }
}
.right-panel {
diff --git a/src/renderer/src/assets/css/pages/tools/index.scss b/src/renderer/src/assets/css/pages/tools/index.scss
index db8f1a9..95a2196 100644
--- a/src/renderer/src/assets/css/pages/tools/index.scss
+++ b/src/renderer/src/assets/css/pages/tools/index.scss
@@ -11,7 +11,7 @@
flex-wrap: wrap;
justify-content: flex-start;
- > .card-box {
+ > .card-box, > div {
width: 180px;
height: 290px;
flex: 0 0 auto;
@@ -77,6 +77,7 @@
flex-direction: row;
align-items: center;
gap: 20px;
+ margin-top: 20px;
:first-child, :last-child {
height: 0;
@@ -99,7 +100,7 @@
flex-wrap: wrap;
justify-content: flex-start;
- > .card-box {
+ > .card-box, > div {
width: 180px;
height: 290px;
flex: 0 0 auto;
diff --git a/src/renderer/src/assets/css/pages/tools/store.scss b/src/renderer/src/assets/css/pages/tools/store.scss
index b87a3f5..3bead2e 100644
--- a/src/renderer/src/assets/css/pages/tools/store.scss
+++ b/src/renderer/src/assets/css/pages/tools/store.scss
@@ -26,7 +26,7 @@
flex-wrap: wrap;
justify-content: center;
- > .card-box {
+ > div {
width: 180px;
height: 290px;
flex: 0 0 auto;
diff --git a/src/renderer/src/assets/css/pages/tools/user.scss b/src/renderer/src/assets/css/pages/tools/user.scss
index 97eb6c0..54d0e74 100644
--- a/src/renderer/src/assets/css/pages/tools/user.scss
+++ b/src/renderer/src/assets/css/pages/tools/user.scss
@@ -68,7 +68,7 @@
flex-wrap: wrap;
justify-content: center;
- > .card-box {
+ > div {
width: 180px;
height: 290px;
flex: 0 0 auto;
diff --git a/src/renderer/src/assets/svg/handle.svg b/src/renderer/src/assets/svg/handle.svg
new file mode 100644
index 0000000..5d9156f
--- /dev/null
+++ b/src/renderer/src/assets/svg/handle.svg
@@ -0,0 +1 @@
+
diff --git a/src/renderer/src/components/common/Sidebar/Item.tsx b/src/renderer/src/components/common/Sidebar/Item.tsx
index 1031b15..7f6885d 100644
--- a/src/renderer/src/components/common/Sidebar/Item.tsx
+++ b/src/renderer/src/components/common/Sidebar/Item.tsx
@@ -3,10 +3,11 @@ import Icon from '@ant-design/icons'
import Submenu from '@/components/common/Sidebar/Submenu'
type ItemProps = {
- icon?: IconComponent
+ icon?: IconComponent | string
text?: string
path: string
children?: ReactNode
+ extend?: ReactNode
end?: boolean
}
@@ -42,9 +43,19 @@ const Item = (props: ItemProps) => {
}
>
- {props.icon &&
}
+ {props.icon &&
+ (typeof props.icon === 'string' ? (
+

+ ) : (
+
+ ))}
{props.text}
+ {props.extend}
{props.children && (
diff --git a/src/renderer/src/components/common/Sidebar/Separate.tsx b/src/renderer/src/components/common/Sidebar/Separate.tsx
index 4c42aff..b1ffa0f 100644
--- a/src/renderer/src/components/common/Sidebar/Separate.tsx
+++ b/src/renderer/src/components/common/Sidebar/Separate.tsx
@@ -4,7 +4,7 @@ const Separate = ({
className,
...props
}: DetailedHTMLProps, HTMLDivElement>) => {
- return
+ return
}
export default Separate
diff --git a/src/renderer/src/components/dnd/DragHandle.tsx b/src/renderer/src/components/dnd/DragHandle.tsx
new file mode 100644
index 0000000..eb81812
--- /dev/null
+++ b/src/renderer/src/components/dnd/DragHandle.tsx
@@ -0,0 +1,27 @@
+import { HandleContextInst } from '@/components/dnd/HandleContext'
+import Icon from '@ant-design/icons'
+import '@/assets/css/components/dnd/drag-handle.scss'
+
+interface DragHandleProps {
+ padding?: string | number
+}
+
+const DragHandle = ({ padding }: DragHandleProps) => {
+ // eslint-disable-next-line @typescript-eslint/unbound-method
+ const { attributes, listeners, ref } = useContext(HandleContextInst)
+
+ return (
+
+ )
+}
+
+export default DragHandle
diff --git a/src/renderer/src/components/dnd/Draggable.tsx b/src/renderer/src/components/dnd/Draggable.tsx
new file mode 100644
index 0000000..756570d
--- /dev/null
+++ b/src/renderer/src/components/dnd/Draggable.tsx
@@ -0,0 +1,47 @@
+import { CSSProperties, PropsWithChildren } from 'react'
+import { useDraggable } from '@dnd-kit/core'
+import { HandleContext, HandleContextInst } from '@/components/dnd/HandleContext'
+
+interface DraggableProps extends PropsWithChildren {
+ id: string
+ data: ToolMenuItem
+}
+
+const Draggable = ({ id, data, children }: DraggableProps) => {
+ const {
+ attributes,
+ isDragging,
+ listeners,
+ setNodeRef: draggableRef,
+ setActivatorNodeRef,
+ transform
+ } = useDraggable({
+ id,
+ data
+ })
+ const context = useMemo(
+ () => ({
+ attributes,
+ listeners,
+ ref: setActivatorNodeRef
+ }),
+ [attributes, listeners, setActivatorNodeRef]
+ )
+ const style: CSSProperties | undefined = transform
+ ? {
+ opacity: isDragging ? 0 : undefined,
+ transform: `translate3d(${transform.x}px, ${transform.y}px, 0)`,
+ zIndex: 10000
+ }
+ : undefined
+
+ return (
+
+
+ {children}
+
+
+ )
+}
+
+export default Draggable
diff --git a/src/renderer/src/components/dnd/DraggableOverlay.tsx b/src/renderer/src/components/dnd/DraggableOverlay.tsx
new file mode 100644
index 0000000..0e4f3fd
--- /dev/null
+++ b/src/renderer/src/components/dnd/DraggableOverlay.tsx
@@ -0,0 +1,22 @@
+import { PropsWithChildren } from 'react'
+import { defaultDropAnimationSideEffects, DragOverlay, DropAnimation } from '@dnd-kit/core'
+
+interface DraggableOverlayProps extends PropsWithChildren {
+ isDelete?: boolean
+}
+
+const dropAnimationConfig: DropAnimation = {
+ sideEffects: defaultDropAnimationSideEffects({
+ styles: {
+ active: {
+ opacity: '0.4'
+ }
+ }
+ })
+}
+
+const DraggableOverlay = ({ children }: DraggableOverlayProps) => {
+ return {children}
+}
+
+export default DraggableOverlay
diff --git a/src/renderer/src/components/dnd/Droppable.tsx b/src/renderer/src/components/dnd/Droppable.tsx
new file mode 100644
index 0000000..08229b0
--- /dev/null
+++ b/src/renderer/src/components/dnd/Droppable.tsx
@@ -0,0 +1,16 @@
+import { DetailedHTMLProps, HTMLAttributes } from 'react'
+import { useDroppable } from '@dnd-kit/core'
+
+interface DroppableProps extends DetailedHTMLProps, HTMLDivElement> {
+ id: string
+}
+
+const Droppable = ({ id, ...props }: DroppableProps) => {
+ const { setNodeRef: droppableRef } = useDroppable({
+ id
+ })
+
+ return
+}
+
+export default Droppable
diff --git a/src/renderer/src/components/dnd/HandleContext.ts b/src/renderer/src/components/dnd/HandleContext.ts
new file mode 100644
index 0000000..88420b2
--- /dev/null
+++ b/src/renderer/src/components/dnd/HandleContext.ts
@@ -0,0 +1,13 @@
+import { DraggableSyntheticListeners } from '@dnd-kit/core'
+
+export interface HandleContext {
+ attributes: Record
+ listeners: DraggableSyntheticListeners
+ ref(node: HTMLElement | null): void
+}
+
+export const HandleContextInst = createContext({
+ attributes: {},
+ listeners: undefined,
+ ref() {}
+})
diff --git a/src/renderer/src/components/dnd/Sortable.tsx b/src/renderer/src/components/dnd/Sortable.tsx
new file mode 100644
index 0000000..81cd0b7
--- /dev/null
+++ b/src/renderer/src/components/dnd/Sortable.tsx
@@ -0,0 +1,54 @@
+import { CSSProperties, PropsWithChildren } from 'react'
+import { useSortable } from '@dnd-kit/sortable'
+import { HandleContext, HandleContextInst } from '@/components/dnd/HandleContext'
+
+interface SortableProps extends PropsWithChildren {
+ id: string
+ data: ToolMenuItem
+ isDelete?: boolean
+}
+
+const Sortable = ({ id, data, isDelete, children }: SortableProps) => {
+ const {
+ attributes,
+ isDragging,
+ listeners,
+ setNodeRef: draggableRef,
+ setActivatorNodeRef,
+ transform,
+ transition
+ } = useSortable({
+ id,
+ data
+ })
+ const context = useMemo(
+ () => ({
+ attributes,
+ listeners,
+ ref: setActivatorNodeRef
+ }),
+ [attributes, listeners, setActivatorNodeRef]
+ )
+ const style: CSSProperties | undefined = transform
+ ? {
+ opacity: isDragging ? 0.4 : undefined,
+ transform: `translate3d(${transform.x}px, ${transform.y}px, 0)`,
+ zIndex: 10000,
+ transition
+ }
+ : undefined
+
+ return (
+
+
+ {children}
+
+
+ )
+}
+
+export default Sortable
diff --git a/src/renderer/src/components/tools/RepositoryCard.tsx b/src/renderer/src/components/tools/RepositoryCard.tsx
index eebe6dd..bbbe8d6 100644
--- a/src/renderer/src/components/tools/RepositoryCard.tsx
+++ b/src/renderer/src/components/tools/RepositoryCard.tsx
@@ -1,14 +1,17 @@
-import { DetailedHTMLProps, HTMLAttributes, ReactNode } from 'react'
+import { DetailedHTMLProps, HTMLAttributes } from 'react'
import VanillaTilt, { TiltOptions } from 'vanilla-tilt'
import '@/assets/css/components/tools/repository-card.scss'
import Card from '@/components/common/Card'
import FlexBox from '@/components/common/FlexBox'
+import Draggable from '@/components/dnd/Draggable'
+import DragHandle from '@/components/dnd/DragHandle.tsx'
interface RepositoryCardProps
extends DetailedHTMLProps, HTMLDivElement> {
- icon: ReactNode
+ icon: string
toolName: string
toolId: string
+ ver: string
options?: TiltOptions
onOpen?: () => void
onEdit?: () => void
@@ -25,6 +28,7 @@ const RepositoryCard = ({
icon,
toolName,
toolId,
+ ver,
options = {
reverse: true,
max: 8,
@@ -48,51 +52,58 @@ const RepositoryCard = ({
}, [options])
return (
-
-
- {icon}
-
- {toolName &&
{toolName}
}
- {toolId &&
{`ID: ${toolId}`}
}
-
-
- {onOpen && (
-
- 打开
-
- )}
- {onEdit && onPublish && (
-
- )}
- {onSource && (
-
- 源码
-
- )}
- {onCancelReview && (
-
- 取消审核
-
- )}
- {onDelete && (
-
- 删除
-
- )}
-
- {children}
-
-
+
+
+
+
+ {children}
+
+
+
+

+
+
+
{toolName}
+
{`ID: ${toolId}`}
+
+
+ {onOpen && (
+
+ 打开
+
+ )}
+ {onEdit && onPublish && (
+
+ )}
+ {onSource && (
+
+ 源码
+
+ )}
+ {onCancelReview && (
+
+ 取消审核
+
+ )}
+ {onDelete && (
+
+ 删除
+
+ )}
+
+
+
+
)
}
diff --git a/src/renderer/src/components/tools/StoreCard.tsx b/src/renderer/src/components/tools/StoreCard.tsx
index 43f32da..0a77fb8 100644
--- a/src/renderer/src/components/tools/StoreCard.tsx
+++ b/src/renderer/src/components/tools/StoreCard.tsx
@@ -1,4 +1,4 @@
-import { DetailedHTMLProps, HTMLAttributes, MouseEvent, ReactNode } from 'react'
+import { DetailedHTMLProps, HTMLAttributes, MouseEvent } from 'react'
import VanillaTilt, { TiltOptions } from 'vanilla-tilt'
import protocolCheck from 'custom-protocol-check'
import Icon from '@ant-design/icons'
@@ -10,9 +10,11 @@ import { r_tool_add_favorite, r_tool_remove_favorite } from '@/services/tool'
import Card from '@/components/common/Card'
import FlexBox from '@/components/common/FlexBox'
import { getUserId } from '@/util/auth.tsx'
+import DragHandle from '@/components/dnd/DragHandle.tsx'
+import Draggable from '@/components/dnd/Draggable.tsx'
interface StoreCardProps extends DetailedHTMLProps, HTMLDivElement> {
- icon: ReactNode
+ icon: string
toolName: string
toolId: string
toolDesc: string
@@ -40,8 +42,8 @@ const StoreCard = ({
['max-glare']: 0.3,
scale: 1.03
},
- author,
- showAuthor = true,
+ author,
+ showAuthor = true,
ver,
platform,
supportPlatform,
@@ -176,81 +178,101 @@ const StoreCard = ({
return (
<>
-
-
- {icon}
-
-
- {platform.slice(0, 1)}-{ver}
-
-
-
-
{toolName}
-
{`ID: ${toolId}`}
- {toolDesc &&
{`简介:${toolDesc}`}
}
-
- {showAuthor && (
-
-
-
- }
- style={{ background: COLOR_BACKGROUND }}
- />
+
+
+
+
+
+ {platform.slice(0, 1)}-{ver}
+
+
+
+ {platform !== 'ANDROID' && supportPlatform.includes('ANDROID') && (
+
+
+
+ )}
+ {platform === 'DESKTOP' && supportPlatform.includes('WEB') && (
+
+
+
+ )}
+ {platform === 'WEB' && supportPlatform.includes('DESKTOP') && (
+
+
+
+ )}
+
+
+
+ {author.id !== userId && (
+
+
+
+ )}
+
-
- {author.userInfo.nickname}
-
- )}
-
- {platform !== 'ANDROID' && supportPlatform.includes('ANDROID') && (
-
-
-
+
+

+
+
+
{toolName}
+
{`ID: ${toolId}`}
+ {toolDesc &&
{`简介:${toolDesc}`}
}
+
+ {showAuthor && (
+
+
+
+ }
+ style={{ background: COLOR_BACKGROUND }}
+ />
+
+
+ {author.userInfo.nickname}
+
+
)}
- {platform === 'DESKTOP' && supportPlatform.includes('WEB') && (
-
-
-
- )}
- {platform === 'WEB' && supportPlatform.includes('DESKTOP') && (
-
-
-
- )}
-
-
-
- {author.id !== userId && (
-
-
-
- )}
-
-
-
+
+
+
{contextHolder}
>
)
diff --git a/src/renderer/src/constants/common.constants.ts b/src/renderer/src/constants/common.constants.ts
index ead8e99..99e5df7 100644
--- a/src/renderer/src/constants/common.constants.ts
+++ b/src/renderer/src/constants/common.constants.ts
@@ -1,7 +1,7 @@
export const PRODUCTION_NAME = 'Oxygen Toolbox'
export const STORAGE_TOKEN_KEY = 'JWT_TOKEN'
export const STORAGE_USER_INFO_KEY = 'USER_INFO'
-export const STORAGE_FAVORITE_KEY = 'FAVORITE'
+export const STORAGE_TOOL_MENU_ITEM_KEY = 'TOOL_MENU_ITEM'
export const COLOR_ORIGIN = 'white'
export const COLOR_PRODUCTION = '#4E47BB'
export const COLOR_MAIN = COLOR_PRODUCTION
diff --git a/src/renderer/src/global.d.ts b/src/renderer/src/global.d.ts
index 233d760..fbed311 100644
--- a/src/renderer/src/global.d.ts
+++ b/src/renderer/src/global.d.ts
@@ -661,3 +661,11 @@ interface ToolFavoriteAddRemoveParam {
toolId: string
platform: Platform
}
+
+interface ToolMenuItem {
+ icon: string
+ toolName: string
+ toolId: string
+ authorUsername: string
+ ver: string
+}
diff --git a/src/renderer/src/pages/Tools/Store.tsx b/src/renderer/src/pages/Tools/Store.tsx
index f187751..eca029d 100644
--- a/src/renderer/src/pages/Tools/Store.tsx
+++ b/src/renderer/src/pages/Tools/Store.tsx
@@ -134,12 +134,7 @@ const Store = () => {
return (
- }
+ icon={firstTool!.icon}
toolName={firstTool!.name}
toolId={firstTool!.toolId}
toolDesc={firstTool!.description}
diff --git a/src/renderer/src/pages/Tools/User.tsx b/src/renderer/src/pages/Tools/User.tsx
index 2124196..4fb4cd1 100644
--- a/src/renderer/src/pages/Tools/User.tsx
+++ b/src/renderer/src/pages/Tools/User.tsx
@@ -197,12 +197,7 @@ const User = () => {
return (
- }
+ icon={firstTool!.icon}
toolName={firstTool!.name}
toolId={firstTool!.toolId}
toolDesc={firstTool!.description}
diff --git a/src/renderer/src/pages/Tools/View.tsx b/src/renderer/src/pages/Tools/View.tsx
index d61e981..ab10c0b 100644
--- a/src/renderer/src/pages/Tools/View.tsx
+++ b/src/renderer/src/pages/Tools/View.tsx
@@ -32,7 +32,10 @@ const View = () => {
.compile(files, importMap, toolVo.entryPoint)
.then((result) => {
const output = result.outputFiles[0].text
- setCompiledCode(`${output}\n${baseDist}`)
+ setCompiledCode('')
+ setTimeout(() => {
+ setCompiledCode(`${output}\n${baseDist}`)
+ })
})
.catch((reason) => {
void message.error(`编译失败:${reason}`)
@@ -72,9 +75,7 @@ const View = () => {
break
case DATABASE_NO_RECORD_FOUND:
void message.error('未找到指定工具')
- setTimeout(() => {
- navigateToRepository(navigate)
- }, 3000)
+ navigateToRepository(navigate)
break
default:
void message.error('获取工具信息失败,请稍后重试')
@@ -103,11 +104,11 @@ const View = () => {
return
}
if (username === '!' && !ver) {
- navigateToView(navigate, '!', toolId!, platform as Platform)
+ navigateToView(navigate, '!', toolId!, platform as Platform, 'latest')
return
}
getTool()
- }, [])
+ }, [username, toolId, ver, searchParams])
return (
diff --git a/src/renderer/src/pages/Tools/index.tsx b/src/renderer/src/pages/Tools/index.tsx
index 9269bbb..1a3d848 100644
--- a/src/renderer/src/pages/Tools/index.tsx
+++ b/src/renderer/src/pages/Tools/index.tsx
@@ -166,9 +166,10 @@ const ToolCard = ({ tools, onDelete, onUpgrade, onSubmit, onCancel }: ToolCardPr
return (
}
+ icon={selectedTool.icon}
toolName={selectedTool.name}
toolId={selectedTool.toolId}
+ ver={selectedTool.ver}
onOpen={handleOnOpenTool}
onEdit={handleOnEditTool()}
onSource={handleOnSourceTool()}
@@ -563,65 +564,71 @@ const Tools = () => {
))}
{hasNextPage && }
-
-
- 收藏
-
-
-
- {starToolData
- ?.reduce((previousValue: ToolVo[], currentValue) => {
- if (
- !previousValue.some(
- (value) =>
- value.author.id === currentValue.author.id &&
- value.toolId === currentValue.toolId
- )
- ) {
- previousValue.push(currentValue)
- }
- return previousValue
- }, [])
- .map((item) => {
- const tools = starToolData.filter(
- (value) =>
- value.author.id === item.author.id &&
- value.toolId === item.toolId
- )
- const webTool = tools.find((value) => value.platform === 'WEB')
- const desktopTool = tools.find(
- (value) => value.platform === 'DESKTOP'
- )
- const androidTool = tools.find(
- (value) => value.platform === 'ANDROID'
- )
- const firstTool =
- (checkDesktop()
- ? desktopTool || webTool
- : webTool || desktopTool) || androidTool
-
- return (
-
+ {starToolData.length ? (
+ <>
+
+
+ 收藏
+
+
+
+ {starToolData
+ ?.reduce((previousValue: ToolVo[], currentValue) => {
+ if (
+ !previousValue.some(
+ (value) =>
+ value.author.id ===
+ currentValue.author.id &&
+ value.toolId === currentValue.toolId
+ )
+ ) {
+ previousValue.push(currentValue)
}
- toolName={firstTool!.name}
- toolId={firstTool!.toolId}
- toolDesc={firstTool!.description}
- author={firstTool!.author}
- ver={firstTool!.ver}
- platform={firstTool!.platform}
- supportPlatform={tools.map((value) => value.platform)}
- favorite={firstTool!.favorite}
- />
- )
- })}
- {hasNextStarPage && }
-
+ return previousValue
+ }, [])
+ .map((item) => {
+ const tools = starToolData.filter(
+ (value) =>
+ value.author.id === item.author.id &&
+ value.toolId === item.toolId
+ )
+ const webTool = tools.find(
+ (value) => value.platform === 'WEB'
+ )
+ const desktopTool = tools.find(
+ (value) => value.platform === 'DESKTOP'
+ )
+ const androidTool = tools.find(
+ (value) => value.platform === 'ANDROID'
+ )
+ const firstTool =
+ (checkDesktop()
+ ? desktopTool || webTool
+ : webTool || desktopTool) || androidTool
+
+ return (
+ value.platform
+ )}
+ favorite={firstTool!.favorite}
+ />
+ )
+ })}
+ {hasNextStarPage && (
+
+ )}
+
+ >
+ ) : undefined}
diff --git a/src/renderer/src/pages/ToolsFramework.tsx b/src/renderer/src/pages/ToolsFramework.tsx
index 13bd59c..4b284cb 100644
--- a/src/renderer/src/pages/ToolsFramework.tsx
+++ b/src/renderer/src/pages/ToolsFramework.tsx
@@ -1,70 +1,184 @@
+import { DndContext, DragOverEvent, DragStartEvent } from '@dnd-kit/core'
+import Droppable from '@/components/dnd/Droppable'
+import type { DragEndEvent } from '@dnd-kit/core/dist/types'
import '@/assets/css/pages/tools-framework.scss'
import { tools } from '@/router/tools'
import FitFullscreen from '@/components/common/FitFullscreen'
import Sidebar from '@/components/common/Sidebar'
import FullscreenLoadingMask from '@/components/common/FullscreenLoadingMask'
+import { arrayMove, SortableContext } from '@dnd-kit/sortable'
+import { getViewPath } from '@/util/navigation.tsx'
+import Sortable from '@/components/dnd/Sortable.tsx'
+import DragHandle from '@/components/dnd/DragHandle.tsx'
+import DraggableOverlay from '@/components/dnd/DraggableOverlay.tsx'
+import { getToolMenuItem, saveToolMenuItem } from '@/util/common.tsx'
const ToolsFramework = () => {
+ const [isDelete, setIsDelete] = useState(false)
+ const [toolMenuItem, setToolMenuItem] = useState
(getToolMenuItem)
+ const [activeItem, setActiveItem] = useState(null)
+
+ const handleOnDragStart = ({ active }: DragStartEvent) => {
+ setActiveItem(active.data.current as ToolMenuItem)
+ }
+
+ const handleOnDragOver = ({ over }: DragOverEvent) => {
+ setIsDelete(over === null)
+ }
+
+ const handleOnDragEnd = ({ active, over }: DragEndEvent) => {
+ if (over && active.id !== over?.id) {
+ const activeIndex = toolMenuItem.findIndex(
+ ({ authorUsername, toolId, ver }) =>
+ `${authorUsername}:${toolId}:${ver}` === active.id
+ )
+ const overIndex = toolMenuItem.findIndex(
+ ({ authorUsername, toolId, ver }) =>
+ `${authorUsername}:${toolId}:${ver}` === over.id
+ )
+ setToolMenuItem(arrayMove(toolMenuItem, activeIndex, overIndex))
+ }
+
+ if (!over) {
+ setToolMenuItem(
+ toolMenuItem.filter(
+ ({ authorUsername, toolId, ver }) =>
+ `${authorUsername}:${toolId}:${ver}` !== active?.id
+ )
+ )
+ }
+
+ if (!active.data.current?.sortable && over) {
+ const newItem = active.data.current as ToolMenuItem
+ if (
+ toolMenuItem.findIndex(
+ ({ authorUsername, toolId, ver }) =>
+ authorUsername === newItem.authorUsername &&
+ toolId === newItem.toolId &&
+ ver === newItem.ver
+ ) === -1
+ ) {
+ setToolMenuItem([...toolMenuItem, newItem])
+ }
+ }
+
+ console.log('active', active)
+ console.log('over', over)
+
+ setActiveItem(null)
+ }
+
+ const handleOnDragCancel = () => {
+ setActiveItem(null)
+ }
+
+ useEffect(() => {
+ saveToolMenuItem(toolMenuItem)
+ }, [toolMenuItem])
+
return (
<>
-
-
-
-
-
-
-
-
+
+
+
- {tools.map((tool) => {
- return tool.menu &&
- tool.id !== 'tools-store' &&
- tool.id !== 'tools-repository' ? (
-
- {tool.children &&
- tool.children.map((subTool) => {
- return (
-
- )
- })}
-
- ) : undefined
- })}
+
+
-
-
-
-
-
-
- >
- }
- >
-
-
-
+
+
+
+
+
+
+
+ >
+ }
+ >
+
+
+
+
>
)
diff --git a/src/renderer/src/util/common.tsx b/src/renderer/src/util/common.tsx
index f32740e..f6c6e9c 100644
--- a/src/renderer/src/util/common.tsx
+++ b/src/renderer/src/util/common.tsx
@@ -1,6 +1,8 @@
import { createRoot } from 'react-dom/client'
import FullscreenLoadingMask from '@/components/common/FullscreenLoadingMask'
import { floor } from 'lodash'
+import { getLocalStorage, setLocalStorage } from '@/util/browser.tsx'
+import { STORAGE_TOOL_MENU_ITEM_KEY } from '@/constants/common.constants.ts'
export const randomInt = (start: number, end: number) => {
if (start > end) {
@@ -133,3 +135,15 @@ const formatByte = (size: number, unit: ByteUnit): string => {
}
export const checkDesktop = () => import.meta.env.VITE_PLATFORM === 'DESKTOP'
+
+export const saveToolMenuItem = (toolMenuItem: ToolMenuItem[]) => {
+ setLocalStorage(STORAGE_TOOL_MENU_ITEM_KEY, JSON.stringify(toolMenuItem))
+}
+
+export const getToolMenuItem = (): ToolMenuItem[] => {
+ const s = getLocalStorage(STORAGE_TOOL_MENU_ITEM_KEY)
+ if (!s) {
+ return []
+ }
+ return JSON.parse(s) as ToolMenuItem[]
+}