From bc3d9d97bd1b1ea3382241f2f71b6e8c1351a697 Mon Sep 17 00:00:00 2001 From: liaoxuezhi Date: Wed, 4 Nov 2020 21:58:43 +0800 Subject: [PATCH 1/3] Update each.md --- docs/components/each.md | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/docs/components/each.md b/docs/components/each.md index 885298b54..b41764423 100755 --- a/docs/components/each.md +++ b/docs/components/each.md @@ -10,13 +10,19 @@ order: 45 ## 基本用法 -```schema:height="160" scope="body" +```schema:height="160" scope="page" { - "type": "each", - "value": ["A", "B", "C"], - "items": { - "type": "tpl", - "tpl": "<%= this.item %> " + "type": "page", + "data": { + "arr": ["A", "B", "C"] + }, + "body": { + "type": "each", + "name": "arr", + "items": { + "type": "tpl", + "tpl": "<%= this.item %> " + } } } ``` From ef5a24fab4852ad458f11b3d0b2d48670cb68e2c Mon Sep 17 00:00:00 2001 From: liaoxuezhi Date: Thu, 5 Nov 2020 10:59:58 +0800 Subject: [PATCH 2/3] Update each.md --- docs/components/each.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/components/each.md b/docs/components/each.md index b41764423..438e875ca 100755 --- a/docs/components/each.md +++ b/docs/components/each.md @@ -21,7 +21,7 @@ order: 45 "name": "arr", "items": { "type": "tpl", - "tpl": "<%= this.item %> " + "tpl": "<%= data.item %> " } } } From 34c61c5c89e95ab1de299ac9476ad8944c84eeac Mon Sep 17 00:00:00 2001 From: liaoxuezhi Date: Thu, 5 Nov 2020 18:17:06 +0800 Subject: [PATCH 3/3] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=20search-box=20=E5=B7=B2?= =?UTF-8?q?=E7=BB=8F=E5=A4=9A=E4=B8=AA=E5=AE=B9=E5=99=A8=E7=BB=84=E4=BB=B6?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=20onQuery=20=E5=93=8D=E5=BA=94=20(#1036)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scss/_variables.scss | 1 + scss/components/_page.scss | 5 ++ scss/components/_search-box.scss | 8 +++ src/Schema.ts | 11 ++-- src/components/SearchBox.tsx | 63 ++++++++++++++++++++-- src/index.tsx | 1 + src/renderers/CRUD.tsx | 9 ++-- src/renderers/Form/index.tsx | 11 ++++ src/renderers/Page.tsx | 19 +++++-- src/renderers/SearchBox.tsx | 89 ++++++++++++++++++++++++++++++++ src/renderers/Service.tsx | 6 ++- src/renderers/Wizard.tsx | 10 ++++ 12 files changed, 216 insertions(+), 17 deletions(-) create mode 100644 src/renderers/SearchBox.tsx diff --git a/scss/_variables.scss b/scss/_variables.scss index 6b8134c1b..afdf0db24 100644 --- a/scss/_variables.scss +++ b/scss/_variables.scss @@ -137,6 +137,7 @@ $gap-lg: px2rem(30px) !default; $icon-color: $gray600 !default; $icon-onHover-color: $gray900 !default; +$icon-onDisabled-color: $gray500 !default; $label--default-bg: $gray700 !default; $label--primary-bg: $primary !default; diff --git a/scss/components/_page.scss b/scss/components/_page.scss index 30c9e6f2b..0380da21d 100644 --- a/scss/components/_page.scss +++ b/scss/components/_page.scss @@ -39,6 +39,11 @@ .#{$ns}Page-toolbar { text-align: right; padding-right: $gap-base; + display: flex; + flex-direction: row; + justify-content: flex-end; + align-items: center; + flex-wrap: wrap; } } diff --git a/scss/components/_search-box.scss b/scss/components/_search-box.scss index 03a7b723a..cc759b7fa 100644 --- a/scss/components/_search-box.scss +++ b/scss/components/_search-box.scss @@ -30,6 +30,7 @@ } &-activeBtn, + &-searchBtn, &-cancelBtn { cursor: pointer; color: $icon-color; @@ -39,6 +40,13 @@ } } + &.is-disabled &-activeBtn, + &.is-disabled &-searchBtn, + &.is-disabled &-cancelBtn { + color: $icon-onDisabled-color; + pointer-events: none; + } + > input { outline: none; border: none; diff --git a/src/Schema.ts b/src/Schema.ts index b941feac5..37b1fa35e 100644 --- a/src/Schema.ts +++ b/src/Schema.ts @@ -47,14 +47,11 @@ import {WrapperSchema} from './renderers/Wrapper'; import {TableSchema} from './renderers/Table'; import {DialogSchema} from './renderers/Dialog'; import {DrawerSchema} from './renderers/Drawer'; +import {SearchBoxSchema} from './renderers/SearchBox'; // 每加个类型,这补充一下。 export type SchemaType = - | 'page' | 'form' - | 'tpl' - | 'html' - | 'remark' | 'button' | 'submit' | 'reset' @@ -99,6 +96,7 @@ export type SchemaType = | 'map' | 'mapping' | 'nav' + | 'page' | 'operation' | 'panel' | 'plain' @@ -106,12 +104,16 @@ export type SchemaType = | 'progress' | 'qrcode' | 'qr-code' + | 'remark' + | 'search-box' | 'service' | 'status' | 'switch' | 'table' | 'static-table' // 这个几个跟表单项同名,再form下面用必须带前缀 static- | 'tabs' + | 'html' + | 'tpl' | 'tasks' | 'vbox' | 'video' @@ -157,6 +159,7 @@ export type SchemaObject = | PlainSchema | ProgressSchema | QRCodeSchema + | SearchBoxSchema | ServiceSchema | StatusSchema | SwitchSchema diff --git a/src/components/SearchBox.tsx b/src/components/SearchBox.tsx index 9fa72132d..2688acb43 100644 --- a/src/components/SearchBox.tsx +++ b/src/components/SearchBox.tsx @@ -4,13 +4,19 @@ import {Icon} from './icons'; import {uncontrollable} from 'uncontrollable'; import {autobind} from '../utils/helper'; import {LocaleProps, localeable} from '../locale'; +import debounce from 'lodash/debounce'; export interface SearchBoxProps extends ThemeProps, LocaleProps { name?: string; + disabled?: boolean; + mini?: boolean; + searchImediately?: boolean; onChange?: (text: string) => void; placeholder?: string; + defaultValue?: string; value?: string; active?: boolean; + defaultActive?: boolean; onActiveChange?: (active: boolean) => void; onSearch?: (value: string) => void; onCancel?: () => void; @@ -18,6 +24,26 @@ export interface SearchBoxProps extends ThemeProps, LocaleProps { export class SearchBox extends React.Component { inputRef: React.RefObject = React.createRef(); + static defaultProps = { + mini: true, + searchImediately: true + }; + + lazyEmitSearch = debounce( + () => { + const onSearch = this.props.onSearch; + onSearch?.(this.props.value || ''); + }, + 250, + { + leading: false, + trailing: true + } + ); + + componentWillUnmount() { + this.lazyEmitSearch.cancel(); + } @autobind handleActive() { @@ -36,9 +62,23 @@ export class SearchBox extends React.Component { @autobind handleChange(e: React.ChangeEvent) { - const {onChange, onSearch} = this.props; + const {onChange, onSearch, searchImediately} = this.props; onChange?.(e.currentTarget.value); - onSearch?.(e.currentTarget.value); + searchImediately && this.lazyEmitSearch(); + } + + @autobind + handleSearch() { + const {value, onSearch} = this.props; + onSearch?.(value || ''); + } + + @autobind + handleKeyUp(e: React.KeyboardEvent) { + if (e.key === 'Enter') { + this.handleSearch(); + e.preventDefault(); + } } render() { @@ -48,21 +88,36 @@ export class SearchBox extends React.Component { active, name, onChange, + disabled, placeholder, + mini, translate: __ } = this.props; return ( -
+
- {active ? ( + {!mini ? ( + + + + ) : active ? ( diff --git a/src/index.tsx b/src/index.tsx index 8400f68b0..b66a12c72 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -132,6 +132,7 @@ import './renderers/Switch'; import './renderers/Wizard'; import './renderers/Chart'; import './renderers/Container'; +import './renderers/SearchBox'; import './renderers/Service'; import './renderers/Video'; import './renderers/Audio'; diff --git a/src/renderers/CRUD.tsx b/src/renderers/CRUD.tsx index 335b46812..f08531f65 100644 --- a/src/renderers/CRUD.tsx +++ b/src/renderers/CRUD.tsx @@ -396,7 +396,7 @@ export default class CRUD extends React.Component { this.handleChildPopOverClose = this.handleChildPopOverClose.bind(this); this.search = this.search.bind(this); this.silentSearch = this.silentSearch.bind(this); - this.handlQuery = this.handlQuery.bind(this); + this.handleQuery = this.handleQuery.bind(this); this.renderHeaderToolbar = this.renderHeaderToolbar.bind(this); this.renderFooterToolbar = this.renderFooterToolbar.bind(this); this.clearSelection = this.clearSelection.bind(this); @@ -1345,7 +1345,7 @@ export default class CRUD extends React.Component { } } - handlQuery(values: object, forceReload: boolean = false) { + handleQuery(values: object, forceReload: boolean = false) { const {store, syncLocation, env, pageField, perPageField} = this.props; store.updateQuery( @@ -1371,7 +1371,7 @@ export default class CRUD extends React.Component { } receive(values: object) { - this.handlQuery(values, true); + this.handleQuery(values, true); } reloadTarget(target: string, data: any) { @@ -1801,6 +1801,7 @@ export default class CRUD extends React.Component { lastPage: store.lastPage, perPage: store.perPage, total: store.total, + onQuery: this.handleQuery, onAction: this.handleAction, onChangePage: this.handleChangePage, onBulkAction: this.handleBulkAction, @@ -1992,7 +1993,7 @@ export default class CRUD extends React.Component { onAction: this.handleAction, onSave: this.handleSave, onSaveOrder: this.handleSaveOrder, - onQuery: this.handlQuery, + onQuery: this.handleQuery, onSelect: this.handleSelect, onPopOverOpened: this.handleChildPopOverOpen, onPopOverClosed: this.handleChildPopOverClose, diff --git a/src/renderers/Form/index.tsx b/src/renderers/Form/index.tsx index ea7e36d78..739987739 100644 --- a/src/renderers/Form/index.tsx +++ b/src/renderers/Form/index.tsx @@ -368,6 +368,7 @@ export default class Form extends React.Component { this.onInit = this.onInit.bind(this); this.handleAction = this.handleAction.bind(this); + this.handleQuery = this.handleQuery.bind(this); this.handleDialogConfirm = this.handleDialogConfirm.bind(this); this.handleDialogClose = this.handleDialogClose.bind(this); this.handleDrawerConfirm = this.handleDrawerConfirm.bind(this); @@ -954,6 +955,14 @@ export default class Form extends React.Component { } } + handleQuery(query: any) { + if (this.props.initApi) { + this.receive(query); + } else { + this.props.onQuery?.(query); + } + } + handleDialogConfirm( values: object[], action: Action, @@ -1188,6 +1197,7 @@ export default class Form extends React.Component { disabled: disabled || (control as Schema).disabled || form.loading, btnDisabled: form.loading || form.validating, onAction: this.handleAction, + onQuery: this.handleQuery, onChange: formLazyChange === false ? this.handleChange : this.lazyHandleChange, addHook: this.addHook, @@ -1355,6 +1365,7 @@ export default class Form extends React.Component { children: body, actions: this.buildActions(), onAction: this.handleAction, + onQuery: this.handleQuery, disabled: store.loading, btnDisabled: store.loading || store.validating, headerClassName, diff --git a/src/renderers/Page.tsx b/src/renderers/Page.tsx index 77d2a2e45..97cf85e34 100644 --- a/src/renderers/Page.tsx +++ b/src/renderers/Page.tsx @@ -184,6 +184,7 @@ export default class Page extends React.Component { // autobind 会让继承里面的 super 指向有问题,所以先这样! bulkBindFunctions(this, [ 'handleAction', + 'handleQuery', 'handleDialogConfirm', 'handleDialogClose', 'handleDrawerConfirm', @@ -305,6 +306,10 @@ export default class Page extends React.Component { } } + handleQuery(query: any) { + this.receive(query); + } + handleDialogConfirm(values: object[], action: Action, ...args: Array) { const {store} = this.props; @@ -428,12 +433,14 @@ export default class Page extends React.Component { toolbar, render, store, + initApi, env, classnames: cx } = this.props; const subProps = { - onAction: this.handleAction + onAction: this.handleAction, + onQuery: initApi ? this.handleQuery : undefined }; let header, right; @@ -496,11 +503,13 @@ export default class Page extends React.Component { asideClassName, classnames: cx, header, - showErrorMsg + showErrorMsg, + initApi } = this.props; const subProps = { onAction: this.handleAction, + onQuery: initApi ? this.handleQuery : undefined, loading: store.loading }; @@ -560,7 +569,8 @@ export default class Page extends React.Component { onConfirm: this.handleDialogConfirm, onClose: this.handleDialogClose, show: store.dialogOpen, - onAction: this.handleAction + onAction: this.handleAction, + onQuery: initApi ? this.handleQuery : undefined } )} @@ -577,7 +587,8 @@ export default class Page extends React.Component { onConfirm: this.handleDrawerConfirm, onClose: this.handleDrawerClose, show: store.drawerOpen, - onAction: this.handleAction + onAction: this.handleAction, + onQuery: initApi ? this.handleQuery : undefined } )}
diff --git a/src/renderers/SearchBox.tsx b/src/renderers/SearchBox.tsx new file mode 100644 index 000000000..efcc84775 --- /dev/null +++ b/src/renderers/SearchBox.tsx @@ -0,0 +1,89 @@ +import Spinner from '../components/Spinner'; +import {Renderer, RendererProps} from '../factory'; +import React from 'react'; +import {BaseSchema} from '../Schema'; +import SearchBox from '../components/SearchBox'; +import {autobind, getVariable, setVariable} from '../utils/helper'; + +/** + * 搜索框渲染器 + */ +export interface SearchBoxSchema extends BaseSchema { + /** + * 指定为搜索框。 + * + * 文档:https://baidu.gitee.io/amis/docs/components/search-box + */ + type: 'search-box'; + + /** + * 关键字名字。 + * + * @default keywords + */ + name?: string; + + /** + * 是否为 Mini 样式。 + */ + mini?: boolean; + + /** + * 是否立马搜索。 + */ + searchImediately?: boolean; +} + +interface SearchBoxProps extends RendererProps, SearchBoxSchema { + name: string; + onQuery?: (query: {[propName: string]: string}) => void; +} + +@Renderer({ + test: /(^|\/)search\-box$/, + name: 'search' +}) +export class SearchBoxRenderer extends React.Component { + static defaultProps = { + name: 'keywords', + mini: false, + searchImediately: false + }; + + static propsList: Array = ['mini', 'searchImediately']; + + @autobind + handleCancel() { + const name = this.props.name; + const onQuery = this.props.onQuery; + const data: any = {}; + setVariable(data, name, ''); + onQuery?.(data); + } + + @autobind + handleSearch(text: string) { + const {name, onQuery: onQuery} = this.props; + const data: any = {}; + setVariable(data, name, text); + onQuery?.(data); + } + + render() { + const {data, name, onQuery: onQuery, mini, searchImediately} = this.props; + + const value = getVariable(data, name); + return ( + + ); + } +} diff --git a/src/renderers/Service.tsx b/src/renderers/Service.tsx index dc789db53..b6cafc160 100644 --- a/src/renderers/Service.tsx +++ b/src/renderers/Service.tsx @@ -261,7 +261,11 @@ export default class Service extends React.Component { } handleQuery(query: any) { - this.receive(query); + if (this.props.api || this.props.schemaApi) { + this.receive(query); + } else { + this.props.onQuery?.(query); + } } reloadTarget(target: string, data?: any) { diff --git a/src/renderers/Wizard.tsx b/src/renderers/Wizard.tsx index ee210dcf9..bdc843ea8 100644 --- a/src/renderers/Wizard.tsx +++ b/src/renderers/Wizard.tsx @@ -539,6 +539,15 @@ export default class Wizard extends React.Component { } } + @autobind + handleQuery(query: any) { + if (this.props.initApi) { + this.receive(query); + } else { + this.props.onQuery?.(query); + } + } + openFeedback(dialog: any, ctx: any) { return new Promise(resolve => { const {store} = this.props; @@ -945,6 +954,7 @@ export default class Wizard extends React.Component { onReset: this.handleReset, onSubmit: this.handleSubmit, onAction: this.handleAction, + onQuery: this.handleQuery, disabled: store.loading, popOverContainer: popOverContainer || this.getPopOverContainer,