Merge branch 'baidu:master' into master

This commit is contained in:
zhou999 2021-12-08 20:05:25 +08:00 committed by GitHub
commit ba8ef2a8bc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
158 changed files with 6285 additions and 2209 deletions

View File

@ -38,21 +38,29 @@ exports[`factory unregistered Renderer 1`] = `
<div
class="cxd-Alert cxd-Alert--danger"
>
<p>
Error: 找不到对应的渲染器
</p>
<p>
Path:
my-renderer
</p>
<pre>
<code>
{
<div
class="cxd-Alert-content"
>
<div
class="cxd-Alert-desc"
>
<p>
Error: 找不到对应的渲染器
</p>
<p>
Path:
my-renderer
</p>
<pre>
<code>
{
"type": "my-renderer",
"a": 23
}
</code>
</pre>
</code>
</pre>
</div>
</div>
</div>
</div>
`;

View File

@ -33,43 +33,63 @@ exports[`Renderer:fieldSet 1`] = `
type="submit"
/>
<fieldset
class="cxd-Collapse cxd-Collapse--collapsable no-border"
class="cxd-Collapse is-active no-border"
>
<legend
class="cxd-Collapse-header bg-dark"
>
<span
class="cxd-Collapse-arrow"
/>
<span
class="cxd-TplField"
>
<span>
<span
class="text-muted"
/>
</span>
</span>
</legend>
<div
class="cxd-Collapse-contentWrapper"
>
<div
class="cxd-Collapse-content cxd-Collapse-body bg-white"
class="cxd-Collapse-body bg-white"
>
<div
class="cxd-Form--horizontal"
class="cxd-Collapse-content"
>
<div
class="cxd-Form-item cxd-Form-item--horizontal"
data-role="form-item"
class="cxd-Form--horizontal"
>
<label
class="cxd-Form-label cxd-Form-itemColumn--normal"
>
<span />
</label>
<div
class="cxd-Form-value"
class="cxd-Form-item cxd-Form-item--horizontal"
data-role="form-item"
>
<label
class="cxd-Form-label cxd-Form-itemColumn--normal"
>
<span />
</label>
<div
class="cxd-Form-control cxd-TextControl"
class="cxd-Form-value"
>
<div
class="cxd-TextControl-input"
class="cxd-Form-control cxd-TextControl"
>
<input
autocomplete="off"
name="text"
placeholder=""
size="10"
type="text"
value=""
/>
<div
class="cxd-TextControl-input"
>
<input
autocomplete="off"
name="text"
placeholder=""
size="10"
type="text"
value=""
/>
</div>
</div>
</div>
</div>

View File

@ -17,13 +17,21 @@ exports[`Renderer:alert 1`] = `
<div
class="cxd-Alert cxd-Alert--success"
>
<span
class="cxd-TplField"
<div
class="cxd-Alert-content"
>
<span>
提示内容
</span>
</span>
<div
class="cxd-Alert-desc"
>
<span
class="cxd-TplField"
>
<span>
提示内容
</span>
</span>
</div>
</div>
</div>
</div>
</div>

View File

@ -92,7 +92,7 @@ exports[`Renderer:Page classNames 1`] = `
style={Object {}}
>
<div
className="cxd-Page-aside aside-class-name"
className="cxd-Page-aside cxd-Page-aside--withWidth aside-class-name"
>
<div
className="cxd-TplField cxd-Page-asideTplWrapper"
@ -1110,6 +1110,15 @@ exports[`Renderer:Page initApi error show Message 1`] = `
<div
className="cxd-Alert cxd-Alert--danger"
>
<div
className="cxd-Alert-content"
>
<div
className="cxd-Alert-desc"
>
Internal Error
</div>
</div>
<button
className="cxd-Alert-close"
onClick={[Function]}
@ -1120,7 +1129,6 @@ exports[`Renderer:Page initApi error show Message 1`] = `
icon="close"
/>
</button>
Internal Error
</div>
</div>
</div>

View File

@ -68,7 +68,7 @@ const filters = [
},
path: '$$',
filter: '| raw',
expectValue: undefined
expectValue: {a: 1}
},
{
type: 'raw7',
@ -87,8 +87,8 @@ const filters = [
b: 'b'
}
},
path: '${value}',
filter: '| json:0',
path: '${value| json:0}',
filter: '',
expectValue: '{"a":"a","b":"b"}'
},
{
@ -96,8 +96,8 @@ const filters = [
data: {
value: '{"a":"a","b":"b"}'
},
path: '${value}',
filter: '| toJson',
path: '${value| toJson}',
filter: '',
expectValue: {
a: 'a',
b: 'b'
@ -108,8 +108,8 @@ const filters = [
data: {
value: 1559649981
},
path: '${value}',
filter: '| date',
path: '${value| date}',
filter: '',
expectValue: moment(1559649981, 'X').format('LLL')
},
{
@ -117,8 +117,8 @@ const filters = [
data: {
value: 9999
},
path: '${value}',
filter: '| number',
path: '${value| number}',
filter: '',
expectValue: '9,999'
},
{
@ -126,8 +126,8 @@ const filters = [
data: {
value: ' abc '
},
path: '${value}',
filter: '| trim',
path: '${value| trim}',
filter: '',
expectValue: 'abc'
},
{
@ -135,8 +135,8 @@ const filters = [
data: {
value: 0.8232343
},
path: '${value}',
filter: '| percent',
path: '${value| percent}',
filter: '',
expectValue: '82%'
},
// duration
@ -145,8 +145,8 @@ const filters = [
data: {
value: 1
},
path: '${value}',
filter: '| duration',
path: '${value| duration}',
filter: '',
expectValue: '1秒'
},
{
@ -154,8 +154,8 @@ const filters = [
data: {
value: 61
},
path: '${value}',
filter: '| duration',
path: '${value| duration}',
filter: '',
expectValue: '1分1秒'
},
{
@ -163,8 +163,8 @@ const filters = [
data: {
value: 233233
},
path: '${value}',
filter: '| duration',
path: '${value| duration}',
filter: '',
expectValue: '2天16时47分13秒'
},
// bytes
@ -173,8 +173,8 @@ const filters = [
data: {
value: 1024
},
path: '${value}',
filter: '| bytes',
path: '${value| bytes}',
filter: '',
expectValue: '1.02 KB'
},
{
@ -182,8 +182,8 @@ const filters = [
data: {
value: 1024000
},
path: '${value}',
filter: '| bytes',
path: '${value| bytes}',
filter: '',
expectValue: '1.02 MB'
},
{
@ -191,8 +191,8 @@ const filters = [
data: {
value: -1024
},
path: '${value}',
filter: '| bytes',
path: '${value| bytes}',
filter: '',
expectValue: '-1.02 KB'
},
{
@ -200,8 +200,8 @@ const filters = [
data: {
value: 0.5
},
path: '${value}',
filter: '| bytes',
path: '${value| bytes}',
filter: '',
expectValue: '0.5 B'
},
// round
@ -210,8 +210,8 @@ const filters = [
data: {
value: '啥啊'
},
path: '${value}',
filter: '| round',
path: '${value| round}',
filter: '',
expectValue: 0
},
{
@ -219,8 +219,8 @@ const filters = [
data: {
value: 1.22
},
path: '${value}',
filter: '| round:1',
path: '${value| round:1}',
filter: '',
expectValue: '1.2'
},
{
@ -228,8 +228,8 @@ const filters = [
data: {
value: 1.26
},
path: '${value}',
filter: '| round:1',
path: '${value| round:1}',
filter: '',
expectValue: '1.3'
},
{
@ -237,8 +237,8 @@ const filters = [
data: {
value: 'this is a very loooooong sentence.'
},
path: '${value}',
filter: '| truncate:10',
path: '${value| truncate:10}',
filter: '',
expectValue: 'this is a ...'
},
{
@ -246,8 +246,8 @@ const filters = [
data: {
value: 'this is a very loooooong sentence.'
},
path: '${value}',
filter: '| truncate:null',
path: '${value| truncate:null}',
filter: '',
expectValue: 'this is a very loooooong sentence.'
},
{
@ -255,8 +255,8 @@ const filters = [
data: {
value: 'http://www.baidu.com?query=123'
},
path: '${value}',
filter: '| url_encode',
path: '${value| url_encode}',
filter: '',
expectValue: 'http%3A%2F%2Fwww.baidu.com%3Fquery%3D123'
},
{
@ -264,8 +264,8 @@ const filters = [
data: {
value: 'http%3A%2F%2Fwww.baidu.com%3Fquery%3D123'
},
path: '${value}',
filter: '| url_decode:10',
path: '${value| url_decode:10}',
filter: '',
expectValue: 'http://www.baidu.com?query=123'
},
{
@ -273,8 +273,8 @@ const filters = [
data: {
value: ''
},
path: '${value}',
filter: '| default',
path: '${value| default}',
filter: '',
expectValue: undefined
},
{
@ -282,8 +282,8 @@ const filters = [
data: {
value: ''
},
path: '${value}',
filter: '| default:-',
path: '${value| default:-}',
filter: '',
expectValue: '-'
},
{
@ -291,8 +291,8 @@ const filters = [
data: {
value: ['a', 'b', 'c']
},
path: '${value}',
filter: '| join:,',
path: '${value| join:,}',
filter: '',
expectValue: 'a,b,c'
},
{
@ -300,8 +300,8 @@ const filters = [
data: {
value: 'a,b,c'
},
path: '${value}',
filter: '| split',
path: '${value| split}',
filter: '',
expectValue: ['a', 'b', 'c']
},
{
@ -309,8 +309,8 @@ const filters = [
data: {
value: ['a', 'b', 'c']
},
path: '${value}',
filter: '| first',
path: '${value| first}',
filter: '',
expectValue: 'a'
},
{
@ -318,8 +318,8 @@ const filters = [
data: {
value: ['a', 'b', 'c']
},
path: '${value}',
filter: '| nth:1',
path: '${value| nth:1}',
filter: '',
expectValue: 'b'
},
{
@ -327,8 +327,8 @@ const filters = [
data: {
value: ['a', 'b', 'c']
},
path: '${value}',
filter: '| last',
path: '${value| last}',
filter: '',
expectValue: 'c'
},
{
@ -336,8 +336,8 @@ const filters = [
data: {
value: 5
},
path: '${value}',
filter: '| minus:1',
path: '${value| minus:1}',
filter: '',
expectValue: 4
},
{
@ -345,8 +345,8 @@ const filters = [
data: {
value: 5
},
path: '${value}',
filter: '| plus:1',
path: '${value| plus:1}',
filter: '',
expectValue: 6
},
{
@ -357,8 +357,8 @@ const filters = [
b: '2'
}
},
path: '${value}',
filter: '| pick:a',
path: '${value| pick:a}',
filter: '',
expectValue: '1'
},
{
@ -379,8 +379,8 @@ const filters = [
}
]
},
path: '${value}',
filter: '| pick:value',
path: '${value| pick:value}',
filter: '',
expectValue: ['a', 'b', 'c']
},
{
@ -401,8 +401,8 @@ const filters = [
}
]
},
path: '${value}',
filter: '| pick_if_exist:$value',
path: '${value| pick_if_exist:value}',
filter: '',
expectValue: ['a', 'b', 'c']
},
{
@ -410,8 +410,8 @@ const filters = [
data: {
value: '1559649981'
},
path: '${value}',
filter: '| str2date:X:YYYY-MM-DD HH-mm-ss',
path: '${value| str2date:X:YYYY-MM-DD HH-mm-ss}',
filter: '',
expectValue: moment('1559649981', 'X').format('YYYY-MM-DD HH-mm-ss')
},
{
@ -419,8 +419,8 @@ const filters = [
data: {
value: 'a'
},
path: '${value}',
filter: '| asArray',
path: '${value| asArray}',
filter: '',
expectValue: ['a']
},
{
@ -428,8 +428,8 @@ const filters = [
data: {
value: 'I love amis'
},
path: '${value}',
filter: '| base64Encode',
path: '${value| base64Encode}',
filter: '',
expectValue: 'SSBsb3ZlIGFtaXM='
},
{
@ -437,8 +437,8 @@ const filters = [
data: {
value: 'SSBsb3ZlIGFtaXM='
},
path: '${value}',
filter: '| base64Decode',
path: '${value| base64Decode}',
filter: '',
expectValue: 'I love amis'
},
{
@ -446,8 +446,8 @@ const filters = [
data: {
value: 'AbC'
},
path: '${value}',
filter: '| lowerCase',
path: '${value| lowerCase}',
filter: '',
expectValue: 'abc'
},
{
@ -455,8 +455,8 @@ const filters = [
data: {
value: 'aBc'
},
path: '${value}',
filter: '| upperCase',
path: '${value| upperCase}',
filter: '',
expectValue: 'ABC'
}
];

View File

@ -264,7 +264,7 @@ icon 也可以是 url 地址,比如
{
"type": "crud",
"name": "crud",
"api": "/api/sample?waitSeconds=1",
"api": "/api/mock2/sample?waitSeconds=1",
"columns": [
{
"name": "id",
@ -821,6 +821,41 @@ props.onAction(event, {
除了 ctrl 和 command 还支持 shift、alt。
## Action 作为容器组件
> 1.5.0 及以上版本
action 还可以使用 `body` 来渲染其他组件,让那些不支持行为的组件支持点击事件,比如下面的例子
```schema: scope="body"
[{
"type": "action",
"body": [{
"type": "color",
"value": "#108cee"
}],
"actionType": "dialog",
"dialog": {
"title": "弹框",
"body": "这是个简单的弹框。"
}
},{
"type": "action",
"body": {
"type": "image",
"src": "https://internal-amis-res.cdn.bcebos.com/images/2020-1/1578395692722/4f3cb4202335.jpeg@s_0,w_216,l_1,f_jpg,q_80"
},
"tooltip": "点击会有弹框",
"actionType": "dialog",
"dialog": {
"title": "弹框",
"body": "这是个简单的弹框。"
}
}]
```
在这种模式下不支持按钮的各种配置项,比如 `label`、`size`、`icon` 等,因为它只作为容器组件,没有展现。
## 通用属性表
所有`actionType`都支持的通用配置项

View File

@ -12,28 +12,144 @@ order: 27
## 基本使用
`level`属性支持 4 种预设样式:`info`, `success`, `warning`, `danger`
```schema: scope="body"
[
{
"type": "alert",
"body": "提示类提示",
"level": "info"
"body": "提示类文案",
"level": "info",
"className": "mb-1"
},
{
"type": "alert",
"body": "成功类提示",
"level": "success"
"title": "提示类标题",
"body": "提示类文案",
"level": "info",
"className": "mb-3"
},
{
"type": "alert",
"body": "警告类提示",
"level": "warning"
"body": "成功类文案",
"level": "success",
"className": "mb-1"
},
{
"type": "alert",
"body": "危险类提示",
"level": "danger"
}
"title": "成功类标题",
"body": "成功类文案",
"level": "success",
"className": "mb-3"
},
{
"type": "alert",
"body": "警告类文案",
"level": "warning",
"className": "mb-1"
},
{
"type": "alert",
"title": "警告类标题",
"body": "警告类文案",
"level": "warning",
"className": "mb-3"
},
{
"type": "alert",
"body": "危险类文案",
"level": "danger",
"className": "mb-1"
},
{
"type": "alert",
"title": "危险类标题",
"body": "危险类文案",
"level": "danger",
},
]
```
## 图标
配置`"showIcon": true`后展示图标让信息更加醒目。可以通过`icon`属性自定义设置 icon 内容,如果`icon`属性为空,则根据`level`值添加默认 icon。
```schema: scope="body"
[
{
"type": "alert",
"body": "提示类文案",
"level": "info",
"showIcon": true,
"className": "mb-1"
},
{
"type": "alert",
"title": "提示类标题",
"body": "提示类文案",
"level": "info",
"showIcon": true,
"className": "mb-3"
},
{
"type": "alert",
"body": "成功类文案",
"level": "success",
"showIcon": true,
"className": "mb-1"
},
{
"type": "alert",
"title": "成功类标题",
"body": "成功类文案",
"level": "success",
"showIcon": true,
"className": "mb-3"
},
{
"type": "alert",
"body": "警告类文案",
"level": "warning",
"showIcon": true,
"className": "mb-1"
},
{
"type": "alert",
"title": "警告类标题",
"body": "警告类文案",
"level": "warning",
"showIcon": true,
"className": "mb-3"
},
{
"type": "alert",
"body": "危险类文案",
"level": "danger",
"showIcon": true,
"className": "mb-1"
},
{
"type": "alert",
"title": "危险类标题",
"body": "危险类文案",
"level": "danger",
"showIcon": true,
"className": "mb-3"
},
{
"type": "alert",
"body": "自定义ICON文案",
"showIcon": true,
"icon": "warning",
"className": "mb-1"
},
{
"type": "alert",
"title": "自定义ICON标题",
"body": "自定义ICON文案",
"showIcon": true,
"icon": "warning"
},
]
```
@ -45,24 +161,33 @@ order: 27
[
{
"type": "alert",
"body": "默认提示",
"level": "info"
"body": "显示关闭按钮的提示",
"level": "info",
"showCloseButton": true,
"showIcon": true,
"className": "mb-2"
},
{
"type": "alert",
"title": "可关闭提示",
"body": "显示关闭按钮的提示",
"level": "info",
"showCloseButton": true
"level": "success",
"showCloseButton": true,
"showIcon": true
}
]
```
## 属性表
| 属性名 | 类型 | 默认值 | 说明 |
| --------------- | ----------------------------------------- | --------- | -------------------------------------------------------- |
| type | `string` | `"alert"` | 指定为 alert 渲染器 |
| className | `string` | | 外层 Dom 的类名 |
| level | `string` | `info` | 级别,可以是:`info`、`success`、`warning` 或者 `danger` |
| body | [SchemaNode](../../docs/types/schemanode) | | 显示内容 |
| showCloseButton | `boolean` | false | 是否显示关闭按钮 |
| 属性名 | 类型 | 默认值 | 说明 |
| -------------------- | ----------------------------------------- | --------- | -------------------------------------------------------- |
| type | `string` | `"alert"` | 指定为 alert 渲染器 |
| className | `string` | | 外层 Dom 的类名 |
| level | `string` | `info` | 级别,可以是:`info`、`success`、`warning` 或者 `danger` |
| body | [SchemaNode](../../docs/types/schemanode) | | 显示内容 |
| showCloseButton | `boolean` | `false` | 是否显示关闭按钮 |
| closeButtonClassName | `string` | | 关闭按钮的 CSS 类名 |
| showIcon | `boolean` | `false` | 是否显示 icon |
| icon | `string` | | 自定义 icon |
| iconClassName | `string` | | icon 的 CSS 类名 |

View File

@ -26,19 +26,19 @@ order: 29
## 属性表
| 属性名 | 类型 | 默认值 | 说明 |
| ---------------- | ------------------------------------------------------------------------------------------------------------------- | ------- | ----------------------------------------------------- |
| className | `string` | | 指定添加 button 类名 |
| href | `string` | | 点击跳转的地址,指定此属性 button 的行为和 a 链接一致 |
| size | `'xs' \| 'sm' \| 'md' \| 'lg' ` | | 设置按钮大小 |
| actionType | `'button' \| 'reset' \| 'submit'\| 'clear'\| 'url'` | button | 设置按钮类型 |
| level | `'link' \| 'primary' \| 'enhance' \| 'secondary' \| 'info'\|'success' \| 'warning' \| 'danger' \| 'light'\| 'dark' \| 'default'` | default | 设置按钮样式 |
| tooltip | `'string' \| 'TooltipObject'` | | 气泡提示内容 |
| tooltipPlacement | `'top' \| 'right' \| 'bottom' \| 'left' ` | top | 气泡框位置器 |
| tooltipTrigger | `'hover' \| 'focus'` | | 触发 tootip |
| disabled | `'boolean'` | false | 按钮失效状态 |
| block | `'boolean'` | false | 将按钮宽度调整为其父宽度的选项 |
| loading | `'boolean'` | false | 显示按钮 loading 效果 |
| loadingOn | `'string'` | | 显示按钮 loading 表达式 |
| 属性名 | 类型 | 默认值 | 说明 |
| ---------------- | -------------------------------------------------------------------------------------------------------------------------------- | ------- | ----------------------------------------------------- |
| className | `string` | | 指定添加 button 类名 |
| href | `string` | | 点击跳转的地址,指定此属性 button 的行为和 a 链接一致 |
| size | `'xs' \| 'sm' \| 'md' \| 'lg' ` | | 设置按钮大小 |
| actionType | `'button' \| 'reset' \| 'submit'\| 'clear'\| 'url'` | button | 设置按钮类型 |
| level | `'link' \| 'primary' \| 'enhance' \| 'secondary' \| 'info'\|'success' \| 'warning' \| 'danger' \| 'light'\| 'dark' \| 'default'` | default | 设置按钮样式 |
| tooltip | `'string' \| 'TooltipObject'` | | 气泡提示内容 |
| tooltipPlacement | `'top' \| 'right' \| 'bottom' \| 'left' ` | top | 气泡框位置器 |
| tooltipTrigger | `'hover' \| 'focus'` | | 触发 tootip |
| disabled | `'boolean'` | false | 按钮失效状态 |
| block | `'boolean'` | false | 将按钮宽度调整为其父宽度的选项 |
| loading | `'boolean'` | false | 显示按钮 loading 效果 |
| loadingOn | `'string'` | | 显示按钮 loading 表达式 |
更多使用说明,请参看 [Grid Props](https://react-bootstrap.github.io/layout/grid/#col-props)

View File

@ -85,6 +85,74 @@ order: 31
}
```
## 设置头像文本
如果没有 avatar还可以通过 `avatarText` 设置头像文本
```schema: scope="body"
{
"type": "card",
"href": "https://github.com/baidu/amis",
"header": {
"title": "标题",
"subTitle": "副标题",
"description": "这是一段描述",
"avatarText": "AMIS"
},
"body": "这里是内容"
}
```
> 1.5.0 及以上版本
可以设置文本背景色,它会根据数据分配一个颜色,主要配合 `cards` 使用
```schema
{
"type": "page",
"data": {
"items": [
{
"engine": "Trident",
"browser": "Internet Explorer 4.0"
},
{
"engine": "Chrome",
"browser": "Chrome 44"
},
{
"engine": "Gecko",
"browser": "Firefox 1.0"
},
{
"engine": "Presto",
"browser": "Opera 10"
},
{
"engine": "Webkie",
"browser": "Safari 12"
}
]
},
"body": {
"type": "cards",
"source": "$items",
"card": {
"header": {
"avatarText": "${engine|substring:0:2|upperCase}",
"avatarTextBackground": ["#FFB900", "#D83B01", "#B50E0E", "#E81123", "#B4009E", "#5C2D91", "#0078D7", "#00B4FF", "#008272"]
},
"body": [
{
"label": "Browser",
"name": "browser"
}
]
}
}
}
```
## 点击卡片的行为
> 1.4.0 及以上版本
@ -115,6 +183,85 @@ order: 31
注意它和前面的 `href` 配置冲突,如果设置了 `href` 这个将不会生效
## 设置多媒体卡片
> 1.5.0 及以上版本
通过设置 `media` 可以设置为多媒体卡片, 通过 `mediaPosition` 可以设置多媒体位置
```schema: scope="body"
{
"type": "card",
"header": {
"title": "标题"
},
"media": {
"type": "image",
"className": "w-36 h-24",
"url": "https://internal-amis-res.cdn.bcebos.com/images/2020-1/1578395692722/4f3cb4202335.jpeg@s_0,w_216,l_1,f_jpg,q_80",
"position": "left"
},
"body": "这里是内容",
"secondary": "次要说明",
"actions": [
{
"type": "button",
"label": "操作",
"actionType": "dialog",
"className": "mr-4",
"dialog": {
"title": "操作",
"body": "你正在编辑该卡片"
}
},
{
"type": "button",
"label": "操作",
"actionType": "dialog",
"className": "mr-2.5",
"dialog": {
"title": "操作",
"body": "你正在编辑该卡片"
}
},
{
"type": "dropdown-button",
"level": "link",
"icon": "fa fa-ellipsis-h",
"className": "pr-1 flex",
"hideCaret": true,
"buttons": [
{
"type": "button",
"label": "编辑",
"actionType": "dialog",
"dialog": {
"title": "编辑",
"body": "你正在编辑该卡片"
}
},
{
"type": "button",
"label": "删除",
"actionType": "dialog",
"dialog": {
"title": "提示",
"body": "你删掉了该卡片"
}
}
]
}
],
"toolbar": [
{
"type": "tpl",
"tpl": "标签",
"className": "label label-warning"
}
]
}
```
## 配置工具栏
> 1.5.0 及以上版本
@ -220,21 +367,49 @@ order: 31
## 属性表
| 属性名 | 类型 | 默认值 | 说明 |
| ---------------------- | ------------------------------------ | ----------------------------------- | -------------------------------------- |
| type | `string` | `"card"` | 指定为 Card 渲染器 |
| className | `string` | `"panel-default"` | 外层 Dom 的类名 |
| href | [模板](../../docs/concepts/template) | | 外部链接 |
| header | `Object` | | Card 头部内容设置 |
| header.className | `string` | | 头部类名 |
| header.title | [模板](../../docs/concepts/template) | | 标题 |
| header.subTitle | [模板](../../docs/concepts/template) | | 副标题 |
| header.desc | [模板](../../docs/concepts/template) | | 描述 |
| header.avatar | [模板](../../docs/concepts/template) | | 图片 |
| header.avatarText | [模板](../../docs/concepts/template) | | 如果不配置图片,则会在图片处显示该文本 |
| header.highlight | `boolean` | | 是否显示激活样式 |
| header.avatarClassName | `string` | `"pull-left thumb avatar b-3x m-r"` | 图片类名 |
| body | `Array` | | 内容容器,主要用来放置非表单项组件 |
| bodyClassName | `string` | `"padder m-t-sm m-b-sm"` | 内容区域类名 |
| actions | Array<[Action](./action)> | | 配置按钮集合 |
| itemAction | [Action](./action) | | 点击卡片的行为 |
| 属性名 | 类型 | 默认值 | 说明 |
| ----------------------------- | ------------------------------------ | ----------------------------------- | ---------------------------------------- |
| type | `string` | `"card"` | 指定为 Card 渲染器 |
| className | `string` | | 外层 Dom 的类名 |
| href | [模板](../../docs/concepts/template) | | 外部链接 |
| header | `Object` | | Card 头部内容设置 |
| header.className | `string` | | 头部类名 |
| header.title | [模板](../../docs/concepts/template) | | 标题 |
| header.titleClassName | `string` | | 标题类名 |
| header.subTitle | [模板](../../docs/concepts/template) | | 副标题 |
| header.subTitleClassName | `string` | | 副标题类名 |
| header.subTitlePlaceholder | `string` | | 副标题占位 |
| header.description | [模板](../../docs/concepts/template) | | 描述 |
| header.descriptionClassName | `string` | | 描述类名 |
| header.descriptionPlaceholder | `string` | | 描述占位 |
| header.avatar | [模板](../../docs/concepts/template) | | 图片 |
| header.avatarClassName | `string` | `"pull-left thumb avatar b-3x m-r"` | 图片包括层类名 |
| header.imageClassName | `string` | | 图片类名 |
| header.avatarText | [模板](../../docs/concepts/template) | | 如果不配置图片,则会在图片处显示该文本 |
| header.avatarTextBackground | `Array` | | 设置文本背景色,它会根据数据分配一个颜色 |
| header.avatarTextClassName | `string` | | 图片文本类名 |
| header.highlight | `boolean` | `false` | 是否显示激活样式 |
| header.highlightClassName | `string` | | 激活样式类名 |
| header.href | [模板](../../docs/concepts/template) | | 点击卡片跳转的链接地址 |
| header.blank | `boolean` | `true` | 是否新窗口打开 |
| body | `Array` | | 内容容器,主要用来放置非表单项组件 |
| bodyClassName | `string` | | 内容区域类名 |
| actions | Array<[Action](./action)> | | 配置按钮集合 |
| actionsCount | `number` | `4` | 按钮集合每行个数 |
| itemAction | [Action](./action) | | 点击卡片的行为 |
| media | `Object` | | Card 多媒体部内容设置 |
| media.type | `'image'\|'video'` | | 多媒体类型 |
| media.url | `string` | | 图片/视频链接 |
| media.position | `'left'\|'right'\|'top'\|'bottom'` | `'left'` | 多媒体位置 |
| media.className | `string` | `"w-44 h-28"` | 多媒体类名 |
| media.isLive | `boolean` | `false` | 视频是否为直播 |
| media.autoPlay | `boolean` | `false` | 视频是否自动播放 |
| media.poster | `string` | `false` | 视频封面 |
| secondary | [模板](../../docs/concepts/template) | | 次要说明 |
| toolbar | Array<[Action](./action)> | | 工具栏按钮 |
| dragging | `boolean` | `false` | 是否显示拖拽图标 |
| selectable | `boolean` | `false` | 卡片是否可选 |
| checkable | `boolean` | `true` | 卡片选择按钮是否禁用 |
| selected | `boolean` | `false` | 卡片选择按钮是否选中 |
| hideCheckToggler | `boolean` | `false` | 卡片选择按钮是否隐藏 |
| multiple | `boolean` | `false` | 卡片是否为多选 |

View File

@ -12,13 +12,11 @@ order: 38
## 基本用法
```schema
```schema: scope="body"
{
"body": {
"type": "code",
"language": "html",
"value": "<div>html</div>"
}
"type": "code",
"language": "html",
"value": "<div>html</div>"
}
```
@ -42,6 +40,7 @@ language 支持从上下文获取数据
```schema
{
"type": "page",
"data": {
"lang": "javascript"
},
@ -59,6 +58,7 @@ language 支持从上下文获取数据
```schema
{
"type": "page",
"data": {
"sourcecode": "<div>html</div>"
},
@ -74,15 +74,31 @@ language 支持从上下文获取数据
## 主题及 tab 大小
```schema
通过 `editorTheme` 设置主题,`tagSize` 设置 tab 宽度
```schema: scope="body"
{
"body": {
"type": "code",
"language": "javascript",
"tagSize": 4,
"editorTheme": "vs-dark",
"value": "function amis() {\n\tconsole.log('amis');\n}"
}
"type": "code",
"language": "javascript",
"tagSize": 4,
"value": "function amis() {\n\tconsole.log('amis');\n}"
}
```
## 超出换行
> 1.5.1 及以上版本
通过 `wordWrap` 设置是否折行,默认是折行
```schema: scope="body"
{
"type": "code",
"language": "typescript",
"tagSize": 4,
"wordWrap": false,
"value": "function amis() {\n\tconsole.log('amis')\tconsole.log('amis')\tconsole.log('amis')\tconsole.log('amis')\tconsole.log('amis')\tconsole.log('amis');\n}"
}
```
@ -90,38 +106,36 @@ language 支持从上下文获取数据
还可以通过 `customLang` 参数来自定义高亮,比如下面这个示例
```schema
```schema: scope="body"
{
"body": {
"type": "code",
"customLang": {
"name": "myLog",
"tokens": [
{
"name": "custom-error",
"regex": "\\[error.*",
"color": "#ff0000",
"fontStyle": "bold"
},
{
"name": "custom-notice",
"regex": "\\[notice.*",
"color": "#FFA500"
},
{
"name": "custom-info",
"regex": "\\[info.*",
"color": "#808080"
},
{
"name": "custom-date",
"regex": "\\[[a-zA-Z 0-9:]+\\]",
"color": "#008800"
}
]
},
"value": "[Sun Mar 7 16:02:00 2021] [notice] Apache/1.3.29 (Unix) configured -- resuming normal operations\n[Sun Mar 7 16:02:00 2021] [info] Server built: Feb 27 2021 13:56:37\n[Sun Mar 7 16:02:00 2021] [notice] Accept mutex: sysvsem (Default: sysvsem)\n[Sun Mar 7 17:21:44 2021] [info] [client xx.xx.xx.xx] (104)Connection reset by peer: client stopped connection\n[Sun Mar 7 17:23:53 2021] statistics: Use of uninitialized value in concatenation (.) or string at /home/httpd line 528.\n[Sun Mar 7 17:23:53 2021] statistics: Can't create file /home/httpd/twiki/data/Main/WebStatistics.txt - Permission denied\n[Sun Mar 7 17:27:37 2021] [info] [client xx.xx.xx.xx] (104)Connection reset by peer: client stopped connection\n[Sun Mar 7 17:31:39 2021] [info] [client xx.xx.xx.xx] (104)Connection reset by peer: client stopped connection\n[Sun Mar 7 21:16:17 2021] [error] [client xx.xx.xx.xx] File does not exist: /home/httpd/twiki/view/Main/WebHome"
}
"type": "code",
"customLang": {
"name": "myLog",
"tokens": [
{
"name": "custom-error",
"regex": "\\[error.*",
"color": "#ff0000",
"fontStyle": "bold"
},
{
"name": "custom-notice",
"regex": "\\[notice.*",
"color": "#FFA500"
},
{
"name": "custom-info",
"regex": "\\[info.*",
"color": "#808080"
},
{
"name": "custom-date",
"regex": "\\[[a-zA-Z 0-9:]+\\]",
"color": "#008800"
}
]
},
"value": "[Sun Mar 7 16:02:00 2021] [notice] Apache/1.3.29 (Unix) configured -- resuming normal operations\n[Sun Mar 7 16:02:00 2021] [info] Server built: Feb 27 2021 13:56:37\n[Sun Mar 7 16:02:00 2021] [notice] Accept mutex: sysvsem (Default: sysvsem)\n[Sun Mar 7 17:21:44 2021] [info] [client xx.xx.xx.xx] (104)Connection reset by peer: client stopped connection\n[Sun Mar 7 17:23:53 2021] statistics: Use of uninitialized value in concatenation (.) or string at /home/httpd line 528.\n[Sun Mar 7 17:23:53 2021] statistics: Can't create file /home/httpd/twiki/data/Main/WebStatistics.txt - Permission denied\n[Sun Mar 7 17:27:37 2021] [info] [client xx.xx.xx.xx] (104)Connection reset by peer: client stopped connection\n[Sun Mar 7 17:31:39 2021] [info] [client xx.xx.xx.xx] (104)Connection reset by peer: client stopped connection\n[Sun Mar 7 21:16:17 2021] [error] [client xx.xx.xx.xx] File does not exist: /home/httpd/twiki/view/Main/WebHome"
}
```
@ -143,3 +157,4 @@ language 支持从上下文获取数据
| language | `string` | | 所使用的高亮语言,默认是 plaintext |
| tabSize | `number` | 4 | 默认 tab 大小 |
| editorTheme | `string` | 'vs' | 主题,还有 'vs-dark' |
| wordWrap | `string` | `true` | 是否折行 |

230
docs/zh-CN/components/collapse.md Executable file → Normal file
View File

@ -12,20 +12,224 @@ order: 36
```schema: scope="body"
{
"type": "collapse",
"title": "标题",
"body": "这里是内容"
"type": "collapse-group",
"activeKey": ["1"],
"body": [
{
"type": "collapse",
"key": "1",
"header": "标题1",
"body": "这里是内容1"
},
{
"type": "collapse",
"key": "2",
"header": "标题2",
"body": "这里是内容2"
},
{
"type": "collapse",
"key": "3",
"header": "标题3",
"body": "这里是内容3"
}
]
}
```
## 属性表
## 手风琴模式
```schema: scope="body"
{
"type": "collapse-group",
"activeKey": ["1"],
"accordion": true,
"body": [
{
"type": "collapse",
"key": "1",
"header": "标题1",
"body": "这里是内容1"
},
{
"type": "collapse",
"key": "2",
"header": "标题2",
"body": "这里是内容2"
},
{
"type": "collapse",
"key": "3",
"header": "标题3",
"body": "这里是内容3"
}
]
}
```
## 自定义图标
```schema: scope="body"
{
"type": "collapse-group",
"activeKey": ["1"],
"expandIcon": {
"type": "icon",
"icon": "caret-right"
},
"body": [
{
"type": "collapse",
"key": "1",
"header": "标题1",
"body": "这里是内容1"
},
{
"type": "collapse",
"key": "2",
"header": "标题2",
"body": "这里是内容2"
},
{
"type": "collapse",
"key": "3",
"header": "标题3",
"body": "这里是内容3"
}
]
}
```
## 设置图标位置
```schema: scope="body"
{
"type": "collapse-group",
"expandIconPosition": "right",
"activeKey": ["1"],
"body": [
{
"type": "collapse",
"key": "1",
"header": "标题1",
"body": "这里是内容1"
},
{
"type": "collapse",
"key": "2",
"header": "标题2",
"body": "这里是内容2"
},
{
"type": "collapse",
"key": "3",
"header": "标题3",
"body": "这里是内容3"
}
]
}
```
## 面板嵌套
```schema: scope="body"
{
"type": "collapse-group",
"activeKey": ["1"],
"body": [
{
"type": "collapse",
"key": "1",
"header": "标题1",
"body": {
"type": "collapse-group",
"activeKey": ["1"],
"body": [
{
"type": "collapse",
"key": "1",
"header": "嵌套面板标题",
"body": "嵌套面板内容"
}
]
}
},
{
"type": "collapse",
"key": "2",
"header": "标题2",
"body": "这里是内容2"
}
]
}
```
## 折叠面板禁用
```schema: scope="body"
{
"type": "collapse-group",
"activeKey": ["1"],
"body": [
{
"type": "collapse",
"key": "1",
"header": "标题1",
"body": "这里是内容1"
},
{
"type": "collapse",
"disabled": true,
"key": "2",
"header": "标题2",
"body": "这里是内容2"
}
]
}
```
## 折叠面板图标隐藏
```schema: scope="body"
{
"type": "collapse-group",
"body": [
{
"type": "collapse",
"key": "1",
"header": "标题1",
"body": "这里是内容1"
},
{
"type": "collapse",
"showArrow": false,
"key": "2",
"header": "标题2",
"body": "这里是内容2"
}
]
}
```
## CollapseGroup 属性表
| 属性名 | 类型 | 默认值 | 说明 |
| - | - | - | - |
| type | `string` | `"collapse-group"` | 指定为 collapse-group 渲染器 |
| activeKey | `Array<string \| number \| never> \| string \| number` | - | 初始化激活面板的key |
| accordion | `boolean` | `false` | 手风琴模式 |
| expandIcon | `SchemaNode` | - | 自定义切换图标 |
| expandIconPosition | `string` | `"left"` | 设置图标位置,可选值`left \| right` |
## Collapse 属性表
| 属性名 | 类型 | 默认值 | 说明 |
| - | - | - | - |
| type | `string` | `"collapse"` | 指定为 collapse 渲染器 |
| disabled | `boolean` | `false` | 禁用 |
| collapsed | `boolean` | `true` | 初始状态是否折叠 |
| key | `string \| number` | - | 标识 |
| header | `string \| SchemaNode` | - | 标题 |
| body | `string \| SchemaNode` | - | 内容 |
| showArrow | `boolean` | `true` | 是否展示图标 |
| 属性名 | 类型 | 默认值 | 说明 |
| ---------------- | ----------------------------------------- | -------------------------------------- | ---------------------- |
| type | `string` | `"collapse"` | 指定为 Collapse 渲染器 |
| title | [SchemaNode](../../docs/types/schemanode) | | 标题 |
| body | [SchemaNode](../../docs/types/schemanode) | | 内容 |
| className | `string` | `bg-white wrapper` | CSS 类名 |
| headingClassName | `string` | `font-thin b-b b-light text-lg p-b-xs` | 标题 CSS 类名 |
| bodyClassName | `string` | | 内容 CSS 类名。 |
| collapsed | `boolean` | `false` | 默认是否要收起。 |

View File

@ -19,7 +19,7 @@ CRUD即增删改查组件主要用来展现数据列表并支持各类
```schema: scope="body"
{
"type": "crud",
"api": "/api/sample",
"api": "/api/mock2/sample",
"syncLocation": false,
"columns": [
{
@ -116,7 +116,7 @@ CRUD即增删改查组件主要用来展现数据列表并支持各类
"title": "新增表单",
"body": {
"type": "form",
"api": "post:/api/sample",
"api": "post:/api/mock2/sample",
"body": [
{
"type": "input-text",
@ -134,7 +134,7 @@ CRUD即增删改查组件主要用来展现数据列表并支持各类
},
{
"type": "crud",
"api": "/api/sample?orderBy=id&orderDir=desc",
"api": "/api/mock2/sample?orderBy=id&orderDir=desc",
"syncLocation": false,
"columns": [
{
@ -175,7 +175,7 @@ CRUD即增删改查组件主要用来展现数据列表并支持各类
```schema: scope="body"
{
"type": "crud",
"api": "/api/sample?orderBy=id&orderDir=desc",
"api": "/api/mock2/sample?orderBy=id&orderDir=desc",
"syncLocation": false,
"columns": [
{
@ -200,7 +200,7 @@ CRUD即增删改查组件主要用来展现数据列表并支持各类
"actionType": "ajax",
"level": "danger",
"confirmText": "确认要删除?",
"api": "delete:/api/sample/${id}"
"api": "delete:/api/mock2/sample/${id}"
}
]
}
@ -215,7 +215,7 @@ CRUD即增删改查组件主要用来展现数据列表并支持各类
```schema: scope="body"
{
"type": "crud",
"api": "/api/sample?orderBy=id&orderDir=desc",
"api": "/api/mock2/sample?orderBy=id&orderDir=desc",
"syncLocation": false,
"columns": [
{
@ -242,8 +242,8 @@ CRUD即增删改查组件主要用来展现数据列表并支持各类
"title": "新增表单",
"body": {
"type": "form",
"initApi": "/api/sample/${id}",
"api": "post:/api/sample/${id}",
"initApi": "/api/mock2/sample/${id}",
"api": "post:/api/mock2/sample/${id}",
"body": [
{
"type": "input-text",
@ -282,7 +282,7 @@ Table 模式支持 [Table](./table) 中的所有功能。
```schema: scope="body"
{
"type": "crud",
"api": "/api/sample",
"api": "/api/mock2/sample",
"syncLocation": false,
"columns": [
{
@ -475,7 +475,7 @@ Cards 模式支持 [Cards](./cards) 中的所有功能。
{
"type": "crud",
"syncLocation": false,
"api": "/api/sample",
"api": "/api/mock2/sample",
"filter": {
"title": "条件搜索",
"body": [
@ -526,7 +526,7 @@ Cards 模式支持 [Cards](./cards) 中的所有功能。
```schema: scope="body"
{
"type": "crud",
"api": "/api/sample",
"api": "/api/mock2/sample",
"syncLocation": false,
"autoGenerateFilter": true,
"columns": [
@ -599,7 +599,7 @@ Cards 模式支持 [Cards](./cards) 中的所有功能。
{
"type": "crud",
"syncLocation": false,
"api": "/api/sample",
"api": "/api/mock2/sample",
"defaultParams": {
"perPage": 50
},
@ -642,7 +642,7 @@ Cards 模式支持 [Cards](./cards) 中的所有功能。
{
"type": "crud",
"syncLocation": false,
"api": "/api/sample",
"api": "/api/mock2/sample",
"interval": 3000,
"columns": [
{
@ -687,7 +687,7 @@ Cards 模式支持 [Cards](./cards) 中的所有功能。
{
"type": "crud",
"syncLocation": false,
"api": "/api/sample",
"api": "/api/mock2/sample",
"columns": [
{
"name": "id",
@ -721,7 +721,7 @@ amis 只负责生成排序组件,并将排序参数传递给接口,而不会
{
"type": "crud",
"syncLocation": false,
"api": "/api/sample",
"api": "/api/mock2/sample",
"columns": [
{
"name": "id",
@ -754,7 +754,7 @@ amis 只负责生成搜索组件,并将搜索参数传递给接口,而不会
{
"type": "crud",
"syncLocation": false,
"api": "/api/sample",
"api": "/api/mock2/sample",
"columns": [
{
"name": "id",
@ -797,8 +797,8 @@ amis 只负责生成下拉选择器组件,并将搜索参数传递给接口,
{
"type": "crud",
"syncLocation": false,
"api": "/api/sample",
"quickSaveApi": "/api/sample/bulkUpdate",
"api": "/api/mock2/sample",
"quickSaveApi": "/api/mock2/sample/bulkUpdate",
"columns": [
{
"name": "id",
@ -821,8 +821,8 @@ amis 只负责生成下拉选择器组件,并将搜索参数传递给接口,
{
"type": "crud",
"syncLocation": false,
"api": "/api/sample",
"quickSaveApi": "/api/sample/bulkUpdate",
"api": "/api/mock2/sample",
"quickSaveApi": "/api/mock2/sample/bulkUpdate",
"columns": [
{
"name": "id",
@ -852,8 +852,8 @@ amis 只负责生成下拉选择器组件,并将搜索参数传递给接口,
{
"type": "crud",
"syncLocation": false,
"api": "/api/sample",
"quickSaveApi": "/api/sample/bulkUpdate",
"api": "/api/mock2/sample",
"quickSaveApi": "/api/mock2/sample/bulkUpdate",
"columns": [
{
"name": "id",
@ -896,8 +896,8 @@ amis 只负责生成下拉选择器组件,并将搜索参数传递给接口,
{
"type": "crud",
"syncLocation": false,
"api": "/api/sample",
"quickSaveApi": "/api/sample/bulkUpdate",
"api": "/api/mock2/sample",
"quickSaveApi": "/api/mock2/sample/bulkUpdate",
"columns": [
{
"name": "id",
@ -941,8 +941,8 @@ amis 只负责生成下拉选择器组件,并将搜索参数传递给接口,
{
"type": "crud",
"syncLocation": false,
"api": "/api/sample",
"quickSaveItemApi": "/api/sample/$id",
"api": "/api/mock2/sample",
"quickSaveItemApi": "/api/mock2/sample/$id",
"columns": [
{
"name": "id",
@ -986,7 +986,7 @@ amis 只负责生成下拉选择器组件,并将搜索参数传递给接口,
{
"type": "crud",
"syncLocation": false,
"api": "/api/sample",
"api": "/api/mock2/sample",
"columns": [
{
"name": "id",
@ -1007,7 +1007,7 @@ amis 只负责生成下拉选择器组件,并将搜索参数传递给接口,
"X"
],
"saveImmediately": {
"api": "/api/sample/$id"
"api": "/api/mock2/sample/$id"
}
}
},
@ -1034,8 +1034,8 @@ amis 只负责生成下拉选择器组件,并将搜索参数传递给接口,
{
"type": "crud",
"syncLocation": false,
"api": "/api/sample",
"quickSaveApi": "/api/sample/bulkUpdate",
"api": "/api/mock2/sample",
"quickSaveApi": "/api/mock2/sample/bulkUpdate",
"columns": [
{
"name": "id",
@ -1059,7 +1059,7 @@ crud 组件支持通过配置`headerToolbar`和`footerToolbar`属性,实现在
{
"type": "crud",
"syncLocation": false,
"api": "/api/sample",
"api": "/api/mock2/sample",
"headerToolbar": [
{
"type": "tpl",
@ -1147,7 +1147,7 @@ crud 组件支持通过配置`headerToolbar`和`footerToolbar`属性,实现在
"type": "crud",
"name": "myCRUD",
"syncLocation": false,
"api": "/api/sample",
"api": "/api/mock2/sample",
"headerToolbar": [
{
"label": "点击弹框",
@ -1191,7 +1191,7 @@ crud 组件支持通过配置`headerToolbar`和`footerToolbar`属性,实现在
{
"type": "crud",
"syncLocation": false,
"api": "/api/sample",
"api": "/api/mock2/sample",
"headerToolbar": [],
"footerToolbar": ["switch-per-page", "pagination"],
"columns": [
@ -1214,7 +1214,7 @@ crud 组件支持通过配置`headerToolbar`和`footerToolbar`属性,实现在
"X"
],
"saveImmediately": {
"api": "/api/sample/$id"
"api": "/api/mock2/sample/$id"
}
}
}
@ -1330,7 +1330,7 @@ crud 组件支持通过配置`headerToolbar`和`footerToolbar`属性,实现在
{
"type": "crud",
"syncLocation": false,
"api": "/api/sample",
"api": "/api/mock2/sample",
"headerToolbar": [
"bulkActions"
],
@ -1338,7 +1338,7 @@ crud 组件支持通过配置`headerToolbar`和`footerToolbar`属性,实现在
{
"label": "批量删除",
"actionType": "ajax",
"api": "delete:/api/sample/${ids|raw}",
"api": "delete:/api/mock2/sample/${ids|raw}",
"confirmText": "确定要批量删除?"
},
{
@ -1348,7 +1348,7 @@ crud 组件支持通过配置`headerToolbar`和`footerToolbar`属性,实现在
"title": "批量编辑",
"body": {
"type": "form",
"api": "/api/sample/bulkUpdate2",
"api": "/api/mock2/sample/bulkUpdate2",
"body": [
{
"type": "hidden",
@ -1398,7 +1398,7 @@ crud 组件支持通过配置`headerToolbar`和`footerToolbar`属性,实现在
- `items` `Array<object>` 选中的行数据。
- `rows` items 的别名,推荐用 items。
- `selectedItems` `Array<object>` 选中的行数据,建议直接用 items。
- `unselectedItems` `Array<object>` 没选中的行数据也可获取。
- `unSelectedItems` `Array<object>` 没选中的行数据也可获取。
- `ids` `string` 多个 id 值用英文逗号隔开,前提是行数据中有 id 字段,或者有指定的 `primaryField` 字段。
- `第一行所有行数据` 还有第一行的所有行数据也会包含进去。
@ -1422,7 +1422,7 @@ crud 组件支持通过配置`headerToolbar`和`footerToolbar`属性,实现在
{
"type": "crud",
"syncLocation": false,
"api": "/api/sample",
"api": "/api/mock2/sample",
"headerToolbar": [
"bulkActions"
],
@ -1432,7 +1432,7 @@ crud 组件支持通过配置`headerToolbar`和`footerToolbar`属性,实现在
{
"label": "批量删除",
"actionType": "ajax",
"api": "delete:/api/sample/${ids|raw}",
"api": "delete:/api/mock2/sample/${ids|raw}",
"confirmText": "确定要批量删除?"
},
{
@ -1442,7 +1442,7 @@ crud 组件支持通过配置`headerToolbar`和`footerToolbar`属性,实现在
"title": "批量编辑",
"body": {
"type": "form",
"api": "/api/sample/bulkUpdate2",
"api": "/api/mock2/sample/bulkUpdate2",
"body": [
{
"type": "hidden",
@ -1493,7 +1493,7 @@ crud 组件支持通过配置`headerToolbar`和`footerToolbar`属性,实现在
{
"type": "crud",
"syncLocation": false,
"api": "/api/sample",
"api": "/api/mock2/sample",
"checkOnItemClick": true,
"headerToolbar": [
"bulkActions"
@ -1502,7 +1502,7 @@ crud 组件支持通过配置`headerToolbar`和`footerToolbar`属性,实现在
{
"label": "批量删除",
"actionType": "ajax",
"api": "delete:/api/sample/${ids|raw}",
"api": "delete:/api/mock2/sample/${ids|raw}",
"confirmText": "确定要批量删除?"
},
{
@ -1512,7 +1512,7 @@ crud 组件支持通过配置`headerToolbar`和`footerToolbar`属性,实现在
"title": "批量编辑",
"body": {
"type": "form",
"api": "/api/sample/bulkUpdate2",
"api": "/api/mock2/sample/bulkUpdate2",
"body": [
{
"type": "hidden",
@ -1565,7 +1565,7 @@ crud 组件支持通过配置`headerToolbar`和`footerToolbar`属性,实现在
{
"type": "crud",
"syncLocation": false,
"api": "/api/sample",
"api": "/api/mock2/sample",
"headerToolbar": ["statistics"],
"columns": [
{
@ -1604,7 +1604,7 @@ crud 组件支持通过配置`headerToolbar`和`footerToolbar`属性,实现在
{
"type": "crud",
"syncLocation": false,
"api": "/api/sample",
"api": "/api/mock2/sample",
"headerToolbar": ["load-more"],
"columns": [
{
@ -1643,7 +1643,7 @@ crud 组件支持通过配置`headerToolbar`和`footerToolbar`属性,实现在
{
"type": "crud",
"syncLocation": false,
"api": "/api/sample",
"api": "/api/mock2/sample",
"headerToolbar": ["export-csv"],
"columns": [
{
@ -1684,11 +1684,11 @@ crud 组件支持通过配置`headerToolbar`和`footerToolbar`属性,实现在
{
"type": "crud",
"syncLocation": false,
"api": "/api/sample",
"api": "/api/mock2/sample",
"headerToolbar": [{
"type": "export-csv",
"label": "全量导出 CSV",
"api": "/api/sample"
"api": "/api/mock2/sample"
}],
"columns": [
{
@ -1731,7 +1731,7 @@ crud 组件支持通过配置`headerToolbar`和`footerToolbar`属性,实现在
{
"type": "crud",
"syncLocation": false,
"api": "/api/sample",
"api": "/api/mock2/sample",
"headerToolbar": ["export-excel"],
"columns": [
{
@ -1772,7 +1772,7 @@ crud 组件支持通过配置`headerToolbar`和`footerToolbar`属性,实现在
{
"type": "crud",
"syncLocation": false,
"api": "/api/sample",
"api": "/api/mock2/sample",
"headerToolbar": [{
"type": "export-excel",
"label": "只导出 engine 和 browser 列",
@ -1820,7 +1820,7 @@ crud 组件支持通过配置`headerToolbar`和`footerToolbar`属性,实现在
"headerToolbar": [{
"type": "export-excel",
"label": "全量导出 Excel",
"api": "/api/sample"
"api": "/api/mock2/sample"
}],
"columns": [
{
@ -1865,7 +1865,7 @@ crud 组件支持通过配置`headerToolbar`和`footerToolbar`属性,实现在
"type": "export-excel",
"label": "自定义导出 Excel",
"filename": "自定义文件名${test}",
"api": "/api/sample"
"api": "/api/mock2/sample"
}],
"columns": [
{
@ -1904,7 +1904,7 @@ crud 组件支持通过配置`headerToolbar`和`footerToolbar`属性,实现在
{
"type": "crud",
"syncLocation": false,
"api": "/api/sample",
"api": "/api/mock2/sample",
"filter": {
"title": "条件搜索",
"body": [
@ -1958,7 +1958,7 @@ crud 组件支持通过配置`headerToolbar`和`footerToolbar`属性,实现在
{
"type": "crud",
"syncLocation": false,
"api": "/api/sample",
"api": "/api/mock2/sample",
"headerToolbar": [
"reload"
],
@ -1997,7 +1997,7 @@ crud 组件支持通过配置`headerToolbar`和`footerToolbar`属性,实现在
{
"type": "crud",
"syncLocation": false,
"api": "/api/sample",
"api": "/api/mock2/sample",
"headerToolbar": [
{
"type": "reload",
@ -2053,7 +2053,7 @@ crud 组件支持通过配置`headerToolbar`和`footerToolbar`属性,实现在
{
"type": "crud",
"syncLocation": false,
"api": "/api/sample",
"api": "/api/mock2/sample",
"draggable": true,
"columns": [
{
@ -2096,7 +2096,7 @@ crud 组件支持通过配置`headerToolbar`和`footerToolbar`属性,实现在
},
{
"type": "form",
"api": "/api/sample/$id",
"api": "/api/mock2/sample/$id",
"body": [
{
"type": "input-text",
@ -2124,7 +2124,7 @@ crud 组件支持通过配置`headerToolbar`和`footerToolbar`属性,实现在
{
"type": "crud",
"syncLocation": false,
"api": "/api/sample",
"api": "/api/mock2/sample",
"draggable": true,
"columns": [
{
@ -2198,7 +2198,7 @@ crud 组件支持通过配置`headerToolbar`和`footerToolbar`属性,实现在
```schema: scope="body"
{
"type": "crud",
"api": "/api/sample",
"api": "/api/mock2/sample",
"syncLocation": false,
"headerToolbar": [
{
@ -2300,7 +2300,7 @@ CRUD 中不限制有多少个单条操作、添加一个操作对应的添加一
{
"type": "crud",
"syncLocation": false,
"api": "/api/sample",
"api": "/api/mock2/sample",
"loadDataOnce": true,
"columns": [
{
@ -2338,7 +2338,7 @@ CRUD 中不限制有多少个单条操作、添加一个操作对应的添加一
{
"type": "crud",
"syncLocation": false,
"api": "/api/sample",
"api": "/api/mock2/sample",
"loadDataOnce": true,
"source": "${rows | filter:engine:match:keywords}",
"filter":{
@ -2433,7 +2433,7 @@ CRUD 中不限制有多少个单条操作、添加一个操作对应的添加一
```schema: scope="body"
{
"type": "crud",
"api": "/api/sample",
"api": "/api/mock2/sample",
"syncLocation": false,
"itemAction": {
"type": "button",
@ -2481,7 +2481,7 @@ itemAction 里的 onClick 还能通过 `data` 参数拿到当前行的数据,
```schema: scope="body"
{
"type": "crud",
"api": "/api/sample",
"api": "/api/mock2/sample",
"syncLocation": false,
"itemAction": {
"type": "button",

View File

@ -90,7 +90,7 @@ Dialog 弹框 主要由 [Action](./action) 触发,主要展示一个对话框
```schema: scope="body"
{
"type": "crud",
"api": "/api/sample",
"api": "/api/mock2/sample",
"draggable": true,
"columns": [
{
@ -126,7 +126,7 @@ Dialog 弹框 主要由 [Action](./action) 触发,主要展示一个对话框
"body": [
{
"type": "form",
"api": "/api/sample/$id",
"api": "/api/mock2/sample/$id",
"body": [
{
"type": "input-text",
@ -147,7 +147,7 @@ Dialog 弹框 主要由 [Action](./action) 触发,主要展示一个对话框
```schema: scope="body"
{
"type": "crud",
"api": "/api/sample",
"api": "/api/mock2/sample",
"draggable": true,
"columns": [
{
@ -186,7 +186,7 @@ Dialog 弹框 主要由 [Action](./action) 触发,主要展示一个对话框
"body": [
{
"type": "form",
"api": "/api/sample/$id",
"api": "/api/mock2/sample/$id",
"body": [
{
"type": "input-text",

View File

@ -35,6 +35,37 @@ order: 44
}
```
## 关闭下拉菜单
配置`"closeOnClick": true`可以实现点击按钮后自动关闭下拉菜单。
```schema
{
"type": "page",
"body": {
"type": "dropdown-button",
"label": "下拉菜单",
"closeOnClick": true,
"closeOnOutside": true,
"buttons": [
{
"type": "button",
"label": "按钮1",
"disabled": true
},
{
"type": "button",
"label": "按钮2"
},
{
"type": "button",
"label": "按钮3"
}
]
}
}
```
## 触发方式
> 1.4.0 及以上版本
@ -177,6 +208,7 @@ order: 44
| buttons | `Array<action>` | | 配置下拉按钮 |
| iconOnly | `boolean` | | 只显示 icon |
| defaultIsOpened | `boolean` | | 默认是否打开 |
| closeOnOutside | `boolean` | | 点击外侧区域是否收起 |
| closeOnOutside | `boolean` | `true` | 点击外侧区域是否收起 |
| closeOnClick | `boolean` | `false` | 点击按钮后自动关闭下拉菜单 |
| trigger | `click``hover` | `click` | 触发方式 |
| hideCaret | `boolean` | false | 隐藏下拉图标 |

View File

@ -1010,6 +1010,7 @@ Table 类型的表单项,要实现服务端校验,可以使用 `路径key`
| inputClassName | `string` | | 表单控制器类名 |
| labelClassName | `string` | | label 的类名 |
| name | `string` | | 字段名,指定该表单项提交时的 key |
| value | `string` | | 表单默认值 |
| label | [模板](../../../docs/concepts/template) 或 `false` | | 表单项标签 |
| labelRemark | [Remark](../remark) | | 表单项标签描述 |
| description | [模板](../../../docs/concepts/template) | | 表单项描述 |

View File

@ -166,6 +166,60 @@ order: 24
}
```
## 宽度占比
在表单项内部可以通过 `columnRatio` 来控制宽度,整体是 12 等分
```schema: scope="body"
{
"type": "form",
"body": [
{
"type": "group",
"body": [
{
"type": "input-text",
"name": "text1",
"label": "text1",
"columnRatio": 8
},
{
"type": "input-text",
"name": "text2",
"label": "text2",
"columnRatio": 4
}
]
},
{
"type": "divider"
},
{
"type": "group",
"body": [
{
"type": "input-text",
"name": "text1",
"label": "text1",
"columnRatio": 6
},
{
"type": "input-text",
"name": "text2",
"label": "text2",
"columnRatio": 4
},
{
"type": "input-text",
"name": "text3",
"label": "text3"
}
]
}
]
}
```
## 属性表
| 属性名 | 类型 | 默认值 | 说明 |

View File

@ -227,7 +227,7 @@ order: 24
}
```
另一个方法是使用 group它能实现每行显示不同列数可以实现更灵活的控制
另一个方法是使用 group它能实现每行显示不同列数以及不同列的宽度分配情况,可以实现更灵活的控制
```schema: scope="body"
[
@ -259,12 +259,14 @@ order: 24
{
"type": "input-text",
"name": "text3",
"label": "文本3"
"label": "文本3",
"columnRatio": 4
},
{
"type": "input-text",
"name": "text4",
"label": "文本4"
"label": "文本4",
"columnRatio": 6
},
{
"type": "input-text",
@ -904,6 +906,66 @@ Form 支持轮询初始化接口,步骤如下:
上面示例是一种[组件间联动](../../docs/concepts/linkage#%E7%BB%84%E4%BB%B6%E9%97%B4%E8%81%94%E5%8A%A8)
### 显示提交的返回结果
默认情况下表单提交返回结果会写入当前表单的数据域,如果要显示在当前表单,可以直接使用 `static` 类型,比如下面的例子
```schema: scope="body"
{
"type": "form",
"api": "/api/mock2/form/saveForm",
"title": "用户信息",
"body": [
{
"type": "input-text",
"name": "name",
"label": "姓名"
},
{
"type": "static",
"name": "id",
"visibleOn": "typeof data.id !== 'undefined'",
"label": "返回 ID"
}
]
}
```
### 将提交返回内容发送到其它组件
还可以将返回结果发送到其它组件,首先设置另一个表单的 `name`,然后通过 `reload` 配置参数来提交
```schema: scope="body"
[
{
"type": "form",
"api": "/api/mock2/form/saveForm",
"title": "用户信息",
"reload": "otherForm?id=${id}",
"body": [
{
"type": "input-text",
"name": "name",
"label": "姓名"
}
]
},
{
"type": "form",
"name": "otherForm",
"title": "返回结果",
"actions": [],
"body": [
{
"type": "static",
"name": "id",
"label": "返回 ID"
}
]
}
]
```
### 将数据域发送给目标组件
配置`target`属性为目标组件`name`值,可以在触发提交行为后,将当前表单的数据域发送给目标组件。

View File

@ -18,7 +18,7 @@ order: 11
{
"type": "input-color",
"name": "color",
"label": "颜色"
"label": "普通色盘"
}
]
}
@ -26,9 +26,39 @@ order: 11
## 选择器预设颜色值
颜色选择器底部预设有会写可选的颜色值,默认为:`['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505', '#BD10E0', '#9013FE', '#4A90E2', '#50E3C2', '#B8E986', '#000000', '#4A4A4A', '#9B9B9B', '#FFFFFF']`
颜色选择器底部预设有会写可选的颜色值,默认为:
你可以配置`presetColors`数组进行自定义。
```
['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505', '#BD10E0', '#9013FE', '#4A90E2', '#50E3C2', '#B8E986', '#000000', '#4A4A4A', '#9B9B9B', '#FFFFFF']
```
你可以配置`presetColors`数组进行自定义。颜色支持两种格式`string` 和 `{color: string; title: string}`,并支持两种格式混合使用。`string`格式支持所有合法的 CSS 颜色码,`object`类型下的`color`属性为 CSS 颜色码,`title`属性为颜色名称,鼠标悬浮于色块时会显示对应颜色名称。
```schema: scope="body"
{
"type": "form",
"api": "/api/mock2/form/saveForm",
"body": [
{
"type": "input-color",
"name": "color",
"label": "自定义预设色盘",
"presetColors": [
{"color": "#d4380d", "title": "熔岩红"},
{"color": "#ffa940", "title": "金桔橙"},
{"color": "#ffec3d", "title": "土豪金"},
{"color": "#73d13d", "title": "苹果绿"},
{"color": "#73E3EC", "title": "蒂芙尼青"},
{"color": "#2f54eb", "title": "冰川蓝"},
{"color": "#9254de", "title": "薰衣草紫"},
{"color": "#ffc0cb", "title": "樱花粉"},
"rgb(212, 56, 13)", "rgba(255, 169, 64, 1.0)", "hsl(54,100%,62%)", "hsla(98, 62%, 53%, 1.0)", "#73E3EC", "#2f54eb", "#9254de", "pink"
]
}
]
}
```
## rgba
@ -42,7 +72,7 @@ order: 11
{
"type": "input-color",
"name": "color",
"label": "颜色",
"label": "带透明度调节的色盘",
"format": "rgba"
}
]
@ -53,10 +83,10 @@ order: 11
当做选择器表单项使用时,除了支持 [普通表单项属性表](./formitem#%E5%B1%9E%E6%80%A7%E8%A1%A8) 中的配置以外,还支持下面一些配置
| 属性名 | 类型 | 默认值 | 说明 |
| ---------------- | --------------- | ------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------- |
| format | `string` | `hex` | 请选择 `hex`、`hls`、`rgb`或者`rgba`。 |
| presetColors | `Array<string>` | [选择器预设颜色值](./color#%E9%80%89%E6%8B%A9%E5%99%A8%E9%A2%84%E8%AE%BE%E9%A2%9C%E8%89%B2%E5%80%BC) | 选择器底部的默认颜色,数组内为空则不显示默认颜色 |
| allowCustomColor | `boolean` | `true` | 为`false`时只能选择颜色,使用 `presetColors` 设定颜色选择范围 |
| clearable | `boolean` | `"label"` | 是否显示清除按钮 |
| resetValue | `string` | `""` | 清除后,表单项值调整成该值 |
| 属性名 | 类型 | 默认值 | 说明 |
| ---------------- | --------------- | ---------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- |
| format | `string` | `hex` | 请选择 `hex`、`hls`、`rgb`或者`rgba`。 |
| presetColors | `Array<string>` | [选择器预设颜色值](./input-color#%E9%80%89%E6%8B%A9%E5%99%A8%E9%A2%84%E8%AE%BE%E9%A2%9C%E8%89%B2%E5%80%BC) | 选择器底部的默认颜色,数组内为空则不显示默认颜色 |
| allowCustomColor | `boolean` | `true` | 为`false`时只能选择颜色,使用 `presetColors` 设定颜色选择范围 |
| clearable | `boolean` | `"label"` | 是否显示清除按钮 |
| resetValue | `string` | `""` | 清除后,表单项值调整成该值 |

View File

@ -138,7 +138,7 @@ order: 13
- `today`: 当前日期
- `day`或`days`: 天
- `week`或`weeks`:
- `week`或`weeks`:
- `month`或`months`: 月
- `year`或`years`: 年
@ -312,6 +312,185 @@ order: 13
}
```
## 日历日程
```schema: scope="body"
{
"type": "input-date",
"embed": true,
"schedules": [
{
"startTime": "2021-12-11 05:14:00",
"endTime": "2021-12-11 06:14:00",
"content": "这是一个日程1"
},
{
"startTime": "2021-12-21 05:14:00",
"endTime": "2021-12-22 05:14:00",
"content": "这是一个日程2"
}
]
}
```
## 日历日程-自定义颜色
```schema: scope="body"
{
"type": "input-date",
"embed": true,
"schedules": [
{
"startTime": "2021-12-11 05:14:00",
"endTime": "2021-12-11 06:14:00",
"content": "这是一个日程1",
"className": "bg-success"
},
{
"startTime": "2021-12-21 05:14:00",
"endTime": "2021-12-22 05:14:00",
"content": "这是一个日程2",
"className": "bg-info"
}
]
}
```
```schema: scope="body"
{
"type": "input-date",
"embed": true,
"scheduleClassNames": ["bg-success", "bg-info"],
"schedules": [
{
"startTime": "2021-12-11 05:14:00",
"endTime": "2021-12-11 06:14:00",
"content": "这是一个日程1"
},
{
"startTime": "2021-12-21 05:14:00",
"endTime": "2021-12-22 05:14:00",
"content": "这是一个日程2"
}
]
}
```
## 日历日程-自定义日程展示
```schema: scope="body"
{
"type": "input-date",
"embed": true,
"schedules": [
{
"startTime": "2021-12-11 05:14:00",
"endTime": "2021-12-11 06:14:00",
"content": "这是一个日程1"
},
{
"startTime": "2021-12-21 05:14:00",
"endTime": "2021-12-22 05:14:00",
"content": "这是一个日程2"
}
],
"scheduleAction": {
"actionType": "drawer",
"drawer": {
"title": "日程",
"body": {
"type": "table",
"columns": [
{
"name": "time",
"label": "时间"
},
{
"name": "content",
"label": "内容"
}
],
"data": "${scheduleData}"
}
}
}
}
```
## 日历日程-支持从数据源中获取日程
```schema
{
"type": "page",
"data": {
"schedules": [
{
"startTime": "2021-12-11 05:14:00",
"endTime": "2021-12-11 06:14:00",
"content": "这是一个日程1"
},
{
"startTime": "2021-12-21 05:14:00",
"endTime": "2021-12-22 05:14:00",
"content": "这是一个日程2"
}
]
},
"body": [
{
"type": "input-date",
"embed": true,
"schedules": "${schedules}"
}
]
}
```
## 放大模式
```schema: scope="body"
{
"type": "input-date",
"embed": true,
"largeMode": true,
"schedules": [
{
"startTime": "2021-12-11 05:14:00",
"endTime": "2021-12-11 06:14:00",
"content": "这是一个日程1"
},
{
"startTime": "2021-12-12 02:14:00",
"endTime": "2021-12-13 05:14:00",
"content": "这是一个日程2"
},
{
"startTime": "2021-12-20 05:14:00",
"endTime": "2021-12-21 05:14:00",
"content": "这是一个日程3"
},
{
"startTime": "2021-12-21 05:14:00",
"endTime": "2021-12-22 05:14:00",
"content": "这是一个日程4"
},
{
"startTime": "2021-12-22 02:14:00",
"endTime": "2021-12-23 05:14:00",
"content": "这是一个日程5"
},
{
"startTime": "2021-12-22 02:14:00",
"endTime": "2021-12-22 05:14:00",
"content": "这是一个日程6"
},
{
"startTime": "2021-12-22 02:14:00",
"endTime": "2021-12-22 05:14:00",
"content": "这是一个日程7"
}
]
}
```
## 原生日期组件
原生数字日期将直接使用浏览器的实现,最终展现效果和浏览器有关,而且只支持 `min`、`max`、`step` 这几个属性设置。
@ -348,3 +527,7 @@ order: 13
| clearable | `boolean` | `true` | 是否可清除 |
| embed | `boolean` | `false` | 是否内联模式 |
| timeConstraints | `object` | `true` | 请参考: [react-datetime](https://github.com/YouCanBookMe/react-datetime) |
| schedules | `Array<{startTime: Date, endTime: Date, content: any, className?: string}> \| string` | | 日历中展示日程可设置静态数据或从上下文中取数据className参考[背景色](https://baidu.gitee.io/amis/zh-CN/style/background/background-color) |
| scheduleClassNames | `Array<string>` | `['bg-warning', 'bg-danger', 'bg-success', 'bg-info', 'bg-secondary']` | 日历中展示日程的颜色,参考[背景色](https://baidu.gitee.io/amis/zh-CN/style/background/background-color) |
| scheduleAction | `SchemaNode` | | 自定义日程展示 |
| largeMode | `boolean` | `false` | 放大模式 |

View File

@ -59,15 +59,15 @@ order: 35
"valueField": "id",
"labelField": "engine",
"label": "多选",
"source": "/api/sample",
"source": "/api/mock2/sample",
"size": "lg",
"value": "4,5",
"multiple": true,
"pickerSchema": {
"mode": "table",
"name": "thelist",
"quickSaveApi": "/api/sample/bulkUpdate",
"quickSaveItemApi": "/api/sample/$id",
"quickSaveApi": "/api/mock2/sample/bulkUpdate",
"quickSaveItemApi": "/api/mock2/sample/$id",
"draggable": true,
"headerToolbar": {
"wrapWithPanel": false,
@ -203,7 +203,7 @@ order: 35
"body": {
"type": "form",
"name": "sample-edit-form",
"api": "/api/sample/$id",
"api": "/api/mock2/sample/$id",
"body": [
{
"type": "input-text",
@ -255,7 +255,7 @@ order: 35
"icon": "fa fa-times text-danger",
"actionType": "ajax",
"confirmText": "您确认要删除?",
"api": "delete:/api/sample/$id"
"api": "delete:/api/mock2/sample/$id"
}
],
"toggled": true
@ -284,15 +284,15 @@ order: 35
"labelField": "engine",
"label": "Picker",
"embed": true,
"source": "/api/sample",
"source": "/api/mock2/crud/tree?waitSeconds=1",
"size": "lg",
"value": "4,5",
"multiple": true,
"pickerSchema": {
"mode": "table",
"name": "thelist",
"quickSaveApi": "/api/sample/bulkUpdate",
"quickSaveItemApi": "/api/sample/$id",
"quickSaveApi": "/api/mock2/sample/bulkUpdate",
"quickSaveItemApi": "/api/mock2/sample/$id",
"draggable": true,
"headerToolbar": {
"wrapWithPanel": false,
@ -428,7 +428,7 @@ order: 35
"body": {
"type": "form",
"name": "sample-edit-form",
"api": "/api/sample/$id",
"api": "/api/mock2/sample/$id",
"body": [
{
"type": "input-text",
@ -480,7 +480,7 @@ order: 35
"icon": "fa fa-times text-danger",
"actionType": "ajax",
"confirmText": "您确认要删除?",
"api": "delete:/api/sample/$id"
"api": "delete:/api/mock2/sample/$id"
}
],
"toggled": true

View File

@ -887,6 +887,37 @@ leftOptions 动态加载,默认 source 接口是返回 options 部分,而 le
}
```
### 人员点选
> 请通过网络面板查看接口请求返回。
实际上就是采用的[「关联选择模式」](#关联选择模式)的 select注意要看那一部分文档需要知道怎么动态加载 leftOptions。左侧部分和人员都是通过 source 接口返回。需要懒加载的项通过设置 `defer` 为 true 来标记。左右两部分都支持懒加载。
都是通过 deferApi 来实现,后端根据传过来的参数决定是懒加载树,还是栏加载人员。
- 有 dep 值则是懒加载部门树
- 有 ref 值则是懒加载人员
```schema: scope="body"
{
"type": "form",
"api": "/api/mock2/form/saveForm",
"body": [
{
"label": "人员选择",
"type": "select",
"name": "b",
"multiple": true,
"sortable": true,
"searchable": true,
"selectMode": "associated",
"leftMode": "tree",
"source": "/api/mock2/form/departUser",
"deferApi": "/api/mock2/form/departUser?ref=${ref}&dep=${value}"
}
]
}
```
## searchApi
**发送**

View File

@ -0,0 +1,373 @@
---
title: GridNav 宫格导航
description:
type: 0
group: ⚙ 组件
menuName: GridNav 宫格导航
icon:
order: 54
---
宫格菜单导航,不支持配置初始化接口初始化数据域,所以需要搭配类似像`Service`、`Form`或`CRUD`这样的,具有配置接口初始化数据域功能的组件,或者手动进行数据域初始化,然后通过`source`属性,获取数据链中的数据,完成菜单展示。
## 基本用法
通过 source 关联上下文数据,或者通过 name 关联。
```schema
{
"type": "page",
"data": {
"items": [
{
"icon": "https://internal-amis-res.cdn.bcebos.com/images/icon-1.png",
"text": "导航1"
},
{
"icon": "https://internal-amis-res.cdn.bcebos.com/images/icon-1.png",
"text": "导航2"
},
{
"icon": "https://internal-amis-res.cdn.bcebos.com/images/icon-1.png",
"text": "导航3"
},
{
"icon": "https://internal-amis-res.cdn.bcebos.com/images/icon-1.png",
"text": "导航4"
}
]
},
"body": [
{
"type": "grid-nav",
"source": "${items}"
},
{
"type": "divider"
},
{
"type": "grid-nav",
"name": "items"
}
]
}
```
也可以静态展示,即不关联数据固定显示。
```schema
{
"type": "page",
"body": {
"type": "grid-nav",
"options": [
{
"icon": "https://internal-amis-res.cdn.bcebos.com/images/icon-1.png",
"text": "导航1"
},
{
"icon": "https://internal-amis-res.cdn.bcebos.com/images/icon-1.png",
"text": "导航2"
},
{
"icon": "https://internal-amis-res.cdn.bcebos.com/images/icon-1.png",
"text": "导航3"
},
{
"icon": "https://internal-amis-res.cdn.bcebos.com/images/icon-1.png",
"text": "导航4"
}
]
}
}
```
## 自定义列数
默认一行展示四个格子,可以通过 `columnNum` 自定义列数
```schema
{
"type": "page",
"data": {
"items": [
{
"icon": "https://internal-amis-res.cdn.bcebos.com/images/icon-1.png",
"text": "导航1"
},
{
"icon": "https://internal-amis-res.cdn.bcebos.com/images/icon-1.png",
"text": "导航2"
},
{
"icon": "https://internal-amis-res.cdn.bcebos.com/images/icon-1.png",
"text": "导航3"
}
]
},
"body": {
"type": "grid-nav",
"columnNum": 3,
"source": "${items}"
}
}
```
## 正方形格子
设置 `square` 属性后,格子的高度会和宽度保持一致。
```schema
{
"type": "page",
"data": {
"items": [
{
"icon": "https://internal-amis-res.cdn.bcebos.com/images/icon-1.png",
"text": "导航1"
},
{
"icon": "https://internal-amis-res.cdn.bcebos.com/images/icon-1.png",
"text": "导航2"
},
{
"icon": "https://internal-amis-res.cdn.bcebos.com/images/icon-1.png",
"text": "导航3"
},
{
"icon": "https://internal-amis-res.cdn.bcebos.com/images/icon-1.png",
"text": "导航4"
}
]
},
"body": {
"type": "grid-nav",
"source": "${items}",
"square": true
}
}
```
## 格子间距
通过 `gutter` 属性设置格子之间的距离。
```schema
{
"type": "page",
"data": {
"items": [
{
"icon": "https://internal-amis-res.cdn.bcebos.com/images/icon-1.png",
"text": "导航1"
},
{
"icon": "https://internal-amis-res.cdn.bcebos.com/images/icon-1.png",
"text": "导航2"
},
{
"icon": "https://internal-amis-res.cdn.bcebos.com/images/icon-1.png",
"text": "导航3"
},
{
"icon": "https://internal-amis-res.cdn.bcebos.com/images/icon-1.png",
"text": "导航4"
}
]
},
"body": {
"type": "grid-nav",
"source": "${items}",
"gutter": 20
}
}
```
## 内容横排
`direction` 属性设置为 `horizontal`,可以让宫格的内容呈横向排列。
```schema
{
"type": "page",
"data": {
"items": [
{
"icon": "https://internal-amis-res.cdn.bcebos.com/images/icon-1.png",
"text": "导航1"
},
{
"icon": "https://internal-amis-res.cdn.bcebos.com/images/icon-1.png",
"text": "导航2"
},
{
"icon": "https://internal-amis-res.cdn.bcebos.com/images/icon-1.png",
"text": "导航3"
},
{
"icon": "https://internal-amis-res.cdn.bcebos.com/images/icon-1.png",
"text": "导航4"
}
]
},
"body": {
"type": "grid-nav",
"direction": "horizontal",
"source": "${items}"
}
}
```
## 图标占比
设置 `iconRatio` 可以控制图标宽度占比,默认 60%,设置 1-100 的数字。
```schema
{
"type": "page",
"data": {
"items": [
{
"icon": "https://internal-amis-res.cdn.bcebos.com/images/icon-1.png",
"text": "导航1"
},
{
"icon": "https://internal-amis-res.cdn.bcebos.com/images/icon-1.png",
"text": "导航2"
},
{
"icon": "https://internal-amis-res.cdn.bcebos.com/images/icon-1.png",
"text": "导航3"
},
{
"icon": "https://internal-amis-res.cdn.bcebos.com/images/icon-1.png",
"text": "导航4"
}
]
},
"body": {
"type": "grid-nav",
"iconRatio": "40",
"source": "${items}"
}
}
```
## 角标提示
设置 badge 属性后,会在图标右上角展示相应的角标,支持红点、数字、彩带模式。
```schema
{
"type": "page",
"data": {
"items": [
{
"icon": "https://internal-amis-res.cdn.bcebos.com/images/icon-1.png",
"text": "导航1",
"badge": {
"mode": "text",
"text": "10"
}
},
{
"icon": "https://internal-amis-res.cdn.bcebos.com/images/icon-1.png",
"text": "导航2",
"badge": {
"mode": "dot"
}
},
{
"icon": "https://internal-amis-res.cdn.bcebos.com/images/icon-1.png",
"text": "导航3",
"badge": {
"mode": "ribbon",
"text": "热销"
}
},
{
"icon": "https://internal-amis-res.cdn.bcebos.com/images/icon-1.png",
"text": "导航4"
}
]
},
"body": {
"type": "grid-nav",
"source": "${items}",
"border": false
}
}
```
## 点击交互
设置 clickAction 属性支持通用点击交互,详见 [Action](./action) 配置
```schema
{
"type": "page",
"data": {
"items": [
{
"icon": "https://internal-amis-res.cdn.bcebos.com/images/icon-1.png",
"text": "外部跳转",
"link": "https://www.baidu.com",
"blank": true
},
{
"icon": "https://internal-amis-res.cdn.bcebos.com/images/icon-1.png",
"text": "弹框",
"clickAction": {
"actionType": "dialog",
"dialog": {
"title": "弹框",
"body": "这是个简单的弹框。"
}
}
},
{
"icon": "https://internal-amis-res.cdn.bcebos.com/images/icon-1.png",
"text": "内部跳转",
"link": "/docs/index"
},
{
"icon": "https://internal-amis-res.cdn.bcebos.com/images/icon-1.png",
"text": "导航4"
}
]
},
"body": {
"type": "grid-nav",
"source": "${items}",
"border": false
}
}
```
## 属性表
| 属性名 | 类型 | 默认值 | 说明 |
| ------------------- | --------------- | ---------- | -------------------------------------------------------- |
| type | `string` | `grid-nav` | |
| className | `string` | | 外层 CSS 类名 |
| itemClassName | `string` | | 列表项 css 类名 |
| value | `Array<object>` | | 图片数组 |
| source | `string` | | 数据源 |
| square | `boolean` | | 是否将列表项固定为正方形 |
| center | `boolean` | `true` | 是否将列表项内容居中显示 |
| border | `boolean` | `true` | 是否显示列表项边框 |
| gutter | `number` | | 列表项之间的间距,默认单位为`px` |
| reverse | `boolean` | | 是否调换图标和文本的位置 |
| iconRatio | `number` | 60 | 图标宽度占比,单位% |
| direction | `string` | `vertical` | 列表项内容排列的方向,可选值为 `horizontal` 、`vertical` |
| columnNum | `number` | 4 | 列数 |
| options.icon | `string` | | 列表项图标 |
| options.text | `string` | | 列表项文案 |
| options.badge | `BadgeSchema` | | 列表项角标,详见 [Badge](./badge) |
| options.link | `string` | | 内部页面路径或外部跳转 URL 地址,优先级高于 clickAction |
| options.blank | `boolean` | | 是否新页面打开link 为 url 时有效 |
| options.clickAction | `ActionSchema` | | 列表项点击交互 详见 [Action](./action) |
```
```

View File

@ -334,7 +334,7 @@ List 的内容、Card 卡片的内容配置同上
| title | `string` | | 标题 |
| imageCaption | `string` | | 描述 |
| placeholder | `string` | | 占位文本 |
| defaultImage | `string` | | 默认显示的图片地址 |
| defaultImage | `string` | | 无数据时显示的图片 |
| src | `string` | | 缩略图地址 |
| href | [模板](../../docs/concepts/template) | | 外部链接地址 |
| originalSrc | `string` | | 原图地址 |

View File

@ -15,7 +15,7 @@ order: 56
```schema: scope="body"
{
"type": "service",
"api": "/api/sample?perPage=5",
"api": "/api/mock2/sample?perPage=5",
"body": [
{
"type": "panel",
@ -84,7 +84,7 @@ order: 56
```schema: scope="body"
{
"type": "service",
"api": "/api/sample?perPage=5",
"api": "/api/mock2/sample?perPage=5",
"body": [
{
"type": "panel",

View File

@ -172,6 +172,31 @@ Page 默认将页面分为几个区域,分别是**内容区(`body`**、**
}
```
## aside 可调整宽度
通过配置 `asideResizor`,可以让侧边栏支持动态调整宽度,同时可以通过 `asideMinWidth`、`asideMaxWidth` 设置 aside 最大最小宽度。
```schema
{
"type": "page",
"asideResizor": true,
"asideMinWidth": 150,
"asideMaxWidth": 400,
"aside": [
{
"type": "tpl",
"tpl": "这是侧边栏部分"
}
],
"body": [
{
"type": "tpl",
"tpl": "这是内容区"
}
]
}
```
上面的配置会自动创建一个 `<style>` 标签,其中内容就是:
```css
@ -191,6 +216,9 @@ Page 默认将页面分为几个区域,分别是**内容区(`body`**、**
| subTitle | [SchemaNode](../../docs/types/schemanode) | | 页面副标题 |
| remark | [Remark](./remark) | | 标题附近会出现一个提示图标,鼠标放上去会提示该内容。 |
| aside | [SchemaNode](../../docs/types/schemanode) | | 往页面的边栏区域加内容 |
| asideResizor | `boolean` | | 页面的边栏区域宽度是否可调整 |
| asideMinWidth | `number` | | 页面边栏区域的最小宽度 |
| asideMaxWidth | `number` | | 页面边栏区域的最大宽度 |
| toolbar | [SchemaNode](../../docs/types/schemanode) | | 往页面的右上角加内容,需要注意的是,当有 title 时,该区域在右上角,没有时该区域在顶部 |
| body | [SchemaNode](../../docs/types/schemanode) | | 往页面的内容区域加内容 |
| className | `string` | | 外层 dom 类名 |

View File

@ -42,6 +42,24 @@ order: 60
"type": "progress",
"value": 60,
"map": ["bg-danger", "bg-success"]
},
{
"type": "divider"
},
{
"type": "progress",
"value": 60,
"map": ["bg-danger", "bg-success"],
"mode": "circle"
},
{
"type": "divider"
},
{
"type": "progress",
"value": 10,
"map": ["bg-danger", "bg-success"],
"mode": "circle"
}
]
}
@ -122,31 +140,106 @@ List 的内容、Card 卡片的内容配置同上
## 显示动画
需要同时开启显示背景间隔才能看出来
需要条形进度条才生效
```schema
{
"type": "page",
"body": [
{
"type": "progress",
"animate": true,
"value": 60
},
{
"type": "divider"
},
{
"type": "progress",
"animate": true,
"value": 60,
"stripe": true
}
]
}
```
## 圆形进度条
```schema
{
"type": "page",
"body": {
"type": "progress",
"stripe": true,
"animate": true,
"value": 60
"value": 60,
"mode": "circle"
}
}
```
## 仪表盘进度条
可设置缺口位置和缺口角度
```schema
{
"type": "page",
"body": {
"type": "progress",
"value": 60,
"mode": "dashboard",
"gapDegree": 22,
"gapPosition": "bottom"
}
}
```
## 圆形进度条设置线条宽度
可设置 strokeWidth 调整线条宽度
```schema
{
"type": "page",
"body": {
"type": "progress",
"value": 60,
"mode": "dashboard",
"strokeWidth": 3
}
}
```
## 自定义格式输出内容
```schema
{
"type": "page",
"body": {
"type": "progress",
"mode": "circle",
"value": 60,
"valueTpl": "${value}个"
}
}
```
## 属性表
| 属性名 | 类型 | 默认值 | 说明 |
| -------------------- | --------------- | -------------------------------------------------------------------- | -------------------------------------------------------------------------------------- |
| type | `string` | | 如果在 Table、Card 和 List 中,为`"color"`;在 Form 中用作静态展示,为`"static-color"` |
| className | `string` | | 外层 CSS 类名 |
| progressClassName | `string` | `progress-xs progress-striped active m-b-none` | 进度调 CSS 类名 |
| progressBarClassName | `string` | | 完成进度条 CSS 类名 |
| value | `string` | | 进度值 |
| placeholder | `string` | `-` | 占位文本 |
| showLabel | `boolean` | `true` | 是否展示进度文本 |
| strip | `boolean` | ` false` | 背景是否显示条纹 |
| animate | `boolean` | `false` | 背景条纹是否有动画 |
| map | `Array<string>` | `['bg-danger', 'bg-warning', 'bg-info', 'bg-success', 'bg-success']` | 进度颜色映射 |
| 属性名 | 类型 | 默认值 | 说明 |
| -------------------- | --------------- | -------------------------------------------------------------------- | ------------------------------------------------- |
| type | `string` | | 如果在 Form 中用作静态展示,为`"static-progress"` |
| mode | `string` | `line` | 进度「条」的类型,可选`line circle dashboard` |
| className | `string` | | 外层 CSS 类名 |
| progressClassName | `string` | `progress-xs progress-striped active m-b-none` | 进度条 CSS 类名 |
| progressBarClassName | `string` | | 完成进度条 CSS 类名 |
| value | `string` | | 进度值 |
| placeholder | `string` | `-` | 占位文本 |
| showLabel | `boolean` | `true` | 是否展示进度文本 |
| stripe | `boolean` | `false` | 背景是否显示条纹 |
| animate | `boolean` | `false` | type 为 line可支持动画 |
| map | `Array<string>` | `['bg-danger', 'bg-warning', 'bg-info', 'bg-success', 'bg-success']` | 进度颜色映射 |
| valueTpl | `string` | `${value}%` | 自定义格式化内容 |
| strokeWidth | `number` | `6` | 圆形进度条线宽度 |
| gapDegree | `number` | `75` | 仪表盘缺角角度,可取值 0 ~ 295 |
| gapPosition | `string` | `bottom` | 仪表盘进度条缺口位置,可选`top bottom left right` |

View File

@ -55,7 +55,7 @@ icon:
```schema: scope="body"
{
"type": "crud",
"api": "/api/sample",
"api": "/api/mock2/sample",
"syncLocation": false,
"headerToolbar": [
{

View File

@ -216,6 +216,32 @@ amis 中部分组件,作为展示组件,自身没有**使用接口初始化
它将`data`返回的对象作为 amis 页面配置,进行了解析渲染,实现动态渲染页面的功能。
`schemaApi` 同样支持 `jsonp` 请求,完整用法请参考 amis-admin 项目。
```schema: scope="body"
{
"type": "service",
"schemaApi": "jsonp:/api/mock2/service/jsonp"
}
```
`schemaApi接口` 返回的内容其实是一段立即执行的 js 代码。我们可以通过 `callback` 参数执行函数名,或者通过 `request._callback` 获取
```js
(function () {
window.axiosJsonpCallbackxxxx &&
window.axiosJsonpCallbackxxxx({
status: 0,
msg: '',
data: {
type: 'page',
title: 'jsonp 示例',
body: 'this is tpl from jsonp'
}
});
})();
```
## 动态渲染表单项
默认 Service 可以通过配置`schemaApi` [动态渲染页面内容](../service#%E5%8A%A8%E6%80%81%E6%B8%B2%E6%9F%93%E9%A1%B5%E9%9D%A2),但是如果想渲染表单项,请返回下面这种格式:

View File

@ -15,7 +15,7 @@ order: 67
```schema: scope="body"
{
"type": "service",
"api": "/api/sample?perPage=5",
"api": "/api/mock2/sample?perPage=5",
"body": [
{
"type": "table",
@ -309,7 +309,7 @@ order: 67
```schema: scope="body"
{
"type": "crud",
"api": "/api/sample?waitSeconds=1",
"api": "/api/mock2/sample?waitSeconds=1",
"columns": [
{
"name": "id",
@ -331,7 +331,7 @@ order: 67
```schema: scope="body"
{
"type": "crud",
"api": "/api/sample?waitSeconds=1",
"api": "/api/mock2/sample?waitSeconds=1",
"columns": [
{
"name": "id",
@ -355,7 +355,7 @@ order: 67
```schema: scope="body"
{
"type": "service",
"api": "/api/sample?perPage=5",
"api": "/api/mock2/sample?perPage=5",
"body": [
{
"type": "table",
@ -387,7 +387,7 @@ order: 67
```schema: scope="body"
{
"type": "service",
"api": "/api/sample?perPage=5",
"api": "/api/mock2/sample?perPage=5",
"body": [
{
"type": "table",
@ -414,7 +414,7 @@ order: 67
```schema: scope="body"
{
"type": "service",
"api": "/api/sample?perPage=5",
"api": "/api/mock2/sample?perPage=5",
"body": [
{
"type": "table",
@ -448,7 +448,7 @@ order: 67
```schema: scope="body"
{
"type": "service",
"api": "/api/sample?perPage=5",
"api": "/api/mock2/sample?perPage=5",
"body": [
{
"type": "table",
@ -481,7 +481,7 @@ order: 67
```schema: scope="body"
{
"type": "service",
"api": "/api/sample?perPage=5",
"api": "/api/mock2/sample?perPage=5",
"body": [
{
"type": "table",
@ -531,7 +531,7 @@ order: 67
```schema: scope="body"
{
"type": "service",
"api": "/api/sample?perPage=5",
"api": "/api/mock2/sample?perPage=5",
"className": "w-xxl",
"body": [
{
@ -584,7 +584,7 @@ order: 67
```schema: scope="body"
{
"type": "crud",
"api": "/api/sample?waitSeconds=1",
"api": "/api/mock2/sample?waitSeconds=1",
"columns": [
{
"name": "id",
@ -604,7 +604,7 @@ order: 67
```schema: scope="body"
{
"type": "crud",
"api": "/api/sample?waitSeconds=1",
"api": "/api/mock2/sample?waitSeconds=1",
"columns": [
{
"name": "id",
@ -628,7 +628,7 @@ order: 67
```schema: scope="body"
{
"type": "crud",
"api": "/api/sample?waitSeconds=1",
"api": "/api/mock2/sample?waitSeconds=1",
"columns": [
{
"name": "id",
@ -653,7 +653,7 @@ order: 67
```schema: scope="body"
{
"type": "crud",
"api": "/api/sample?waitSeconds=1",
"api": "/api/mock2/sample?waitSeconds=1",
"columns": [
{
"name": "id",
@ -685,7 +685,7 @@ order: 67
```schema: scope="body"
{
"type": "crud",
"api": "/api/sample?waitSeconds=1",
"api": "/api/mock2/sample?waitSeconds=1",
"columns": [
{
"name": "id",
@ -758,7 +758,7 @@ order: 67
```schema: scope="body"
{
"type": "crud",
"api": "/api/sample?waitSeconds=1",
"api": "/api/mock2/sample?waitSeconds=1",
"affixHeader": false,
"combineNum": 1,
"columns": [
@ -792,7 +792,7 @@ order: 67
```schema: scope="body"
{
"type": "crud",
"api": "/api/sample?waitSeconds=1",
"api": "/api/mock2/sample?waitSeconds=1",
"columns": [
{
"name": "id",
@ -1005,7 +1005,7 @@ order: 67
```schema: scope="body"
{
"type": "service",
"api": "/api/sample?perPage=5",
"api": "/api/mock2/sample?perPage=5",
"body": [
{
"type": "table",
@ -1303,7 +1303,7 @@ order: 67
```schema: scope="body"
{
"type": "service",
"api": "/api/sample?perPage=5",
"api": "/api/mock2/sample?perPage=5",
"body": [
{
"type": "table",
@ -1361,7 +1361,7 @@ order: 67
```schema: scope="body"
{
"type": "service",
"api": "/api/sample?perPage=10",
"api": "/api/mock2/sample?perPage=10",
"body": [
{
"type": "table",
@ -1410,7 +1410,7 @@ order: 67
```schema: scope="body"
{
"type": "service",
"api": "/api/sample?perPage=10",
"api": "/api/mock2/sample?perPage=10",
"body": [
{
"type": "table",
@ -1448,7 +1448,7 @@ order: 67
```schema: scope="body"
{
"type": "service",
"api": "/api/sample?perPage=10",
"api": "/api/mock2/sample?perPage=10",
"body": [
{
"type": "table",
@ -1494,7 +1494,7 @@ order: 67
```schema: scope="body"
{
"type": "service",
"api": "/api/sample?perPage=10",
"api": "/api/mock2/sample?perPage=10",
"body": [
{
"type": "table",
@ -1544,7 +1544,7 @@ order: 67
```schema: scope="body"
{
"type": "service",
"api": "/api/sample?perPage=10",
"api": "/api/mock2/sample?perPage=10",
"body": [{
"type": "table",
"source": "$rows",
@ -1586,7 +1586,7 @@ order: 67
```schema: scope="body"
{
"type": "service",
"api": "/api/sample?perPage=10",
"api": "/api/mock2/sample?perPage=10",
"body": [{
"type": "table",
"source": "$rows",

View File

@ -376,6 +376,8 @@ order: 12
## 过滤器
1.5.0 开始,更推荐用函数调用的语法来写,如 `${xxx | html}` 改用 `${html(xxx)}`。具体请查看[新表达式语法](./expression#新表达式语法)
过滤器是对数据映射的一种增强,它的作用是对获取数据做一些处理,基本用法如下:
```
@ -1371,6 +1373,33 @@ ${xxx | upperCase}
}
```
### substring
> 1.5.0 及以上版本
取字符串的一部分,第一个参数是起始,第二个参数的结束
##### 基本用法
下面写法将会取前两个字符
```
${xxx | substring:0:2}
```
```schema
{
"type": "page",
"data": {
"text": "World"
},
"body": {
"type": "tpl",
"tpl": "Hello ${text|substring:0:2}"
}
}
```
### base64Encode
base64 加密

View File

@ -35,3 +35,60 @@ order: 13
组件不同的配置项会有不同的效果,请大家在组件文档中多留意。
> 表达式的执行结果预期应该是`boolean`类型值如果不是amis 会根据 JavaScript 的规则将结果视作`boolean`类型进行判断
## 新表达式语法
> 1.5.0 及以上版本
原来的表达式用的就是原生 js灵活性虽大但是安全性不佳为了与后端公式保持统一故引入了新的规则`${这里是表达式}`,也就是说如果开始字符是 `${``}` 结尾则认为是新版本的表达式。这个规则与模板中的语法保持一致。
- `${a == 1}` 变量 a 是否和 1 相等
- `${a % 2}` 变量 a 是否为偶数。
表达式中的语法与默认模板中的语法保持一致,所以以下示例直接用模板来方便呈现结果。
```schema
{
"type": "page",
"data": {
"a": 1,
"key": "y",
"obj": {
"x": 2,
"y": 3
},
"arr": [1, 2, 3]
},
"body": [
"a is ${a} <br />",
"a + 1 is ${a + 1} <br />",
"obj.x is ${obj.x} <br />",
"obj['x'] is ${obj['x']} <br />",
"obj[key] is ${obj[key]} <br />",
"arr[0] is ${arr[0]} <br />",
"arr[a] is ${arr[a]} <br />",
"arr[a + 1] is ${arr[a + 1]} <br />",
]
}
```
### 公式
除了支持简单表达式外,还集成了很多公式(函数)如:
```schema
{
"type": "page",
"data": {
"a": "",
"语文成绩": 81
},
"body": [
"1, 2, 3, 4 的平均数位 ${ AVG(1, 2, 3, 4)}",
"当前成绩:${IF(语文成绩 > 80, '优秀', '继续努力')}"
]
}
```
!!!include(amis-formula/dist/doc.md)!!!

View File

@ -49,14 +49,14 @@ order: 14
"name": "text1",
"label": false,
"placeholder": "选中 类型1 时可见",
"visibleOn": "this.foo == 1"
"visibleOn": "${foo == 1}"
},
{
"type": "input-text",
"name": "text2",
"label": false,
"placeholder": "选中 类型2 时不可点",
"disabledOn": "this.foo == 2"
"disabledOn": "${foo == 2}"
}
]
}
@ -259,7 +259,7 @@ order: 14
},
{
"type": "crud",
"api": "/api/sample",
"api": "/api/mock2/sample",
"columns": [
{
"name": "id",
@ -306,7 +306,7 @@ order: 14
{
"type": "crud",
"name": "my_crud",
"api": "/api/sample",
"api": "/api/mock2/sample",
"columns": [
{
"name": "id",

View File

@ -243,7 +243,7 @@ Page
}, {
"type": "crud",
"className": "m-t-sm",
"api": "/api/sample",
"api": "/api/mock2/sample",
"columns": [{
"name": "id",
"label": "ID"

View File

@ -53,6 +53,19 @@ order: 11
}
```
### 表达式
> 1.5.0 及以上版本
支持简单的表达式运算以及公式调用,具体请查看[新表达式语法](./expression#新表达式语法)
```json
{
"type": "tpl",
"tpl": "${xxx == 1 ? 'One' : 'Others'}"
}
```
## JavaScript 模板引擎
amis 还支持用 JavaScript 模板引擎进行组织输出,内部采用 [lodash template](https://lodash.com/docs/4.17.15#template) 进行实现。
@ -101,7 +114,7 @@ amis 还支持用 JavaScript 模板引擎进行组织输出,内部采用 [loda
## 注意事项
#### 1. 模板字符串 和 模板引擎 不可以交叉使用
#### 1. 模板字符串 和 Javascript 模板引擎 不可以交叉使用
例如:
@ -111,3 +124,11 @@ amis 还支持用 JavaScript 模板引擎进行组织输出,内部采用 [loda
"tpl": "${data.xxx === 'a'}" //错误!
}
```
```json
{
"type": "tpl",
"tpl": "${xxx === 'a'}" // 正确
}
```

View File

@ -101,9 +101,44 @@ import 'amis/lib/locale/en-US';
}
```
## 全局文本替换
> 1.5.0 及以上版本
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"
{

View File

@ -138,7 +138,7 @@ api 的来源有两方面,一个是各种组件的 api 及 source 配置,另
"eventType": "api",
"eventData": {
"method": "get",
"url": "/api/sample?page=1&perPage=10",
"url": "/api/mock2/sample?page=1&perPage=10",
"query": {
"page": 1,
"perPage": 10

View File

@ -29,7 +29,7 @@ amis 是一个低代码前端框架,它使用 JSON 配置来生成页面,可
"type": "crud",
"draggable": true,
"syncLocation": false,
"api": "/api/sample",
"api": "/api/mock2/sample",
"keepItemSelectionOnPageChange": true,
"autoGenerateFilter": true,
"bulkActions": [
@ -37,7 +37,7 @@ amis 是一个低代码前端框架,它使用 JSON 配置来生成页面,可
"type": "button",
"label": "批量删除",
"actionType": "ajax",
"api": "delete:/api/sample/${ids|raw}",
"api": "delete:/api/mock2/sample/${ids|raw}",
"confirmText": "确定要批量删除?"
},
{
@ -49,7 +49,7 @@ amis 是一个低代码前端框架,它使用 JSON 配置来生成页面,可
"name": "sample-bulk-edit",
"body": {
"type": "form",
"api": "/api/sample/bulkUpdate2",
"api": "/api/mock2/sample/bulkUpdate2",
"body": [
{
"type": "hidden",
@ -65,8 +65,8 @@ amis 是一个低代码前端框架,它使用 JSON 配置来生成页面,可
}
}
],
"quickSaveApi": "/api/sample/bulkUpdate",
"quickSaveItemApi": "/api/sample/$id",
"quickSaveApi": "/api/mock2/sample/bulkUpdate",
"quickSaveItemApi": "/api/mock2/sample/$id",
"headerToolbar": [
"bulkActions",
{
@ -74,7 +74,7 @@ amis 是一个低代码前端框架,它使用 JSON 配置来生成页面,可
"label": "重置测试数据",
"actionType": "ajax",
"size": "sm",
"api": "/api/sample/reset"
"api": "/api/mock2/sample/reset"
},
"export-excel",
{
@ -160,7 +160,7 @@ amis 是一个低代码前端框架,它使用 JSON 配置来生成页面,可
"actionType": "ajax",
"label": "删除",
"confirmText": "您确认要删除?",
"api": "delete:/api/sample/$id"
"api": "delete:/api/mock2/sample/$id"
}
]
}

View File

@ -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({

View File

@ -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
```
如果发现有字段被意外替换了,可以通过设置这个属性来避免

View File

@ -102,6 +102,19 @@ API 类型用于配置请求接口的格式,涉及请求方式、请求地址
但这种方式无法显示错误信息,只能通过返回 http 状态码来标识错误。
### 配置弹框时间
可以通过 `msgTimeout` 控制弹框时间,它的时间是毫秒
```json
{
"status": 2,
"msg": "error",
"msgTimeout": 10000,
"data": {}
}
```
## 复杂配置
API 还支持配置对象类型
@ -437,7 +450,7 @@ API 还支持配置对象类型
```schema: scope="body"
{
"type": "crud",
"api": "/api/sample?waitSeconds=1",
"api": "/api/mock2/sample?waitSeconds=1",
"columns": [
{
"name": "id",
@ -463,7 +476,7 @@ API 还支持配置对象类型
```schema: scope="body"
{
"type": "crud",
"api": "/api/sample?waitSeconds=1",
"api": "/api/mock2/sample?waitSeconds=1",
"columns": [
{
"name": "id",

View File

@ -146,16 +146,16 @@ export class App extends React.PureComponent<{
location: Location;
}> {
state = {
viewMode: localStorage.getItem('viewMode') || 'pc',
viewMode: localStorage.getItem('amis-viewMode') || 'pc',
offScreen: false,
folded: false,
headerVisible: true,
themes: themes,
theme:
themes.find(item => item?.value === localStorage.getItem('theme')) ||
themes.find(item => item?.value === localStorage.getItem('amis-theme')) ||
themes[0],
locale: localStorage.getItem('locale')
? localStorage.getItem('locale').replace('zh-cn', 'zh-CN')
locale: localStorage.getItem('amis-locale')
? localStorage.getItem('amis-locale').replace('zh-cn', 'zh-CN')
: '',
navigations: [],
filter: '' // 导航过滤,方便找组件
@ -308,7 +308,7 @@ export class App extends React.PureComponent<{
options={locales}
onChange={locale => {
this.setState({locale: locale.value});
localStorage.setItem('locale', locale.value);
localStorage.setItem('amis-locale', locale.value);
window.location.reload();
}}
/>
@ -323,7 +323,7 @@ export class App extends React.PureComponent<{
options={this.state.themes}
onChange={theme => {
this.setState({theme});
localStorage.setItem('theme', `${theme.value}`);
localStorage.setItem('amis-theme', `${theme.value}`);
document
.querySelector('body')
.classList[theme.value === 'dark' ? 'add' : 'remove']('dark');
@ -340,7 +340,7 @@ export class App extends React.PureComponent<{
options={viewModes}
onChange={viewMode => {
this.setState({viewMode: viewMode.value});
localStorage.setItem('viewMode', viewMode.value);
localStorage.setItem('amis-viewMode', viewMode.value);
window.location.reload();
}}
/>

View File

@ -854,6 +854,14 @@ export const components = [
makeMarkdownRenderer
)
},
{
label: 'GridNav 宫格导航',
path: '/zh-CN/components/grid-nav',
getComponent: () =>
import('../../docs/zh-CN/components/grid-nav.md').then(
makeMarkdownRenderer
)
},
{
label: 'Json',
path: '/zh-CN/components/json',

View File

@ -213,8 +213,10 @@ export default class PlayGround extends React.Component {
return response;
},
isCancel: value => axios.isCancel(value),
notify: (type, msg) =>
toast[type] ? toast[type](msg) : console.warn('[Notify]', type, msg),
notify: (type, msg, conf) =>
toast[type]
? toast[type](msg, conf)
: console.warn('[Notify]', type, msg),
alert,
confirm,
copy: (content, options) => {
@ -223,6 +225,9 @@ export default class PlayGround extends React.Component {
},
tracker(eventTrack) {
console.debug('eventTrack', eventTrack);
},
replaceText: {
AMIS_HOST: 'https://baidu.gitee.io/amis'
}
};
@ -319,7 +324,8 @@ export default class PlayGround extends React.Component {
theme: this.props.theme,
locale: this.props.locale,
affixHeader: false,
affixFooter: false
affixFooter: false,
useMobileUI: true
};
if (this.props.viewMode === 'mobile') {

View File

@ -18,7 +18,7 @@ function loadEditor() {
);
}
const viewMode = localStorage.getItem('viewMode') || 'pc';
const viewMode = localStorage.getItem('amis-viewMode') || 'pc';
export default function (schema, showCode, envOverrides) {
if (!schema['$schema']) {

View File

@ -31,7 +31,7 @@
}
</style>
<script type="text/x-jsx">
let theme = localStorage.getItem('theme') || 'cxd';
let theme = localStorage.getItem('amis-theme') || 'cxd';
if (theme === 'default') {
theme = 'cxd';
}

View File

@ -71,7 +71,7 @@
document.querySelectorAll('link[title]').forEach(item => {
item.disabled = true;
});
let theme = localStorage.getItem('theme') || 'cxd';
let theme = localStorage.getItem('amis-theme') || 'cxd';
if (theme == 'default') {
theme = 'cxd';
}

View File

@ -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';

View File

@ -840,6 +840,7 @@ body {
top: 100px;
bottom: 0;
overflow-y: auto;
overflow-x: hidden;
border-right: 1px solid #e8ebee;
}
}
@ -1050,5 +1051,18 @@ body.dark {
&-code {
border-left: 1px solid #ddd;
width: 350px;
flex-shrink: 0;
}
}
@media screen and (max-width: 767px) {
.Playgroud {
flex-direction: column;
&-code {
border-left: none;
width: auto;
height: 200px;
}
}
}

View File

@ -215,7 +215,7 @@ fis.match('*.html:jsx', {
// 这些用了 esm
fis.match(
'{echarts/extension/**.js,zrender/**.js,ansi-to-react/lib/index.js}',
'{echarts/extension/**.js,zrender/**.js,ansi-to-react/lib/index.js,markdown-it-html5-media/**.js}',
{
parser: fis.plugin('typescript', {
sourceMap: false,

View File

@ -24,7 +24,7 @@ function mockRequest(event, context) {
method: 'GET',
body: body,
url: `/api/${event.pathParameters.subpath}`,
originalUrl: '',
originalUrl: ''
};
}
@ -34,9 +34,20 @@ function mockResponse(event, context, callback) {
callback(null, {
statusCode: 200,
headers: createHeaders(event.headers),
body: JSON.stringify(json),
body: JSON.stringify(json)
});
},
send(res) {
callback(null, {
statusCode: 200,
headers: {
...createHeaders(event.headers),
'Content-Type': 'text/javascript'
},
json: false,
body: res
});
}
};
}
@ -52,6 +63,6 @@ function createHeaders(headers) {
'Access-Control-Allow-Headers': 'x-requested-with,content-type',
'Access-Control-Allow-Methods': 'GET,POST,PUT,DELETE,OPTIONS,HEAD',
'Access-Control-Allow-Credentials': 'true',
'Access-Control-Allow-Origin': referer ? `${referer}` : '*',
'Access-Control-Allow-Origin': referer ? `${referer}` : '*'
};
}

110
mock/cfc/mock/crud/tree.js Normal file
View File

@ -0,0 +1,110 @@
const data = [
{
"engine": "Trident - afurms",
"browser": "Internet Explorer 4.0",
"platform": "Win 95+",
"version": "4",
"grade": "X",
"id": 1,
"children": [
{
"engine": "Trident - f7006",
"browser": "Internet Explorer 5.0",
"platform": "Win 95+",
"version": "5",
"grade": "C",
"id": 2
},
{
"engine": "Trident - t6r3s4",
"browser": "Internet Explorer 5.5",
"platform": "Win 95+",
"version": "5.5",
"grade": "A",
"id": 3
},
{
"engine": "Trident - 3a99nb",
"browser": "Internet Explorer 6",
"platform": "Win 98+",
"version": "6",
"grade": "A",
"id": 4
}
]
},
{
"engine": "Trident - plb6cd",
"browser": "Internet Explorer 7",
"platform": "Win XP SP2+",
"version": "7",
"grade": "A",
"id": 5,
"children": [
{
"engine": "Trident - dpgbw",
"browser": "AOL browser (AOL desktop)",
"platform": "Win XP",
"version": "6",
"grade": "A",
"id": 6
}
]
},
{
"engine": "Gecko - syo6k7",
"browser": "Firefox 1.0",
"platform": "Win 98+ / OSX.2+",
"version": "1.7",
"grade": "A",
"id": 7
},
{
"engine": "Gecko - xha3vk",
"browser": "Firefox 1.5",
"platform": "Win 98+ / OSX.2+",
"version": "1.8",
"grade": "A",
"id": 8
},
{
"engine": "Gecko - wc71bb",
"browser": "Firefox 2.0",
"platform": "Win 98+ / OSX.2+",
"version": "1.8",
"grade": "A",
"id": 9
},
{
"engine": "Gecko - xfqpti",
"browser": "Firefox 3.0",
"platform": "Win 2k+ / OSX.3+",
"version": "1.9",
"grade": "A",
"id": 10
}
];
module.exports = function (req, res) {
const perPage = 10;
const page = req.query.page || 1;
let items = data.concat();
if (req.query.keywords) {
const keywords = req.query.keywords;
items = items.filter(function (item) {
return ~JSON.stringify(item).indexOf(keywords);
});
}
const ret = {
status: 0,
msg: 'ok',
data: {
count: items.length,
rows: items.concat().splice((page - 1) * perPage, perPage)
}
};
res.json(ret);
};

View File

@ -0,0 +1,205 @@
const departments = [
{
label: '总公司',
value: 1,
children: [
{
label: '部门 A',
value: 2
},
{
label: '部门 B',
value: 3,
children: [
{
label: '子部门1',
value: 5
},
{
label: '子部门2',
value: 6
}
]
},
{
label: '部门 C',
value: 4
}
]
}
];
function findTree(tree, iterator) {
function find(list) {
if (Array.isArray(list)) {
for (let i = 0, len = list.length; i < len; i++) {
const item = list[i];
if (iterator(item)) {
return item;
} else if (Array.isArray(item.children)) {
const result = find(item.children);
if (result) {
return result;
}
}
}
}
return null;
}
return find(tree, iterator);
}
const users = {
1: [
{
label: '用户 1',
value: 'user1'
},
{
label: '用户 2',
value: 'user2'
}
],
2: [
{
label: '用户 a',
value: 'usera'
},
{
label: '用户 b',
value: 'userb'
}
],
3: [
{
label: '用户 x',
value: 'userx'
},
{
label: '用户 y',
value: 'usery'
}
],
4: [
{
label: '用户 I',
value: 'useri'
},
{
label: '用户 II',
value: 'userii'
}
],
5: [
{
label: '用户一',
value: 'useryi'
},
{
label: '用户二',
value: 'userer'
}
],
6: [
{
label: '用户k',
value: 'userk'
},
{
label: '用户j',
value: 'userj'
}
]
};
module.exports = function (req, res) {
if (req.query.ref) {
const options = users[req.query.ref];
res.json({
status: 0,
msg: '',
data: {
options
}
});
return;
} else if (req.query.dep) {
const resolved = findTree(departments, item => item.value == req.query.dep);
res.json({
status: 0,
msg: '',
data: {
options: resolved
? resolved.children.map(item => {
item = {
...item
};
if (item.children) {
item.defer = true;
delete item.children;
}
return item;
})
: []
}
});
return;
}
res.json({
status: 0,
msg: '',
data: {
options: [
{
leftOptions: departments.map(item => {
item = {
...item
};
if (item.children) {
item.defer = true;
delete item.children;
}
return item;
}),
children: [
{
ref: 1,
defer: true
},
{
ref: 2,
defer: true
},
{
ref: 3,
defer: true
},
{
ref: 4,
defer: true
},
{
ref: 5,
defer: true
},
{
ref: 6,
defer: true
}
]
}
]
}
});
};

View File

@ -1,8 +1,9 @@
{
"status": 404,
"msg": "测试返回错误",
"data": {
"title": "Test Page Component",
"date": "2017-10-13"
}
"status": 404,
"msg": "测试返回错误",
"msgTimeout": 10000,
"data": {
"title": "Test Page Component",
"date": "2017-10-13"
}
}

View File

@ -0,0 +1,20 @@
module.exports = function (req, res) {
const callback = req.query._callback;
const responseData = {
status: 0,
msg: '',
data: {
type: 'page',
title: 'jsonp 示例',
body: 'this is tpl from jsonp'
}
};
const js = `
(function() {
window.${callback} && window.${callback}(${JSON.stringify(responseData)})
})();
`;
return res.send(js);
};

View File

@ -25,7 +25,11 @@ module.exports = function (req, res) {
res.status(500).json({status: 500, msg: err});
} else {
res.set(result.headers);
res.status(result.statusCode).json(JSON.parse(result.body));
if (!result.json) {
res.status(result.statusCode).send(result.body);
} else {
res.status(result.statusCode).json(JSON.parse(result.body));
}
}
});
};

View File

@ -1,6 +1,6 @@
{
"name": "amis",
"version": "1.4.2-beta.1",
"version": "1.5.3",
"description": "一种MIS页面生成工具",
"main": "lib/index.js",
"scripts": {
@ -38,13 +38,14 @@
}
],
"dependencies": {
"amis-formula": "^1.2.7",
"ansi-to-react": "^6.1.6",
"async": "2.6.0",
"attr-accept": "2.2.2",
"blueimp-canvastoblob": "2.1.0",
"classnames": "2.3.1",
"downshift": "6.1.7",
"echarts": "5.2.1",
"echarts": "5.2.2",
"echarts-stat": "^1.2.0",
"exceljs": "^4.3.0",
"file-saver": "^2.0.2",
@ -53,23 +54,24 @@
"hoist-non-react-statics": "^3.3.2",
"hotkeys-js": "^3.8.7",
"immutability-helper": "^3.1.1",
"keycode": "^2.1.9",
"keycode": "^2.2.1",
"lodash": "^4.17.15",
"markdown-it": "^12.0.6",
"markdown-it-html5-media": "^0.6.0",
"markdown-it-html5-media": "^0.7.0",
"match-sorter": "^6.3.1",
"mobx": "^4.5.0",
"mobx-react": "^6.1.4",
"mobx-state-tree": "^3.17.3",
"moment": "^2.19.3",
"monaco-editor": "0.28.1",
"mpegts.js": "^1.6.9",
"monaco-editor": "0.30.1",
"mpegts.js": "^1.6.10",
"papaparse": "^5.3.0",
"prop-types": "^15.6.1",
"punycode": "^2.1.1",
"qrcode.react": "^1.0.1",
"qs": "6.5.1",
"rc-input-number": "^7.3.3",
"rc-input-number": "^7.3.4",
"rc-progress": "^3.1.4",
"react": "^16.8.6",
"react-color": "^2.19.3",
"react-cropper": "^2.1.8",
@ -83,7 +85,7 @@
"react-transition-group": "4.4.2",
"react-visibility-sensor": "5.1.1",
"sortablejs": "1.14.0",
"tinymce": "^5.9.2",
"tinymce": "^5.10.2",
"tslib": "^2.3.1",
"uncontrollable": "7.2.1",
"video-react": "0.14.1"
@ -119,7 +121,7 @@
"bce-sdk-js": "^0.2.9",
"concurrently": "^6.2.2",
"copy-to-clipboard": "3.3.1",
"core-js": "^3.18.1",
"core-js": "^3.19.2",
"css": "3.0.0",
"faker": "^5.5.3",
"fis-optimizer-terser": "^1.0.1",
@ -139,8 +141,8 @@
"fs-walk": "0.0.2",
"glob": "^7.2.0",
"history": "^4.7.2",
"husky": "^7.0.0",
"jest": "^27.2.1",
"husky": "^7.0.4",
"jest": "^27.4.2",
"jest-canvas-mock": "^2.3.0",
"js-yaml": "^4.1.0",
"json5": "^2.2.0",
@ -194,7 +196,8 @@
"lib": [
"es6",
"dom",
"ES2015"
"ES2015",
"ES2021"
],
"sourceMap": true,
"jsx": "react",

View File

@ -140,51 +140,62 @@ module.exports = function (content, file) {
const placeholder = {};
let index = 1;
content = content.replace(
/```(schema|html)(?::(.*?))?\n([\s\S]*?)```/g,
function (_, lang, attr, code) {
const setting = {};
attr &&
attr.split(/\s+/).forEach(function (item) {
var parts = item.split('=');
content = content
.replace(/\!\!\!include\s*\(([^\)]+?)\)\!\!\!/g, (_, val) => {
const result = fis.project.lookup(val, file);
if (parts[1] && /^('|").*\1/.test(parts[1])) {
parts[1] = parts[1].substring(1, parts[1].length - 1);
}
setting[parts[0]] = parts[1] ? decodeURIComponent(parts[1]) : '';
if (parts[0] === 'height') {
setting.height = parseInt(setting.height, 10) /*编辑器的高度*/;
attr = attr.replace(item, `height="${setting.height}"`);
}
});
// placeholder[index] = `<iframe class="doc-iframe" width="100%" height="${setting.height || 200}px" frameBorder="0" src="/play?code=${encodeURIComponent(code)}&scope=${encodeURIComponent(setting.scope)}"></iframe>`;
if (lang === 'html') {
if (~code.indexOf('<html') || ~code.indexOf('<link')) {
return _;
}
placeholder[
index
] = `<!--amis-preview-start--><div class="amis-doc"><div class="preview">${code}</div><pre><code class="lang-html">${prism.highlight(
code
.replace(/"data:(\w+\/\w+);.*?"/g, '"data:$1; ..."')
.replace(/<svg([^>]*)>[\s\S]*?<\/svg>/g, '<svg$1>...</svg>')
.replace(/class="([^"]*?)\.\.\.([^"]*?)"/g, 'class="$1..."'),
prism.languages[lang],
lang
)}</code></pre></div><!--amis-preview-end-->`;
} else {
placeholder[
index
] = `<!--amis-preview-start--><div class="amis-preview" style="min-height: ${setting.height}px"><script type="text/schema" ${attr}>${code}</script></div><!--amis-preview-end-->`;
if (result) {
// 暂时不支持嵌套 include
return result.file.getContent();
}
return `[[${index++}]]`;
}
);
return _;
})
.replace(
/```(schema|html)(?::(.*?))?\n([\s\S]*?)```/g,
function (_, lang, attr, code) {
const setting = {};
attr &&
attr.split(/\s+/).forEach(function (item) {
var parts = item.split('=');
if (parts[1] && /^('|").*\1/.test(parts[1])) {
parts[1] = parts[1].substring(1, parts[1].length - 1);
}
setting[parts[0]] = parts[1] ? decodeURIComponent(parts[1]) : '';
if (parts[0] === 'height') {
setting.height = parseInt(setting.height, 10) /*编辑器的高度*/;
attr = attr.replace(item, `height="${setting.height}"`);
}
});
// placeholder[index] = `<iframe class="doc-iframe" width="100%" height="${setting.height || 200}px" frameBorder="0" src="/play?code=${encodeURIComponent(code)}&scope=${encodeURIComponent(setting.scope)}"></iframe>`;
if (lang === 'html') {
if (~code.indexOf('<html') || ~code.indexOf('<link')) {
return _;
}
placeholder[
index
] = `<!--amis-preview-start--><div class="amis-doc"><div class="preview">${code}</div><pre><code class="lang-html">${prism.highlight(
code
.replace(/"data:(\w+\/\w+);.*?"/g, '"data:$1; ..."')
.replace(/<svg([^>]*)>[\s\S]*?<\/svg>/g, '<svg$1>...</svg>')
.replace(/class="([^"]*?)\.\.\.([^"]*?)"/g, 'class="$1..."'),
prism.languages[lang],
lang
)}</code></pre></div><!--amis-preview-end-->`;
} else {
placeholder[
index
] = `<!--amis-preview-start--><div class="amis-preview" style="min-height: ${setting.height}px"><script type="text/schema" ${attr}>${code}</script></div><!--amis-preview-end-->`;
}
return `[[${index++}]]`;
}
);
content = marked(content).replace(/<p>\[\[(\d+)\]\]<\/p>/g, function (_, id) {
return placeholder[id] || '';

View File

@ -68,6 +68,7 @@
--fontWeightNormal: 400;
--fontWeightBase: var(--fontWeightNormal);
--fontWeightMd: 500;
--fontWeightBold: 700;
--background: var(--white);
@ -401,6 +402,15 @@
--Calendar-shortcuts-height: #{px2rem(30px)};
--Calendar-wLabel-color: #999;
--Calendar-icon-bottom: #{px2rem(-4px)};
--Calendar-icon-width: #{px2rem(10px)};
--Calendar-icon-height: #{px2rem(10px)};
--Calendar-borderWidth: #{px2rem(1px)};
--Calendar-rdt-day: #{px2rem(100px)};
--Calendar-schedule-content-padding: 0 #{px2rem(4px)};
--Calendar-schedule-content-height: #{px2rem(20px)};
--Calendar-schedule-content-color: #{$white};
--Card-actions-borderColor: #{lighten($borderColor, 5%)};
--Card-actions-fontSize: var(--fontSizeBase);
--Card-actions-onChecked-onHover-bg: #{darken(#d9f3fb, 5%)};
@ -411,6 +421,7 @@
--Card-borderColor: var(--borderColor);
--Card-borderRadius: var(--borderRadius);
--Card-borderWidth: var(--borderWidth);
--Card-secondary-color: #83868c;
--Card-onChecked-bg: #d9f3fb;
--Card-onChecked-borderColor: #{darken(#d9f3fb, 10%)};
--Card-onChecked-color: #{darken(#d9f3fb, 40%)};
@ -481,6 +492,7 @@
) / 2 - var(--ColorPicker-borderWidth)
);
--ColorPicker-placeholderColor: var(--Form-input-placeholderColor);
--ColorPicker-boxShadow: var(--boxShadow);
--Combo--horizontal-dragger-top: var(--Form-label-paddingTop);
--Combo--horizontal-item-gap: var(--gap-xs);
@ -571,7 +583,7 @@
--LocationPicker-borderRadius: var(--Form-input-borderWidth);
--Divider-borderStyle: dashed;
--Divider-borderStyle: solid;
--Drawer-bg: var(--background);
--Drawer-body-padding: var(--gap-base);
@ -1290,31 +1302,32 @@
);
--TagControl-sugTip-color: var(--info);
--Toast--danger-bgColor: var(--danger);
--Toast--danger-borderColor: var(--danger);
--Toast-color: var(--text-color);
--Toast--danger-bgColor: var(--background);
--Toast--danger-borderColor: transparent;
--Toast--danger-color: var(--Toast-color);
--Toast--info-bgColor: var(--info);
--Toast--info-borderColor: var(--info);
--Toast--info-bgColor: var(--background);
--Toast--info-borderColor: transparent;
--Toast--info-color: var(--Toast-color);
--Toast--success-bgColor: var(--success);
--Toast--success-borderColor: var(--success);
--Toast--success-bgColor: var(--background);
--Toast--success-borderColor: transparent;
--Toast--success-color: var(--Toast-color);
--Toast--warning-bgColor: var(--warning);
--Toast--warning-borderColor: var(--warning);
--Toast--warning-bgColor: var(--background);
--Toast--warning-borderColor: transparent;
--Toast--warning-color: var(--Toast-color);
--Toast-border-width: 0;
--Toast-borderRadius: var(--borderRadiusLg);
--Toast-borderRadius: #{px2rem(2px)};
--Toast-box-shadow: var(--boxShadow);
--Toast-close-color: var(--icon-color);
--Toast-close--onHover-color: var(--Toast-close-color);
--Toast-close-color: var(--white);
--Toast-color: var(--white);
--Toast-icon-width: #{px2rem(16px)};
--Toast-icon-height: var(--Toast-icon-width);
--Toast-opacity: 0.8;
--Toast-paddingL: #{px2rem(40px)};
--Toast-paddingX: var(--gap-sm);
--Toast-paddingX: var(--gap-md);
--Toast-paddingY: var(--gap-xs);
--Toast-title-display: block;
--Toast-width: #{px2rem(300px)};
@ -1404,6 +1417,9 @@
--Steps-line-success-bg: var(--Steps-status-success);
--Progress-borderRadius: var(--borderRadius);
--Progress-animate-backgroundColor: #fff;
--Progress-bar-backgroundColor: #d3d9e6;
--ColumnToggler-backgroundColor: var(--white);
--ColumnToggler-borderRadius: #{px2rem(4px)};
--ColumnToggler-lineHeight: #{px2rem(24px)};

3
scss/base/_common.scss Normal file
View File

@ -0,0 +1,3 @@
.has-popover {
position: relative;
}

View File

@ -6,23 +6,46 @@
border-radius: var(--Alert-borderRadius);
margin-bottom: var(--Alert-marginBottom);
position: relative;
color: var(--Alert-fontColor);
display: flex;
flex-flow: row nowrap;
justify-content: space-between;
align-items: flex-start;
&-icon {
margin-right: #{px2rem(8px)};
font-size: var(--fontSizeLg);
}
&-content {
flex: 1;
.#{$ns}Alert-title {
color: var(--text-color);
font-size: var(--fontSizeBase);
font-weight: 500;
line-height: #{px2rem(24px)};
margin-bottom: #{px2rem(4px)};
}
.#{$ns}Alert-desc {
line-height: #{px2rem(24px)};
}
}
&-close {
position: absolute;
outline: none;
padding: 0;
cursor: pointer;
background: transparent;
border: 0;
float: right;
line-height: 1;
color: #000;
text-shadow: 0 1px 0 #fff;
filter: alpha(opacity=20);
opacity: 0.2;
right: var(--Alert-paddingX);
top: 50%;
transform: translateY(-50%);
margin-left: #{px2rem(8px)};
line-height: #{px2rem(24px)};
&:hover {
color: #000;

View File

@ -104,6 +104,7 @@
border: none;
flex-grow: 1;
overflow: auto;
scroll-behavior: smooth;
background: var(--Tabs-content-bg);
> .#{$ns}AnchorNav-section {

View File

@ -51,6 +51,11 @@
pointer-events: auto;
border: var(--Button-borderWidth) solid var(--Button-onDisabled-borderColor);
background: var(--Button-onDisabled-bg);
& > svg,
& > svg path {
fill: currentColor;
}
}
&:not(:disabled):not(.is-disabled) {
@ -365,3 +370,10 @@ input[type='button'] {
margin-top: var(--gap-xs);
}
}
.#{$ns}Action {
display: inline-block;
&:hover {
cursor: pointer;
}
}

View File

@ -0,0 +1,81 @@
.#{$ns}ScheduleCalendar {
&-icon {
position: absolute;
bottom: var(--Calendar-icon-bottom);
left: 50%;
transform: translateX(-50%);
display: block;
width: var(--Calendar-icon-width);
height: var(--Calendar-icon-height);
border-radius: 50%;
z-index: 10;
}
&-action {
display: block;
padding: 0;
width: 100%;
height: 100%;
border: none;
background: transparent;
color: inherit;
&:not(:disabled):not(.is-disabled):hover {
color: inherit;
background: transparent;
border-color: transparent;
}
}
.rdtDay {
position: relative;
}
&-text-overflow {
white-space:nowrap;
overflow:hidden;
text-overflow:ellipsis;
position: absolute;
width: 100%;
}
}
.#{$ns}ScheduleCalendar-large {
width: 100%;
.rdtPicker {
width: 100%;
table {
border-collapse: collapse;
border-spacing: 0;
td {
border: var(--Calendar-borderWidth) solid var(--borderColor);
}
}
}
.rdtDay {
height: var(--Calendar-rdt-day);
vertical-align: top;
}
.#{$ns}ScheduleCalendar-large-day-wrap {
position: absolute;
top: 0;
left: 0;
min-width: 100%;
height: 100%;
.#{$ns}ScheduleCalendar-large-schedule-content {
position: relative;
z-index: 10;
border-radius: var(--borderRadius);
text-align: left;
padding: var(--Calendar-schedule-content-padding);
height: var(--Calendar-schedule-content-height);
color: var(--Calendar-schedule-content-color);
}
}
.#{$ns}ScheduleCalendar-action {
z-index: 20;
position: relative;
}
}

View File

@ -13,8 +13,9 @@
}
&-title {
color: var(--text--loud-color);
color: var(--text-color);
font-size: var(--fontSizeMd);
font-weight: var(--fontWeightMd);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
@ -37,15 +38,14 @@
}
&-dragBtn {
display: inline-block;
cursor: pointer;
float: right;
margin: var(--gap-sm);
padding: 0 var(--gap-sm);
}
&-heading {
display: flex;
flex-direction: row;
padding: var(--gap-sm) var(--gap-base);
padding: var(--gap-md);
flex: 1 0 auto;
}
@ -60,6 +60,7 @@
}
&-avtarText {
flex-shrink: 0;
background: var(--primary);
color: var(--white);
width: 50px;
@ -79,32 +80,30 @@
flex-grow: 1;
height: 100%;
position: relative;
margin-right: var(--gap-md);
}
&-meta + &-toolbar {
margin-left: var(--gap-md);
}
&-toolbar {
margin-right: calc(-1 * var(--gap-base));
line-height: normal;
text-align: right;
}
&-highlight {
background: var(--success);
width: px2rem(8px);
height: px2rem(8px);
border-radius: 100%;
display: inline-block;
position: absolute;
top: var(--gap-xs);
right: px2rem(2px);
margin: 0 var(--gap-sm);
}
&-body {
padding: var(--gap-base);
padding: var(--gap-md);
flex: 1 0 auto;
}
&-heading + &-body {
padding-top: var(--gap-xs);
padding-top: 0;
}
&-field {
@ -133,6 +132,7 @@
flex-direction: row;
width: 100%;
table-layout: fixed;
white-space: nowrap;
> a {
background: transparent;
@ -215,4 +215,145 @@
.is-dragging > & {
opacity: var(--Card-onDragging-opacity);
}
&-footer-wrapper {
display: flex;
align-items: center;
justify-content: space-between;
}
&-actions-wrapper {
flex: 1;
display: flex;
align-items: center;
justify-content: flex-end;
flex-wrap: wrap;
}
&-checkbox {
margin: 0 var(--gap-sm) !important;
}
&-secondary {
max-width: 12.5rem;
color: var(--Card-secondary-color);
padding: 0 var(--gap-md);
font-size: var(--fontSizeMd);
font-weight: var(--fontWeightBase);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
&-secondary + &-actions-wrapper {
.#{$ns}Card-actions {
margin-left: unset;
border: none;
width: unset;
}
.#{$ns}Card-actions > a {
border: none;
}
}
&-multiMedia--top,
&-multiMedia--bottom,
&-multiMedia--left,
&-multiMedia--right {
.#{$ns}Card-actions-wrapper {
.#{$ns}Card-actions {
margin-left: unset;
border: none;
width: unset;
}
.#{$ns}Card-actions > a {
border: none;
}
}
}
&-multiMedia--top {
display: block;
.#{$ns}Card-multiMedia-img {
border-bottom-left-radius: unset;
border-bottom-right-radius: unset;
}
}
&-multiMedia--bottom {
display: flex;
flex-direction: column-reverse;
.#{$ns}Card-actions {
border-bottom: var(--Card-borderWidth) solid
var(--Card-actions-borderColor);
}
.#{$ns}Card-multiMedia-img {
border-top-left-radius: unset;
border-top-right-radius: unset;
}
}
&-multiMedia--top,
&-multiMedia--bottom {
.#{$ns}Card-body {
padding-bottom: var(--gap-md);
}
}
&-multiMedia--left {
display: flex;
align-items: center;
.#{$ns}Card-actions {
margin-left: var(--gap-md);
border-left: var(--Card-borderWidth) solid var(--Card-actions-borderColor);
}
.#{$ns}Card-multiMedia-img,
.#{$ns}Card-multiMedia-video {
margin: var(--gap-md) 0 var(--gap-md) var(--gap-md);
}
}
&-multiMedia--right {
display: flex;
align-items: center;
justify-content: space-between;
flex-direction: row-reverse;
.#{$ns}Card-actions {
margin-right: var(--gap-md);
border-right: var(--Card-borderWidth) solid
var(--Card-actions-borderColor);
}
.#{$ns}Card-multiMedia-img,
.#{$ns}Card-multiMedia-video {
margin: var(--gap-md) var(--gap-md) var(--gap-md) 0;
}
}
&-multiMedia-flex {
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
}
&-multiMedia-img {
display: block;
width: 100%;
height: auto;
object-fit: cover;
border-radius: var(--Card-borderRadius);
}
&-multiMedia-video {
width: px2rem(180px);
height: px2rem(120px);
.#{$ns}Video {
width: 100%;
height: 100%;
min-width: unset;
border-radius: var(--Card-borderRadius);
overflow: hidden;
.#{$ns}Video-player {
width: 100%;
height: 100%;
.video-react {
width: 100%;
height: 100%;
padding-top: unset !important;
}
}
}
}
}

View File

@ -0,0 +1,15 @@
.#{$ns}CollapseGroup {
.#{$ns}Collapse:not(:last-child) {
border-bottom: none;
}
&.icon-position-right {
.#{$ns}Collapse-header {
.#{$ns}Collapse-arrow {
float: right;
}
}
}
}

View File

@ -1,7 +1,7 @@
.#{$ns}Collapse {
border: var(--Collapse-border);
padding: 0;
margin-bottom: var(--Form-item-gap);
line-height: px2rem(20px);
&-header {
font-size: var(--Collapse-header-fontSize);
@ -9,7 +9,7 @@
color: var(--text--loud-color);
padding: var(--Collapse-header-padding);
margin: 0;
border-bottom: var(--borderWidth) solid var(--Collapse-border-color);
cursor: pointer;
background: var(--Collapse-header-bg);
&-wrapper {
@ -30,8 +30,7 @@
display: inline-block;
width: px2rem(16px);
text-align: center;
margin-right: px2rem(8px);
cursor: pointer;
margin-right: var(--gap-sm);
&:before {
content: '';
@ -39,37 +38,50 @@
display: inline-block;
width: px2rem(6px);
height: px2rem(6px);
top: px2rem(-4px);
top: px2rem(-2px);
border-color: var(--text-color);
border-style: solid;
border-width: px2rem(1px) px2rem(1px) 0 0;
transform: rotate(135deg);
transform: rotate(45deg);
transform-origin: 50% 50%;
}
}
&-TplField {
&-icon-tranform {
display: inline-block;
width: px2rem(16px);
text-align: center;
margin-right: var(--gap-xs);
}
.#{$ns}TplField {
display: inline-block;
}
&.is-collapsed &-arrow:before {
transform: rotate(45deg);
transform-origin: 0% 50%;
&.is-active &-arrow:before {
transform: rotate(135deg);
transform-origin: 50% 30%;
}
&.is-collapsed &-header {
border-bottom: var(--Collapse-header-collapsed-border);
&.is-active &-icon-tranform {
transform: rotate(90deg);
}
&--collapsable &-header {
cursor: pointer;
&--disabled &-header {
cursor: not-allowed;
user-select: none;
color: var(--text--muted-color);
&:hover {
background-color: var(--Collapse-header-bg-disabled-color);
}
}
&--disabled &-arrow:before {
border-color: var(--text--muted-color);
}
// title 显示在底部的模式
&--title-bottom &-header {
text-align: center;
color: var(--link-color);
border-left: none;
font-size: var(--fontSizeBase);
border-top: var(--Collapse-header-collapsed-borderTop);
border-bottom: var(--Collapse-header-collapsed-borderBottom);
@ -80,15 +92,13 @@
}
&--title-bottom &-arrow:before {
top: px2rem(2px);
transform: rotate(-45deg);
transform-origin: 0% 50%;
top: px2rem(-4px);
transform: rotate(135deg);
}
&--title-bottom.is-collapsed &-arrow:before {
top: px2rem(-6px);
transform: rotate(135deg);
transform-origin: 0% 50%;
&--title-bottom.is-active &-arrow:before {
top: 0;
transform: rotate(-45deg);
}
&-contentWrapper {

View File

@ -0,0 +1,128 @@
.u-hairline::after {
position: absolute;
box-sizing: border-box;
content: ' ';
pointer-events: none;
top: -50%;
right: -50%;
bottom: -50%;
left: -50%;
border: 0 solid var(--borderColorLight);
z-index: 1;
transform: scale(0.5);
}
.#{$ns}GridNav {
display: flex;
flex-wrap: wrap;
&-top {
position: relative;
&::after {
border-top-width: px2rem(1px);
}
}
}
.#{$ns}GridNavItem {
position: relative;
box-sizing: border-box;
&--square {
height: 0;
position: relative;
}
&-icon {
width: var(--rv-grid-item-icon-size);
}
&-text {
color: var(--text-color);
font-size: var(--fontSizeSm);
line-height: 1.5;
word-break: break-all;
flex-shrink: 0;
}
&-icon + &-text {
margin-top: px2rem(8px);
}
&-image {
display: inline-block;
svg,
img {
max-width: 100%;
display: block;
width: 60%;
margin: 0 auto;
}
}
&-content {
display: flex;
flex-direction: column;
box-sizing: border-box;
height: 100%;
padding: var(--gap-md) var(--gap-sm);
background-color: var(--white);
position: relative;
.#{$ns}Badge-text {
z-index: 10;
}
&--border::after {
border-width: 0 var(--borderWidth) var(--borderWidth) 0;
}
&--square {
position: absolute;
top: 0;
right: 0;
left: 0;
}
&--center {
align-items: center;
justify-content: center;
}
&--horizontal {
flex-direction: row;
.#{$ns}GridNavItem-text {
margin: 0 0 0 var(--gap-sm);
}
}
&--reverse {
flex-direction: column-reverse;
.#{$ns}GridNavItem-text {
margin: 0 0 var(--gap-sm);
}
}
&--horizontal &--reverse {
flex-direction: row-reverse;
.#{$ns}GridNavItem-text {
margin: 0 var(--gap-sm) 0 0;
}
}
&--surround {
&::after {
border-width: var(--borderWidth);
}
}
&--clickable {
cursor: pointer;
}
}
}

View File

@ -167,6 +167,7 @@
}
}
&-origin:hover &-overlay,
&-thumbWrap:hover &-overlay {
display: flex;
}

View File

@ -1,5 +1,6 @@
.#{$ns}InputBox {
@include input-input();
@include input-border();
&--inline {
display: inline-flex;

View File

@ -75,8 +75,8 @@
cursor: move;
position: absolute;
left: 0;
top: px2rem(11px);
display: none;
line-height: 0;
> .icon, > .#{$ns}Badge > .icon {
color: var(--icon-color);
@ -174,12 +174,6 @@
pointer-events: none;
}
&.active,
&.is-active {
background: var(--Nav-item-onActive-bg) !important;
}
&.active > a,
&.is-active > .#{$ns}Nav-item-atcions,
&.is-active > a,
@ -188,6 +182,7 @@
&.is-active > .#{$ns}Badge > a {
color: var(--Nav-item-onActive-color);
position: relative;
background: var(--Nav-item-onActive-bg) !important;
&::after {
transform: scaleY(1);

View File

@ -68,6 +68,35 @@
&-asideTplWrapper {
padding: var(--gap-xs);
}
&-asideResizor {
position: absolute;
right: -0.375rem;
top: 50%;
cursor: ew-resize;
writing-mode: vertical-lr;
width: 0.75rem;
height: 1.5rem;
margin-top: -0.75rem;
border: 0.0625rem solid #dee2e6;
background-color: #fff;
border-radius: 0.142rem;
font-size: 12px;
line-height: 0.625rem;
text-align: center;
user-select: none;
color: #666;
&:hover {
color: #000;
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.12),
0 2px 4px 0 rgba(0, 0, 0, 0.08);
}
&:after {
content: '···';
}
}
}
.#{$ns}Page-toolbar {
@ -93,9 +122,13 @@
border: inherit;
}
min-width: var(--Page-aside-width);
max-width: var(--Page-aside-maxWidth);
width: var(--Page-aside-width);
border-right: var(--borderWidth) solid var(--borderColor);
&--withWidth {
min-width: var(--Page-aside-width);
max-width: var(--Page-aside-maxWidth);
}
}
.#{$ns}Page--withSidebar {

View File

@ -49,6 +49,7 @@
}
&-columns {
touch-action: none;
position: relative;
display: flex;
cursor: grab;

View File

@ -52,7 +52,3 @@
margin-top: px2rem(-4px);
}
}
.has-popover {
position: relative;
}

View File

@ -1,38 +1,146 @@
.#{$ns}Progress {
height: 10px;
background: #ebebeb;
overflow: hidden;
border-radius: var(--Progress-borderRadius);
&-line {
display: inline-block;
width: 100%;
margin-right: calc(-2em + -8px);
padding-right: calc(2em + 8px);
&-bar {
float: left;
width: 0;
height: 100%;
font-size: 12px;
line-height: 10px;
color: #fff;
text-align: center;
background: var(--primary);
transition: width var(--animation-duration) ease;
border-radius: var(--Progress-borderRadius);
&--stripe {
background-image: linear-gradient(
45deg,
rgba(255, 255, 255, 0.15) 25%,
transparent 25%,
transparent 50%,
rgba(255, 255, 255, 0.15) 50%,
rgba(255, 255, 255, 0.15) 75%,
transparent 75%,
transparent
);
background-size: 2rem 2rem;
&-inter {
height: 10px;
background-color: var(--Progress-bar-backgroundColor);
overflow: hidden;
border-radius: var(--Progress-borderRadius);
}
&--animate {
animation: progress-bar-stripes 1s linear infinite;
&-no-label {
padding-right: 0;
margin-right: 0;
}
&-text {
display: inline-block;
width: 2em;
margin-left: 8px;
color: var(--text-color);
font-size: var(--fontSizeXs);
white-space: nowrap;
text-align: left;
word-break: normal;
.icon {
font-size: 15px;
}
}
&-bar {
float: left;
width: 0;
height: 100%;
font-size: 12px;
line-height: 10px;
text-align: center;
background: var(--primary);
transition: width var(--animation-duration) ease;
&--stripe {
background-image: linear-gradient(
45deg,
rgba(255, 255, 255, 0.15) 25%,
transparent 25%,
transparent 50%,
rgba(255, 255, 255, 0.15) 50%,
rgba(255, 255, 255, 0.15) 75%,
transparent 75%,
transparent
);
background-size: 2rem 2rem;
}
&--animate {
position: relative;
transition: all 0.4s cubic-bezier(0.08, 0.82, 0.17, 1) 0s;
&::before {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
background: var(--Progress-animate-backgroundColor);
border-radius: 10px;
opacity: 0;
animation: progress-bar-active 2.4s cubic-bezier(0.23, 1, 0.32, 1)
infinite;
content: '';
}
}
&--stripe-animate {
animation: progress-bar-stripes 1s linear infinite;
}
}
}
&-circle {
width: 120px;
height: 120px;
position: relative;
&-text {
position: absolute;
top: 50%;
left: 50%;
width: 100%;
margin: 0;
padding: 0;
color: var(--text-color);
font-size: 1em;
line-height: 1;
white-space: normal;
text-align: center;
transform: translate(-50%, -50%);
.icon {
font-size: 1.2em;
}
}
.bg-warning-circle {
&-path {
stroke: var(--warning);
}
}
.bg-danger-circle {
&-path {
stroke: var(--danger);
}
}
.bg-info-circle {
&-path {
stroke: var(--info);
}
}
.bg-success-circle {
&-path {
stroke: var(--success);
}
}
.bg-primary-circle {
&-path {
stroke: var(--primary);
}
}
}
}
@keyframes progress-bar-active {
0% {
transform: translateX(-100%) scaleX(0);
opacity: 0.1;
}
20% {
transform: translateX(-100%) scaleX(0);
opacity: 0.5;
}
100% {
transform: translateX(0) scaleX(1);
opacity: 0;
}
}

View File

@ -1,5 +1,6 @@
.#{$ns}ResultBox {
@include input-input();
@include input-border();
flex-wrap: wrap;
padding: 0 px2rem(3px);
min-height: var(--Form-input-height);

View File

@ -62,11 +62,12 @@
&--icon {
background: transparent;
animation: spin 2s linear infinite;
width: var(--fontSizeLg);
height: var(--fontSizeLg);
width: 16px; // reload 那个 icon 必须用这个高宽
height: 16px;
svg.icon {
width: var(--fontSizeLg);
height: var(--fontSizeLg);
width: 16px;
height: 16px;
top: 0;
}
}
}

View File

@ -549,6 +549,9 @@
width: px2rem(1px);
padding-right: 0;
cursor: move;
> svg {
vertical-align: middle;
}
}
> tbody > tr > td.#{$ns}Table-expandCell {
@ -879,6 +882,9 @@
text-decoration: none;
color: var(--icon-onHover-color);
}
> svg {
vertical-align: -2px;
}
}
&-table > tbody > tr:hover .#{$ns}Table-dragBtn,

View File

@ -55,7 +55,9 @@
.#{$ns}Toast {
display: flex;
align-items: center;
flex-flow: row nowrap;
justify-content: space-between;
align-items: flex-start;
&-wrap {
pointer-events: none;
@ -75,7 +77,6 @@
color: var(--Toast-color);
position: relative;
opacity: var(--Toast-opacity);
cursor: pointer;
opacity: 0;
transform: translateZ(0);
@ -95,22 +96,52 @@
}
&-close {
color: var(--Toast-close-color);
display: inline-flex;
margin-left: var(--gap-xs);
font-size: var(--fontSizeLg);
line-height: var(--gap-xl);
height: var(--gap-xl);
color: var(--Toast-close-color);
margin-left: var(--gap-sm);
opacity: 0.8;
align-items: center;
cursor: pointer;
&:hover {
color: var(--Toast-close--onHover-color);
opacity: 1;
}
& > svg {
top: 0;
}
}
&-body {
display: inline-block;
vertical-align: middle;
white-space: pre-wrap;
flex-grow: 1;
&-content {
flex: 1;
display: flex;
flex-flow: column nowrap;
justify-content: space-between;
align-items: flex-start;
.#{$ns}Toast-title {
color: var(--text-color);
font-size: var(--fontSizeBase);
font-weight: 500;
line-height: var(--gap-xl);
margin-bottom: var(--gap-xs);
}
.#{$ns}Toast-body {
font-size: var(--fontSizeSm);
display: inline-block;
vertical-align: middle;
white-space: pre-wrap;
flex-grow: 1;
line-height: var(--gap-xl);
.#{$ns}Html {
word-break: break-word;
}
}
}
&-icon {
@ -118,8 +149,10 @@
text-rendering: auto;
-webkit-font-smoothing: antialiased;
vertical-align: middle;
margin-right: var(--gap-xs);
margin-right: var(--gap-sm);
height: var(--gap-xl);
line-height: var(--gap-xl);
align-items: center;
> svg {
top: 0;

View File

@ -57,7 +57,7 @@
&-preview {
display: flex;
align-items: center;
margin-left: var(--gap-xs);
margin-right: var(--gap-sm);
cursor: pointer;
}
@ -73,6 +73,35 @@
display: inline-block;
line-height: 1;
}
&-arrow {
margin-right: var(--gap-xs);
// margin-left: var(--gap-xs);
width: var(--gap-md);
text-align: center;
display: flex;
align-items: center;
justify-content: center;
line-height: 1;
> svg {
transition: transform var(--animation-duration);
display: inline-block;
color: var(--Form-select-caret-iconColor);
width: 10px;
height: 10px;
top: 0;
}
}
&.is-opened &-arrow > svg {
transform: rotate(180deg);
}
}
.#{$ns}ColorPicker-popover {
border: none;
box-shadow: none;
}
.#{$ns}ColorControl:not(.is-inline) > .#{$ns}ColorPicker {
@ -82,7 +111,7 @@
// override
.sketch-picker {
box-shadow: none !important;
border-radius: 0 !important;
border: none !important;
border-radius: var(--borderRadius) !important;
box-shadow: var(--ColorPicker-boxShadow) !important;
}

View File

@ -159,6 +159,8 @@
.#{$ns}Combo-itemDrager {
padding: var(--Combo--horizontal-dragger-top) px2rem(6px) 0 0;
display: flex;
align-items: center;
}
}
@ -224,6 +226,8 @@
position: absolute;
top: var(--Combo--horizontal-dragger-top);
left: px2rem(-30px);
display: flex;
align-items: center;
}
&.is-draggable {

View File

@ -31,6 +31,7 @@
}
fieldset.#{$ns}Collapse {
margin-bottom: var(--Form-item-gap);
> legend {
font-weight: var(--fontWeightNormal);
padding: var(--gap-xs) 0;

View File

@ -1,4 +1,15 @@
.#{$ns}FileControl {
&-templateInfo {
display: block;
margin-bottom: var(--gap-base);
padding: var(--Button-paddingY) var(--Button-paddingX);
cursor: pointer;
> svg {
margin-right: var(--gap-xs);
}
}
&-dropzone {
outline: none;
}

View File

@ -122,6 +122,7 @@
&-tab {
padding: 0;
overflow: auto;
}
&-tabs {

View File

@ -59,6 +59,10 @@
pointer-events: none;
}
&.is-draggable {
position: relative;
}
&--outline &-sublist &-item--isLeaf {
&:before {
position: absolute;
@ -101,6 +105,10 @@
}
}
&.is-draggable &-itemLabel:hover::after {
display: none;
}
&-item-icons {
visibility: hidden;
transition: visibility var(--animation-duration) ease;
@ -227,6 +235,11 @@
width: calc(var(--Tree-itemArrowWidth) + var(--gap-xs));
}
&-itemDrager {
cursor: move;
color: var(--icon-color);
}
&-spinner {
margin-right: var(--gap-xs);
}
@ -274,4 +287,33 @@
&-placeholder {
color: var(--text--muted-color);
}
&-dropIndicator {
position: absolute;
height: px2rem(2px);
background-color: var(--Tree-itemLabel--onChecked-color);
border-radius: px2rem(1px);
z-index: 1;
&::after {
position: absolute;
top: px2rem(-3px);
left: px2rem(-6px);
width: px2rem(8px);
height: px2rem(8px);
background-color: transparent;
border: px2rem(2px) solid var(--Tree-itemLabel--onChecked-color);
border-radius: 50%;
content: '';
}
&--hover {
border-radius: 0;
background-color: var(--Tree-item-onHover-bg);
&::after {
display: none;
}
}
}
}

View File

@ -92,8 +92,6 @@ $link-color: $info;
--Rating-onActive-color: #fadb14;
--Divider-borderStyle: solid;
--Panel--default-bg: #fff;
--Layout-aside-bg: #001529;

View File

@ -2,6 +2,7 @@
@import '../base/reset';
@import '../base/normalize';
@import '../base/typography';
@import '../base/common';
@import '../layout/layout';
@import '../layout/grid';
@ -36,7 +37,9 @@
@import '../components/button-group';
@import '../components/dropdown';
@import '../components/each';
@import '../components/calendar';
@import '../components/collapse';
@import '../components/collapse-group';
@import '../components/color';
@import '../components/condition-builder';
@import '../components/context-menu';
@ -68,6 +71,7 @@
@import '../components/icon';
@import '../components/steps';
@import '../components/portlet';
@import '../components/grid-nav';
@import '../components/form/fieldset';
@import '../components/form/group';

View File

@ -72,6 +72,8 @@ $R7: px2rem(16px);
$R8: 50%;
// yunshe4.0 box-shadow
$L1: 0px 4px 6px 0px rgba(8, 14, 26, 0.06),
0px 1px 10px 0px rgba(8, 14, 26, 0.05), 0px 2px 4px -1px rgba(8, 14, 26, 0.04);
:root {
--borderColor: #eceff8;
@ -81,7 +83,7 @@ $R8: 50%;
--text--muted-color: #{$G6};
--text--loud-color: #333;
--link-onHover-decoration: underline;
--link-onHover-decoration: none;
--icon-color: #999;
--icon-onHover-color: var(--primary);
@ -219,6 +221,15 @@ $R8: 50%;
--Switch-onDisabled-color: #{$G11};
// --Switch-onDisabled-circle-BackgroundColor: #fff;
--Calendar-icon-bottom: #{px2rem(-4px)};
--Calendar-icon-width: #{px2rem(10px)};
--Calendar-icon-height: #{px2rem(10px)};
--Calendar-borderWidth: #{px2rem(1px)};
--Calendar-rdt-day: #{px2rem(100px)};
--Calendar-schedule-content-padding: 0 #{px2rem(4px)};
--Calendar-schedule-content-height: #{px2rem(20px)};
--Calendar-schedule-content-color: #{$white};
--ColorPicker-borderWidth: #{px2rem(1px)};
--ColorPicker-borderRadius: #{$R3};
--ColorPicker-bg: var(--white);
@ -228,16 +239,16 @@ $R8: 50%;
--ColorPicker-placeholderColor: #999;
--ColorPicker-onDisabled-bg: #f5f5f5;
--ColorPicker-onDisabled-color: #999;
--ColorPicker-boxShadow: #{$L1};
--Collapse-border: #{px2rem(1px)} solid #{$G8};
--Collapse-border-color: #{$G8};
--Collapse-header-fontSize: #{$T3};
--Collapse-header-fontWeight: #{$W3};
--Collapse-header-fontWeight: #{$W2};
--Collapse-header-padding: #{px2rem(16px)};
--Collapse-header-bg: #{$G10};
--Collapse-header-onHover-bg: #{$G9};
--Collapse-header-collapsed-border: none;
--Collapse-header-collapsed-borderTop: none;
--Collapse-header-bg-disabled-color: #{$G10};
--Collapse-header-collapsed-borderBottom: none;
--Collapse-header-wrapper-direction: row-reverse;
--Collapse-content-padding: #{px2rem(16px)};
@ -501,6 +512,9 @@ $R8: 50%;
--TransferSelect-heading-borderBottom: 0;
// Alert
--Alert-fontColor: #{$G4};
--Alert-title-fontColor: #{$G2};
--Alert-height: #{px2rem(40px)};
--Alert-paddingX: #{px2rem(16px)};
--Alert-fontSize: #{$T2};
@ -525,8 +539,9 @@ $R8: 50%;
// Toast size
--Toast-width: #{px2rem(300px)};
--Toast-borderRadius: 0;
--Toast-borderRadius: #{$R3};
--Toast-paddingL: #{px2rem(26px)};
--Toast-paddingX: #{px2rem(16px)};
--Toast--info-paddingL: 0;
--Toast-border-width: #{px2rem(1px)};
--Toast-opacity: 1;
@ -534,8 +549,6 @@ $R8: 50%;
// Toast color
--Toast-color: var(--white);
--Toast-borderRadius: #{$R3};
--Toast-paddingX: #{px2rem(16px)};
--Toast--danger-color: #{$G2};
--Toast--danger-bgColor: #{$G11};

View File

@ -57,7 +57,8 @@
.#{$ns}Select-menu {
border-radius: #{$R3};
.#{$ns}Select-option {
height: var(--Form-input-height);
// height: var(--Form-input-height);
line-height: 1.1;
padding: px2rem(5px) px2rem(12px);
}
}
@ -148,13 +149,6 @@
}
}
.#{$ns}ColorPicker {
.#{$ns}PopOver {
border: none;
box-shadow: var(--boxShadow);
}
}
.#{$ns}IconPickerControl {
.#{$ns}IconPickerControl-input--withAC {
.#{$ns}IconPickerControl-sugsPanel {
@ -291,6 +285,7 @@
svg {
width: #{px2rem(12px)};
height: #{px2rem(12px)};
top: 0;
}
}

View File

@ -140,8 +140,9 @@ export function renderChild(
const transform = props.propsTransform;
if (transform) {
// @ts-ignore
props = {...props};
delete props.propsTransform;
props = transform(props);
}

View File

@ -12,6 +12,7 @@ import {FormSchema} from './renderers/Form';
import {CarouselSchema} from './renderers/Carousel';
import {ChartSchema} from './renderers/Chart';
import {CollapseSchema} from './renderers/Collapse';
import {CollapseGroupSchema} from './renderers/CollapseGroup';
import {ColorSchema} from './renderers/Color';
import {ContainerSchema} from './renderers/Container';
import {CRUDSchema} from './renderers/CRUD';
@ -131,6 +132,7 @@ export type SchemaType =
| 'carousel'
| 'chart'
| 'collapse'
| 'collapse-group'
| 'color'
| 'container'
| 'crud'
@ -317,6 +319,7 @@ export type SchemaType =
| 'tree-select'
| 'table-view'
| 'portlet'
| 'grid-nav'
// 原生 input 类型
| 'native-date'
@ -339,6 +342,7 @@ export type SchemaObject =
| CarouselSchema
| ChartSchema
| CollapseSchema
| CollapseGroupSchema
| ColorSchema
| ContainerSchema
| CRUDSchema
@ -487,7 +491,7 @@ export interface SchemaApiObject {
/**
* API
*/
method?: 'get' | 'post' | 'put' | 'delete' | 'patch';
method?: 'get' | 'post' | 'put' | 'delete' | 'patch' | 'jsonp';
/**
* API
@ -732,11 +736,6 @@ export interface BaseSchema {
*
*/
visibleOn?: SchemaExpression;
/**
* 使
*/
useMobileUI?: boolean;
}
export interface Option {

View File

@ -10,7 +10,8 @@ import {
extendObject,
guid,
isObjectShallowModified,
syncDataFromSuper
syncDataFromSuper,
isSuperDataModified
} from './utils/helper';
import {dataMapping} from './utils/tpl-builtin';
import {RootStoreContext} from './WithRootStore';
@ -174,7 +175,9 @@ export function HocStoreFactory(renderer: {
}
} else if (
shouldSync === true ||
isObjectShallowModified(prevProps.data, props.data)
isObjectShallowModified(prevProps.data, props.data) ||
(props.syncSuperStore !== false &&
isSuperDataModified(props.data, prevProps.data, store))
) {
if (props.store && props.store.data === props.data) {
store.initData(

View File

@ -9,8 +9,13 @@ import {Icon} from './icons';
export interface AlertProps {
level: 'danger' | 'info' | 'success' | 'warning';
className: string;
title?: string;
className?: string;
showCloseButton: boolean;
showIcon?: boolean;
icon?: string | React.ReactNode;
iconClassName?: string;
closeButtonClassName?: string;
onClose?: () => void;
classnames: ClassNamesFn;
classPrefix: string;
@ -60,21 +65,44 @@ export class Alert extends React.Component<AlertProps, AlertState> {
className,
level,
children,
showCloseButton
showCloseButton,
title,
icon,
showIcon,
iconClassName,
closeButtonClassName
} = this.props;
const iconNode = icon ? (
typeof icon === 'string' ? (
<Icon icon={icon} className={cx(`Alert-icon icon`)} />
) : React.isValidElement(icon) ? (
React.cloneElement(icon, {
className: cx(`Alert-icon`, icon.props?.className)
})
) : null
) : showIcon ? (
<Icon icon={`alert-${level}`} className={cx(`Alert-icon icon`)} />
) : null;
return this.state.show ? (
<div className={cx('Alert', level ? `Alert--${level}` : '', className)}>
{showIcon && iconNode ? (
<div className={cx('Alert-icon', iconClassName)}>{iconNode}</div>
) : null}
<div className={cx('Alert-content')}>
{title ? <div className={cx('Alert-title')}>{title}</div> : null}
<div className={cx('Alert-desc')}>{children}</div>
</div>
{showCloseButton ? (
<button
className={cx('Alert-close')}
className={cx('Alert-close', closeButtonClassName)}
onClick={this.handleClick}
type="button"
>
<Icon icon="close" className="icon" />
</button>
) : null}
{children}
</div>
) : null;
}

View File

@ -105,7 +105,8 @@ export class AssociatedSelection extends BaseSelection<
disabled,
leftMode,
cellRender,
multiple
multiple,
onDeferLoad
} = this.props;
const selectdOption = BaseSelection.resolveSelected(
@ -127,6 +128,7 @@ export class AssociatedSelection extends BaseSelection<
onChange={this.handleLeftSelect}
multiple={false}
clearable={false}
onDeferLoad={onDeferLoad}
/>
) : (
<GroupedSelecton

View File

@ -82,6 +82,7 @@ export class Button extends React.Component<ButtonProps> {
<Comp
type={Comp === 'input' || Comp === 'button' ? type : undefined}
{...pickEventsProps(rest)}
onClick={rest.onClick && disabled ? () => {} : rest.onClick}
href={href}
className={cx(
overrideClassName

203
src/components/Card.tsx Normal file
View File

@ -0,0 +1,203 @@
import React from 'react';
import {isClickOnInput} from '../utils/helper';
import {ClassNamesFn, themeable, ThemeProps} from '../theme';
export interface CardProps extends ThemeProps {
className?: string;
headerClassName?: string;
titleClassName?: string;
subTitleClassName?: string;
descriptionClassName?: string;
avatarTextStyle?: object;
avatarTextClassName?: string;
avatarClassName?: string;
secondaryClassName?: string;
imageClassName?: string;
bodyClassName?: string;
footerClassName?: string;
media?: React.ReactNode;
mediaPosition?: 'top' | 'left' | 'right' | 'bottom';
toolbar?: React.ReactNode;
children?: React.ReactNode;
actions?: React.ReactNode;
title?: string | JSX.Element;
subTitle?: string | JSX.Element;
subTitlePlaceholder?: string | JSX.Element;
description?: string | JSX.Element;
descriptionPlaceholder?: string | JSX.Element;
avatar?: string;
avatarText?: string | JSX.Element;
secondary?: string | JSX.Element;
onClick?: (e: React.MouseEvent<HTMLDivElement>) => void;
classnames: ClassNamesFn;
}
export class Card extends React.Component<CardProps> {
static defaultProps: Partial<CardProps> = {
className: '',
avatarClassName: '',
headerClassName: '',
footerClassName: '',
secondaryClassName: '',
avatarTextClassName: '',
bodyClassName: '',
titleClassName: '',
subTitleClassName: '',
descriptionClassName: '',
imageClassName: '',
mediaPosition: 'left'
};
constructor(props: CardProps) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick(e: React.MouseEvent<HTMLDivElement>) {
if (isClickOnInput(e)) {
return;
}
this.props.onClick && this.props.onClick(e);
}
render() {
const {
classnames: cx,
className,
headerClassName,
bodyClassName,
titleClassName,
subTitleClassName,
descriptionClassName,
avatarClassName,
avatarTextStyle,
imageClassName,
avatarTextClassName,
secondaryClassName,
footerClassName,
media,
mediaPosition,
actions,
children,
onClick,
toolbar,
title,
subTitle,
subTitlePlaceholder,
description,
descriptionPlaceholder,
secondary,
avatar,
avatarText
} = this.props;
let heading = null;
const isShowHeading =
avatar ||
avatarText ||
title ||
subTitle ||
subTitlePlaceholder ||
description ||
descriptionPlaceholder ||
toolbar;
if (isShowHeading) {
heading = (
<div className={cx('Card-heading', headerClassName)}>
{avatar ? (
<span className={cx('Card-avtar', avatarClassName)}>
<img className={cx('Card-img', imageClassName)} src={avatar} />
</span>
) : avatarText ? (
<span
className={cx('Card-avtarText', avatarTextClassName)}
style={avatarTextStyle}
>
{avatarText}
</span>
) : null}
<div className={cx('Card-meta')}>
{title ? (
<div className={cx('Card-title', titleClassName)}>{title}</div>
) : null}
{subTitle || subTitlePlaceholder ? (
<div className={cx('Card-subTitle', subTitleClassName)}>
{subTitle
? subTitle
: subTitlePlaceholder
? subTitlePlaceholder
: null}
</div>
) : null}
{description || descriptionPlaceholder ? (
<div className={cx('Card-desc', descriptionClassName)}>
{description
? description
: descriptionPlaceholder
? descriptionPlaceholder
: null}
</div>
) : null}
</div>
{toolbar}
</div>
);
}
const body = children;
return (
<div
onClick={this.handleClick}
className={cx('Card', className, {
'Card--link': onClick
})}
>
{media ? (
<div className={cx(`Card-multiMedia--${mediaPosition}`)}>
{media}
<div className={cx('Card-multiMedia-flex')}>
{heading}
{body ? (
<div className={cx('Card-body', bodyClassName)}>{body}</div>
) : null}
{secondary || actions ? (
<div className={cx('Card-footer-wrapper', footerClassName)}>
{secondary ? (
<div className={cx('Card-secondary', secondaryClassName)}>
{secondary}
</div>
) : null}
{actions ? (
<div className={cx('Card-actions-wrapper')}>{actions}</div>
) : null}
</div>
) : null}
</div>
</div>
) : (
<div>
{heading}
{body ? (
<div className={cx('Card-body', bodyClassName)}>{body}</div>
) : null}
{secondary || actions ? (
<div className={cx('Card-footer-wrapper', footerClassName)}>
{secondary ? (
<div className={cx('Card-secondary', secondaryClassName)}>
{secondary}
</div>
) : null}
{actions ? (
<div className={cx('Card-actions-wrapper')}>{actions}</div>
) : null}
</div>
) : null}
</div>
)}
</div>
);
}
}
export default themeable(Card);

View File

@ -12,6 +12,8 @@ import Transition, {
EXITING
} from 'react-transition-group/Transition';
import {autobind} from '../utils/helper';
import {isClickOnInput} from '../utils/helper';
import {TranslateFn} from '../locale';
const collapseStyles: {
[propName: string]: string;
@ -22,24 +24,87 @@ const collapseStyles: {
};
export interface CollapseProps {
show?: boolean;
key?: string;
id?: string;
mountOnEnter?: boolean;
unmountOnExit?: boolean;
className?: string;
classPrefix: string;
classnames: ClassNamesFn;
headerPosition?: 'top' | 'bottom';
header?: React.ReactElement;
body: any;
bodyClassName?: string;
disabled?: boolean;
collapsable?: boolean;
collapsed?: boolean;
showArrow?: boolean;
expandIcon?: React.ReactElement | null;
headingClassName?: string;
collapseHeader?: React.ReactElement | null;
size?: 'xs' | 'sm' | 'md' | 'lg' | 'base';
onCollapse?: (item: any, collapsed: boolean) => void;
wrapperComponent?: any;
headingComponent?: any;
translate?: TranslateFn;
propsUpdate?: boolean;
}
export class Collapse extends React.Component<CollapseProps, any> {
static defaultProps: Pick<
CollapseProps,
'show' | 'mountOnEnter' | 'unmountOnExit'
> = {
show: false,
export interface CollapseState {
collapsed: boolean;
}
export class Collapse extends React.Component<CollapseProps, CollapseState> {
static defaultProps: Partial<CollapseProps> = {
mountOnEnter: false,
unmountOnExit: false
unmountOnExit: false,
headerPosition: 'top',
wrapperComponent: 'div',
headingComponent: 'div',
className: '',
headingClassName: '',
bodyClassName: '',
collapsable: true,
disabled: false,
showArrow: true,
propsUpdate: false
};
state: CollapseState = {
collapsed: false
};
constructor(props: CollapseProps) {
super(props);
this.toggleCollapsed = this.toggleCollapsed.bind(this);
this.state.collapsed = !!props.collapsed;
}
static getDerivedStateFromProps(nextProps: CollapseProps, preState: CollapseState) {
if (nextProps.propsUpdate && nextProps.collapsed !== preState.collapsed) {
return {
collapsed: !!nextProps.collapsed
};
}
return null;
}
toggleCollapsed(e: React.MouseEvent<HTMLElement>) {
if (isClickOnInput(e)) {
return;
}
const props = this.props;
if (props.disabled || props.collapsable === false) {
return;
}
props.onCollapse && props.onCollapse(props, !this.state.collapsed);
this.setState({
collapsed: !this.state.collapsed
});
}
contentDom: any;
contentRef = (ref: any) => (this.contentDom = ref);
@ -77,18 +142,54 @@ export class Collapse extends React.Component<CollapseProps, any> {
render() {
const {
show,
children,
classnames: cx,
mountOnEnter,
unmountOnExit
unmountOnExit,
classPrefix: ns,
size,
wrapperComponent: WrapperComponent,
headingComponent: HeadingComponent,
className,
headingClassName,
headerPosition,
collapseHeader,
header,
body,
bodyClassName,
collapsable,
translate: __,
showArrow,
expandIcon,
disabled,
children
} = this.props;
return (
const finalHeader = this.state.collapsed ? header : collapseHeader || header;
let dom = [
finalHeader ? (
<HeadingComponent
key="header"
onClick={this.toggleCollapsed}
className={cx(`Collapse-header`, headingClassName)}
>
{showArrow && collapsable
? expandIcon
? React.cloneElement(expandIcon, {
...expandIcon.props,
className: cx('Collapse-icon-tranform')
})
: <span className={cx('Collapse-arrow')} />
: ''}
{finalHeader}
</HeadingComponent>
) : null,
<Transition
key="body"
mountOnEnter={mountOnEnter}
unmountOnExit={unmountOnExit}
in={show}
in={!this.state.collapsed}
timeout={300}
onEnter={this.handleEnter}
onEntering={this.handleEntering}
@ -105,17 +206,35 @@ export class Collapse extends React.Component<CollapseProps, any> {
className={cx('Collapse-contentWrapper', collapseStyles[status])}
ref={this.contentRef}
>
{React.cloneElement(children as any, {
...(children as React.ReactElement).props,
className: cx(
'Collapse-content',
(children as React.ReactElement).props.className
)
})}
<div className={cx('Collapse-body', bodyClassName)}>
<div className={cx('Collapse-content')}>{body || children}</div>
</div>
</div>
);
}}
</Transition>
];
if (headerPosition === 'bottom') {
dom.reverse();
}
return (
<WrapperComponent
className={cx(
`Collapse`,
{
'is-active': !this.state.collapsed,
[`Collapse--${size}`]: size,
'Collapse--disabled': disabled,
'Collapse--title-bottom': headerPosition === 'bottom'
},
className
)}
>
{dom}
</WrapperComponent>
);
}
}

View File

@ -0,0 +1,130 @@
/**
* @file CollapseGroup
* @description group
* @author hongyang03
*/
import React from 'react';
import {CollapseProps} from '../renderers/Collapse';
import {SchemaNode} from '../types';
import {ClassNamesFn, themeable} from '../theme';
export interface CollapseGroupProps {
defaultActiveKey?: Array<string | number | never> | string | number;
accordion?: boolean;
expandIcon?: SchemaNode;
expandIconPosition?: 'left' | 'right';
body?: Array<React.ReactElement>;
className?: string;
classnames: ClassNamesFn;
classPrefix: string;
}
export interface CollapseGroupState {
activeKey: Array<string | number | never>;
}
class CollapseGroup extends React.Component<
CollapseGroupProps,
CollapseGroupState
> {
static defaultProps: Partial<CollapseGroupProps> = {
className: '',
accordion: false,
expandIconPosition: 'left'
};
constructor(props: CollapseGroupProps) {
super(props);
// 传入的activeKey会被自动转换为defaultActiveKey
let activeKey = props.defaultActiveKey;
if (!Array.isArray(activeKey)) {
activeKey = activeKey ? [activeKey] : [];
}
if (props.accordion) {
// 手风琴模式下只展开第一个元素
activeKey = activeKey.length ? [activeKey[0]] : [];
}
this.state = {
activeKey: activeKey.map((key: number | string) => String(key))
};
}
collapseChange(item: CollapseProps, collapsed: boolean) {
let activeKey = this.state.activeKey;
if (collapsed) {
if (this.props.accordion) {
activeKey = [];
}
else {
for(let i = 0; i < activeKey.length; i++) {
if (activeKey[i] === item.id) {
activeKey.splice(i, 1);
break;
}
}
}
}
else {
if (this.props.accordion) {
activeKey = [item.id];
}
else {
activeKey.push(item.id);
}
}
this.setState({
activeKey
});
}
getItems = (children: React.ReactNode) => {
if (!Array.isArray(children)) {
return children;
}
return children.map((child: React.ReactElement, index: number) => {
let props = child.props;
const id = props.schema.key || String(index);
const collapsed = this.state.activeKey.indexOf(id) === -1;
return React.cloneElement(child as any, {
...props,
key: id,
id,
collapsed,
expandIcon: this.props.expandIcon,
propsUpdate: true,
onCollapse: (item: CollapseProps, collapsed: boolean) => this.collapseChange(item, collapsed)
});
});
};
render() {
const {
classnames: cx,
className,
expandIconPosition,
children
} = this.props;
return (
<div
className={cx(
`CollapseGroup`,
{
'icon-position-right': expandIconPosition === 'right',
},
className
)}
>
{this.getItems(children)}
</div>
);
}
}
export default themeable(CollapseGroup);

View File

@ -12,9 +12,11 @@ import Overlay from './Overlay';
import {uncontrollable} from 'uncontrollable';
import PopOver from './PopOver';
import {ClassNamesFn, themeable, ThemeProps} from '../theme';
import {autobind} from '../utils/helper';
import {autobind, isObject} from '../utils/helper';
import {localeable, LocaleProps} from '../locale';
export type PresetColor = {color: string; title: string} | string;
export interface ColorProps extends LocaleProps, ThemeProps {
placeholder?: string;
format: string;
@ -27,7 +29,7 @@ export interface ColorProps extends LocaleProps, ThemeProps {
placement?: string;
value?: any;
onChange: (value: any) => void;
presetColors?: string[];
presetColors?: PresetColor[];
resetValue?: string;
allowCustomColor?: boolean;
}
@ -229,11 +231,20 @@ export class ColorControl extends React.PureComponent<
`ColorPicker`,
{
'is-disabled': disabled,
'is-focused': isFocused
'is-focused': isFocused,
'is-opened': isOpened
},
className
)}
>
<span onClick={this.handleClick} className={cx('ColorPicker-preview')}>
<i
ref={this.preview}
className={`${ns}ColorPicker-previewIcon`}
style={{background: this.state.inputValue || '#ccc'}}
/>
</span>
<input
ref={this.input}
type="text"
@ -255,12 +266,8 @@ export class ColorControl extends React.PureComponent<
</a>
) : null}
<span onClick={this.handleClick} className={cx('ColorPicker-preview')}>
<i
ref={this.preview}
className={`${ns}ColorPicker-previewIcon`}
style={{background: this.state.inputValue || '#ccc'}}
/>
<span className={cx('ColorPicker-arrow')}>
<Icon icon="caret" className="icon" onClick={this.handleClick} />
</span>
{isOpened ? (
@ -280,6 +287,7 @@ export class ColorControl extends React.PureComponent<
>
{allowCustomColor ? (
<SketchPicker
styles={{}}
disableAlpha={!!~['rgb', 'hex'].indexOf(format as string)}
color={value}
presetColors={presetColors}
@ -288,7 +296,21 @@ export class ColorControl extends React.PureComponent<
) : (
<GithubPicker
color={value}
colors={presetColors}
colors={
Array.isArray(presetColors)
? (presetColors
.filter(
item => typeof item === 'string' || isObject(item)
)
.map(item =>
typeof item === 'string'
? item
: isObject(item)
? item?.color
: item
) as string[])
: undefined
}
onChangeComplete={this.handleChange}
/>
)}

View File

@ -278,6 +278,15 @@ export interface DateProps extends LocaleProps, ThemeProps {
borderMode?: 'full' | 'half' | 'none';
// 是否为内嵌模式,如果开启就不是 picker 了,直接页面点选。
embed?: boolean;
schedules?: Array<{
startTime: Date,
endTime: Date,
content: any,
className?: string
}>;
scheduleClassNames?: Array<string>;
largeMode?: boolean;
onScheduleClick?: (scheduleData: any) => void;
// 下面那个千万不要写,写了就会导致 keyof DateProps 得到的结果是 string | number;
// [propName: string]: any;
@ -302,7 +311,8 @@ export class DatePicker extends React.Component<DateProps, DatePickerState> {
viewMode: 'days' as 'years' | 'months' | 'days' | 'time',
shortcuts: '',
closeOnSelect: true,
overlayPlacement: 'auto'
overlayPlacement: 'auto',
scheduleClassNames: ['bg-warning', 'bg-danger', 'bg-success', 'bg-info', 'bg-secondary']
};
state: DatePickerState = {
isOpened: false,
@ -546,7 +556,11 @@ export class DatePicker extends React.Component<DateProps, DatePickerState> {
format,
borderMode,
embed,
minDate
minDate,
schedules,
largeMode,
scheduleClassNames,
onScheduleClick
} = this.props;
const __ = this.props.translate;
@ -554,12 +568,33 @@ export class DatePicker extends React.Component<DateProps, DatePickerState> {
let date: moment.Moment | undefined = this.state.value;
if (embed) {
let schedulesData: DateProps['schedules'] = undefined;
if (schedules && Array.isArray(schedules)) {
// 设置日程颜色
let index = 0;
schedulesData = schedules.map((schedule: any) => {
let className = schedule.className;
if (!className && scheduleClassNames) {
className = scheduleClassNames[index];
index++;
if (index >= scheduleClassNames.length) {
index = 0;
}
}
return {
...schedule,
className
};
});
}
return (
<div
className={cx(
`DateCalendar`,
{
'is-disabled': disabled
'is-disabled': disabled,
'ScheduleCalendar': schedulesData,
'ScheduleCalendar-large': largeMode
},
className
)}
@ -578,6 +613,9 @@ export class DatePicker extends React.Component<DateProps, DatePickerState> {
locale={locale}
minDate={minDate}
// utc={utc}
schedules={schedulesData}
largeMode={largeMode}
onScheduleClick={onScheduleClick}
/>
</div>
);

View File

@ -225,12 +225,14 @@ export class Drawer extends React.Component<DrawerProps, DrawerState> {
fadeStyles[status]
)}
>
<a
onClick={disabled ? undefined : onHide}
className={`${ns}Drawer-close`}
>
<Icon icon="close" className="icon" />
</a>
{show ? (
<a
onClick={disabled ? undefined : onHide}
className={`${ns}Drawer-close`}
>
<Icon icon="close" className="icon" />
</a>
) : null}
{status === EXITED ? null : children}
</div>
</div>

Some files were not shown because too many files have changed in this diff Show More