Merge branch 'master' into fix-date-defaultValue

This commit is contained in:
liaoxuezhi 2023-11-23 19:29:51 +08:00 committed by GitHub
commit b292a66c42
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
72 changed files with 1006 additions and 289 deletions

View File

@ -958,11 +958,17 @@ Cards 模式支持 [Cards](./cards) 中的所有功能。
可以在列上配置`"sortable": true`,该列表头右侧会渲染一个可点击的排序图标,可以切换`正序`和`倒序`。
> 如果想默认就基于某个字段排序,可以结合 `defaultParams` 一起配置。
```schema: scope="body"
{
"type": "crud",
"syncLocation": false,
"api": "/api/mock2/sample",
"defaultParams": {
"orderBy": "engine",
"orderDir": "desc"
},
"columns": [
{
"name": "id",
@ -1074,7 +1080,7 @@ amis 只负责生成下拉选择器组件,并将搜索参数传递给接口,
#### 下拉数据源
过滤器的数据域支持API接口和上下文数据(`3.6.0`及以上版本)
过滤器的数据域支持 API 接口和上下文数据(`3.6.0`及以上版本)
```schema
{
@ -3378,22 +3384,22 @@ itemAction 里的 onClick 还能通过 `data` 参数拿到当前行的数据,
除了 Table 组件默认支持的列配置CRUD 的列配置还额外支持以下属性:
| 属性名 | 类型 | 默认值 | 说明 | 版本 |
| ------------------ | --------------------------------------------------------------- | ------- | --------------------------------------------------------------------------- | --- |
| ------------------ | --------------------------------------------------------------- | ------- | --------------------------------------------------------------------------- | ---- |
| sortable | `boolean` | `false` | 是否可排序 |
| searchable | `boolean` \| `Schema` | `false` | 是否可快速搜索,开启`autoGenerateFilter`后,`searchable`支持配置`Schema` |
| filterable | `boolean` \| [`QuickFilterConfig`](./crud.md#quickfilterconfig) | `false` | 是否可快速搜索,`options`属性为静态选项,支持设置`source`属性从接口获取选项 |
| quickEdit | `boolean` \| [`QuickEditConfig`](./crud.md#quickeditconfig) | - | 快速编辑,一般需要配合`quickSaveApi`接口使用 |
| quickEditEnabledOn | `SchemaExpression` | - | 开启快速编辑条件[表达式](../../docs/concepts/expression) | |
| quickEditEnabledOn | `SchemaExpression` | - | 开启快速编辑条件[表达式](../../docs/concepts/expression) | |
#### QuickFilterConfig
| 属性名 | 类型 | 默认值 | 说明 | 版本 |
| ------------- | ----------------------------- | ------- | -------------------------------------------------------- | ------- |
| options | `Array<any>` | - | 静态选项 | |
| multiple | `boolean` | `false` | 是否支持多选 | |
| source | [`Api`](../../docs/types/api) \| `string` | - | 选项 API 接口 | `3.6.0`版本后支持上下文变量 |
| refreshOnOpen | `boolean` | `false` | 配置 source 前提下,每次展开筛选浮层是否重新加载选项数据 | `2.9.0` |
| strictMode | `boolean` | `false` | 严格模式,开启严格模式后,会采用 JavaScript 严格相等比较 | `2.3.0` |
| 属性名 | 类型 | 默认值 | 说明 | 版本 |
| ------------- | ----------------------------------------- | ------- | -------------------------------------------------------- | --------------------------- |
| options | `Array<any>` | - | 静态选项 | |
| multiple | `boolean` | `false` | 是否支持多选 | |
| source | [`Api`](../../docs/types/api) \| `string` | - | 选项 API 接口 | `3.6.0`版本后支持上下文变量 |
| refreshOnOpen | `boolean` | `false` | 配置 source 前提下,每次展开筛选浮层是否重新加载选项数据 | `2.9.0` |
| strictMode | `boolean` | `false` | 严格模式,开启严格模式后,会采用 JavaScript 严格相等比较 | `2.3.0` |
#### QuickEditConfig
@ -4948,7 +4954,7 @@ value 结构说明:
#### 行记录中字段赋值
需要通过表达式配置动态`name`或`id`和`componentName`或`componentId`。例如修改`engine`选中状态的同时选中`version`,勾选`id`的同时去掉对`engine`的选中。
需要通过表达式配置动态`id`和`componentId`。例如修改`engine`选中状态的同时选中`version`,勾选`id`的同时去掉对`engine`的选中。
```schema: scope="body"
{
@ -4960,13 +4966,12 @@ value 结构说明:
{
"name": "id",
"label": "ID",
"id": "u:3db3f2b1b99e",
"onEvent": {
"click": {
"actions": [
{
"actionType": "setValue",
"componentId": "u:4868d7db0139_${index}",
"componentId": "version_${index}",
"args": {
"value": false
}
@ -4981,13 +4986,12 @@ value 结构说明:
"label": "engine",
"quickEdit": true,
"quickEditEnabledOn": "this.id < 5",
"id": "u:0b9be99f3403",
"onEvent": {
"change": {
"actions": [
{
"actionType": "setValue",
"componentName": "version_${index}",
"componentId": "version_${index}",
"args": {
"value": true
}
@ -4997,12 +5001,12 @@ value 结构说明:
}
},
{
"name": "version_${index}",
"name": "version",
"type": "checkbox",
"label": "version",
"quickEdit": true,
"quickEditEnabledOn": "this.id < 5",
"id": "u:4868d7db0139_${index}"
"id": "version_${index}"
}
],
"id": "u:f5bad706d7c5"

View File

@ -1901,6 +1901,7 @@ order: 54
{
"type": "button",
"label": "新增一行(未指定添加位置)",
"className": "mr-2",
"onEvent": {
"click": {
"actions": [
@ -1985,6 +1986,7 @@ order: 54
{
"type": "button",
"label": "删除行(指定行号)",
"className": "mr-2",
"onEvent": {
"click": {
"actions": [
@ -2184,6 +2186,7 @@ order: 54
{
"type": "button",
"label": "更新index为1和3的行记录",
"className": "mr-2",
"onEvent": {
"click": {
"actions": [
@ -2205,6 +2208,7 @@ order: 54
{
"type": "button",
"label": "更新a=a3的行记录",
"className": "mr-2",
"onEvent": {
"click": {
"actions": [
@ -2315,6 +2319,74 @@ order: 54
}
```
#### 行记录内表单项联动
需要通过表达式配置动态 `id``componentId`
```schema: scope="body"
{
"type": "form",
"api": "/api/mock2/form/saveForm",
"body": [
{
"type": "input-table",
"label": "表格表单",
"id": "setValue-input-table",
"name": "table",
"columns": [
{
"type": "input-number",
"name": "num1",
"label": "数量",
"onEvent": {
"change": {
"actions": [
{
"actionType": "setValue",
"componentId": "num2_${index}",
"args": {
"value": "${num1 * 10}"
}
}
]
}
}
},
{
"name": "num2",
"id": "num2_${index}",
"label": "金额"
}
],
"addable": true,
"footerAddBtn": {
"label": "新增",
"icon": "fa fa-plus",
"hidden": true
},
"strictMode": true,
"minLength": 0,
"needConfirm": false,
"showTableAddBtn": false
}
],
"data": {
"table": [
{
"id": 1,
"num1": 1,
"num2": "10"
},
{
"id": 2,
"num1": "2",
"num2": 20
}
]
}
}
```
### clear
```schema: scope="body"

View File

@ -60,5 +60,7 @@ order: 30
| vendor | 'baidu' | 'baidu' \| 'gaode' | 地图厂商,目前只实现了百度地图和高德地图 |
| ak | `string` | 无 | 百度/高德地图的 ak |
| clearable | `boolean` | false | 输入框是否可清空 |
| placeholder | `string` | '请选择位置' | 默认提示 |
| coordinatesType | `string` | 'bd09' | 默为百度/高德坐标,可设置为'gcj02', 高德地图不支持坐标转换 |
| placeholder | `string` | '请选择位置' | 默认提示 |
| autoSelectCurrentLoc | `boolean` | false | 是否自动选中当前地理位置 |
| onlySelectCurrentLoc | `boolean` | false | 是否限制只能选中当前地理位置设置为true后可用于充当定位组件 |
| coordinatesType | `string` | 'bd09' | 默为百度/高德坐标,可设置为'gcj02', 高德地图不支持坐标转换 |

View File

@ -25,6 +25,24 @@ order: 51
}
```
## 禁用状态
```schema: scope="body"
{
"type": "form",
"api": "/api/mock2/form/saveForm",
"body": [
{
"name": "switch",
"type": "switch",
"disabled": true,
"label": "开关",
"option": "开关说明"
}
]
}
```
## 配置真假值
默认情况:
@ -105,7 +123,7 @@ order: 51
## 不同尺寸
> 2.0.0 及以上版本
> `2.0.0` 及以上版本
```schema: scope="body"
{
@ -127,18 +145,123 @@ order: 51
}
```
## 加载状态
> `3.6.0` 及以上版本
设置`"loading": true`, 标识开关操作的异步任务仍在执行中。另外`loadingOn`支持表达式
```schema: scope="body"
{
"type": "form",
"api": "/api/mock2/form/saveForm",
"data": {
"shouldLoading": true
},
"body": [
{
"type": "switch",
"name": "switch1",
"label": "",
"loading": true,
"value": true
},
{
"type": "switch",
"name": "switch2",
"label": "",
"size": "sm",
"disabled": true,
"loading": true
}
]
}
```
配合`ajax`动作,实现开关操作后台异步任务:
```schema: scope="body"
{
"type": "page",
"body": [
{
"type": "form",
"id": "demo-form",
"body": [
{
"type": "hidden",
"name": "isFetching",
"value": false
},
{
"type": "switch",
"name": "switch",
"label": "操作状态开关",
"mode": "horizontal",
"loadingOn": "${isFetching}",
"onEvent": {
"change": {
"actions": [
{
"actionType": "toast",
"args": {
"msgType": "warning",
"msg": "任务${switch === true ? '派发' : '取消'}成功,等待后台操作..."
}
},
{
"actionType": "setValue",
"componentId": "demo-form",
"args": {
"value": {
"isFetching": true
}
}
},
{
"actionType": "ajax",
"api": {
"url": "/api/mock2/form/saveForm?waitSeconds=3",
"method": "get",
"messages": {
"success": "操作成功",
"failed": "操作失败"
}
}
},
{
"actionType": "setValue",
"componentId": "demo-form",
"args": {
"value": {
"isFetching": false
}
}
}
]
}
}
}
]
}
]
}
```
## 属性表
除了支持 [普通表单项属性表](./formitem#%E5%B1%9E%E6%80%A7%E8%A1%A8) 中的配置以外,还支持下面一些配置
| 属性名 | 类型 | 默认值 | 说明 |
| ---------- | --------------------------- | ------- | -------------------- |
| 属性名 | 类型 | 默认值 | 说明 | 版本 |
| ---------- | --------------------------- | ------- | -------------------- | --- |
| option | `string` | | 选项说明 |
| onText | `string / IconSchema` | | 开启时开关显示的内容 |
| offText | `string / IconSchema` | | 关闭时开关显示的内容 |
| trueValue | `boolean / string / number` | `true` | 标识真值 |
| falseValue | `boolean / string / number` | `false` | 标识假值 |
| size | `"sm" \| "md"` | `"md"` | 开关大小 |
| loading | `boolean` | `false` | 是否处于加载状态 | `3.6.0` |
IconSchema 配置
| 属性名 | 类型 | 默认值 | 说明 |

View File

@ -103,3 +103,7 @@ amisScoped.updateProps({
## CRUD api 分页功能失效
如果 api 地址中有变量,比如 `/api/mock2/sample/${id}`amis 就不会自动加上分页参数,需要自己加上,改成 `/api/mock2/sample/${id}?page=${page}&perPage=${perPage}`
## CRUD 性能较慢怎么办?
3.4.1 之后版本有个 `lazyRenderAfter` 配置项,默认是 100可以改小点延迟渲染不在屏幕中的行

View File

@ -259,7 +259,13 @@ export function embed(
...props,
scopeRef: (ref: any) => {
if (ref) {
Object.assign(scoped, ref);
Object.keys(ref).forEach(key => {
let value = ref[key];
if (typeof value === 'function') {
value = value.bind(ref);
}
(scoped as any)[key] = value;
});
callback?.();
}
}

View File

@ -688,7 +688,10 @@ if (fis.project.currentMedia() === 'publish-sdk') {
postprocessor: convertSCSSIE11
});
const ghPages = fis.media('gh-pages');
ghPages.set('project.files', ['examples/index.html']);
ghPages.set('project.files', [
'examples/index.html',
'/examples/static/*.docx'
]);
ghPages.match('*.scss', {
parser: fis.plugin('sass', {
@ -1008,6 +1011,10 @@ if (fis.project.currentMedia() === 'publish-sdk') {
useHash: true
});
ghPages.match('*.docx', {
useHash: false
});
ghPages.match('*.{js,ts,tsx,jsx}', {
optimizer: fis.plugin('terser'),
useHash: true

View File

@ -6,5 +6,5 @@
"packages/amis"
],
"useWorkspaces": false,
"version": "3.5.1"
"version": "3.5.3"
}

View File

@ -45,6 +45,7 @@
"qs": "6.9.7"
},
"devDependencies": {
"@fortawesome/fontawesome-free": "^6.1.1",
"@babel/generator": "^7.22.9",
"@babel/parser": "^7.22.7",
"@babel/traverse": "^7.22.8",
@ -141,4 +142,4 @@
"printBasicPrototype": false
}
}
}
}

View File

@ -1,6 +1,6 @@
{
"name": "amis-core",
"version": "3.5.1",
"version": "3.5.3",
"description": "amis-core",
"main": "lib/index.js",
"module": "esm/index.js",
@ -46,7 +46,7 @@
"esm"
],
"dependencies": {
"amis-formula": "^3.5.1",
"amis-formula": "^3.5.3",
"classnames": "2.3.2",
"file-saver": "^2.0.2",
"hoist-non-react-statics": "^3.3.2",

View File

@ -474,7 +474,7 @@ export class RootRenderer extends React.Component<RootRendererProps> {
const store = this.store;
if (store.runtimeError) {
this.renderRuntimeError();
return this.renderRuntimeError();
}
return (

View File

@ -0,0 +1,58 @@
/**
* @file ErrorBoundary
* @description JavaScript
* @author wibetter
*/
import React from 'react';
interface ErrorBoundaryProps {
// 自定义错误信息,控制台输出
customErrorMsg?: string;
fallback?: () => void;
children: any;
}
interface ErrorBoundaryStates {
hasError: boolean;
}
export default class ErrorBoundary extends React.Component<
ErrorBoundaryProps,
ErrorBoundaryStates
> {
constructor(props: any) {
super(props);
this.state = {hasError: false};
}
componentDidCatch(error: any, errorInfo: any) {
const {customErrorMsg} = this.props;
if (customErrorMsg) {
console.warn(customErrorMsg);
}
console.warn('错误对象:', error);
console.warn('错误信息:', errorInfo);
this.setState({
hasError: true
});
}
render() {
const {fallback} = this.props;
if (this.state.hasError) {
if (fallback) {
return fallback();
}
// 默认渲染错误信息
return (
<div className="renderer-error-boundary">
</div>
);
}
return this.props.children;
}
}

View File

@ -96,6 +96,7 @@ import type {FilterContext} from 'amis-formula';
import LazyComponent from './components/LazyComponent';
import Overlay from './components/Overlay';
import PopOver from './components/PopOver';
import ErrorBoundary from './components/ErrorBoundary';
import {FormRenderer} from './renderers/Form';
import type {FormHorizontal, FormSchemaBase} from './renderers/Form';
import {
@ -182,6 +183,7 @@ export {
LazyComponent,
Overlay,
PopOver,
ErrorBoundary,
addSchemaFilter,
OptionsControlProps,
FormOptionsControl,

View File

@ -522,6 +522,18 @@ export default class Form extends React.Component<FormProps, object> {
)
);
}
// withStore 里面与上层数据会做同步
// 这个时候变更的数据没有同步 onChange 出去,出现数据不一致的问题。
// https://github.com/baidu/amis/issues/8773
this.toDispose.push(
reaction(
() => store.initedAt,
() => {
store.inited && this.emitChange(!!this.props.submitOnChange);
}
)
);
}
componentDidMount() {

View File

@ -528,7 +528,8 @@ export const TableStore = iRendererStore
exportExcelLoading: false,
searchFormExpanded: false, // 用来控制搜索框是否展开了,那个自动根据 searchable 生成的表单 autoGenerateFilter
lazyRenderAfter: 100,
tableLayout: 'auto'
tableLayout: 'auto',
theadHeight: 0
})
.views(self => {
function getColumnsExceptBuiltinTypes() {
@ -1001,7 +1002,7 @@ export const TableStore = iRendererStore
},
buildStyles(style: any) {
style = {...style};
style = {...style, '--Table-thead-height': self.theadHeight + 'px'};
getFilteredColumns().forEach(column => {
style[`--Table-column-${column.index}-width`] =
@ -1050,13 +1051,7 @@ export const TableStore = iRendererStore
);
}
if (config.multiple !== undefined) {
self.multiple = config.multiple;
} else {
// 如果通过crud或pickermultiple始终设置了true或false
// 如果仅使用table默认multiple为true但props未设置multiple的情况下其实是展示单选
self.multiple = false;
}
config.multiple !== undefined && (self.multiple = config.multiple);
config.footable !== undefined && (self.footable = config.footable);
config.expandConfig !== undefined &&
(self.expandConfig = config.expandConfig);
@ -1301,9 +1296,9 @@ export const TableStore = iRendererStore
if (!table) {
return;
}
const cols = [].slice.call(
table.querySelectorAll(':scope>thead>tr>th[data-index]')
);
const thead = table.querySelector(':scope>thead') as HTMLElement;
const cols = [].slice.call(thead.querySelectorAll('tr>th[data-index]'));
self.theadHeight = thead.offsetHeight;
cols.forEach((col: HTMLElement) => {
const index = parseInt(col.getAttribute('data-index')!, 10);
const column = self.columns[index];

View File

@ -1,7 +1,7 @@
// https://json-schema.org/draft-07/json-schema-release-notes.html
import type {JSONSchema7} from 'json-schema';
import {ListenerAction} from './actions/Action';
import {debounceConfig} from './utils/renderer-event';
import {debounceConfig, trackConfig} from './utils/renderer-event';
export interface Option {
/**
@ -458,7 +458,8 @@ export interface EventTrack {
| 'tabChange'
| 'pageLoaded'
| 'pageHidden'
| 'pageVisible';
| 'pageVisible'
| string;
/**
*
@ -618,6 +619,7 @@ export interface BaseSchemaWithoutType {
weight?: number; // 权重
actions: ListenerAction[]; // 执行的动作集
debounce?: debounceConfig;
track?: trackConfig;
};
};
/**

View File

@ -33,6 +33,8 @@ export function attachmentAdpator(
// 很可能是中文被 url-encode 了
if (filename && filename.replace(/[^%]/g, '').length > 2) {
filename = decodeURIComponent(filename);
// 有些后端用错了,导致空格转义成了 +,这里转回来
filename = filename.replace(/\+/g, ' ');
}
}

View File

@ -10,10 +10,16 @@ export interface debounceConfig {
leading?: boolean;
trailing?: boolean;
}
export interface trackConfig {
id: string;
name: string;
}
// 事件监听器
export interface EventListeners {
[propName: string]: {
debounce?: debounceConfig;
track?: trackConfig;
weight?: number; // 权重
actions: ListenerAction[]; // 执行的动作集
};
@ -26,6 +32,7 @@ export interface OnEventProps {
weight?: number; // 权重
actions: ListenerAction[]; // 执行的动作集,
debounce?: debounceConfig;
track?: trackConfig;
};
};
}
@ -36,6 +43,7 @@ export interface RendererEventListener {
type: string;
weight: number;
debounce: debounceConfig | null;
track: trackConfig | null;
actions: ListenerAction[];
executing?: boolean;
debounceInstance?: any;
@ -118,6 +126,7 @@ export const bindEvent = (renderer: any) => {
renderer,
type: key,
debounce: listener.debounce || null,
track: listeners[key].track || null,
weight: listener.weight || 0,
actions: listener.actions
});
@ -127,6 +136,7 @@ export const bindEvent = (renderer: any) => {
renderer,
type: key,
debounce: listeners[key].debounce || null,
track: listeners[key].track || null,
weight: listeners[key].weight || 0,
actions: listeners[key].actions
});
@ -245,6 +255,17 @@ export async function dispatchEvent(
checkExecuted();
}
if (listener?.track) {
const {id: trackId, name: trackName} = listener.track;
renderer?.props?.env?.tracker({
eventType: listener.type,
eventData: {
trackId,
trackName
}
});
}
// 停止后续监听器执行
if (rendererEvent.stoped) {
break;

View File

@ -59,7 +59,7 @@
"sortablejs": "^1.14.0"
},
"devDependencies": {
"@fortawesome/fontawesome-free": "^5.15.3",
"@fortawesome/fontawesome-free": "^6.1.1",
"@rollup/plugin-commonjs": "^22.0.0",
"@rollup/plugin-json": "^4.1.0",
"@rollup/plugin-node-resolve": "^13.3.0",
@ -120,4 +120,4 @@
"react": ">=16.8.6",
"react-dom": ">=16.8.6"
}
}
}

View File

@ -801,14 +801,6 @@
}
}
.ae-Editor-renderer-error {
padding: 5px;
font-family: PingFangSC-Medium;
font-size: 14px;
color: #cf1322;
border: 1px dashed #cf1322;
}
.ae-Editor-tip {
user-select: none;
max-width: 100px;

View File

@ -23,7 +23,8 @@ import {
JSONUpdate,
getFixDialogType
} from '../util';
import {createObject, createObjectFromChain} from 'amis-core';
import {createObjectFromChain} from 'amis-core';
import {ErrorBoundary} from 'amis-core';
import {CommonConfigWrapper} from './CommonConfigWrapper';
import type {Schema} from 'amis';
import type {DataScope} from 'amis-core';
@ -40,14 +41,11 @@ export function makeWrapper(
type Props = RendererProps & {
$$id: string;
};
type States = {
hasError: boolean;
};
const store = manager.store;
const renderer = rendererConfig.component;
@observer
class Wrapper extends React.Component<Props, States> {
class Wrapper extends React.Component<Props> {
static displayName = renderer.displayName;
static propsList = ((renderer && renderer.propsList) || []).concat([
'$$id'
@ -57,11 +55,6 @@ export function makeWrapper(
editorNode?: EditorNodeType;
scopeId?: string;
constructor(props: Props) {
super(props);
this.state = {hasError: false};
}
UNSAFE_componentWillMount() {
const parent: EditorNodeType = (this.context as any) || store.root;
if (!info.id) {
@ -133,16 +126,6 @@ export function makeWrapper(
}
}
componentDidCatch(error: any, errorInfo: any) {
console.warn(`${info.name}(${info.id})渲染发生错误:`);
console.warn('当前渲染器信息:', info);
console.warn('错误对象:', error);
console.warn('错误信息:', errorInfo);
this.setState({
hasError: true
});
}
componentWillUnmount() {
if (this.editorNode && isAlive(this.editorNode)) {
const parent: EditorNodeType = (this.context as any) || store.root;
@ -186,14 +169,6 @@ export function makeWrapper(
render() {
const {$$id, ...rest} = this.props;
if (this.state.hasError) {
return (
<div className="ae-Editor-renderer-error">
{info.name}({info.id})
</div>
);
}
/*
* NodeWrapper ContainerWrapper
* NodeWrapper dom data-editor-id
@ -207,18 +182,31 @@ export function makeWrapper(
: info.regions
? ContainerWrapper
: NodeWrapper; /*)*/
return (
<EditorNodeContext.Provider
value={this.editorNode || (this.context as any)}
>
<Wrapper
{...rest}
render={this.renderChild}
$$editor={info}
$$node={this.editorNode}
ref={this.wrapperRef}
/>
<ErrorBoundary
customErrorMsg={`拦截到${
info.type
}当前组件信息: ${JSON.stringify(this.props.$schema)}`}
fallback={() => {
return (
<div className="renderer-error-boundary">
{info?.type}
</div>
);
}}
>
<Wrapper
{...rest}
render={this.renderChild}
$$editor={info}
$$node={this.editorNode}
ref={this.wrapperRef}
/>
</ErrorBoundary>
</EditorNodeContext.Provider>
);
}

View File

@ -49,7 +49,7 @@
"mobx-state-tree": "^3.17.3"
},
"devDependencies": {
"@fortawesome/fontawesome-free": "^5.15.3",
"@fortawesome/fontawesome-free": "^6.1.1",
"@rollup/plugin-commonjs": "^22.0.0",
"@rollup/plugin-json": "^4.1.0",
"@rollup/plugin-node-resolve": "^13.3.0",
@ -108,4 +108,4 @@
"react": ">=16.8.6",
"react-dom": ">=16.8.6"
}
}
}

View File

@ -45,7 +45,7 @@ export class CRUDCardsPlugin extends BaseCRUDPlugin {
$schema = '/schemas/CRUD2CardsSchema.json';
docLink = '/amis/zh-CN/components/crud2';
docLink = '/amis/zh-CN/components/cards';
previewSchema: Record<string, any> = this.generatePreviewSchema('cards');

View File

@ -45,7 +45,7 @@ export class CRUDListPlugin extends BaseCRUDPlugin {
$schema = '/schemas/CRUD2ListSchema.json';
docLink = '/amis/zh-CN/components/crud2';
docLink = '/amis/zh-CN/components/list';
previewSchema: Record<string, any> = this.generatePreviewSchema('list');

View File

@ -38,7 +38,7 @@ export class CRUDTablePlugin extends BaseCRUDPlugin {
$schema = '/schemas/CRUD2TableSchema.json';
docLink = '/amis/zh-CN/components/crud2';
docLink = '/amis/zh-CN/components/table2';
previewSchema: Record<string, any> = this.generatePreviewSchema('table2');

View File

@ -1,4 +1,4 @@
import {EditorNodeType, getSchemaTpl} from 'amis-editor-core';
import {EditorNodeType, getSchemaTpl, tipedLabel} from 'amis-editor-core';
import {registerEditorPlugin} from 'amis-editor-core';
import {BasePlugin, BaseEventContext} from 'amis-editor-core';
import {ValidatorTag} from '../../validator';
@ -82,11 +82,30 @@ export class LocationControlPlugin extends BasePlugin {
{label: '国测局坐标', value: 'gcj02'}
]
},
getSchemaTpl('switch', {
name: 'autoSelectCurrentLoc',
label: tipedLabel(
'自动选择',
'开启后,自动选中用户当前的地理位置'
)
}),
getSchemaTpl('switch', {
name: 'onlySelectCurrentLoc',
label: tipedLabel(
'只读模式',
'开启后,只能使用当前地理位置,不可选择其他地理位置'
)
}),
getSchemaTpl('clearable'),
getSchemaTpl('labelRemark'),
getSchemaTpl('remark'),
getSchemaTpl('placeholder'),
getSchemaTpl('placeholder', {
visibleOn: '!onlySelectCurrentLoc'
}),
getSchemaTpl('placeholder', {
name: 'getLocationPlaceholder',
visibleOn: 'onlySelectCurrentLoc'
}),
getSchemaTpl('description')
]
},

View File

@ -249,6 +249,11 @@ export class TreeSelectControlPlugin extends BasePlugin {
actionType: 'setValue',
actionLabel: '赋值',
description: '触发组件数据更新'
},
{
actionType: 'reload',
actionLabel: '重新加载',
description: '触发组件数据刷新并重新渲染'
}
];

View File

@ -78,6 +78,11 @@ interface EventDialogData {
open: boolean;
wait?: number;
};
trackConfig?: {
open: boolean;
id: string;
name: string;
};
[propName: string]: any;
}
@ -235,6 +240,16 @@ export class EventControl extends React.Component<
...eventInfo.debounce
};
}
if (!eventInfo.track) {
eventInfo.track = {
open: false
};
} else {
eventInfo.track = {
open: true,
...eventInfo.track
};
}
this.setState({
eventDialogData: eventInfo,
showEventDialog: true
@ -243,11 +258,11 @@ export class EventControl extends React.Component<
eventDialogSubmit(formData: any) {
const {onChange} = this.props;
const {eventName, debounce = {}} = formData;
const {eventName, debounce = {}, track = {}} = formData;
let onEvent = {
...this.state.onEvent
};
let eventConfig = onEvent[`${eventName}`];
let eventConfig = {...onEvent[`${eventName}`]};
if (!debounce.open) {
delete eventConfig.debounce;
} else {
@ -258,6 +273,18 @@ export class EventControl extends React.Component<
}
};
}
if (!track.open) {
delete eventConfig.track;
} else {
eventConfig = {
...eventConfig,
track: {
id: track.id,
name: track.name
}
};
}
onEvent[`${eventName}`] = {
...eventConfig
};
@ -1256,6 +1283,27 @@ export class EventControl extends React.Component<
max: 10000,
min: 0,
type: 'input-number'
},
{
label: '事件埋点',
type: 'switch',
name: 'track.open',
description:
'开启事件埋点后,每次事件触发都会发送埋点数据到后台'
},
{
label: 'track-id',
required: true,
hiddenOn: '!track.open',
name: 'track.id',
type: 'input-text'
},
{
label: 'track-name',
required: true,
hiddenOn: '!track.open',
name: 'track.name',
type: 'input-text'
}
],
onSubmit: this.eventDialogSubmit.bind(this)

View File

@ -13,6 +13,10 @@ export interface ActionEventConfig {
debounce?: {
wait: number;
};
track?: {
id: string;
name: string;
};
};
}

View File

@ -1,6 +1,6 @@
{
"name": "amis-formula",
"version": "3.5.1",
"version": "3.5.3",
"description": "负责 amis 里面的表达式实现,内置公式,编辑器等",
"main": "lib/index.js",
"module": "esm/index.js",

View File

@ -3,7 +3,7 @@
"main": "lib/index.js",
"module": "esm/index.js",
"types": "lib/index.d.ts",
"version": "3.5.1",
"version": "3.5.3",
"description": "",
"scripts": {
"build": "npm run clean-dist && NODE_ENV=production rollup -c ",
@ -36,8 +36,8 @@
},
"dependencies": {
"@rc-component/mini-decimal": "^1.0.1",
"amis-core": "^3.5.1",
"amis-formula": "^3.5.1",
"amis-core": "^3.5.3",
"amis-formula": "^3.5.3",
"classnames": "2.3.2",
"codemirror": "^5.63.0",
"downshift": "6.1.12",

View File

@ -1972,6 +1972,9 @@
--Switch-checked-bgColor: var(--switch-default-on-bg-color);
--Switch-checked-onHover-bgColor: var(--switch-default-on-hover-bg-color);
--Switch-checked-onActive-bgColor: var(--colors-brand-4);
--Switch-spinner-icon-width: var(--sizes-base-7);
--Switch-spinner-icon-width--sm: var(--sizes-base-5);
--switch-spinner-left--sm: var(--sizes-size-0);
--collapse-default-top-border-color: var(--colors-neutral-line-8);
--collapse-default-top-border-width: var(--borders-width-2);
@ -3292,7 +3295,6 @@
--spinner-size-size: var(--sizes-base-16);
--spinner-lg-size: var(--sizes-base-24);
--spinner-overlay-bg: var(--colors-neutral-fill-11);
--spinner-color: var(--colors-brand-5);
--Spinner--lg-height: var(--spinner-lg-size);
--Spinner--lg-width: var(--spinner-lg-size);
@ -3304,6 +3306,7 @@
--Spinner-overlay-bg: rgba(255, 255, 255, 0.4);
--Spinner-width: var(--spinner-size-size);
--Spinner-color: var(--spinner-base-color);
--Spinner-color--disabled: rgba(0, 0, 0, 0.65);
// Image
--image-image-normal-paddingTop: var(--sizes-size-3);

View File

@ -9,3 +9,11 @@
.visibility-sensor {
min-height: 5px;
}
.renderer-error-boundary {
padding: 5px;
font-family: PingFangSC-Medium;
font-size: 14px;
color: #cf1322;
border: 1px dashed #cf1322;
}

View File

@ -77,6 +77,15 @@
height: auto;
}
&.#{$ns}Spinner-icon--disabled > svg.icon {
color: var(--Spinner-color--disabled);
fill: var(--Spinner-color--disabled);
& path {
fill: var(--Spinner-color--disabled);
}
}
&--lg {
width: var(--Spinner--lg-width);
height: var(--Spinner--lg-height);

View File

@ -273,8 +273,12 @@
padding-top: 0;
}
&--affixHeader > thead {
visibility: collapse;
&--affixHeader {
margin-top: calc(var(--Table-thead-height) * -1);
> thead {
visibility: hidden;
}
}
&--withCombine {

View File

@ -153,6 +153,44 @@
border-radius: 10px;
}
}
&-spinner {
position: absolute;
top: var(--Switch-slider-margin);
bottom: var(--Switch-slider-margin);
left: var(--Switch-slider-margin);
& > &-icon > svg.icon {
width: var(--Switch-spinner-icon-width);
height: var(--Switch-spinner-icon-width);
}
&--sm {
position: absolute;
top: var(--switch-size-sm-slider-margin);
bottom: var(--switch-size-sm-slider-margin);
left: var(--switch-spinner-left--sm);
& > .#{$ns}Switch-spinner-icon > svg.icon {
width: var(--Switch-spinner-icon-width--sm);
height: var(--Switch-spinner-icon-width--sm);
}
}
&.#{$ns}Switch-spinner--checked {
left: calc(
100% - var(--Switch-slider-width) - var(--Switch-slider-margin)
);
right: var(--Switch-slider-margin);
&.#{$ns}Switch-spinner--sm {
left: calc(
100% - var(--Switch-slider-width--sm) - var(--Switch-slider-margin)
);
right: var(--Switch-slider-margin);
}
}
}
}
.#{$ns}Switch-option {

