add prompt js page

This commit is contained in:
xion 2024-09-25 08:56:20 +08:00
parent d53c18606e
commit 3da62fd254
26 changed files with 436 additions and 115 deletions

View File

@ -4,6 +4,9 @@ import { App as ContainerApp } from './pages/container';
import { App as PanelApp } from './pages/panel';
import { App as PublishApp } from './pages/publish';
import { App as CodeEditorApp } from './pages/code-editor';
import { App as MapApp } from './pages/map';
import { App as PromptApp } from './pages/prompt';
import '@abearxiong/container/dist/container.css';
export const App = () => {
@ -20,6 +23,8 @@ export const App = () => {
<Route path='/panel/*' element={<PanelApp />} />
<Route path='/publish/*' element={<PublishApp />} />
<Route path='/code-editor' element={<CodeEditorApp />} />
<Route path='/map/*' element={<MapApp />} />
<Route path='/prompt/*' element={<PromptApp />} />
<Route path='/404' element={<div>404</div>} />
<Route path='*' element={<div>404</div>} />
</Routes>

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,64 @@
/* #### Generated By: http://www.cufonfonts.com #### */
@font-face {
font-family: 'D-DIN';
font-style: normal;
font-weight: normal;
src: url('D-DIN.woff') format('woff');
}
@font-face {
font-family: 'D-DIN-Italic';
font-style: italic;
font-weight: normal;
src: url('D-DIN-Italic.woff') format('woff');
}
@font-face {
font-family: 'D-DIN-Bold';
font-style: normal;
font-weight: bold;
src: url('D-DIN-Bold.woff') format('woff');
}
@font-face {
font-family: 'D-DIN Condensed';
font-style: normal;
font-weight: normal;
src: url('D-DINCondensed.woff') format('woff');
}
@font-face {
font-family: 'D-DIN Exp';
font-style: normal;
font-weight: normal;
src: url('D-DINExp.woff') format('woff');
}
@font-face {
font-family: 'D-DIN Exp-Italic';
font-style: italic;
font-weight: normal;
src: url('D-DINExp-Italic.woff') format('woff');
}
@font-face {
font-family: 'D-DIN Condensed-Bold';
font-style: normal;
font-weight: bold;
src: url('D-DINCondensed-Bold.woff') format('woff');
}
@font-face {
font-family: 'D-DIN Exp-Bold';
font-style: normal;
font-weight: bold;
src: url('D-DINExp-Bold.woff') format('woff');
}

View File

@ -0,0 +1,16 @@
/* #### Generated By: http://www.cufonfonts.com #### */
@font-face {
font-family: 'Montserrat';
font-style: normal;
font-weight: 100 900;
src: url('Montserrat-VariableFont_wght.ttf') format('truetype');
}
@font-face {
font-family: 'Montserrat-Italic';
font-style: italic;
font-weight: 100 900;
src: url('Montserrat-Italic-VariableFont_wght.ttf') format('truetype');
}

View File

@ -0,0 +1,6 @@
@font-face {
font-family: 'Orbitron';
font-style: normal;
font-weight: 400 900;
src: url('Orbitron-VariableFont_wght.ttf') format('truetype');
}

View File

@ -0,0 +1,3 @@
@import "./D-DIN/font.css";
@import "./Montserrat/font.css";
@import "./Orbitron/font.css";

View File

@ -1,3 +1,5 @@
@import './fonts/font.css';
.scrollbar {
/* 整个滚动条 */
&::-webkit-scrollbar {
@ -14,7 +16,7 @@
/* 滚动条滑块(竖向:vertical 横向:horizontal) */
&::-webkit-scrollbar-thumb {
cursor: pointer;
background-color: black;
background-color: #c1c1c1;
border-radius: 5px;
}

View File

@ -12,4 +12,7 @@
h3 {
@apply text-lg font-bold;
}
.layout-menu {
@apply bg-gray-900 p-2 text-white flex justify-between h-12;
}
}

View File

@ -9,4 +9,35 @@ body {
#root {
width: 100%;
height: 100%;
}
.w-tc-editor, .w-tc-editor > textarea {
/* 整个滚动条 */
&::-webkit-scrollbar {
width: 3px;
height: 3px;
}
/* 滚动条有滑块的轨道部分 */
&::-webkit-scrollbar-track-piece {
background-color: transparent;
border-radius: 1px;
}
/* 滚动条滑块(竖向:vertical 横向:horizontal) */
&::-webkit-scrollbar-thumb {
cursor: pointer;
background-color: #c1c1c1;
border-radius: 5px;
}
/* 滚动条滑块hover */
&::-webkit-scrollbar-thumb:hover {
background-color: #999999;
}
/* 同时有垂直和水平滚动条时交汇的部分 */
&::-webkit-scrollbar-corner {
display: block; /* 修复交汇时出现的白块 */
}
}

View File

@ -1,4 +1,5 @@
import CodeEditor from '@uiw/react-textarea-code-editor';
import { useEffect, useState } from 'react';
import { clsx } from 'clsx';
@ -7,29 +8,35 @@ type Props = {
onChange?: (data: string) => void;
readonly?: boolean;
className?: string;
style?: React.CSSProperties;
language?: string;
listen?: boolean;
};
export const TextArea = (props: Props) => {
const [code, setCode] = useState<string>('');
useEffect(() => {
setCode(props.value || '');
}, []);
}, [props.value]);
const _onChange = (value: string) => {
setCode(value);
props.onChange && props.onChange(value);
if (props.onChange) {
props.onChange(value);
}
};
return (
<div className={clsx('min-h-16 max-h-52 overflow-scroll p-1 ', props.className)}>
<div className={clsx('min-h-16 max-h-52 overflow-scroll scrollbar p-1 ', props.className)}>
<CodeEditor
value={code}
language='js'
className='border rounded-sm'
className='border rounded-sm '
readOnly={props.readonly}
placeholder='Please enter JS code.'
onChange={(evn) => _onChange(evn.target.value)}
padding={10}
style={{
backgroundColor: '#f5f5f5',
// backgroundColor: '#f5f5f5',
fontFamily: 'ui-monospace,SFMono-Regular,SF Mono,Consolas,Liberation Mono,Menlo,monospace',
...props.style,
}}
/>
</div>

View File

@ -1,11 +1,13 @@
import { Button, Input, message, Modal, Table } from 'antd';
import { useEffect, useState } from 'react';
import { Fragment, useEffect, useMemo, useState } from 'react';
import { TextArea } from '../components/TextArea';
import { useContainerStore } from '../store';
import { useShallow } from 'zustand/react/shallow';
import { Form } from 'antd';
import copy from 'copy-to-clipboard';
import { useNavigate } from 'react-router';
import { EditOutlined, SettingOutlined, LinkOutlined, SaveOutlined, DeleteOutlined, LeftOutlined } from '@ant-design/icons';
import clsx from 'clsx';
const FormModal = () => {
const [form] = Form.useForm();
const containerStore = useContainerStore(
@ -22,8 +24,6 @@ const FormModal = () => {
const open = containerStore.showEdit;
if (open) {
form.setFieldsValue(containerStore.formData || {});
} else {
form.resetFields();
}
}, [containerStore.showEdit]);
const onFinish = async (values: any) => {
@ -59,7 +59,12 @@ const FormModal = () => {
<Input />
</Form.Item>
<Form.Item name='code' label='code'>
<TextArea value={containerStore.formData.code} />
<TextArea
value={containerStore.formData.code}
style={{
height: '200px',
}}
/>
</Form.Item>
<Form.Item label=' ' colon={false}>
<Button type='primary' htmlType='submit'>
@ -85,111 +90,122 @@ export const ContainerList = () => {
getList: state.getList,
loading: state.loading,
publishData: state.publishData,
updateData: state.updateData,
formData: state.formData,
};
}),
);
const [codeEdit, setCodeEdit] = useState(false);
const [code, setCode] = useState('');
useEffect(() => {
containerStore.getList();
}, []);
const columns = [
{
title: 'ID',
dataIndex: 'id',
render: (text: string) => {
return (
<div
className='w-40 truncate cursor-pointer'
title={text}
onClick={() => {
copy(text);
message.success('copy success');
}}>
{text}
</div>
);
},
},
{
title: 'Title',
dataIndex: 'title',
},
{
title: 'Code',
dataIndex: 'code',
width: '40%',
render: (text: string, record: any) => {
return <TextArea value={text} readonly />;
},
},
{
title: 'Source',
dataIndex: 'source',
},
{
title: 'Operation',
dataIndex: 'operation',
render: (text: string, record: any) => {
return (
<div className='flex gap-2'>
<Button
type='primary'
onClick={() => {
containerStore.setFormData(record);
containerStore.setShowEdit(true);
}}>
Edit
</Button>
<Button
onClick={() => {
navicate('/container/preview/' + record.id);
}}>
Preview
</Button>
<Button
onClick={() => {
containerStore.publishData(record);
}}>
Publish
</Button>
<Button
danger
onClick={() => {
containerStore.deleteData(record.id);
}}>
Delete
</Button>
</div>
);
},
},
];
const onAdd = () => {
containerStore.setFormData({});
containerStore.setShowEdit(true);
};
return (
<div className='w-full h-full flex flex-col'>
<div className='mb-2 w-full p-2 bg-white rounded-lg'>
<Button
className='w-20 '
type='primary'
onClick={() => {
containerStore.setFormData({});
containerStore.setShowEdit(true);
}}>
Add
</Button>
<div className='flex flex-grow overflow-hidden h-full'>
<div className='flex-grow overflow-auto scrollbar bg-gray-100'>
<div className='flex flex-wrap gap-x-10 gap-y-4 rounded pt-10 justify-center'>
{containerStore.list.length > 0 &&
containerStore.list.map((item) => {
return (
<Fragment key={item.id}>
<div
className='flex text-sm gap flex-col w-[400px] max-h-[400px] bg-white p-4 rounded-lg'
key={item.id}
onClick={() => {
setCode(item.code);
containerStore.setFormData(item);
setCodeEdit(true);
}}>
<div className='px-4 cursor-pointer'>
<div
className='font-bold'
onClick={(e) => {
copy(item.code);
e.stopPropagation();
message.success('copy code success');
}}>
{item.title || '-'}
</div>
<div className='font-light'>{item.description ? item.description : '-'}</div>
</div>
<div className='w-full text-xs'>
<TextArea className='max-h-[240px] scrollbar' value={item.code} readonly />
</div>
<div className='flex mt-2 '>
<Button.Group>
<Button onClick={() => containerStore.publishData(item)} icon={<SettingOutlined />}></Button>
<Button
onClick={(e) => {
containerStore.setFormData(item);
containerStore.setShowEdit(true);
setCodeEdit(false);
e.stopPropagation();
}}
icon={<EditOutlined />}></Button>
<Button
onClick={(e) => {
// navicate('/container/preview/' + item.id);
window.open('/container/preview/' + item.id);
e.stopPropagation();
}}
icon={<LinkOutlined />}></Button>
<Button
onClick={(e) => {
containerStore.deleteData(item.id);
e.stopPropagation();
}}
icon={<DeleteOutlined />}></Button>
</Button.Group>
</div>
</div>
</Fragment>
);
})}
{new Array(4).fill(0).map((_, index) => {
return <div key={index} className='w-[400px]'></div>;
})}
{containerStore.list.length == 0 && (
<div className='text-center' key={'no-data'}>
No Data
</div>
)}
</div>
</div>
<div className={clsx('bg-gray-100 border-l border-bg-slate-300 w-[600px] flex-shrink-0', !codeEdit && 'hidden')}>
<div className='bg-white p-2'>
<div className='mt-2 ml-2 flex gap-2'>
<Button
onClick={() => {
setCodeEdit(false);
containerStore.setFormData({});
}}
icon={<LeftOutlined />}></Button>
<Button
onClick={() => {
console.log('save', containerStore.formData);
containerStore.updateData({ ...containerStore.formData, code });
}}
icon={<SaveOutlined />}></Button>
</div>
</div>
<div className='h-[94%] p-2 rounded-2 shadow-sm'>
<TextArea
value={code}
onChange={(value) => {
setCode(value);
}}
className='h-full max-h-full scrollbar'
style={{ overflow: 'auto' }}
/>
</div>
</div>
</div>
<div className='flex-grow overflow-scroll'>
<Table
pagination={false}
scroll={{
y: 600,
}}
loading={containerStore.loading}
dataSource={containerStore.list}
rowKey='id'
columns={columns}
/>
</div>
<div className='h-2'></div>
<FormModal />
</div>
);

View File

@ -1,17 +1,17 @@
import { Navigate, Route, Routes } from 'react-router-dom';
import { ContainerList } from './edit/List';
import { Main } from './layouts';
import { Preview } from './preview';
import { Preview, PreviewWrapper } from './preview';
export const App = () => {
return (
<Routes>
<Route element={<Main />}>
<Route path='/' element={<Navigate to='/container/edit/list' />}></Route>
<Route path='edit/list' element={<ContainerList />} />
<Route path='preview/:id' element={<Preview />} />
<Route path='preview/:id/wrapper' element={<PreviewWrapper />} />
<Route path='/' element={<div>Home</div>} />
</Route>
<Route path='preview/:id' element={<Preview />} />
</Routes>
);
};

View File

@ -1,15 +1,41 @@
import { Outlet } from 'react-router';
import { PlusOutlined } from '@ant-design/icons';
import { Button } from 'antd';
import { Outlet, useLocation } from 'react-router';
import { useContainerStore } from '../store';
import { useEffect } from 'react';
import { useShallow } from 'zustand/react/shallow';
export const Main = () => {
const containerStore = useContainerStore(
useShallow((state) => {
return {
setFormData: state.setFormData,
setShowEdit: state.setShowEdit,
};
}),
);
const location = useLocation();
const isEdit = location.pathname.includes('edit/list');
return (
<div className='flex w-full h-full flex-col bg-gray-200'>
<div className='h-12 bg-white p-2 mb-2'>Container</div>
<div className='flex w-full h-full flex-col'>
<div className='layout-menu'>
Container
<Button
className={!isEdit ? 'hidden' : ''}
icon={<PlusOutlined />}
onClick={() => {
console.log('add');
containerStore.setFormData({});
containerStore.setShowEdit(true);
}}
/>
</div>
<div
className='flex'
style={{
height: 'calc(100vh - 4rem)',
height: 'calc(100vh - 3rem)',
}}>
<div className='flex-grow overflow-hidden mx-2'>
<div className='flex-grow overflow-hidden'>
<div className='w-full h-full rounded-lg'>
<Outlet />
</div>

View File

@ -57,6 +57,75 @@ export const Preview = () => {
const containerRef = useRef<Container | null>(null);
const [data, setData] = useState<any>({});
useEffect(() => {
if (!id) return;
fetch();
}, []);
const fetch = async () => {
const res = await query.post({
path: 'container',
key: 'get',
id,
});
if (res.code === 200) {
const data = res.data;
// setData([data]);
console.log('data', data);
const code = {
id: data.id,
title: data.title,
codeId: data.id,
code: data.code,
data: data.data,
};
init([code]);
} else {
message.error(res.msg || 'Failed to fetch data');
}
};
const refresh = (data: any) => {
if (!data.id) return;
const code = {
id: data.id,
title: data.title,
codeId: data.id,
code: data.code,
data: data.data,
hash: '',
};
init([code], true);
};
useListener(id, { refresh: refresh });
const init = async (data: any[], replace: boolean = false) => {
// console.log('data', data, ref.current);
if (containerRef.current) {
const container = containerRef.current;
console.log('data', data, container.data);
container.updateData(data);
await new Promise((resolve) => {
setTimeout(resolve, 2000);
});
console.log('update---data', data, container.data);
container.destroy(id!);
container.renderId(id!);
return;
}
console.log('data', containerRef.current);
const container = new Container({
root: ref.current!,
data: data as any,
showChild: false,
});
container.renderId(id!);
containerRef.current = container;
};
return <div className='mx-auto border bg-gray-200 h-full w-full' ref={ref}></div>;
};
export const PreviewWrapper = () => {
const params = useParams<{ id: string }>();
const id = params.id;
const ref = useRef<HTMLDivElement>(null);
const containerRef = useRef<Container | null>(null);
useEffect(() => {
if (!id) return;
fetch();
@ -121,13 +190,13 @@ export const Preview = () => {
};
return (
<div className='w-full h-full bg-gray-200'>
<div className='text-center mb-10 font-bold text-4xl mt-4 '>Preview</div>
<div className='text-center mb-4 pt-2 font-bold text-4xl '>Preview</div>
<div
className='flex '
style={{
height: 'calc(100% - 32px)',
}}>
<div className='mx-auto border bg-white h-h-full w-[80%] h-[80%]' ref={ref}></div>
<div className='mx-auto border bg-white h-h-full w-[80%] h-[80%] overflow-y-auto' ref={ref}></div>
</div>
</div>
);

16
src/pages/map/index.tsx Normal file
View File

@ -0,0 +1,16 @@
export const App = () => {
const serverList = ['container', 'panel', 'publish', 'code-editor', 'map'];
return (
<div className='p-2 w-full h-full bg-gray-200'>
<div className='flex flex-col w-1/2 m-auto bg-white p-4 border rounded-md shadow-md'>
{serverList.map((item) => {
return (
<div key={item} className='flex flex-col'>
<div>{item}</div>
</div>
);
})}
</div>
</div>
);
};

View File

@ -0,0 +1,57 @@
import { Button, Form, Input } from 'antd';
import { TextArea } from '../container/components/TextArea';
import clsx from 'clsx';
export const App = () => {
const [form] = Form.useForm();
const onFinish = (values: any) => {
console.log('Success:', values);
};
const onSave = () => {
//
};
const isEdit = form.getFieldValue('id');
return (
<div className='w-full h-full felx flex-col bg-gray-200'>
<h1 className='text-center py-4'>Prompt JS Code Generate</h1>
<div className='py-2 px-4 w-3/4 min-w-[600px] mx-auto border shadow rounded bg-white'>
<Form form={form} onFinish={onFinish} className='mt-4' labelCol={{ span: 4 }}>
<Form.Item name='id' hidden>
<Input />
</Form.Item>
<Form.Item name='title' label='Title'>
<Input />
</Form.Item>
<Form.Item name='description' label='Description'>
<Input.TextArea rows={4} />
</Form.Item>
<Form.Item name='code' label='Code'>
<TextArea className='max-h-full' style={{ minHeight: 300 }} />
</Form.Item>
<Form.Item label=' ' colon={false}>
<div className='flex gap-2'>
<Button type='primary' htmlType='submit'>
Generate
</Button>
<Button htmlType='reset'>Reset</Button>
<Button
type='primary'
onClick={() => {
//
}}>
Save
</Button>
<Button
className={clsx(isEdit ? 'block' : 'hidden')}
onClick={() => {
//
}}>
Preview
</Button>
</div>
</Form.Item>
</Form>
</div>
</div>
);
};