Commit f27a8f3f authored by timel's avatar timel

feat: filter and order

parent 367ed2cb
...@@ -2,17 +2,19 @@ import { Database } from "sql.js"; ...@@ -2,17 +2,19 @@ import { Database } from "sql.js";
import { CardData, CardMeta, CardText } from "@/api"; import { CardData, CardMeta, CardText } from "@/api";
import { isNil } from "lodash-es";
import { constructCardMeta } from "."; import { constructCardMeta } from ".";
const TYPE_MONSTER = 0x1; const TYPE_MONSTER = 0x1;
/** 过滤条件 */
export interface FtsConditions { export interface FtsConditions {
// 过滤条件 types: number[]; // 卡片类型
types?: number[]; // 卡片类型 levels: number[]; // 星阶/刻度/link值
levels?: number[]; // 星阶/刻度/link值 races: number[]; // 种族
atk?: [number, number]; // 攻击力区间 attributes: number[]; // 属性
def?: [number, number]; // 防御力区间 atk: { min: number | null; max: number | null }; // 攻击力区间
races?: number[]; // 种族 def: { min: number | null; max: number | null }; // 防御力区间
attributes?: number[]; // 属性
} }
export interface FtsParams { export interface FtsParams {
query: string; // 用于全文检索的query query: string; // 用于全文检索的query
...@@ -61,10 +63,16 @@ function getFtsCondtions(conditions: FtsConditions): string { ...@@ -61,10 +63,16 @@ function getFtsCondtions(conditions: FtsConditions): string {
?.map((level) => `level = ${level}`) ?.map((level) => `level = ${level}`)
.join(" OR "); .join(" OR ");
const atkCondition = atk const atkCondition = atk
? `atk BETWEEN ${atk[0]} AND ${atk[1]} AND ${assertMonster}` ? `atk BETWEEN ${handleFinite(atk.min, "min")} AND ${handleFinite(
atk.max,
"max"
)} AND ${assertMonster}`
: undefined; : undefined;
const defCondition = def const defCondition = def
? `def BETWEEN ${def[0]} AND ${def[1]} AND ${assertMonster}` ? `def BETWEEN ${handleFinite(def.min, "min")} AND ${handleFinite(
def.max,
"max"
)} AND ${assertMonster}`
: undefined; : undefined;
const raceCondition = races?.map((race) => `race = ${race}`).join(" OR "); const raceCondition = races?.map((race) => `race = ${race}`).join(" OR ");
const attributeCondition = attributes const attributeCondition = attributes
...@@ -85,3 +93,8 @@ function getFtsCondtions(conditions: FtsConditions): string { ...@@ -85,3 +93,8 @@ function getFtsCondtions(conditions: FtsConditions): string {
return merged; return merged;
} }
function handleFinite(value: number | null, type: "min" | "max"): number {
if (isNil(value)) return type === "min" ? -2 : 9999999;
return value;
}
This diff is collapsed.
...@@ -206,7 +206,14 @@ const DeckEditor: React.FC<{ ...@@ -206,7 +206,14 @@ const DeckEditor: React.FC<{
const Search: React.FC = () => { const Search: React.FC = () => {
const { modal } = App.useApp(); const { modal } = App.useApp();
const [searchWord, setSearchWord] = useState(""); const [searchWord, setSearchWord] = useState("");
const [searchConditions, setSearchConditions] = useState<FtsConditions>({}); const [searchConditions, setSearchConditions] = useState<FtsConditions>({
atk: { min: null, max: null },
def: { min: null, max: null },
levels: [],
races: [],
attributes: [],
types: [],
});
const [searchResult, setSearchResult] = useState<CardMeta[]>([]); const [searchResult, setSearchResult] = useState<CardMeta[]>([]);
const sortRef = useRef<(a: CardMeta, b: CardMeta) => number>( const sortRef = useRef<(a: CardMeta, b: CardMeta) => number>(
...@@ -218,34 +225,26 @@ const Search: React.FC = () => { ...@@ -218,34 +225,26 @@ const Search: React.FC = () => {
setSearchResult([...searchResult.sort(sortRef.current)]); setSearchResult([...searchResult.sort(sortRef.current)]);
}; };
const genSort = (key: keyof CardMeta["data"], scale: 1 | -1 = 1) => {
return () =>
setSortRef(
(a: CardMeta, b: CardMeta) =>
((a.data?.[key] ?? 0) - (b.data?.[key] ?? 0)) * scale
);
};
const dropdownOptions: MenuProps["items"] = ( const dropdownOptions: MenuProps["items"] = (
[ [
["从新到旧", () => setSortRef((a, b) => b.id - a.id)], ["从新到旧", () => setSortRef((a, b) => b.id - a.id)],
["从旧到新", () => setSortRef((a, b) => a.id - b.id)], ["从旧到新", () => setSortRef((a, b) => a.id - b.id)],
[ ["攻击力从高到低", genSort("atk")],
"攻击力从高到低", ["攻击力从低到高", genSort("atk", -1)],
() => setSortRef((a, b) => (b.data?.atk ?? 0) - (a.data?.atk ?? 0)), ["守备力从高到低", genSort("def")],
], ["守备力从低到高", genSort("def", -1)],
[ ["星/阶/刻/Link从高到低", genSort("level")],
"攻击力从低到高", ["星/阶/刻/Link从低到高", genSort("level", -1)],
() => setSortRef((a, b) => (a.data?.atk ?? 0) - (b.data?.atk ?? 0)), ["灵摆刻度从高到低", genSort("lscale")],
], ["灵摆刻度从低到高", genSort("lscale", -1)],
[
"守备力从高到低",
() => 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 ] as const
).map(([label, onClick], key) => ({ key, label, onClick })); ).map(([label, onClick], key) => ({ key, label, onClick }));
...@@ -268,6 +267,28 @@ const Search: React.FC = () => { ...@@ -268,6 +267,28 @@ const Search: React.FC = () => {
} }
}, },
}); });
const showFilterModal = () => {
const { destroy } = modal.info({
width: 500,
centered: true,
title: null,
icon: null,
content: (
<Filter
conditions={searchConditions}
onConfirm={(newConditions) => {
setSearchConditions(newConditions);
destroy();
setTimeout(handleSearch, 50); // 先收起再搜索
}}
onCancel={() => destroy()}
/>
),
footer: null,
});
};
return ( return (
<div className={styles.container} ref={dropRef}> <div className={styles.container} ref={dropRef}>
<div className={styles.title}> <div className={styles.title}>
...@@ -292,25 +313,7 @@ const Search: React.FC = () => { ...@@ -292,25 +313,7 @@ const Search: React.FC = () => {
block block
type="text" type="text"
icon={<FilterOutlined />} icon={<FilterOutlined />}
onClick={() => { onClick={showFilterModal}
const { destroy } = modal.info({
width: 500,
centered: true,
title: null,
icon: null,
content: (
<Filter
conditions={searchConditions}
onConfirm={(newConditions) => {
setSearchConditions(newConditions);
destroy();
}}
onCancel={() => destroy()}
/>
),
footer: null,
});
}}
> >
筛选 筛选
{/* TODO: 下面这个Badge应根据有无筛选规则而显示 */} {/* TODO: 下面这个Badge应根据有无筛选规则而显示 */}
......
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