import React from 'react'; import {Renderer, RendererEnv, RendererProps} from '../factory'; import {ServiceStore, IServiceStore} from '../store/service'; import {Api, SchemaNode, PlainObject, Payload} from '../types'; import {filter} from '../utils/tpl'; import cx from 'classnames'; import { BaseSchema, SchemaApi, SchemaTokenizeableString, SchemaTpl } from '../Schema'; import {withStore} from '../components/WithStore'; import {flow, Instance, types} from 'mobx-state-tree'; import {getVariable, guid, isObject} from '../utils/helper'; import {StoreNode} from '../store/node'; import isPlainObject from 'lodash/isPlainObject'; import {isPureVariable, resolveVariableAndFilter} from '../utils/tpl-builtin'; import { buildApi, isApiOutdated, isEffectiveApi, normalizeApi } from '../utils/api'; /** * Mapping 映射展示控件。 * 文档:https://baidu.gitee.io/amis/docs/components/mapping */ export interface MappingSchema extends BaseSchema { /** * 指定为映射展示控件 */ type: 'map' | 'mapping'; /** * 关联字段名。 */ name?: string; /** * 配置映射规则,值可以使用模板语法。当 key 为 * 时表示 else,也就是说值没有映射到任何规则时用 * 对应的值展示。 */ map?: { [propName: string]: SchemaTpl; }; /** * 如果想远程拉取字典,请配置 source 为接口。 */ source?: SchemaApi | SchemaTokenizeableString; /** * 占位符 */ placeholder?: string; } export const Store = StoreNode.named('MappingStore') .props({ fetching: false, errorMsg: '', map: types.frozen<{ [propName: string]: any; }>({}) }) .actions(self => { const load: (env: RendererEnv, api: Api, data: any) => Promise = flow( function* (env, api, data) { try { self.fetching = true; const ret: Payload = yield env.fetcher(api, data); if (ret.ok) { const data = ret.data || {}; (self as any).setMap(data); } else { throw new Error(ret.msg || 'fetch error'); } } catch (e) { self.errorMsg = e.message; } finally { self.fetching = false; } } ); return { load, setMap(options: any) { if (isObject(options)) { self.map = { ...options }; } } }; }); export type IStore = Instance; export interface MappingProps extends Omit, Omit { store: IStore; } export const MappingField = withStore(props => Store.create( { id: guid(), storeType: Store.name }, props.env ) )( class extends React.Component { static defaultProps: Partial = { placeholder: '-', map: { '*': '通配值' } }; constructor(props: MappingProps) { super(props); props.store.syncProps(props, undefined, ['map']); } componentDidMount() { const {store, source, data} = this.props; this.reload(); } componentDidUpdate(prevProps: MappingProps) { const props = this.props; const {store, source, data} = this.props; store.syncProps(props, prevProps, ['map']); if (isPureVariable(source)) { const prev = resolveVariableAndFilter( prevProps.source as string, prevProps.data, '| raw' ); const curr = resolveVariableAndFilter(source as string, data, '| raw'); if (prev !== curr) { store.setMap(curr); } } else if ( isApiOutdated( prevProps.source, props.source, prevProps.data, props.data ) ) { this.reload(); } } reload() { const {source, data, env} = this.props; const store = this.props.store; if (isPureVariable(source)) { store.setMap(resolveVariableAndFilter(source, data, '| raw')); } else if (isEffectiveApi(source, data)) { const api = normalizeApi(source, 'get'); api.cache = api.cache ?? 30 * 1000; store.load(env, api, data); } } render() { const { className, placeholder, render, classnames: cx, name, data, store } = this.props; const map = store.map; let key = this.props.value ?? (name ? getVariable(data, name) : undefined); let viewValue: React.ReactNode = ( {placeholder} ); key = typeof key === 'string' ? key.trim() : key; // trim 一下,干掉一些空白字符。 let value: any = undefined; if ( typeof key !== 'undefined' && map && (value = map[key] ?? (key === true && map['1'] ? map['1'] : key === false && map['0'] ? map['0'] : map['*'])) !== undefined ) { viewValue = render('tpl', value); } return {viewValue}; } } ); @Renderer({ test: /(^|\/)(?:map|mapping)$/, name: 'mapping' }) export class MappingFieldRenderer extends React.Component { render() { return ; } }