View File

@ -109,9 +109,11 @@ $ns: 'a-';
--colors-neutral-line-9: #f3f2f5;
--colors-neutral-line-10: #f8f7fa;
--colors-neutral-line-11: #ffffff;
--fonts-base-family: -apple-system, BlinkMacSystemFont, SF Pro SC, SF Pro Text,
Helvetica Neue, Helvetica, PingFang SC, Segoe UI, Roboto, Hiragino Sans GB,
Arial, microsoft yahei ui, Microsoft YaHei, SimSun, sans-serif;
--fonts-base-family: -apple-system, 'Noto Sans', 'Helvetica Neue', Helvetica,
'Nimbus Sans L', Arial, 'Liberation Sans', 'PingFang SC', 'Hiragino Sans GB',
'Noto Sans CJK SC', 'Source Han Sans SC', 'Source Han Sans CN',
'Microsoft YaHei', 'Wenquanyi Micro Hei', 'WenQuanYi Zen Hei', 'ST Heiti',
SimHei, 'WenQuanYi Zen Hei Sharp', sans-serif;
--fonts-size-1: 48px;
--fonts-size-2: 40px;
--fonts-size-3: 32px;

View File

@ -109,9 +109,11 @@ $ns: 'antd-';
--colors-neutral-line-9: #f2f4f5;
--colors-neutral-line-10: #f7f9fa;
--colors-neutral-line-11: #ffffff;
--fonts-base-family: -apple-system, BlinkMacSystemFont, SF Pro SC, SF Pro Text,
Helvetica Neue, Helvetica, PingFang SC, Segoe UI, Roboto, Hiragino Sans GB,
Arial, microsoft yahei ui, Microsoft YaHei, SimSun, sans-serif;
--fonts-base-family: -apple-system, 'Noto Sans', 'Helvetica Neue', Helvetica,
'Nimbus Sans L', Arial, 'Liberation Sans', 'PingFang SC', 'Hiragino Sans GB',
'Noto Sans CJK SC', 'Source Han Sans SC', 'Source Han Sans CN',
'Microsoft YaHei', 'Wenquanyi Micro Hei', 'WenQuanYi Zen Hei', 'ST Heiti',
SimHei, 'WenQuanYi Zen Hei Sharp', sans-serif;
--fonts-size-1: 48px;
--fonts-size-2: 40px;
--fonts-size-3: 32px;

