feat: service 支持 js: 请求,使得动态渲染支持 JavaScript 函数 (#4866)

This commit is contained in:
吴多益 2022-07-29 18:10:07 +08:00 committed by GitHub
parent 78955f4ea9
commit e871f24c61
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 109 additions and 18 deletions

View File

@ -216,6 +216,8 @@ amis 中部分组件,作为展示组件,自身没有**使用接口初始化
它将`data`返回的对象作为 amis 页面配置,进行了解析渲染,实现动态渲染页面的功能。
### jsonp 请求
`schemaApi` 同样支持 `jsonp` 请求,完整用法请参考 amis-admin 项目。
```schema: scope="body"
@ -242,6 +244,44 @@ amis 中部分组件,作为展示组件,自身没有**使用接口初始化
})();
```
### js 请求
> 2.1.0 及以上版本
`schemaApi` 支持 `js` 请求,它会发起一个 xhr 请求去下载 js 文件后执行
```schema: scope="body"
{
"type": "service",
"schemaApi": "js:/api/mock2/service/jsschema"
}
```
这个接口的返回结果期望是一段 JavaScript 代码,和普通 json 返回结果最大的不同是这里可以执行 JavaScript 代码,比如支持 onClick 函数
```javascript
return {
type: 'button',
label: '按钮修改',
onClick: (e, props) => {
alert('消息通知');
}
};
```
这段代码里可以通过 api 变量拿到当前请求的 api 参数,比如 url 地址,可以通过判断进行二次处理
```javascript
console.log(api);
return {
type: 'button',
label: '按钮修改',
onClick: (e, props) => {
alert(api.url);
}
};
```
## 动态渲染表单项
默认 Service 可以通过配置`schemaApi` [动态渲染页面内容](../service#%E5%8A%A8%E6%80%81%E6%B8%B2%E6%9F%93%E9%A1%B5%E9%9D%A2),但是如果想渲染表单项,请返回下面这种格式:

View File

@ -37,48 +37,48 @@
document.write(
`<link rel="stylesheet" title="ang" ${
theme !== 'ang' ? 'disabled' : ''
} href="${__uri('../../scss/themes/ang.scss')}" />`
} href="${__uri('amis/lib/themes/ang.css')}" />`
);
document.write(
`<link rel="stylesheet" title="cxd" ${
theme !== 'cxd' ? 'disabled' : ''
} href="${__uri('../../scss/themes/cxd.scss')}" />`
} href="${__uri('amis/lib/themes/cxd.css')}" />`
);
document.write(
`<link rel="stylesheet" title="dark" ${
theme !== 'dark' ? 'disabled' : ''
} href="${__uri('../../scss/themes/dark.scss')}" />`
} href="${__uri('amis/lib/themes/dark.css')}" />`
);
document.write(
`<link rel="stylesheet" title="antd" ${
theme !== 'antd' ? 'disabled' : ''
} href="${__uri('../../scss/themes/antd.scss')}" />`
} href="${__uri('amis/lib/themes/antd.css')}" />`
);
} else {
document.write(
`<link rel="stylesheet" title="ang" ${
theme !== 'ang' ? 'disabled' : ''
} href="${__uri('../../scss/themes/ang-ie11.scss')}" />`
} href="${__uri('amis/lib/themes/ang-ie11.css')}" />`
);
document.write(
`<link rel="stylesheet" title="cxd" ${
theme !== 'cxd' ? 'disabled' : ''
} href="${__uri('../../scss/themes/cxd-ie11.scss')}" />`
} href="${__uri('amis/lib/themes/cxd-ie11.css')}" />`
);
document.write(
`<link rel="stylesheet" title="dark" ${
theme !== 'dark' ? 'disabled' : ''
} href="${__uri('../../scss/themes/dark-ie11.scss')}" />`
} href="${__uri('amis/lib/themes/dark-ie11.css')}" />`
);
document.write(
`<link rel="stylesheet" title="antd" ${
theme !== 'antd' ? 'disabled' : ''
} href="${__uri('../../scss/themes/antd-ie11.scss')}" />`
} href="${__uri('amis/lib/themes/antd-ie11.css')}" />`
);
}
</script>
<!--ignore-->
<link rel="stylesheet" href="../../scss/helper.scss" />
<link rel="stylesheet" href="amis/lib/helper.css" />
<!--ignore-->
</head>

View File

