diff --git a/package.json b/package.json
index 004b134..ad2ab8e 100644
--- a/package.json
+++ b/package.json
@@ -27,6 +27,7 @@
"clsx": "^2.1.1",
"copy-to-clipboard": "^3.3.3",
"d3": "^7.9.0",
+ "eventemitter3": "^5.0.1",
"immer": "^10.1.1",
"lodash-es": "^4.17.21",
"marked": "^14.1.2",
@@ -42,8 +43,10 @@
"devDependencies": {
"@eslint/js": "^9.11.0",
"@tailwindcss/aspect-ratio": "^0.4.2",
+ "@tailwindcss/line-clamp": "^0.4.4",
"@tailwindcss/typography": "^0.5.15",
"@types/d3": "^7.4.3",
+ "@types/lodash-es": "^4.17.12",
"@types/node": "^22.5.5",
"@types/react": "^18.3.8",
"@types/react-dom": "^18.3.0",
@@ -53,6 +56,7 @@
"eslint-plugin-react-hooks": "^5.1.0-rc.0",
"eslint-plugin-react-refresh": "^0.4.12",
"globals": "^15.9.0",
+ "postcss-import": "^16.1.0",
"react-is": "^18.3.1",
"tailwind-merge": "^2.5.2",
"tailwindcss": "^3.4.13",
diff --git a/plugins/flex.js b/plugins/flex.js
new file mode 100644
index 0000000..ad53cd5
--- /dev/null
+++ b/plugins/flex.js
@@ -0,0 +1,25 @@
+const plugin = require('tailwindcss/plugin');
+
+const flexCenterBaseStyles = {
+ display: 'flex',
+ 'justify-content': 'center',
+ 'align-items': 'center',
+};
+
+/** flex 居中 */
+const flexCenter = plugin(function ({ addUtilities }) {
+ addUtilities({
+ /** flex 居中 */
+ '.flex-row-center': flexCenterBaseStyles,
+ '.flex-col-center': { ...flexCenterBaseStyles, 'flex-direction': 'column' },
+ '.layout-menu': {},
+ '.scrollbar': {},
+ '.card': {},
+ '.card-title': {},
+ '.card-subtitle': {},
+ '.card-body': {},
+ '.card-footer': {},
+ });
+});
+
+module.exports = flexCenter;
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index ebd977f..3928fbc 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -53,6 +53,9 @@ importers:
d3:
specifier: ^7.9.0
version: 7.9.0
+ eventemitter3:
+ specifier: ^5.0.1
+ version: 5.0.1
immer:
specifier: ^10.1.1
version: 10.1.1
@@ -93,12 +96,18 @@ importers:
'@tailwindcss/aspect-ratio':
specifier: ^0.4.2
version: 0.4.2(tailwindcss@3.4.13)
+ '@tailwindcss/line-clamp':
+ specifier: ^0.4.4
+ version: 0.4.4(tailwindcss@3.4.13)
'@tailwindcss/typography':
specifier: ^0.5.15
version: 0.5.15(tailwindcss@3.4.13)
'@types/d3':
specifier: ^7.4.3
version: 7.4.3
+ '@types/lodash-es':
+ specifier: ^4.17.12
+ version: 4.17.12
'@types/node':
specifier: ^22.5.5
version: 22.5.5
@@ -126,6 +135,9 @@ importers:
globals:
specifier: ^15.9.0
version: 15.9.0
+ postcss-import:
+ specifier: ^16.1.0
+ version: 16.1.0(postcss@8.4.47)
react-is:
specifier: ^18.3.1
version: 18.3.1
@@ -733,6 +745,11 @@ packages:
peerDependencies:
tailwindcss: '>=2.0.0 || >=3.0.0 || >=3.0.0-alpha.1'
+ '@tailwindcss/line-clamp@0.4.4':
+ resolution: {integrity: sha512-5U6SY5z8N42VtrCrKlsTAA35gy2VSyYtHWCsg1H87NU1SXnEfekTVlrga9fzUDrrHcGi2Lb5KenUWb4lRQT5/g==}
+ peerDependencies:
+ tailwindcss: '>=2.0.0 || >=3.0.0 || >=3.0.0-alpha.1'
+
'@tailwindcss/typography@0.5.15':
resolution: {integrity: sha512-AqhlCXl+8grUz8uqExv5OTtgpjuVIwFTSXTrh8y9/pw6q2ek7fJ+Y8ZEVw7EB2DCcuCOtEjf9w3+J3rzts01uA==}
peerDependencies:
@@ -855,6 +872,12 @@ packages:
'@types/hast@3.0.4':
resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==}
+ '@types/lodash-es@4.17.12':
+ resolution: {integrity: sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==}
+
+ '@types/lodash@4.17.9':
+ resolution: {integrity: sha512-w9iWudx1XWOHW5lQRS9iKpK/XuRhnN+0T7HvdCCd802FYkT1AMTnxndJHGrNJwRoRHkslGr4S29tjm1cT7x/7w==}
+
'@types/mdast@4.0.4':
resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==}
@@ -1861,6 +1884,12 @@ packages:
peerDependencies:
postcss: ^8.0.0
+ postcss-import@16.1.0:
+ resolution: {integrity: sha512-7hsAZ4xGXl4MW+OKEWCnF6T5jqBw80/EE9aXg1r2yyn1RsVEU8EtKXbijEODa+rg7iih4bKf7vlvTGYR4CnPNg==}
+ engines: {node: '>=18.0.0'}
+ peerDependencies:
+ postcss: ^8.0.0
+
postcss-js@4.0.1:
resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==}
engines: {node: ^12 || ^14 || >= 16}
@@ -3142,6 +3171,10 @@ snapshots:
dependencies:
tailwindcss: 3.4.13
+ '@tailwindcss/line-clamp@0.4.4(tailwindcss@3.4.13)':
+ dependencies:
+ tailwindcss: 3.4.13
+
'@tailwindcss/typography@0.5.15(tailwindcss@3.4.13)':
dependencies:
lodash.castarray: 4.4.0
@@ -3300,6 +3333,12 @@ snapshots:
dependencies:
'@types/unist': 3.0.3
+ '@types/lodash-es@4.17.12':
+ dependencies:
+ '@types/lodash': 4.17.9
+
+ '@types/lodash@4.17.9': {}
+
'@types/mdast@4.0.4':
dependencies:
'@types/unist': 3.0.3
@@ -4428,6 +4467,13 @@ snapshots:
read-cache: 1.0.0
resolve: 1.22.8
+ postcss-import@16.1.0(postcss@8.4.47):
+ dependencies:
+ postcss: 8.4.47
+ postcss-value-parser: 4.2.0
+ read-cache: 1.0.0
+ resolve: 1.22.8
+
postcss-js@4.0.1(postcss@8.4.47):
dependencies:
camelcase-css: 2.0.1
diff --git a/src/App.tsx b/src/App.tsx
index 76b6cc0..1e929a3 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -1,5 +1,5 @@
import { BrowserRouter as Router, Route, Routes, Navigate } from 'react-router-dom';
-import { ConfigProvider } from 'antd';
+import { ConfigProvider, App as AntApp } from 'antd';
import { App as ContainerApp } from './pages/container';
import { App as PanelApp } from './pages/panel';
import { App as PublishApp } from './pages/publish';
diff --git a/src/components/card/CardBlank.tsx b/src/components/card/CardBlank.tsx
new file mode 100644
index 0000000..332cf2e
--- /dev/null
+++ b/src/components/card/CardBlank.tsx
@@ -0,0 +1,17 @@
+import clsx from 'clsx';
+import twMerge from 'tailwind-merge';
+
+type CardBlankProps = {
+ number?: number;
+ className?: string;
+};
+export const CardBlank = (props: CardBlankProps) => {
+ const { number = 4, className } = props;
+ return (
+ <>
+ {new Array(number).fill(0).map((_, index) => {
+ return
;
+ })}
+ >
+ );
+};
diff --git a/src/globals.css b/src/globals.css
index 1ad8d9b..51fd0b6 100644
--- a/src/globals.css
+++ b/src/globals.css
@@ -3,6 +3,13 @@
@tailwind utilities;
@layer base {
+ html,
+ body {
+ width: 100%;
+ height: 100%;
+ font-size: 16px;
+ font-family: 'Montserrat', sans-serif;
+ }
h1 {
@apply text-2xl font-bold;
}
@@ -12,7 +19,40 @@
h3 {
@apply text-lg font-bold;
}
+}
+
+@layer components {
+ .btn {
+ @apply bg-blue-500 text-white font-bold py-2 px-4 rounded;
+ }
+ .card {
+ @apply bg-white shadow-md rounded-lg p-4;
+ .card-title {
+ @apply text-lg font-bold;
+ }
+ .card-subtitle {
+ @apply text-sm text-gray-500;
+ }
+ .card-description {
+ @apply text-gray-700 break-words;
+ }
+ .card-code {
+ @apply bg-gray-100 p-2;
+ }
+ .card-body {
+ @apply text-gray-700;
+ }
+ .card-footer {
+ @apply text-sm text-gray-500;
+ }
+ }
+}
+
+@layer utilities {
.layout-menu {
@apply bg-gray-900 p-2 text-white flex justify-between h-12;
}
+ .bg-custom-blue {
+ background-color: #3490dc;
+ }
}
diff --git a/src/hooks/index.ts b/src/hooks/index.ts
new file mode 100644
index 0000000..f545587
--- /dev/null
+++ b/src/hooks/index.ts
@@ -0,0 +1 @@
+export * from './message';
diff --git a/src/hooks/message.tsx b/src/hooks/message.tsx
new file mode 100644
index 0000000..57af24a
--- /dev/null
+++ b/src/hooks/message.tsx
@@ -0,0 +1,22 @@
+import { App } from 'antd';
+
+export const useMessage = () => {
+ const { message: antMessage, modal, notification } = App.useApp();
+ return {
+ success: antMessage.success,
+ error: antMessage.error,
+ warning: antMessage.warning,
+ info: antMessage.info,
+ loading: antMessage.loading,
+ open: antMessage.open,
+ destroy: antMessage.destroy,
+ modal: modal,
+ notification: notification,
+ message: antMessage,
+ com: (
+ <>
+
+ >
+ ),
+ };
+};
diff --git a/src/pages/ai-chat/AiModule.tsx b/src/pages/ai-chat/AiModule.tsx
index 3e3854c..3026ced 100644
--- a/src/pages/ai-chat/AiModule.tsx
+++ b/src/pages/ai-chat/AiModule.tsx
@@ -1,36 +1,110 @@
import { useShallow } from 'zustand/react/shallow';
import { useAiStore } from './store/ai-store';
import { CloseOutlined } from '@ant-design/icons';
-import { Button } from 'antd';
+import { Button, Form, Input } from 'antd';
+import { useEffect } from 'react';
+import { TextArea } from '../container/components/TextArea';
+import clsx from 'clsx';
+import { marked } from 'marked';
export const AiMoudle = () => {
+ const [form] = Form.useForm();
const aiStore = useAiStore(
useShallow((state) => {
return {
open: state.open,
setOpen: state.setOpen,
runAi: state.runAi,
+ formData: state.formData,
+ setFormData: state.setFormData,
+ messages: state.messages,
+ setMessages: state.setMessage,
};
}),
);
- if (!aiStore.open) {
- return null;
- }
+
+ useEffect(() => {
+ if (!aiStore.open) {
+ return;
+ }
+ const isNull = JSON.stringify(aiStore.formData) === '{}';
+ if (!isNull) {
+ form.setFieldsValue(aiStore.formData);
+ } else {
+ form.setFieldsValue({ inputs: [] });
+ }
+ }, [aiStore.open, aiStore.formData]);
+ useEffect(() => {
+ if (!aiStore.open) {
+ aiStore.setMessages([]);
+ }
+ }, [aiStore.open]);
+ const onSend = () => {
+ const data = form.getFieldsValue();
+ aiStore.setFormData(data);
+ aiStore.runAi();
+ };
return (
-
+
Ai Moudle
-
-
chat message
-
-
+
+
+
chat message
+ {aiStore?.messages?.map((message, index) => {
+ const html = marked.parse(message?.content);
+ return (
+
+ );
+ })}
+
+
diff --git a/src/pages/ai-chat/store/ai-store.ts b/src/pages/ai-chat/store/ai-store.ts
index c473b48..87d1ceb 100644
--- a/src/pages/ai-chat/store/ai-store.ts
+++ b/src/pages/ai-chat/store/ai-store.ts
@@ -1,6 +1,19 @@
import { query } from '@/modules';
import { message } from 'antd';
import { create } from 'zustand';
+type ResData = {
+ created_at: string;
+ done?: boolean;
+ done_reason?: string;
+ eval_count?: number;
+ eval_duration?: number;
+ load_duration?: number;
+ message?: { role?: string; content?: string }[];
+ model?: string;
+ prompt_eval_count?: number;
+ prompt_eval_duration?: number;
+ total_duration?: number;
+};
export type AiStore = {
open: boolean;
@@ -12,9 +25,13 @@ export type AiStore = {
sendMsg: (msg: string) => void;
formData: any;
setFormData: (data: any) => void;
+ data: any;
+ setData: (data: any) => void;
runAi: () => any;
title: string;
setTitle: (title: string) => void;
+ messages: { role: string; content: string }[];
+ setMessage: (message: { role: string; content: string }[]) => void;
};
export const useAiStore = create
((set, get) => {
@@ -28,31 +45,60 @@ export const useAiStore = create((set, get) => {
},
formData: {},
setFormData: (data) => set({ formData: data }),
+ data: {},
+ setData: (data) => {
+ const { key, presetData = {} } = data;
+ console.log('key', presetData, data);
+ if (!key) {
+ console.error('key is required');
+ return;
+ }
+ const { inputs = [] } = presetData.data || {};
+ const formData = {
+ key,
+ inputs: inputs.map((input) => {
+ return {
+ key: input.key,
+ value: input.value,
+ type: 'string',
+ };
+ }),
+ messages: [],
+ };
+ console.log('formData', formData);
+ set({ key, data, formData });
+ },
runAi: async () => {
- const { formData } = get();
+ const { formData, messages } = get();
+ const loading = message.loading('loading');
const res = await query.post({
path: 'ai',
key: 'run',
data: {
key: formData.key,
inputs: [
- {
- key: 'title',
- value: '根据描述生成代码',
- },
- {
- key: 'description',
- value: '我想获取一个card, 包含标题和内容,标题是evision,内容是这是一个测试',
- },
+ // {
+ // key: 'title',
+ // value: '根据描述生成代码',
+ // },
+ // {
+ // key: 'description',
+ // value: '我想获取一个card, 包含标题和内容,标题是evision,内容是这是一个测试',
+ // },
+ ...formData.inputs,
],
},
});
+ loading();
if (res.code === 200) {
console.log(res.data);
message.success('Success');
+ set({ messages: [...messages, res.data.message] });
}
},
title: '',
setTitle: (title) => set({ title }),
+ messages: [],
+ setMessage: (messages) => set({ messages }),
};
});
diff --git a/src/pages/code-editor/index.tsx b/src/pages/code-editor/index.tsx
index 86d71a1..cd4a73f 100644
--- a/src/pages/code-editor/index.tsx
+++ b/src/pages/code-editor/index.tsx
@@ -4,13 +4,15 @@ import { useLocation, useNavigate } from 'react-router';
import { useCodeEditorStore, ParseData } from './store';
import { useToCodeEditor } from './hooks/use-to-code-editor';
export { useToCodeEditor };
-import { Button, message } from 'antd';
+import { Button, message, Tooltip } from 'antd';
+import { LeftOutlined, SaveOutlined } from '@ant-design/icons';
export const App = () => {
const ref = useRef(null);
const editorRef = useRef(null);
const location = useLocation();
const store = useCodeEditorStore();
const [mounted, setMounted] = useState(false);
+ const navigator = useNavigate();
useEffect(() => {
initEditor();
const state = location.state as ParseData;
@@ -63,12 +65,27 @@ export const App = () => {
}
store.onUpdate(value);
}, [store.dataType]);
+
return (
-
-
Code Editor
-
-
-
+
+
Code Editor
+
+
+
+ }
+ onClick={() => {
+ navigator(-1);
+ }}>
+
+
+ }>
+
+
+
);
diff --git a/src/pages/container/edit/List.tsx b/src/pages/container/edit/List.tsx
index b51b4a0..d3118b9 100644
--- a/src/pages/container/edit/List.tsx
+++ b/src/pages/container/edit/List.tsx
@@ -58,6 +58,9 @@ const FormModal = () => {
+
+
+
@@ -127,7 +130,7 @@ export const ContainerList = () => {
}}>
{item.title || '-'}
-
{item.description ? item.description : '-'}
+
{item.description ? item.description : '-'}
diff --git a/src/pages/container/index.tsx b/src/pages/container/index.tsx
index 3b3387f..b74f56a 100644
--- a/src/pages/container/index.tsx
+++ b/src/pages/container/index.tsx
@@ -9,9 +9,10 @@ export const App = () => {
}>
} />
} />
- Home
} />
} />
);
};
+
+export * from './module/Select';
diff --git a/src/pages/container/module/Select.tsx b/src/pages/container/module/Select.tsx
new file mode 100644
index 0000000..f701d51
--- /dev/null
+++ b/src/pages/container/module/Select.tsx
@@ -0,0 +1,39 @@
+import { query } from '@/modules';
+import { Select as AntSelect, message, SelectProps } from 'antd';
+import { useEffect, useState } from 'react';
+
+export const Select = (props: SelectProps) => {
+ const [options, setOptions] = useState<{ value: string; id: string }[]>([]);
+ useEffect(() => {
+ fetch();
+ }, []);
+ const fetch = async () => {
+ const res = await query.post({
+ path: 'container',
+ key: 'list',
+ });
+ if (res.code !== 200) {
+ message.error(res.message || '获取容器列表失败');
+ return;
+ }
+ const data = res.data || [];
+ setOptions(
+ data.map((item: any) => {
+ return {
+ label: item.title,
+ value: item.id,
+ };
+ }),
+ );
+ };
+ return (
+ {
+ // const labelValue = options.find((item) => item.value === e);
+ // props.onChange?.(e, options);
+ // }}
+ />
+ );
+};
diff --git a/src/pages/map/index.tsx b/src/pages/map/index.tsx
index 334e1d2..205fcad 100644
--- a/src/pages/map/index.tsx
+++ b/src/pages/map/index.tsx
@@ -1,5 +1,68 @@
-export const App = () => {
- const serverList = ['container', 'panel', 'publish', 'code-editor', 'map'];
+import clsx from 'clsx';
+import { useNavigate } from 'react-router-dom';
+const serverList = ['container', 'panel', 'publish', 'code-editor', 'map', 'ai-chat'];
+const serverPath = [
+ {
+ path: 'container',
+ links: ['edit/list', 'preview/:id', 'edit/:id'],
+ },
+ {
+ path: 'panel',
+ links: ['edit/list', 'flow/:id', 'deck/:id'],
+ },
+ {
+ path: 'publish',
+ links: ['edit/list'],
+ },
+ {
+ path: 'map',
+ links: ['/'],
+ },
+ {
+ path: 'ai-chat',
+ links: ['/'],
+ },
+];
+const ServerPath = () => {
+ const navigate = useNavigate();
+ return (
+
+
Map
+
+ {serverPath.map((item) => {
+ const links = item.links.map((link) => {
+ const hasId = link.includes(':id');
+ const _path = link === '/' ? item.path : item.path + '/' + link;
+ return (
+
{
+ if (hasId) {
+ return;
+ }
+ if (link) {
+ navigate(`/${item.path}/${link}`);
+ } else {
+ navigate(`/${item.path}`);
+ }
+ }}>
+
{_path}
+
+ );
+ });
+ return (
+
+ {links}
+
+ );
+ })}
+
+
+ );
+};
+export const App = ServerPath;
+export const ServerList = () => {
return (
diff --git a/src/pages/panel/edit/List.tsx b/src/pages/panel/edit/List.tsx
index a6edbf6..8055f1f 100644
--- a/src/pages/panel/edit/List.tsx
+++ b/src/pages/panel/edit/List.tsx
@@ -1,12 +1,14 @@
import { useEditStore } from '../store';
-import { Button, Input, message, Modal, Table } from 'antd';
+import { Button, Input, message, Modal, Table, Tooltip } from 'antd';
import { useEffect, useState } from 'react';
import { useShallow } from 'zustand/react/shallow';
import { Form } from 'antd';
import copy from 'copy-to-clipboard';
import { useNavigate } from 'react-router';
import { useToCodeEditor } from '@/pages/code-editor';
-
+import { CardBlank } from '@/components/card/CardBlank';
+import { DeleteOutlined, EditOutlined, ForkOutlined, GoldOutlined, PlusOutlined, ToolOutlined } from '@ant-design/icons';
+import { isObjectNull } from '@/utils/is-null';
const FormModal = () => {
const [form] = Form.useForm();
const editStore = useEditStore(
@@ -15,6 +17,7 @@ const FormModal = () => {
showEdit: state.showEditModal,
setShowEdit: state.setShowEditModal,
formData: state.formData,
+ setFormData: state.setFormData,
updateData: state.updateData,
};
}),
@@ -22,17 +25,26 @@ const FormModal = () => {
useEffect(() => {
const open = editStore.showEdit;
if (open) {
- form.setFieldsValue(editStore.formData || {});
- } else {
- form.resetFields();
+ if (isObjectNull(editStore.formData.data)) {
+ form.setFieldsValue({});
+ } else form.setFieldsValue(editStore.formData);
}
}, [editStore.showEdit]);
const onFinish = async (values: any) => {
+ let defaultData = {
+ nodes: [],
+ edges: [],
+ viewport: {},
+ };
+ if (!isEdit) {
+ values.data = defaultData;
+ }
editStore.updateData(values);
};
const onClose = () => {
editStore.setShowEdit(false);
form.resetFields();
+ editStore.setFormData({});
};
const isEdit = editStore.formData.id;
return (
@@ -52,9 +64,15 @@ const FormModal = () => {
- {/*
-
- */}
+
+
+
+
+
+
+
+
+