View File

@ -106,9 +106,12 @@ $ns: 'cxd-';
--colors-neutral-line-9: #f2f3f5;
--colors-neutral-line-10: #f7f8fa;
--colors-neutral-line-11: #ffffff;
--fonts-base-family: -apple-system, BlinkMacSystemFont, SF Pro SC, SF Pro Text,
Helvetica Neue, Helvetica, PingFang SC, Segoe UI, Roboto, Hiragino Sans GB,
Arial, microsoft yahei ui, Microsoft YaHei, SimSun, sans-serif;
// 参考 https://zenozeng.github.io/fonts.css/
--fonts-base-family: -apple-system, 'Noto Sans', 'Helvetica Neue', Helvetica,
'Nimbus Sans L', Arial, 'Liberation Sans', 'PingFang SC', 'Hiragino Sans GB',
'Noto Sans CJK SC', 'Source Han Sans SC', 'Source Han Sans CN',
'Microsoft YaHei', 'Wenquanyi Micro Hei', 'WenQuanYi Zen Hei', 'ST Heiti',
SimHei, 'WenQuanYi Zen Hei Sharp', sans-serif;
--fonts-size-1: 48px;
--fonts-size-2: 40px;
--fonts-size-3: 32px;

View File

@ -117,9 +117,11 @@ $ns: 'dark-';
--colors-neutral-line-9: #f2f4f5;
--colors-neutral-line-10: #f7f9fa;
--colors-neutral-line-11: #ffffff;
--fonts-base-family: -apple-system, BlinkMacSystemFont, SF Pro SC, SF Pro Text,
Helvetica Neue, Helvetica, PingFang SC, Segoe UI, Roboto, Hiragino Sans GB,
Arial, microsoft yahei ui, Microsoft YaHei, SimSun, sans-serif;
--fonts-base-family: -apple-system, 'Noto Sans', 'Helvetica Neue', Helvetica,
'Nimbus Sans L', Arial, 'Liberation Sans', 'PingFang SC', 'Hiragino Sans GB',
'Noto Sans CJK SC', 'Source Han Sans SC', 'Source Han Sans CN',
'Microsoft YaHei', 'Wenquanyi Micro Hei', 'WenQuanYi Zen Hei', 'ST Heiti',
SimHei, 'WenQuanYi Zen Hei Sharp', sans-serif;
--fonts-size-1: 48px;
--fonts-size-2: 40px;
--fonts-size-3: 32px;

