Commit 760de93a authored by timel's avatar timel

feat: support change order

parent 07bd9ee3
Pipeline #23032 failed with stages
in 13 minutes and 44 seconds
import { InfoCircleFilled } from "@ant-design/icons";
import { import {
Button, Button,
Checkbox,
Form,
Input,
InputNumber, InputNumber,
type InputNumberProps,
Select, Select,
type SelectProps, type SelectProps,
type InputNumberProps,
Tooltip, Tooltip,
} from "antd"; } from "antd";
import {
CheckOutlined,
DeleteOutlined,
EditOutlined,
FilterOutlined,
SearchOutlined,
SortAscendingOutlined,
UndoOutlined,
InfoCircleFilled,
} from "@ant-design/icons";
import { useState } from "react"; import { useState } from "react";
import { fetchStrings, Region } from "@/api"; import { fetchStrings, Region } from "@/api";
...@@ -58,7 +46,7 @@ export const Filter: React.FC<{ ...@@ -58,7 +46,7 @@ export const Filter: React.FC<{
mode="multiple" mode="multiple"
allowClear allowClear
style={{ width: "100%" }} style={{ width: "100%" }}
placeholder="Please select" placeholder="请选择"
options={Array.from(Attribute2StringCodeMap.entries()).map( options={Array.from(Attribute2StringCodeMap.entries()).map(
([key, value]) => ({ ([key, value]) => ({
value: key, value: key,
...@@ -81,7 +69,7 @@ export const Filter: React.FC<{ ...@@ -81,7 +69,7 @@ export const Filter: React.FC<{
mode="multiple" mode="multiple"
allowClear allowClear
style={{ width: "100%" }} style={{ width: "100%" }}
placeholder="Please select" placeholder="请选择"
options={levels.map((level) => ({ options={levels.map((level) => ({
value: level, value: level,
label: level.toString(), label: level.toString(),
...@@ -102,7 +90,7 @@ export const Filter: React.FC<{ ...@@ -102,7 +90,7 @@ export const Filter: React.FC<{
mode="multiple" mode="multiple"
allowClear allowClear
style={{ width: "100%" }} style={{ width: "100%" }}
placeholder="Please select" placeholder="请选择"
options={Array.from(Race2StringCodeMap.entries()).map( options={Array.from(Race2StringCodeMap.entries()).map(
([key, value]) => ({ ([key, value]) => ({
value: key, value: key,
...@@ -125,7 +113,7 @@ export const Filter: React.FC<{ ...@@ -125,7 +113,7 @@ export const Filter: React.FC<{
mode="multiple" mode="multiple"
allowClear allowClear
style={{ width: "100%" }} style={{ width: "100%" }}
placeholder="Please select" placeholder="请选择"
options={Array.from(Type2StringCodeMap.entries()).map( options={Array.from(Type2StringCodeMap.entries()).map(
([key, value]) => ({ ([key, value]) => ({
value: key, value: key,
......
import { proxy } from "valtio";
import { type CardMeta } from "@/api";
import { compareCards, type EditingDeck, type Type } from "./utils";
export const editDeckStore = proxy({
deckName: "",
main: [] as CardMeta[],
extra: [] as CardMeta[],
side: [] as CardMeta[],
add(type: Type, card: CardMeta) {
editDeckStore[type].push(card);
editDeckStore[type].sort(compareCards);
editDeckStore.edited = true;
},
remove(type: Type, card: CardMeta) {
const index = editDeckStore[type].findIndex((item) => item.id === card.id);
if (index !== -1) {
editDeckStore[type].splice(index, 1);
editDeckStore.edited = true;
}
},
set(deck: EditingDeck) {
editDeckStore.deckName = deck.deckName;
editDeckStore.main = deck.main.sort(compareCards);
editDeckStore.extra = deck.extra.sort(compareCards);
editDeckStore.side = deck.side.sort(compareCards);
editDeckStore.edited = false;
},
clear() {
editDeckStore.main = [];
editDeckStore.extra = [];
editDeckStore.side = [];
editDeckStore.edited = true;
},
edited: false,
}) satisfies EditingDeck;
...@@ -12,11 +12,11 @@ import { ...@@ -12,11 +12,11 @@ import {
Badge, Badge,
Button, Button,
ConfigProvider, ConfigProvider,
Dropdown,
Input, Input,
type MenuProps,
Space, Space,
type ThemeConfig, type ThemeConfig,
Dropdown,
type MenuProps,
} from "antd"; } from "antd";
import classNames from "classnames"; import classNames from "classnames";
import { memo, useEffect, useRef, useState } from "react"; import { memo, useEffect, useRef, useState } from "react";
...@@ -24,7 +24,7 @@ import { DndProvider, useDrag, useDrop } from "react-dnd"; ...@@ -24,7 +24,7 @@ import { DndProvider, useDrag, useDrop } from "react-dnd";
import { HTML5Backend } from "react-dnd-html5-backend"; import { HTML5Backend } from "react-dnd-html5-backend";
import { LoaderFunction } from "react-router-dom"; import { LoaderFunction } from "react-router-dom";
import { v4 as v4uuid } from "uuid"; import { v4 as v4uuid } from "uuid";
import { proxy, useSnapshot } from "valtio"; import { useSnapshot } from "valtio";
import { subscribeKey } from "valtio/utils"; import { subscribeKey } from "valtio/utils";
import { type CardMeta, searchCards } from "@/api"; import { type CardMeta, searchCards } from "@/api";
...@@ -41,12 +41,11 @@ import { ...@@ -41,12 +41,11 @@ import {
import { CardDetail } from "./CardDetail"; import { CardDetail } from "./CardDetail";
import { DeckSelect } from "./DeckSelect"; import { DeckSelect } from "./DeckSelect";
import { editDeckStore } from "./editDeckStore";
import { Filter } from "./Filter"; import { Filter } from "./Filter";
import styles from "./index.module.scss"; import styles from "./index.module.scss";
import { import {
canAdd, canAdd,
compareCards,
type EditingDeck,
editingDeckToIDeck, editingDeckToIDeck,
iDeckToEditingDeck, iDeckToEditingDeck,
type Type, type Type,
...@@ -60,42 +59,6 @@ const theme: ThemeConfig = { ...@@ -60,42 +59,6 @@ const theme: ThemeConfig = {
}, },
}; };
const items: MenuProps["items"] = [
{
key: "5",
label: "从新到旧",
},
{
key: "6",
label: "从旧到新",
},
{
key: "1",
label: "攻击力从高到低",
},
{
key: "2",
label: "攻击力从低到高",
},
{
key: "3",
label: "守备力从高到低",
},
{
key: "4",
label: "守备力从低到高",
},
{
key: "7",
label: "星/阶/刻/Link从高到低",
},
{
key: "8",
label: "星/阶/刻/Link从低到高",
},
];
export const loader: LoaderFunction = async () => { export const loader: LoaderFunction = async () => {
// 必须先加载卡组,不然页面会崩溃 // 必须先加载卡组,不然页面会崩溃
if (!initStore.decks) { if (!initStore.decks) {
...@@ -111,7 +74,7 @@ export const Component: React.FC = () => { ...@@ -111,7 +74,7 @@ export const Component: React.FC = () => {
const { sqlite } = useSnapshot(initStore); const { sqlite } = useSnapshot(initStore);
const [selectedDeck, setSelectedDeck] = useState<IDeck>(deckStore.decks[0]); const [selectedDeck, setSelectedDeck] = useState<IDeck>(deckStore.decks[0]);
const { message, notification, modal } = App.useApp(); const { message } = App.useApp();
const handleDeckEditorReset = async () => { const handleDeckEditorReset = async () => {
editDeckStore.set(await iDeckToEditingDeck(selectedDeck)); editDeckStore.set(await iDeckToEditingDeck(selectedDeck));
...@@ -247,11 +210,53 @@ const Search: React.FC = () => { ...@@ -247,11 +210,53 @@ const Search: React.FC = () => {
const [searchWord, setSearchWord] = useState(""); const [searchWord, setSearchWord] = useState("");
const [searchConditions, setSearchConditions] = useState<FtsConditions>({}); const [searchConditions, setSearchConditions] = useState<FtsConditions>({});
const [searchResult, setSearchResult] = useState<CardMeta[]>([]); const [searchResult, setSearchResult] = useState<CardMeta[]>([]);
const [filterOpen, setFilterOpen] = useState(false);
const sortRef = useRef<(a: CardMeta, b: CardMeta) => number>(
(a: CardMeta, b: CardMeta) => a.id - b.id
);
const setSortRef = (sort: (a: CardMeta, b: CardMeta) => number) => {
sortRef.current = sort;
setSearchResult([...searchResult.sort(sortRef.current)]);
};
const dropdownOptions: MenuProps["items"] = (
[
["从新到旧", () => setSortRef((a, b) => b.id - a.id)],
["从旧到新", () => setSortRef((a, b) => a.id - b.id)],
[
"攻击力从高到低",
() => setSortRef((a, b) => (b.data?.atk ?? 0) - (a.data?.atk ?? 0)),
],
[
"攻击力从低到高",
() => setSortRef((a, b) => (a.data?.atk ?? 0) - (b.data?.atk ?? 0)),
],
[
"守备力从高到低",
() => setSortRef((a, b) => (b.data?.def ?? 0) - (a.data?.def ?? 0)),
],
[
"守备力从低到高",
() => setSortRef((a, b) => (a.data?.def ?? 0) - (b.data?.def ?? 0)),
],
[
"星/阶/刻/Link从高到低",
() => setSortRef((a, b) => (a.data?.level ?? 0) - (b.data?.level ?? 0)),
],
[
"星/阶/刻/Link从低到高",
() => setSortRef((a, b) => (b.data?.level ?? 0) - (a.data?.level ?? 0)),
],
] as const
).map(([label, onClick], key) => ({ key, label, onClick }));
const handleSearch = async () => { const handleSearch = async () => {
const result = ( const result = (
await searchCards({ query: searchWord, conditions: searchConditions }) await searchCards({ query: searchWord, conditions: searchConditions })
).filter((card) => !isToken(card.data.type ?? 0)); // 衍生物不显示 )
.filter((card) => !isToken(card.data.type ?? 0))
.sort(sortRef.current); // 衍生物不显示
setSearchResult(result); setSearchResult(result);
}; };
...@@ -314,8 +319,7 @@ const Search: React.FC = () => { ...@@ -314,8 +319,7 @@ const Search: React.FC = () => {
{false && <Badge dot offset={[5, -5]} />} {false && <Badge dot offset={[5, -5]} />}
</Button> </Button>
<Dropdown <Dropdown
overlayStyle={{ backdropFilter: "blur(10px)" }} menu={{ items: dropdownOptions }}
menu={{ items }}
trigger={["click"]} trigger={["click"]}
placement="bottom" placement="bottom"
arrow arrow
...@@ -389,12 +393,12 @@ const DeckZone: React.FC<{ ...@@ -389,12 +393,12 @@ const DeckZone: React.FC<{
ref={dropRef} ref={dropRef}
> >
<div className={styles["card-continer"]}> <div className={styles["card-continer"]}>
{cards.map((item, i) => ( {cards.map((card) => (
<Card <Card
value={item} value={card}
key={i} key={v4uuid()}
source={type} source={type}
onRightClick={() => editDeckStore.remove(type, item)} onRightClick={() => editDeckStore.remove(type, card)}
/> />
))} ))}
<div className={styles["editing-zone-name"]}>{type.toUpperCase()}</div> <div className={styles["editing-zone-name"]}>{type.toUpperCase()}</div>
...@@ -416,7 +420,7 @@ const SearchResults: React.FC<{ ...@@ -416,7 +420,7 @@ const SearchResults: React.FC<{
{results.map((card) => ( {results.map((card) => (
<Card <Card
value={card} value={card}
key={v4uuid()} key={card.id}
source="search" source="search"
onClick={() => handleClick(card)} onClick={() => handleClick(card)}
/> />
...@@ -457,36 +461,3 @@ const Card: React.FC<{ ...@@ -457,36 +461,3 @@ const Card: React.FC<{
</div> </div>
); );
}); });
const editDeckStore = proxy({
deckName: "",
main: [] as CardMeta[],
extra: [] as CardMeta[],
side: [] as CardMeta[],
add(type: Type, card: CardMeta) {
editDeckStore[type].push(card);
editDeckStore[type].sort(compareCards);
editDeckStore.edited = true;
},
remove(type: Type, card: CardMeta) {
const index = editDeckStore[type].findIndex((item) => item.id === card.id);
if (index !== -1) {
editDeckStore[type].splice(index, 1);
editDeckStore.edited = true;
}
},
set(deck: EditingDeck) {
editDeckStore.deckName = deck.deckName;
editDeckStore.main = deck.main.sort(compareCards);
editDeckStore.extra = deck.extra.sort(compareCards);
editDeckStore.side = deck.side.sort(compareCards);
editDeckStore.edited = false;
},
clear() {
editDeckStore.main = [];
editDeckStore.extra = [];
editDeckStore.side = [];
editDeckStore.edited = true;
},
edited: false,
}) satisfies EditingDeck;
...@@ -30,7 +30,9 @@ export const theme: ThemeConfig = { ...@@ -30,7 +30,9 @@ export const theme: ThemeConfig = {
colorBgContainer: "hsla(0, 0%, 100%, 0.05)", colorBgContainer: "hsla(0, 0%, 100%, 0.05)",
}, },
Dropdown: { Dropdown: {
colorBgElevated: "hsla(0, 0%, 100%, 0.15)", colorBgElevated: "#3f4d60",
boxShadow:
"0 6px 16px 0 rgb(51 51 51 / 80%), 0 3px 6px -4px rgba(0, 0, 0, 0.12), 0 9px 28px 8px rgba(0, 0, 0, 0.05)",
}, },
}, },
}; };
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment