Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
N
Neos
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Locked Files
Issues
0
Issues
0
List
Boards
Labels
Service Desk
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Security & Compliance
Security & Compliance
Dependency List
License Compliance
Packages
Packages
List
Container Registry
Analytics
Analytics
CI / CD
Code Review
Insights
Issues
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
love_飞影
Neos
Commits
e2e6ef39
Commit
e2e6ef39
authored
Aug 10, 2023
by
timel
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: validate and sort in build card
parent
80ddb009
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
141 additions
and
28 deletions
+141
-28
src/common.ts
src/common.ts
+23
-0
src/ui/BuildDeck/index.module.scss
src/ui/BuildDeck/index.module.scss
+1
-1
src/ui/BuildDeck/index.tsx
src/ui/BuildDeck/index.tsx
+68
-27
src/ui/BuildDeck/utils.ts
src/ui/BuildDeck/utils.ts
+49
-0
No files found.
src/common.ts
View file @
e2e6ef39
...
...
@@ -95,6 +95,29 @@ export function extraCardTypes(typeCode: number): number[] {
].
filter
((
target
)
=>
(
target
&
typeCode
)
>
0
);
}
/** 这张卡能不能放入额外卡组 */
export
function
isExtraDeckCard
(
typeCode
:
number
):
boolean
{
const
extraTypes
=
[
TYPE_PENDULUM
,
TYPE_LINK
,
TYPE_SYNCHRO
,
TYPE_XYZ
,
TYPE_FUSION
,
];
return
extraTypes
.
reduce
((
acc
,
cur
)
=>
(
acc
|
cur
)
&
typeCode
,
0
)
>
0
;
}
/** 这张卡是怪兽、魔法、陷阱 */
export
function
tellCardBasicType
(
typeCode
:
number
):
number
{
const
basicTypes
=
[
TYPE_MONSTER
,
TYPE_SPELL
,
TYPE_TRAP
];
return
basicTypes
.
reduce
((
acc
,
cur
)
=>
(
acc
|
cur
)
&
typeCode
,
0
);
}
/** 是不是衍生物 */
export
function
isToken
(
typeCode
:
number
):
boolean
{
return
(
typeCode
&
TYPE_TOKEN
)
>
0
;
}
// 属性
// const ATTRIBUTE_ALL = 0x7f; //
const
ATTRIBUTE_EARTH
=
0x01
;
//
...
...
src/ui/BuildDeck/index.module.scss
View file @
e2e6ef39
...
...
@@ -95,7 +95,7 @@
position
:
relative
;
background-size
:
contain
;
.cardname
{
font-size
:
0
.9rem
;
font-size
:
12px
;
position
:
absolute
;
padding
:
5px
;
top
:
0
;
...
...
src/ui/BuildDeck/index.tsx
View file @
e2e6ef39
...
...
@@ -37,10 +37,14 @@ import { CardDetail } from "./CardDetail";
import
{
DeckSelect
}
from
"
./DeckSelect
"
;
import
styles
from
"
./index.module.scss
"
;
import
{
canAdd
,
compareCards
,
type
EditingDeck
,
editingDeckToIDeck
,
iDeckToEditingDeck
,
type
Type
,
}
from
"
./utils
"
;
import
{
isToken
}
from
"
@/common
"
;
const
theme
:
ThemeConfig
=
{
components
:
{
...
...
@@ -87,7 +91,17 @@ export const Component: React.FC = () => {
</
div
>
<
div
className=
{
styles
.
content
}
>
<
div
className=
{
styles
.
deck
}
>
<
DeckEditor
deck=
{
selectedDeck
}
onSave=
{
()
=>
{}
}
/>
<
DeckEditor
deck=
{
selectedDeck
}
onReset=
{
async
()
=>
{
editDeckStore
.
set
(
await
iDeckToEditingDeck
(
selectedDeck
));
}
}
onSave=
{
async
()
=>
{
const
tmpIDeck
=
editingDeckToIDeck
(
editDeckStore
);
await
deckStore
.
update
(
selectedDeck
.
deckName
,
tmpIDeck
);
setSelectedDeck
(
tmpIDeck
);
}
}
/>
</
div
>
<
div
className=
{
styles
.
select
}
>
{
sqlite
.
progress
===
1
?
(
...
...
@@ -109,8 +123,9 @@ Component.displayName = "Build";
/** 正在编辑的卡组 */
const
DeckEditor
:
React
.
FC
<
{
deck
:
IDeck
;
onSave
:
(
deck
:
IDeck
)
=>
void
;
}
>
=
({
deck
,
onSave
})
=>
{
onReset
:
()
=>
void
;
onSave
:
()
=>
void
;
}
>
=
({
deck
,
onReset
,
onSave
})
=>
{
const
snapEditDeck
=
useSnapshot
(
editDeckStore
);
useEffect
(()
=>
{
iDeckToEditingDeck
(
deck
).
then
(
editDeckStore
.
set
);
...
...
@@ -132,17 +147,27 @@ const DeckEditor: React.FC<{
value=
{
snapEditDeck
.
deckName
}
/>
<
Space
style=
{
{
marginRight
:
6
}
}
>
<
Button
type=
"text"
size=
"small"
icon=
{
<
DeleteOutlined
/>
}
>
<
Button
type=
"text"
size=
"small"
icon=
{
<
DeleteOutlined
/>
}
onClick=
{
editDeckStore
.
clear
}
>
清空
</
Button
>
<
Button
type=
"text"
size=
"small"
icon=
{
<
UndoOutlined
/>
}
>
<
Button
type=
"text"
size=
"small"
icon=
{
<
UndoOutlined
/>
}
onClick=
{
()
=>
onReset
()
}
>
重置
</
Button
>
<
Button
type=
"text"
size=
"small"
icon=
{
<
CheckOutlined
/>
}
onClick=
{
()
=>
onSave
(
editingDeckToIDeck
(
editDeckStore
)
)
}
onClick=
{
()
=>
onSave
()
}
>
保存
</
Button
>
...
...
@@ -162,7 +187,9 @@ const CardSelect: React.FC = () => {
const
[
searchWord
,
setSearchWord
]
=
useState
(
""
);
const
[
searchResult
,
setSearchResult
]
=
useState
<
CardMeta
[]
>
([]);
const
handleSearch
=
async
()
=>
{
const
result
=
await
searchCards
(
searchWord
);
const
result
=
(
await
searchCards
(
searchWord
)).
filter
((
card
)
=>
isToken
(
card
.
data
.
type
??
0
)
);
// 衍生物不显示
setSearchResult
(
result
);
};
return
(
...
...
@@ -218,24 +245,20 @@ const CardSelect: React.FC = () => {
/** 正在组卡的zone,包括main/extra/side */
const
DeckZone
:
React
.
FC
<
{
type
:
"
main
"
|
"
extra
"
|
"
side
"
;
type
:
Type
;
}
>
=
({
type
})
=>
{
const
cards
=
useSnapshot
(
editDeckStore
)[
type
];
const
[
_
,
dropRef
]
=
useDrop
({
accept
:
[
"
Card
"
],
// 指明该区域允许接收的拖放物。可以是单个,也可以是数组
// 里面的值就是useDrag所定义的type
// 当拖拽物在这个拖放区域放下时触发,这个item就是拖拽物的item(拖拽物携带的数据)
drop
:
({
value
,
source
,
}:
{
value
:
CardMeta
;
source
:
"
main
"
|
"
extra
"
|
"
side
"
|
"
search
"
;
})
=>
{
drop
:
({
value
,
source
}:
{
value
:
CardMeta
;
source
:
Type
|
"
search
"
})
=>
{
if
(
type
===
source
)
return
;
editDeckStore
.
add
(
type
,
value
);
if
(
source
!==
"
search
"
)
{
editDeckStore
.
remove
(
source
,
value
);
if
(
canAdd
(
value
,
type
,
editDeckStore
))
{
editDeckStore
.
add
(
type
,
value
);
if
(
source
!==
"
search
"
)
{
editDeckStore
.
remove
(
source
,
value
);
}
}
},
});
...
...
@@ -243,7 +266,12 @@ const DeckZone: React.FC<{
<
div
className=
{
styles
[
type
]
}
ref=
{
dropRef
}
>
<
div
className=
{
styles
[
"
card-continer
"
]
}
>
{
cards
.
map
((
item
,
i
)
=>
(
<
Card
value=
{
item
}
key=
{
i
}
source=
{
type
}
/>
<
Card
value=
{
item
}
key=
{
i
}
source=
{
type
}
onRightClick=
{
()
=>
editDeckStore
.
remove
(
type
,
item
)
}
/>
))
}
</
div
>
</
div
>
...
...
@@ -282,8 +310,10 @@ const SearchResults: React.FC<{
/** 本组件内使用的单张卡片,增加了文字在图片下方 */
const
Card
:
React
.
FC
<
{
value
:
CardMeta
;
source
:
"
main
"
|
"
extra
"
|
"
side
"
|
"
search
"
;
}
>
=
memo
(({
value
,
source
})
=>
{
source
:
Type
|
"
search
"
;
onClick
?:
()
=>
void
;
onRightClick
?:
()
=>
void
;
}
>
=
memo
(({
value
,
source
,
onClick
,
onRightClick
})
=>
{
const
ref
=
useRef
<
HTMLDivElement
>
(
null
);
const
[{
isDragging
},
drag
]
=
useDrag
({
type
:
"
Card
"
,
...
...
@@ -297,7 +327,12 @@ const Card: React.FC<{
<
div
className=
{
styles
.
card
}
ref=
{
ref
}
style=
{
{
opacity
:
isDragging
?
0
:
1
}
}
style=
{
{
opacity
:
isDragging
&&
source
!==
"
search
"
?
0
:
1
}
}
onClick=
{
onClick
}
onContextMenu=
{
(
e
)
=>
{
e
.
preventDefault
();
onRightClick
?.();
}
}
>
<
div
className=
{
styles
.
cardname
}
>
{
value
.
text
.
name
}
</
div
>
<
YgoCard
className=
{
styles
.
cardcover
}
code=
{
value
.
id
}
/>
...
...
@@ -310,10 +345,11 @@ const editDeckStore = proxy({
main
:
[]
as
CardMeta
[],
extra
:
[]
as
CardMeta
[],
side
:
[]
as
CardMeta
[],
add
(
type
:
"
main
"
|
"
extra
"
|
"
side
"
,
card
:
CardMeta
)
{
add
(
type
:
Type
,
card
:
CardMeta
)
{
editDeckStore
[
type
].
push
(
card
);
editDeckStore
[
type
].
sort
(
compareCards
);
},
remove
(
type
:
"
main
"
|
"
extra
"
|
"
side
"
,
card
:
CardMeta
)
{
remove
(
type
:
Type
,
card
:
CardMeta
)
{
const
index
=
editDeckStore
[
type
].
findIndex
((
item
)
=>
item
.
id
===
card
.
id
);
if
(
index
!==
-
1
)
{
editDeckStore
[
type
].
splice
(
index
,
1
);
...
...
@@ -321,8 +357,13 @@ const editDeckStore = proxy({
},
set
(
deck
:
EditingDeck
)
{
editDeckStore
.
deckName
=
deck
.
deckName
;
editDeckStore
.
main
=
deck
.
main
;
editDeckStore
.
extra
=
deck
.
extra
;
editDeckStore
.
side
=
deck
.
side
;
editDeckStore
.
main
=
deck
.
main
.
sort
(
compareCards
);
editDeckStore
.
extra
=
deck
.
extra
.
sort
(
compareCards
);
editDeckStore
.
side
=
deck
.
side
.
sort
(
compareCards
);
},
clear
()
{
editDeckStore
.
main
=
[];
editDeckStore
.
extra
=
[];
editDeckStore
.
side
=
[];
},
})
satisfies
EditingDeck
;
src/ui/BuildDeck/utils.ts
View file @
e2e6ef39
import
{
type
CardMeta
,
fetchCard
}
from
"
@/api
"
;
import
{
isExtraDeckCard
,
isToken
,
tellCardBasicType
}
from
"
@/common
"
;
import
{
type
IDeck
}
from
"
@/stores
"
;
export
type
Type
=
"
main
"
|
"
extra
"
|
"
side
"
;
/** 用在卡组编辑 */
export
interface
EditingDeck
{
deckName
:
string
;
...
...
@@ -24,3 +27,49 @@ export const editingDeckToIDeck = (deck: EditingDeck): IDeck => ({
extra
:
deck
.
extra
.
map
((
card
)
=>
card
.
id
),
side
:
deck
.
side
.
map
((
card
)
=>
card
.
id
),
});
/** 能不能添加到正在编辑的卡组的区域 */
export
const
canAdd
=
(
card
:
CardMeta
,
type
:
Type
,
editDeckStore
:
EditingDeck
):
{
result
:
boolean
;
reason
?:
string
}
=>
{
let
result
=
true
,
reason
;
const
initialCards
=
editDeckStore
[
type
];
// 如果是衍生物,则不能添加
if
(
isToken
(
card
.
data
.
type
??
0
))
{
result
=
false
;
reason
=
"
不能添加衍生物
"
;
}
// 超出数量,则不能添加
const
countLimit
=
type
===
"
main
"
?
60
:
15
;
if
(
initialCards
.
length
>=
countLimit
)
{
result
=
false
;
reason
=
`超过
${
countLimit
}
张的上限`
;
}
// 接着需要检查卡的种类
if
(
(
type
===
"
extra
"
&&
!
isExtraDeckCard
(
card
.
data
.
type
??
0
))
||
(
type
===
"
main
"
&&
isExtraDeckCard
(
card
.
data
.
type
??
0
))
)
{
result
=
false
;
reason
=
"
卡片种类不符合
"
;
}
// 同名卡不超过三张
const
maxSameCard
=
3
;
// TODO: 禁卡表
const
sameCardCount
=
initialCards
.
filter
((
c
)
=>
c
.
id
===
card
.
id
).
length
;
if
(
sameCardCount
>=
maxSameCard
)
{
result
=
false
;
reason
=
`超过同名卡
${
maxSameCard
}
张的上限`
;
}
return
{
result
,
reason
};
};
/** 卡组内部排序,给array.sort用 */
export
const
compareCards
=
(
a
:
CardMeta
,
b
:
CardMeta
):
number
=>
{
const
aType
=
tellCardBasicType
(
a
.
data
.
type
??
0
);
const
bType
=
tellCardBasicType
(
b
.
data
.
type
??
0
);
if
(
aType
!==
bType
)
return
aType
-
bType
;
return
a
.
id
-
b
.
id
;
};
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment