ant-design / sunflower Goto Github PK
View Code? Open in Web Editor NEW🦹 Process components for antd4 & antd3 by alipay industry technology
Home Page: https://ant-design.github.io/sunflower
License: MIT License
🦹 Process components for antd4 & antd3 by alipay industry technology
Home Page: https://ant-design.github.io/sunflower
License: MIT License
有两个疑问可以麻烦解答一下吗?
sorter
,onFilter
是通过Table的onChange
事件去单独处理吗?search
方法的参数如何与Column中的sorter
,onFilter
参数进行合并?import { Modal, Form } from 'antd';
import { useModalForm } from 'sunflower-antd';
function App(props) {
const { modalProps, formProps, show, close, visible } = useModal({
defaultVisible: false, // default default visible
autoSubmitClose: true, // default close modal after submit
async submit() {},
})
return <>
<Modal okText="submit" {...modalProps}>
<Form {...formProps} />
</Modal>
modal visible: {visible}
<a onClick={() => show()}>show modal</a>
<a onClick={() => close()}>close modal</a>
</>
}
export default App;
click Modal "ok", will trigger submit, then close modal
useModalForm 可以做一个表格操作的例子吗?类似https://ant.design/components/table-cn/#components-table-demo-jsx 包括添加、编辑
用于将值回填到 Form
type StoreBaseValue = string | number | boolean;
type StoreValue = StoreBaseValue | Store | StoreBaseValue[];
interface Store {
[name: string]: StoreValue;
}
initialValues: () => (Store | Promise<Store>)
const obj = useSearchResult({
initialValues,
search,
});
修改点:
antd3
import { Form, Table } from 'antd';
import { useFormTable } from 'sunflower-antd';
function App(props) {
const { formProps, tableProps, form } = useFormTable({
form: props.form,
search() {},
})
return <>
<Form layout="inline" {...formProps} >
<Form.Item>
{form.getFieldDecorator('username')(
<Input />,
)}
</Form.Item>
</Form>
<Table {...tableProps} />
</>
}
export default Form.create()(App);
antd4
import { Form, Table } from 'antd';
import { useFormTable } from 'sunflower-antd';
function App() {
const { formProps, tableProps, form } = useFormTable({
form, // 可选通过 const [form] = Form.useForm() 传入,也可不传
search() {},
})
return <>
<Form layout="inline" {...formProps} >
<Form.Item>
<Input />
</Form.Item>
</Form>
<Table {...tableProps} />
</>
}
export default App;
Hi guys, thanks in advance for the help
I've imported useStepsForm and used it as shown in the example code. Since the very beginning of using this hook I've not been able to preserve the field values when returning to a previous step in my form. The validation does work in this case.
If I then take the form prop returned from the useStepsForm hook and name it something besides 'form', the validation fails but the data from each field is properly preserved on previous steps after progressing further in the form. I suspect the validation fails because in this case the form isn't actually the same one as before, so it doesn't recognize the validation rules.
Any insights on this issue would be greatly appreciated! Let me know if you need any other information
const [rootForm] = Form.useForm();
const { form, current, gotoStep, submit } = useStepsForm({
submit: async (values) => {
onFinish(values);
await new Promise((r) => setTimeout(r, 1000));
return 'ok';
},
form: rootForm,
total: fieldsKeys.length,
isBackValidate: false,
});
import { Modal, Form } from 'antd';
import { useModal } from 'sunflower-antd';
function App(props) {
const { modalProps, show, close, visible } = useModal({
defaultVisible: false, // default default visible
autoSubmitClose: true, // default close modal after submit
async submit() {},
})
return (
<>
<Modal {...modalProps}>
{content}
</Modal>
modal visible: {visible}
<a onClick={() => show()}>show modal</a>
<a onClick={() => close()}>close modal</a>
</>)
}
export default App;
https://ant.design/components/table-cn/#components-table-demo-ajax
const { Table } = useRemoteTable({
search({ sorter, fitlers, currentPage, pageSize }) {
return request();
};
});
<Table />
Is it possible to make gotoStep wihtout checking form validation(Form.item required)?
sunflower 出发点是为了让使用 antd 开发业务更高效。
业务中有不少看起来差不多的页面,但是这些看起来差不多的流程在不同的应用需要重复使用 antd 进行编码。虽然可采用模板,但是模板的维护性跟更新都是问题,而另外的一个方式是做组件。
最先的方式就是做组件,一个组件对应一个配置。对于使用者,根据文档来对这个组件进行 json 配置使用,比如要做一个搜索表单:
之前的做法是这样的,开发一个 SearchResult 组件,进行配置使用:
<FormTable config={{
fields: [{
label: 'Username',
name: 'username'
type: 'input',
}],
columns: [{
title: 'Username',
dataIndex: 'username'
}, {
title: 'Email',
dataIndex: 'email'
}],
search: () => {}
}}/>
在业务中进行使用起来刚开始好像很方便,但其实有不少问题:
之前的做法是在配置中给出业务可能需要的点(哪些可能呢?找一个一个的业线来看,还不行就靠猜😆),比如在配置中有了很不靠谱的属性:
{
tableTop: <div>这里业务可加上文本</div>,
searchRight: <a>这里业务可加上链接</a>
}
想统一业务线不这样,但是从技术角度限制业务也不太对(产品说这里放个链接就是方便)。
还有一个问题就是 fields 的 type 太多了。一个是 antd 太强大,有不同的输入方式,比如 input,select,dataPicker 等,还有一个是业务中自定义的也很多。做到组件里这个组件也就不可维护了,也许也可采用自定义、插件等方式,比如:
{
type: 'custom',
render(value, onChange) {}
}
其实代码又有了 jsx,就成为了 json + jsx,维护性跟易用性也下降了。
那业务中什么是能抽象的呢?比如搜索表单这个流程,用户进行搜索之后查看,是否能只包括这个流程,而 ui 则使用模板的方式。这个是第二次探索。受限于 antd3 的 Form 需要 HOC,新的方式采用 render props 来做,也就是:
{
formTable(config, ({ getFieldDecorator, onSubmit, dataSource ... }) => <>
<Form onSubmit={onSubmit}>
<Form.Item>
{getFieldDecorator('username', {
rules: [{ required: true, message: 'Please input your username!' }],
})(
<Input />,
)}
</Form.Item>
</Form>
<Table columns={[{
title: 'Username',
dataIndex: 'username',
key: 'username',
}]} dataSource={dataSource} />
</>)
}
对于用户,使用这个流程就不需要再去维护 onChange 等方法,方法跟状态都是流程在维护。而对于 ui 也可灵活自由。但是也有问题,render props 用起来也相对麻烦。接着又尝试修改 render props 到 react hooks。但是 antd3 的 Form 搞不定这个,因此只能暂时先用最新版本的底层 form 来做,使用则为:
function Component() {
const { form, onFinish, dataSource, onPageChange, currentPage ... } = useFormTable({
search: (values) => request(values),
});
return <div>
<Form form={form} onFinish={onFinish}>
<Form.Item
label="Username"
name="username"
>
<Input placeholder="Username" />
</Form.Item>
<Form.Item>
<Button type="primary" htmlType="submit">
Search
</Button>
</Form.Item>
</Form>
<Table
columns={[
{
title: 'Username',
dataIndex: 'username',
key: 'username',
},
{
title: 'Email',
dataIndex: 'email',
key: 'email',
}
]}
pagination={{
currentPage,
onChange: onPageChange,
}},
dataSource={dataSource}
rowKey="id"
/>
</div>;
}
看起来还不错。但流程复杂后,要用户在代码中去绑定的方法跟状态也会比较多,能否更方便一些?
绑定 props 复杂,那就 HOC 来搞,也就是 react hooks 做流程,HOC 做组件方便用户使用
function Component() {
import { useFormTable } from '@sunflower-antd/form-table';
const { Form, Table } = useFormTable({
search: (values) => request(values),
});
return <div>
<Form>
<Form.Item
label="Username"
name="username"
>
<Input placeholder="Username" />
</Form.Item>
<Form.Item>
<Button type="primary" htmlType="submit">
Search
</Button>
</Form.Item>
</Form>
<Table
columns={[
{
title: 'Username',
dataIndex: 'username',
key: 'username',
},
{
title: 'Email',
dataIndex: 'email',
key: 'email',
}
]}
rowKey="id"
/>
</div>;
}
Form, Table 其实就是 antd 的 Form 跟 Table 做的 HOC,让用户不用去绑定太多。用户能传 antd 对应组件的 props 到组件,这样 ui 能力则是 antd 的 ui 能力。而对于自定义的需求,也可使用方法跟状态,比如不想用 Table,想自定义方式:
import { useFormTable } from '@sunflower-antd/form-table';
const { Form, responseData } = useFormTable({
search: (values) => request(values),
});
return <div>
<Form>
...
</Form>
{
responseList.list.map(item => <div>
{item.username}
</div>)
}
</div>
经过探索,sunflower 采用底层为 @sunflower-hooks
,这一层是不包括 HOC 的 hooks,是描述业务流程的本质,称为“流程hooks”。在上层,使用 @sunflower-antd
,这一层是包括 HOC 组件的,方便用户使用,依赖于 @sunflower-hooks
,可称为“组件hooks”。
比如 @sunflower-antd/form-table
就是 antd 的表单搜索表格,底层依赖于 @sunflower-hooks/search-result
。@sunflower-hooks/search-result
去处理搜索返回的流程,用于查找等流程。也就是说 @sunflower-hooks/search-result
也可有不同的组件来依赖,比如 antd mobile 等。
以下是需要沉淀的 hooks:
我自己尝试按照这个思路实现了一下搜索业务,发现存在一个问题,每次render的时候使用的都是不同的组件,更详细的内容可以参考这个讨论。
另外useSearchResult的源码似乎有问题,useEffect等hook应该只能在纯函数组件的函数体中调用,我尝试源码中的写法会直接报错。
import React from 'react';
import { useForm } from 'sunflower-antd';
import { Input, Button, Form } from 'antd';
export default Form.create()(props => {
const { form } = props;
const { formProps, tableProps } = useForm({
form,
async submit(values) {},
defaultFormValues: {},
});
return <div>
<Form layout="inline" {...formProps}>
<Form.Item label="Username">
{
form.getFieldDecorator('username')(
<Input placeholder="Username" />
)
}
</Form.Item>
<Form.Item label="Email">
{
form.getFieldDecorator('email')(
<Input placeholder="Email" />
)
}
</Form.Item>
<Form.Item>
<Button onClick={() => form.resetFields()}>
Reset
</Button>
</Form.Item>
<Form.Item>
<Button type="primary" htmlType="submit">
Search
</Button>
</Form.Item>
</Form>
</div>
});
import React from 'react'
import { useStepsForm } from 'sunflower-antd'
import { Steps, Input, Button, Form, Result } from 'antd'
const { Step } = Steps
const layout = {
labelCol: { span: 8 },
wrapperCol: { span: 16 },
}
const tailLayout = {
wrapperCol: { offset: 8, span: 16 },
}
const Example = () => {
const { form, current, gotoStep, stepsProps, formProps, submit, formLoading } = useStepsForm({
async submit(values) {
const { username, email, address } = values
console.log(username, email, address)
await new Promise((r) => setTimeout(r, 1000))
return 'ok'
},
total: 4,
})
const formList = [
<>
<Form.Item
label='username'
name='username'
rules={[
{
required: true,
message: 'Please input username',
},
]}>
<Input placeholder='Username' />
</Form.Item>
<Form.Item label='Email' name='email'>
<Input placeholder='Email' />
</Form.Item>
<Form.Item {...tailLayout}>
<Button onClick={() => gotoStep(current + 1)}>Next</Button>
</Form.Item>
</>,
<>
<Form.Item
label='Age'
name='age'
rules={[
{
required: true,
message: 'Please input age',
},
]}>
<Input placeholder='age' />
</Form.Item>
<Form.Item {...tailLayout}>
<Button onClick={() => gotoStep(current + 1)}>Next</Button>
</Form.Item>
<Button onClick={() => gotoStep(current - 1)}>Prev</Button>
</>,
<>
<Form.Item
label='Address'
name='address'
rules={[
{
required: true,
message: 'Please input address',
},
]}>
<Input placeholder='Address' />
</Form.Item>
<Form.Item {...tailLayout}>
<Button
style={{ marginRight: 10 }}
type='primary'
loading={formLoading}
onClick={() => {
submit().then((result) => {
if (result === 'ok') {
gotoStep(current + 1)
}
})
}}>
Submit
</Button>
<Button onClick={() => gotoStep(current - 1)}>Prev</Button>
</Form.Item>
</>,
]
return (
<div>
<Steps {...stepsProps}>
<Step title='Step 1' />
<Step title='Step 2' />
<Step title='Step 3' />
<Step title='Step 4' />
</Steps>
<div style={{ marginTop: 60 }}>
<Form {...layout} {...formProps} style={{ maxWidth: 600 }}>
{formList[current]}
</Form>
{current === 3 && (
<Result
status='success'
title='Submit is succeed!'
extra={
<>
<Button
type='primary'
onClick={() => {
form.resetFields()
gotoStep(0)
}}>
Buy it again
</Button>
<Button>Check detail</Button>
</>
}
/>
)}
</div>
</div>
)
}
export default Example
In a scenario where I have more than two form fields, it sends the form ignoring the required fields because I can change it by clicking on the Steps.
It is a necessary behavior for users to change the form by clicking on it. In this case, the stepper structure does not work properly.
Only when I disable the onChange I can I get the form completely. But I don't think this is the best method.
Thank you for your help.
import { Steps, Form, Button } from 'antd'
import { useStepsForm } from 'sunflower-antd'
const { Step } = Steps
function App(props) {
const { form } = props
const { stepProps, current, total, formProps } = useStepsForm({
form,
total: 2,
defaultCurrent: 0,
async submit() {}, // submit form
})
return (
<>
<Steps {...stepProps}>
<Step title="first step" description="this is my first step" />
<Step title="second step" />
</Steps>
<Form {...formProps}>
{
current === 0 && (
<>
<Form.Item label="username">
{
form.getFieldDecorator('username')(
<Input placeholder="Username" />
)
}
</Form.Item>
</>
)
}
{
current === 1 && (
<>
<Form.Item label="Email">
{
form.getFieldDecorator('email')(
<Input placeholder="Email" />
)
}
</Form.Item>
</>
)
}
</Form>
{
current < total - 1 && <Button onClick={stepProps.goStep(1)}>下一步</Button>
}
</>
)
}
auto controlled step state and get the related form props
build error
function App(props) {
const { Form, Table, form } = useFormTable({
async search({ username, email, currentPage, pageSize, reset }) {
const res = await queryUsers({ username, email, currentPage, pageSize });
return {
list: res.result.list,
total: res.result.total,
};
},
resetAutoSearch: true,
});
const resetForm = () => {
reset(['username', 'email'])
}
return <>
<Form layout="inline">
<Form.Item label="用户名" name="username">
<Input placeholder="Username" />
</Form.Item>
<Form.Item label="邮箱" name="email">
<Input placeholder="Email" />
</Form.Item>
<Form.Item>
<Button onClick={resetForm}>重置</Button>
</Form.Item>
<Form.Item>
<Button type="primary" htmlType="submit">
搜索
</Button>
</Form.Item>
</Form>
<Table
style={{ marginTop: 12 }}
bordered
columns={[
{
title: '用户名',
dataIndex: 'username',
key: 'username',
},
{
title: '邮箱',
dataIndex: 'email',
key: 'email',
},
]}
rowKey="id"
/>
</>
}
useFormTable 接收resetAutoSearch默认为false, 暴露出reset 方法,若resetAutoSearch传值为true, 在清空表单时,再调用一次搜索接口,刷新表格
reset方法接收一个数组传参,只清除指定formFileds。默认清空整个表单。
@sunflower-antd
依赖 @sunflower-hooks
。
用户使用一般情况只用管 @sunflower-antd
比如:
import { useSearchResult } from '@sunflower-antd/search-result';
function Component() {
const { Form, Table } = useSearchResult(config);
return <div>
<Form />
<Table />
</div>
}
这样让 @sunflower-hooks 也更好测试,以及可被其他依赖,比如 antd mobile 等。
Hi, I ran yarn
then yarn start
after forking this repo, but I have no idea why it still fails like this :
Steps to reproduce :
yarn
to install dependencies.yarn start
.The error said :
[BABEL] /workspace/sunflower/.umirc.ts: Cannot find module '/workspace/sunflower/node_modules/@babel/preset-env/node_modules/@babel/compat-data/data/corejs3-shipped-proposals' (While processing: "/workspace/sunflower/node_modules/@umijs/babel-preset-umi/node.js$0")
Error: [BABEL] /workspace/sunflower/.umirc.ts: Cannot find module '/workspace/sunflower/node_modules/@babel/preset-env/node_modules/@babel/compat-data/data/corejs3-shipped-proposals' (While processing: "/workspace/sunflower/node_modules/@umijs/babel-preset-umi/node.js$0")
Any help will be appreciated, thank you.
I have a new Project with AntD V5 - are there any issues with this Repo?
Thanks hints...
const obj = useSearchResult(config);
config:
{
search: (payload: Store) => (Promise<SearchResponseData> | SearchResponseData); // 发送请求,返回值必须为 SearchResponseData
defaultPageSize?: number; // 默认的 pageSize,默认为 10
defaultCurrentPage?: number; // 默认的当前页,默认为 1
firstAutoSearch?: boolean; // 第一次默认请求,默认为 true
}
responseData
{
list: object[]; // 列表
total?: number; // 总数
}
obj
{
Form, // antd form 组件
Table, // antd Table 组件
loading, // 是否在请求中
requestData, // 请求参数
setRequestData, // 设置请求参数,比如用于自定义的一些请求 data 处理
responseData, // 返回数据
form, // antd form 实例
}
其中 Form.Item 有属性:
<Form.Item
label="Username"
name="username"
rules={[{ required: true, message: 'Please input your username!' }]}
>
<Input placeholder="Username" />
</Form.Item>
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.