375 lines
11 KiB
TypeScript

import { ContainerEdit } from '@kevisual/container/edit';
import { useCallback, useEffect, useRef, useState } from 'react';
import { useParams } from 'react-router';
import { query, useStore, ws } from '@/modules';
import { Button, message, Tooltip } from 'antd';
import { getContainerData } from '@/modules/deck-to-flow/deck';
import { usePanelStore } from '../store';
import { useShallow } from 'zustand/react/shallow';
import { TextArea } from '@/pages/container/components/TextArea';
import { CloseOutlined, MessageOutlined, SaveOutlined, SelectOutlined } from '@ant-design/icons';
import { useDeckPageStore } from './deck-store';
import { FormModal } from './Model.tsx';
import { useAiStore } from '@/pages/ai-chat/index.tsx';
export const clearBlank = (newStyle: any) => {
let change = false;
for (let key in newStyle) {
if (newStyle[key] === '' || newStyle[key] === undefined || newStyle[key] === null) {
delete newStyle[key];
change = true;
}
}
return change;
};
export const useListener = (id?: string, opts?: any) => {
const { refresh, cids = [] } = opts || {};
const connected = useStore((state) => state.connected);
// 监听服务器的消息
useEffect(() => {
if (!id) return;
if (!connected) return;
if (cids.length === 0) return;
console.log('cids', cids);
ws.send(
JSON.stringify({
type: 'subscribe',
data: {
type: 'pageEdit',
data: {
pid: id,
cids: cids,
},
},
}),
);
ws.addEventListener('message', listener);
return () => {
if (!id) return;
if (!connected) return;
ws.removeEventListener('message', listener);
};
}, [id, connected, cids]);
const listener = (event) => {
const parseIfJson = (data: string) => {
try {
return JSON.parse(data);
} catch (e) {
return data;
}
};
const receivedData = parseIfJson(event.data);
if (typeof receivedData === 'string') return;
if (receivedData.type === 'pageEdit' && receivedData.source === 'container') {
const { data: containerData, pid } = receivedData;
if (pid !== id) return;
if (refresh) {
refresh(containerData);
}
}
};
};
export const Deck = () => {
const params = useParams<{ id: string }>();
const id = params.id;
const ref = useRef<HTMLDivElement>(null);
const containerRef = useRef<ContainerEdit | null>(null);
const deckPageStore = useDeckPageStore();
const { code, setCode } = deckPageStore;
const { selected, setSelected } = deckPageStore;
const { cids } = deckPageStore;
const aiStore = useAiStore(
useShallow((state) => {
return {
setOpen: state.setOpen,
setKey: state.setKey,
};
}),
);
const panelStore = usePanelStore(
useShallow((state) => {
return {
updateNodeDataStyle: state.updateNodeDataStyle,
};
}),
);
useEffect(() => {
if (!id) return;
deckPageStore.setId(id);
fetch();
}, []);
useEffect(() => {
ref.current?.addEventListener('onContainer', onContainer);
return () => {
ref.current?.removeEventListener('onContainer', onContainer);
if (ref.current) {
const children = ref.current;
children.innerHTML = '';
}
};
}, []);
const fetch = async () => {
const res = await query.post({
path: 'page',
key: 'getDeck',
id,
});
if (res.code === 200) {
const data = res.data;
console.log('data', data);
const { page, containerList } = data;
const result = getContainerData({ page, containerList });
console.log('result', result);
deckPageStore.setPageData(result);
deckPageStore.setCids();
init(result);
}
};
const refresh = async (data: any) => {
console.log('refresh', data);
if (!data.id) return;
const code = {
codeId: data.id,
code: data.code,
hash: '',
};
const container = containerRef.current!;
// @ts-ignore
await container.updateDataCode([code]);
const containerList = container.data.filter((item) => item.codeId === data.id);
await new Promise((resolve) => {
setTimeout(resolve, 2000);
});
// container.reRender();
containerList.forEach((item) => {
container.renderId(item.id);
});
// @ts-ignore
window.c = container;
};
useListener(id, { refresh, cids });
const onContainer = (e) => {
const { data } = e;
const types = ['position', 'resize'];
if (types.includes(data.type)) {
const { type, data: containerData } = data;
if (type === 'position') {
const { cid, left, top, rid } = containerData;
const newData = {
id: rid,
nodeData: {
id: cid,
data: {
style: {
// position: 'absolute',
left,
top,
},
},
},
};
if (left && top) {
panelStore.updateNodeDataStyle(newData);
}
updateStyle(cid, { left, top });
setTimeout(() => {
setCodeStyle(cid);
}, 1000);
} else if (type === 'resize') {
const { cid, rid, width, height } = containerData;
const newData = {
id: rid,
nodeData: {
id: cid,
data: {
style: {
width,
height,
},
},
},
};
if (width && height) {
// @ts-ignore
newData.nodeData.data.style.position = 'absolute';
panelStore.updateNodeDataStyle(newData);
}
updateStyle(cid, { width, height });
setTimeout(() => {
setCodeStyle(cid);
}, 1000);
}
} else if (data.type === 'active') {
if (!data?.data?.cid) {
setSelected(null);
return;
}
const { cid, rid } = data?.data || {};
setSelected(data);
setCodeStyle(cid);
} else {
console.log('onContainer', data);
}
};
const onSave = () => {
const { cid, rid } = selected?.data || {};
let data: any;
try {
data = JSON.parse(code);
} catch (error) {
message.error('JSON format error');
return;
}
// clearBlank(data);
const newData = {
id: rid,
nodeData: {
id: cid,
data: {
style: data,
},
},
};
panelStore.updateNodeDataStyle(newData, true);
const newDataStyle = updateStyle(cid, data);
reRender(newDataStyle, cid);
};
const setCodeStyle = (cid: string) => {
const pageData = deckPageStore.getPageData();
const selected = deckPageStore.getSeleted();
const _data = pageData.find((item) => item.id === cid);
const node = _data?.data?.node || {};
if (selected?.data?.cid === cid) {
setCode('');
setCode(JSON.stringify(node?.data?.style || {}, null, 2));
}
};
useEffect(() => {
if (selected) {
const { cid } = selected?.data || {};
cid && setCodeStyle(cid);
}
}, [deckPageStore.selected]);
const updateStyle = (rid: string, style: any) => {
const pageData = deckPageStore.getPageData();
const _pageData = pageData.map((item) => {
if (item.id === rid) {
const newStyle = {
...item.data.node.data.style,
...style,
};
// 过滤掉空的style
clearBlank(newStyle);
return {
...item,
style: newStyle,
data: {
...item.data,
node: {
...item.data.node,
data: {
...item.data.node.data,
style: newStyle,
},
},
},
};
}
return item;
});
deckPageStore.setPageData([..._pageData]);
return _pageData;
};
const init = async (data: any[]) => {
// console.log('data', data, ref.current);
const container = new ContainerEdit({
root: ref.current!,
data: data,
showChild: true,
// edit: false,
});
container.render(id!);
containerRef.current = container;
containerRef.current.event.on('save', (data) => {
console.log('save', data);
const { id, code } = data;
});
};
const reRender = async (data: any[], cid?: string) => {
if (containerRef.current) {
const container = containerRef.current;
await container.updateData(data);
await container.renderId(cid!);
return;
}
};
return (
<div className='w-full h-full relative'>
<div className='w-full h-full bg-gray-200 '>
<div className='text-center mb-10 font-bold text-4xl pt-4 flex items-center justify-center group'>
Deck
<Tooltip>
<Button
className='ml-4 invisible group-hover:visible'
icon={<SelectOutlined />}
onClick={() => {
deckPageStore.setShowEdit(true);
}}></Button>
</Tooltip>
</div>
<div
className='flex '
style={{
height: 'calc(100% - 32px)',
}}>
<div className='mx-auto border rounded-md bg-white w-[80%] h-[80%] scrollbar overflow-scroll relative' ref={ref}></div>
</div>
</div>
{selected && (
<div className='absolute bottom-5 z-50 w-full h-[200px]'>
<div className=' p-2 card w-[80%] mx-auto border bg-slate-200 rounded-md overflow-scroll scrollbar'>
{/* <pre>{JSON.stringify(selected, null, 2)}</pre> */}
<div>
<Button.Group>
<Tooltip title='Close'>
<Button
onClick={() => {
setSelected(null);
}}
icon={<CloseOutlined />}></Button>
</Tooltip>
<Tooltip title='Ai Chat'>
<Button
onClick={() => {
aiStore.setOpen(true);
aiStore.setKey(location.pathname);
}}
icon={<MessageOutlined />}></Button>
</Tooltip>
<Tooltip title='Save'>
<Button
onClick={() => {
onSave();
}}
icon={<SaveOutlined />}></Button>
</Tooltip>
</Button.Group>
</div>
<div className='h-[200px]'>
<TextArea
className='h-[100px] rounded-md'
value={code}
onChange={(v) => {
setCode(v);
}}
/>
</div>
</div>
</div>
)}
<FormModal />
</div>
);
};