Merge branch 'baidu:master' into master

This commit is contained in:
刘丹 2022-06-13 14:04:13 +08:00 committed by GitHub
commit e75a2d7f2d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 173 additions and 55 deletions

View File

@ -433,23 +433,50 @@ window.echarts = amisRequire('echarts');
然后通过 script 标签引入这两个文件。
## 动态处理 echart 配置
echarts 的 config 一般是静态配置的,支持简单的数据映射。如果你觉得还不够灵活可以通过自己手写逻辑代码来完成配置。
通过配置 dataFiler 来处理。
```schema: scope="body"
{
"type": "chart",
"data": {
"line": [65, 63, 10, 73, 42, 21],
"line2": [22, 33, 90, 20, 11, 33]
},
"config": {
"xAxis": {
"type": "category",
"data": ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]
},
"yAxis": {
"type": "value"
}
},
"dataFilter": "config.series = [];Object.keys(data).forEach(function(key) {config.series.push({data: data[key], type: 'line'})})"
}
```
## 属性表
| 属性名 | 类型 | 默认值 | 说明 |
| ------------------ | -------------------------------------------- | --------- | ------------------------------------------------------------------ |
| type | `string` | `"chart"` | 指定为 chart 渲染器 |
| className | `string` | | 外层 Dom 的类名 |
| body | [SchemaNode](../../docs/types/schemanode) | | 内容容器 |
| api | [api](../../docs/types/api) | | 配置项接口地址 |
| source | [数据映射](../../docs/concepts/data-mapping) | | 通过数据映射获取数据链中变量值作为配置 |
| initFetch | `boolean` | | 组件初始化时,是否请求接口 |
| interval | `number` | | 刷新时间(最小 1000) |
| config | `object``string` | | 设置 eschars 的配置项,当为`string`的时候可以设置 function 等配置项 |
| style | `object` | | 设置根元素的 style |
| width | `string` | | 设置根元素的宽度 |
| height | `string` | | 设置根元素的高度 |
| replaceChartOption | `boolean` | `false` | 每次更新是完全覆盖配置项还是追加? |
| `trackExpression` | `string` | | 当这个表达式的值有变化时更新图表 |
| 属性名 | 类型 | 默认值 | 说明 |
| ------------------ | -------------------------------------------- | --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| type | `string` | `"chart"` | 指定为 chart 渲染器 |
| className | `string` | | 外层 Dom 的类名 |
| body | [SchemaNode](../../docs/types/schemanode) | | 内容容器 |
| api | [api](../../docs/types/api) | | 配置项接口地址 |
| source | [数据映射](../../docs/concepts/data-mapping) | | 通过数据映射获取数据链中变量值作为配置 |
| initFetch | `boolean` | | 组件初始化时,是否请求接口 |
| interval | `number` | | 刷新时间(最小 1000) |
| config | `object``string` | | 设置 eschars 的配置项,当为`string`的时候可以设置 function 等配置项 |
| style | `object` | | 设置根元素的 style |
| width | `string` | | 设置根元素的宽度 |
| height | `string` | | 设置根元素的高度 |
| replaceChartOption | `boolean` | `false` | 每次更新是完全覆盖配置项还是追加? |
| trackExpression | `string` | | 当这个表达式的值有变化时更新图表 |
| dataFilter | `string` | | 自定义 echart config 转换函数签名function(config, echarts, data) {return config;} 配置时直接写函数体。其中 config 是当前 echart 配置echarts 就是 echarts 对象data 为上下文数据。 |
## 动作表

View File

@ -55,9 +55,9 @@ title: 快速开始
使用方法:
- JS SDK
- 引入 sdk 中的文件 `<link rel="stylesheet" href="sdk/ helper.css" />`
- 引入 sdk 中的文件 `<link rel="stylesheet" href="sdk/helper.css" />`
- React
- `import 'amis/lib/ helper.css'`;
- `import 'amis/lib/helper.css'`;
目前这个文件没有和主题文件合并在一起,用户可以选择性加载。

View File

