first commit

This commit is contained in:
liu
2026-07-13 15:23:29 +08:00
commit 50885a98c8
473 changed files with 33772 additions and 0 deletions
+118
View File
@@ -0,0 +1,118 @@
import React, { useState, useRef } from 'react';
import { Card, Space, Divider, Typography, message } from 'antd';
import XinForm, { type XinFormRef } from '@/components/XinForm';
import type { FormColumn } from '@/components/XinFormField/FieldRender/typings';
import IconSelect from '@/components/XinFormField/IconSelector';
const { Title, Paragraph, Text } = Typography;
/**
* 图标选择器组件使用示例页面
*/
const IconSelectExample: React.FC = () => {
const [icon, setIcon] = useState<string>('');
const formRef = useRef<XinFormRef>(undefined);
// 表单列配置
const columns: FormColumn<any>[] = [
{
dataIndex: 'systemName',
title: '系统名称',
valueType: 'text',
rules: [{ required: true, message: '请输入系统名称' }],
fieldProps: {
placeholder: '请输入系统名称',
},
},
{
dataIndex: 'systemIcon',
title: '系统图标',
rules: [{ required: true, message: '请选择系统图标' }],
fieldRender: () => <IconSelect placeholder="请选择系统图标" />,
},
{
dataIndex: 'description',
title: '系统描述',
valueType: 'textarea',
fieldProps: {
placeholder: '请输入系统描述',
rows: 4,
},
},
];
return (
<div>
<Typography style={{ margin: '12px 0 24px 0' }}>
<Title level={2}></Title>
<Paragraph>
Ant Design Select + Modal + Tabs
</Paragraph>
</Typography>
<Space direction="vertical" size="large" style={{ width: '100%' }}>
<Card title="独立使用" bordered>
<Text></Text>
<div className="mt-2">
<IconSelect
value={icon}
onChange={(value) => {
setIcon(value || '');
if (value) {
message.success(`选中图标: ${value}`);
} else {
message.info('已清空图标');
}
}}
placeholder="请选择图标"
/>
</div>
<Divider />
<Text></Text>
<div className="mt-2">
<IconSelect
value="HomeOutlined"
disabled={true}
placeholder="禁用状态"
/>
</div>
<Divider />
<Text></Text>
<div className="mt-2">
<IconSelect
value="SettingOutlined"
readonly={true}
placeholder="只读状态"
/>
</div>
<Text type="secondary" className="mt-2 block text-sm">
</Text>
</Card>
<Card title="在 XinForm 中使用" bordered>
<XinForm
formRef={formRef}
columns={columns}
onFinish={async (values) => {
console.log('XinForm 提交:', values);
message.success('提交成功!');
message.info(`系统图标: ${values.systemIcon}`);
return true;
}}
submitter={{
submitText: '提交表单',
render: (dom) => dom.submit,
}}
/>
</Card>
</Space>
</div>
);
};
export default IconSelectExample;
+156
View File
@@ -0,0 +1,156 @@
import React, { useState } from 'react';
import { Card, Form, Button, Space, Typography } from 'antd';
import ImageUploader from '@/components/XinFormField/ImageUploader';
import type { ISysFileInfo } from '@/domain/iSysFile';
const { Title, Paragraph } = Typography;
/**
* 图片上传器示例页面
*/
const ImageUploaderExample: React.FC = () => {
const [form] = Form.useForm();
const [singleValue, setSingleValue] = useState<ISysFileInfo | null>(null);
const [multipleValue, setMultipleValue] = useState<ISysFileInfo[]>([]);
// 提交表单
const handleSubmit = async () => {
try {
const values = await form.validateFields();
console.log('Form values:', values);
console.log('Single image:', singleValue);
console.log('Multiple images:', multipleValue);
} catch (error) {
console.error('Validation failed:', error);
}
};
// 重置表单
const handleReset = () => {
form.resetFields();
setSingleValue(null);
setMultipleValue([]);
};
return (
<div>
<Typography style={{ margin: '12px 0 24px 0' }}>
<Title level={2}></Title>
<Paragraph>
Ant Design Upload
</Paragraph>
</Typography>
<Card title="基础用法" style={{ marginBottom: 24 }}>
<Form form={form} layout="vertical">
<Form.Item
label="单张图片上传"
name="avatar"
extra="支持单张图片上传,最大 5MB,尺寸不超过 1920x1080"
>
<ImageUploader
action="/sys/file/list/upload/image"
mode="single"
value={singleValue}
onChange={(value) => setSingleValue(value as ISysFileInfo)}
/>
</Form.Item>
<Form.Item
label="多张图片上传"
name="gallery"
extra="支持最多 5 张图片上传"
>
<ImageUploader
action="/sys/file/list/upload/image"
mode="multiple"
maxCount={5}
value={multipleValue}
onChange={(value) => setMultipleValue(value as ISysFileInfo[])}
/>
</Form.Item>
<Form.Item>
<Space>
<Button type="primary" onClick={handleSubmit}>
</Button>
<Button onClick={handleReset}></Button>
</Space>
</Form.Item>
</Form>
</Card>
<Card title="高级配置" style={{ marginBottom: 24 }}>
<Form layout="vertical">
<Form.Item
label="自定义尺寸限制"
extra="限制图片尺寸为 800x600,大小不超过 2MB"
>
<ImageUploader
action="/sys/file/list/upload/image"
mode="single"
maxWidth={800}
maxHeight={600}
maxSize={2}
/>
</Form.Item>
<Form.Item
label="裁剪功能 - 自由裁剪"
extra="启用裁剪功能,可自由调整裁剪区域"
>
<ImageUploader
action="/sys/file/list/upload/image"
mode="single"
croppable
/>
</Form.Item>
<Form.Item
label="裁剪功能 - 1:1 正方形"
extra="固定 1:1 比例裁剪,适合头像上传"
>
<ImageUploader
action="/sys/file/list/upload/image"
mode="single"
croppable
cropperOptions={{ aspect: 1 }}
/>
</Form.Item>
<Form.Item
label="裁剪功能 - 圆形裁剪"
extra="圆形裁剪模式,适合头像上传"
>
<ImageUploader
action="/sys/file/list/upload/image"
mode="single"
croppable
cropperOptions={{ cropShape: 'round', aspect: 1 }}
/>
</Form.Item>
<Form.Item
label="裁剪功能 - 16:9 宽屏"
extra="固定 16:9 比例裁剪,适合横幅图片"
>
<ImageUploader
action="/sys/file/list/upload/image"
mode="single"
croppable
cropperOptions={{ aspect: 16 / 9 }}
/>
</Form.Item>
<Form.Item label="禁用状态">
<ImageUploader
action="/sys/file/list/upload/image"
mode="single"
disabled
/>
</Form.Item>
</Form>
</Card>
</div>
);
};
export default ImageUploaderExample;
+128
View File
@@ -0,0 +1,128 @@
import React, { useState, useRef } from 'react';
import { Card, Space, Divider, Typography, message } from 'antd';
import XinForm, { type XinFormRef } from '@/components/XinForm';
import type { FormColumn } from '@/components/XinFormField/FieldRender/typings';
import UserSelector from '@/components/XinFormField/UserSelector';
const { Title, Paragraph, Text } = Typography;
/**
* 用户选择器组件使用示例页面
*/
const UserSelectorExample: React.FC = () => {
const [singleUser, setSingleUser] = useState<number | null>(null);
const [multipleUsers, setMultipleUsers] = useState<number[]>([]);
const formRef = useRef<XinFormRef>(undefined);
// 表单列配置
const columns: FormColumn<any>[] = [
{
dataIndex: 'taskName',
title: '任务名称',
valueType: 'text',
rules: [{ required: true, message: '请输入任务名称' }],
fieldProps: {
placeholder: '请输入任务名称',
},
},
{
dataIndex: 'owner_id',
title: '任务负责人',
rules: [{ required: true, message: '请选择负责人' }],
fieldRender: () => <UserSelector placeholder="请选择负责人" />,
},
{
dataIndex: 'participant_ids',
title: '参与人员',
rules: [
{ required: true, message: '请至少选择一个参与人员' },
{
validator: (_: any, value: number[]) => {
if (value && value.length > 10) {
return Promise.reject('最多选择10个参与人员');
}
return Promise.resolve();
},
},
],
fieldRender: () => <UserSelector mode="multiple" maxTagCount={3} placeholder="请选择参与人员" />,
},
{
dataIndex: 'description',
title: '任务描述',
valueType: 'textarea',
fieldProps: {
placeholder: '请输入任务描述',
},
},
];
return (
<div>
<Typography style={{ margin: '12px 0 24px 0' }}>
<Title level={2}></Title>
<Paragraph>
AntDesign
</Paragraph>
</Typography>
<Space direction="vertical" size="large" style={{ width: '100%' }}>
<Card title="独立使用" bordered>
<Text></Text>
<div className="mt-2">
<UserSelector
value={singleUser}
onChange={(value) => {
setSingleUser(value as number);
message.success(`选中用户ID: ${value}`);
}}
placeholder="请选择用户"
/>
</div>
<Text type="secondary" className="mt-2 block">
: {singleUser || '未选择'}
</Text>
<Divider />
<Text></Text>
<div className="mt-2">
<UserSelector
mode="multiple"
value={multipleUsers}
onChange={(value) => {
setMultipleUsers(value as number[]);
message.success(`选中${(value as number[]).length}个用户`);
}}
placeholder="请选择多个用户"
maxTagCount={3}
/>
</div>
<Text type="secondary" className="mt-2 block">
: {multipleUsers.length > 0 ? multipleUsers.join(', ') : '未选择'}
</Text>
</Card>
<Card title="在 XinForm 中使用" bordered>
<XinForm
formRef={formRef}
columns={columns}
onFinish={async (values) => {
console.log('表单提交:', values);
message.success('提交成功!');
message.info(`负责人ID: ${values.owner_id}, 参与人IDs: ${values.participant_ids?.join(', ')}`);
return true;
}}
submitter={{
submitText: '提交表单',
render: (dom) => dom.submit,
}}
/>
</Card>
</Space>
</div>
);
};
export default UserSelectorExample;
+710
View File
@@ -0,0 +1,710 @@
import React, { useRef } from 'react';
import { Card, Space, Typography, message, Button, Divider, Input, Tag } from 'antd';
import XinForm from '@/components/XinForm';
import type { XinFormRef } from '@/components/XinForm';
import type { FormColumn } from '@/components/XinFormField/FieldRender/typings';
const { Title, Paragraph, Text } = Typography;
// 基础表单字段配置
const basicColumns: FormColumn<any>[] = [
{
dataIndex: 'username',
title: '用户名',
valueType: 'text',
rules: [{ required: true, message: '请输入用户名' }],
fieldProps: {
placeholder: '请输入用户名',
},
},
{
dataIndex: 'password',
title: '密码',
valueType: 'password',
rules: [{ required: true, message: '请输入密码' }],
fieldProps: {
placeholder: '请输入密码',
},
},
{
dataIndex: 'email',
title: '邮箱',
valueType: 'text',
rules: [
{ required: true, message: '请输入邮箱' },
{ type: 'email', message: '请输入有效的邮箱地址' },
],
fieldProps: {
placeholder: '请输入邮箱地址',
},
},
{
dataIndex: 'description',
title: '个人简介',
valueType: 'textarea',
fieldProps: {
placeholder: '请输入个人简介',
rows: 3,
},
},
];
// Grid 布局表单字段配置
const gridColumns: FormColumn<any>[] = [
{
dataIndex: 'name',
title: '姓名',
valueType: 'text',
colProps: { span: 12 },
rules: [{ required: true, message: '请输入姓名' }],
fieldProps: { placeholder: '请输入姓名' },
},
{
dataIndex: 'age',
title: '年龄',
valueType: 'digit',
colProps: { span: 12 },
fieldProps: { placeholder: '请输入年龄', min: 0, max: 150, style: { width: '100%' } },
},
{
dataIndex: 'gender',
title: '性别',
valueType: 'radio',
colProps: { span: 12 },
fieldProps: {
options: [
{ label: '男', value: 'male' },
{ label: '女', value: 'female' },
],
},
},
{
dataIndex: 'status',
title: '状态',
valueType: 'switch',
colProps: { span: 12 },
valuePropName: 'checked',
},
{
dataIndex: 'birthDate',
title: '出生日期',
valueType: 'date',
colProps: { span: 12 },
fieldProps: { style: { width: '100%' } },
},
{
dataIndex: 'salary',
title: '薪资',
valueType: 'money',
colProps: { span: 12 },
fieldProps: { style: { width: '100%' }, placeholder: '请输入薪资' },
},
];
// 完整表单字段配置 - 展示所有字段类型
const fullColumns: FormColumn<any>[] = [
{
dataIndex: 'text',
title: '文本输入',
valueType: 'text',
tooltip: '这是一个帮助提示',
fieldProps: { placeholder: '普通文本输入' },
},
{
dataIndex: 'password',
title: '密码输入',
valueType: 'password',
fieldProps: { placeholder: '密码输入' },
},
{
dataIndex: 'digit',
title: '数字输入',
valueType: 'digit',
extra: '请输入0-100之间的数字',
fieldProps: {
placeholder: '数字输入',
min: 0,
max: 100,
},
},
{
dataIndex: 'money',
title: '金额输入',
valueType: 'money',
fieldProps: {
placeholder: '金额输入',
width: '100%',
},
},
{
dataIndex: 'select',
title: '下拉选择',
valueType: 'select',
fieldProps: {
placeholder: '请选择',
options: [
{ label: '选项一', value: 'option1' },
{ label: '选项二', value: 'option2' },
{ label: '选项三', value: 'option3' },
],
},
},
{
dataIndex: 'treeSelect',
title: '树形选择',
valueType: 'treeSelect',
fieldProps: {
placeholder: '请选择',
treeData: [
{ title: '父节点1', value: 'parent1', children: [
{ title: '子节点1-1', value: 'child1-1' },
{ title: '子节点1-2', value: 'child1-2' },
]},
{ title: '父节点2', value: 'parent2' },
],
},
},
{
dataIndex: 'cascader',
title: '级联选择',
valueType: 'cascader',
fieldProps: {
placeholder: '请选择',
options: [
{ label: '浙江', value: 'zhejiang', children: [
{ label: '杭州', value: 'hangzhou' },
{ label: '宁波', value: 'ningbo' },
]},
{ label: '江苏', value: 'jiangsu', children: [
{ label: '南京', value: 'nanjing' },
{ label: '苏州', value: 'suzhou' },
]},
],
},
},
{
dataIndex: 'radio',
title: '单选框',
valueType: 'radio',
fieldProps: {
options: [
{ label: '选项A', value: 'a' },
{ label: '选项B', value: 'b' },
{ label: '选项C', value: 'c' },
],
},
},
{
dataIndex: 'radioButton',
title: '单选按钮',
valueType: 'radioButton',
fieldProps: {
options: [
{ label: '按钮A', value: 'a' },
{ label: '按钮B', value: 'b' },
{ label: '按钮C', value: 'c' },
],
},
},
{
dataIndex: 'checkbox',
title: '多选框',
valueType: 'checkbox',
fieldProps: {
options: [
{ label: '选项1', value: '1' },
{ label: '选项2', value: '2' },
{ label: '选项3', value: '3' },
],
},
},
{
dataIndex: 'switch',
title: '开关',
valueType: 'switch',
valuePropName: 'checked',
},
{
dataIndex: 'rate',
title: '评分',
valueType: 'rate',
},
{
dataIndex: 'slider',
title: '滑动条',
valueType: 'slider',
},
{
dataIndex: 'date',
title: '日期选择',
valueType: 'date',
},
{
dataIndex: 'dateTime',
title: '日期时间',
valueType: 'dateTime',
},
{
dataIndex: 'dateRange',
title: '日期范围',
valueType: 'dateRange',
},
{
dataIndex: 'time',
title: '时间选择',
valueType: 'time',
},
{
dataIndex: 'color',
title: '颜色选择',
valueType: 'color',
},
{
dataIndex: 'icon',
title: '图标选择',
valueType: 'icon',
},
{
dataIndex: 'image',
title: '图片上传',
valueType: 'image',
fieldProps: {
action: '/file/upload', // 假设的上传接口
maxCount: 1,
}
},
{
dataIndex: 'user',
title: '用户选择',
valueType: 'user',
fieldProps: {
placeholder: '请选择用户',
}
},
{
dataIndex: 'textarea',
title: '多行文本',
valueType: 'textarea',
fieldProps: { placeholder: '请输入多行文本', rows: 3 },
},
];
// 字段分组配置
const groupColumns: FormColumn<any>[] = [
{
valueType: 'text',
colProps: { span: 24 },
fieldRender: () => <Divider titlePlacement={'left'}></Divider>
},
{
dataIndex: 'name',
title: '姓名',
valueType: 'text',
colProps: { span: 12 },
rules: [{ required: true, message: '请输入姓名' }],
},
{
dataIndex: 'phone',
title: '电话',
valueType: 'text',
colProps: { span: 12 },
tooltip: '请输入11位手机号',
},
{
valueType: 'text',
colProps: { span: 24 },
fieldRender: () => <Divider titlePlacement={'left'}></Divider>
},
{
dataIndex: 'department',
title: '部门',
valueType: 'select',
colProps: { span: 12 },
fieldProps: {
options: [
{ label: '技术部', value: 'tech' },
{ label: '产品部', value: 'product' },
{ label: '运营部', value: 'operation' },
],
},
},
{
dataIndex: 'position',
title: '职位',
valueType: 'text',
colProps: { span: 12 },
},
{
dataIndex: 'remark',
title: '备注',
valueType: 'textarea',
extra: '可选填写',
},
];
// 依赖联动配置
interface DependencyFormData {
hasDiscount: boolean;
discount: number;
productType: string;
subType: string;
}
const dependencyColumns: FormColumn<DependencyFormData>[] = [
{
dataIndex: 'hasDiscount',
title: '是否有折扣',
valueType: 'switch',
valuePropName: 'checked',
tooltip: '开启后可设置折扣比例',
},
{
dataIndex: 'discount',
title: '折扣比例',
valueType: 'slider',
dependency: {
dependencies: ['hasDiscount'],
visible: (values) => values.hasDiscount === true,
},
fieldProps: {
min: 0,
max: 100,
marks: { 0: '0%', 50: '50%', 100: '100%' },
},
},
{
dataIndex: 'productType',
title: '产品类型',
valueType: 'select',
fieldProps: {
placeholder: '请选择产品类型',
options: [
{ label: '电子产品', value: 'electronics' },
{ label: '服装', value: 'clothing' },
{ label: '食品', value: 'food' },
],
},
},
{
dataIndex: 'subType',
title: '子类型',
valueType: 'select',
dependency: {
dependencies: ['productType'],
visible: (values) => !!values.productType,
disabled: (values) => !values.productType,
fieldProps: (values) => {
const subTypeOptions: Record<string, { label: string; value: string }[]> = {
electronics: [
{ label: '手机', value: 'phone' },
{ label: '电脑', value: 'computer' },
{ label: '平板', value: 'tablet' },
],
clothing: [
{ label: '上衣', value: 'top' },
{ label: '裤子', value: 'pants' },
{ label: '鞋子', value: 'shoes' },
],
food: [
{ label: '零食', value: 'snack' },
{ label: '饮料', value: 'drink' },
{ label: '水果', value: 'fruit' },
],
};
return {
placeholder: '请先选择产品类型',
options: subTypeOptions[values.productType as string] || [],
};
},
},
},
];
// 自定义渲染配置
const customColumns: FormColumn<any>[] = [
{
dataIndex: 'username',
title: '用户名',
valueType: 'text',
rules: [{ required: true, message: '请输入用户名' }],
},
{
dataIndex: 'tags',
title: '标签',
valueType: 'text',
fieldRender: (form) => (
<Space>
<Input
placeholder="输入标签后按回车"
onPressEnter={(e) => {
const value = (e.target as HTMLInputElement).value;
const tags = form.getFieldValue('tags') || [];
if (value && !tags.includes(value)) {
form.setFieldValue('tags', [...tags, value]);
}
(e.target as HTMLInputElement).value = '';
}}
/>
<Space>
{(form.getFieldValue('tags') || []).map((tag: string, index: number) => (
<Tag
key={index}
closable
onClose={() => {
const tags = form.getFieldValue('tags') || [];
form.setFieldValue('tags', tags.filter((_: string, i: number) => i !== index));
}}
>
{tag}
</Tag>
))}
</Space>
</Space>
),
},
{
dataIndex: 'hiddenField',
title: '隐藏字段',
valueType: 'text',
hidden: true,
},
];
// Modal/Drawer 表单字段配置
const modalColumns: FormColumn<any>[] = [
{
dataIndex: 'title',
title: '标题',
valueType: 'text',
rules: [{ required: true, message: '请输入标题' }],
fieldProps: { placeholder: '请输入标题' },
},
{
dataIndex: 'type',
title: '类型',
valueType: 'select',
rules: [{ required: true, message: '请选择类型' }],
fieldProps: {
placeholder: '请选择类型',
options: [
{ label: '类型一', value: 'type1' },
{ label: '类型二', value: 'type2' },
],
},
},
{
dataIndex: 'priority',
title: '优先级',
valueType: 'radioButton',
fieldProps: {
options: [
{ label: '低', value: 'low' },
{ label: '中', value: 'medium' },
{ label: '高', value: 'high' },
],
},
},
{
dataIndex: 'content',
title: '内容',
valueType: 'textarea',
fieldProps: { placeholder: '请输入内容', rows: 4 },
},
];
/**
* XinForm 组件使用示例页面
*/
const XinFormExample: React.FC = () => {
const modalFormRef = useRef<XinFormRef>(undefined);
const drawerFormRef = useRef<XinFormRef>(undefined);
const handleFinish = async (values: any) => {
console.log('表单提交:', values);
message.success('表单提交成功!');
return true;
};
return (
<div>
<Typography style={{ margin: '12px 0 24px 0' }}>
<Title level={2}>XinForm </Title>
<Paragraph>
Ant Design Form JSON
</Paragraph>
</Typography>
<Space direction="vertical" size="large" style={{ width: '100%' }}>
{/* 基础表单 */}
<Card title="基础表单" variant={'borderless'}>
<Paragraph type="secondary">
columns
</Paragraph>
<XinForm
columns={basicColumns}
layout="vertical"
onFinish={handleFinish}
/>
</Card>
{/* Grid 布局表单 */}
<Card title="Grid 布局表单" variant={'borderless'}>
<Paragraph type="secondary">
使 grid colProps
</Paragraph>
<XinForm
columns={gridColumns}
layout="vertical"
grid={true}
rowProps={{ gutter: 16 }}
onFinish={handleFinish}
/>
</Card>
{/* 所有字段类型 */}
<Card title="所有字段类型" variant={'borderless'}>
<Paragraph type="secondary">
XinForm textpassworddigitmoneyselecttreeSelectcascaderradiocheckboxswitchratesliderdatetimecoloricontextarea
<br />
<Text code>tooltip</Text> <Text code>extra</Text> <Text code>width</Text>
</Paragraph>
<XinForm
columns={fullColumns}
layout="vertical"
onFinish={handleFinish}
/>
</Card>
{/* 字段分组 */}
<Card title="字段分组" variant={'borderless'}>
<Paragraph type="secondary">
使 <Text code>group</Text> 线
</Paragraph>
<XinForm
columns={groupColumns}
layout="vertical"
grid={true}
rowProps={{ gutter: 16 }}
onFinish={handleFinish}
/>
</Card>
{/* 依赖联动 */}
<Card title="依赖联动" variant={'borderless'}>
<Paragraph type="secondary">
使 <Text code>dependency</Text>
<br />
- <Text code>visible</Text>/
<br />
- <Text code>disabled</Text>
<br />
- <Text code>fieldProps</Text>
</Paragraph>
<XinForm<DependencyFormData>
columns={dependencyColumns}
layout="vertical"
initialValues={{ hasDiscount: false }}
onFinish={handleFinish}
/>
</Card>
{/* 自定义渲染 */}
<Card title="自定义渲染" variant={'borderless'}>
<Paragraph type="secondary">
使 <Text code>valueType="custom"</Text> + <Text code>renderField</Text>
<br />
使 <Text code>hidden</Text>
</Paragraph>
<XinForm
columns={customColumns}
layout="vertical"
onFinish={handleFinish}
/>
</Card>
{/* ModalForm 弹窗表单 */}
<Card title="ModalForm 弹窗表单" variant={'borderless'}>
<Paragraph type="secondary">
使 layoutType="ModalForm" trigger formRef /
</Paragraph>
<Space>
<XinForm
columns={modalColumns}
layoutType="ModalForm"
layout="vertical"
trigger={<Button type="primary">使 Trigger </Button>}
modalProps={{ title: '新建任务', width: 520 }}
onFinish={handleFinish}
/>
<Button onClick={() => modalFormRef.current?.open()}>
使 Ref
</Button>
<XinForm
columns={modalColumns}
layoutType="ModalForm"
layout="vertical"
formRef={modalFormRef}
modalProps={{ title: '新建任务 (Ref)', width: 520 }}
onFinish={handleFinish}
/>
</Space>
</Card>
{/* DrawerForm 抽屉表单 */}
<Card title="DrawerForm 抽屉表单" variant={'borderless'}>
<Paragraph type="secondary">
使 layoutType="DrawerForm"
</Paragraph>
<Space>
<XinForm
columns={modalColumns}
layoutType="DrawerForm"
layout="vertical"
trigger={<Button type="primary">使 Trigger </Button>}
drawerProps={{ title: '新建任务', width: 520 }}
onFinish={handleFinish}
/>
<Button onClick={() => drawerFormRef.current?.open()}>
使 Ref
</Button>
<XinForm
columns={modalColumns}
layoutType="DrawerForm"
layout="vertical"
formRef={drawerFormRef}
drawerProps={{ title: '新建任务 (Ref)', width: 520 }}
onFinish={handleFinish}
/>
</Space>
</Card>
{/* 自定义提交按钮 */}
<Card title="自定义提交按钮" variant={'borderless'}>
<Paragraph type="secondary">
submitter
</Paragraph>
<Divider titlePlacement={'left'}></Divider>
<XinForm
columns={basicColumns.slice(0, 2)}
layout="vertical"
onFinish={handleFinish}
submitter={{
submitText: '确认提交',
resetText: '清空表单',
}}
/>
<Divider titlePlacement={'left'}></Divider>
<Text type="secondary"> submitter.render = false </Text>
<XinForm
columns={basicColumns.slice(0, 2)}
layout="vertical"
onFinish={handleFinish}
submitter={{ render: false }}
/>
</Card>
</Space>
</div>
);
};
export default XinFormExample;
+145
View File
@@ -0,0 +1,145 @@
import { Button, Tag } from 'antd';
import { ExportOutlined } from '@ant-design/icons';
import type {XinTableColumn, XinTableProps} from '@/components/XinTable/typings';
import XinTable from '@/components/XinTable';
// 模拟用户数据类型
interface User {
id: number;
name: string;
email: string;
age: number;
status: number;
role: string;
department: string;
createdAt: string;
}
// 模拟数据
const mockData: User[] = Array.from({ length: 50 }, (_, i) => ({
id: i + 1,
name: `用户${i + 1}`,
email: `user${i + 1}@example.com`,
age: Math.floor(Math.random() * 40) + 20,
status: Math.random() > 0.3 ? 1 : 0,
role: ['管理员', '编辑', '访客'][Math.floor(Math.random() * 3)],
department: ['技术部', '产品部', '运营部', '市场部'][Math.floor(Math.random() * 4)],
createdAt: new Date(Date.now() - Math.random() * 10000000000).toLocaleDateString(),
}));
/**
* XinTable 示例页面
*/
const XinTableExample = () => {
// 表格列配置
const columns: XinTableColumn<User>[] = [
{
title: '序号',
width: 60,
valueType: 'text',
dataIndex: 'id',
},
{
dataIndex: 'name',
title: '用户名',
width: 120,
valueType: 'text',
required: true,
},
{
dataIndex: 'email',
title: '邮箱',
valueType: 'text',
width: 200,
ellipsis: true,
},
{
dataIndex: 'age',
title: '年龄',
width: 80,
valueType: 'digit',
sorter: (a: User, b: User) => a.age - b.age,
hideInSearch: true
},
{
dataIndex: 'status',
title: '状态',
width: 100,
valueType: 'select',
// 自定义渲染 - 用户外理显示内容
render: (value: number) => {
const enumMap: Record<number, { text: string; color: string }> = {
1: { text: '启用', color: 'green' },
0: { text: '禁用', color: 'red' },
};
const item = enumMap[value];
return item ? <Tag color={item.color}>{item.text}</Tag> : '-';
},
filters: [
{ text: '启用', value: 1 },
{ text: '禁用', value: 0 },
],
},
{
dataIndex: 'role',
title: '角色',
width: 100,
valueType: 'select',
fieldProps: {
options: [
{ label: '管理员', value: '管理员' },
{ label: '编辑', value: '编辑' },
{ label: '访客', value: '访客' },
],
}
},
{
dataIndex: 'department',
title: '部门',
width: 120,
valueType: 'select',
fieldProps: {
options: [
{ label: '技术部', value: '技术部' },
{ label: '产品部', value: '产品部' },
{ label: '运营部', value: '运营部' },
{ label: '市场部', value: '市场部' },
],
},
hideInSearch: true,
},
{
dataIndex: 'createdAt',
title: '创建时间',
width: 120,
hideInForm: true,
hideInSearch: true,
},
];
// 自定义工具栏
const customToolbar: XinTableProps['toolBarRender'] = (dom) => [
<Button key="export" icon={<ExportOutlined />}>
</Button>,
dom.columnSetting,
dom.hideBorder,
dom.reload,
dom.columnHeight
];
return (
<XinTable
columns={columns}
rowKey="id"
dataSource={mockData}
accessName='system.user.list'
api={'/system-user/list'}
toolBarRender={customToolbar}
/>
);
};
export default XinTableExample;