123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263 |
- import React, {
- useEffect,
- useMemo,
- useRef,
- useState,
- } from "react";
- import { Collapse, Input, Space, Spin, Tooltip } from "antd";
- import { useModel } from "umi";
- import { Dnd } from "@antv/x6-plugin-dnd";
- import insertCss from "insert-css";
- import { SearchOutlined } from "@ant-design/icons";
- import { CompoundedComponent } from "@/types";
- import { useRequest } from "ahooks";
- import ImageNode from "@/components/ImageNode";
- import { basic } from "@/components/basic";
- import { flowchart } from "@/components/flowchart";
- import { er } from "@/components/er";
- import { lane } from "@/components/lane";
- export default function Libary() {
- const { graph, initDnd, startDrag } = useModel("graphModel");
- const containerRef = useRef<HTMLDivElement>(null);
- const [search, setSearch] = useState("");
- const [showSearch, setShowSearch] = useState(false);
- const [systemSearchResult, setSystemSearchResult] = useState<
- CompoundedComponent[]
- >([]);
- const [activeKeys, setActiveKeys] = useState(["1", "2", "3", "4"]);
- useEffect(() => {
- if (!containerRef.current || !graph) return;
- const dnd = new Dnd({
- target: graph,
- scaled: false,
- dndContainer: containerRef.current,
- });
- initDnd(dnd);
- }, [graph, containerRef.current]);
- insertCss(`
- .shalu-collapse-item {
- border-bottom: 1px solid #dfe2e5 !important;
- border-radius: 0 !important;
- }
- .shalu-tabs-content {
- height: 100%;
- overflow: auto;
- }
- `);
- const paramRef = useRef<Record<string, any>>({
- appkey: "66dbfc87e4b0eb0606e13055",
- page: 1,
- size: 25,
- query: "",
- });
- const [iconList, setIconList] = useState<CompoundedComponent[]>([]);
- const searchServer = async (): Promise<any> => {
- const params = new URLSearchParams(paramRef.current).toString();
- const res = await fetch(`https://iconsapi.com/api/search?${params}`).then(
- (res) => res.json()
- );
- const list: CompoundedComponent[] = (res?.pages?.elements || []).map(
- (item: { iconName: string; url: string }) => {
- return {
- name: item.iconName,
- icon: item.url,
- node: {
- ...ImageNode,
- data: {
- ...ImageNode.data,
- fill: {
- ...ImageNode.data.fill,
- fillType: "image",
- imageUrl: item.url,
- },
- },
- },
- };
- }
- );
- if(paramRef.current.page === 1) {
- setIconList(list);
- } else {
- setIconList([...iconList, ...list]);
- }
- return res?.pages || {};
- };
- const { data, loading, run } = useRequest(searchServer, {
- manual: true,
- cacheKey: "iconsapi",
- cacheTime: 1000 * 60 * 60 * 24,
- });
- const renderItem = (data: any, key: number) => {
- return (
- <Tooltip title={data.name} key={key}>
- <img
- className="w-32px cursor-move"
- src={data.icon}
- onMouseDown={(e) => startDrag(e, data.node)}
- />
- </Tooltip>
- );
- };
- const items = useMemo(() => {
- const list = [
- {
- key: "1",
- label: "基础图形",
- children: (
- <Space wrap size={6}>
- {basic.map((item, index) => renderItem(item, index))}
- </Space>
- ),
- },
- {
- key: "2",
- label: "Flowchart流程图",
- children: (
- <Space wrap size={6}>
- {flowchart.map((item, index) => renderItem(item, index))}
- </Space>
- ),
- },
- {
- key: "3",
- label: "实体关系图(E-R图)",
- children: (
- <Space wrap size={6}>
- {er.map((item, index) => renderItem(item, index))}
- </Space>
- ),
- },
- {
- key: "4",
- label: "泳池/泳道",
- children: (
- <Space wrap size={6}>
- {lane.map((item, index) => renderItem(item, index))}
- </Space>
- ),
- },
- ];
- if (showSearch) {
- list.unshift({
- key: "search-icon",
- label: "网络图形",
- children: (
- <Spin spinning={loading}>
- <Space wrap size={6}>
- {iconList.map((item, index) => renderItem(item, index))}
- </Space>
- <div
- className="text-12px text-center color-#9aa5b8 w-full h-20px leading-20px cursor-pointer hover:bg-#f2f2f2 hover:color-#212930"
- onClick={() => {
- if(!(data && data?.pageCount === data?.curPage)) {
- handleNextPage(data.curPage + 1);
- }
- }}
- >
- {
- loading ? '加载中...'
- : data && data?.pageCount === data?.curPage ? '暂无更多' : '加载更多'
- }
- </div>
- </Spin>
- ),
- });
- if (!activeKeys.includes("search-icon")) {
- setActiveKeys((state) => [...state, "search-icon"]);
- }
- }
- if (showSearch && systemSearchResult.length) {
- list.unshift({
- key: "search-system",
- label: "系统图形",
- children: (
- <Space wrap size={6}>
- {systemSearchResult.map((item, index) => renderItem(item, index))}
- </Space>
- ),
- });
- if (!activeKeys.includes("search-system")) {
- setActiveKeys((state) => [...state, "search-system"]);
- }
- }
- return list;
- }, [systemSearchResult, showSearch, data, iconList, loading]);
- const handleChange = (keys: string | string[]) => {
- setActiveKeys(Array.isArray(keys) ? keys : [keys]);
- };
- const handleSearch = () => {
- if (!search) return;
- const allData = [...basic, ...flowchart, ...er, ...lane];
- setShowSearch(true);
- setSystemSearchResult(allData.filter((item) => item.name.includes(search)));
- paramRef.current = {
- ...paramRef.current,
- page: 1,
- query: search,
- };
- run();
- };
- const handleNextPage = (page: number) => {
- paramRef.current = {
- ...paramRef.current,
- page,
- };
- run();
- }
- useEffect(() => {
- if (!search) {
- setActiveKeys(["1", "2", "3", "4"]);
- setShowSearch(false);
- setSystemSearchResult([]);
- paramRef.current = {
- ...paramRef.current,
- page: 1,
- query: "",
- };
- setActiveKeys((state) =>
- state.filter(
- (item) => !(item === "search-system" || item === "search-icon")
- )
- );
- }
- }, [search]);
- return (
- <div ref={containerRef} className="h-full overflow-auto">
- <div className="px-4">
- <Input
- prefix={<SearchOutlined />}
- size="small"
- allowClear
- placeholder="请输入搜索内容"
- value={search}
- onChange={(e) => setSearch(e.target.value)}
- onPressEnter={handleSearch}
- />
- </div>
- <Collapse
- ghost
- items={items}
- activeKey={activeKeys}
- onChange={handleChange}
- />
- </div>
- );
- }
|