mirror of
https://gitee.com/baidu/amis.git
synced 2024-11-29 18:48:45 +08:00
feat: env 增加 replaceText 配置用于 schema 里的文本替换 (#2992)
* feat: env 增加 replaceText 配置用于 schema 里的文本替换 * 优化一下,改成黑名单模式;并且对 key 进行排序避免前缀匹配问题 * 修复编译报错
This commit is contained in:
parent
82006c490e
commit
f936777b64
@ -101,9 +101,42 @@ import 'amis/lib/locale/en-US';
|
||||
}
|
||||
```
|
||||
|
||||
## 全局文本替换
|
||||
|
||||
amis 渲染的 env 参数可以配置全局文本替换,能基于它实现多语言替换功能
|
||||
|
||||
```javascript
|
||||
// sdk 中的写法
|
||||
let amisScoped = amis.embed(
|
||||
'#root',
|
||||
amisJSON,
|
||||
{
|
||||
// 这是 props
|
||||
},
|
||||
{
|
||||
replaceText: {
|
||||
AMIS_HOST: 'https://baidu.gitee.io/amis'
|
||||
}
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
比如下面的例子会对 `AMIS_HOST` 进行替换
|
||||
|
||||
```schema: scope="body"
|
||||
{
|
||||
"type": "tpl",
|
||||
"tpl": "AMIS_HOST"
|
||||
}
|
||||
```
|
||||
|
||||
这个配置会对配置中的绝大部分字段进行替换,除了 `type, name, mode, target, reload` 这几个有特殊功能的字段。
|
||||
|
||||
可以通过配置 `replaceTextIgnoreKeys` 属性来进行修改,让其它字段也避免被替换。
|
||||
|
||||
## JSON 配置中设置多语言
|
||||
|
||||
在 JSON 配置中,也可以设置不同语言下的不同展现,比如前面设置了 `locale` 为 `en-US`,这时在任意 JSON 配置中都能使用 `en-US` 对象来覆盖这个语言下的效果。
|
||||
在 JSON 配置中,也可以设置不同语言下的不同展现,比如前面设置了 `locale` 为 `en-US`,这时在任意 JSON 配置中都能使用 `en-US` 对象来覆盖这个语言下的效果,用于实现简单的替换效果。
|
||||
|
||||
```schema: scope="body"
|
||||
{
|
||||
|
@ -63,12 +63,9 @@ title: 常见问题
|
||||
|
||||
## 如何支持配置中的 URL 地址替换?
|
||||
|
||||
有个常用场景是在开发时使用 `localhost` 地址,而线上使用 `xxx.com`,这时有两个办法:
|
||||
> 1.5.0 及以上版本
|
||||
|
||||
1. 自己做 JSON 替换,这样可以实现更灵活的替换
|
||||
2. 通过外部 data 传入
|
||||
|
||||
这里介绍第二个方法,在渲染 amis 的时候,有第三个参数,可以传递 data,这时就能增加一个域名变量,比如
|
||||
有个常用场景是在开发时使用 `localhost` 地址,而线上使用 `xxx.com`,这时可以使用 `replaceText` 属性,它是第四个参数,也就是 env 参数
|
||||
|
||||
```javascript
|
||||
let amis = amisRequire('amis/embed');
|
||||
@ -76,21 +73,24 @@ let amisJSON = {
|
||||
type: 'page',
|
||||
body: {
|
||||
type: 'service',
|
||||
api: '${HOST|raw}/api'
|
||||
api: 'HOST/api'
|
||||
}
|
||||
};
|
||||
let amisScoped = amis.embed('#root', amisJSON, {
|
||||
data: {
|
||||
HOST: 'http://localhost:3000'
|
||||
let amisScoped = amis.embed(
|
||||
'#root',
|
||||
amisJSON,
|
||||
{},
|
||||
{
|
||||
replaceText: {
|
||||
HOST: 'http://localhost'
|
||||
}
|
||||
}
|
||||
});
|
||||
);
|
||||
```
|
||||
|
||||
这样只需要修改 HOST 变量的值就能控制这个 URL 地址。
|
||||
|
||||
## 如何更新全局 data?
|
||||
|
||||
使用下面的方式
|
||||
使用下面的方式
|
||||
|
||||
```
|
||||
amisScoped.updateProps({
|
||||
|
@ -714,3 +714,43 @@ render 有三个参数,后面会详细说明这三个参数内的属性
|
||||
#### hideValidateFailedDetail: boolean
|
||||
|
||||
Form 表单验证失败时在 notify 消息提示中是否隐藏详细信息,默认展示,设置为 true 时隐藏
|
||||
|
||||
#### replaceText
|
||||
|
||||
> 1.5.0 及以上版本
|
||||
|
||||
可以用来实现变量替换及多语言功能,比如下面的例子
|
||||
|
||||
```javascript
|
||||
let amisScoped = amis.embed(
|
||||
'#root',
|
||||
{
|
||||
type: 'page',
|
||||
body: {
|
||||
type: 'service',
|
||||
api: 'service/api'
|
||||
}
|
||||
},
|
||||
{},
|
||||
{
|
||||
replaceText: {
|
||||
service: 'http://localhost'
|
||||
},
|
||||
replaceTextKeys: ['api']
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
它会替换 `api` 里的 `service` 字符串
|
||||
|
||||
#### replaceTextIgnoreKeys
|
||||
|
||||
> 1.5.0 及以上版本
|
||||
|
||||
和前面的 `replaceText` 配合使用,某些字段会禁用文本替换,默认有以下:
|
||||
|
||||
```
|
||||
type, name, mode, target, reload
|
||||
```
|
||||
|
||||
如果发现有字段被意外替换了,可以通过设置这个属性来避免
|
||||
|
@ -223,6 +223,9 @@ export default class PlayGround extends React.Component {
|
||||
},
|
||||
tracker(eventTrack) {
|
||||
console.debug('eventTrack', eventTrack);
|
||||
},
|
||||
replaceText: {
|
||||
AMIS_HOST: 'https://baidu.gitee.io/amis'
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -5,6 +5,7 @@ import 'core-js/es/array/find-index';
|
||||
import 'core-js/es/string/starts-with';
|
||||
import 'core-js/es/string/ends-with';
|
||||
import 'core-js/es/string/includes';
|
||||
import 'core-js/es/string/replace-all';
|
||||
import 'core-js/es/number/is-nan';
|
||||
import 'core-js/es/promise';
|
||||
import 'core-js/es/object/assign';
|
||||
|
@ -4,18 +4,31 @@
|
||||
|
||||
import {SchemaNode, Schema} from './types';
|
||||
import {RendererProps, RendererConfig, addSchemaFilter} from './factory';
|
||||
import {findObjectsWithKey} from './utils/helper';
|
||||
const isMobile = (window as any).matchMedia?.('(max-width: 768px)').matches
|
||||
? true
|
||||
: false;
|
||||
|
||||
addSchemaFilter(function (schema: Schema, renderer, props?: any) {
|
||||
if (schema && schema.mobile && isMobile) {
|
||||
return {...schema, ...schema.mobile};
|
||||
// 这里不能用 addSchemaFilter 是因为还需要更深层的替换,比如 select 里的 options
|
||||
export const envOverwrite = (schema: any, locale?: string) => {
|
||||
if (schema.mobile && isMobile) {
|
||||
Object.assign(schema, schema.mobile);
|
||||
delete schema.mobile;
|
||||
}
|
||||
|
||||
if (props?.locale && schema[props.locale]) {
|
||||
return {...schema, ...schema[props.locale]};
|
||||
if (locale) {
|
||||
let schemaNodes = findObjectsWithKey(schema, locale);
|
||||
for (let schemaNode of schemaNodes) {
|
||||
Object.assign(schemaNode, schemaNode[locale]);
|
||||
delete schemaNode[locale];
|
||||
}
|
||||
}
|
||||
|
||||
return schema;
|
||||
});
|
||||
if (isMobile) {
|
||||
let schemaNodes = findObjectsWithKey(schema, 'mobile');
|
||||
for (let schemaNode of schemaNodes) {
|
||||
Object.assign(schemaNode, schemaNode['mobile']);
|
||||
delete schemaNode['mobile'];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -3,7 +3,14 @@ import {RendererStore, IRendererStore, IIRendererStore} from './store/index';
|
||||
import {getEnv, destroy} from 'mobx-state-tree';
|
||||
import {wrapFetcher} from './utils/api';
|
||||
import {normalizeLink} from './utils/normalizeLink';
|
||||
import {findIndex, promisify, qsparse, string2regExp} from './utils/helper';
|
||||
import {
|
||||
findIndex,
|
||||
isObject,
|
||||
JSONTraverse,
|
||||
promisify,
|
||||
qsparse,
|
||||
string2regExp
|
||||
} from './utils/helper';
|
||||
import {
|
||||
Api,
|
||||
fetcherResult,
|
||||
@ -25,6 +32,7 @@ import {getDefaultLocale, makeTranslator, LocaleProps} from './locale';
|
||||
import ScopedRootRenderer, {RootRenderProps} from './Root';
|
||||
import {HocStoreFactory} from './WithStore';
|
||||
import {EnvContext, RendererEnv} from './env';
|
||||
import {envOverwrite} from './envOverwrite';
|
||||
|
||||
export interface TestFunc {
|
||||
(
|
||||
@ -127,6 +135,14 @@ export interface RenderOptions {
|
||||
affixOffsetTop?: number;
|
||||
affixOffsetBottom?: number;
|
||||
richTextToken?: string;
|
||||
/**
|
||||
* 替换文本,用于实现 URL 替换、语言替换等
|
||||
*/
|
||||
replaceText?: {[propName: string]: any};
|
||||
/**
|
||||
* 文本替换的黑名单,因为属性太多了所以改成黑名单的 fangs
|
||||
*/
|
||||
replaceTextIgnoreKeys?: String[];
|
||||
[propName: string]: any;
|
||||
}
|
||||
|
||||
@ -335,7 +351,15 @@ const defaultOptions: RenderOptions = {
|
||||
},
|
||||
// 用于跟踪用户在界面中的各种操作
|
||||
tracker(eventTrack: EventTrack, props: PlainObject) {},
|
||||
rendererResolver: resolveRenderer
|
||||
rendererResolver: resolveRenderer,
|
||||
replaceTextIgnoreKeys: [
|
||||
'type',
|
||||
'name',
|
||||
'mode',
|
||||
'target',
|
||||
'reload',
|
||||
'persistData'
|
||||
]
|
||||
};
|
||||
let stores: {
|
||||
[propName: string]: IRendererStore;
|
||||
@ -355,6 +379,9 @@ export function render(
|
||||
const translate = props.translate || makeTranslator(locale);
|
||||
let store = stores[options.session || 'global'];
|
||||
|
||||
// 根据环境覆盖 schema,这个要在最前面做,不然就无法覆盖 validations
|
||||
envOverwrite(schema, locale);
|
||||
|
||||
if (!store) {
|
||||
options = {
|
||||
...defaultOptions,
|
||||
@ -387,6 +414,25 @@ export function render(
|
||||
env.locale = locale;
|
||||
}
|
||||
|
||||
// 进行文本替换
|
||||
if (env.replaceText && isObject(env.replaceText)) {
|
||||
const replaceKeys = Object.keys(env.replaceText);
|
||||
replaceKeys.sort().reverse(); // 避免用户将短的放前面
|
||||
const replaceTextIgnoreKeys = new Set(env.replaceTextIgnoreKeys || []);
|
||||
JSONTraverse(schema, (value: any, key: string, object: any) => {
|
||||
if (typeof value === 'string' && !replaceTextIgnoreKeys.has(key)) {
|
||||
for (const replaceKey of replaceKeys) {
|
||||
if (~value.indexOf(replaceKey)) {
|
||||
object[key] = value.replaceAll(
|
||||
replaceKey,
|
||||
env.replaceText[replaceKey]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<EnvContext.Provider value={env}>
|
||||
<ScopedRootRenderer
|
||||
|
@ -1706,3 +1706,22 @@ export function isClickOnInput(e: React.MouseEvent<HTMLElement>) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 遍历 schema
|
||||
* @param json
|
||||
* @param mapper
|
||||
*/
|
||||
export function JSONTraverse(
|
||||
json: any,
|
||||
mapper: (value: any, key: string | number, host: Object) => any
|
||||
) {
|
||||
Object.keys(json).forEach(key => {
|
||||
const value: any = json[key];
|
||||
if (isPlainObject(value) || Array.isArray(value)) {
|
||||
JSONTraverse(value, mapper);
|
||||
} else {
|
||||
mapper(value, key, json);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -3,7 +3,7 @@
|
||||
"outDir": "output/",
|
||||
"module": "commonjs",
|
||||
"target": "es5",
|
||||
"lib": ["es6", "dom", "ES2015"],
|
||||
"lib": ["es6", "dom", "ES2015", "es2021"],
|
||||
"sourceMap": true,
|
||||
"jsx": "react",
|
||||
"moduleResolution": "node",
|
||||
|
@ -3,7 +3,7 @@
|
||||
"outDir": "output/",
|
||||
"module": "commonjs",
|
||||
"target": "es5",
|
||||
"lib": ["es6", "dom", "ES2015"],
|
||||
"lib": ["es6", "dom", "ES2015", "es2021"],
|
||||
"sourceMap": true,
|
||||
"jsx": "react",
|
||||
"moduleResolution": "node",
|
||||
|
Loading…
Reference in New Issue
Block a user