generated from template/astro-template
temp
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { DragModal, DragModalTitle, getComputedHeight } from '@/components/a/drag-modal/index.tsx';
|
||||
import { DragModal, DragModalTitle, getComputedHeight, useDragSize } from '@/components/a/drag-modal/index.tsx';
|
||||
import { useChatStore } from '../store';
|
||||
import { useShallow } from 'zustand/shallow';
|
||||
import { Button } from '@/components/a/button.tsx';
|
||||
@@ -68,6 +68,7 @@ export const ChatContextDialog = () => {
|
||||
setShowContext(false);
|
||||
};
|
||||
const [role, setRole] = useState<string>('user');
|
||||
const dragSize = useDragSize();
|
||||
return (
|
||||
<DragModal
|
||||
focus={modelId === 'chat-context'}
|
||||
@@ -111,14 +112,8 @@ export const ChatContextDialog = () => {
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
defaultSize={{
|
||||
width: 600,
|
||||
height: 400,
|
||||
}}
|
||||
style={{
|
||||
left: computedHeight.width / 2 - 300,
|
||||
top: computedHeight.height / 2 - 200,
|
||||
}}
|
||||
defaultSize={dragSize.defaultSize}
|
||||
style={dragSize.style}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { DragModal, DragModalTitle, getComputedHeight } from '@/components/a/drag-modal/index.tsx';
|
||||
import { DragModal, DragModalTitle, getComputedHeight, useDragSize } from '@/components/a/drag-modal/index.tsx';
|
||||
import { useChatStore } from '../store';
|
||||
import { useShallow } from 'zustand/shallow';
|
||||
import { ChatCopyList } from './List';
|
||||
@@ -16,6 +16,7 @@ export const ChatCopyDialog = () => {
|
||||
if (!store.showCopy) {
|
||||
return null;
|
||||
}
|
||||
const dragSize = useDragSize();
|
||||
return (
|
||||
<DragModal
|
||||
focus={store.modelId === 'chat-copy'}
|
||||
@@ -33,14 +34,8 @@ export const ChatCopyDialog = () => {
|
||||
<ChatCopyList />
|
||||
</div>
|
||||
}
|
||||
defaultSize={{
|
||||
width: 600,
|
||||
height: 400,
|
||||
}}
|
||||
style={{
|
||||
left: computedHeight.width / 2 - 300,
|
||||
top: computedHeight.height / 2 - 200,
|
||||
}}
|
||||
defaultSize={dragSize.defaultSize}
|
||||
style={dragSize.style}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
import { DragModal, DragModalTitle, getComputedHeight } from '@/components/a/drag-modal/index.tsx';
|
||||
import { DragModal, DragModalTitle, getComputedHeight, useDragSize } from '@/components/a/drag-modal/index.tsx';
|
||||
import { useChatStore } from '../store';
|
||||
import { useShallow } from 'zustand/shallow';
|
||||
import { ChatHistoryList } from './List';
|
||||
|
||||
export const ChatHistoryDialog = ({ storeId }: { storeId: string }) => {
|
||||
const { showList, setShowList, setModelId, modelId } = useChatStore(
|
||||
useShallow((state) => ({ showList: state.showList, setShowList: state.setShowList, setModelId: state.setModelId, modelId: state.modelId })),
|
||||
useShallow((state) => ({ showList: state.showList, setShowList: state.setShowList, setModelId: state.setModelId, modelId: state.modelId })),
|
||||
);
|
||||
const computedHeight = getComputedHeight();
|
||||
if (!showList) {
|
||||
return null;
|
||||
}
|
||||
const dragSize = useDragSize();
|
||||
return (
|
||||
<DragModal
|
||||
focus={modelId === 'chat-history'}
|
||||
@@ -28,14 +29,7 @@ export const ChatHistoryDialog = ({ storeId }: { storeId: string }) => {
|
||||
<ChatHistoryList storeId={storeId} />
|
||||
</div>
|
||||
}
|
||||
defaultSize={{
|
||||
width: 600,
|
||||
height: 400,
|
||||
}}
|
||||
style={{
|
||||
left: computedHeight.width / 2 - 300,
|
||||
top: computedHeight.height / 2 - 200,
|
||||
}}
|
||||
{...dragSize}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { DragModal, DragModalTitle, getComputedHeight } from '@/components/a/drag-modal/index.tsx';
|
||||
import { DragModal, DragModalTitle, getComputedHeight, useDragSize } from '@/components/a/drag-modal/index.tsx';
|
||||
import { useChatStore } from '../store';
|
||||
import { useShallow } from 'zustand/shallow';
|
||||
import { ChatSettingList } from './List';
|
||||
@@ -16,6 +16,7 @@ export const ChatModelSettingDialog = () => {
|
||||
if (!showChatSetting) {
|
||||
return null;
|
||||
}
|
||||
const dragSize = useDragSize();
|
||||
return (
|
||||
<DragModal
|
||||
focus={modelId === 'chat-model-setting'}
|
||||
@@ -33,14 +34,7 @@ export const ChatModelSettingDialog = () => {
|
||||
<ChatSettingList />
|
||||
</div>
|
||||
}
|
||||
defaultSize={{
|
||||
width: 600,
|
||||
height: 400,
|
||||
}}
|
||||
style={{
|
||||
left: computedHeight.width / 2 - 300,
|
||||
top: computedHeight.height / 2 - 200,
|
||||
}}
|
||||
{...dragSize}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { DragModal, DragModalTitle, getComputedHeight } from '@/components/a/drag-modal/index.tsx';
|
||||
import { DragModal, DragModalTitle, getComputedHeight, useDragSize } from '@/components/a/drag-modal/index.tsx';
|
||||
import { useChatStore } from '../store';
|
||||
import { useShallow } from 'zustand/shallow';
|
||||
import { ChatSettingList } from './List';
|
||||
@@ -16,6 +16,7 @@ export const ChatSettingDialog = () => {
|
||||
if (!showSetting) {
|
||||
return null;
|
||||
}
|
||||
const dragSize = useDragSize();
|
||||
return (
|
||||
<DragModal
|
||||
focus={modelId === 'chat-setting'}
|
||||
@@ -33,14 +34,7 @@ export const ChatSettingDialog = () => {
|
||||
<ChatSettingList />
|
||||
</div>
|
||||
}
|
||||
defaultSize={{
|
||||
width: 600,
|
||||
height: 400,
|
||||
}}
|
||||
style={{
|
||||
left: computedHeight.width / 2 - 300,
|
||||
top: computedHeight.height / 2 - 200,
|
||||
}}
|
||||
{...dragSize}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -75,7 +75,7 @@ export const Chat = ({ storeId }: { storeId: string }) => {
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className='w-full h-full relative'>
|
||||
<div className='w-full h-full relative flex flex-col lg:flex-row'>
|
||||
{!id && (
|
||||
<div className='w-full h-full flex flex-col justify-center items-center'>
|
||||
<ChatHistoryList storeId={storeId} />
|
||||
|
||||
@@ -41,7 +41,7 @@ export const ModelNav = () => {
|
||||
return (
|
||||
<div className='flex gap-2 items-center'>
|
||||
<div className='hidden lg:block font-bold text-gray-600'>提示词规划器</div>
|
||||
<div className='flex gap-2 items-center' onClick={() => setShowChatSetting(true)}>
|
||||
{/* <div className='flex gap-2 items-center' onClick={() => setShowChatSetting(true)}>
|
||||
{currentUserModel && (
|
||||
<Tooltip
|
||||
title={
|
||||
@@ -58,7 +58,7 @@ export const ModelNav = () => {
|
||||
</Tooltip>
|
||||
)}
|
||||
{!currentUserModel && <div className='text-sm text-gray-500 cursor-pointer'>Not Selected Model</div>}
|
||||
</div>
|
||||
</div> */}
|
||||
<div className='flex gap-2 items-center'>
|
||||
{/* <Tooltip title='设置'>
|
||||
<IconButton
|
||||
|
||||
18
src/apps/ai-editor/index.tsx
Normal file
18
src/apps/ai-editor/index.tsx
Normal file
@@ -0,0 +1,18 @@
|
||||
import { ToastProvider } from '@/modules/toast/Provider';
|
||||
|
||||
export const App = () => {
|
||||
return (
|
||||
<ToastProvider>
|
||||
<AIEditor />
|
||||
</ToastProvider>
|
||||
);
|
||||
};
|
||||
|
||||
export const AIEditor = () => {
|
||||
return (
|
||||
<div className='flex h-full w-full flex-col items-center justify-center'>
|
||||
<div className='text-2xl font-bold'>AI Editor</div>
|
||||
<div className='mt-4 text-gray-500'>This is a placeholder for the AI Editor application.</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -5,14 +5,35 @@ import { Tooltip } from '@/components/a/tooltip';
|
||||
export const queryMark = new QueryMark({ query: query, markType: 'md' });
|
||||
import Fuse from 'fuse.js';
|
||||
import { debounce } from 'lodash-es';
|
||||
import { SquareArrowOutUpRight } from 'lucide-react';
|
||||
import { Plus, SquareArrowOutUpRight } from 'lucide-react';
|
||||
import { Pencil, Trash2 } from 'lucide-react';
|
||||
import { Button } from '@/components/a/button';
|
||||
import { Confirm } from '@/components/a/confirm';
|
||||
import { toast } from 'react-toastify';
|
||||
import { ToastProvider } from '@/modules/toast/Provider';
|
||||
import { Modal } from '@/components/a/modal';
|
||||
import { useForm, Controller } from 'react-hook-form'; // 添加此行
|
||||
|
||||
export const App = () => {
|
||||
return (
|
||||
<ToastProvider>
|
||||
<AiMark />
|
||||
</ToastProvider>
|
||||
);
|
||||
};
|
||||
const AiMark = () => {
|
||||
const [list, setList] = useState<Mark[]>([]);
|
||||
const [searchTerm, setSearchTerm] = useState('');
|
||||
const [searchResults, setSearchResults] = useState<Mark[]>([]);
|
||||
|
||||
const fuseRef = useRef<Fuse<Mark>>(null);
|
||||
|
||||
const [open, setOpen] = useState(false);
|
||||
const [formData, setFormData] = useState<Mark>();
|
||||
const onOpenEdit = (data: Mark) => {
|
||||
setOpen(true);
|
||||
setFormData(data);
|
||||
};
|
||||
// 实际执行搜索的函数
|
||||
const performSearch = (term: string) => {
|
||||
if (!term.trim()) {
|
||||
@@ -47,6 +68,7 @@ export const App = () => {
|
||||
if (res.code === 200) {
|
||||
const list = res.data?.list || [];
|
||||
setList(list!);
|
||||
|
||||
const fuse = new Fuse(list, {
|
||||
keys: ['title', 'description', 'tags', 'summary'],
|
||||
});
|
||||
@@ -65,13 +87,23 @@ export const App = () => {
|
||||
const link = item.link;
|
||||
window.open(link);
|
||||
};
|
||||
|
||||
const refresh = () => {
|
||||
init();
|
||||
};
|
||||
// 确定要显示的列表:有搜索内容时显示搜索结果,否则显示全部
|
||||
const displayList = searchTerm.trim() ? searchResults : list;
|
||||
|
||||
return (
|
||||
<div className='w-full h-full overflow-auto'>
|
||||
<div className='sticky top-0 p-4 bg-white z-10 shadow-sm'>
|
||||
<div className='w-full h-full overflow-auto scrollbar'>
|
||||
<div className='sticky top-0 p-4 bg-white z-10 shadow-sm flex gap-2'>
|
||||
<Button
|
||||
size='icon'
|
||||
onClick={() => {
|
||||
setFormData(undefined);
|
||||
setOpen(true);
|
||||
}}>
|
||||
<Plus />
|
||||
</Button>
|
||||
<input
|
||||
type='text'
|
||||
value={searchTerm}
|
||||
@@ -102,11 +134,141 @@ export const App = () => {
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
<Action data={item} onEdit={onOpenEdit} refresh={refresh} />
|
||||
</div>
|
||||
);
|
||||
})
|
||||
)}
|
||||
</div>
|
||||
<Modal open={open} setOpen={setOpen}>
|
||||
<MarkForm
|
||||
data={formData}
|
||||
onCancel={() => setOpen(false)}
|
||||
onSuccess={() => {
|
||||
setOpen(false);
|
||||
init();
|
||||
}}
|
||||
/>
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const MarkForm = (props?: { data?: Mark; onSuccess?: () => void; onCancel?: () => void }) => {
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
control,
|
||||
formState: { errors },
|
||||
} = useForm<Mark>({
|
||||
defaultValues: props?.data || {
|
||||
title: '',
|
||||
summary: '',
|
||||
link: '',
|
||||
tags: [],
|
||||
},
|
||||
});
|
||||
|
||||
const onSubmit = async (data: Mark) => {
|
||||
try {
|
||||
const res = await queryMark.updateMark(data);
|
||||
|
||||
if (res.code === 200) {
|
||||
toast.success(data.id ? '更新成功' : '创建成功');
|
||||
props?.onSuccess?.();
|
||||
} else {
|
||||
toast.error(res?.message || '操作失败');
|
||||
}
|
||||
} catch (error) {
|
||||
toast.error('操作失败');
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit(onSubmit)} className='space-y-4 p-2'>
|
||||
{props?.data?.id && <input type='hidden' {...register('id')} />}
|
||||
|
||||
<div>
|
||||
<label className='block text-sm font-medium mb-1'>标题</label>
|
||||
<input
|
||||
type='text'
|
||||
{...register('title', { required: '标题是必填项' })}
|
||||
className='w-full p-2 border rounded-md focus:outline-none focus:ring-2 focus:ring-blue-300'
|
||||
/>
|
||||
{errors.title && <p className='text-red-500 text-xs mt-1'>{errors.title.message}</p>}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className='block text-sm font-medium mb-1'>链接</label>
|
||||
<input
|
||||
type='text'
|
||||
{...register('link', { required: '链接是必填项' })}
|
||||
className='w-full p-2 border rounded-md focus:outline-none focus:ring-2 focus:ring-blue-300'
|
||||
/>
|
||||
{errors.link && <p className='text-red-500 text-xs mt-1'>{errors.link.message}</p>}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className='block text-sm font-medium mb-1'>摘要</label>
|
||||
<textarea {...register('summary')} rows={3} className='w-full p-2 border rounded-md focus:outline-none focus:ring-2 focus:ring-blue-300' />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className='block text-sm font-medium mb-1'>标签</label>
|
||||
<Controller
|
||||
name='tags'
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<input
|
||||
type='text'
|
||||
value={field.value?.join(', ') || ''}
|
||||
onChange={(e) => {
|
||||
const value = e.target.value;
|
||||
field.onChange(
|
||||
value
|
||||
.split(',')
|
||||
.map((tag) => tag.trim())
|
||||
.filter((tag) => tag !== ''),
|
||||
);
|
||||
}}
|
||||
className='w-full p-2 border rounded-md focus:outline-none focus:ring-2 focus:ring-blue-300'
|
||||
placeholder='用逗号分隔多个标签'
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className='flex justify-end space-x-2 mt-4'>
|
||||
<Button onClick={props?.onCancel} type='button'>
|
||||
取消
|
||||
</Button>
|
||||
<Button type='submit'>保存</Button>
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
export const Action = (props: { data: Mark; onEdit?: (data: Mark) => any; refresh?: any }) => {
|
||||
const onDelete = async () => {
|
||||
const res = await queryMark.deleteMark(props.data.id);
|
||||
if (res.code === 200) {
|
||||
toast.success('删除成功');
|
||||
props?.refresh?.();
|
||||
} else {
|
||||
toast.error(res?.message || '请求失败');
|
||||
}
|
||||
};
|
||||
return (
|
||||
<div className='flex gap-2 mt-2'>
|
||||
<Button className='flex items-center gap-1 transition' onClick={() => props?.onEdit?.(props.data)} title='编辑'>
|
||||
<Pencil className='w-4 h-4' />
|
||||
编辑
|
||||
</Button>
|
||||
<Confirm onOk={onDelete}>
|
||||
<Button className='flex items-center gap-1 text-red-600 transition' title='删除'>
|
||||
<Trash2 className='w-4 h-4' />
|
||||
删除
|
||||
</Button>
|
||||
</Confirm>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
13
src/apps/kevisual-lib/Layout.tsx
Normal file
13
src/apps/kevisual-lib/Layout.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
type LayoutProps = {
|
||||
children?: React.ReactNode;
|
||||
className?: string;
|
||||
style?: React.CSSProperties;
|
||||
};
|
||||
export const Layout = (props: LayoutProps) => {
|
||||
const { children, className, style } = props;
|
||||
return (
|
||||
<div className={className} style={style}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user