diff --git a/docs/renderers/Form/Tag.md b/docs/renderers/Form/Tag.md index 0e48b5e19..24afe2a39 100644 --- a/docs/renderers/Form/Tag.md +++ b/docs/renderers/Form/Tag.md @@ -2,20 +2,15 @@ 标签输入框。 -- `type` 请设置成 `tag` -- `clearable` 在有值的时候是否显示一个删除图标在右侧。 -- `resetValue` 默认为 `""`, 删除后设置此配置项给定的值。 -- `optionsTip` 选项提示,默认为 `最近您使用的标签` -- `options` 选项配置,类型为数组,成员格式如下,或者直接为字符串,配置后用户输入内容时会作为选项提示辅助输入 - - `label` 文字 - - `value` 值 -- `source` 通过 `options` 只能配置静态数据,如果设置了 `source` 则会从接口拉取,实现动态效果。 -- `joinValues` 默认为 `true` -- 单选模式:当用户选中某个选项时,选项中的 value 将被作为该表单项的值提交,否则,整个选项对象都会作为该表单项的值提交。 -- 多选模式:选中的多个选项的 `value` 会通过 `delimiter` 连接起来,否则直接将以数组的形式提交值。 -- `delimiter` 默认为 `,` -- `extractValue` 默认为 `false`, `joinValues`设置为`false`时生效, 开启后将选中的选项 value 的值封装为数组,作为当前表单项的值。 -- **还有更多通用配置请参考** [FormItem](./FormItem.md) +- `type` 请设置成 `tag` +- `clearable` 在有值的时候是否显示一个删除图标在右侧。 +- `options` 选项配置,类型为数组,成员格式如下,或者直接为字符串,配置后用户输入内容时会作为选项提示辅助输入,可以不指定,当不指定时完全由用户手动输入。 + - `label` 文字 + - `value` 值 + - `children` 如果需要简单分组,可以考虑把选项包在某个选项的 children 里面。 +- `delimiter` 默认为 `,`,当标签在输入中,输入了这个字符时,也能自动创建一个新标签。 +- `extractValue` 默认为 `false`, `joinValues`设置为`false`时生效, 开启后将选中的选项 value 的值封装为数组,作为当前表单项的值。 +- **还有更多通用配置请参考** [FormItem](./FormItem.md) ```schema:height="200" scope="form-item" { @@ -25,7 +20,7 @@ } ``` -带提示功能 +待选项的标签输入。 ```schema:height="240" scope="form-item" { diff --git a/examples/components/Form/Full.jsx b/examples/components/Form/Full.jsx index 2a134a2dd..e87038d71 100644 --- a/examples/components/Form/Full.jsx +++ b/examples/components/Form/Full.jsx @@ -65,6 +65,7 @@ export default { label: '标签', placeholder: '', clearable: true, + inline: true, options: [ { label: '诸葛亮', diff --git a/scss/components/_list-menu.scss b/scss/components/_list-menu.scss index 56a26119a..9b91961e1 100644 --- a/scss/components/_list-menu.scss +++ b/scss/components/_list-menu.scss @@ -1,4 +1,5 @@ .#{$ns}ListMenu { + background: $ListMenu-item-bg; min-width: px2rem(200px); border: $ListMenu-borderWidth solid $ListMenu-bordrColor; border-radius: $ListMenu-borderRadius; @@ -19,7 +20,6 @@ &-item { display: flex; min-height: $ListMenu-item-height; - background: $ListMenu-item-bg; color: $ListMenu-item-color; line-height: $Form-input-lineHeight; font-size: $Form-input-fontSize; diff --git a/scss/components/form/_tag.scss b/scss/components/form/_tag.scss index f7e36d886..1890517b2 100644 --- a/scss/components/form/_tag.scss +++ b/scss/components/form/_tag.scss @@ -1,8 +1,20 @@ .#{$ns}TagControl { position: relative; + &-input.is-focused { + border-radius: 0; + } + + &.is-inline { + display: inline-block; + } + > .#{$ns}TagControl-popover { + box-shadow: none; + padding: 0; + border: none; width: 100%; + margin-top: $Form-select-popoverGap - $Form-select-outer-borderWidth; &.#{$ns}PopOver--leftBottomLeftTop { top: 100% !important; @@ -14,3 +26,15 @@ } } } + +.#{$ns}TagControl-popover { + &.#{$ns}PopOver--leftBottomLeftTop > .#{$ns}ListMenu { + border-top-left-radius: 0; + border-top-right-radius: 0; + } + + &.#{$ns}PopOver--leftTopLeftBottom > .#{$ns}ListMenu { + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; + } +} diff --git a/src/renderers/Form/Tag.tsx b/src/renderers/Form/Tag.tsx index ca4d97088..c6c694b09 100644 --- a/src/renderers/Form/Tag.tsx +++ b/src/renderers/Form/Tag.tsx @@ -311,6 +311,7 @@ export default class TagControl extends React.PureComponent< onBlur: this.handleBlur, disabled })} + className={cx('TagControl-input')} result={selectedOptions} onResultChange={this.handleChange} itemRender={this.renderItem} diff --git a/src/renderers/Service.tsx b/src/renderers/Service.tsx index ed5c0b350..2970bc4ef 100644 --- a/src/renderers/Service.tsx +++ b/src/renderers/Service.tsx @@ -2,14 +2,14 @@ import React from 'react'; import PropTypes from 'prop-types'; import {Renderer, RendererProps} from '../factory'; import {ServiceStore, IServiceStore} from '../store/service'; -import {Api, SchemaNode, ApiObject, RendererData} from '../types'; +import {Api, SchemaNode, ApiObject, RendererData, Action} from '../types'; import {filter, evalExpression} from '../utils/tpl'; import cx from 'classnames'; import Scoped, {ScopedContext, IScopedContext} from '../Scoped'; import {observer} from 'mobx-react'; import {isApiOutdated, isEffectiveApi} from '../utils/api'; import {Spinner} from '../components'; -import {autobind} from '../utils/helper'; +import {autobind, isVisible} from '../utils/helper'; export interface ServiceProps extends RendererProps { api?: Api; @@ -43,6 +43,7 @@ export default class Service extends React.Component { super(props); this.handleQuery = this.handleQuery.bind(this); + this.handleAction = this.handleAction.bind(this); this.reload = this.reload.bind(this); this.silentReload = this.silentReload.bind(this); this.initInterval = this.initInterval.bind(this); @@ -182,6 +183,60 @@ export default class Service extends React.Component { this.receive(query); } + reloadTarget(target: string, data?: any) { + // 会被覆写 + } + + openFeedback(dialog: any, ctx: any) { + return new Promise(resolve => { + const {store} = this.props; + const parentStore = store.parentStore; + + // 暂时自己不支持弹出 dialog + if (parentStore && parentStore.openDialog) { + store.setCurrentAction({ + type: 'button', + actionType: 'dialog', + dialog: dialog + }); + store.openDialog(ctx, undefined, confirmed => { + resolve(confirmed); + }); + } + }); + } + + handleAction( + e: React.UIEvent | void, + action: Action, + data: object, + throwErrors: boolean = false, + delegate?: IScopedContext + ) { + const {onAction, store, env, api} = this.props; + + if (api && action.actionType === 'ajax') { + store.setCurrentAction(action); + store + .saveRemote(action.api as string, data, { + successMessage: action.messages && action.messages.success, + errorMessage: action.messages && action.messages.failed + }) + .then(async () => { + if (action.feedback && isVisible(action.feedback, store.data)) { + await this.openFeedback(action.feedback, store.data); + } + + action.redirect && + env.jumpTo(filter(action.redirect, store.data), action); + action.reload && this.reloadTarget(action.reload, store.data); + }) + .catch(() => {}); + } else { + onAction(e, action, data, throwErrors, delegate || this.context); + } + } + renderBody() { const {render, store, body: schema, classnames: cx} = this.props; @@ -190,7 +245,8 @@ export default class Service extends React.Component { { render('body', store.schema || schema, { key: store.schemaKey || 'body', - onQuery: this.handleQuery + onQuery: this.handleQuery, + onAction: this.handleAction }) as JSX.Element } @@ -248,4 +304,9 @@ export class ServiceRenderer extends Service { const scoped = this.context as IScopedContext; scoped.unRegisterComponent(this); } + + reloadTarget(target: string, data?: any) { + const scoped = this.context as IScopedContext; + scoped.reload(target, data); + } }