1
1
import {
2
2
Button ,
3
3
ButtonGroup ,
4
+ Callout ,
4
5
Card ,
5
6
Divider ,
6
7
H6 ,
@@ -21,10 +22,12 @@ import { OperationList } from 'components/OperationList'
21
22
import { OperationSetList } from 'components/OperationSetList'
22
23
import { neoLayoutAtom } from 'store/pref'
23
24
25
+ import { Operation } from '../models/operation'
24
26
import { LevelSelect } from './LevelSelect'
25
27
import { OperatorFilter , useOperatorFilter } from './OperatorFilter'
26
28
import { withSuspensable } from './Suspensable'
27
29
import { UserFilter } from './UserFilter'
30
+ import { AddToOperationSetButton } from './operation-set/AddToOperationSet'
28
31
29
32
export const Operations : ComponentType = withSuspensable ( ( ) => {
30
33
const [ queryParams , setQueryParams ] = useState <
@@ -42,6 +45,8 @@ export const Operations: ComponentType = withSuspensable(() => {
42
45
const [ selectedUser , setSelectedUser ] = useState < MaaUserInfo > ( )
43
46
const [ neoLayout , setNeoLayout ] = useAtom ( neoLayoutAtom )
44
47
const [ tab , setTab ] = useState < 'operation' | 'operationSet' > ( 'operation' )
48
+ const [ multiselect , setMultiselect ] = useState ( false )
49
+ const [ selectedOperations , setSelectedOperations ] = useState < Operation [ ] > ( [ ] )
45
50
46
51
return (
47
52
< >
@@ -74,7 +79,15 @@ export const Operations: ComponentType = withSuspensable(() => {
74
79
title = "作业集"
75
80
/>
76
81
</ Tabs >
77
- < ButtonGroup className = "ml-auto" >
82
+ < Button
83
+ minimal
84
+ icon = "multi-select"
85
+ title = "启动多选"
86
+ className = "ml-auto mr-2"
87
+ active = { multiselect }
88
+ onClick = { ( ) => setMultiselect ( ( v ) => ! v ) }
89
+ />
90
+ < ButtonGroup >
78
91
< Button
79
92
icon = "grid-view"
80
93
active = { neoLayout }
@@ -178,6 +191,43 @@ export const Operations: ComponentType = withSuspensable(() => {
178
191
</ ButtonGroup >
179
192
</ div >
180
193
</ div >
194
+ { multiselect && (
195
+ < Callout className = "mt-2 p-0 select-none" >
196
+ < details >
197
+ < summary className = "px-2 py-4 cursor-pointer hover:bg-zinc-500 hover:bg-opacity-5" >
198
+ 已选择 { selectedOperations . length } 份作业
199
+ </ summary >
200
+ < div className = "p-2 flex flex-wrap gap-1" >
201
+ { selectedOperations . map ( ( operation ) => (
202
+ < Button
203
+ key = { operation . id }
204
+ small
205
+ minimal
206
+ rightIcon = "cross"
207
+ onClick = { ( ) =>
208
+ setSelectedOperations ( ( old ) =>
209
+ old . filter ( ( op ) => op . id !== operation . id ) ,
210
+ )
211
+ }
212
+ >
213
+ { operation . parsedContent . doc . title }
214
+ </ Button >
215
+ ) ) }
216
+ </ div >
217
+ </ details >
218
+ < AddToOperationSetButton
219
+ minimal
220
+ outlined
221
+ intent = "primary"
222
+ icon = "add-to-folder"
223
+ className = "absolute top-2 right-2"
224
+ disabled = { selectedOperations . length === 0 }
225
+ operationIds = { selectedOperations . map ( ( op ) => op . id ) }
226
+ >
227
+ 添加到作业集
228
+ </ AddToOperationSetButton >
229
+ </ Callout >
230
+ ) }
181
231
</ >
182
232
) }
183
233
@@ -218,6 +268,17 @@ export const Operations: ComponentType = withSuspensable(() => {
218
268
{ tab === 'operation' && (
219
269
< OperationList
220
270
{ ...queryParams }
271
+ multiselect = { multiselect }
272
+ selectedOperations = { selectedOperations }
273
+ onSelect = { ( operation , selected ) =>
274
+ setSelectedOperations ( ( old ) => {
275
+ const newList = old . filter ( ( op ) => op . id !== operation . id )
276
+ if ( selected ) {
277
+ newList . push ( operation )
278
+ }
279
+ return newList
280
+ } )
281
+ }
221
282
operator = { operatorFilter . enabled ? operatorFilter : undefined }
222
283
// 按热度排序时列表前几页的变化不会太频繁,可以不刷新第一页,节省点流量
223
284
revalidateFirstPage = { queryParams . orderBy !== 'hot' }
0 commit comments