524 lines
13 KiB
Smarty
524 lines
13 KiB
Smarty
{{- $global := . }}
|
||
{{- $templateID := printf "%s_%s" .Package .StructName }}
|
||
{{- if .IsAdd }}
|
||
// ============ 新增字段配置 ============
|
||
|
||
// 搜索表单新增字段
|
||
{{- range .Fields}}
|
||
{{- if .FieldSearchType}}
|
||
{{ GenerateReactSearchFormItem . }}
|
||
{{- end }}
|
||
{{- end }}
|
||
|
||
// 表格列新增配置
|
||
{{- range .Fields}}
|
||
{{- if .Table}}
|
||
{{ GenerateReactTableColumn . }}
|
||
{{- end }}
|
||
{{- end }}
|
||
|
||
// 表单新增字段
|
||
{{- range .Fields}}
|
||
{{- if .Form}}
|
||
{{ GenerateReactFormItem . }}
|
||
{{- end }}
|
||
{{- end }}
|
||
|
||
// 详情新增字段
|
||
{{- range .Fields}}
|
||
{{- if .Desc }}
|
||
{{ GenerateReactDescriptionItem . }}
|
||
{{- end }}
|
||
{{- end }}
|
||
|
||
{{- else }}
|
||
{{- if not .OnlyTemplate}}
|
||
/**
|
||
* KRA - {{.Description}}管理页面
|
||
* Auto-generated by KRA AutoCode
|
||
*/
|
||
import React, { useState, useRef, useEffect } from 'react';
|
||
import { PageContainer, ProTable } from '@ant-design/pro-components';
|
||
import type { ActionType, ProColumns } from '@ant-design/pro-components';
|
||
import {
|
||
Button,
|
||
Space,
|
||
Modal,
|
||
message,
|
||
Form,
|
||
Input,
|
||
Tag,
|
||
{{- if .HasTimer }}
|
||
DatePicker,
|
||
{{- end }}
|
||
{{- if or .DictTypes .HasDataSource }}
|
||
Select,
|
||
{{- end }}
|
||
Drawer,
|
||
Descriptions,
|
||
Popconfirm,
|
||
{{- if .HasPic }}
|
||
Image,
|
||
{{- end }}
|
||
{{- if .IsTree }}
|
||
TreeSelect,
|
||
{{- end }}
|
||
{{- if .HasRichText }}
|
||
Typography,
|
||
{{- end }}
|
||
} from 'antd';
|
||
import type { FormInstance } from 'antd/es/form';
|
||
import {
|
||
PlusOutlined,
|
||
DeleteOutlined,
|
||
EditOutlined,
|
||
EyeOutlined,
|
||
{{- if .IsTree }}
|
||
PlusCircleOutlined,
|
||
{{- end }}
|
||
} from '@ant-design/icons';
|
||
import {
|
||
create{{.StructName}},
|
||
delete{{.StructName}},
|
||
delete{{.StructName}}ByIds,
|
||
update{{.StructName}},
|
||
find{{.StructName}},
|
||
get{{.StructName}}List,
|
||
{{- if .HasDataSource }}
|
||
get{{.StructName}}DataSource,
|
||
{{- end }}
|
||
} from '@/services/kratos/{{.PackageName}}';
|
||
import type { {{.StructName}} } from '@/services/kratos/{{.PackageName}}';
|
||
{{- if .DictTypes }}
|
||
import { useDictionary } from '@/hooks/useDictionary';
|
||
{{- end }}
|
||
import dayjs from 'dayjs';
|
||
|
||
const {{.StructName}}Page: React.FC = () => {
|
||
// 表格操作引用
|
||
const actionRef = useRef<ActionType>();
|
||
const formRef = useRef<FormInstance>(null);
|
||
|
||
// 状态定义
|
||
const [drawerVisible, setDrawerVisible] = useState(false);
|
||
const [drawerType, setDrawerType] = useState<'create' | 'edit'>('create');
|
||
const [detailVisible, setDetailVisible] = useState(false);
|
||
const [currentRecord, setCurrentRecord] = useState<{{.StructName}} | null>(null);
|
||
const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);
|
||
const [btnLoading, setBtnLoading] = useState(false);
|
||
{{- if .HasDataSource }}
|
||
const [dataSource, setDataSource] = useState<Record<string, Array<{ label: string; value: any }>>>({});
|
||
{{- end }}
|
||
{{- if .IsTree }}
|
||
const [treeData, setTreeData] = useState<{{.StructName}}[]>([]);
|
||
{{- end }}
|
||
|
||
{{- if .DictTypes }}
|
||
// 字典数据
|
||
{{- range $index, $element := .DictTypes}}
|
||
const { options: {{$element}}Options, getLabel: get{{$element | toPascalCase}}Label } = useDictionary('{{$element}}');
|
||
{{- end }}
|
||
{{- end }}
|
||
|
||
{{- if .HasDataSource }}
|
||
// 获取数据源
|
||
const fetchDataSource = async () => {
|
||
try {
|
||
const res = await get{{.StructName}}DataSource();
|
||
if (res.code === 0) {
|
||
setDataSource(res.data || {});
|
||
}
|
||
} catch (error) {
|
||
console.error('获取数据源失败:', error);
|
||
}
|
||
};
|
||
|
||
useEffect(() => {
|
||
fetchDataSource();
|
||
}, []);
|
||
{{- end }}
|
||
|
||
// 新增
|
||
const handleAdd = ({{- if .IsTree -}}parentRecord?: {{.StructName}}{{- end -}}) => {
|
||
setDrawerType('create');
|
||
setCurrentRecord({{- if .IsTree -}}parentRecord ? { parentID: parentRecord.{{.PrimaryField.FieldJson}} } as {{.StructName}} : {{- end -}}null);
|
||
setDrawerVisible(true);
|
||
};
|
||
|
||
// 编辑
|
||
const handleEdit = async (record: {{.StructName}}) => {
|
||
try {
|
||
const res = await find{{.StructName}}({ {{.PrimaryField.FieldJson}}: record.{{.PrimaryField.FieldJson}}! });
|
||
if (res.code === 0) {
|
||
setDrawerType('edit');
|
||
setCurrentRecord(res.data);
|
||
setDrawerVisible(true);
|
||
}
|
||
} catch (error) {
|
||
message.error('获取详情失败');
|
||
}
|
||
};
|
||
|
||
// 查看详情
|
||
const handleDetail = async (record: {{.StructName}}) => {
|
||
try {
|
||
const res = await find{{.StructName}}({ {{.PrimaryField.FieldJson}}: record.{{.PrimaryField.FieldJson}}! });
|
||
if (res.code === 0) {
|
||
setCurrentRecord(res.data);
|
||
setDetailVisible(true);
|
||
}
|
||
} catch (error) {
|
||
message.error('获取详情失败');
|
||
}
|
||
};
|
||
|
||
// 删除
|
||
const handleDelete = async (record: {{.StructName}}) => {
|
||
try {
|
||
const res = await delete{{.StructName}}({ {{.PrimaryField.FieldJson}}: record.{{.PrimaryField.FieldJson}}! });
|
||
if (res.code === 0) {
|
||
message.success('删除成功');
|
||
actionRef.current?.reload();
|
||
} else {
|
||
message.error(res.msg || '删除失败');
|
||
}
|
||
} catch (error) {
|
||
message.error('删除失败');
|
||
}
|
||
};
|
||
|
||
// 批量删除
|
||
const handleBatchDelete = async () => {
|
||
if (selectedRowKeys.length === 0) {
|
||
message.warning('请选择要删除的数据');
|
||
return;
|
||
}
|
||
Modal.confirm({
|
||
title: '确认删除',
|
||
content: `确定要删除选中的 ${selectedRowKeys.length} 条数据吗?`,
|
||
onOk: async () => {
|
||
try {
|
||
const res = await delete{{.StructName}}ByIds({ {{.PrimaryField.FieldJson}}s: selectedRowKeys as {{ GenerateTSType .PrimaryField.FieldType }}[] });
|
||
if (res.code === 0) {
|
||
message.success('删除成功');
|
||
setSelectedRowKeys([]);
|
||
actionRef.current?.reload();
|
||
} else {
|
||
message.error(res.msg || '删除失败');
|
||
}
|
||
} catch (error) {
|
||
message.error('删除失败');
|
||
}
|
||
},
|
||
});
|
||
};
|
||
|
||
// 关闭抽屉
|
||
const handleDrawerClose = () => {
|
||
setDrawerVisible(false);
|
||
setCurrentRecord(null);
|
||
formRef.current?.resetFields();
|
||
};
|
||
|
||
// 提交表单
|
||
const handleSubmit = async () => {
|
||
try {
|
||
const values = await formRef.current?.validateFields();
|
||
setBtnLoading(true);
|
||
const submitData = { ...currentRecord, ...values };
|
||
const res = drawerType === 'create'
|
||
? await create{{.StructName}}(submitData)
|
||
: await update{{.StructName}}(submitData);
|
||
if (res.code === 0) {
|
||
message.success(drawerType === 'create' ? '创建成功' : '更新成功');
|
||
handleDrawerClose();
|
||
actionRef.current?.reload();
|
||
} else {
|
||
message.error(res.msg || (drawerType === 'create' ? '创建失败' : '更新失败'));
|
||
}
|
||
} catch (error) {
|
||
// 表单验证失败
|
||
} finally {
|
||
setBtnLoading(false);
|
||
}
|
||
};
|
||
|
||
// 表格列定义
|
||
const columns: ProColumns<{{.StructName}}>[] = [
|
||
{{- if .GvaModel }}
|
||
{
|
||
title: '创建时间',
|
||
dataIndex: 'CreatedAt',
|
||
valueType: 'dateTime',
|
||
width: 180,
|
||
sorter: true,
|
||
hideInSearch: true,
|
||
},
|
||
{{- end }}
|
||
{{- range .Fields}}
|
||
{{- if .Table}}
|
||
{{ GenerateReactProTableColumn . }}
|
||
{{- end }}
|
||
{{- end }}
|
||
{
|
||
title: '操作',
|
||
valueType: 'option',
|
||
fixed: 'right',
|
||
width: {{- if .IsTree -}}280{{- else -}}200{{- end }},
|
||
render: (_, record) => [
|
||
{{- if .IsTree }}
|
||
<Button
|
||
key="addChild"
|
||
type="link"
|
||
size="small"
|
||
icon={<PlusCircleOutlined />}
|
||
onClick={() => handleAdd(record)}
|
||
>
|
||
新增子节点
|
||
</Button>,
|
||
{{- end }}
|
||
<Button
|
||
key="detail"
|
||
type="link"
|
||
size="small"
|
||
icon={<EyeOutlined />}
|
||
onClick={() => handleDetail(record)}
|
||
>
|
||
查看
|
||
</Button>,
|
||
<Button
|
||
key="edit"
|
||
type="link"
|
||
size="small"
|
||
icon={<EditOutlined />}
|
||
onClick={() => handleEdit(record)}
|
||
>
|
||
编辑
|
||
</Button>,
|
||
{{- if .IsTree }}
|
||
!record.children?.length && (
|
||
{{- end }}
|
||
<Popconfirm
|
||
key="delete"
|
||
title="确定删除吗?"
|
||
onConfirm={() => handleDelete(record)}
|
||
okText="确定"
|
||
cancelText="取消"
|
||
>
|
||
<Button type="link" size="small" danger icon={<DeleteOutlined />}>
|
||
删除
|
||
</Button>
|
||
</Popconfirm>
|
||
{{- if .IsTree }}
|
||
)
|
||
{{- end }},
|
||
],
|
||
},
|
||
];
|
||
|
||
return (
|
||
<PageContainer>
|
||
<ProTable<{{.StructName}}>
|
||
actionRef={actionRef}
|
||
columns={columns}
|
||
rowKey="{{.PrimaryField.FieldJson}}"
|
||
scroll={{ "{{" }} x: 'max-content' {{ "}}" }}
|
||
rowSelection={{
|
||
selectedRowKeys,
|
||
onChange: (keys) => setSelectedRowKeys(keys),
|
||
}}
|
||
request={async (params, sort) => {
|
||
// 处理排序
|
||
const sortKey = Object.keys(sort || {})[0];
|
||
let orderKey = '';
|
||
if (sortKey) {
|
||
// 排序字段映射
|
||
const sortMap: Record<string, string> = {
|
||
CreatedAt: 'created_at',
|
||
ID: 'id',
|
||
{{- range .Fields}}
|
||
{{- if .Table}}
|
||
{{- if .Sort}}
|
||
{{- if .ColumnName}}
|
||
{{.FieldJson}}: '{{.ColumnName}}',
|
||
{{- end}}
|
||
{{- end}}
|
||
{{- end}}
|
||
{{- end}}
|
||
};
|
||
orderKey = sortMap[sortKey] || sortKey.replace(/[A-Z]/g, (match) => `_${match.toLowerCase()}`);
|
||
}
|
||
try {
|
||
const res = await get{{.StructName}}List({
|
||
page: params.current || 1,
|
||
pageSize: params.pageSize || 10,
|
||
{{- range .Fields}}
|
||
{{- if .FieldSearchType}}
|
||
{{- if eq .FieldSearchType "BETWEEN"}}
|
||
start{{.FieldName}}: params.start{{.FieldName}},
|
||
end{{.FieldName}}: params.end{{.FieldName}},
|
||
{{- else}}
|
||
{{.FieldJson}}: params.{{.FieldJson}},
|
||
{{- end}}
|
||
{{- end }}
|
||
{{- end }}
|
||
{{- if .GvaModel }}
|
||
startCreatedAt: params.startCreatedAt,
|
||
endCreatedAt: params.endCreatedAt,
|
||
{{- end }}
|
||
orderKey: orderKey,
|
||
desc: sort?.[sortKey] === 'descend',
|
||
});
|
||
{{- if .IsTree }}
|
||
if (res.code === 0) {
|
||
setTreeData(res.data || []);
|
||
}
|
||
return {
|
||
data: res.data || [],
|
||
success: res.code === 0,
|
||
};
|
||
{{- else }}
|
||
return {
|
||
data: res.data?.list || [],
|
||
total: res.data?.total || 0,
|
||
success: res.code === 0,
|
||
};
|
||
{{- end }}
|
||
} catch (error) {
|
||
return {
|
||
data: [],
|
||
total: 0,
|
||
success: false,
|
||
};
|
||
}
|
||
}}
|
||
pagination={{- if .IsTree -}}={false}{{- else -}}={{
|
||
defaultPageSize: 10,
|
||
showSizeChanger: true,
|
||
pageSizeOptions: ['10', '30', '50', '100'],
|
||
showTotal: (total) => `共 ${total} 条`,
|
||
}}{{- end }}
|
||
search={{
|
||
labelWidth: 'auto',
|
||
collapsed: true,
|
||
collapseRender: (collapsed) => (collapsed ? '展开' : '收起'),
|
||
defaultCollapsed: true,
|
||
span: {{- if .GvaModel -}}6{{- else -}}8{{- end }},
|
||
}}
|
||
toolBarRender={() => [
|
||
<Button
|
||
key="add"
|
||
type="primary"
|
||
icon={<PlusOutlined />}
|
||
onClick={() => handleAdd()}
|
||
>
|
||
新增
|
||
</Button>,
|
||
<Button
|
||
key="delete"
|
||
danger
|
||
icon={<DeleteOutlined />}
|
||
disabled={selectedRowKeys.length === 0}
|
||
onClick={handleBatchDelete}
|
||
>
|
||
批量删除
|
||
</Button>,
|
||
]}
|
||
/>
|
||
|
||
{/* 新增/编辑抽屉 */}
|
||
<Drawer
|
||
title={drawerType === 'create' ? '新增{{.Description}}' : '编辑{{.Description}}'}
|
||
open={drawerVisible}
|
||
onClose={handleDrawerClose}
|
||
width={600}
|
||
destroyOnClose
|
||
extra={
|
||
<Space>
|
||
<Button onClick={handleDrawerClose}>取消</Button>
|
||
<Button type="primary" loading={btnLoading} onClick={handleSubmit}>
|
||
确定
|
||
</Button>
|
||
</Space>
|
||
}
|
||
>
|
||
<Form
|
||
ref={formRef}
|
||
layout="vertical"
|
||
initialValues={currentRecord || {}}
|
||
>
|
||
{{- if .IsTree }}
|
||
<Form.Item name="parentID" label="父节点">
|
||
<TreeSelect
|
||
placeholder="请选择父节点"
|
||
allowClear
|
||
treeData={[{ {{.PrimaryField.FieldJson}}: 0, {{.TreeJson}}: '根节点', children: treeData }]}
|
||
fieldNames={{ "{{" }} label: '{{.TreeJson}}', value: '{{.PrimaryField.FieldJson}}', children: 'children' {{ "}}" }}
|
||
treeDefaultExpandAll
|
||
/>
|
||
</Form.Item>
|
||
{{- end }}
|
||
{{- range .Fields}}
|
||
{{- if .Form}}
|
||
{{ GenerateReactFormItem . }}
|
||
{{- end }}
|
||
{{- end }}
|
||
</Form>
|
||
</Drawer>
|
||
|
||
{/* 详情抽屉 */}
|
||
<Drawer
|
||
title="{{.Description}}详情"
|
||
open={detailVisible}
|
||
onClose={() => {
|
||
setDetailVisible(false);
|
||
setCurrentRecord(null);
|
||
}}
|
||
width={600}
|
||
>
|
||
{currentRecord && (
|
||
<Descriptions column={1} bordered>
|
||
{{- if .IsTree }}
|
||
<Descriptions.Item label="父节点">{currentRecord.parentID || '根节点'}</Descriptions.Item>
|
||
{{- end }}
|
||
{{- range .Fields}}
|
||
{{- if .Desc }}
|
||
{{ GenerateReactDescriptionItem . }}
|
||
{{- end }}
|
||
{{- end }}
|
||
{{- if .GvaModel }}
|
||
<Descriptions.Item label="创建时间">
|
||
{currentRecord.CreatedAt ? dayjs(currentRecord.CreatedAt).format('YYYY-MM-DD HH:mm:ss') : '-'}
|
||
</Descriptions.Item>
|
||
<Descriptions.Item label="更新时间">
|
||
{currentRecord.UpdatedAt ? dayjs(currentRecord.UpdatedAt).format('YYYY-MM-DD HH:mm:ss') : '-'}
|
||
</Descriptions.Item>
|
||
{{- end }}
|
||
</Descriptions>
|
||
)}
|
||
</Drawer>
|
||
</PageContainer>
|
||
);
|
||
};
|
||
|
||
export default {{.StructName}}Page;
|
||
{{- else}}
|
||
/**
|
||
* KRA - {{.Description}}页面
|
||
* Auto-generated by KRA AutoCode
|
||
*/
|
||
import React from 'react';
|
||
import { PageContainer } from '@ant-design/pro-components';
|
||
|
||
const {{.StructName}}Page: React.FC = () => {
|
||
return (
|
||
<PageContainer>
|
||
<div>{{.Description}}页面</div>
|
||
</PageContainer>
|
||
);
|
||
};
|
||
|
||
export default {{.StructName}}Page;
|
||
{{- end }}
|
||
{{- end }}
|