Skip to content

Commit

Permalink
Merge pull request #349 from hmydgz/dev
Browse files Browse the repository at this point in the history
✨ feat: 新增作业集编辑的作业排序功能
  • Loading branch information
guansss authored Dec 23, 2024
2 parents 0492be0 + 32868ee commit 01c9bfd
Show file tree
Hide file tree
Showing 6 changed files with 129 additions and 89 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
"linkify-react": "^3.0.4",
"linkifyjs": "^3.0.5",
"lodash-es": "^4.17.21",
"maa-copilot-client": "https://github.com/MaaAssistantArknights/maa-copilot-client-ts.git#0.1.0-SNAPSHOT.758.dbc2eb1",
"maa-copilot-client": "https://github.com/MaaAssistantArknights/maa-copilot-client-ts.git#0.1.0-SNAPSHOT.784.58f96e5",
"normalize.css": "^8.0.1",
"prettier": "^3.2.5",
"react": "^18.0.0",
Expand Down
8 changes: 2 additions & 6 deletions src/apis/operation-set.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
CopilotSetPageRes,
CopilotSetQuery,
CopilotSetStatus,
CopilotSetUpdateReq,
} from 'maa-copilot-client'
import useSWR from 'swr'
import useSWRInfinite from 'swr/infinite'
Expand Down Expand Up @@ -184,12 +185,7 @@ export async function createOperationSet(req: {
})
}

