160 lines
4.4 KiB
TypeScript
160 lines
4.4 KiB
TypeScript
import { useEffect, useMemo, useState } from 'react';
|
|
import { View, Text, ScrollView } from '@tarojs/components';
|
|
|
|
export type StoreCategoryNode = {
|
|
id: string;
|
|
name: string;
|
|
children?: StoreCategoryNode[];
|
|
};
|
|
|
|
export type CategorySelection = {
|
|
parentId: string;
|
|
parentName: string;
|
|
childId: string;
|
|
childName: string;
|
|
};
|
|
|
|
export const EMPTY_CATEGORY: CategorySelection = {
|
|
parentId: '',
|
|
parentName: '',
|
|
childId: '',
|
|
childName: '',
|
|
};
|
|
|
|
export function formatCategoryLabel(sel: CategorySelection): string {
|
|
if (sel.childName) return sel.childName;
|
|
if (sel.parentName) return sel.parentName;
|
|
return '全部分类';
|
|
}
|
|
|
|
type CategoryPickerProps = {
|
|
open: boolean;
|
|
tree: StoreCategoryNode[];
|
|
value: CategorySelection;
|
|
onClose: () => void;
|
|
onConfirm: (next: CategorySelection) => void;
|
|
};
|
|
|
|
type TabKey = 'parent' | 'child';
|
|
|
|
export default function CategoryPicker({
|
|
open,
|
|
tree,
|
|
value,
|
|
onClose,
|
|
onConfirm,
|
|
}: CategoryPickerProps) {
|
|
const [draft, setDraft] = useState<CategorySelection>(value);
|
|
const [activeTab, setActiveTab] = useState<TabKey>('parent');
|
|
|
|
useEffect(() => {
|
|
if (!open) return;
|
|
setDraft(value);
|
|
setActiveTab(value.parentId ? 'child' : 'parent');
|
|
}, [open, value]);
|
|
|
|
const children = useMemo(() => {
|
|
const parent = tree.find((n) => n.id === draft.parentId);
|
|
return parent?.children ?? [];
|
|
}, [tree, draft.parentId]);
|
|
|
|
if (!open) return null;
|
|
|
|
function selectParent(node: StoreCategoryNode | null) {
|
|
if (!node) {
|
|
setDraft(EMPTY_CATEGORY);
|
|
return;
|
|
}
|
|
setDraft({
|
|
parentId: node.id,
|
|
parentName: node.name,
|
|
childId: '',
|
|
childName: '',
|
|
});
|
|
setActiveTab('child');
|
|
}
|
|
|
|
function selectChild(node: StoreCategoryNode | null) {
|
|
if (!node) {
|
|
setDraft((prev) => ({ ...prev, childId: '', childName: '' }));
|
|
return;
|
|
}
|
|
setDraft((prev) => ({
|
|
...prev,
|
|
childId: node.id,
|
|
childName: node.name,
|
|
}));
|
|
}
|
|
|
|
function handleConfirm() {
|
|
onConfirm(draft);
|
|
onClose();
|
|
}
|
|
|
|
return (
|
|
<View className="region-picker-overlay" onClick={onClose}>
|
|
<View className="region-picker-sheet" onClick={(e) => e.stopPropagation()}>
|
|
<View className="region-picker-toolbar">
|
|
<View className="region-picker-tabs">
|
|
<Text
|
|
className={`region-picker-tab${activeTab === 'parent' ? ' active' : ''}`}
|
|
onClick={() => setActiveTab('parent')}
|
|
>
|
|
{draft.parentName || '大类'}
|
|
</Text>
|
|
<Text
|
|
className={`region-picker-tab${activeTab === 'child' ? ' active' : ''}${!draft.parentId ? ' disabled' : ''}`}
|
|
onClick={() => draft.parentId && setActiveTab('child')}
|
|
>
|
|
{draft.childName || '细类'}
|
|
</Text>
|
|
</View>
|
|
<Text className="region-picker-confirm ready" onClick={handleConfirm}>
|
|
确定
|
|
</Text>
|
|
</View>
|
|
|
|
<ScrollView className="region-picker-list" scrollY showScrollbar={false}>
|
|
{activeTab === 'parent' ? (
|
|
<>
|
|
<View
|
|
className={`region-picker-option${!draft.parentId ? ' selected' : ''} region-picker-option--all`}
|
|
onClick={() => selectParent(null)}
|
|
>
|
|
<Text>全部分类</Text>
|
|
</View>
|
|
{tree.map((item) => (
|
|
<View
|
|
key={item.id}
|
|
className={`region-picker-option${draft.parentId === item.id ? ' selected' : ''}`}
|
|
onClick={() => selectParent(item)}
|
|
>
|
|
<Text>{item.name}</Text>
|
|
</View>
|
|
))}
|
|
</>
|
|
) : (
|
|
<>
|
|
<View
|
|
className={`region-picker-option${!draft.childId ? ' selected' : ''} region-picker-option--all`}
|
|
onClick={() => selectChild(null)}
|
|
>
|
|
<Text>全部细类</Text>
|
|
</View>
|
|
{children.map((item) => (
|
|
<View
|
|
key={item.id}
|
|
className={`region-picker-option${draft.childId === item.id ? ' selected' : ''}`}
|
|
onClick={() => selectChild(item)}
|
|
>
|
|
<Text>{item.name}</Text>
|
|
</View>
|
|
))}
|
|
</>
|
|
)}
|
|
</ScrollView>
|
|
</View>
|
|
</View>
|
|
);
|
|
}
|