Last active: 2 years ago
import LabelItem from '@/components/LabelItem';
import { globalSelectDone } from '@/projects/editor/data';
import { SearchData, SearchDataContent } from '@/show/interface';
import { SceneInfo, SpotBase } from '@/show/lib';
import { useAppDispatch, useAppSelector } from '@/store';
import { addSearchData, delSearchData } from '@/store/core/slice';
import { resetWithTarget, setShowSceneLibrary } from '@/store/ui/slice';
import { searchTypeList, serializeList } from '@/utils/api/search-list';
import { DeleteOutlined, DownOutlined } from '@ant-design/icons';
import { useBoolean, useRequest } from 'ahooks';
import { Button, Cascader, Dropdown, Empty, Menu, Popover } from 'antd';
import { useMemo, useState } from 'react';
import { MarkItem } from '../SandTable/Style';
import { Content } from './index.styles';
// 名称 map
export const typeMap = {
scene: '场景',
spot: '热点',
};
// 内容单个项目
const ListItem = ({
item,
handleDelete,
}: {
item: SearchDataContent;
handleDelete: (id: string) => void;
}) => {
const [show, setShow] = useBoolean(false);
const handleOk = (id: string) => {
handleDelete(id);
setShow.setFalse();
};
return (
<MarkItem>
<Content>
<div>{item.data.name}</div>
<div className="type">
{typeMap[item.type]}
<Popover
content={
<div>
<Button
size="small"
onClick={setShow.setFalse}
style={{ marginRight: 4 }}
>
取消
</Button>
<Button
size="small"
type="primary"
onClick={() => handleOk(item.data.id)}
>
确定
</Button>
</div>
}
title="确认删除?"
visible={show}
>
<DeleteOutlined onClick={() => setShow.toggle()} />
</Popover>
</div>
</Content>
</MarkItem>
);
};
const SearchPanel = () => {
const { data } = useRequest(searchTypeList);
const list = useMemo(() => serializeList(data?.data), [data?.data]);
// 选择的状态
// 保存的为顺序 ID 父id - 子id [1, 2, 4]
const [value, setValue] = useState<number[]>([]);
const handleChange = (value: number[] | null) => {
if (value == null) {
setValue([]);
} else {
setValue(value);
}
};
// value 末尾 id
const currentId = useMemo(
() => (value?.length ? value[value?.length - 1] : 0),
[value]
);
// 搜索内容
const searchData = useAppSelector((state) => state.core.proj.data.searchData);
// 当为一级分类时,父分类的 content
const parentContent = useMemo(
() => searchData?.[currentId]?.content,
[currentId, searchData]
);
const dispatch = useAppDispatch();
// 添加场景回调
const sceneSelectDone = (
data: [number, number, string],
scene?: SceneInfo
) => {
if (!data || !scene) return;
// 处理父分组添加
dispatch(
addSearchData({
list: value,
data: {
type: 'scene',
data: { id: data[2], name: scene.name },
},
})
);
globalSelectDone.sceneCb = undefined;
};
const spotSelectDone = (data: SpotBase) => {
dispatch(
addSearchData({
list: value,
data: {
type: 'spot',
data: { id: data.id, name: data.name },
},
})
);
};
// 处理添加内容
const handleAdd = (type: 'scene' | 'spot') => {
switch (type) {
case 'scene': {
globalSelectDone.sceneCb = sceneSelectDone;
dispatch(setShowSceneLibrary(true));
break;
}
case 'spot': {
globalSelectDone.spotCb = spotSelectDone;
dispatch(
resetWithTarget({
showSpotLibrary: true,
})
);
break;
}
}
};
// dropdown 状态
const menu = (
<Menu
items={[
{
label: '场景',
key: '1',
onClick: () => handleAdd('scene'),
},
{
label: '热点',
key: '2',
onClick: () => handleAdd('spot'),
},
]}
/>
);
// 处理删除内容
const handleDelete = (id: string) => {
dispatch(delSearchData({ list: value, id }));
};
/**
* 递归创建搜索内容的 DOM 树
*/
const walker = (
list: number[],
children: SearchData[] | undefined,
index: number
): JSX.Element[] | undefined => {
const len = list?.length ? list.length : 0;
if (!children) return;
const target = children.find((c) => c.id === list[index]);
if (!target) return;
if (index === len - 1) {
if (!target.content.length) return;
return target.content.map((item) => (
<ListItem key={item.data.id} item={item} handleDelete={handleDelete} />
));
} else {
return walker(list, target.children, index + 1);
}
};
return (
<>
<LabelItem>选择分类</LabelItem>
<Cascader
options={list}
fieldNames={{
label: 'name',
value: 'id',
children: 'children',
}}
// changeOnSelect
// Bug Cascader 类型推断不准确
onChange={handleChange as any}
style={{
width: '100%',
}}
/>
<LabelItem>当前分类下内容</LabelItem>
<div style={{ marginTop: 10 }}>
<Popover content={!value?.length ? '请先选择分类' : ''}>
<div>
<Dropdown disabled={!value?.length} overlay={menu}>
<Button block type="primary">
增加内容
<DownOutlined />
</Button>
</Dropdown>
</div>
</Popover>
</div>
<div style={{ margin: '10px 0' }}>
{value?.length === 1 ? (
parentContent?.length ? (
parentContent.map((item) => (
<ListItem
key={item.data.id}
item={item}
handleDelete={handleDelete}
/>
))
) : (
<Empty />
)
) : (
walker(value, searchData?.[value[0]]?.children, 1) ?? <Empty />
)}
</div>
</>
);
};
export default SearchPanel;