export async function updateOperationSet(req: {
id: number
name: string
description: string
status: CopilotSetStatus
}) {
export async function updateOperationSet(req: CopilotSetUpdateReq) {
await new OperationSetApi().updateCopilotSet({ copilotSetUpdateReq: req })
}

Expand Down
9 changes: 8 additions & 1 deletion src/apis/operation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,14 @@ export function useOperations({

const isReachingEnd = !!pages?.some((page) => !page.hasNext)

const operations = pages?.map((page) => page.data).flat()
const _operations = pages?.map((page) => page.data).flat() ?? []

// 按 operationIds 的顺序排序
const operations = operationIds?.length
? operationIds
?.map((id) => _operations?.find((v) => v.id === id))
.filter((v) => !!v)
: _operations

return {
error,
Expand Down
4 changes: 2 additions & 2 deletions src/components/operation-set/AddToOperationSet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ export function AddToOperationSet({

return (
<>
<div className="py-2">
<div className="py-2 px-px">
{error && (
<Callout intent="danger" icon="error" title="错误">
{formatError(error)}
Expand All @@ -165,7 +165,7 @@ export function AddToOperationSet({
<div key={id}>
<Checkbox
className={clsx(
'block m-0 p-2 !pl-10 hover:bg-slate-200',
'block m-0 p-2 !pl-10 hover:bg-slate-200 dark:hover:bg-slate-800',
checkboxOverrides[id] !== undefined &&
checkboxOverrides[id] !== alreadyAdded(id) &&
'font-bold',
Expand Down
189 changes: 113 additions & 76 deletions src/components/operation-set/OperationSetEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,42 @@ import {
NonIdealState,
TextArea,
} from '@blueprintjs/core'
import {
DndContext,
DragEndEvent,
PointerSensor,
useSensor,
useSensors,
} from '@dnd-kit/core'
import {
SortableContext,
arrayMove,
verticalListSortingStrategy,
} from '@dnd-kit/sortable'

import { useOperations } from 'apis/operation'
import {
addToOperationSet,
createOperationSet,
removeFromOperationSet,
updateOperationSet,
useRefreshOperationSet,
useRefreshOperationSets,
} from 'apis/operation-set'
import clsx from 'clsx'
import { Ref, useCallback, useImperativeHandle, useRef, useState } from 'react'
import { UpdateCopilotSetRequest } from 'maa-copilot-client'
import {
Ref,
useCallback,
useEffect,
useImperativeHandle,
useRef,
useState,
} from 'react'
import { Controller, UseFormSetError, useForm } from 'react-hook-form'

import { FormField } from 'components/FormField'
import { AppToaster } from 'components/Toaster'
import { Sortable } from 'components/dnd'
import { Operation } from 'models/operation'
import { OperationSet } from 'models/operation-set'
import { formatError } from 'utils/error'

Expand Down Expand Up @@ -65,17 +85,19 @@ export function OperationSetEditorDialog({
name,
description,
status,
idsToAdd,
idsToRemove,
copilotIds,
}) => {
const updateInfo = async () => {
if (isEdit) {
await updateOperationSet({
const params: UpdateCopilotSetRequest['copilotSetUpdateReq'] = {
id: operationSet!.id,
name,
description,
status,
})
copilotIds,
}

await updateOperationSet(params)

AppToaster.show({
intent: 'success',
Expand All @@ -102,35 +124,7 @@ export function OperationSetEditorDialog({
}
}

const addOperations = async () => {
if (operationSet && idsToAdd?.length) {
await addToOperationSet({
operationSetId: operationSet.id,
operationIds: idsToAdd,
})

AppToaster.show({
intent: 'success',
message: `添加作业成功`,
})
}
}

const removeOperations = async () => {
if (operationSet && idsToRemove?.length) {
await removeFromOperationSet({
operationSetId: operationSet.id,
operationIds: idsToRemove,
})

AppToaster.show({
intent: 'success',
message: `移除作业成功`,
})
}
}

await Promise.all([updateInfo(), addOperations(), removeOperations()])
await updateInfo()

onClose()
}
Expand Down Expand Up @@ -168,6 +162,7 @@ interface FormValues {

idsToAdd?: number[]
idsToRemove?: number[]
copilotIds?: number[]
}

function OperationSetForm({ operationSet, onSubmit }: FormProps) {
Expand Down Expand Up @@ -207,7 +202,7 @@ function OperationSetForm({ operationSet, onSubmit }: FormProps) {
return (
<form
className={clsx(
'p-4 w-[500px] max-w-[100vw] max-h-[calc(100vh-20rem)] overflow-y-auto',
'p-4 w-[500px] max-w-[100vw] max-h-[calc(100vh-20rem)] min-h-[18rem] overflow-y-auto',
isEdit && 'lg:w-[1000px]',
)}
onSubmit={localOnSubmit}
Expand All @@ -219,6 +214,7 @@ function OperationSetForm({ operationSet, onSubmit }: FormProps) {
<>
<div className="grow">
<OperationSelector
key={operationSet.id}
operationSet={operationSet}
selectorRef={operationSelectorRef}
/>
Expand Down Expand Up @@ -330,7 +326,9 @@ interface OperationSelectorProps {
}

interface OperationSelectorRef {
getValues(): { idsToAdd: number[]; idsToRemove: number[] }
getValues(): {
copilotIds?: number[]
}
}

function OperationSelector({
Expand All @@ -341,6 +339,12 @@ function OperationSelector({
operationIds: operationSet.copilotIds,
})

const [renderedOperations, setRenderedOperations] = useState<Operation[]>([])
useEffect(() => {
setRenderedOperations([...(operations ?? [])])
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [operations.length])

const [checkboxOverrides, setCheckboxOverrides] = useState(
{} as Record<number, boolean>,
)
Expand All @@ -354,26 +358,34 @@ function OperationSelector({
selectorRef,
() => ({
getValues() {
const idsToAdd: number[] = []
const idsToRemove: number[] = []

Object.entries(checkboxOverrides).forEach(([idKey, checked]) => {
const id = +idKey
if (isNaN(id)) return

if (checked && !alreadyAdded(id)) {
idsToAdd.push(id)
} else if (!checked && alreadyAdded(id)) {
idsToRemove.push(id)
}
})

return { idsToAdd, idsToRemove }
const copilotIds: number[] = []
copilotIds.push(
...renderedOperations
.map(({ id }) => (checkboxOverrides[id] === false ? 0 : id))
.filter((id) => !!id),
)

return { copilotIds }
},
}),
[checkboxOverrides, alreadyAdded],
[checkboxOverrides, renderedOperations],
)

const sensors = useSensors(useSensor(PointerSensor))

const handleDragEnd = (event: DragEndEvent) => {
const { active, over } = event

if (active.id !== over?.id) {
setRenderedOperations((items) => {
const oldIndex = items.findIndex((v) => v.id === active.id)
const newIndex = items.findIndex((v) => v.id === over?.id)

return arrayMove(items, oldIndex, newIndex)
})
}
}

return (
<div className="py-2">
{error && (
Expand All @@ -382,28 +394,53 @@ function OperationSelector({
</Callout>
)}

{operations?.map(({ id, parsedContent }) => (
<div key={id}>
<Checkbox
className={clsx(
'flex items-center m-0 p-2 !pl-10 hover:bg-slate-200',
checkboxOverrides[id] !== undefined &&
checkboxOverrides[id] !== alreadyAdded(id) &&
'font-bold',
)}
checked={checkboxOverrides[id] ?? alreadyAdded(id)}
onChange={(e) => {
const checked = (e.target as HTMLInputElement).checked
setCheckboxOverrides((prev) => ({ ...prev, [id]: checked }))
}}
>
<div className="tabular-nums text-slate-500">{id}:&nbsp;</div>
<div className="truncate text-ellipsis">
{parsedContent.doc.title}
</div>
</Checkbox>
</div>
))}
<DndContext sensors={sensors} onDragEnd={handleDragEnd}>
<SortableContext
items={(renderedOperations ?? []).map(({ id }) => id)}
strategy={verticalListSortingStrategy}
>
{renderedOperations?.map(({ id, parsedContent }) => (
<Sortable key={id} id={id}>
{({ listeners, attributes }) => (
<div
key={id}
className="flex items-center hover:bg-slate-200 dark:hover:bg-slate-800"
>
<Icon
className="cursor-grab active:cursor-grabbing p-1 -my-1 -ml-2 -mr-1 rounded-[1px]"
icon="drag-handle-vertical"
{...listeners}
{...attributes}
/>
<Checkbox
className={clsx(
'flex items-center m-0 p-2 !pl-10 flex-1',
checkboxOverrides[id] !== undefined &&
checkboxOverrides[id] !== alreadyAdded(id) &&
'font-bold',
)}
checked={checkboxOverrides[id] ?? alreadyAdded(id)}
onChange={(e) => {
const checked = (e.target as HTMLInputElement).checked
setCheckboxOverrides((prev) => ({
...prev,
[id]: checked,
}))
}}
>
<div className="tabular-nums text-slate-500">
{id}:&nbsp;
</div>
<div className="truncate text-ellipsis">
{parsedContent.doc.title}
</div>
</Checkbox>
</div>
)}
</Sortable>
))}
</SortableContext>
</DndContext>
</div>
)
}
6 changes: 3 additions & 3 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3642,9 +3642,9 @@ lru-cache@^6.0.0:
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.2.0.tgz#0bd445ca57363465900f4d1f9bd8db343a4d95c3"
integrity sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==

"maa-copilot-client@https://github.com/MaaAssistantArknights/maa-copilot-client-ts.git#0.1.0-SNAPSHOT.758.dbc2eb1":
version "0.1.0-SNAPSHOT.758.dbc2eb1"
resolved "https://github.com/MaaAssistantArknights/maa-copilot-client-ts.git#d6750070b90fec2cac02d3f3d718a3165fd5d55a"
"maa-copilot-client@https://github.com/MaaAssistantArknights/maa-copilot-client-ts.git#0.1.0-SNAPSHOT.784.58f96e5":
version "0.1.0-SNAPSHOT.784.58f96e5"
resolved "https://github.com/MaaAssistantArknights/maa-copilot-client-ts.git#cecb29af32fa36d6f11ed46469765abe56caafaf"

make-dir@^2.1.0:
version "2.1.0"
Expand Down

0 comments on commit 01c9bfd

Please sign in to comment.