diff --git a/docs/zh-CN/concepts/linkage.md b/docs/zh-CN/concepts/linkage.md index bac557daa..1baa43b80 100755 --- a/docs/zh-CN/concepts/linkage.md +++ b/docs/zh-CN/concepts/linkage.md @@ -485,3 +485,160 @@ amis 会将返回的 `data` 写入表单数据域,因此下面的 `static` 组 上例中点击按钮会刷新`target1`和`target2`组件。 事实上,**组件间联动也可以实现上述任意的 [基本联动效果](./linkage#%E5%9F%BA%E6%9C%AC%E8%81%94%E5%8A%A8)(显隐联动、接口联动等其他联动)。** + +### 动态目标 + +> 2.9.0 及以上版本 + +刷新目标支持表达式,比如目标可以配置成 `form-${ xxx ? '1' : '2'}`。 + +```schema: scope="body" +[ + { + "title": "查询条件", + "type": "form", + "target": "my_crud_${searchTarget}", + "body": [ + { + "type": "radios", + "name": "searchTarget", + "label": "选择目标", + value: 1, + options: [ + { + value: 1, + label: "列表 1" + }, + { + value: 2, + label: "列表 2" + } + ], + }, + { + "type": "input-text", + "name": "keywords", + "label": "关键字:" + } + ], + "submitText": "搜索" + }, + { + "type": "crud", + "api": "/api/mock2/sample", + "name": "my_crud_1", + "title": "列表 1", + "columns": [ + { + "name": "id", + "label": "ID" + }, + { + "name": "engine", + "label": "Rendering engine" + }, + { + "name": "browser", + "label": "Browser" + }, + { + "name": "platform", + "label": "Platform(s)" + }, + { + "name": "version", + "label": "Engine version" + } + ] + }, + + { + "type": "crud", + "api": "/api/mock2/sample", + "name": "my_crud_2", + "title": "列表 2", + "columns": [ + { + "name": "id", + "label": "ID" + }, + { + "name": "engine", + "label": "Rendering engine" + }, + { + "name": "browser", + "label": "Browser" + }, + { + "name": "platform", + "label": "Platform(s)" + }, + { + "name": "version", + "label": "Engine version" + } + ] + } +] +``` + +如果目标组件在列表中,则实际渲染的时候会存在多份,通过某个固定名字没办法找到对应的组件。比如某个 crud 里面,某列设置的是一个 service,通过 service 拉取数据。如果想在操作栏里面某个操作完后刷新对应的 service,通过固定的名字是没办法找到对应的 service 的。所以名字 `name` 也支持动态名字。如: `my-service-${id}` 把行数据中动态的 id 设置进去。 + +```schema: scope="body" +{ + type: 'crud', + api: "/api/mock2/sample", + columns: [ + { + name: 'id', + label: 'ID' + }, + + { + type: 'service', + api: "/api/mock2/sample/${id}", + label: 'Service', + name: "my-servce-${id}", + body: [ + { + type: "tpl", + tpl: "${browser}" + } + ] + }, + + { + type: 'operation', + label: '操作', + buttons: [ + { + type: "button", + label: "编辑", + actionType: "dialog", + dialog: { + "title": "编辑", + + body: [ + { + type: 'form', + api: "/api/mock2/sample/${id}", + body: [ + { + type: 'input-text', + name: 'browser', + label: 'Browser' + } + ] + } + ] + }, + reload: "my-servce-${id}" + } + ] + } + ] +} +``` + +> 这个例子 api 是 mock 的,所以修改后刷新没效果。 diff --git a/packages/amis-core/src/RootRenderer.tsx b/packages/amis-core/src/RootRenderer.tsx index bbb365d8e..d38b30b94 100644 --- a/packages/amis-core/src/RootRenderer.tsx +++ b/packages/amis-core/src/RootRenderer.tsx @@ -201,7 +201,7 @@ export class RootRenderer extends React.Component { action.reload && this.reloadTarget( delegate || this.context, - action.reload, + filter(action.reload, ctx), store.data ); }) diff --git a/packages/amis-core/src/Scoped.tsx b/packages/amis-core/src/Scoped.tsx index 389532373..de29c2630 100644 --- a/packages/amis-core/src/Scoped.tsx +++ b/packages/amis-core/src/Scoped.tsx @@ -21,6 +21,7 @@ import { } from './utils/helper'; import {RendererData, ActionObject} from './types'; import {isPureVariable} from './utils/isPureVariable'; +import {filter} from './utils'; export interface ScopedComponentType extends React.Component { focus?: () => void; @@ -36,11 +37,7 @@ export interface ScopedComponentType extends React.Component { ctx?: RendererData ) => void; context: any; - setData?: ( - value?: object, - replace?: boolean, - index?: number - ) => void; + setData?: (value?: object, replace?: boolean, index?: number) => void; } export interface IScopedContext { @@ -109,7 +106,8 @@ function createScopedTools( const resolved = find( components, component => - component.props.name === name || component.props.id === name + filter(component.props.name, component.props.data) === name || + component.props.id === name ); return resolved || (parent && parent.getComponentByName(name)); }, diff --git a/packages/amis-core/src/renderers/Form.tsx b/packages/amis-core/src/renderers/Form.tsx index 1e8ec055b..00a0cbf03 100644 --- a/packages/amis-core/src/renderers/Form.tsx +++ b/packages/amis-core/src/renderers/Form.tsx @@ -1091,10 +1091,11 @@ export default class Form extends React.Component { dispatchEvent('validateSucc', this.props.data); if (target) { - this.submitToTarget(target, values); + this.submitToTarget(filter(target, values), values); dispatchEvent('submitSucc', createObject(this.props.data, values)); } else if (action.actionType === 'reload') { - action.target && this.reloadTarget(action.target, values); + action.target && + this.reloadTarget(filter(action.target, values), values); } else if (action.actionType === 'dialog') { store.openDialog(data); } else if (action.actionType === 'drawer') { @@ -1193,7 +1194,10 @@ export default class Form extends React.Component { ); finalRedirect && env.jumpTo(finalRedirect, action); } else if (action.reload || reload) { - this.reloadTarget(action.reload || reload!, store.data); + this.reloadTarget( + filter(action.reload || reload!, store.data), + store.data + ); } action.close && this.closeTarget(action.close); @@ -1256,7 +1260,8 @@ export default class Form extends React.Component { action.redirect && filter(action.redirect, store.data); redirect && env.jumpTo(redirect, action); - action.reload && this.reloadTarget(action.reload, store.data); + action.reload && + this.reloadTarget(filter(action.reload, store.data), store.data); action.close && this.closeTarget(action.close); }) .catch(e => { @@ -1268,7 +1273,7 @@ export default class Form extends React.Component { } else if (action.actionType === 'reload') { store.setCurrentAction(action); if (action.target) { - this.reloadTarget(action.target, data); + this.reloadTarget(filter(action.target, data), data); } else { this.receive(data); } diff --git a/packages/amis/src/renderers/CRUD.tsx b/packages/amis/src/renderers/CRUD.tsx index 024176a95..c3ac6f73f 100644 --- a/packages/amis/src/renderers/CRUD.tsx +++ b/packages/amis/src/renderers/CRUD.tsx @@ -680,7 +680,7 @@ export default class CRUD extends React.Component { const redirect = action.redirect && filter(action.redirect, data); redirect && !action.blank && env.jumpTo(redirect, action); action.reload - ? this.reloadTarget(action.reload, data) + ? this.reloadTarget(filter(action.reload, data), data) : redirect ? null : this.search(undefined, undefined, true, true); @@ -779,7 +779,7 @@ export default class CRUD extends React.Component { } action.reload - ? this.reloadTarget(action.reload, data) + ? this.reloadTarget(filter(action.reload, data), data) : this.search( {[pageField || 'page']: 1}, undefined, @@ -1038,7 +1038,7 @@ export default class CRUD extends React.Component { const reload = action.reload ?? dialogAction.reload; if (reload) { - this.reloadTarget(reload, ctx); + this.reloadTarget(filter(reload, ctx), ctx); } let redirect = action.redirect ?? dialogAction.redirect; @@ -1279,7 +1279,7 @@ export default class CRUD extends React.Component { .then(() => { const finalReload = options?.reload ?? reload; finalReload - ? this.reloadTarget(finalReload, data) + ? this.reloadTarget(filter(finalReload, data), data) : this.search(undefined, undefined, true, true); }) .catch(() => {}); @@ -1301,7 +1301,7 @@ export default class CRUD extends React.Component { .then(() => { const finalReload = options?.reload ?? reload; finalReload - ? this.reloadTarget(finalReload, data) + ? this.reloadTarget(filter(finalReload, data), data) : this.search(undefined, undefined, true, true); }) .catch(() => { @@ -1406,7 +1406,7 @@ export default class CRUD extends React.Component { store .saveRemote(saveOrderApi, model) .then(() => { - reload && this.reloadTarget(reload, model); + reload && this.reloadTarget(filter(reload, model), model); this.search(undefined, undefined, true, true); }) .catch(() => {}); diff --git a/packages/amis/src/renderers/CRUD2.tsx b/packages/amis/src/renderers/CRUD2.tsx index 9d7016f0b..7f1d91e4d 100644 --- a/packages/amis/src/renderers/CRUD2.tsx +++ b/packages/amis/src/renderers/CRUD2.tsx @@ -658,7 +658,7 @@ export default class CRUD2 extends React.Component { errorMessage: messages && messages.saveSuccess }) .then(() => { - reload && this.reloadTarget(reload, data); + reload && this.reloadTarget(filter(reload, data), data); this.getData(undefined, undefined, true, true); }) .catch(() => {}); @@ -678,7 +678,7 @@ export default class CRUD2 extends React.Component { store .saveRemote(quickSaveItemApi, sendData) .then(() => { - reload && this.reloadTarget(reload, data); + reload && this.reloadTarget(filter(reload, data), data); this.getData(undefined, undefined, true, true); }) .catch(() => { @@ -784,7 +784,7 @@ export default class CRUD2 extends React.Component { store .saveRemote(saveOrderApi, model) .then(() => { - reload && this.reloadTarget(reload, model); + reload && this.reloadTarget(filter(reload, model), model); this.getData(undefined, undefined, true, true); }) .catch(() => {}); diff --git a/packages/amis/src/renderers/Dialog.tsx b/packages/amis/src/renderers/Dialog.tsx index f232c8ac9..2f030e743 100644 --- a/packages/amis/src/renderers/Dialog.tsx +++ b/packages/amis/src/renderers/Dialog.tsx @@ -891,7 +891,8 @@ export class DialogRenderer extends Dialog { const reidrect = action.redirect && filter(action.redirect, store.data); reidrect && env.jumpTo(reidrect, action); - action.reload && this.reloadTarget(action.reload, store.data); + action.reload && + this.reloadTarget(filter(action.reload, store.data), store.data); if (action.close) { this.handleSelfClose(); this.closeTarget(action.close); diff --git a/packages/amis/src/renderers/Drawer.tsx b/packages/amis/src/renderers/Drawer.tsx index bfa1788d1..27669fe63 100644 --- a/packages/amis/src/renderers/Drawer.tsx +++ b/packages/amis/src/renderers/Drawer.tsx @@ -852,7 +852,8 @@ export class DrawerRenderer extends Drawer { const redirect = action.redirect && filter(action.redirect, store.data); redirect && env.jumpTo(redirect, action); - action.reload && this.reloadTarget(action.reload, store.data); + action.reload && + this.reloadTarget(filter(action.reload, store.data), store.data); if (action.close) { this.handleSelfClose(); this.closeTarget(action.close); diff --git a/packages/amis/src/renderers/Page.tsx b/packages/amis/src/renderers/Page.tsx index 694b1ec90..938511014 100644 --- a/packages/amis/src/renderers/Page.tsx +++ b/packages/amis/src/renderers/Page.tsx @@ -508,7 +508,8 @@ export default class Page extends React.Component { const redirect = action.redirect && filter(action.redirect, store.data); redirect && env.jumpTo(redirect, action); - action.reload && this.reloadTarget(action.reload, store.data); + action.reload && + this.reloadTarget(filter(action.reload, store.data), store.data); }) .catch(e => { if (throwErrors || action.countDown) { diff --git a/packages/amis/src/renderers/Service.tsx b/packages/amis/src/renderers/Service.tsx index 2ef4b6d5b..cde130fe0 100644 --- a/packages/amis/src/renderers/Service.tsx +++ b/packages/amis/src/renderers/Service.tsx @@ -47,7 +47,7 @@ export const eventTypes = [ 'onWsFetched' ] as const; -export type ProviderEventType = typeof eventTypes[number]; +export type ProviderEventType = (typeof eventTypes)[number]; export type DataProviderCollection = Partial< Record @@ -684,7 +684,8 @@ export default class Service extends React.Component { const redirect = action.redirect && filter(action.redirect, store.data); redirect && env.jumpTo(redirect, action); - action.reload && this.reloadTarget(action.reload, store.data); + action.reload && + this.reloadTarget(filter(action.reload, store.data), store.data); }) .catch(e => { if (throwErrors || action.countDown) { diff --git a/packages/amis/src/renderers/Table2/index.tsx b/packages/amis/src/renderers/Table2/index.tsx index fbfee62e3..8f44ba44e 100644 --- a/packages/amis/src/renderers/Table2/index.tsx +++ b/packages/amis/src/renderers/Table2/index.tsx @@ -910,7 +910,7 @@ export default class Table2 extends React.Component { errorMessage: messages && messages.saveSuccess }) .then(() => { - reload && this.reloadTarget(reload, data); + reload && this.reloadTarget(filter(reload, data), data); }) .catch(() => {}); } else { @@ -929,7 +929,7 @@ export default class Table2 extends React.Component { store .saveRemote(quickSaveItemApi, sendData) .then(() => { - reload && this.reloadTarget(reload, data); + reload && this.reloadTarget(filter(reload, data), data); }) .catch(() => { options?.resetOnFailed && this.reset(); diff --git a/packages/amis/src/renderers/Wizard.tsx b/packages/amis/src/renderers/Wizard.tsx index aa0fb8db4..08ab94569 100644 --- a/packages/amis/src/renderers/Wizard.tsx +++ b/packages/amis/src/renderers/Wizard.tsx @@ -600,11 +600,12 @@ export default class Wizard extends React.Component { action.redirect && filter(action.redirect, store.data); reidrect && env.jumpTo(reidrect, action); - action.reload && this.reloadTarget(action.reload, store.data); + action.reload && + this.reloadTarget(filter(action.reload, store.data), store.data); }) .catch(reason => {}); } else if (action.actionType === 'reload') { - action.target && this.reloadTarget(action.target, data); + action.target && this.reloadTarget(filter(action.target, data), data); } else if (action.actionType === 'goto-step') { const targetStep = (data as any).step; @@ -708,7 +709,7 @@ export default class Wizard extends React.Component { // 最后一步 if (target) { - this.submitToTarget(target, store.data); + this.submitToTarget(filter(target, store.data), store.data); this.setState({completeStep: steps.length}); } else if (action.api || step.api || api) { let finnalAsyncApi = action.asyncApi || step.asyncApi || asyncApi; @@ -797,7 +798,7 @@ export default class Wizard extends React.Component { env.jumpTo(finalRedirect, action); } else if (action.reload || step.reload || reload) { this.reloadTarget( - action.reload || step.reload || reload!, + filter(action.reload || step.reload || reload!, store.data), store.data ); }