30 KiB
Executable File
title | description | type | group | menuName | icon | order |
---|---|---|---|---|---|---|
Service 功能型容器 | 0 | ⚙ 组件 | Service | 63 |
amis 中部分组件,作为展示组件,自身没有使用接口初始化数据域的能力,例如:Table、Cards、List等,他们需要使用某些配置项,例如source
,通过数据映射功能,在当前的 数据链 中获取数据,并进行数据展示。
而Service
组件就是专门为该类组件而生,它的功能是:配置初始化接口,进行数据域的初始化,然后在Service
内容器中配置子组件,这些子组件通过数据链的方法,获取Service
所拉取到的数据
基本使用
最基本的使用,是配置初始化接口api
,将接口返回的数据添加到自身的数据域中,以供子组件通过数据链进行获取使用。
{
"type": "service",
"api": "/api/mock2/page/initData",
"body": {
"type": "panel",
"title": "$title",
"body": "现在是:${date}"
}
}
你可以通过查看网络面板看到,service
接口返回的数据结构为:
{
"status": 0,
"msg": "",
"data": {
"title": "Test Page Component",
"date": "2017-10-13"
}
}
在service
的子组件中,就可以使用${title}
和${date}
展示数据
展示列表
另外一种使用较为频繁的场景是:serivce + table 进行列表渲染
{
"type": "service",
"api": "/api/mock2/crud/table?perPage=5",
"body": [
{
"type": "table",
"title": "表格1",
"source": "$rows",
"columns": [
{
"name": "engine",
"label": "Engine"
},
{
"name": "version",
"label": "Version"
}
]
},
{
"type": "table",
"source": "$rows",
"columns": [
{
"name": "engine",
"label": "Engine"
},
{
"name": "version",
"label": "Version"
}
]
}
]
}
上例中 service 接口返回数据结构如下:
{
"status": 0,
"msg": "ok",
"data": {
"count": 57,
"rows": [
{
"engine": "Trident",
"browser": "Internet Explorer 4.0",
"platform": "Win 95+",
"version": "4",
"grade": "X",
"id": 1
},
{
"engine": "Trident",
"browser": "Internet Explorer 5.0",
"platform": "Win 95+",
"version": "5",
"grade": "C",
"id": 2
},
{
"engine": "Trident",
"browser": "Internet Explorer 5.5",
"platform": "Win 95+",
"version": "5.5",
"grade": "A",
"id": 3
},
{
"engine": "Trident",
"browser": "Internet Explorer 6",
"platform": "Win 98+",
"version": "6",
"grade": "A",
"id": 4
},
{
"engine": "Trident",
"browser": "Internet Explorer 7",
"platform": "Win XP SP2+",
"version": "7",
"grade": "A",
"id": 5
},
{
"engine": "Trident",
"browser": "AOL browser (AOL desktop)",
"platform": "Win XP",
"version": "6",
"grade": "A",
"id": 6
},
{
"engine": "Gecko",
"browser": "Firefox 1.0",
"platform": "Win 98+ / OSX.2+",
"version": "1.7",
"grade": "A",
"id": 7
},
{
"engine": "Gecko",
"browser": "Firefox 1.5",
"platform": "Win 98+ / OSX.2+",
"version": "1.8",
"grade": "A",
"id": 8
},
{
"engine": "Gecko",
"browser": "Firefox 2.0",
"platform": "Win 98+ / OSX.2+",
"version": "1.8",
"grade": "A",
"id": 9
},
{
"engine": "Gecko",
"browser": "Firefox 3.0",
"platform": "Win 2k+ / OSX.3+",
"version": "1.9",
"grade": "A",
"id": 10
}
]
}
}
table
中配置source
属性为${rows}
就可以获取到rows
变量的数据,并用于展示。
动态渲染页面
Service
还有个重要的功能就是支持配置 schemaApi
,通过它可以实现动态渲染页面内容。
{
"type": "service",
"schemaApi": "/api/mock2/service/schema?type=tabs"
}
同样观察schemaApi接口
返回的数据结构:
{
"status": 0,
"msg": "",
"data": {
"type": "tabs",
"tabs": [
{
"title": "TabA",
"body": "卡片A内容"
},
{
"title": "TabB",
"body": "卡片B内容"
}
]
}
}
它将data
返回的对象作为 amis 页面配置,进行了解析渲染,实现动态渲染页面的功能。
jsonp 请求
schemaApi
同样支持 jsonp
请求,完整用法请参考 amis-admin 项目。
{
"type": "service",
"schemaApi": "jsonp:/api/mock2/service/jsonp"
}
schemaApi接口
返回的内容其实是一段立即执行的 js 代码。我们可以通过 callback
参数执行函数名,或者通过 request._callback
获取
(function () {
window.axiosJsonpCallbackxxxx &&
window.axiosJsonpCallbackxxxx({
status: 0,
msg: '',
data: {
type: 'page',
title: 'jsonp 示例',
body: 'this is tpl from jsonp'
}
});
})();
js 请求
2.1.0 及以上版本
schemaApi
支持 js
请求,它会发起一个 xhr 请求去下载 js 文件后执行
{
"type": "service",
"schemaApi": "js:/api/mock2/service/jsschema"
}
这个接口的返回结果期望是一段 JavaScript 代码,和普通 json 返回结果最大的不同是这里可以执行 JavaScript 代码,比如支持 onClick 函数
return {
type: 'button',
label: '按钮修改',
onClick: (e, props) => {
alert('消息通知');
}
};
这段代码里可以通过 api 变量拿到当前请求的 api 参数,比如 url 地址,可以通过判断进行二次处理
console.log(api);
return {
type: 'button',
label: '按钮修改',
onClick: (e, props) => {
alert(api.url);
}
};
动态渲染表单项
默认 Service 可以通过配置schemaApi
动态渲染页面内容,但是如果想渲染表单项,请返回下面这种格式:
{
"status": 0,
"msg": "",
"data": {
"type": "container",
"body": [
{
"type": "input-text",
"name": "text",
"label": "文本输入"
}
]
}
}
例如下例:
{
"type": "service",
"schemaApi": "/api/mock2/service/schema?type=controls"
}
schemaApi
除了能返回表单项之外,还能同时返回表单数据,如果你这样返回接口
{
"status": 0,
"msg": "",
"data": {
"data": {
"a": "b" // 这样返回的选项会选中第二个选项B
},
"body": [
{
"type": "select",
"name": "a",
"label": "选项",
"options": [
{"label": "A", "value": "a"},
{"label": "B", "value": "b"}
]
}
]
}
}
接口联动
api
和schemaApi
都支持接口联动
{
"title": "",
"type": "form",
"api": "/api/mock/saveForm?waitSeconds=1",
"mode": "horizontal",
"body": [
{
"label": "数据模板",
"type": "select",
"name": "tpl",
"value": "tpl1",
"size": "sm",
"options": [
{
"label": "模板1",
"value": "tpl1"
},
{
"label": "模板2",
"value": "tpl2"
},
{
"label": "模板3",
"value": "tpl3"
}
],
"description": "<span class=\"text-danger\">请修改该下拉选择器查看效果</span>"
},
{
"type": "service",
"api": "/api/mock2/form/initData?tpl=${tpl}",
"body": [
{
"label": "名称",
"type": "input-text",
"name": "name"
},
{
"label": "作者",
"type": "input-text",
"name": "author"
},
{
"label": "请求时间",
"type": "input-datetime",
"name": "date"
}
]
}
],
"actions": []
}
上例可看到,变更数据模板的值,会触发 service 重新请求,并更新当前数据域中的数据
更多相关见接口联动
定时轮询刷新
设置 interval
可以定时刷新 api
和 schemaApi
接口,单位是毫秒,如"interval": 2000
则设置轮询间隔为 2s ,注意最小间隔时间是 1 秒。支持通过stopAutoRefreshWhen
表达式定义轮询停止条件。
{
"type": "service",
"api": "/api/mock2/number/random?waitSeconds=1",
"interval": 2000,
"stopAutoRefreshWhen": "this.random === 6",
"body": {
"type": "panel",
"title": "随机数字",
"body": "现在是:${random}"
}
}
静默轮询
设置silentPolling: true
可以关闭等待接口加载时的 loading 动画,该配置仅在配置interval
时生效。
{
"type": "service",
"api": "/api/mock2/number/random?waitSeconds=1",
"interval": 2000,
"silentPolling": true,
"stopAutoRefreshWhen": "this.random === 6",
"body": {
"type": "panel",
"title": "随机数字",
"body": "现在是:${random}"
}
}
通过 WebSocket 实时获取数据
Service 支持通过 WebSocket 获取数据,只需要设置 ws(由于无示例服务,所以无法在线演示)。
{
"type": "service",
"ws": "ws://localhost:8777",
"body": {
"type": "panel",
"title": "$title",
"body": "随机数:${random}"
}
}
1.4.0 及以上版本
或者是对象的方式支持配置初始 data
,这个 data 会在建立连接时发送初始数据
{
"type": "service",
"ws": {
"url": "ws://localhost:8777?name=${name}",
"data": {
"name": "${name}"
}
},
"body": {
"label": "名称",
"type": "static",
"name": "name"
}
}
可以只设置 ws,通过 ws 来获取所有数据,也可以同时设置 api 和 ws,让 api 用于获取全部数据,而 ws 用于获取实时更新的数据。
后端实现示例,基于 ws:
const WebSocket = require('ws');
const ws = new WebSocket.Server({port: 8777});
ws.on('connection', function connection(ws) {
setInterval(() => {
const random = Math.floor(Math.random() * Math.floor(100));
// 返回给 amis 的数据
const data = {
random
};
// 发送前需要转成字符串
ws.send(JSON.stringify(data));
}, 500);
});
WebSocket 客户端的默认实现是使用标准 WebSocket,如果后端使用定制的 WebSocket,比如 socket.io,可以通过覆盖 env.wsFetcher
来自己实现数据获取方法,默认实现是:
1.4.0 及以上版本修改了 ws 类型,将之前的字符串改成了对象的方式,会有两个参数 url 和 body
下面是目前 amis 中 WebSocket 支持的默认实现:
wsFetcher(ws, onMessage, onError) {
if (ws) {
const socket = new WebSocket(ws.url);
socket.onopen = event => {
if (ws.body) {
socket.send(JSON.stringify(ws.body));
}
};
socket.onmessage = event => {
if (event.data) {
let data;
try {
data = JSON.parse(event.data);
} catch (error) {}
if (typeof data !== 'object') {
let key = ws.responseKey || 'data';
data = {
[key]: event.data
};
}
onMessage(data);
}
};
socket.onerror = onError;
return {
close: socket.close
};
} else {
return {
close: () => {}
};
}
}
通过 onMessage 来通知 amis 数据修改了,并返回 close 函数来关闭连接。
1.8.0 及以上版本
如果 WebSocket 返回的结果不是 JSON 而只是某个字符串,需要配置 responseKey
属性来将这个结果放在这个 key 上,比如下面的例子
{
"type": "service",
"ws": {
"url": "ws://localhost:8777?name=${name}",
"data": {
"name": "${name}"
},
"responseKey": "name"
},
"body": {
"label": "名称",
"type": "static",
"name": "name"
}
}
对应的后端就只需要返回字符串
const WebSocket = require('ws');
const ws = new WebSocket.Server({port: 8777});
ws.on('connection', function connection(ws) {
setInterval(() => {
const random = Math.floor(Math.random() * Math.floor(100));
ws.send(random);
}, 500);
});
调用外部函数获取数据
1.4.0 及以上版本
对于更复杂的数据获取情况,可以使用 dataProvider
属性来实现外部函数获取数据,它支持字符串和函数两种形式
{
"type": "service",
"dataProvider": "setData({ now: new Date().toString() })",
"body": {
"type": "tpl",
"tpl": "现在是:${now}"
}
}
函数将会传递两个参数:data
和 setData
,其中 data
可以拿到上下文数据,而 setData
函数可以用来更新数据,比如下面的例子
{
"type": "service",
"dataProvider": "const timer = setInterval(() => { setData({date: new Date().toString()}) }, 1000); return () => { clearInterval(timer) }",
"body": {
"type": "tpl",
"tpl": "现在是:${date}"
}
}
上面这个例子还返回了一个函数,这个函数会在组件销毁的时候执行,可以用来清理资源。
下面是使用函数类型的示例,注意这个示例不能放在 JSON 中,只能在 jssdk 或 react 项目里使用。
{
"type": "service",
"dataProvider": (data, setData) => {
const timer = setInterval(() => {
setData({date: new Date().toString()})
}, 1000);
return () => { clearInterval(timer) }
},
"body": {
"type": "tpl",
"tpl": "现在是:${now}"
}
}
1.8.0 及以上版本
新增了一个 env
属性,可以调用系统环境中的方法,比如 env.fetcher、tracker 等,比如下面的例子会调用 env.notify
来弹出提示
{
"type": "service",
"dataProvider": "env.notify('info', 'msg')"
}
函数触发事件
2.3.0 及以上版本
{
"type": "service",
"api": "/api/mock2/page/initData",
"dataProvider": {
"inited": "setData({ addedNumber: data.number + 1 })",
"onApiFetched": "setData({ year: new Date(data.date).getFullYear(), })"
},
"data": {
"number": 8887
},
"body": {
"type": "panel",
"title": "$title",
"body": [
{
"type": "tpl",
"wrapperComponent": "p",
"tpl": "静态数字为:<strong>${addedNumber}</strong>"
},
{
"type": "tpl",
"wrapperComponent": "p",
"tpl": "接口返回值的日期为:<strong>${date}</strong>"
},
{
"type": "tpl",
"wrapperComponent": "p",
"tpl": "接口返回值的年份为:<strong>${year}</strong>"
},
]
}
}
隐藏错误信息
2.8.1 及以上版本
默认会将接口返回的错误信息展示在 Service 的顶部区域,可以通过设置"showErrorMsg": false
隐藏错误提示。
{
"type": "service",
"api": "/api/mock2/page/initDataError",
"body": [
{
"type": "tpl",
"tpl": "展示错误信息"
},
{
"type": "icon",
"icon": "fa-solid fa-arrow-up"
}
]
}
设置"showErrorMsg": false
隐藏错误提示,仅保留 toast 提示
{
"type": "service",
"api": "/api/mock2/page/initDataError",
"showErrorMsg": false,
"body": [
{
"type": "tpl",
"tpl": "不展示错误信息"
}
]
}
属性表
属性名 | 类型 | 默认值 | 说明 | 版本 |
---|---|---|---|---|
type | string |
"service" |
指定为 service 渲染器 | |
className | string |
外层 Dom 的类名 | ||
body | SchemaNode | 内容容器 | ||
api | API | 初始化数据域接口地址 | ||
ws | string |
WebScocket 地址 | ||
dataProvider | string | Record<"inited" | "onApiFetched" | "onSchemaApiFetched" | "onWsFetched", string> |
数据获取函数 |
|
|
initFetch | boolean |
是否默认拉取 | ||
schemaApi | API | 用来获取远程 Schema 接口地址 | ||
initFetchSchema | boolean |
是否默认拉取 Schema | ||
messages | Object |
消息提示覆写,默认消息读取的是接口返回的 toast 提示文字,但是在此可以覆写它。 | ||
messages.fetchSuccess | string |
接口请求成功时的 toast 提示文字 | ||
messages.fetchFailed | string |
"初始化失败" |
接口请求失败时 toast 提示文字 | |
interval | number |
轮询时间间隔,单位 ms(最低 1000) | ||
silentPolling | boolean |
false |
配置轮询时是否显示加载动画 | |
stopAutoRefreshWhen | 表达式 | 配置停止轮询的条件 | ||
showErrorMsg | boolean |
true |
是否以 Alert 的形式显示 api 接口响应的错误信息,默认展示 | 2.8.1 |
事件表
当前组件会对外派发以下事件,可以通过onEvent
来监听这些事件,并通过actions
来配置执行的动作,在actions
中可以通过${事件参数名}
或${event.data.[事件参数名]}
来获取事件产生的数据,详细请查看事件动作。
[name]
为当前数据域中的字段名,例如:当前数据域为 {username: 'amis'},则可以通过${username}获取对应的值。
事件名称 | 事件参数 | 说明 |
---|---|---|
init | - | 组件实例被创建并插入 DOM 中时触发。2.4.1 及以上版本 |
fetchInited | responseData: any 请求的响应数据responseStatus: number 响应状态,0 表示成功responseMsg: string 响应消息, error 表示接口是否成功[name]: any 当前数据域中指定字段的值 |
api 接口请求完成时触发 |
fetchSchemaInited | responseData: any 请求的响应数据responseStatus: number 响应状态,0 表示成功responseMsg: string 响应消息, error 表示接口是否成功[name]: any 当前数据域中指定字段的值 |
schemaApi 接口请求完成时触发 |
init
开始初始化。
{
"type": "service",
"api": "/api/mock2/page/initData",
"body": {
"type": "panel",
"title": "$title",
"body": "现在是:${date}"
},
"onEvent": {
"init": {
"actions": [
{
"actionType": "toast",
"args": {
"msg": "init"
}
}
]
}
}
}
fetchInited
api 接口请求完成。
{
"type": "service",
"api": "/api/mock2/page/initData",
"body": [
{
"type": "panel",
"title": "$title",
"body": "现在是:${date}"
}
],
"onEvent": {
"fetchInited": {
"actions": [
{
"actionType": "toast",
"args": {
"msg": "title:${event.data.responseData.title},date:${date},status:${event.data.responseStatus}"
}
}
]
}
}
}
fetchSchemaInited
schemaApi 接口请求完成。
[
{
"type": "service",
"schemaApi": "/api/mock2/service/schema?type=tabs",
"onEvent": {
"fetchSchemaInited": {
"actions": [
{
"actionType": "toast",
"args": {
"msg": "type:${event.data.responseData.type},status:${event.data.responseStatus}"
}
}
]
}
}
}
]
动作表
当前组件对外暴露以下特性动作,其他组件可以通过指定actionType: 动作名称
、componentId: 该组件id
来触发这些动作,详细请查看事件动作。
动作名称 | 动作配置 | 说明 |
---|---|---|
reload | - | 重新加载,调用 api ,刷新数据域数据 |
rebuild | - | 重新构建,调用 schemaApi ,重新构建容器内 Schema |
setValue | - | 更新数据域数据 |
reload
只做刷新
重新发送api
请求,刷新 Page 时,只配置componentId
目标组件 ID 即可。
[
{
"type": "button",
"label": "刷新请求",
"onEvent": {
"click": {
"actions": [
{
"componentId": "service-reload",
"actionType": "reload"
}
]
}
}
},
{
"type": "service",
"id": "service-reload",
"name": "service-reload",
"api": "/api/mock2/number/random",
"body": "现在是:${random}"
}
]
发送数据并刷新
刷新 Service 组件时,如果配置了data
,将发送data
给目标组件,并将该数据合并到目标组件的数据域中(如果配置"dataMergeMode": "override"
将覆盖目标组件的数据),然后重新请求数据。
[
{
"type": "button",
"label": "刷新请求",
"onEvent": {
"click": {
"actions": [
{
"componentId": "service-reload",
"actionType": "reload",
"data": {
"date": "${NOW()}"
}
}
]
}
}
},
{
"type": "service",
"id": "service-reload",
"name": "service-reload",
"api": "/api/mock2/number/random",
"body": "现在是:${random}, 当前时间:${date}"
}
]
rebuild
重新构建,基于 args 传参和 schemaApi 绑定变量,让 service 获取不同的 schema。
[
{
"type": "alert",
"body": "请选择一种构建方式生成组件",
"level": "info",
"showIcon": true,
"className": "mb-3",
"visibleOn": "this.schemaType == null"
},
{
"type": "button-group",
"tiled": true,
"className": "mb-3",
"buttons": [
{
"type": "action",
"label": "构建form",
"icon": "fa fa-hammer",
"onEvent": {
"click": {
"actions": [
{
"actionType": "rebuild",
"componentId": "service-rebuild",
"args": {
"schemaType": "form"
}
}
]
}
}
},
{
"type": "action",
"label": "构建tabs",
"icon": "fa fa-hammer",
"onEvent": {
"click": {
"actions": [
{
"actionType": "rebuild",
"componentId": "service-rebuild",
"args": {
"schemaType": "tabs"
}
}
]
}
}
},
{
"type": "action",
"label": "构建crud",
"icon": "fa fa-hammer",
"onEvent": {
"click": {
"actions": [
{
"actionType": "rebuild",
"componentId": "service-rebuild",
"args": {
"schemaType": "crud"
}
}
]
}
}
}
]
},
{
"type": "service",
"id": "service-rebuild",
"name": "service-rebuild",
"schemaApi": {
"url": "/api/mock2/service/schema?type=${schemaType}",
"method": "post",
"sendOn": "this.schemaType != null"
}
}
]
setValue
通过setValue
更新指定 Service 的数据。
合并数据
默认setValue
会将新数据与目标组件数据进行合并。
[
{
"type": "button",
"label": "更新数据",
"onEvent": {
"click": {
"actions": [
{
"actionType": "setValue",
"componentId": "service-setvalue",
"args": {
"value": {
"name": "aisuda",
"email": "aisuda@baidu.com"
}
}
}
]
}
}
},
{
"type": "service",
"id": "service-setvalue",
"name": "service-setvalue",
"data": {
"name": "amis",
"email": "amis@baidu.com"
},
"body": [
{
"type": "tpl",
"tpl": "名字:${name|default:'-'},邮箱:${email|default:'-'}"
}
]
}
]
覆盖数据
可以通过"dataMergeMode": "override"
来覆盖目标组件数据。
[
{
"type": "button",
"label": "更新数据",
"onEvent": {
"click": {
"actions": [
{
"actionType": "setValue",
"componentId": "service-setvalue",
"args": {
"value": {
"name": "aisuda"
}
},
"dataMergeMode": "override"
}
]
}
}
},
{
"type": "service",
"id": "service-setvalue",
"name": "service-setvalue",
"data": {
"name": "amis",
"email": "amis@baidu.com"
},
"body": [
{
"type": "tpl",
"tpl": "名字:${name|default:'-'},邮箱:${email|default:'-'}"
}
]
}
]