@ -37,12 +37,13 @@ function mockResponse(event, context, callback) {
body: JSON.stringify(json)
});
},
send(res) {
send(res, headers = {}) {
callback(null, {
statusCode: 200,
headers: {
...createHeaders(event.headers),
'Content-Type': 'text/javascript'
'Content-Type': 'text/javascript',
...headers
},
json: false,
body: res

View File

@ -0,0 +1,14 @@
module.exports = function (req, res) {
return res.send(
`
return {
type: 'button',
label: '按钮修改',
onClick: (e, props) => {
alert('消息通知');
}
}
`,
{'Content-Type': 'text/javascript'}
);
};

View File

@ -98,7 +98,7 @@ export interface RenderOptions
export interface fetcherConfig {
url: string;
method?: 'get' | 'post' | 'put' | 'patch' | 'delete' | 'jsonp';
method?: 'get' | 'post' | 'put' | 'patch' | 'delete' | 'jsonp' | 'js';
data?: any;
config?: any;
}

View File

@ -76,7 +76,7 @@ export interface BaseApiObject {
/**
* API
*/
method?: 'get' | 'post' | 'put' | 'delete' | 'patch' | 'jsonp';
method?: 'get' | 'post' | 'put' | 'delete' | 'patch' | 'jsonp' | 'js';
/**
* API
@ -230,7 +230,7 @@ export interface fetcherResult {
}
export interface fetchOptions {
method?: 'get' | 'post' | 'put' | 'patch' | 'delete' | 'jsonp';
method?: 'get' | 'post' | 'put' | 'patch' | 'delete' | 'jsonp' | 'js';
successMessage?: string;
errorMessage?: string;
autoAppend?: boolean;

View File

@ -19,7 +19,8 @@ import isPlainObject from 'lodash/isPlainObject';
import {debug, warning} from './debug';
import {evaluate, parse} from 'amis-formula';
const rSchema = /(?:^|raw\:)(get|post|put|delete|patch|options|head|jsonp):/i;
const rSchema =
/(?:^|raw\:)(get|post|put|delete|patch|options|head|jsonp|js):/i;
interface ApiCacheConfig extends ApiObject {
cachedPromise: Promise<any>;
@ -178,7 +179,7 @@ export function buildApi(
}
// get 类请求,把 data 附带到 url 上。
if (api.method === 'get' || api.method === 'jsonp') {
if (api.method === 'get' || api.method === 'jsonp' || api.method === 'js') {
if (
!api.data &&
((!~raw.indexOf('$') && autoAppend) || api.forceAppendDataToQuery)
@ -424,6 +425,10 @@ export function wrapFetcher(
return wrapAdaptor(jsonpFetcher(api), api);
}
if (api.method?.toLocaleLowerCase() === 'js') {
return wrapAdaptor(jsFetcher(fn, api), api);
}
if (typeof api.cache === 'number' && api.cache > 0) {
const apiCache = getApiCache(api);
return wrapAdaptor(
@ -469,6 +474,37 @@ export function wrapAdaptor(promise: Promise<fetcherResult>, api: ApiObject) {
: promise.then(ret => responseAdaptor(ret, api));
}
/**
* js new Function schemaApi JavaScript
* @param api
* @returns
*/
export function jsFetcher(
fetcher: (config: fetcherConfig) => Promise<fetcherResult>,
api: ApiObject
): Promise<fetcherResult> {
return new Promise((resolve, reject) => {
// 大概也不会有人用 post
api.method = 'get';
fetcher(api).then((response: fetcherResult) => {
if (typeof response.data === 'string') {
const result = new Function('api', response.data)(api);
resolve({
status: 200,
headers: {},
data: {
status: 0,
msg: '',
data: result
}
});
} else {
reject('must return string: ' + response.data);
}
});
});
}
export function jsonpFetcher(api: ApiObject): Promise<fetcherResult> {
return new Promise((resolve, reject) => {
let script: HTMLScriptElement | null = document.createElement('script');

View File

@ -490,7 +490,7 @@ export interface SchemaApiObject {
/**
* API
*/
method?: 'get' | 'post' | 'put' | 'delete' | 'patch' | 'jsonp';
method?: 'get' | 'post' | 'put' | 'delete' | 'patch' | 'jsonp' | 'js';
/**
* API

View File

@ -33,7 +33,7 @@ export interface fetcherResult {
}
export interface fetchOptions {
method?: 'get' | 'post' | 'put' | 'patch' | 'delete' | 'jsonp';
method?: 'get' | 'post' | 'put' | 'patch' | 'delete' | 'jsonp' | 'js';
successMessage?: string;
errorMessage?: string;
autoAppend?: boolean;