umijs / sula Goto Github PK
View Code? Open in Web Editor NEW🚀 Pluggable enterprise-level configurable framework based on antd.
Home Page: https://docs.sula.vercel.app
License: MIT License
🚀 Pluggable enterprise-level configurable framework based on antd.
Home Page: https://docs.sula.vercel.app
License: MIT License
Sula 的同学们内部讨论了下,觉得当前5分钟左右的视频教程是最有效的,因此最近我们会先着手录制《Sula 五分钟》系列教程,大家有特别希望了解的点请踊跃回复,非常感谢。
Currently, Sula + AntD + dependencies (lodash, moment, axios etc.) are coming up to 1.4MB bundle size in production mode. This is large and preferable to reduce it as much as possible.
rc-menu
(35KB), rc-select
(34KB), rc-field-form
(31KB) etc. that are being used in addition to AntD. Not sure if this duplication is required. It would be great if all these rc-*
components could be replaced with just AntD components, since AntD is anyway being used for the rest. Also, just 35KB for menu (rc-menu), or 34KB for select (rc-select) etc. sounds lot of wasteful size.axios
(12.47Kb) is a required dependency for sula core functionality? Can it be made a peer-dependency (optional by the user)?moment
(57.62KB) probably need to be replaced with day.js
lodash
(26.39KB) not sure how to reduce it - probably most of these used methods are already supported by ES6 natively? For example, assign
, isArray
etc. are natively supported by JS and utilizing them will reduce the additional dependency.const config = {
action: [
{
type: 'modalform',
fields: [...],
submit: ...
},
(ctx) => {
// access fieldsValue and submit result by ctx.results
}
]
}
当前 sula fetch 是基于 axios 的封装。
sula 针对数据返回阶段添加了各种钩子,这些钩子都是基于后端业务级响应格式的,这些钩子分为两类handler和adapter,你可以通过全局设置和局部设置两种方式来使用他们。
同时fetch还有两个插件(符合sula插件规范)扩展点,
handler的主要职责是做出行为处理,
针对后端业务级响应数据进行转换供handler以及业务模块的消费。
示例
// global.js (umi)
import FetchPlugin from '@sula/plugin-fetch';
// 全局配置
FetchPlugin.use({
bizRedirectHandler(response) {
const { data, code } = response;
if (Number(code) === 302 && data) {
window.location.href = data;
}
return response;
}
})
// 针对某个url的配置
FetchPlugin.use('/task/v1/info', {
bizRedirectHandler(response) {
const { data, code } = response;
if (Number(code) === 302 && data) {
window.location.href = data;
}
return response;
}
})
const config = {
type: 'fetch',
url: '/api/info.json',
method: 'GET',
params: {
name: 'sula'
},
convertParams: ({params}) => {
return {
...params,
category: 'fruit',
}
},
// 也支持数组,前一个的转换params会传入下一个
convertParams: [({params}) => {
return {
...params,
category: 'fruit',
}
}, 'uniformTransform'] // 采用插件名称
}
const config = {
type: 'fetch',
url: '/api/info.json',
method: 'GET',
params: {
name: 'sula'
},
converter: ({data}) => {
return {
...data,
area: 'apple',
}
},
// 同convertParams,也支持数组格式
}
类型:boolean | string;
类型:boolean | string;
add ValidatorPlugin
const formConfig = {
fields: [
{
name: "input",
label: "Input",
field: {
type: "input",
},
rules: [
{
validator: 'yourValidatorPlugin',
},
{
validator(ctx) {
return new Promise((resolve, reject) => {
if (ctx.value === "sula") {
resolve();
} else {
reject(new Error("请输入sula"));
}
});
},
},
],
},
],
};
eslint --cache --ext .js,.jsx,.ts,.tsx --format=pretty ./src
Unable to resolve path to module sula.
希望可以定制 CreateForm/StepForm/ModalForm submit 和 back 按钮属性
增加buttonProps
{
submitButtonProps: {
icon: 'plus',
children: '新建'
},
submit: {
url: '/v1/api',
}
}
如题,可否给给demo
const validator = (ctx) => {
return new Promise((resolve, reject) => {
if(ctx.value) {
resolve();
} else {
reject(new Error(`请输入${ctx.label}`))
}
})
}
add List Component, just like,
import { List } from 'sula';
and try to keep it's consistent with usage of table.
bizRequestAdapter(requestConfig) {
const { url, params, method } = requestConfig;
// requestConfig参数能取到url, params, method等参数,但是取不到headers参数
......
}
希望扩展一些参数
bizRequestAdapter(requestConfig) {
const { header } = requestConfig;
// 可以取到headers等一些参数
.....
}
想弄一个表单设计器,需要满足下面的需求
1、使用react-dnd拖拽(或者其他)
2、基于sula完成渲染表单
3、表单元素校验(支持单个校验或多个元素关联校验)
4、自定义组件 :查询框(点击Input弹出modal,选中记录 回填数据),table等
5、等等。。
另外:sula和formily的设计思路,覆盖的场景的区别?
大致效果如下:
model为view的时候,如果input框内容过长 disabled状态下是看不全内容的
request插件期望可以完全自定义
1.一个表单配置了N个下拉选择框 都是远程数据,其中可能有大部分是重复的数据源
2.保存的axios想使用系统里面自定义好的axios实例(已经定义了拦截器、响应器不想再在sula中再写一次)
是否可以提供一个扩展 传递一个axios实例,完成替换掉sula的axios;
这样重复的字典数据请求,授权的拦截、响应的拦截 在系统层面可以保存一致
dependency
配置相互关联表单栈溢出fields配置如下,CNY与USD的值相互影响关联
fields: [
{
name: 'CNY',
label: 'CNY',
field: 'inputnumber',
dependency: {
value: {
relates: ['USD'],
type: (ctx) => {
const { form, relates } = ctx;
const relatesValue = form.getFieldsValue(relates);
const value = relatesValue[relates[0]];
ctx.form.setFieldValue(ctx.name, value / 7);
},
},
},
},
{
name: 'USD',
label: 'USD',
field: 'inputnumber',
dependency: {
value: {
relates: ['CNY'],
type: (ctx) => {
const { form, relates } = ctx;
const relatesValue = form.getFieldsValue(relates);
const value = relatesValue[relates[0]];
ctx.form.setFieldValue(ctx.name, value * 7);
},
},
},
},
],
触发关联时报错:RangeError: Maximum call stack size exceeded
暂时解决方案:
不通过dependency
,直接在onValuesChange
中写关联逻辑
希望完全在后端管理界面描述的json,前端只需要嵌入sula事先打包好的框架js即可,无需做前端开发,仅需装载页面对应的json即可完成页面功能
模板无背景色,希望StepForm添加默认背景。
StepForm默认白色背景
const config = {
fields: [
{
name: 'name',
label: 'Name',
field: 'input',
show: true,
editable:true,
search:true
}, {
name: 'name2',
label: 'Name2',
field: 'input',
editable:true,
}
]}
这里show表示可以显示在表格中, editable表示可以显示在表单中可编辑,search表示可以作为表单查询
先创建ant design pro 模板,完全复制文档中的代码,依据文档中的说明安装插件和依赖,结果样式显示有问题。
最后一行return <CreateForm {...config} /> vscode提示如下错误:
(alias) const CreateForm: typeof _CreateForm
import CreateForm
No overload matches this call.
Overload 1 of 2, '(props: Readonly): CreateForm', gave the following error.
不能将类型“{ fields: { name: string; label: string; field: string; }[]; submit: { url: string; method: string; }; }”分配给类型“Pick<Readonly & Readonly<{ children?: ReactNode; }>, "name" | "form" | "slot" | "style" | "title" | "children" | ... 283 more ... | "ctxGetter">”。
The types of 'submit.method' are incompatible between these types.
不能将类型“string”分配给类型“"POST" | "head" | "link" | "get" | "GET" | "delete" | "DELETE" | "HEAD" | "options" | "OPTIONS" | "post" | "put" | "PUT" | "patch" | "PATCH" | "LINK" | "unlink" | "UNLINK" | undefined”。
Overload 2 of 2, '(props: CreateFormProps, context?: any): CreateForm', gave the following error.
不能将类型“{ fields: { name: string; label: string; field: string; }[]; submit: { url: string; method: string; }; }”分配给类型“Pick<Readonly & Readonly<{ children?: ReactNode; }>, "name" | "form" | "slot" | "style" | "title" | "children" | ... 283 more ... | "ctxGetter">”。
属性“submit”的类型不兼容。
不能将类型“{ url: string; method: string; }”分配给类型“RequestConfig”。ts(2769)
速览问题 (Alt+F8)
没有可用的快速修复
什么是表单关联,即当值放生变化时所关联影响的表单组件属性的行为,如下图,
表单关联场景可以分为四类:
以显示关联为例,当A为1且B为2或3时,C显示,否则C不显示。
基于以上我们可以将此规则抽象为,
const config = {
name: 'C',
dependency: {
visible: {
relates: ['A', 'B'],
inputs: [[1], [2,3]],
output: false,
defaultOutput: true, // inputs 未匹配时
}
}
}
表单关联场景非常多,例如下面场景
基于以上我们可以将此规则抽象为,
const config = {
name: 'C',
dependency: {
visible: {
relates: ['A'],
type: (ctx) => {
fetch('/api/record.json').then(res => {
ctx.form.setFieldVisible(res.foo);
})
},
defaultOutput: true,
}
}
}
假设A为1,C不显示,B为2,C也不显示,可以将此规则抽象为
const config = {
name: 'C',
dependency: {
visible: {
cases: [
{
relates: ['A'],
inputs: [[1]],
output: false,
},
{
relates: ['B'],
inputs: [[2]],
output: false,
}
],
defaultOutput: true,
}
}
}
export type DependencyType = 'source' | 'value' | 'visible' | 'disabled';
export interface Dependency {
relates: FieldNamePath[];
inputs?: any[][];
output?: any;
defaultOutput?: any;
type?: DependencyPlugin;
cases?: Dependency[];
}
export type Dependencies = Record<DependencyType, Dependency >;
使用ModalForm如何通过ref修改成edit模式?
同时ModalForm组件,使用remoteValues查询的时候需要动态带参,没找到哪儿可以传进参数的地方,只有看到写死的方式。
另一个问题,直接return ctx.modal.modalOk()或者return ctx.modal.modalCancel()外部show().then()中接收到的都是undefined,这时候我需要发送请求成功时才刷新页面,就得手动返回信息才能做判断,有些麻烦
Currently antd
components are being loaded statically. This increases the bundle size and initial load time. Since not all components are needed at the start, please load all AntD React components dynamically.
Lazy loading the components will avoid loading unused components. For example, if user registers their own components for, say table, then their components will be loaded (and the in-built table will never be loaded).
Please use something like below:
const Button = React.lazy(() => import(/* webpackChunkName: "sula-antd" */ 'antd/lib/button/button'));
const Col = React.lazy(() => import(/* webpackChunkName: "sula-antd" */ 'antd/lib/grid/col'));
const Form = React.lazy(() => import(/* webpackChunkName: "sula-antd" */ 'antd/lib/form/Form'));
const FormItem = React.lazy(() => import(/* webpackChunkName: "sula-antd" */ 'antd/lib/form/FormItem'));
const Input = React.lazy(() => import(/* webpackChunkName: "sula-antd" */ 'antd/lib/input/Input'));
const InputPassword = React.lazy(() => import(/* webpackChunkName: "sula-antd" */ 'antd/lib/input/Password'));
const Message = React.lazy(() => import(/* webpackChunkName: "sula-antd" */ 'antd/lib/message/index'));
const Row = React.lazy(() => import(/* webpackChunkName: "sula-antd" */ 'antd/lib/grid/row'));
const Spin = React.lazy(() => import(/* webpackChunkName: "sula-antd" */ 'antd/lib/spin/index'));
行为链指一串行为序列,例如,请求 -> 弹窗 -> 刷新表格 -> 隐藏loading,这就是一串行为序列,sula 使用 rope(绳子)来喻指行为序列。
sula rope 分为两部分 rope-container 和 rope。
示例
const config = {
render: {
type: 'button',
props: {
children: 'Submit'
},
confirm: 'Confirm to submit?',
tooltip: 'Submit information',
disabled: false,
visible: false,
action: {
type: 'fetch',
url: '/api/info',
method: 'GET'
}
}
}
示例
const config = {
before: (ctx) => ctx.name === 'sula',
finish: (ctx) => { ... }
error: (ctx) => {...}
final: (ctx) => {...}
resultPropName: validateFields
}
例如有a,b,c,d四个行为,在rope中最终都会转成
const config = ['a', 'b', 'c', 'd'];
例如,
const config = {
type: 'a',
finish: {
type: 'b',
finish: ['c', 'd']
}
}
一个和渲染插件结合的示例
const config = {
type: 'button',
props: {
children: 'Submit'
},
action: [
{
type: 'validateGroupFields',
args: ['TaskGroup'],
resultPropName: 'taskGroupFieldsValue',
},
(ctx) => {
return 'hello'
},
{
type: 'fetch',
url: '/api/info',
convertParams(ctx) {
return {
...ctx.results.taskGroupFieldsValue,
word: ctx.result,
}
}
}
]
}
sula 的request,如何获取resonse header中的信息
响应头中的信息能否拿到,bizDataAdapter只能拿到响应内容
如题
sula中未引入antd样式,在src目录下新建global.ts并导入antd主题
// global.ts
import 'antd/dist/antd.less';
如果我想换个主题怎么做,例如修改@primary-color.
antd中关于定制主题,可以通过建立一个单独的 less 变量文件,引入这个文件覆盖 antd.less 里的变量。这种方式我试了不可行。所以有其他解决方案吗?
当前view模式是直接采用禁用表单组件来呈现,应增加”字符串“组件做view展示
Currently the antd
, react
, eCharts
etc. are hard-linked into the package, making the produced bundle size large. Please consider
dynamic imports
This will reduce the bundle size, and webpack compile times will be small.
UMI documentation allows to use external scripts such as:
scripts: process.env.NODE_ENV === 'development' ? [
'https://gw.alipayobjects.com/os/lib/react/16.13.1/umd/react.development.js',
'https://gw.alipayobjects.com/os/lib/react-dom/16.13.1/umd/react-dom.development.js',
] : [
'https://gw.alipayobjects.com/os/lib/react/16.13.1/umd/react.production.min.js',
'https://gw.alipayobjects.com/os/lib/react-dom/16.13.1/umd/react-dom.production.min.js',
],
But in the code, in many places, the packages are hard coded as:
import React from 'react';
import { Form as AForm } from 'antd';
It would be great if the code loads such third party packages
window.React
is not already available. If already available, use it, instead of importing插件可以简单分为UI展示插件与行为插件,通过插件可以方便扩展配置规则,同时「基于宿主配置组件」的上下文(后面简称ctx),插件可以承载更加强大与灵活的能力。
所有插件都有如下形态
{
plugin: 'someone', // 可以理解为下面没有额外属性的简写
// 或者
plugin: {
type: 'someone', // 插件名称
prop1: 'foo',
prop2: 'boo'
}
// 或者
plugin: (ctx) => {
// ...someone logic
}
}
插件支持三种类型(还有dependency、convertParams、converter插件会单独一part列出):
除了config,渲染插件可以通过ctx来改变组件属性。
常见行为如请求、路由跳转、弹窗等,行为插件基于ctx可以将行为反馈结果施加到宿主配置组件。
与Form配置组件结合使用,antd数据录入组件及符合antd表单自定义组件规范的都可以转化为表单插件。
export type PluginType = 'render' | 'field' | 'action';
以下RenderPlugin、FieldPlugin、ActionPlugin对应了三种插件的配置规则。
// 渲染插件
export type RenderPluginFunction = (ctx: Ctx) => React.ReactElement;
// 渲染插件配置规则
export type RenderPlugin = {
type: string | RenderPluginFunction;
props: Record<string, any>;
functionProps: Record<string, (ctx: Ctx) => string>;
};
示例
const ctx = {name: 'sula'}
// 配置
const config = {
type: 'tag',
props: {
children: 'i am ${name}'
},
functionProps: {
closable: (ctx) => ctx.name === 'sula'
}
}
// 结果为
<Tag closable>i am sula</Tag>
// 行为插件
export type ActionPluginFunction = (ctx: Ctx) => Promise<any> | any | void;
export type ActionBeforeFunction = (ctx: Ctx) => Promise<boolean> | boolean | void;
export type ActionHookFunction = (ctx: Ctx) => void;
// 行为插件配置规则
export type ActionPlugin = {
type: string | ActionPluginFunction;
before?: ActionBeforeFunction;
error?: ActionHookFunction;
final?: ActionHookFunction;
finish?: ActionPlugin | ActionPlugin[];
[key: string]: any;
};
示例
const ctx = {
table: {
refresh: () => {},
}
}
// 配置
const config = {
render: {
type: 'button',
props: {
children: 'submit'
},
action: (ctx) => { // 可以变成一个插件例如refreshTable
ctx.table.refresh();
}
},
}
// 结果为
<Button onClick={() => {
ctx.table.refresh();
}}>submit</Button>
表单插件与渲染插件格式上是类似的,只是ctx与场景上的不同。
// 表单插件配置规则
export type FieldPlugin = RenderPlugin;
示例
const ctx = {mode: 'view'}
// 配置
const config = {
type: 'input',
props: {
placeholder: '@{mode} mode'
},
functionProps: {
disabled: (ctx) => ctx.name === 'view'
}
}
// 结果为
<Input placeholder="view mode" disabled />
插件是为了将重复逻辑更加复用,因此直接写方法可以理解为是插件的“内联”形式。
// 注册渲染插件
sula.renderType('tag', (ctx, config) => {
return <Tag {...config.props} />;
});
render: {
type: 'tag',
props: {
type: 'primary'
}
}
// 内联写法
render: (ctx) => {
return <Tag type="primary" />;
}
itemLayout: {
span: {
xxl: 6,
xl: 8,
lg: 8,
md: 8,
sm: 8,
xs: 12
}
}
由于适配antd的列过滤数据格式,因此search下发搜索参数为数组,但与后端模型类型不符,需要改为字符串类型。
Form 配置组件(Sula-Form)为表单插件(Field Plugin)的宿主配置组件,扮演了向表单插件提供 ctx 的角色;另一方面对 antd-form v4 的实例 API 进行了扩充与改写(支持表单关联)以及其他功能性扩充。
配置A
const config = {
container: {
// 分组 group
type: 'card',
props: {
title: 'Head',
},
},
fields: [
{
name: 'input',
field: {
type: 'input',
props: {
placeholder: 'i am input',
},
},
},
{
name: 'select',
field: {
type: 'select',
props: {
placeholder: 'i am select',
},
},
},
{
// 嵌套
container: {
type: 'card',
props: {
title: 'Inner Head',
},
},
fields: [
{
name: 'innerInput',
field: 'input',
},
],
},
],
};
<Form {...config} />;
Form可以理解为是 antd form的载体,将antd form的配置属性放入React Context中,供Field与FieldGroup组件使用,施加在 Form 上的 container 和 Field 配置并不被 Form直接消费,而是转交给 FieldGroup 和 Field 组件消费
为了支持分组(Group)与表单(Field)更灵活的布局,配置可以映射为对应组件
const config = {
name: 'input',
field: {
type: 'select',
}
}
return <Field {...config} />
const config = {
container: {
type: 'card',
props: { title: 'Head' }
},
fields: [{
name: 'input',
field: {
type: 'select',
}
}]
}
return <FieldGroup {...config} />
因此对于最上面的配置A可以映射为多种形式
const config = {...};
<Form>
<FieldGroup {...config} />
</Form>
<Form>
<FieldGroup container={{type: 'card', props: {title: 'Head'}}}>
<Field name="input" label="Input" field={{type: 'input', props: {placeholder: "i am input"}}} />
<Field name="select" label="Select" field={{type: 'select', props: {placeholder: "i am select"}}} />
<FieldGroup container={{type: 'card', props: {title: 'Inner Head'}}}>
<Field name="innerInput" field={{type: 'input'}}/>
</FieldGroup>
</FieldGroup>
</Form>
const config = {
actionsPosition: 'bottom', // 'default' / 'center' / 'right'
actionsRender: [
{
type: 'button',
props: {
children: 'Submit',
type: 'primary',
},
action: 'validateFields',
},
],
};
对应and Form.Item, 扩展属性
sula form 对 antd form 进行了实例API与扩展,
setFieldValue 会触发表单关联
setFieldSource
setFieldVisible
setFieldDisabled
getFieldValue
getFieldSource
getFieldVisible
getFieldDisabled
validateGroupFields
见 表单关联规范
支持
只支持 visible 关联
Same as onFinish but called when each field changed
请问一下, sula 是否支持我自己写一个上一页 下一页, 不带total 这种分页
业务场景: 由于后端大部份 是通过mysql 来存储, 有的时候考虑到mysql select count(*) 的性能问题, 我们是不给前端返回分页总数的, 但是sula 目前的分页组件,好像需要返回总数才可以使用, 所以请问一下, 我们如何自定义这种分页组件,给官方提pr ?
目前有这块的需求,要做表单设计的,发现sula-builder没有公开出来,目前看了下schema-generator
https://github.com/form-render/schema-generator
如果您和您的公司或组织使用了sula,非常感谢您的支持,非常欢迎您能留下公司或产品名,您的回复将成为维护者和观望者的信心来源。
在不泄露信息的前提下,建议把截图晒一晒~
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.