mirror of
https://gitee.com/baidu/amis.git
synced 2024-11-30 11:07:52 +08:00
补充搜索功能
This commit is contained in:
parent
1442357975
commit
2f22d7621b
@ -72,3 +72,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.#{$ns}TransferControl {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
@ -35,7 +35,7 @@ export interface TransferPorps extends ThemeProps, CheckboxesProps {
|
|||||||
onSearch?: (
|
onSearch?: (
|
||||||
term: string,
|
term: string,
|
||||||
setCancel: (cancel: () => void) => void
|
setCancel: (cancel: () => void) => void
|
||||||
) => Promise<Options>;
|
) => Promise<Options | void>;
|
||||||
|
|
||||||
// 自定义选择框相关
|
// 自定义选择框相关
|
||||||
selectRender?: (props: TransferPorps) => JSX.Element;
|
selectRender?: (props: TransferPorps) => JSX.Element;
|
||||||
@ -216,6 +216,7 @@ export class Transfer extends React.Component<TransferPorps, TransferState> {
|
|||||||
renderSearchResult() {
|
renderSearchResult() {
|
||||||
const {
|
const {
|
||||||
searchResultMode,
|
searchResultMode,
|
||||||
|
selectMode,
|
||||||
noResultsText,
|
noResultsText,
|
||||||
searchResultColumns,
|
searchResultColumns,
|
||||||
classnames: cx,
|
classnames: cx,
|
||||||
@ -224,8 +225,9 @@ export class Transfer extends React.Component<TransferPorps, TransferState> {
|
|||||||
option2value
|
option2value
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const options = this.state.searchResult || [];
|
const options = this.state.searchResult || [];
|
||||||
|
const mode = searchResultMode || selectMode;
|
||||||
|
|
||||||
return searchResultMode === 'table' ? (
|
return mode === 'table' ? (
|
||||||
<TableCheckboxes
|
<TableCheckboxes
|
||||||
placeholder={noResultsText}
|
placeholder={noResultsText}
|
||||||
className={cx('Transfer-checkboxes')}
|
className={cx('Transfer-checkboxes')}
|
||||||
@ -235,7 +237,7 @@ export class Transfer extends React.Component<TransferPorps, TransferState> {
|
|||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
option2value={option2value}
|
option2value={option2value}
|
||||||
/>
|
/>
|
||||||
) : searchResultMode === 'tree' ? (
|
) : mode === 'tree' ? (
|
||||||
<TreeCheckboxes
|
<TreeCheckboxes
|
||||||
placeholder={noResultsText}
|
placeholder={noResultsText}
|
||||||
className={cx('Transfer-checkboxes')}
|
className={cx('Transfer-checkboxes')}
|
||||||
|
@ -45,7 +45,7 @@ export interface FormItemProps extends RendererProps {
|
|||||||
formHorizontal: FormHorizontal;
|
formHorizontal: FormHorizontal;
|
||||||
defaultSize?: 'xs' | 'sm' | 'md' | 'lg' | 'full';
|
defaultSize?: 'xs' | 'sm' | 'md' | 'lg' | 'full';
|
||||||
size?: 'xs' | 'sm' | 'md' | 'lg' | 'full';
|
size?: 'xs' | 'sm' | 'md' | 'lg' | 'full';
|
||||||
disabled: boolean;
|
disabled?: boolean;
|
||||||
btnDisabled: boolean;
|
btnDisabled: boolean;
|
||||||
defaultValue: any;
|
defaultValue: any;
|
||||||
value: any;
|
value: any;
|
||||||
|
@ -6,7 +6,13 @@ import Checkbox from '../../components/Checkbox';
|
|||||||
import PopOver from '../../components/PopOver';
|
import PopOver from '../../components/PopOver';
|
||||||
import {RootCloseWrapper} from 'react-overlays';
|
import {RootCloseWrapper} from 'react-overlays';
|
||||||
import {Icon} from '../../components/icons';
|
import {Icon} from '../../components/icons';
|
||||||
import {autobind, flattenTree, isEmpty, filterTree} from '../../utils/helper';
|
import {
|
||||||
|
autobind,
|
||||||
|
flattenTree,
|
||||||
|
isEmpty,
|
||||||
|
filterTree,
|
||||||
|
string2regExp
|
||||||
|
} from '../../utils/helper';
|
||||||
import {dataMapping} from '../../utils/tpl-builtin';
|
import {dataMapping} from '../../utils/tpl-builtin';
|
||||||
import {OptionsControl, OptionsControlProps} from '../Form/Options';
|
import {OptionsControl, OptionsControlProps} from '../Form/Options';
|
||||||
import {Option, Options} from '../../components/Select';
|
import {Option, Options} from '../../components/Select';
|
||||||
@ -357,7 +363,7 @@ export default class NestedSelectControl extends React.Component<
|
|||||||
const inputValue = evt.currentTarget.value;
|
const inputValue = evt.currentTarget.value;
|
||||||
const {options, labelField, valueField} = this.props;
|
const {options, labelField, valueField} = this.props;
|
||||||
|
|
||||||
const regexp = new RegExp(`${inputValue}`, 'i');
|
const regexp = string2regExp(inputValue);
|
||||||
|
|
||||||
let filtedOptions =
|
let filtedOptions =
|
||||||
inputValue && this.state.isOpened
|
inputValue && this.state.isOpened
|
||||||
|
@ -2,13 +2,22 @@ import {OptionsControlProps, OptionsControl} from './Options';
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Transfer from '../../components/Transfer';
|
import Transfer from '../../components/Transfer';
|
||||||
import {Option} from './Options';
|
import {Option} from './Options';
|
||||||
import {autobind} from '../../utils/helper';
|
import {
|
||||||
|
autobind,
|
||||||
|
filterTree,
|
||||||
|
string2regExp,
|
||||||
|
createObject
|
||||||
|
} from '../../utils/helper';
|
||||||
import {Api} from '../../types';
|
import {Api} from '../../types';
|
||||||
|
import Spinner from '../../components/Spinner';
|
||||||
|
import find from 'lodash/find';
|
||||||
|
import {optionValueCompare} from '../../components/Select';
|
||||||
|
|
||||||
export interface TransferProps extends OptionsControlProps {
|
export interface TransferProps extends OptionsControlProps {
|
||||||
sortable?: boolean;
|
sortable?: boolean;
|
||||||
selectMode?: 'table' | 'list' | 'tree';
|
selectMode?: 'table' | 'list' | 'tree';
|
||||||
columns?: Array<any>;
|
columns?: Array<any>;
|
||||||
|
searchable?: boolean;
|
||||||
searchApi?: Api; // todo 通过传递进去 onSearch 实现。
|
searchApi?: Api; // todo 通过传递进去 onSearch 实现。
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -23,20 +32,38 @@ export class TransferRenderer extends React.Component<TransferProps> {
|
|||||||
joinValues,
|
joinValues,
|
||||||
delimiter,
|
delimiter,
|
||||||
valueField,
|
valueField,
|
||||||
extractValue
|
extractValue,
|
||||||
|
options,
|
||||||
|
setOptions
|
||||||
} = this.props;
|
} = this.props;
|
||||||
let newValue: any = value;
|
let newValue: any = value;
|
||||||
|
let newOptions = options.concat();
|
||||||
|
|
||||||
if (Array.isArray(value)) {
|
if (Array.isArray(value)) {
|
||||||
|
if (joinValues || extractValue) {
|
||||||
|
newValue = value.map(item => {
|
||||||
|
const resolved = find(
|
||||||
|
options,
|
||||||
|
optionValueCompare(
|
||||||
|
item[valueField || 'value'],
|
||||||
|
valueField || 'value'
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!resolved) {
|
||||||
|
newOptions.push(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
return item[valueField || 'value'];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (joinValues) {
|
if (joinValues) {
|
||||||
newValue = value
|
newValue = newValue.join(delimiter || ',');
|
||||||
.map(item => item[valueField || 'value'])
|
|
||||||
.join(delimiter || ',');
|
|
||||||
} else if (extractValue) {
|
|
||||||
newValue = value.map(item => item[valueField || 'value']);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
newOptions.length > options.length && setOptions(newOptions);
|
||||||
onChange(newValue);
|
onChange(newValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,6 +72,56 @@ export class TransferRenderer extends React.Component<TransferProps> {
|
|||||||
return option;
|
return option;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@autobind
|
||||||
|
async handleSearch(term: string) {
|
||||||
|
const {searchApi, options, labelField, valueField, env, data} = this.props;
|
||||||
|
|
||||||
|
if (searchApi) {
|
||||||
|
const payload = await env.fetcher(searchApi, createObject(data, {term}));
|
||||||
|
|
||||||
|
if (!payload.ok) {
|
||||||
|
env.notify('error', payload.msg || '搜索请求异常');
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = payload.data.options || payload.data.items || payload.data;
|
||||||
|
if (!Array.isArray(result)) {
|
||||||
|
env.notify('error', '期望接口返回数组信息');
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.map(item => {
|
||||||
|
let resolved: any = null;
|
||||||
|
|
||||||
|
if (Array.isArray(options)) {
|
||||||
|
resolved = find(
|
||||||
|
options,
|
||||||
|
optionValueCompare(item[valueField || 'value'], valueField)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return resolved || item;
|
||||||
|
});
|
||||||
|
} else if (term) {
|
||||||
|
const regexp = string2regExp(term);
|
||||||
|
|
||||||
|
return filterTree(
|
||||||
|
options,
|
||||||
|
(option: Option) => {
|
||||||
|
return !!(
|
||||||
|
(Array.isArray(option.children) && option.children.length) ||
|
||||||
|
regexp.test(option[labelField || 'label']) ||
|
||||||
|
regexp.test(option[valueField || 'value'])
|
||||||
|
);
|
||||||
|
},
|
||||||
|
0,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
classnames: cx,
|
classnames: cx,
|
||||||
@ -52,7 +129,10 @@ export class TransferRenderer extends React.Component<TransferProps> {
|
|||||||
selectedOptions,
|
selectedOptions,
|
||||||
sortable,
|
sortable,
|
||||||
selectMode,
|
selectMode,
|
||||||
columns
|
columns,
|
||||||
|
loading,
|
||||||
|
searchable,
|
||||||
|
searchApi
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -65,7 +145,10 @@ export class TransferRenderer extends React.Component<TransferProps> {
|
|||||||
sortable={sortable}
|
sortable={sortable}
|
||||||
selectMode={selectMode}
|
selectMode={selectMode}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
|
onSearch={searchable ? this.handleSearch : undefined}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<Spinner overlay key="info" show={loading} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -251,9 +251,10 @@ export function anyChanged(
|
|||||||
to: {[propName: string]: any},
|
to: {[propName: string]: any},
|
||||||
strictMode: boolean = true
|
strictMode: boolean = true
|
||||||
): boolean {
|
): boolean {
|
||||||
return (typeof attrs === 'string' ? attrs.split(/\s*,\s*/) : attrs).some(
|
return (typeof attrs === 'string'
|
||||||
key => (strictMode ? from[key] !== to[key] : from[key] != to[key])
|
? attrs.split(/\s*,\s*/)
|
||||||
);
|
: attrs
|
||||||
|
).some(key => (strictMode ? from[key] !== to[key] : from[key] != to[key]));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function rmUndefined(obj: PlainObject) {
|
export function rmUndefined(obj: PlainObject) {
|
||||||
@ -362,7 +363,7 @@ export function makeColumnClassBuild(
|
|||||||
let count = 12;
|
let count = 12;
|
||||||
let step = Math.floor(count / steps);
|
let step = Math.floor(count / steps);
|
||||||
|
|
||||||
return function(schema: Schema) {
|
return function (schema: Schema) {
|
||||||
if (
|
if (
|
||||||
schema.columnClassName &&
|
schema.columnClassName &&
|
||||||
/\bcol-(?:xs|sm|md|lg)-(\d+)\b/.test(schema.columnClassName)
|
/\bcol-(?:xs|sm|md|lg)-(\d+)\b/.test(schema.columnClassName)
|
||||||
@ -470,7 +471,7 @@ export function promisify<T extends Function>(
|
|||||||
) => Promise<any> & {
|
) => Promise<any> & {
|
||||||
raw: T;
|
raw: T;
|
||||||
} {
|
} {
|
||||||
let promisified = function() {
|
let promisified = function () {
|
||||||
try {
|
try {
|
||||||
const ret = fn.apply(null, arguments);
|
const ret = fn.apply(null, arguments);
|
||||||
if (ret && ret.then) {
|
if (ret && ret.then) {
|
||||||
@ -1029,6 +1030,17 @@ export function getLevelFromClassName(
|
|||||||
return defaultValue;
|
return defaultValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function string2regExp(value: string, caseSensitive = false) {
|
||||||
|
if (typeof value !== 'string') {
|
||||||
|
throw new TypeError('Expected a string');
|
||||||
|
}
|
||||||
|
|
||||||
|
return new RegExp(
|
||||||
|
value.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&').replace(/-/g, '\\x2d'),
|
||||||
|
!caseSensitive ? 'i' : ''
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export function pickEventsProps(props: any) {
|
export function pickEventsProps(props: any) {
|
||||||
const ret: any = {};
|
const ret: any = {};
|
||||||
props &&
|
props &&
|
||||||
@ -1040,7 +1052,7 @@ export function pickEventsProps(props: any) {
|
|||||||
|
|
||||||
export const autobind = boundMethod;
|
export const autobind = boundMethod;
|
||||||
|
|
||||||
export const bulkBindFunctions = function<
|
export const bulkBindFunctions = function <
|
||||||
T extends {
|
T extends {
|
||||||
[propName: string]: any;
|
[propName: string]: any;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user