diff --git a/docs/zh-CN/components/service.md b/docs/zh-CN/components/service.md
index bae585d45..b7731aa9a 100755
--- a/docs/zh-CN/components/service.md
+++ b/docs/zh-CN/components/service.md
@@ -10,7 +10,7 @@ order: 63
amis 中部分组件,作为展示组件,自身没有**使用接口初始化数据域的能力**,例如:[Table](./table)、[Cards](./cards)、[List](./list)等,他们需要使用某些配置项,例如`source`,通过[数据映射](../../docs/concepts/data-mapping)功能,在当前的 **数据链** 中获取数据,并进行数据展示。
-而`Service`组件就是专门为该类组件而生,它的功能是::**配置初始化接口,进行数据域的初始化,然后在`Service`内容器中配置子组件,这些子组件通过数据链的方法,获取`Service`所拉取到的数据**
+而`Service`组件就是专门为该类组件而生,它的功能是:**配置初始化接口,进行数据域的初始化,然后在`Service`内容器中配置子组件,这些子组件通过数据链的方法,获取`Service`所拉取到的数据**
## 基本使用
@@ -584,3 +584,18 @@ ws.on('connection', function connection(ws) {
| interval | `number` | | 轮询时间间隔(最低 3000) |
| silentPolling | `boolean` | `false` | 配置轮询时是否显示加载动画 |
| stopAutoRefreshWhen | [表达式](../../docs/concepts/expression) | | 配置停止轮询的条件 |
+
+## 事件表
+
+| 事件名称 | 事件参数 | 说明 |
+| ----------------- | -------------------- | -------------------- |
+| fetchInited | api 初始化数据 | api 初始化完成 |
+| fetchSchemaInited | schemaApi 初始化数据 | schemaApi 初始化完成 |
+
+## 动作表
+
+| 动作名称 | 动作配置 | 说明 |
+| -------- | -------- | ----------------------------------------------- |
+| reload | - | 重新加载,调用 api,刷新数据域数据 |
+| rebuild | - | 重新构建,调用 schemaApi,重新构建容器内 Schema |
+| setValue | - | 更新数据域数据 |
diff --git a/examples/components/EventAction/FormEvent.jsx b/examples/components/EventAction/FormEvent.jsx
index adfce8dff..995e2be62 100644
--- a/examples/components/EventAction/FormEvent.jsx
+++ b/examples/components/EventAction/FormEvent.jsx
@@ -27,7 +27,7 @@ export default {
onEvent: {
click: {
actions: [
- {
+ {
actionType: 'reset',
componentId: 'form-action-receiver'
}
@@ -86,7 +86,7 @@ export default {
{
name: 'form-action-receiver',
id: 'form-action-receiver',
- title: "表单:用于接收上面按钮的动作,派发form本身的事件",
+ title: '表单:用于接收上面按钮的动作,派发form本身的事件',
type: 'form',
debug: true,
api: '/api/mock2/form/saveForm',
@@ -298,7 +298,7 @@ export default {
type: 'form',
debug: true,
api: '/api/mock2/form/saveForm',
- title: "表单:派发formItem的校验事件",
+ title: '表单:派发formItem的校验事件',
data: {
data1: '初始化数据1',
data2: '初始化数据2'
diff --git a/examples/components/EventAction/ServiceEvent.jsx b/examples/components/EventAction/ServiceEvent.jsx
new file mode 100644
index 000000000..e2c9b5876
--- /dev/null
+++ b/examples/components/EventAction/ServiceEvent.jsx
@@ -0,0 +1,227 @@
+export default {
+ type: 'page',
+ title: 'Service功能型容器',
+ regions: ['body', 'toolbar', 'header'],
+ body: [
+ {
+ type: 'tpl',
+ tpl: '事件',
+ inline: false,
+ wrapperComponent: 'h2'
+ },
+ {
+ type: 'tpl',
+ tpl: 'fetchInited
事件',
+ inline: false,
+ wrapperComponent: 'h3'
+ },
+ {
+ type: 'action',
+ level: 'success',
+ label: 'fetchInited',
+ actionType: 'dialog',
+ dialog: {
+ title: 'fetchInited',
+ body: [
+ {
+ type: 'service',
+ name: 'service-api',
+ api: '/api/mock2/page/initData',
+ body: {
+ type: 'panel',
+ title: '$title',
+ body: '现在是:${date}'
+ },
+ onEvent: {
+ fetchInited: {
+ actions: [
+ {
+ actionType: 'toast',
+ msgType: 'success',
+ msg: 'API inited: ${date}'
+ }
+ ]
+ }
+ }
+ }
+ ]
+ }
+ },
+ {
+ type: 'divider'
+ },
+ {
+ type: 'tpl',
+ tpl: 'fetchSchemaInited
事件',
+ inline: false,
+ wrapperComponent: 'h3'
+ },
+ {
+ type: 'action',
+ level: 'info',
+ label: 'fetchSchemaInited',
+ actionType: 'dialog',
+ dialog: {
+ title: 'fetchSchemaInited',
+ body: [
+ {
+ type: 'service',
+ name: 'service-schema-api',
+ schemaApi: '/api/mock2/service/schema?type=form',
+ onEvent: {
+ fetchSchemaInited: {
+ actions: [
+ {
+ actionType: 'toast',
+ msgType: 'info',
+ msg: 'SchemaAPI inited: title: ${title}'
+ }
+ ]
+ }
+ }
+ }
+ ]
+ }
+ },
+ {
+ type: 'divider'
+ },
+ {
+ type: 'tpl',
+ tpl: '动作',
+ inline: false,
+ wrapperComponent: 'h2'
+ },
+ {
+ type: 'tpl',
+ tpl: 'reload
动作',
+ inline: false,
+ wrapperComponent: 'h3'
+ },
+ {
+ type: 'action',
+ label: 'reload触发器',
+ level: 'primary',
+ className: 'mb-3',
+ onEvent: {
+ click: {
+ actions: [
+ {
+ actionType: 'reload',
+ componentId: 'service-reload'
+ }
+ ]
+ }
+ }
+ },
+ {
+ type: 'service',
+ id: 'service-reload',
+ name: 'service-reload',
+ api: '/api/mock2/number/random',
+ body: {
+ type: 'panel',
+ title: 'reload动作',
+ body: '现在是:${random}'
+ }
+ },
+ {
+ type: 'divider'
+ },
+ {
+ type: 'tpl',
+ tpl: 'rebuild
动作',
+ inline: false,
+ wrapperComponent: 'h3'
+ },
+ {
+ type: 'alert',
+ body: '请选择一种构建方式生成组件',
+ level: 'info',
+ showIcon: true,
+ className: 'mb-3',
+ visibleOn: 'this.schemaType == null'
+ },
+ {
+ type: 'button-group',
+ tiled: true,
+ className: 'mb-3',
+ buttons: ['form', 'tabs', 'crud'].map(schema => ({
+ type: 'action',
+ label: `构建${schema}`,
+ icon: 'fa fa-hammer',
+ onEvent: {
+ click: {
+ actions: [
+ {
+ actionType: 'rebuild',
+ componentId: 'service-rebuild',
+ args: {
+ schemaType: `${schema}`
+ }
+ }
+ ]
+ }
+ }
+ }))
+ },
+ {
+ type: 'service',
+ id: 'service-rebuild',
+ name: 'service-rebuild',
+ schemaApi: {
+ url: '/api/mock2/service/schema?type=${schemaType}',
+ method: 'post',
+ sendOn: 'this.schemaType != null'
+ }
+ },
+ {
+ type: 'divider'
+ },
+ {
+ type: 'tpl',
+ tpl: 'setValue
动作',
+ inline: false,
+ wrapperComponent: 'h3'
+ },
+ {
+ type: 'action',
+ label: 'setValue触发器',
+ level: 'primary',
+ className: 'mb-3',
+ onEvent: {
+ click: {
+ actions: [
+ {
+ actionType: 'setValue',
+ componentId: 'service-setvalue',
+ value: {language: ['🇨🇳 中国']}
+ }
+ ]
+ }
+ }
+ },
+ {
+ type: 'service',
+ id: 'service-setvalue',
+ name: 'service-setvalue',
+ data: {
+ language: ['🇺🇸 美国']
+ },
+ body: {
+ type: 'panel',
+ title: 'setValue动作',
+ body: [
+ {
+ type: 'each',
+ name: 'language',
+ items: {
+ type: 'tpl',
+ tpl: "<%= data.item %> "
+ }
+ }
+ ]
+ }
+ }
+ ]
+};
diff --git a/examples/components/Example.jsx b/examples/components/Example.jsx
index 7b78bf946..1628a3d19 100644
--- a/examples/components/Example.jsx
+++ b/examples/components/Example.jsx
@@ -86,6 +86,7 @@ import InputTreeEventSchema from './EventAction/InputTreeEvent';
import treeSelectEventSchema from './EventAction/treeSelectEvent';
import FormEventActionSchema from './EventAction/FormEvent';
import TransferEventSchema from './EventAction/TransferEvent';
+import ServiceEventSchema from './EventAction/ServiceEvent';
import WizardSchema from './Wizard';
import ChartSchema from './Chart';
import EChartsEditorSchema from './ECharts';
@@ -608,6 +609,11 @@ export const examples = [
label: '穿梭框类组件',
path: 'examples/event/transfer',
component: makeSchemaRenderer(TransferEventSchema)
+ },
+ {
+ label: 'Service组件',
+ path: 'examples/event/service',
+ component: makeSchemaRenderer(ServiceEventSchema)
}
]
},
diff --git a/examples/components/Services/Schema.jsx b/examples/components/Services/Schema.jsx
index 0029dc7ca..a22896606 100644
--- a/examples/components/Services/Schema.jsx
+++ b/examples/components/Services/Schema.jsx
@@ -38,7 +38,6 @@ export default {
}
]
},
-
{
name: 'service1',
type: 'service',
diff --git a/mock/cfc/mock/number/random.js b/mock/cfc/mock/number/random.js
new file mode 100644
index 000000000..9ed2f58f5
--- /dev/null
+++ b/mock/cfc/mock/number/random.js
@@ -0,0 +1,18 @@
+module.exports = function (req, res) {
+ const showError = req.query.error;
+
+ if (showError) {
+ return res.json({
+ status: 404,
+ msg: 'Not Found'
+ });
+ } else {
+ return res.json({
+ status: 0,
+ msg: '随机返回一个数字',
+ data: {
+ random: Math.random() * 1000
+ }
+ });
+ }
+};
diff --git a/src/actions/ToastAction.ts b/src/actions/ToastAction.ts
index e7b963daa..a75745e0f 100644
--- a/src/actions/ToastAction.ts
+++ b/src/actions/ToastAction.ts
@@ -5,6 +5,7 @@ import {
ListenerContext,
registerAction
} from './Action';
+import {resolveVariableAndFilter} from '../utils/tpl-builtin';
export interface IToastAction extends ListenerAction {
msg: string;
@@ -31,7 +32,11 @@ export class ToastAction implements Action {
renderer: ListenerContext,
event: RendererEvent
) {
- event.context.env.notify?.(action.msgType || 'info', action.msg, action);
+ event.context.env.notify?.(
+ action.msgType || 'info',
+ resolveVariableAndFilter(action.msg, event?.data, '| raw'),
+ action
+ );
}
}
diff --git a/src/renderers/Service.tsx b/src/renderers/Service.tsx
index 32219927b..6d36b41c7 100644
--- a/src/renderers/Service.tsx
+++ b/src/renderers/Service.tsx
@@ -31,6 +31,9 @@ import {
} from '../Schema';
import {IIRendererStore} from '../store';
+import type {ScopedComponentType} from '../Scoped';
+import type {ListenerAction} from '../actions/Action';
+
/**
* Service 服务类控件。
* 文档:https://baidu.gitee.io/amis/docs/components/service
@@ -209,6 +212,32 @@ export default class Service extends React.Component {
}
}
+ doAction(action: ListenerAction, args: any) {
+ if (action?.actionType === 'rebuild') {
+ const {
+ schemaApi,
+ store,
+ dataProvider,
+ messages: {fetchSuccess, fetchFailed}
+ } = this.props;
+
+ store.updateData(args);
+ clearTimeout(this.timer);
+ if (isEffectiveApi(schemaApi, store.data)) {
+ store
+ .fetchSchema(schemaApi, store.data, {
+ successMessage: fetchSuccess,
+ errorMessage: fetchFailed
+ })
+ .then(this.afterSchemaFetch);
+ }
+
+ if (dataProvider) {
+ this.runDataProvider();
+ }
+ }
+ }
+
@autobind
initFetch() {
const {
@@ -331,7 +360,10 @@ export default class Service extends React.Component {
// 初始化接口返回的是整个 response,
// 保存 ajax 请求的时候返回时数据部分。
const data = result?.hasOwnProperty('ok') ? result.data : result;
- const {onBulkChange} = this.props;
+ const {onBulkChange, dispatchEvent} = this.props;
+
+ dispatchEvent?.('fetchInited', data);
+
if (!isEmpty(data) && onBulkChange) {
onBulkChange(data);
}
@@ -340,7 +372,10 @@ export default class Service extends React.Component {
}
afterSchemaFetch(schema: any) {
- const {onBulkChange, formStore} = this.props;
+ const {onBulkChange, formStore, dispatchEvent} = this.props;
+
+ dispatchEvent?.('fetchSchemaInited', schema);
+
if (formStore && schema?.data && onBulkChange) {
onBulkChange && onBulkChange(schema.data);
}
@@ -586,7 +621,7 @@ export class ServiceRenderer extends Service {
super(props);
const scoped = context;
- scoped.registerComponent(this);
+ scoped.registerComponent(this as ScopedComponentType);
}
reload(subpath?: string, query?: any, ctx?: any, silent?: boolean) {
@@ -613,7 +648,7 @@ export class ServiceRenderer extends Service {
componentWillUnmount() {
super.componentWillUnmount();
const scoped = this.context as IScopedContext;
- scoped.unRegisterComponent(this);
+ scoped.unRegisterComponent(this as ScopedComponentType);
}
reloadTarget(target: string, data?: any) {
@@ -622,6 +657,6 @@ export class ServiceRenderer extends Service {
}
setData(values: object) {
- return super.afterDataFetch(values);
+ return this.props.store.updateData(values);
}
}