@ -66,8 +66,10 @@ class Preview extends React.Component {
}
componentWillUnmount() {
// TODO: amis render
// this.roots.forEach(root => root.unmount());
// unmout
window.requestAnimationFrame(() => {
this.roots.forEach(root => root.unmount());
});
}
divRef(ref) {

View File

@ -974,6 +974,9 @@ export default class Form extends React.Component<FormProps, object> {
if (!dispatcher?.prevented) {
env.notify('error', __('Form.validateFailed'));
}
/** 抛异常是为了在dialog中catch这个错误避免弹窗直接关闭 */
return Promise.reject(__('Form.validateFailed'));
} else {
dispatchEvent('validateSucc', this.props.data);
this.handleAction(

View File

@ -106,6 +106,7 @@
overflow: auto;
scroll-behavior: smooth;
background: var(--Tabs-content-bg);
position: relative;
> .#{$ns}AnchorNav-section {
display: block;

View File

@ -25,7 +25,7 @@ const collapseStyles: {
export interface CollapseProps {
key?: string;
id?: string;
collapseId?: string;
propKey?: string;
mountOnEnter?: boolean;
unmountOnExit?: boolean;

View File

@ -64,7 +64,7 @@ class CollapseGroup extends React.Component<
activeKey = [];
} else {
for (let i = 0; i < activeKey.length; i++) {
if (activeKey[i] === item.id) {
if (activeKey[i] === item.collapseId) {
activeKey.splice(i, 1);
break;
}
@ -72,9 +72,9 @@ class CollapseGroup extends React.Component<
}
} else {
if (this.props.accordion) {
activeKey = [item.id as string];
activeKey = [item.collapseId as string];
} else {
activeKey.push(item.id as string);
activeKey.push(item.collapseId as string);
}
}
this.setState({
@ -90,13 +90,13 @@ class CollapseGroup extends React.Component<
return children.map((child: React.ReactElement, index: number) => {
let props = child.props;
const id = props.propKey || String(index);
const collapsed = this.state.activeKey.indexOf(id) === -1;
const collapseId = props.propKey || String(index);
const collapsed = this.state.activeKey.indexOf(collapseId) === -1;
return React.cloneElement(child as any, {
...props,
key: id,
id,
key: collapseId,
collapseId,
collapsed,
expandIcon: this.props.expandIcon,
propsUpdate: true,

View File

@ -239,7 +239,7 @@ export class FormulaEditor extends React.Component<
handleVariableSelect(item: VariableItem) {
const {evalMode, selfVariableName} = this.props;
if (item && item.value && selfVariableName === item.value) {
if (item && item.value && (selfVariableName && selfVariableName === item.value)) {
toast.warning('不能使用当前变量[self],避免循环引用。');
return;
}

View File

@ -46,7 +46,7 @@ function VariableList(props: VariableListProps) {
: (option: Option, states: ItemRenderStates): JSX.Element => {
return (
<span className={cx(`${classPrefix}-item`, itemClassName)}>
{option.label && option.value === selfVariableName && (
{option.label && (selfVariableName && option.value === selfVariableName) && (
<Badge
classnames={cx}
badge={{
@ -58,7 +58,7 @@ function VariableList(props: VariableListProps) {
<label>{option.label}</label>
</Badge>
)}
{option.label && option.value !== selfVariableName && (
{option.label && (!selfVariableName || option.value !== selfVariableName) && (
<label>{option.label}</label>
)}
{option?.tag ? (

View File

@ -99,7 +99,7 @@ exports[`Renderer:chained-select 1`] = `
<div
class="cxd-Select-value"
>
B 0
B 1
</div>
</div>
<span

View File

@ -1,11 +1,15 @@
import React = require('react');
import {render, waitForElementToBeRemoved} from '@testing-library/react';
import {
render,
waitFor,
waitForElementToBeRemoved
} from '@testing-library/react';
import '../../../src';
import {render as amisRender} from '../../../src';
import {makeEnv, wait} from '../../helper';
test('Renderer:chained-select', async () => {
const {container, findByText} = render(
const {container, findByText, getByText, getByTestId} = render(
amisRender(
{
type: 'form',
@ -24,26 +28,60 @@ test('Renderer:chained-select', async () => {
},
{},
makeEnv({
fetcher: async (config: any) => {
return {
status: 200,
headers: {},
data: {
status: 0,
msg: '',
data: [
{label: 'A 0', value: 'a'},
{label: 'B 0', value: 'b'},
{label: 'C 0', value: 'c'},
{label: 'D 0', value: 'd'}
]
}
};
async fetcher(config: any): Promise<any> {
const level = parseInt(config.query.level, 10) || 0;
const maxLevel = parseInt(config.query.maxLevel, 10) || 0;
if (level >= maxLevel) {
return {
status: 200,
headers: {},
data: {
status: 0,
data: null
}
};
} else {
return {
status: 200,
headers: {},
data: {
status: 0,
msg: '',
data: [
{
label: `A ${level}`,
value: 'a'
},
{
label: `B ${level}`,
value: 'b'
},
{
label: `C ${level}`,
value: 'c'
},
{
label: `D ${level}`,
value: 'd'
}
]
}
};
}
}
})
)
);
await wait(500);
await waitFor(() => {
expect(getByText('B 1')).toBeInTheDocument();
expect(
container.querySelector('[data-testid="spinner"]')
).not.toBeInTheDocument();
});
expect(container).toMatchSnapshot();
});

View File

@ -670,7 +670,7 @@ export default class CRUD extends React.Component<CRUDProps, any> {
throw e;
}
});
} else if (action.actionType === 'reload') {
} else if (action.actionType === 'reload' && !action.target) {
this.reload();
} else if (
pickerMode &&
@ -2067,6 +2067,7 @@ export default class CRUD extends React.Component<CRUDProps, any> {
autoGenerateFilter,
onSelect,
autoFillHeight,
onEvent,
...rest
} = this.props;

View File

@ -122,11 +122,27 @@ const EVAL_CACHE: {[key: string]: Function} = {};
/**
* ECharts JSON
* ECharts intervalformattercolorminmaxlabelFormatterpageFormatteroptionToContentcontentToOptionanimationDelayanimationDurationUpdateanimationDelayUpdateanimationDurationpositionsort
* formattersort
* @param config ECharts
*/
function recoverFunctionType(config: object) {
['formatter', 'sort', 'renderItem'].forEach((key: string) => {
[
'interval',
'formatter',
'color',
'min',
'max',
'labelFormatter',
'pageFormatter',
'optionToContent',
'contentToOption',
'animationDelay',
'animationDurationUpdate',
'animationDelayUpdate',
'animationDuration',
'position',
'sort',
'renderItem'
].forEach((key: string) => {
const objects = findObjectsWithKey(config, key);
for (const object of objects) {
const code = object[key];

View File

@ -104,6 +104,13 @@ export default class TextAreaControl extends React.Component<
this.inputRef.current?.focus();
}
@autobind
@bindRendererEvent<TextAreaProps, TextAreaRendererEvent>('change')
handleChange(e: React.ChangeEvent<HTMLTextAreaElement>) {
const {onChange} = this.props;
onChange && onChange(e);
}
@autobind
@bindRendererEvent<TextAreaProps, TextAreaRendererEvent>('focus')
handleFocus(e: React.FocusEvent<HTMLTextAreaElement>) {
@ -142,7 +149,7 @@ export default class TextAreaControl extends React.Component<
const {...rest} = this.props;
return (
<Textarea {...rest} onFocus={this.handleFocus} onBlur={this.handleBlur} />
<Textarea {...rest} onFocus={this.handleFocus} onBlur={this.handleBlur} onChange={this.handleChange} />
);
}
}

View File

@ -99,11 +99,18 @@ export function StepsCmpt(props: StepsProps) {
useMobileUI
} = props;
const stepsRow =
(resolveVariableAndFilter(source, data, '| raw') as Array<StepSchema>) ||
let sourceResult: Array<StepSchema> = resolveVariableAndFilter(
source,
data,
'| raw'
) as Array<StepSchema>;
const stepsRow: Array<StepSchema> =
(Array.isArray(sourceResult) ? sourceResult : undefined) ||
config ||
steps ||
[];
const resolveRender = (val?: string | SchemaCollection) =>
typeof val === 'string' ? filter(val, data) : val && render('inner', val);
const value = getPropValue(props) ?? 0;

View File

@ -2818,6 +2818,22 @@ export class TableRenderer extends Table {
return scoped.send(subPath, values);
}
}
reload(subPath?: string, query?: any, ctx?: any) {
const scoped = this.context as IScopedContext;
const parents = scoped?.parent?.getComponents();
if (Array.isArray(parents) && parents.length) {
// CRUD的name会透传给Table这样可以保证找到CRUD
const crud = parents.find(cmpt => cmpt?.props?.name === this.props?.name);
return crud?.reload?.(subPath, query, ctx);
}
if (subPath) {
return scoped.reload(subPath, ctx);
}
}
}
export {TableCell};