View File

@ -38,6 +38,8 @@ interface MapPickerProps {
city?: string;
};
onChange?: (value: any) => void;
autoSelectCurrentLoc?: boolean;
onlySelectCurrentLoc?: boolean;
}
interface LocationItem {
@ -109,6 +111,7 @@ export class BaiduMapPicker extends React.Component<
@autobind
async initMap() {
const autoSelectCurrentLoc = this.props.autoSelectCurrentLoc ?? false;
const map = new BMap.Map(this.mapRef.current, {
enableMapClick: false
});
@ -137,11 +140,14 @@ export class BaiduMapPicker extends React.Component<
const geolocationControl = new BMap.GeolocationControl();
geolocationControl.addEventListener('locationSuccess', (e: any) => {
this.getLocations(e.point);
this.getLocations(e.point, autoSelectCurrentLoc);
});
map.addControl(geolocationControl);
map.addEventListener('click', (e: any) => {
if (this.props.onlySelectCurrentLoc) {
return;
}
this.getLocations(e.point, true);
});
@ -187,7 +193,8 @@ export class BaiduMapPicker extends React.Component<
value ? this.getLocations(point) : geolocationControl.location();
}
getLocations(point: any, select?: boolean) {
@autobind
getLocations(point: any, selectCurrentLoc?: boolean) {
const map = this.map;
map.clearOverlays();
@ -231,7 +238,7 @@ export class BaiduMapPicker extends React.Component<
locs
},
() => {
if (!select) {
if (!selectCurrentLoc) {
return;
}
@ -318,7 +325,7 @@ export class BaiduMapPicker extends React.Component<
});
var local = new BMap.LocalSearch(this.map, {
//智能搜索
// 智能搜索
onSearchComplete: () => {
const results = local.getResults();
const poi = results.getPoi(0);
@ -334,20 +341,23 @@ export class BaiduMapPicker extends React.Component<
render() {
const {classnames: cx} = this.props;
const onlySelectCurrentLoc = this.props.onlySelectCurrentLoc ?? false;
const {locIndex, locs, inputValue, sugs} = this.state;
const hasSug = Array.isArray(sugs) && sugs.length;
return (
<div className={cx('MapPicker')}>
<div className={cx('MapPicker-search TextControl-control')}>
<div className={cx('TextControl-input')}>
<input
onChange={this.handleChange}
value={inputValue}
placeholder="搜索地点"
/>
{!onlySelectCurrentLoc && (
<div className={cx('MapPicker-search TextControl-control')}>
<div className={cx('TextControl-input')}>
<input
onChange={this.handleChange}
value={inputValue}
placeholder="搜索地点"
/>
</div>
</div>
</div>
)}
<div
ref={this.mapRef}
@ -361,23 +371,36 @@ export class BaiduMapPicker extends React.Component<
invisible: hasSug
})}
>
{locs.map((item, index) => (
{!onlySelectCurrentLoc &&
locs.map((item, index) => (
<div
onClick={this.handleSelect}
key={index}
data-index={index}
className={cx('MapPicker-item')}
>
<div className={cx('MapPicker-itemTitle')}>{item.title}</div>
<div className={cx('MapPicker-itemDesc')}>{item.address}</div>
{locIndex === index ? (
<Icon icon="success" className="icon" />
) : null}
</div>
))}
{onlySelectCurrentLoc && locs.length > 0 && (
<div
onClick={this.handleSelect}
key={index}
data-index={index}
key="locs-current"
data-index={0}
className={cx('MapPicker-item')}
>
<div className={cx('MapPicker-itemTitle')}>{item.title}</div>
<div className={cx('MapPicker-itemDesc')}>{item.address}</div>
{locIndex === index ? (
<Icon icon="success" className="icon" />
) : null}
<div className={cx('MapPicker-itemTitle')}>{locs[0].title}</div>
<div className={cx('MapPicker-itemDesc')}>{locs[0].address}</div>
{locIndex === 0 ? <Icon icon="success" className="icon" /> : null}
</div>
))}
)}
</div>
{hasSug ? (
{hasSug && !onlySelectCurrentLoc ? (
<div className={cx('MapPicker-sug')}>
{sugs.map(item => (
<div

View File

@ -14,6 +14,7 @@ export interface LocationProps extends ThemeProps, LocaleProps {
vendor: 'baidu' | 'gaode' | 'tenxun';
coordinatesType: 'bd09' | 'gcj02';
placeholder: string;
getLocationPlaceholder: string;
clearable: boolean;
ak: string;
value?: {
@ -27,6 +28,8 @@ export interface LocationProps extends ThemeProps, LocaleProps {
popoverClassName?: string;
onChange: (value: any) => void;
popOverContainer?: any;
autoSelectCurrentLoc?: boolean;
onlySelectCurrentLoc?: boolean;
}
export interface LocationState {
@ -40,6 +43,7 @@ export class LocationPicker extends React.Component<
> {
static defaultProps = {
placeholder: 'LocationPicker.placeholder',
getLocationPlaceholder: 'LocationPicker.getLocation',
clearable: false
};
domRef: React.RefObject<HTMLDivElement> = React.createRef();
@ -155,12 +159,15 @@ export class LocationPicker extends React.Component<
popoverClassName,
disabled,
placeholder,
getLocationPlaceholder,
clearable,
popOverContainer,
vendor,
coordinatesType,
ak,
mobileUI
mobileUI,
autoSelectCurrentLoc,
onlySelectCurrentLoc
} = this.props;
const __ = this.props.translate;
const {isFocused, isOpened} = this.state;
@ -173,6 +180,8 @@ export class LocationPicker extends React.Component<
ak={ak}
value={value}
coordinatesType={coordinatesType}
autoSelectCurrentLoc={autoSelectCurrentLoc}
onlySelectCurrentLoc={onlySelectCurrentLoc}
onChange={this.handleChange}
/>
);
@ -213,7 +222,7 @@ export class LocationPicker extends React.Component<
<span className={cx('LocationPicker-value')}>{value.address}</span>
) : (
<span className={cx('LocationPicker-placeholder')}>
{__(placeholder)}
{__(onlySelectCurrentLoc ? getLocationPlaceholder : placeholder)}
</span>
)}
@ -242,6 +251,8 @@ export class LocationPicker extends React.Component<
ak={ak}
value={value}
coordinatesType={coordinatesType}
autoSelectCurrentLoc={autoSelectCurrentLoc}
onlySelectCurrentLoc={onlySelectCurrentLoc}
onChange={this.handleTempChange}
/>
) : (
@ -268,6 +279,8 @@ export class LocationPicker extends React.Component<
ak={ak}
value={value}
coordinatesType={coordinatesType}
autoSelectCurrentLoc={autoSelectCurrentLoc}
onlySelectCurrentLoc={onlySelectCurrentLoc}
onChange={this.handleChange}
/>
) : (

View File

@ -1271,7 +1271,7 @@ export class Select extends React.Component<SelectProps, SelectState> {
>
<PopOver
overlay
className={cx('Select-popover')}
className={cx('Select-popover', popoverClassName)}
style={{
width:
(overlay &&

View File

@ -35,6 +35,8 @@ export interface SpinnerProps extends ThemeProps, SpinnerExtraProps {
tipPlacement?: 'top' | 'right' | 'bottom' | 'left'; // spinner文案位置
delay?: number; // 延迟显示
overlay?: boolean; // 是否显示遮罩层有children属性才生效
/** 是否处于禁用状态 */
disabled?: boolean;
}
export interface SpinnerExtraProps {
@ -114,7 +116,8 @@ export class Spinner extends React.Component<
tipPlacement: 'bottom' as 'bottom',
delay: 0,
overlay: false,
loadingConfig: {}
loadingConfig: {},
disabled: false
};
state = {
@ -190,7 +193,8 @@ export class Spinner extends React.Component<
icon: iconConfig,
tip,
tipPlacement = '',
loadingConfig
loadingConfig,
disabled
} = this.props;
// 定义了挂载位置时只能使用默认icon
const icon = loadingConfig?.root ? undefined : iconConfig;
@ -241,9 +245,10 @@ export class Spinner extends React.Component<
`Spinner-icon`,
{
[`Spinner-icon--${size}`]: ['lg', 'sm'].includes(size),
[`Spinner-icon--default`]: !icon,
[`Spinner-icon--simple`]: !isCustomIcon && icon,
[`Spinner-icon--custom`]: isCustomIcon
'Spinner-icon--default': !icon,
'Spinner-icon--simple': !isCustomIcon && icon,
'Spinner-icon--custom': isCustomIcon,
'Spinner-icon--disabled': disabled
},
spinnerClassName
)}

View File

@ -6,7 +6,7 @@
import React from 'react';
import {ClassNamesFn, themeable} from 'amis-core';
import {classPrefix, classnames} from '../themes/default';
import {Spinner} from './Spinner';
const sizeMap = {
sm: 'Switch--sm',
@ -39,6 +39,11 @@ interface SwitchProps {
onText?: React.ReactNode;
offText?: React.ReactNode;
checked?: boolean;
loading?: boolean;
loadingConfig?: {
root?: string;
show?: boolean;
};
}
export class Switch extends React.PureComponent<SwitchProps, any> {
@ -80,6 +85,8 @@ export class Switch extends React.PureComponent<SwitchProps, any> {
readOnly,
checked,
classnames: cx,
loading,
loadingConfig,
...rest
} = this.props;
@ -94,27 +101,42 @@ export class Switch extends React.PureComponent<SwitchProps, any> {
: typeof value === 'undefined'
? false
: value == trueValue;
const isDisabled = disabled || loading;
return (
<label
className={cx(
`Switch`,
isChecked ? 'is-checked' : '',
disabled ? 'is-disabled' : '',
className
)}
className={cx(`Switch`, className, {
'is-checked': isChecked,
'is-disabled': isDisabled
})}
>
<input
type="checkbox"
checked={isChecked}
onChange={this.hanldeCheck}
disabled={disabled}
disabled={isDisabled}
readOnly={readOnly}
{...rest}
/>
<span className="text">{isChecked ? onText : offText}</span>
<span className="slider"></span>
<span className="slider">
{loading ? (
<Spinner
classnames={cx}
classPrefix={classPrefix}
className={cx('Switch-spinner', {
'Switch-spinner--sm': size === 'sm',
'Switch-spinner--checked': isChecked
})}
spinnerClassName={cx('Switch-spinner-icon')}
disabled={!isChecked}
size="sm"
icon="loading-outline"
loadingConfig={loadingConfig}
/>
) : null}
</span>
</label>
);
}

View File

@ -77,7 +77,7 @@ const FormulaInput: React.FC<FormulaInputProps> = props => {
} = props;
const schemaType = inputSettings.type;
/** 自上层共享的属性 */
const sharedProps = pick(props, ['disabeld', 'clearable']);
const sharedProps = pick(props, ['disabled', 'clearable']);
const pipInValue = useCallback(
(value?: any) => {
return value;

View File

@ -188,6 +188,8 @@ register('de-DE', {
'loading': 'Wird geladen...',
'loadingFailed': 'Das Laden ist fehlgeschlagen',
'LocationPicker.placeholder': 'Wählen Sie einen Ort',
'LocationPicker.getLocation':
'Klicken Sie hier, um Standortinformationen zu erhalten',
'Month.placeholder': 'Wählen Sie einen Monat',
'Nav.sourceError': 'Fehler beim Abrufen des Links',
'networkError':

View File

@ -180,6 +180,7 @@ register('en-US', {
'loading': 'Loading',
'loadingFailed': 'Loading failed',
'LocationPicker.placeholder': 'Pick location',
'LocationPicker.getLocation': 'Click to obtain location information',
'Month.placeholder': 'Select a month',
'Nav.sourceError': 'Fetch link error',
'networkError': 'Network error or missing CORS configuration',

View File

@ -185,6 +185,7 @@ register('zh-CN', {
'loading': '加载中',
'loadingFailed': '加载失败',
'LocationPicker.placeholder': '请选择位置',
'LocationPicker.getLocation': '点击获取位置信息',
'Month.placeholder': '请选择月份',
'Nav.sourceError': '获取链接错误',
'networkError': '网络错误,可能是未配置跨域 CORS',

View File

@ -28,7 +28,7 @@ exports[`doAction:crud reload 1`] = `
>
<div
class="cxd-Table cxd-Crud-body"
style="--Table-column-3-width: 0px; --Table-column-4-width: 0px; --Table-column-5-width: 0px; --Table-column-6-width: 0px; --Table-column-7-width: 0px; --Table-column-8-width: 0px; --Table-column-9-width: 0px; position: relative;"
style="--Table-thead-height: 0px; --Table-column-3-width: 0px; --Table-column-4-width: 0px; --Table-column-5-width: 0px; --Table-column-6-width: 0px; --Table-column-7-width: 0px; --Table-column-8-width: 0px; --Table-column-9-width: 0px; position: relative;"
>
<div
class="cxd-Table-fixedTop"
@ -335,7 +335,7 @@ exports[`doAction:crud reload with data1 1`] = `
>
<div
class="cxd-Table cxd-Crud-body"
style="--Table-column-3-width: 0px; --Table-column-4-width: 0px; --Table-column-5-width: 0px; --Table-column-6-width: 0px; --Table-column-7-width: 0px; --Table-column-8-width: 0px; --Table-column-9-width: 0px; position: relative;"
style="--Table-thead-height: 0px; --Table-column-3-width: 0px; --Table-column-4-width: 0px; --Table-column-5-width: 0px; --Table-column-6-width: 0px; --Table-column-7-width: 0px; --Table-column-8-width: 0px; --Table-column-9-width: 0px; position: relative;"
>
<div
class="cxd-Table-fixedTop"
@ -642,7 +642,7 @@ exports[`doAction:crud reload with data2 1`] = `
>
<div
class="cxd-Table cxd-Crud-body"
style="--Table-column-3-width: 0px; --Table-column-4-width: 0px; --Table-column-5-width: 0px; --Table-column-6-width: 0px; --Table-column-7-width: 0px; --Table-column-8-width: 0px; --Table-column-9-width: 0px; position: relative;"
style="--Table-thead-height: 0px; --Table-column-3-width: 0px; --Table-column-4-width: 0px; --Table-column-5-width: 0px; --Table-column-6-width: 0px; --Table-column-7-width: 0px; --Table-column-8-width: 0px; --Table-column-9-width: 0px; position: relative;"
>
<div
class="cxd-Table-fixedTop"

View File

@ -53,7 +53,7 @@ exports[`Renderer:input table 1`] = `
>
<div
class="cxd-Table"
style="--Table-column-3-width: 0px; --Table-column-4-width: 0px; position: relative;"
style="--Table-thead-height: 0px; --Table-column-3-width: 0px; --Table-column-4-width: 0px; position: relative;"
>
<div
class="cxd-Table-contentWrap"
@ -378,7 +378,7 @@ exports[`Renderer:input table add 1`] = `
>
<div
class="cxd-Table"
style="--Table-column-3-width: 0px; --Table-column-4-width: 0px; --Table-column-5-width: 0px; position: relative;"
style="--Table-thead-height: 0px; --Table-column-3-width: 0px; --Table-column-4-width: 0px; --Table-column-5-width: 0px; position: relative;"
>
<div
class="cxd-Table-contentWrap"
@ -690,7 +690,7 @@ exports[`Renderer:input-table cell selects delete 1`] = `
>
<div
class="cxd-Table"
style="--Table-column-3-width: 0px; --Table-column-4-width: 0px; position: relative;"
style="--Table-thead-height: 0px; --Table-column-3-width: 0px; --Table-column-4-width: 0px; position: relative;"
>
<div
class="cxd-Table-contentWrap"
@ -1029,7 +1029,7 @@ exports[`Renderer:input-table init display 1`] = `
>
<div
class="cxd-Table"
style="--Table-column-3-width: 0px; --Table-column-4-width: 0px; --Table-column-5-width: 0px; --Table-column-6-width: 0px; position: relative;"
style="--Table-thead-height: 0px; --Table-column-3-width: 0px; --Table-column-4-width: 0px; --Table-column-5-width: 0px; --Table-column-6-width: 0px; position: relative;"
>
<div
class="cxd-Table-contentWrap"
@ -1716,7 +1716,7 @@ exports[`Renderer:input-table with combo column 1`] = `
>
<div
class="cxd-Table"
style="--Table-column-3-width: 0px; position: relative;"
style="--Table-thead-height: 0px; --Table-column-3-width: 0px; position: relative;"
>
<div
class="cxd-Table-contentWrap"

View File

@ -659,6 +659,17 @@ test('Renderer:combo with canAccessSuperData & strictMode & syncFields', async (
expect(comboInputs[0]!.value).toBe('');
expect(comboInputs[1]!.value).toBe('123');
expect(comboInputs[2]!.value).toBe('123456');
fireEvent.click(submitBtn);
await wait(300);
expect(onSubmit).toHaveBeenCalled();
expect(onSubmit.mock.calls[0][0]).toMatchObject({
super_text: '123456',
combo1: [{}],
combo2: [{super_text: '123'}],
combo3: [{super_text: '123456'}]
});
});
// 9. tabsMode

View File

@ -194,6 +194,7 @@ test('Renderer:inputArray with minLength & maxLength', async () => {
expect(container.querySelector('.cxd-Combo-addBtn')).toBeInTheDocument();
// 最大值
fireEvent.click(container.querySelector('.cxd-Combo-addBtn')!);
await wait(300);
await waitFor(() => {
expect(container.querySelector('a.cxd-Combo-delBtn')).toBeInTheDocument();
expect(

View File

@ -540,4 +540,44 @@ test('Renderer:inputDate defaultValue with formula', async () => {
expect(input).toBeInTheDocument();
expect(input.value).toBe('2021-12-06');
});
test('Renderer:inputDate setValue actions with special words', async () => {
const {container, submitBtn, onSubmit, getByText} = await setup([
{
type: 'input-date',
name: 'date',
id: 'date',
label: '日期',
format: 'YYYY-MM-DD'
},
{
type: 'button',
label: '设置值',
onEvent: {
click: {
actions: [
{
actionType: 'setValue',
componentId: 'date',
args: {
value: 'today'
}
}
]
}
}
}
]);
// 打开弹框
fireEvent.click(getByText('设置值'));
await wait(200);
const today = moment();
const inputDate = container.querySelector('.cxd-DatePicker-input');
expect(inputDate).toBeInTheDocument();
expect((inputDate as any)?.value).toEqual(today.format('YYYY-MM-DD'));
});

View File

@ -0,0 +1,92 @@
/**
* InputDateRange
*
*/
import React from 'react';
import PageRenderer from '../../../../amis-core/src/renderers/Form';
import * as renderer from 'react-test-renderer';
import {
render,
fireEvent,
cleanup,
getByText,
waitFor,
screen
} from '@testing-library/react';
import '../../../src';
import {render as amisRender} from '../../../src';
import {makeEnv, wait} from '../../helper';
import moment from 'moment';
const setup = async (items: any[] = []) => {
const onSubmit = jest.fn();
const utils = render(
amisRender(
{
type: 'page',
body: {
type: 'form',
submitText: 'Submit',
api: '/api/mock/saveForm?waitSeconds=1',
mode: 'horizontal',
body: items
}
},
{onSubmit},
makeEnv()
)
);
const submitBtn = utils.container.querySelector(
'button[type="submit"]'
) as HTMLElement;
return {
onSubmit,
submitBtn,
...utils
};
};
test('1.Renderer:inputDateRange setValue actions with special words', async () => {
const {container, submitBtn, onSubmit, getByText} = await setup([
{
type: 'input-date-range',
name: 'date',
id: 'date',
label: '日期',
format: 'YYYY-MM-DD'
},
{
type: 'button',
label: '设置值',
onEvent: {
click: {
actions: [
{
actionType: 'setValue',
componentId: 'date',
args: {
value: 'today,+2days'
}
}
]
}
}
}
]);
// 打开弹框
fireEvent.click(getByText('设置值'));
await wait(200);
const today = moment();
const inputDates = container.querySelectorAll('.cxd-DateRangePicker-input');
expect(inputDates.length).toBe(2);
expect((inputDates[0] as any)?.value).toEqual(today.format('YYYY-MM-DD'));
expect((inputDates[1] as any)?.value).toEqual(
moment(today).add(2, 'days').format('YYYY-MM-DD')
);
});

View File

@ -71,3 +71,42 @@ describe('Renderer:Switch', () => {
expect(container).toMatchSnapshot();
});
});
/**
*
*
*
*/
test('Renderer:Switch with loading status', async () => {
const {container} = render(
amisRender(
{
type: 'form',
body: [
{
"type": "switch",
"name": "switch1",
"label": "",
"loading": true,
"value": true
},
{
"type": "switch",
"name": "switch2",
"label": "",
"disabled": true,
"loading": true,
"value": false
}
],
actions: []
},
{},
makeEnv()
)
);
const loadingDom = container.querySelectorAll('.cxd-Switch-spinner');
expect(loadingDom?.length).toEqual(2);
});

View File

@ -958,19 +958,7 @@ describe('Renderer:table selectable & itemCheckableOn', () => {
]
};
test('radio style', async () => {
const {container} = render(amisRender(schema, {}, makeEnv({})));
await waitFor(() => {
expect(container.querySelector('[type=radio]')).toBeInTheDocument();
});
expect(
container.querySelector('[data-id="1"] [type=radio][disabled=""]')!
).toBeInTheDocument();
});
test('checkbox style', async () => {
schema.multiple = true;
const {container} = render(amisRender(schema, {}, makeEnv({})));
await waitFor(() => {
expect(container.querySelector('[type=checkbox]')).toBeInTheDocument();
@ -980,6 +968,18 @@ describe('Renderer:table selectable & itemCheckableOn', () => {
container.querySelector('[data-id="1"] [type=checkbox][disabled=""]')!
).toBeInTheDocument();
});
test('radio style', async () => {
schema.multiple = false;
const {container} = render(amisRender(schema, {}, makeEnv({})));
await waitFor(() => {
expect(container.querySelector('[type=radio]')).toBeInTheDocument();
});
expect(
container.querySelector('[data-id="1"] [type=radio][disabled=""]')!
).toBeInTheDocument();
});
});
describe('dbClick', () => {

View File

@ -20,7 +20,7 @@ exports[`1. Renderer:crud basic interval headerToolbar footerToolbar 1`] = `
>
<div
class="cxd-Table cxd-Crud-body"
style="--Table-column-3-width: 0px; --Table-column-4-width: 0px; --Table-column-5-width: 0px; position: relative;"
style="--Table-thead-height: 0px; --Table-column-3-width: 0px; --Table-column-4-width: 0px; --Table-column-5-width: 0px; position: relative;"
>
<div
class="cxd-Table-fixedTop"
@ -2157,7 +2157,7 @@ exports[`6. Renderer:crud source & alwaysShowPagination 1`] = `
>
<div
class="cxd-Table cxd-Crud-body"
style="--Table-column-3-width: 0px; --Table-column-4-width: 0px; --Table-column-5-width: 0px; position: relative;"
style="--Table-thead-height: 0px; --Table-column-3-width: 0px; --Table-column-4-width: 0px; --Table-column-5-width: 0px; position: relative;"
>
<div
class="cxd-Table-fixedTop"
@ -2799,7 +2799,7 @@ exports[`13. enderer: crud keepItemSelectionOnPageChange & maxKeepItemSelectionL
</div>
<div
class="cxd-Table cxd-Crud-body"
style="--Table-column-3-width: 0px; --Table-column-4-width: 0px; position: relative; --Table-column-1-width: 0px;"
style="--Table-thead-height: 0px; --Table-column-3-width: 0px; --Table-column-4-width: 0px; position: relative; --Table-column-1-width: 0px;"
>
<div
class="cxd-Table-fixedTop"

View File

@ -83,7 +83,7 @@ exports[`Renderer:Pagination 1`] = `
</div>
<div
class="cxd-Table"
style="--Table-column-3-width: 0px; --Table-column-4-width: 0px; position: relative;"
style="--Table-thead-height: 0px; --Table-column-3-width: 0px; --Table-column-4-width: 0px; position: relative;"
>
<div
class="cxd-Table-fixedTop"

View File

@ -4,7 +4,7 @@ exports[`Renderer:table 1`] = `
<div>
<div
class="cxd-Table"
style="--Table-column-3-width: 0px; --Table-column-4-width: 0px; position: relative;"
style="--Table-thead-height: 0px; --Table-column-3-width: 0px; --Table-column-4-width: 0px; position: relative;"
>
<div
class="cxd-Table-fixedTop"
@ -370,7 +370,7 @@ exports[`Renderer:table align 1`] = `
<div>
<div
class="cxd-Table"
style="--Table-column-3-width: 0px; --Table-column-4-width: 0px; position: relative;"
style="--Table-thead-height: 0px; --Table-column-3-width: 0px; --Table-column-4-width: 0px; position: relative;"
>
<div
class="cxd-Table-fixedTop"
@ -775,7 +775,7 @@ exports[`Renderer:table children 1`] = `
>
<div
class="cxd-Table m-b-none"
style="--Table-column-3-width: 0px; --Table-column-4-width: 0px; --Table-column-5-width: 0px; --Table-column-6-width: 0px; --Table-column-7-width: 0px; --Table-column-8-width: 0px; position: relative;"
style="--Table-thead-height: 0px; --Table-column-3-width: 0px; --Table-column-4-width: 0px; --Table-column-5-width: 0px; --Table-column-6-width: 0px; --Table-column-7-width: 0px; --Table-column-8-width: 0px; position: relative;"
>
<div
class="cxd-Table-fixedTop"
@ -1355,7 +1355,7 @@ exports[`Renderer:table classNameExpr 1`] = `
<div>
<div
class="cxd-Table"
style="--Table-column-3-width: 0px; --Table-column-4-width: 0px; position: relative;"
style="--Table-thead-height: 0px; --Table-column-3-width: 0px; --Table-column-4-width: 0px; position: relative;"
>
<div
class="cxd-Table-fixedTop"
@ -1721,7 +1721,7 @@ exports[`Renderer:table column head style className 1`] = `
<div>
<div
class="cxd-Table className"
style="--Table-column-3-width: 0px; --Table-column-4-width: 0px; position: relative;"
style="--Table-thead-height: 0px; --Table-column-3-width: 0px; --Table-column-4-width: 0px; position: relative;"
>
<div
class="cxd-Table-fixedTop"
@ -2114,7 +2114,7 @@ exports[`Renderer:table combine Renderer:table combineNum only 1`] = `
>
<div
class="cxd-Table"
style="--Table-column-3-width: 0px; --Table-column-4-width: 0px; --Table-column-5-width: 0px; --Table-column-6-width: 0px; --Table-column-7-width: 0px; position: relative;"
style="--Table-thead-height: 0px; --Table-column-3-width: 0px; --Table-column-4-width: 0px; --Table-column-5-width: 0px; --Table-column-6-width: 0px; --Table-column-7-width: 0px; position: relative;"
>
<div
class="cxd-Table-fixedTop"
@ -2689,7 +2689,7 @@ exports[`Renderer:table combine Renderer:table combineNum with fromIndex 1`] = `
>
<div
class="cxd-Table"
style="--Table-column-3-width: 0px; --Table-column-4-width: 0px; --Table-column-5-width: 0px; --Table-column-6-width: 0px; --Table-column-7-width: 0px; position: relative;"
style="--Table-thead-height: 0px; --Table-column-3-width: 0px; --Table-column-4-width: 0px; --Table-column-5-width: 0px; --Table-column-6-width: 0px; --Table-column-7-width: 0px; position: relative;"
>
<div
class="cxd-Table-fixedTop"
@ -3334,7 +3334,7 @@ exports[`Renderer:table groupName-default 1`] = `
>
<div
class="cxd-Table m-b-none"
style="--Table-column-3-width: 0px; --Table-column-4-width: 0px; --Table-column-5-width: 0px; --Table-column-6-width: 0px; --Table-column-8-width: 0px; position: relative;"
style="--Table-thead-height: 0px; --Table-column-3-width: 0px; --Table-column-4-width: 0px; --Table-column-5-width: 0px; --Table-column-6-width: 0px; --Table-column-8-width: 0px; position: relative;"
>
<div
class="cxd-Table-fixedTop"
@ -4113,7 +4113,7 @@ exports[`Renderer:table groupName-middleNoGroupName 1`] = `
>
<div
class="cxd-Table m-b-none"
style="--Table-column-3-width: 0px; --Table-column-4-width: 0px; --Table-column-5-width: 0px; --Table-column-6-width: 0px; --Table-column-8-width: 0px; position: relative;"
style="--Table-thead-height: 0px; --Table-column-3-width: 0px; --Table-column-4-width: 0px; --Table-column-5-width: 0px; --Table-column-6-width: 0px; --Table-column-8-width: 0px; position: relative;"
>
<div
class="cxd-Table-fixedTop"
@ -4896,7 +4896,7 @@ exports[`Renderer:table groupName-startNoGroupName 1`] = `
>
<div
class="cxd-Table m-b-none"
style="--Table-column-3-width: 0px; --Table-column-4-width: 0px; --Table-column-5-width: 0px; --Table-column-6-width: 0px; --Table-column-8-width: 0px; position: relative;"
style="--Table-thead-height: 0px; --Table-column-3-width: 0px; --Table-column-4-width: 0px; --Table-column-5-width: 0px; --Table-column-6-width: 0px; --Table-column-8-width: 0px; position: relative;"
>
<div
class="cxd-Table-fixedTop"
@ -5667,7 +5667,7 @@ exports[`Renderer:table groupName-withTpl 1`] = `
>
<div
class="cxd-Table m-b-none"
style="--Table-column-3-width: 0px; --Table-column-4-width: 0px; --Table-column-5-width: 0px; --Table-column-6-width: 0px; --Table-column-8-width: 0px; position: relative;"
style="--Table-thead-height: 0px; --Table-column-3-width: 0px; --Table-column-4-width: 0px; --Table-column-5-width: 0px; --Table-column-6-width: 0px; --Table-column-8-width: 0px; position: relative;"
>
<div
class="cxd-Table-fixedTop"
@ -6430,7 +6430,7 @@ exports[`Renderer:table isHead fixed 1`] = `
<div>
<div
class="cxd-Table"
style="--Table-column-3-width: 0px; --Table-column-4-width: 0px; position: relative;"
style="--Table-thead-height: 0px; --Table-column-3-width: 0px; --Table-column-4-width: 0px; position: relative;"
>
<div
class="cxd-Table-fixedTop"
@ -6807,7 +6807,7 @@ exports[`Renderer:table list 1`] = `
<div>
<div
class="cxd-Table"
style="--Table-column-3-width: 0px; --Table-column-4-width: 0px; --Table-column-5-width: 0px; --Table-column-6-width: 0px; --Table-column-7-width: 0px; --Table-column-8-width: 0px; --Table-column-9-width: 0px; --Table-column-10-width: 0px; --Table-column-11-width: 0px; position: relative;"
style="--Table-thead-height: 0px; --Table-column-3-width: 0px; --Table-column-4-width: 0px; --Table-column-5-width: 0px; --Table-column-6-width: 0px; --Table-column-7-width: 0px; --Table-column-8-width: 0px; --Table-column-9-width: 0px; --Table-column-10-width: 0px; --Table-column-11-width: 0px; position: relative;"
>
<div
class="cxd-Table-toolbar cxd-Table-headToolbar"

View File

@ -1,6 +1,6 @@
{
"name": "amis",
"version": "3.5.1",
"version": "3.5.3",
"description": "一种MIS页面生成工具",
"main": "lib/index.js",
"module": "esm/index.js",
@ -37,8 +37,8 @@
}
],
"dependencies": {
"amis-core": "^3.5.1",
"amis-ui": "^3.5.1",
"amis-core": "^3.5.3",
"amis-ui": "^3.5.3",
"attr-accept": "2.2.2",
"blueimp-canvastoblob": "2.1.0",
"classnames": "2.3.2",
@ -67,7 +67,6 @@
"qrcode.react": "^3.1.0",
"react-cropper": "^2.1.8",
"react-dropzone": "^11.4.2",
"react-error-boundary": "^4.0.11",
"react-intersection-observer": "9.5.2",
"react-json-view": "1.21.3",
"react-transition-group": "4.4.2",

View File

@ -6,7 +6,8 @@ import {
isPureVariable,
resolveVariableAndFilter,
setVariable,
setThemeClassName
setThemeClassName,
ValidateError
} from 'amis-core';
import {Renderer, RendererProps} from 'amis-core';
import {SchemaNode, Schema, ActionObject} from 'amis-core';
@ -805,6 +806,7 @@ export default class Dialog extends React.Component<DialogProps> {
})
export class DialogRenderer extends Dialog {
static contextType = ScopedContext;
clearErrorTimer: ReturnType<typeof setTimeout>;
constructor(props: DialogProps, context: IScopedContext) {
super(props);
@ -817,6 +819,7 @@ export class DialogRenderer extends Dialog {
const scoped = this.context as IScopedContext;
scoped.unRegisterComponent(this);
super.componentWillUnmount();
clearTimeout(this.clearErrorTimer);
}
tryChildrenToHandle(
@ -897,6 +900,16 @@ export class DialogRenderer extends Dialog {
}
store.updateMessage(reason.message, true);
store.markBusying(false);
if (reason.constructor?.name === ValidateError.name) {
clearTimeout(this.clearErrorTimer);
this.clearErrorTimer = setTimeout(() => {
if (this.isDead) {
return;
}
store.updateMessage('');
}, 3000);
}
});
return true;

View File

@ -5,7 +5,8 @@ import {
filterTarget,
isPureVariable,
resolveVariableAndFilter,
setThemeClassName
setThemeClassName,
ValidateError
} from 'amis-core';
import {Renderer, RendererProps} from 'amis-core';
import {SchemaNode, Schema, ActionObject} from 'amis-core';
@ -214,6 +215,7 @@ export default class Drawer extends React.Component<DrawerProps> {
reaction: any;
$$id: string = guid();
drawer: any;
clearErrorTimer: ReturnType<typeof setTimeout> | undefined;
constructor(props: DrawerProps) {
super(props);
@ -251,6 +253,7 @@ export default class Drawer extends React.Component<DrawerProps> {
componentWillUnmount() {
this.reaction && this.reaction();
clearTimeout(this.clearErrorTimer);
}
buildActions(): Array<ActionSchema> {
@ -850,6 +853,13 @@ export class DrawerRenderer extends Drawer {
.catch(reason => {
store.updateMessage(reason.message, true);
store.markBusying(false);
if (reason.constructor?.name === ValidateError.name) {
clearTimeout(this.clearErrorTimer);
this.clearErrorTimer = setTimeout(() => {
store.updateMessage('');
}, 3000);
}
});
return true;

View File

@ -541,8 +541,10 @@ export default class ComboControl extends React.Component<ComboProps> {
}
getValueAsArray(props = this.props) {
const {flat, joinValues, delimiter, type} = props;
let value = props.value;
const {flat, joinValues, delimiter, type, formItem} = props;
// 因为 combo 多个子表单可能同时发生变化。
// onChagne 触发多次,上次变更还没应用到 props.value 上来,这次触发变更就会包含历史数据,把上次触发的数据给重置成旧的了。
let value = formItem?.tmpValue || props.value;
if (joinValues && flat && typeof value === 'string') {
value = value.split(delimiter || ',');

View File

@ -565,6 +565,21 @@ export default class DateControl extends React.PureComponent<
}
}
setData(value: any) {
const {data, valueFormat, format, utc, onChange} = this.props;
if (
typeof value === 'string' ||
typeof value === 'number' ||
value instanceof Date
) {
const date = filterDate(value as any, data, valueFormat || format);
value = (utc ? moment.utc(date) : date).format(valueFormat || format);
}
onChange(value);
}
// 值的变化
@autobind
async handleChange(nextValue: any) {

View File

@ -246,6 +246,27 @@ export default class DateRangeControl extends React.Component<DateRangeProps> {
}
}
setData(value: any) {
const {data, delimiter, valueFormat, format, joinValues, utc, onChange} =
this.props;
if (typeof value === 'string') {
let arr = typeof value === 'string' ? value.split(delimiter) : value;
value = DateRangePicker.formatValue(
{
startDate: filterDate(arr[0], data, valueFormat || format),
endDate: filterDate(arr[1], data, valueFormat || format)
},
valueFormat || format,
joinValues,
delimiter,
utc
);
}
onChange(value);
}
// 值的变化
@autobind
async handleChange(nextValue: any) {

View File

@ -1775,10 +1775,6 @@ export class TableControlRenderer extends FormTable {
const ctx = this.props.store?.data || {}; // 获取当前上下文数据
if (actionType === 'addItem') {
if (addable === false) {
return;
}
const items = this.state.items.concat();
if (addApi || args) {

View File

@ -29,6 +29,23 @@ export interface LocationControlSchema extends FormBaseControlSchema {
* ak
*/
ak?: string;
/**
*
*/
autoSelectCurrentLoc?: boolean;
/**
*
*
*/
onlySelectCurrentLoc?: boolean;
/**
*
* placeholder
*/
getLocationPlaceholder?: string;
}
export interface LocationControlProps
@ -51,33 +68,6 @@ export class LocationControl extends React.Component<LocationControlProps> {
coordinatesType: 'bd09'
};
domRef: React.RefObject<HTMLDivElement> = React.createRef();
state = {
isOpened: false
};
@autobind
close() {
this.setState({
isOpened: false
});
}
@autobind
open() {
this.setState({
isOpened: true
});
}
@autobind
handleClick() {
this.state.isOpened ? this.close() : this.open();
}
@autobind
getParent() {
return this.domRef.current?.parentElement;
}
@autobind
getTarget() {
@ -85,14 +75,7 @@ export class LocationControl extends React.Component<LocationControlProps> {
}
renderStatic(displayValue = '-') {
const {
classnames: cx,
value,
vendor,
ak,
coordinatesType,
popOverContainer
} = this.props;
const {classnames: cx, value} = this.props;
const __ = this.props.translate;
if (!value) {
@ -107,35 +90,6 @@ export class LocationControl extends React.Component<LocationControlProps> {
ref={this.domRef}
>
<span>{value.address}</span>
<a
className={cx('LocationPicker-toggler', 'ml-1')}
onClick={this.handleClick}
>
<Icon icon="location" className="icon" />
</a>
<Overlay
target={this.getTarget}
container={popOverContainer || this.getParent}
rootClose={false}
show={this.state.isOpened}
>
<PopOver
className={cx('LocationPicker-popover')}
onHide={this.close}
overlay
style={{width: this.getTarget()?.offsetWidth}}
>
{vendor === 'baidu' ? (
<BaiduMapPicker
ak={ak}
value={value}
coordinatesType={coordinatesType}
/>
) : (
<Alert2>{__('{{vendor}} 地图控件不支持', {vendor})}</Alert2>
)}
</PopOver>
</Overlay>
</div>
);
}

View File

@ -1,7 +1,6 @@
import React from 'react';
import toString from 'lodash/toString';
import {getPropValue, FormControlProps} from 'amis-core';
import {ErrorBoundary} from 'react-error-boundary';
import {ErrorBoundary} from 'amis-core';
function renderCommonStatic(props: any, defaultValue: string) {
const {type, render, staticSchema} = props;
@ -135,11 +134,19 @@ export function supportStatic<T extends FormControlProps>() {
}
return (
<div className={cx(`${ns}Form-static`, className)}>
<ErrorBoundary fallback={<>{toString(body)}</>}>
{body}
</ErrorBoundary>
</div>
<ErrorBoundary
customErrorMsg={`拦截到${props.$schema.type}渲染错误当前组件schema: ${props.$schema}`}
fallback={() => {
return (
<div className="renderer-error-boundary">
{props.$schema?.type}
</div>
);
}}
>
<div className={cx(`${ns}Form-static`, className)}>{body}</div>
</ErrorBoundary>
);
}

View File

@ -6,11 +6,13 @@ import {
resolveEventData
} from 'amis-core';
import {Icon, Switch} from 'amis-ui';
import {createObject, autobind, isObject} from 'amis-core';
import {autobind, isObject} from 'amis-core';
import {IconSchema} from '../Icon';
import {FormBaseControlSchema} from '../../Schema';
import {supportStatic} from './StaticHoc';
import type {SpinnerExtraProps} from 'amis-ui';
/**
* Switch
* https://aisuda.bce.baidu.com/amis/zh-CN/components/form/switch
@ -49,13 +51,17 @@ export interface SwitchControlSchema extends FormBaseControlSchema {
/** 开关尺寸 */
size?: 'sm' | 'md';
/** 是否处于加载状态 */
loading?: boolean;
}
export interface SwitchProps extends FormControlProps {
export interface SwitchProps extends FormControlProps, SpinnerExtraProps {
option?: string;
trueValue?: any;
falseValue?: any;
size?: 'sm';
loading?: boolean;
}
export type SwitchRendererEvent = 'change';
@ -129,7 +135,9 @@ export default class SwitchControl extends React.Component<SwitchProps, any> {
trueValue,
falseValue,
onChange,
disabled
disabled,
loading,
loadingConfig
} = this.props;
const {on, off} = this.getResult();
@ -147,6 +155,8 @@ export default class SwitchControl extends React.Component<SwitchProps, any> {
disabled={disabled}
onChange={this.handleChange}
size={size as any}
loading={loading}
loadingConfig={loadingConfig}
/>
)}
</div>

View File

@ -30,7 +30,10 @@ function ItemActionsWrapper(props: ItemActionsProps) {
}
const rect = dom.getBoundingClientRect();
const height = rect.height;
const top = rect.top - frame.getBoundingClientRect().top;
const top =
rect.top -
frame.getBoundingClientRect().top +
parseInt(getComputedStyle(frame)['marginTop'], 10);
divRef.current!.style.cssText += `top: ${top}px;height: ${height}px;`;
}, [store.hoverRow?.id]);

View File

@ -1591,6 +1591,10 @@ export default class Table extends React.Component<TableProps, object> {
document.addEventListener('mousemove', this.handleColResizeMouseMove);
document.addEventListener('mouseup', this.handleColResizeMouseUp);
// 防止选中文本
e.preventDefault();
e.stopPropagation();
}
// 垂直线拖拽移动
@ -1760,8 +1764,6 @@ export default class Table extends React.Component<TableProps, object> {
store,
query,
onQuery,
multiple,
env,
render,
classPrefix: ns,
resizable,
@ -1805,7 +1807,7 @@ export default class Table extends React.Component<TableProps, object> {
style={style}
className={cx(column.pristine.className, stickyClassName)}
>
{store.rows.length && multiple ? (
{store.rows.length && store.multiple ? (
<Checkbox
classPrefix={ns}
partial={store.someChecked && !store.allChecked}
@ -2038,11 +2040,8 @@ export default class Table extends React.Component<TableProps, object> {
const {
render,
store,
multiple,
classPrefix: ns,
classnames: cx,
checkOnItemClick,
popOverContainer,
canAccessSuperData,
itemBadge,
translate
@ -2058,7 +2057,7 @@ export default class Table extends React.Component<TableProps, object> {
ignoreDrag={ignoreDrag}
render={render}
store={store}
multiple={multiple}
multiple={store.multiple}
canAccessSuperData={canAccessSuperData}
classnames={cx}
classPrefix={ns}