forked from yoone/WEB
442 lines
12 KiB
TypeScript
442 lines
12 KiB
TypeScript
import {
|
||
templatecontrollerCreatetemplate,
|
||
templatecontrollerDeletetemplate,
|
||
templatecontrollerGettemplatelist,
|
||
templatecontrollerRendertemplate,
|
||
templatecontrollerRendertemplatedirect,
|
||
templatecontrollerUpdatetemplate,
|
||
} from '@/servers/api/template';
|
||
import { EditOutlined, PlusOutlined, ReloadOutlined } from '@ant-design/icons';
|
||
import {
|
||
ActionType,
|
||
DrawerForm,
|
||
PageContainer,
|
||
ProColumns,
|
||
ProForm,
|
||
ProFormText,
|
||
ProTable,
|
||
} from '@ant-design/pro-components';
|
||
import Editor from '@monaco-editor/react';
|
||
import { App, Button, Card, Popconfirm, Space, Typography } from 'antd';
|
||
import { useEffect, useRef, useState } from 'react';
|
||
|
||
// 自定义hook,用于处理模板预览逻辑
|
||
const useTemplatePreview = () => {
|
||
const [renderedResult, setRenderedResult] = useState<string>('');
|
||
const [previewData, setPreviewData] = useState<any>(null);
|
||
|
||
// 防抖的预览效果
|
||
useEffect(() => {
|
||
if (!previewData || !previewData.value) {
|
||
setRenderedResult('请输入模板内容');
|
||
return;
|
||
}
|
||
|
||
const timer = setTimeout(async () => {
|
||
let testData = {};
|
||
try {
|
||
if (previewData.testData) {
|
||
testData = JSON.parse(previewData.testData);
|
||
}
|
||
} catch (e) {
|
||
testData = {};
|
||
}
|
||
|
||
try {
|
||
// 使用新的直接渲染API,传入模板内容和测试数据
|
||
const res = await templatecontrollerRendertemplatedirect({
|
||
template: previewData.value,
|
||
data: testData,
|
||
});
|
||
if (res.success) {
|
||
setRenderedResult(res.data as unknown as string);
|
||
} else {
|
||
setRenderedResult(`错误: ${res.message}`);
|
||
}
|
||
} catch (error: any) {
|
||
setRenderedResult(`错误: ${error.message}`);
|
||
}
|
||
}, 500); // 防抖 500ms
|
||
|
||
return () => clearTimeout(timer);
|
||
}, [previewData]);
|
||
|
||
// 处理实时预览逻辑
|
||
const handlePreview = (_changedValues: any, allValues: any) => {
|
||
setPreviewData(allValues);
|
||
};
|
||
|
||
// 手动刷新预览
|
||
const refreshPreview = (formValues: any) => {
|
||
setPreviewData(formValues);
|
||
};
|
||
|
||
return {
|
||
renderedResult,
|
||
handlePreview,
|
||
refreshPreview,
|
||
setPreviewData
|
||
};
|
||
};
|
||
|
||
|
||
|
||
const List: React.FC = () => {
|
||
const actionRef = useRef<ActionType>();
|
||
const { message } = App.useApp();
|
||
|
||
const columns: ProColumns<API.Template>[] = [
|
||
{
|
||
title: '名称',
|
||
dataIndex: 'name',
|
||
tip: '名称是唯一的 key',
|
||
formItemProps: {
|
||
rules: [
|
||
{
|
||
required: true,
|
||
message: '名称为必填项',
|
||
},
|
||
],
|
||
},
|
||
},
|
||
{
|
||
title: '标题',
|
||
dataIndex: 'title',
|
||
},
|
||
{
|
||
title: '值',
|
||
dataIndex: 'value',
|
||
},
|
||
{
|
||
title: '更新时间',
|
||
dataIndex: 'updatedAt',
|
||
valueType: 'dateTime',
|
||
hideInSearch: true,
|
||
},
|
||
{
|
||
title: '创建时间',
|
||
dataIndex: 'createdAt',
|
||
valueType: 'dateTime',
|
||
hideInSearch: true,
|
||
},
|
||
{
|
||
title: '操作',
|
||
dataIndex: 'option',
|
||
valueType: 'option',
|
||
render: (_, record) => (
|
||
<Space>
|
||
<UpdateForm tableRef={actionRef} values={record} />
|
||
<Popconfirm
|
||
title="删除"
|
||
description="确认删除?"
|
||
onConfirm={async () => {
|
||
if (!record.id) return;
|
||
try {
|
||
await templatecontrollerDeletetemplate({ id: record.id });
|
||
actionRef.current?.reload();
|
||
} catch (error: any) {
|
||
message.error(error.message);
|
||
}
|
||
}}
|
||
>
|
||
<Button type="primary" danger>
|
||
删除
|
||
</Button>
|
||
</Popconfirm>
|
||
</Space>
|
||
),
|
||
},
|
||
];
|
||
|
||
return (
|
||
<PageContainer header={{ title: '模板列表' }}>
|
||
<ProTable<API.Template>
|
||
headerTitle="查询表格"
|
||
actionRef={actionRef}
|
||
rowKey="id"
|
||
toolBarRender={() => [<CreateForm tableRef={actionRef} />]}
|
||
request={async (params) => {
|
||
const response = (await templatecontrollerGettemplatelist(
|
||
params as any,
|
||
)) as any;
|
||
return {
|
||
data: response.items || [],
|
||
total: response.total || 0,
|
||
success: true,
|
||
};
|
||
}}
|
||
columns={columns}
|
||
/>
|
||
</PageContainer>
|
||
);
|
||
};
|
||
|
||
const CreateForm: React.FC<{
|
||
tableRef: React.MutableRefObject<ActionType | undefined>;
|
||
}> = ({ tableRef }) => {
|
||
const { message } = App.useApp();
|
||
const [form] = ProForm.useForm();
|
||
const { renderedResult, handlePreview, refreshPreview } = useTemplatePreview();
|
||
|
||
return (
|
||
<DrawerForm<API.CreateTemplateDTO>
|
||
title="新建"
|
||
form={form}
|
||
trigger={
|
||
<Button type="primary">
|
||
<PlusOutlined />
|
||
新建
|
||
</Button>
|
||
}
|
||
autoFocusFirstInput
|
||
drawerProps={{
|
||
destroyOnHidden: true,
|
||
width: 1200, // 增加抽屉宽度以容纳调试面板
|
||
}}
|
||
onValuesChange={handlePreview}
|
||
onFinish={async (values) => {
|
||
try {
|
||
await templatecontrollerCreatetemplate(values);
|
||
tableRef.current?.reload();
|
||
message.success('提交成功');
|
||
return true;
|
||
} catch (error: any) {
|
||
message.error(error.message);
|
||
return false;
|
||
}
|
||
}}
|
||
>
|
||
<div style={{ display: 'flex', gap: '20px' }}>
|
||
<div style={{ flex: 1 }}>
|
||
<ProFormText
|
||
name="name"
|
||
label="模板名称"
|
||
placeholder="请输入名称"
|
||
rules={[{ required: true, message: '请输入名称' }]}
|
||
/>
|
||
<ProForm.Item
|
||
name="value"
|
||
label="模板内容"
|
||
rules={[{ required: true, message: '请输入模板内容' }]}
|
||
>
|
||
<Editor
|
||
height="400px"
|
||
defaultLanguage="html"
|
||
options={{
|
||
minimap: { enabled: false },
|
||
lineNumbers: 'on',
|
||
scrollBeyondLastLine: false,
|
||
automaticLayout: true,
|
||
}}
|
||
/>
|
||
</ProForm.Item>
|
||
<ProForm.Item
|
||
name="testData"
|
||
label="测试数据 (JSON)"
|
||
rules={[
|
||
{
|
||
validator: (_: any, value: any) => {
|
||
if (!value) return Promise.resolve();
|
||
try {
|
||
JSON.parse(value);
|
||
return Promise.resolve();
|
||
} catch (e) {
|
||
return Promise.reject(new Error('请输入有效的JSON格式'));
|
||
}
|
||
},
|
||
},
|
||
]}
|
||
>
|
||
<Editor
|
||
height="200px"
|
||
defaultLanguage="json"
|
||
options={{
|
||
minimap: { enabled: false },
|
||
lineNumbers: 'on',
|
||
scrollBeyondLastLine: false,
|
||
automaticLayout: true,
|
||
formatOnPaste: true,
|
||
formatOnType: true,
|
||
}}
|
||
/>
|
||
</ProForm.Item>
|
||
</div>
|
||
|
||
<div style={{ flex: 1 }}>
|
||
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: '8px' }}>
|
||
<Typography.Title level={5} style={{ margin: 0 }}>实时预览</Typography.Title>
|
||
<Button
|
||
type="text"
|
||
icon={<ReloadOutlined />}
|
||
onClick={() => {
|
||
// 获取当前表单数据并触发预览
|
||
const currentValues = form.getFieldsValue();
|
||
refreshPreview(currentValues);
|
||
}}
|
||
title="手动刷新预览"
|
||
/>
|
||
</div>
|
||
<Card
|
||
styles={{
|
||
body: {
|
||
padding: '16px',
|
||
height: '600px',
|
||
overflow: 'auto',
|
||
backgroundColor: '#f5f5f5',
|
||
}
|
||
}}
|
||
>
|
||
<pre style={{ whiteSpace: 'pre-wrap', margin: 0 }}>
|
||
{renderedResult || '修改模板或测试数据后将自动预览结果...'}
|
||
</pre>
|
||
</Card>
|
||
</div>
|
||
</div>
|
||
</DrawerForm>
|
||
);
|
||
};
|
||
|
||
const UpdateForm: React.FC<{
|
||
tableRef: React.MutableRefObject<ActionType | undefined>;
|
||
values: API.Template;
|
||
}> = ({ tableRef, values: initialValues }) => {
|
||
const { message } = App.useApp();
|
||
const [form] = ProForm.useForm();
|
||
const { renderedResult, handlePreview, refreshPreview, setPreviewData } = useTemplatePreview();
|
||
|
||
// 组件挂载时初始化预览数据
|
||
useEffect(() => {
|
||
if (initialValues) {
|
||
setPreviewData({
|
||
name: initialValues.name,
|
||
value: initialValues.value,
|
||
testData: initialValues.testData
|
||
});
|
||
}
|
||
}, [initialValues, setPreviewData]);
|
||
|
||
return (
|
||
<DrawerForm<API.UpdateTemplateDTO>
|
||
title="编辑"
|
||
form={form}
|
||
initialValues={initialValues}
|
||
trigger={
|
||
<Button type="primary">
|
||
<EditOutlined />
|
||
编辑
|
||
</Button>
|
||
}
|
||
autoFocusFirstInput
|
||
drawerProps={{
|
||
destroyOnHidden: true,
|
||
width: 1200, // 增加抽屉宽度以容纳调试面板
|
||
}}
|
||
onValuesChange={handlePreview}
|
||
onFinish={async (values) => {
|
||
if (!initialValues.id) return false;
|
||
try {
|
||
await templatecontrollerUpdatetemplate(
|
||
{ id: initialValues.id },
|
||
values,
|
||
);
|
||
message.success('提交成功');
|
||
tableRef.current?.reload();
|
||
return true;
|
||
} catch (error: any) {
|
||
message.error(error.message);
|
||
return false;
|
||
}
|
||
}}
|
||
>
|
||
<div style={{ display: 'flex', gap: '20px' }}>
|
||
<div style={{ flex: 1 }}>
|
||
<ProFormText
|
||
name="name"
|
||
label="模板名称"
|
||
placeholder="请输入名称"
|
||
rules={[{ required: true, message: '请输入名称' }]}
|
||
/>
|
||
<ProForm.Item
|
||
name="value"
|
||
label="模板内容"
|
||
rules={[{ required: true, message: '请输入模板内容' }]}
|
||
>
|
||
<Editor
|
||
height="400px"
|
||
defaultLanguage="html"
|
||
options={{
|
||
minimap: { enabled: false },
|
||
lineNumbers: 'on',
|
||
scrollBeyondLastLine: false,
|
||
automaticLayout: true,
|
||
}}
|
||
/>
|
||
</ProForm.Item>
|
||
<ProForm.Item
|
||
name="testData"
|
||
label="测试数据 (JSON)"
|
||
rules={[
|
||
{
|
||
validator: (_: any, value: any) => {
|
||
if (!value) return Promise.resolve();
|
||
try {
|
||
JSON.parse(value);
|
||
return Promise.resolve();
|
||
} catch (e) {
|
||
return Promise.reject(new Error('请输入有效的JSON格式'));
|
||
}
|
||
},
|
||
},
|
||
]}
|
||
>
|
||
<Editor
|
||
height="200px"
|
||
defaultLanguage="json"
|
||
options={{
|
||
minimap: { enabled: false },
|
||
lineNumbers: 'on',
|
||
scrollBeyondLastLine: false,
|
||
automaticLayout: true,
|
||
formatOnPaste: true,
|
||
formatOnType: true,
|
||
}}
|
||
/>
|
||
</ProForm.Item>
|
||
</div>
|
||
|
||
<div style={{ flex: 1 }}>
|
||
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: '8px' }}>
|
||
<Typography.Title level={5} style={{ margin: 0 }}>实时预览</Typography.Title>
|
||
<Button
|
||
type="text"
|
||
icon={<ReloadOutlined />}
|
||
onClick={() => {
|
||
// 获取当前表单数据并触发预览
|
||
const currentValues = form.getFieldsValue();
|
||
refreshPreview(currentValues);
|
||
}}
|
||
title="手动刷新预览"
|
||
/>
|
||
</div>
|
||
<Card
|
||
styles={{
|
||
body: {
|
||
padding: '16px',
|
||
height: '600px',
|
||
overflow: 'auto',
|
||
backgroundColor: '#f5f5f5',
|
||
}
|
||
}}
|
||
>
|
||
<pre style={{ whiteSpace: 'pre-wrap', margin: 0 }}>
|
||
{renderedResult || '修改模板或测试数据后将自动预览结果...'}
|
||
</pre>
|
||
</Card>
|
||
</div>
|
||
</div>
|
||
</DrawerForm>
|
||
);
|
||
};
|
||
|
||
export default List;
|