mirror of
https://gitee.com/baidu/amis.git
synced 2024-12-02 12:08:13 +08:00
Merge branch 'baidu:master' into master
This commit is contained in:
commit
d3dfb0da7c
1
.husky/.gitignore
vendored
Normal file
1
.husky/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
_
|
@ -1,4 +1,4 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
. "$(dirname "$0")/_/husky.sh"
|
. "$(dirname "$0")/_/husky.sh"
|
||||||
|
|
||||||
npx pretty-quick --staged
|
npx lint-staged
|
||||||
|
@ -80,6 +80,11 @@ npm run update-snapshot
|
|||||||
- [RickCole21](https://github.com/RickCole21)
|
- [RickCole21](https://github.com/RickCole21)
|
||||||
- [catchonme](https://github.com/catchonme)
|
- [catchonme](https://github.com/catchonme)
|
||||||
- [nwind](https://github.com/nwind)
|
- [nwind](https://github.com/nwind)
|
||||||
|
- [zhangtao07](https://github.com/zhangtao07)
|
||||||
|
- [hsm-lv](https://github.com/hsm-lv)
|
||||||
|
- [RUNZE LU](https://github.com/lurunze1226)
|
||||||
|
- [ucasliyuan](https://github.com/ucasliyuan)
|
||||||
|
- [yangwei9012](https://github.com/yangwei9012)
|
||||||
|
|
||||||
## 低代码平台
|
## 低代码平台
|
||||||
|
|
||||||
|
@ -362,6 +362,72 @@ exports[`Renderer:formula 1`] = `
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
class="cxd-Form-item cxd-Form-item--normal"
|
||||||
|
data-role="form-item"
|
||||||
|
>
|
||||||
|
<label
|
||||||
|
class="cxd-Form-label"
|
||||||
|
>
|
||||||
|
<span>
|
||||||
|
<span
|
||||||
|
class="cxd-TplField"
|
||||||
|
>
|
||||||
|
<span>
|
||||||
|
sum4
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
<div
|
||||||
|
class="cxd-NumberControl cxd-Form-control"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="cxd-Number cxd-Number--borderFull"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="cxd-Number-handler-wrap"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
aria-disabled="false"
|
||||||
|
aria-label="Increase Value"
|
||||||
|
class="cxd-Number-handler cxd-Number-handler-up"
|
||||||
|
role="button"
|
||||||
|
unselectable="on"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="cxd-Number-handler-up-inner"
|
||||||
|
unselectable="on"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
aria-disabled="false"
|
||||||
|
aria-label="Decrease Value"
|
||||||
|
class="cxd-Number-handler cxd-Number-handler-down"
|
||||||
|
role="button"
|
||||||
|
unselectable="on"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="cxd-Number-handler-down-inner"
|
||||||
|
unselectable="on"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="cxd-Number-input-wrap"
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
aria-valuenow="5"
|
||||||
|
autocomplete="off"
|
||||||
|
class="cxd-Number-input"
|
||||||
|
role="spinbutton"
|
||||||
|
step="1"
|
||||||
|
value="5"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
|
@ -53,43 +53,53 @@ exports[`Renderer:rating 1`] = `
|
|||||||
class="cxd-RatingControl cxd-Form-control"
|
class="cxd-RatingControl cxd-Form-control"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class=""
|
class="cxd-Rating"
|
||||||
>
|
>
|
||||||
<span
|
<ul>
|
||||||
class="cxd-Rating is-active"
|
<li
|
||||||
data-forhalf="★"
|
class="cxd-Rating-star is-active"
|
||||||
data-index="0"
|
style="color: rgb(120, 123, 129);"
|
||||||
>
|
>
|
||||||
★
|
<icon-mock
|
||||||
</span>
|
classname="icon icon-star"
|
||||||
<span
|
icon="star"
|
||||||
class="cxd-Rating is-active"
|
/>
|
||||||
data-forhalf="★"
|
</li>
|
||||||
data-index="1"
|
<li
|
||||||
>
|
class="cxd-Rating-star is-active"
|
||||||
★
|
style="color: rgb(120, 123, 129);"
|
||||||
</span>
|
>
|
||||||
<span
|
<icon-mock
|
||||||
class="cxd-Rating is-active"
|
classname="icon icon-star"
|
||||||
data-forhalf="★"
|
icon="star"
|
||||||
data-index="2"
|
/>
|
||||||
>
|
</li>
|
||||||
★
|
<li
|
||||||
</span>
|
class="cxd-Rating-star is-active"
|
||||||
<span
|
style="color: rgb(120, 123, 129);"
|
||||||
class="cxd-Rating"
|
>
|
||||||
data-forhalf="★"
|
<icon-mock
|
||||||
data-index="3"
|
classname="icon icon-star"
|
||||||
>
|
icon="star"
|
||||||
★
|
/>
|
||||||
</span>
|
</li>
|
||||||
<span
|
<li
|
||||||
class="cxd-Rating"
|
class="cxd-Rating-star"
|
||||||
data-forhalf="★"
|
>
|
||||||
data-index="4"
|
<icon-mock
|
||||||
>
|
classname="icon icon-star"
|
||||||
★
|
icon="star"
|
||||||
</span>
|
/>
|
||||||
|
</li>
|
||||||
|
<li
|
||||||
|
class="cxd-Rating-star"
|
||||||
|
>
|
||||||
|
<icon-mock
|
||||||
|
classname="icon icon-star"
|
||||||
|
icon="star"
|
||||||
|
/>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -36,6 +36,11 @@ test('Renderer:formula', async () => {
|
|||||||
name: 'sum3',
|
name: 'sum3',
|
||||||
label: 'sum3'
|
label: 'sum3'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
type: 'number',
|
||||||
|
name: 'sum4',
|
||||||
|
label: 'sum4'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
type: 'formula',
|
type: 'formula',
|
||||||
name: 'sum1',
|
name: 'sum1',
|
||||||
@ -55,6 +60,13 @@ test('Renderer:formula', async () => {
|
|||||||
condition: 'data.b',
|
condition: 'data.b',
|
||||||
value: 0,
|
value: 0,
|
||||||
formula: 'a + b + 2'
|
formula: 'a + b + 2'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'formula',
|
||||||
|
name: 'sum4',
|
||||||
|
condition: 'data.b',
|
||||||
|
value: 0,
|
||||||
|
formula: '${a + b + 2}'
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
title: 'The form',
|
title: 'The form',
|
||||||
@ -80,6 +92,7 @@ test('Renderer:formula', async () => {
|
|||||||
expect(inputs[2].value).toBe('3');
|
expect(inputs[2].value).toBe('3');
|
||||||
expect(inputs[3].value).toBe('4');
|
expect(inputs[3].value).toBe('4');
|
||||||
expect(inputs[4].value).toBe('5');
|
expect(inputs[4].value).toBe('5');
|
||||||
|
expect(inputs[5].value).toBe('5');
|
||||||
|
|
||||||
expect(container).toMatchSnapshot();
|
expect(container).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
46
__tests__/renderers/Timeline.test.tsx
Normal file
46
__tests__/renderers/Timeline.test.tsx
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import React = require('react');
|
||||||
|
import {render, cleanup} from '@testing-library/react';
|
||||||
|
import '../../src/themes/default';
|
||||||
|
import {render as amisRender} from '../../src/index';
|
||||||
|
import {makeEnv} from '../helper';
|
||||||
|
import {clearStoresCache} from '../../src/factory';
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
cleanup();
|
||||||
|
clearStoresCache();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Renderer:timeline', async () => {
|
||||||
|
const {container} = render(
|
||||||
|
amisRender(
|
||||||
|
{
|
||||||
|
type: 'timeline',
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
time: "2019-02-07",
|
||||||
|
title: "节点数据",
|
||||||
|
color: "#ffb200",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
time: "2019-02-08",
|
||||||
|
title: "节点数据",
|
||||||
|
color: "#4F86F4",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
time: "2019-02-09",
|
||||||
|
title: "节点数据",
|
||||||
|
color: "success",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
time: "2019-02-09",
|
||||||
|
title: "节点数据",
|
||||||
|
color: "warning",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{},
|
||||||
|
makeEnv()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
expect(container).toMatchSnapshot();
|
||||||
|
});
|
@ -55,6 +55,26 @@ test('api:buildApi', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('api:buildApi2', () => {
|
||||||
|
expect(
|
||||||
|
buildApi('http://domain.com/#/subpath?a=1&b=2', {
|
||||||
|
a: 1
|
||||||
|
}).url
|
||||||
|
).toBe('http://domain.com/#/subpath?a=1&b=2');
|
||||||
|
|
||||||
|
expect(
|
||||||
|
buildApi('http://domain.com/subpath?a=1&b=2#233', {
|
||||||
|
a: 1
|
||||||
|
}).url
|
||||||
|
).toBe('http://domain.com/subpath?a=1&b=2#233');
|
||||||
|
|
||||||
|
expect(
|
||||||
|
buildApi('http://domain.com/subpath?a=1&b=${a}#233', {
|
||||||
|
a: 1
|
||||||
|
}).url
|
||||||
|
).toBe('http://domain.com/subpath?a=1&b=1#233');
|
||||||
|
});
|
||||||
|
|
||||||
test('api:buildApi:dataMapping', () => {
|
test('api:buildApi:dataMapping', () => {
|
||||||
expect(
|
expect(
|
||||||
buildApi(
|
buildApi(
|
||||||
|
9
__tests__/utils/filter.test.ts
Normal file
9
__tests__/utils/filter.test.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import {registerFilter, resolveVariableAndFilter} from '../../src';
|
||||||
|
|
||||||
|
test('filter:customFilter', () => {
|
||||||
|
registerFilter('customFilter', input => `233`);
|
||||||
|
|
||||||
|
expect(resolveVariableAndFilter('${a | customFilter}', {a: 'abc'})).toEqual(
|
||||||
|
'233'
|
||||||
|
);
|
||||||
|
});
|
@ -12,7 +12,7 @@ order: 99
|
|||||||
|
|
||||||
## 基本用法
|
## 基本用法
|
||||||
|
|
||||||
类型定义为 `app`,通过 pages 定义页面,支持层级,支持内嵌 schema,或者 通过 schemaApi 远程拉取页面,完整用法请参考 [amis-admin](https://github.com/aisuda/amis-admin) 项目
|
类型定义为 `app`,通过 pages 定义页面,支持层级,支持内嵌 schema,或者 通过 schemaApi 远程拉取页面,完整用法请参考 [amis-admin](https://github.com/aisuda/amis-admin) 项目里的代码示例,需要修改 `env`:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
@ -21,7 +21,6 @@ order: 99
|
|||||||
"pages": [
|
"pages": [
|
||||||
{
|
{
|
||||||
"label": "分组1",
|
"label": "分组1",
|
||||||
|
|
||||||
"children": [
|
"children": [
|
||||||
{
|
{
|
||||||
"label": "父页面",
|
"label": "父页面",
|
||||||
|
@ -43,7 +43,10 @@ order: 27
|
|||||||
|
|
||||||
## 动态图片或文字
|
## 动态图片或文字
|
||||||
|
|
||||||
src、text 都支持变量,可以从上下文中动态获取图片或文字,下面的例子中第一个获取到了,而第二个没获取到,因此降级为显示 icon
|
src、text 都支持变量,可以从上下文中动态获取图片或文字,下面的例子中:
|
||||||
|
- 第一个获取到了,显示正常
|
||||||
|
- 第二个没获取到,因此降级为显示 icon
|
||||||
|
- 第三个图片没获取到,由于 text 优先级比 icon 高,所以显示 text
|
||||||
|
|
||||||
```schema
|
```schema
|
||||||
{
|
{
|
||||||
@ -61,6 +64,12 @@ src、text 都支持变量,可以从上下文中动态获取图片或文字,
|
|||||||
"type": "avatar",
|
"type": "avatar",
|
||||||
"icon": "fa fa-user",
|
"icon": "fa fa-user",
|
||||||
"src": "$other"
|
"src": "$other"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "avatar",
|
||||||
|
"src": "$other",
|
||||||
|
"icon": "fa fa-user",
|
||||||
|
"text": "avatar"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -97,13 +106,52 @@ src、text 都支持变量,可以从上下文中动态获取图片或文字,
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"type": "avatar",
|
"type": "avatar",
|
||||||
"size": 20,
|
"size": 'large',
|
||||||
"src": "https://suda.cdn.bcebos.com/images/amis/ai-fake-face.jpg"
|
"icon": "fa fa-user"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "avatar",
|
||||||
|
"size": 'default',
|
||||||
|
"icon": "fa fa-user"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "avatar",
|
||||||
|
"size": 'small',
|
||||||
|
"icon": "fa fa-user"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "avatar",
|
"type": "avatar",
|
||||||
"size": 60,
|
"size": 60,
|
||||||
"src": "https://suda.cdn.bcebos.com/images/amis/ai-fake-face.jpg"
|
"src": "https://suda.cdn.bcebos.com/images/amis/ai-fake-face.jpg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "avatar",
|
||||||
|
"src": "https://suda.cdn.bcebos.com/images/amis/ai-fake-face.jpg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "avatar",
|
||||||
|
"size": 20,
|
||||||
|
"src": "https://suda.cdn.bcebos.com/images/amis/ai-fake-face.jpg"
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
## 控制字符类型距离左右两侧边界单位像素
|
||||||
|
|
||||||
|
通过 gap 可以控制字符类型距离左右两侧边界单位像素
|
||||||
|
|
||||||
|
```schema: scope="body"
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"type": "avatar",
|
||||||
|
"text": 'ejson',
|
||||||
|
"gap": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "avatar",
|
||||||
|
"text": "ejson",
|
||||||
|
"gap": 7
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -111,7 +159,7 @@ src、text 都支持变量,可以从上下文中动态获取图片或文字,
|
|||||||
|
|
||||||
## 图片拉伸方式
|
## 图片拉伸方式
|
||||||
|
|
||||||
通过 `fit` 可以控制图片拉伸方式,默认是 `cover`,具体细节可以参考 MDN [文档](https://developer.mozilla.org/zh-CN/docs/Web/CSS/object-fit)
|
通过 `fit` 可以控制图片拉伸方式,默认是 `'cover'`
|
||||||
|
|
||||||
```schema: scope="body"
|
```schema: scope="body"
|
||||||
[
|
[
|
||||||
@ -143,6 +191,39 @@ src、text 都支持变量,可以从上下文中动态获取图片或文字,
|
|||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## 控制图片是否允许拖动
|
||||||
|
|
||||||
|
通过 draggable 可以控制图片是否允许拖动
|
||||||
|
|
||||||
|
```schema: scope="body"
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"type": "avatar",
|
||||||
|
"fit": "cover",
|
||||||
|
"src": "https://suda.cdn.bcebos.com/images/amis/plumeria.jpeg",
|
||||||
|
"draggable": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "avatar",
|
||||||
|
"fit": "cover",
|
||||||
|
"src": "https://suda.cdn.bcebos.com/images/amis/plumeria.jpeg",
|
||||||
|
"draggable": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
## 图片加载失败后,通过 onError 控制是否进行 text、icon 置换
|
||||||
|
> 如果同时存在 text 和 icon,会优先用 text、接着 icon
|
||||||
|
|
||||||
|
```schema: scope="body"
|
||||||
|
{
|
||||||
|
"type": "avatar",
|
||||||
|
"src": "empty",
|
||||||
|
"text": "avatar",
|
||||||
|
"onError": "return true;"
|
||||||
|
},
|
||||||
|
```
|
||||||
|
|
||||||
## 样式
|
## 样式
|
||||||
|
|
||||||
可以通过 style 来控制背景及文字颜色
|
可以通过 style 来控制背景及文字颜色
|
||||||
@ -161,12 +242,17 @@ src、text 都支持变量,可以从上下文中动态获取图片或文字,
|
|||||||
## 属性表
|
## 属性表
|
||||||
|
|
||||||
| 属性名 | 类型 | 默认值 | 说明 |
|
| 属性名 | 类型 | 默认值 | 说明 |
|
||||||
| --------- | -------- | ------ | --------------------- |
|
| --------- | ----------- | ------ | --------------------- |
|
||||||
| className | `string` | | 外层 dom 的类名 |
|
| className | `string` | | 外层 dom 的类名 |
|
||||||
| fit | `string` | cover | 图片缩放类型 |
|
| style | `object` | | 外层 dom 的样式 |
|
||||||
| src | `string` | | 图片地址 |
|
| fit |`'contain'` \| `'cover'` \| `'fill'` \| `'none'` \| `'scale-down'` | `'cover'` | 具体细节可以参考 MDN [文档](https://developer.mozilla.org/zh-CN/docs/Web/CSS/object-fit) |
|
||||||
| text | `string` | | 文字 |
|
| src | `string` | | 图片地址 |
|
||||||
| icon | `string` | | 图标 |
|
| text | `string` | | 文字 |
|
||||||
| shape | `string` | circle | 形状,也可以是 square |
|
| icon | `string` | `'fa fa-user'` | 图标 |
|
||||||
| size | `number` | 40 | 大小 |
|
| shape | `'circle'` \| `'square'` \| `'rounded'` | `'circle'` | 形状,有三种 `'circle'` (圆形)、`'square'`(正方形)、`'rounded'`(圆角) |
|
||||||
| style | `object` | | 外层 dom 的样式 |
|
| size | `number` \| `'default'` \| `'normal'` \| `'small'` | `'default'` | `'default' \| 'normal' \| 'small'`三种字符串类型代表不同大小(分别是48、40、32),也可以直接数字表示 |
|
||||||
|
| gap | `number` | 4 | 控制字符类型距离左右两侧边界单位像素 |
|
||||||
|
| alt | `number` | | 图像无法显示时的替代文本 |
|
||||||
|
| draggable | `boolean` | | 图片是否允许拖动 |
|
||||||
|
| crossOrigin | `'anonymous'` \| `'use-credentials'` \| `''` | | 图片的 `CORS` 属性设置 |
|
||||||
|
| onError | `string` | | 图片加载失败的字符串,这个字符串是一个New Function内部执行的字符串,参数是event(使用event.nativeEvent获取原生dom事件),这个字符串需要返回boolean值。设置 `"return ture;"` 会在图片加载失败后,使用 `text` 或者 `icon` 代表的信息来进行替换。目前图片加载失败默认是不进行置换。注意:图片加载失败,不包括$获取数据为空情况 |
|
||||||
|
@ -29,7 +29,7 @@ order: 29
|
|||||||
| 属性名 | 类型 | 默认值 | 说明 |
|
| 属性名 | 类型 | 默认值 | 说明 |
|
||||||
| ---------------- | -------------------------------------------------------------------------------------------------------------------------------- | ------- | ----------------------------------------------------- |
|
| ---------------- | -------------------------------------------------------------------------------------------------------------------------------- | ------- | ----------------------------------------------------- |
|
||||||
| className | `string` | | 指定添加 button 类名 |
|
| className | `string` | | 指定添加 button 类名 |
|
||||||
| href | `string` | | 点击跳转的地址,指定此属性 button 的行为和 a 链接一致 |
|
| url | `string` | | 点击跳转的地址,指定此属性 button 的行为和 a 链接一致 |
|
||||||
| size | `'xs' \| 'sm' \| 'md' \| 'lg' ` | | 设置按钮大小 |
|
| size | `'xs' \| 'sm' \| 'md' \| 'lg' ` | | 设置按钮大小 |
|
||||||
| actionType | `'button' \| 'reset' \| 'submit'\| 'clear'\| 'url'` | button | 设置按钮类型 |
|
| actionType | `'button' \| 'reset' \| 'submit'\| 'clear'\| 'url'` | button | 设置按钮类型 |
|
||||||
| level | `'link' \| 'primary' \| 'enhance' \| 'secondary' \| 'info'\|'success' \| 'warning' \| 'danger' \| 'light'\| 'dark' \| 'default'` | default | 设置按钮样式 |
|
| level | `'link' \| 'primary' \| 'enhance' \| 'secondary' \| 'info'\|'success' \| 'warning' \| 'danger' \| 'light'\| 'dark' \| 'default'` | default | 设置按钮样式 |
|
||||||
|
@ -175,6 +175,30 @@ order: 22
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## 使用新表达式语法
|
||||||
|
|
||||||
|
> 1.5.0 及以上版本
|
||||||
|
|
||||||
|
通过新的[表达式](../../../docs/concepts/expression)语法,可以调用其中的函数,比如
|
||||||
|
|
||||||
|
```schema: scope="body"
|
||||||
|
{
|
||||||
|
"type": "form",
|
||||||
|
"debug": true,
|
||||||
|
"body": [
|
||||||
|
{
|
||||||
|
"type": "static",
|
||||||
|
"value": "这个表单没有内容,通过上面的 debug 可以看到输出当前日期"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "formula",
|
||||||
|
"name": "date",
|
||||||
|
"formula": "${DATETOSTR(NOW(), 'YYYY-MM-DD')}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## 属性表
|
## 属性表
|
||||||
|
|
||||||
| 属性名 | 类型 | 默认值 | 说明 |
|
| 属性名 | 类型 | 默认值 | 说明 |
|
||||||
|
68
docs/zh-CN/components/form/input-formula.md
Normal file
68
docs/zh-CN/components/form/input-formula.md
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
---
|
||||||
|
title: InputFormula 公式编辑器
|
||||||
|
description:
|
||||||
|
type: 0
|
||||||
|
group: null
|
||||||
|
menuName: InputFormula
|
||||||
|
icon:
|
||||||
|
order: 21
|
||||||
|
---
|
||||||
|
|
||||||
|
## 基本用法
|
||||||
|
|
||||||
|
用来输入公式。还是 beta 版本,整体待优化。
|
||||||
|
|
||||||
|
```schema: scope="body"
|
||||||
|
{
|
||||||
|
"type": "form",
|
||||||
|
"debug": true,
|
||||||
|
"body": [
|
||||||
|
{
|
||||||
|
"type": "input-formula",
|
||||||
|
"name": "formula",
|
||||||
|
"label": "公式",
|
||||||
|
"variableMode": "tabs",
|
||||||
|
"evalMode": false,
|
||||||
|
"value": "SUM(1 + 2)",
|
||||||
|
"variables": [
|
||||||
|
{
|
||||||
|
"label": "表单字段",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"label": "ID",
|
||||||
|
"value": "id"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "ID2",
|
||||||
|
"value": "id2"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "流程字段",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"label": "ID",
|
||||||
|
"value": "id"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "ID2",
|
||||||
|
"value": "id2"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 属性表
|
||||||
|
|
||||||
|
| 属性名 | 类型 | 默认值 | 说明 |
|
||||||
|
| ------------ | --------------------------------------------------- | ------ | ------------------------------------------------------------------------------ |
|
||||||
|
| header | string | | 弹出来的弹框标题 |
|
||||||
|
| evalMode | Boolean | true | 表达式模式 或者 模板模式,模板模式则需要将表达式写在 `${` 和 `}` 中间。 |
|
||||||
|
| variables | {label: string; value: string; children?: any[];}[] | [] | 可用变量 |
|
||||||
|
| variableMode | string | `list` | 可配置成 `tabs` 或者 `tree` 默认为列表,支持分组。 |
|
||||||
|
| functions | Object[] | | 可以不设置,默认就是 amis-formula 里面定义的函数,如果扩充了新的函数则需要指定 |
|
@ -10,6 +10,8 @@ order: 37
|
|||||||
|
|
||||||
## 基本用法
|
## 基本用法
|
||||||
|
|
||||||
|
默认颜色
|
||||||
|
|
||||||
```schema: scope="body"
|
```schema: scope="body"
|
||||||
{
|
{
|
||||||
"type": "form",
|
"type": "form",
|
||||||
@ -18,7 +20,121 @@ order: 37
|
|||||||
{
|
{
|
||||||
"type": "input-rating",
|
"type": "input-rating",
|
||||||
"name": "rating",
|
"name": "rating",
|
||||||
"label": "评分"
|
"label": "评分",
|
||||||
|
"count": 5,
|
||||||
|
"value": 3
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
自定义颜色。支持各种颜色形式,如 CSS 预定义颜色,十六进制颜色,RGB 颜色,HSL 颜色。
|
||||||
|
|
||||||
|
```schema: scope="body"
|
||||||
|
{
|
||||||
|
"type": "form",
|
||||||
|
"api": "/api/mock2/form/saveForm",
|
||||||
|
"body": [
|
||||||
|
{
|
||||||
|
"type": "input-rating",
|
||||||
|
"name": "rating",
|
||||||
|
"label": "评分",
|
||||||
|
"count": 5,
|
||||||
|
"value": 3,
|
||||||
|
"colors": {
|
||||||
|
"1": "gray",
|
||||||
|
"2": "#678f8d",
|
||||||
|
"3": "rgb(119, 168, 141)",
|
||||||
|
"4": "hsl(147, 22%, 56%)",
|
||||||
|
"5": "#ff6670"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 半星
|
||||||
|
|
||||||
|
```schema: scope="body"
|
||||||
|
{
|
||||||
|
"type": "form",
|
||||||
|
"api": "/api/mock2/form/saveForm",
|
||||||
|
"body": [
|
||||||
|
{
|
||||||
|
"type": "input-rating",
|
||||||
|
"name": "rating",
|
||||||
|
"label": "评分",
|
||||||
|
"count": 5,
|
||||||
|
"value": 3.5,
|
||||||
|
"half": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 带有文字
|
||||||
|
|
||||||
|
```schema: scope="body"
|
||||||
|
{
|
||||||
|
"type": "form",
|
||||||
|
"api": "/api/mock2/form/saveForm",
|
||||||
|
"body": [
|
||||||
|
{
|
||||||
|
"type": "input-rating",
|
||||||
|
"name": "rating",
|
||||||
|
"label": "评分",
|
||||||
|
"count": 5,
|
||||||
|
"value": 3,
|
||||||
|
"texts": {
|
||||||
|
"1": "很差",
|
||||||
|
"2": "较差",
|
||||||
|
"3": "一般",
|
||||||
|
"4": "较好",
|
||||||
|
"5": "很好"
|
||||||
|
},
|
||||||
|
"textClassName": "okde"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 自定义字符
|
||||||
|
|
||||||
|
```schema: scope="body"
|
||||||
|
{
|
||||||
|
"type": "form",
|
||||||
|
"api": "/api/mock2/form/saveForm",
|
||||||
|
"body": [
|
||||||
|
{
|
||||||
|
"type": "input-rating",
|
||||||
|
"name": "rating",
|
||||||
|
"label": "评分",
|
||||||
|
"count": 5,
|
||||||
|
"value": 3,
|
||||||
|
"char": "好"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 只读
|
||||||
|
|
||||||
|
```schema: scope="body"
|
||||||
|
{
|
||||||
|
"type": "form",
|
||||||
|
"api": "/api/mock2/form/saveForm",
|
||||||
|
"body": [
|
||||||
|
{
|
||||||
|
"type": "input-rating",
|
||||||
|
"name": "rating",
|
||||||
|
"label": "评分",
|
||||||
|
"count": 5,
|
||||||
|
"value": 3.6,
|
||||||
|
"half": true,
|
||||||
|
"readOnly": true,
|
||||||
|
"texts": {
|
||||||
|
"5": "3.6"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -28,9 +144,18 @@ order: 37
|
|||||||
|
|
||||||
当做选择器表单项使用时,除了支持 [普通表单项属性表](./formitem#%E5%B1%9E%E6%80%A7%E8%A1%A8) 中的配置以外,还支持下面一些配置
|
当做选择器表单项使用时,除了支持 [普通表单项属性表](./formitem#%E5%B1%9E%E6%80%A7%E8%A1%A8) 中的配置以外,还支持下面一些配置
|
||||||
|
|
||||||
| 属性名 | 类型 | 默认值 | 说明 |
|
| 属性名 | 类型 | 默认值 | 说明 |
|
||||||
| ---------- | --------- | ------- | ---------------------- |
|
| ------------- | ------------------- | --------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
|
||||||
| half | `boolean` | `false` | 是否使用半星选择 |
|
| value | `number` | - | 当前值 |
|
||||||
| count | `number` | `5` | 共有多少星可供选择 |
|
| half | `boolean` | `false` | 是否使用半星选择 |
|
||||||
| readOnly | `boolean` | `false` | 只读 |
|
| count | `number` | `5` | 总星数 |
|
||||||
| allowClear | `boolean` | `true` | 是否允许再次点击后清除 |
|
| readOnly | `boolean` | `false` | 只读 |
|
||||||
|
| allowClear | `boolean` | `true` | 是否允许再次点击后清除 |
|
||||||
|
| colors | `string` / `object` | `{'2': '#abadb1', '3': '#787b81', '5': '#ffa900' }` | 星星被选中的颜色。 若传入字符串,则只有一种颜色。若传入对象,可自定义分段,键名为分段的界限值,键值为对应的类名 |
|
||||||
|
| inactiveColor | `string` | `#e7e7e8` | 未被选中的星星的颜色 |
|
||||||
|
| texts | `object` | - | 星星被选中时的提示文字。可自定义分段,键名为分段的界限值,键值为对应的类名 |
|
||||||
|
| textPosition | `right` / `left` | `right` | 文字的位置 |
|
||||||
|
| char | `string` | `★` | 自定义字符 |
|
||||||
|
| className | `string` | - | 自定义样式类名 |
|
||||||
|
| charClassName | `string` | - | 自定义字符类名 |
|
||||||
|
| textClassName | `string` | - | 自定义文字类名 |
|
||||||
|
@ -1278,7 +1278,8 @@ order: 2
|
|||||||
"label": "选项",
|
"label": "选项",
|
||||||
"name": "select",
|
"name": "select",
|
||||||
"autoFill": {
|
"autoFill": {
|
||||||
"option": "${label}"
|
"option.instantValidate": "${label}",
|
||||||
|
"option.submitValidate": "${label}",
|
||||||
},
|
},
|
||||||
"clearable": true,
|
"clearable": true,
|
||||||
"options": [
|
"options": [
|
||||||
@ -1293,15 +1294,38 @@ order: 2
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "text",
|
"type": "input-text",
|
||||||
"name": "option",
|
"name": "option.instantValidate",
|
||||||
"label": "选中项"
|
"label": "选中项",
|
||||||
|
"description": "填充后立即校验",
|
||||||
|
"required": true,
|
||||||
|
"validateOnChange": true,
|
||||||
|
"validations": {
|
||||||
|
"equals": "Option B"
|
||||||
|
},
|
||||||
|
"validationErrors": {
|
||||||
|
"equals": "校验失败,数据必须为Option B"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "input-text",
|
||||||
|
"name": "option.submitValidate",
|
||||||
|
"label": "选中项1",
|
||||||
|
"description": "填充后提交表单时才校验",
|
||||||
|
"required": true,
|
||||||
|
"validations": {
|
||||||
|
"equals": "Option B"
|
||||||
|
},
|
||||||
|
"validationErrors": {
|
||||||
|
"equals": "校验失败,数据必须为Option B"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
上例中我们配置了`"autoFill": {"option": "${label}"}`,表示将选中项中的`label`的值,自动填充到当前表单项中`name`为`option`的文本框中。
|
上例中我们配置了`"autoFill": {"option.instantValidate": "${label}"}`,表示将选中项中的`label`的值,自动填充到当前表单项中`name`为`option.instantValidate`的文本框中。可以额外配置`"validateOnChange": true`,实现自动填充后立即校验填充项。
|
||||||
|
|
||||||
|
|
||||||
**多选模式**
|
**多选模式**
|
||||||
|
|
||||||
|
@ -93,6 +93,5 @@ order: 51
|
|||||||
| option | `string` | | 选项说明 |
|
| option | `string` | | 选项说明 |
|
||||||
| onText | `string` | | 开启时的文本 |
|
| onText | `string` | | 开启时的文本 |
|
||||||
| offText | `string` | | 关闭时的文本 |
|
| offText | `string` | | 关闭时的文本 |
|
||||||
| trueValue | `any` | `true` | 标识真值 |
|
| trueValue | `boolean / string / number` | `true` | 标识真值 |
|
||||||
| falseValue | `any` | `"false"` | 标识假值 |
|
| falseValue | `boolean / string / number` | `"false"` | 标识假值 |
|
||||||
| option | `string` | | 选项说明 |
|
|
||||||
|
@ -17,106 +17,131 @@ icon:
|
|||||||
"api": "/api/mock2/form/saveForm",
|
"api": "/api/mock2/form/saveForm",
|
||||||
"body": [
|
"body": [
|
||||||
{
|
{
|
||||||
"label": "组合穿梭器",
|
"label": "选人",
|
||||||
"type": "tabs-transfer-picker",
|
"type": "tabs-transfer-picker",
|
||||||
"name": "a",
|
"name": "a",
|
||||||
"sortable": true,
|
"sortable": true,
|
||||||
"selectMode": "tree",
|
"selectMode": "tree",
|
||||||
"searchable": true,
|
"searchable": true,
|
||||||
"pickerSize": "md",
|
"pickerSize": "md",
|
||||||
"options": [
|
"menuTpl": "<div class='flex justify-between'><span>${label}</span>${email ? `<div class='text-muted m-r-xs text-sm text-right'>${email}<br />${phone}</div>`: ''}</div>",
|
||||||
{
|
"valueTpl": "${label}(${value})",
|
||||||
"label": "成员",
|
"options": [
|
||||||
"selectMode": "tree",
|
{
|
||||||
"children": [
|
"label": "成员",
|
||||||
{
|
"selectMode": "tree",
|
||||||
"label": "法师",
|
"children": [
|
||||||
"children": [
|
{
|
||||||
{
|
"label": "法师",
|
||||||
"label": "诸葛亮",
|
"children": [
|
||||||
"value": "zhugeliang"
|
{
|
||||||
}
|
"label": "诸葛亮",
|
||||||
]
|
"value": "zhugeliang",
|
||||||
},
|
"email": "zhugeliang@timi.com",
|
||||||
{
|
"phone": 13111111111
|
||||||
"label": "战士",
|
}
|
||||||
"children": [
|
]
|
||||||
{
|
},
|
||||||
"label": "曹操",
|
{
|
||||||
"value": "caocao"
|
"label": "战士",
|
||||||
},
|
"children": [
|
||||||
{
|
{
|
||||||
"label": "钟无艳",
|
"label": "曹操",
|
||||||
"value": "zhongwuyan"
|
"value": "caocao",
|
||||||
}
|
"email": "caocao@timi.com",
|
||||||
]
|
"phone": 13111111111
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"label": "打野",
|
"label": "钟无艳",
|
||||||
"children": [
|
"value": "zhongwuyan",
|
||||||
{
|
"email": "zhongwuyan@timi.com",
|
||||||
"label": "李白",
|
"phone": 13111111111
|
||||||
"value": "libai"
|
}
|
||||||
},
|
]
|
||||||
{
|
},
|
||||||
"label": "韩信",
|
{
|
||||||
"value": "hanxin"
|
"label": "打野",
|
||||||
},
|
"children": [
|
||||||
{
|
{
|
||||||
"label": "云中君",
|
"label": "李白",
|
||||||
"value": "yunzhongjun"
|
"value": "libai",
|
||||||
}
|
"email": "libai@timi.com",
|
||||||
]
|
"phone": 13111111111
|
||||||
}
|
},
|
||||||
]
|
{
|
||||||
},
|
"label": "韩信",
|
||||||
{
|
"value": "hanxin",
|
||||||
"label": "用户",
|
"email": "hanxin@timi.com",
|
||||||
"selectMode": "chained",
|
"phone": 13111111111
|
||||||
"children": [
|
},
|
||||||
{
|
{
|
||||||
"label": "法师",
|
"label": "云中君",
|
||||||
"children": [
|
"value": "yunzhongjun",
|
||||||
{
|
"email": "yunzhongjun@timi.com",
|
||||||
"label": "诸葛亮",
|
"phone": 13111111111
|
||||||
"value": "zhugeliang"
|
}
|
||||||
}
|
]
|
||||||
]
|
}
|
||||||
},
|
]
|
||||||
{
|
},
|
||||||
"label": "战士",
|
{
|
||||||
"children": [
|
"label": "角色",
|
||||||
{
|
"selectMode": "list",
|
||||||
"label": "曹操",
|
"children": [
|
||||||
"value": "caocao"
|
{
|
||||||
},
|
"label": "角色 1",
|
||||||
{
|
"value": "role1",
|
||||||
"label": "钟无艳",
|
},
|
||||||
"value": "zhongwuyan"
|
{
|
||||||
}
|
"label": "角色 2",
|
||||||
]
|
"value": "role2",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"label": "打野",
|
"label": "角色 3",
|
||||||
"children": [
|
"value": "role3",
|
||||||
{
|
},
|
||||||
"label": "李白",
|
{
|
||||||
"value": "libai"
|
"label": "角色 4",
|
||||||
},
|
"value": "role4",
|
||||||
{
|
}
|
||||||
"label": "韩信",
|
]
|
||||||
"value": "hanxin"
|
},
|
||||||
},
|
{
|
||||||
{
|
"label": "部门",
|
||||||
"label": "云中君",
|
"selectMode": "tree",
|
||||||
"value": "yunzhongjun"
|
"children": [
|
||||||
}
|
{
|
||||||
]
|
"label": "总部",
|
||||||
}
|
"value": "dep0",
|
||||||
]
|
"children": [
|
||||||
}
|
{
|
||||||
]
|
"label": "部门 1",
|
||||||
}
|
"value": "dep1",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"label": "部门 4",
|
||||||
|
"value": "dep4",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "部门 5",
|
||||||
|
"value": "dep5",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "部门 2",
|
||||||
|
"value": "dep2",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "部门 3",
|
||||||
|
"value": "dep3",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
@ -118,6 +118,101 @@ icon:
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## 自定义选项展示
|
||||||
|
|
||||||
|
```schema: scope="body"
|
||||||
|
{
|
||||||
|
"type": "form",
|
||||||
|
"api": "/api/mock2/form/saveForm",
|
||||||
|
"body": [
|
||||||
|
{
|
||||||
|
"label": "组合穿梭器",
|
||||||
|
"type": "tabs-transfer",
|
||||||
|
"name": "a",
|
||||||
|
"sortable": true,
|
||||||
|
"selectMode": "tree",
|
||||||
|
"searchable": true,
|
||||||
|
"menuTpl": "<div class='flex justify-between'><span>${label}</span><span class='text-muted m-r text-sm'>${tag}</span></div>",
|
||||||
|
"valueTpl": "${label}(${value})",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"label": "成员",
|
||||||
|
"selectMode": "list",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"label": "诸葛亮",
|
||||||
|
"value": "zhugeliang",
|
||||||
|
"tag": "法师",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "曹操",
|
||||||
|
"value": "caocao",
|
||||||
|
"tag": "战士",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "钟无艳",
|
||||||
|
"value": "zhongwuyan",
|
||||||
|
"tag": "战士",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "李白",
|
||||||
|
"value": "libai",
|
||||||
|
"tag": "打野"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "韩信",
|
||||||
|
"value": "hanxin",
|
||||||
|
"tag": "打野"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "云中君",
|
||||||
|
"value": "yunzhongjun",
|
||||||
|
"tag": "打野"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "用户",
|
||||||
|
"selectMode": "list",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"label": "诸葛亮",
|
||||||
|
"value": "zhugeliang",
|
||||||
|
"tag": "法师",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "曹操",
|
||||||
|
"value": "caocao",
|
||||||
|
"tag": "战士",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "钟无艳",
|
||||||
|
"value": "zhongwuyan",
|
||||||
|
"tag": "战士",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "李白",
|
||||||
|
"value": "libai",
|
||||||
|
"tag": "打野"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "韩信",
|
||||||
|
"value": "hanxin",
|
||||||
|
"tag": "打野"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "云中君",
|
||||||
|
"value": "yunzhongjun",
|
||||||
|
"tag": "打野"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## 属性表
|
## 属性表
|
||||||
|
|
||||||
更多配置请参考[穿梭器(Transfer)](./transfer)。
|
更多配置请参考[穿梭器(Transfer)](./transfer)。
|
||||||
|
@ -67,6 +67,55 @@ icon:
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## 自定义选项展示
|
||||||
|
|
||||||
|
```schema: scope="body"
|
||||||
|
{
|
||||||
|
"type": "form",
|
||||||
|
"body": [
|
||||||
|
{
|
||||||
|
"label": "默认",
|
||||||
|
"type": "transfer-picker",
|
||||||
|
"name": "transfer",
|
||||||
|
"menuTpl": "<div class='flex justify-between'><span>${label}</span><span class='text-muted m-r text-sm'>${tag}</span></div>",
|
||||||
|
"valueTpl": "${label}(${value})",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"label": "诸葛亮",
|
||||||
|
"value": "zhugeliang",
|
||||||
|
"tag": "法师",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "曹操",
|
||||||
|
"value": "caocao",
|
||||||
|
"tag": "战士",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "钟无艳",
|
||||||
|
"value": "zhongwuyan",
|
||||||
|
"tag": "战士",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "李白",
|
||||||
|
"value": "libai",
|
||||||
|
"tag": "打野"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "韩信",
|
||||||
|
"value": "hanxin",
|
||||||
|
"tag": "打野"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "云中君",
|
||||||
|
"value": "yunzhongjun",
|
||||||
|
"tag": "打野"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## 属性表
|
## 属性表
|
||||||
|
|
||||||
更多配置请参考[穿梭器(Transfer)](./transfer)。
|
更多配置请参考[穿梭器(Transfer)](./transfer)。
|
||||||
|
@ -559,26 +559,77 @@ leftOptions 动态加载,默认 source 接口是返回 options 部分,而 le
|
|||||||
|
|
||||||
适用于需选择的数据/信息源较多时,用户可直观的知道自己所选择的数据/信息的场景,一般左侧框为数据/信息源,右侧为已选数据/信息,被选中信息同时存在于 2 个框内。
|
适用于需选择的数据/信息源较多时,用户可直观的知道自己所选择的数据/信息的场景,一般左侧框为数据/信息源,右侧为已选数据/信息,被选中信息同时存在于 2 个框内。
|
||||||
|
|
||||||
|
## 自定义选项展示
|
||||||
|
|
||||||
|
```schema: scope="body"
|
||||||
|
{
|
||||||
|
"type": "form",
|
||||||
|
"body": [
|
||||||
|
{
|
||||||
|
"label": "默认",
|
||||||
|
"type": "transfer",
|
||||||
|
"name": "transfer",
|
||||||
|
"menuTpl": "<div class='flex justify-between'><span>${label}</span><span class='text-muted m-r text-sm'>${tag}</span></div>",
|
||||||
|
"valueTpl": "${label}(${value})",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"label": "诸葛亮",
|
||||||
|
"value": "zhugeliang",
|
||||||
|
"tag": "法师",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "曹操",
|
||||||
|
"value": "caocao",
|
||||||
|
"tag": "战士",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "钟无艳",
|
||||||
|
"value": "zhongwuyan",
|
||||||
|
"tag": "战士",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "李白",
|
||||||
|
"value": "libai",
|
||||||
|
"tag": "打野"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "韩信",
|
||||||
|
"value": "hanxin",
|
||||||
|
"tag": "打野"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "云中君",
|
||||||
|
"value": "yunzhongjun",
|
||||||
|
"tag": "打野"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## 属性表
|
## 属性表
|
||||||
|
|
||||||
除了支持 [普通表单项属性表](./formitem#%E5%B1%9E%E6%80%A7%E8%A1%A8) 中的配置以外,还支持下面一些配置
|
除了支持 [普通表单项属性表](./formitem#%E5%B1%9E%E6%80%A7%E8%A1%A8) 中的配置以外,还支持下面一些配置
|
||||||
|
|
||||||
| 属性名 | 类型 | 默认值 | 说明 |
|
| 属性名 | 类型 | 默认值 | 说明 |
|
||||||
| ---------------- | ----------------------------------------- | ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
| ---------------- | ----------------------------------------------------- | ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
| options | `Array<object>`或`Array<string>` | | [选项组](./options#%E9%9D%99%E6%80%81%E9%80%89%E9%A1%B9%E7%BB%84-options) |
|
| options | `Array<object>`或`Array<string>` | | [选项组](./options#%E9%9D%99%E6%80%81%E9%80%89%E9%A1%B9%E7%BB%84-options) |
|
||||||
| source | `string`或 [API](../../../docs/types/api) | | [动态选项组](./options#%E5%8A%A8%E6%80%81%E9%80%89%E9%A1%B9%E7%BB%84-source) |
|
| source | `string`或 [API](../../../docs/types/api) | | [动态选项组](./options#%E5%8A%A8%E6%80%81%E9%80%89%E9%A1%B9%E7%BB%84-source) |
|
||||||
| delimeter | `string` | `false` | [拼接符](./options#%E6%8B%BC%E6%8E%A5%E7%AC%A6-delimiter) |
|
| delimeter | `string` | `false` | [拼接符](./options#%E6%8B%BC%E6%8E%A5%E7%AC%A6-delimiter) |
|
||||||
| joinValues | `boolean` | `true` | [拼接值](./options#%E6%8B%BC%E6%8E%A5%E5%80%BC-joinvalues) |
|
| joinValues | `boolean` | `true` | [拼接值](./options#%E6%8B%BC%E6%8E%A5%E5%80%BC-joinvalues) |
|
||||||
| extractValue | `boolean` | `false` | [提取值](./options#%E6%8F%90%E5%8F%96%E5%A4%9A%E9%80%89%E5%80%BC-extractvalue) |
|
| extractValue | `boolean` | `false` | [提取值](./options#%E6%8F%90%E5%8F%96%E5%A4%9A%E9%80%89%E5%80%BC-extractvalue) |
|
||||||
| searchable | `boolean` | `false` | 当设置为 `true` 时表示可以通过输入部分内容检索出选项。 |
|
| searchable | `boolean` | `false` | 当设置为 `true` 时表示可以通过输入部分内容检索出选项。 |
|
||||||
| searchApi | [API](../../../docs/types/api) | | 如果想通过接口检索,可以设置个 api。 |
|
| searchApi | [API](../../../docs/types/api) | | 如果想通过接口检索,可以设置个 api。 |
|
||||||
| statistics | `boolean` | `true` | 是否显示统计数据 |
|
| statistics | `boolean` | `true` | 是否显示统计数据 |
|
||||||
| selectTitle | `string` | `"请选择"` | 左侧的标题文字 |
|
| selectTitle | `string` | `"请选择"` | 左侧的标题文字 |
|
||||||
| resultTitle | `string` | `"当前选择"` | 右侧结果的标题文字 |
|
| resultTitle | `string` | `"当前选择"` | 右侧结果的标题文字 |
|
||||||
| sortable | `boolean` | `false` | 结果可以进行拖拽排序 |
|
| sortable | `boolean` | `false` | 结果可以进行拖拽排序 |
|
||||||
| selectMode | `string` | `list` | 可选:`list`、`table`、`tree`、`chained`、`associated`。分别为:列表形式、表格形式、树形选择形式、级联选择形式,关联选择形式(与级联选择的区别在于,级联是无限极,而关联只有一级,关联左边可以是个 tree)。 |
|
| selectMode | `string` | `list` | 可选:`list`、`table`、`tree`、`chained`、`associated`。分别为:列表形式、表格形式、树形选择形式、级联选择形式,关联选择形式(与级联选择的区别在于,级联是无限极,而关联只有一级,关联左边可以是个 tree)。 |
|
||||||
| searchResultMode | `string` | | 如果不设置将采用 `selectMode` 的值,可以单独配置,参考 `selectMode`,决定搜索结果的展示形式。 |
|
| searchResultMode | `string` | | 如果不设置将采用 `selectMode` 的值,可以单独配置,参考 `selectMode`,决定搜索结果的展示形式。 |
|
||||||
| columns | `Array<Object>` | | 当展示形式为 `table` 可以用来配置展示哪些列,跟 table 中的 columns 配置相似,只是只有展示功能。 |
|
| columns | `Array<Object>` | | 当展示形式为 `table` 可以用来配置展示哪些列,跟 table 中的 columns 配置相似,只是只有展示功能。 |
|
||||||
| leftOptions | `Array<Object>` | | 当展示形式为 `associated` 时用来配置左边的选项集。 |
|
| leftOptions | `Array<Object>` | | 当展示形式为 `associated` 时用来配置左边的选项集。 |
|
||||||
| leftMode | `string` | | 当展示形式为 `associated` 时用来配置左边的选择形式,支持 `list` 或者 `tree`。默认为 `list`。 |
|
| leftMode | `string` | | 当展示形式为 `associated` 时用来配置左边的选择形式,支持 `list` 或者 `tree`。默认为 `list`。 |
|
||||||
| rightMode | `string` | | 当展示形式为 `associated` 时用来配置右边的选择形式,可选:`list`、`table`、`tree`、`chained`。 |
|
| rightMode | `string` | | 当展示形式为 `associated` 时用来配置右边的选择形式,可选:`list`、`table`、`tree`、`chained`。 |
|
||||||
|
| menuTpl | `string` \| [SchemaNode](../../docs/types/schemanode) | | 用来自定义选项展示 |
|
||||||
|
| valueTpl | `string` \| [SchemaNode](../../docs/types/schemanode) | | 用来自定义值的展示 |
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
title: Grid 水平布局
|
title: Grid 水平分栏
|
||||||
description:
|
description:
|
||||||
type: 0
|
type: 0
|
||||||
group: ⚙ 组件
|
group: ⚙ 组件
|
||||||
@ -10,6 +10,73 @@ order: 46
|
|||||||
|
|
||||||
## 基本用法
|
## 基本用法
|
||||||
|
|
||||||
|
默认会水平均分宽度
|
||||||
|
|
||||||
|
```schema: scope="body"
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"type": "grid",
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"columnClassName": "bg-green-300",
|
||||||
|
"body": [
|
||||||
|
{
|
||||||
|
"type": "plain",
|
||||||
|
"text": "第一栏"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"columnClassName": "bg-blue-300",
|
||||||
|
"body": [
|
||||||
|
{
|
||||||
|
"type": "plain",
|
||||||
|
"text": "第二栏"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "grid",
|
||||||
|
"className": "m-t",
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"columnClassName": "bg-green-300",
|
||||||
|
"body": [
|
||||||
|
{
|
||||||
|
"type": "plain",
|
||||||
|
"text": "第一栏"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"columnClassName": "bg-blue-300",
|
||||||
|
"body": [
|
||||||
|
{
|
||||||
|
"type": "plain",
|
||||||
|
"text": "第二栏"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"columnClassName": "bg-red-300",
|
||||||
|
"body": [
|
||||||
|
{
|
||||||
|
"type": "plain",
|
||||||
|
"text": "第三栏"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
## 响应式
|
||||||
|
|
||||||
|
通过 `md` 设置屏幕中等宽度(768px)情况下的分栏
|
||||||
|
|
||||||
```schema: scope="body"
|
```schema: scope="body"
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
|
390
docs/zh-CN/components/timeline.md
Normal file
390
docs/zh-CN/components/timeline.md
Normal file
@ -0,0 +1,390 @@
|
|||||||
|
---
|
||||||
|
title: Timeline 时间轴
|
||||||
|
description:
|
||||||
|
type: 0
|
||||||
|
group: ⚙ 组件
|
||||||
|
menuName: Timeline
|
||||||
|
icon:
|
||||||
|
order: 73
|
||||||
|
---
|
||||||
|
|
||||||
|
时间轴组件
|
||||||
|
|
||||||
|
## 基本用法
|
||||||
|
|
||||||
|
```schema
|
||||||
|
{
|
||||||
|
"type": "page",
|
||||||
|
"body": [
|
||||||
|
{
|
||||||
|
"type": "timeline",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"time": "2019-02-07",
|
||||||
|
"title": "节点数据",
|
||||||
|
"detail": "error",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"time": "2019-02-08",
|
||||||
|
"title": "节点数据",
|
||||||
|
"detail": "success",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"time": "2019-02-09",
|
||||||
|
"title": "节点数据",
|
||||||
|
"detail": "error",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 时间轴节点颜色设置
|
||||||
|
|
||||||
|
```schema
|
||||||
|
{
|
||||||
|
"type": "page",
|
||||||
|
"body": [
|
||||||
|
{
|
||||||
|
"type": "timeline",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"time": "2019-02-07",
|
||||||
|
"title": "节点数据",
|
||||||
|
"color": "#ffb200",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"time": "2019-02-08",
|
||||||
|
"title": "节点数据",
|
||||||
|
"color": "#4F86F4",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"time": "2019-02-09",
|
||||||
|
"title": "节点数据",
|
||||||
|
"color": "success",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"time": "2019-02-09",
|
||||||
|
"title": "节点数据",
|
||||||
|
"color": "warning",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 时间轴节点图标设置
|
||||||
|
|
||||||
|
```schema
|
||||||
|
{
|
||||||
|
"type": "page",
|
||||||
|
"body": [
|
||||||
|
{
|
||||||
|
"type": "timeline",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"time": "2019-02-07",
|
||||||
|
"title": "节点数据error",
|
||||||
|
"detail": "error",
|
||||||
|
"icon": "status-fail"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"time": "2019-02-08",
|
||||||
|
"title": "节点数据success",
|
||||||
|
"detail": "success",
|
||||||
|
"icon": "status-success"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"time": "2019-02-09",
|
||||||
|
"title": "节点数据warning",
|
||||||
|
"detail": "warning",
|
||||||
|
"icon": "status-warning"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 节点标题自定义
|
||||||
|
|
||||||
|
```schema
|
||||||
|
{
|
||||||
|
"type": "page",
|
||||||
|
"body": [
|
||||||
|
{
|
||||||
|
"type": "timeline",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"time": "2019-02-07",
|
||||||
|
"title": [
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"value": "2019年02月7日"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "button",
|
||||||
|
"label": "查看",
|
||||||
|
"actionType": "dialog",
|
||||||
|
"level": "link",
|
||||||
|
"dialog": {
|
||||||
|
"title": "查看详情",
|
||||||
|
"body": "这是详细内容。"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "button",
|
||||||
|
"label": "删除",
|
||||||
|
"level": "link",
|
||||||
|
"actionType": "dialog",
|
||||||
|
"dialog": {
|
||||||
|
"title": "删除",
|
||||||
|
"body": "确认删除吗?"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"time": "2019-02-10",
|
||||||
|
"title": [
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"value": "2019年02月10日"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "button",
|
||||||
|
"label": "查看",
|
||||||
|
"actionType": "dialog",
|
||||||
|
"level": "link",
|
||||||
|
"dialog": {
|
||||||
|
"title": "查看详情",
|
||||||
|
"body": "这是详细内容。"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "button",
|
||||||
|
"label": "删除",
|
||||||
|
"level": "link",
|
||||||
|
"actionType": "dialog",
|
||||||
|
"dialog": {
|
||||||
|
"title": "删除",
|
||||||
|
"body": "确认删除吗?"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 设置节点数据倒序
|
||||||
|
|
||||||
|
```schema
|
||||||
|
{
|
||||||
|
"type": "page",
|
||||||
|
"body": [
|
||||||
|
{
|
||||||
|
"type": "timeline",
|
||||||
|
direction: "vertical",
|
||||||
|
reverse: true,
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"time": "2019-02-07",
|
||||||
|
"title": "节点数据",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"time": "2019-02-08",
|
||||||
|
"title": "节点数据",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"time": "2019-02-09",
|
||||||
|
"title": "节点数据",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"time": "2019-02-10",
|
||||||
|
"title": "节点数据",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 设置时间轴方向
|
||||||
|
|
||||||
|
```schema
|
||||||
|
{
|
||||||
|
"type": "page",
|
||||||
|
"body": [
|
||||||
|
{
|
||||||
|
"type": "timeline",
|
||||||
|
direction: "horizontal",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"time": "2019-02-07",
|
||||||
|
"title": "节点数据",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"time": "2019-02-08",
|
||||||
|
"title": "节点数据",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"time": "2019-02-09",
|
||||||
|
"title": "节点数据",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"time": "2019-02-10",
|
||||||
|
"title": "节点数据",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 设置文字相对时间轴方向(时间轴横向时不支持)
|
||||||
|
### 文字位于时间轴左侧
|
||||||
|
|
||||||
|
```schema
|
||||||
|
{
|
||||||
|
"type": "page",
|
||||||
|
"body": [
|
||||||
|
{
|
||||||
|
"type": "timeline",
|
||||||
|
"mode": "left",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"time": "2019-02-07",
|
||||||
|
"title": "节点数据",
|
||||||
|
"detail": "error",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"time": "2019-02-08",
|
||||||
|
"title": "节点数据",
|
||||||
|
"detail": "success",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 文字交替位于时间轴两侧
|
||||||
|
|
||||||
|
```schema
|
||||||
|
{
|
||||||
|
"type": "page",
|
||||||
|
"body": [
|
||||||
|
{
|
||||||
|
"type": "timeline",
|
||||||
|
"mode": "alternate",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"time": "2019-02-07",
|
||||||
|
"title": "节点数据",
|
||||||
|
"detail": "error",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"time": "2019-02-08",
|
||||||
|
"title": "节点数据",
|
||||||
|
"detail": "success",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 文字位于时间轴右侧
|
||||||
|
|
||||||
|
```schema
|
||||||
|
{
|
||||||
|
"type": "page",
|
||||||
|
"body": [
|
||||||
|
{
|
||||||
|
"type": "timeline",
|
||||||
|
"mode": "right",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"time": "2019-02-07",
|
||||||
|
"title": "节点数据",
|
||||||
|
"detail": "error",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"time": "2019-02-08",
|
||||||
|
"title": "节点数据",
|
||||||
|
"detail": "success",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 远程数据
|
||||||
|
|
||||||
|
```schema
|
||||||
|
{
|
||||||
|
"type": "page",
|
||||||
|
"body": [
|
||||||
|
{
|
||||||
|
"type": "timeline",
|
||||||
|
"source": {
|
||||||
|
"method": "get",
|
||||||
|
"url": "/api/mock2/timeline/timelineItems"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
"source": "/api/mock2/timeline/timelineItems",
|
||||||
|
|
||||||
|
远程拉取接口时,返回的数据结构除了需要满足 amis 接口要求的基本数据结构 以外,必须用"items"作为时间轴数据的 key 值,如下:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"status": 0,
|
||||||
|
"msg": "",
|
||||||
|
"data": {
|
||||||
|
"items": [
|
||||||
|
{"time": "2019-02-07", "title": "数据开发", "detail": "2019-02-07detail", "color":"#ffb200", "icon": "close"},
|
||||||
|
{"time": "2019-02-08", "title": "管理中心", "detail": "2019-02-08detail" },
|
||||||
|
{"time": "2019-02-09", "title": "SQL语句", "detail": "2019-02-09detail", "color":"warning"},
|
||||||
|
{"time": "2019-02-10", "title": "一键部署", "detail": "2019-02-10detail", "icon": "compress-alt"},
|
||||||
|
{"time": "2019-02-10", "title": "一键部署", "detail": "2019-02-11detail"},
|
||||||
|
{"time": "2019-02-10", "title": "一键部署", "detail": "2019-02-12detail", "icon": "close"},
|
||||||
|
{"time": "2019-02-10", "title": "一键部署", "detail": "2019-02-13detail"}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 属性表
|
||||||
|
|
||||||
|
| 属性名 | 类型 | 默认值 | 说明 |
|
||||||
|
| --------- | --------------------------------------------------------------------------------- | ------------ | -------------------------------------------------------------------- |
|
||||||
|
| type | `string` | | `"timeline"` 指定为 时间轴 渲染器 |
|
||||||
|
| items | Array<[timelineItem](#timeline.item)> | [] | 配置节点数据 |
|
||||||
|
| source | [API](../../../docs/types/api) | | 数据源,可通过数据映射获取当前数据域变量、或者配置 API 对象 |
|
||||||
|
| mode | `left` \| `right` \| `alternate` | `right` | 指定文字相对于时间轴的位置,仅 direction=vertical时支持 |
|
||||||
|
| direction | `vertical` \| `horizontal` | `vertical` | 时间轴方向 | |
|
||||||
|
| reverse | `boolean` | `false` | 根据时间倒序显示
|
||||||
|
|
||||||
|
### timeline.item
|
||||||
|
|
||||||
|
| 属性名 | 类型 | 默认值 | 说明 |
|
||||||
|
| ----------- | ----------------------------------------------------- | ------ | --------------------------------------- |
|
||||||
|
| time | `string ` | | 节点时间 |
|
||||||
|
| title | `string \| [SchemaNode](../../docs/types/schemanode)` | | 节点标题 |
|
||||||
|
| detail | `string` | | 节点详细描述(折叠) |
|
||||||
|
| detailCollapsedText | `string` | `展开` | 详细内容折叠时按钮文案 |
|
||||||
|
| detailExpandedText | `string` | `折叠` | 详细内容展开时按钮文案 |
|
||||||
|
| color | `string \| level样式(info、success、warning、danger)` | `#DADBDD` | 时间轴节点颜色 |
|
||||||
|
| icon | `string` | | icon 名,支持 fontawesome v4 或使用 url(优先级高于color) |
|
@ -23,8 +23,6 @@ order: 13
|
|||||||
## 表达式语法
|
## 表达式语法
|
||||||
|
|
||||||
> 表达式语法实际上是 JavaScript 代码,更多 JavaScript 知识查看 [这里](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript)。
|
> 表达式语法实际上是 JavaScript 代码,更多 JavaScript 知识查看 [这里](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript)。
|
||||||
>
|
|
||||||
> 表达式中不要使用`${xxx}`语法,这个是数据映射的语法规则,不要搞混淆了!
|
|
||||||
|
|
||||||
在 amis 的实现过程中,当正则匹配到某个组件存在`xxxOn`语法的属性名时,会尝试进行下面步骤(以上面配置为例):
|
在 amis 的实现过程中,当正则匹配到某个组件存在`xxxOn`语法的属性名时,会尝试进行下面步骤(以上面配置为例):
|
||||||
|
|
||||||
@ -67,7 +65,7 @@ order: 13
|
|||||||
"obj[key] is ${obj[key]} <br />",
|
"obj[key] is ${obj[key]} <br />",
|
||||||
"arr[0] is ${arr[0]} <br />",
|
"arr[0] is ${arr[0]} <br />",
|
||||||
"arr[a] is ${arr[a]} <br />",
|
"arr[a] is ${arr[a]} <br />",
|
||||||
"arr[a + 1] is ${arr[a + 1]} <br />",
|
"arr[a + 1] is ${arr[a + 1]} <br />"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@ -92,3 +90,54 @@ order: 13
|
|||||||
```
|
```
|
||||||
|
|
||||||
!!!include(amis-formula/dist/doc.md)!!!
|
!!!include(amis-formula/dist/doc.md)!!!
|
||||||
|
|
||||||
|
## 函数调用示例
|
||||||
|
|
||||||
|
```schema
|
||||||
|
{
|
||||||
|
"type": "page",
|
||||||
|
"body": [
|
||||||
|
{
|
||||||
|
"type": "form",
|
||||||
|
"wrapWithPanel": false,
|
||||||
|
"body": [
|
||||||
|
{
|
||||||
|
"type": "static",
|
||||||
|
"label": "IF(true, 2, 3)",
|
||||||
|
"tpl": "${IF(true, 2, 3)}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "static",
|
||||||
|
"label": "MAX(1, -1, 2, 3, 5, -9)",
|
||||||
|
"tpl": "${MAX(1, -1, 2, 3, 5, -9)}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "static",
|
||||||
|
"label": "ROUND(3.5)",
|
||||||
|
"tpl": "${ROUND(3.5)}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "static",
|
||||||
|
"label": "AVG(4, 6, 10, 10, 10)",
|
||||||
|
"tpl": "${AVG(4, 6, 10, 10, 10)}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "static",
|
||||||
|
"label": "UPPERMONEY(7682.01)",
|
||||||
|
"tpl": "${UPPERMONEY(7682.01)}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "static",
|
||||||
|
"label": "TIMESTAMP(DATE(2021, 11, 21, 0, 0, 0), 'x')",
|
||||||
|
"tpl": "${TIMESTAMP(DATE(2021, 11, 21, 0, 0, 0), 'x')}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "static",
|
||||||
|
"label": "DATETOSTR(NOW(), 'YYYY-MM-DD')",
|
||||||
|
"tpl": "${DATETOSTR(NOW(), 'YYYY-MM-DD')}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
@ -1,7 +1,42 @@
|
|||||||
---
|
---
|
||||||
title: 移动端定制
|
title: 移动端展现
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 移动端原生 UI
|
||||||
|
|
||||||
|
从 1.6.0 版本开始,amis 会默认在移动端下使用仿原生 UI 的展现,比如日期选择会从底部弹出。
|
||||||
|
|
||||||
|
由于这个仿原生 UI 是新开发的组件,有些 amis PC 版本的高级配置功能还不支持,比如 select 下的搜索过滤等,如果需要这些功能,可以先通过 props 里的 `useMobileUI` 属性关闭。
|
||||||
|
|
||||||
|
方法 1:全局关闭
|
||||||
|
|
||||||
|
```js
|
||||||
|
amis.embed(
|
||||||
|
'#root',
|
||||||
|
{
|
||||||
|
// amis schema
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// 这里是初始 props
|
||||||
|
},
|
||||||
|
{
|
||||||
|
theme: 'antd',
|
||||||
|
useMobileUI: false
|
||||||
|
}
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
方法 2:针对某个组件进行关闭
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "select",
|
||||||
|
"useMobileUI": false
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 移动端定制配置
|
||||||
|
|
||||||
有时候我们需要在移动端下展示不同效果,可以通过 `mobile` 属性来在移动端下覆盖部分属性。
|
有时候我们需要在移动端下展示不同效果,可以通过 `mobile` 属性来在移动端下覆盖部分属性。
|
||||||
|
|
||||||
```schema: scope="body"
|
```schema: scope="body"
|
||||||
|
@ -160,7 +160,7 @@ let amisScoped = amis.embed(
|
|||||||
// 全局 api 请求适配器
|
// 全局 api 请求适配器
|
||||||
// 另外在 amis 配置项中的 api 也可以配置适配器,针对某个特定接口单独处理。
|
// 另外在 amis 配置项中的 api 也可以配置适配器,针对某个特定接口单独处理。
|
||||||
//
|
//
|
||||||
// responseAdaptor(api) {
|
// requestAdaptor(api) {
|
||||||
// return api;
|
// return api;
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
@ -276,7 +276,7 @@ amis.embed(
|
|||||||
|
|
||||||
默认 JSSDK 不是 hash 路由,如果你想改成 hash 路由模式,请查看此处代码实现。只需要修改 env.isCurrentUrl、env.jumpTo 和 env.updateLocation 这几个方法即可。
|
默认 JSSDK 不是 hash 路由,如果你想改成 hash 路由模式,请查看此处代码实现。只需要修改 env.isCurrentUrl、env.jumpTo 和 env.updateLocation 这几个方法即可。
|
||||||
|
|
||||||
参考:https://github.com/baidu/amis/blob/master/examples/components/Example.tsx#L551-L575
|
参考:https://github.com/baidu/amis/blob/master/examples/components/Example.jsx#L551-L575
|
||||||
|
|
||||||
### 销毁
|
### 销毁
|
||||||
|
|
||||||
@ -436,6 +436,8 @@ class MyComponent extends React.Component<any, any> {
|
|||||||
render() {
|
render() {
|
||||||
let amisScoped;
|
let amisScoped;
|
||||||
let theme = 'cxd';
|
let theme = 'cxd';
|
||||||
|
|
||||||
|
// 请勿使用 React.StrictMode,目前还不支持
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<p>通过 amis 渲染页面</p>
|
<p>通过 amis 渲染页面</p>
|
||||||
|
@ -7,8 +7,7 @@ export default {
|
|||||||
type: 'tpl',
|
type: 'tpl',
|
||||||
inline: false,
|
inline: false,
|
||||||
className: 'w-full',
|
className: 'w-full',
|
||||||
tpl:
|
tpl: '<div class="flex justify-between"><div>顶部区域左侧</div><div>顶部区域右侧</div></div>'
|
||||||
'<div class="flex justify-between"><div>顶部区域左侧</div><div>顶部区域右侧</div></div>'
|
|
||||||
},
|
},
|
||||||
// footer: '<div class="p-2 text-center bg-light">底部区域</div>',
|
// footer: '<div class="p-2 text-center bg-light">底部区域</div>',
|
||||||
// asideBefore: '<div class="p-2 text-center">菜单前面区域</div>',
|
// asideBefore: '<div class="p-2 text-center">菜单前面区域</div>',
|
96
examples/app/index.html
Normal file
96
examples/app/index.html
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<title>amis app 模式</title>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||||
|
<link type="image/x-icon" rel="shortcut icon" href="./static/favicon.png" />
|
||||||
|
<meta
|
||||||
|
name="viewport"
|
||||||
|
content="width=device-width, initial-scale=1, maximum-scale=1"
|
||||||
|
/>
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=Edge" />
|
||||||
|
<link rel="stylesheet" href="../static/iconfont.css" />
|
||||||
|
<link rel="stylesheet" href="@fortawesome/fontawesome-free/css/all.css" />
|
||||||
|
<link
|
||||||
|
rel="stylesheet"
|
||||||
|
href="@fortawesome/fontawesome-free/css/v4-shims.css"
|
||||||
|
/>
|
||||||
|
<!--DEPENDENCIES_INJECT_PLACEHOLDER-->
|
||||||
|
<!--STYLE_PLACEHOLDER-->
|
||||||
|
<style>
|
||||||
|
.app-wrapper,
|
||||||
|
.schema-wrapper {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<script type="text/x-jsx">
|
||||||
|
let theme = localStorage.getItem('amis-theme') || 'cxd';
|
||||||
|
if (theme === 'default') {
|
||||||
|
theme = 'cxd';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 非 IE 模式
|
||||||
|
if (window.navigator.userAgent.indexOf('Trident') === -1) {
|
||||||
|
document.write(
|
||||||
|
`<link rel="stylesheet" title="ang" ${
|
||||||
|
theme !== 'ang' ? 'disabled' : ''
|
||||||
|
} href="${__uri('../../scss/themes/ang.scss')}" />`
|
||||||
|
);
|
||||||
|
document.write(
|
||||||
|
`<link rel="stylesheet" title="cxd" ${
|
||||||
|
theme !== 'cxd' ? 'disabled' : ''
|
||||||
|
} href="${__uri('../../scss/themes/cxd.scss')}" />`
|
||||||
|
);
|
||||||
|
document.write(
|
||||||
|
`<link rel="stylesheet" title="dark" ${
|
||||||
|
theme !== 'dark' ? 'disabled' : ''
|
||||||
|
} href="${__uri('../../scss/themes/dark.scss')}" />`
|
||||||
|
);
|
||||||
|
document.write(
|
||||||
|
`<link rel="stylesheet" title="antd" ${
|
||||||
|
theme !== 'antd' ? 'disabled' : ''
|
||||||
|
} href="${__uri('../../scss/themes/antd.scss')}" />`
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
document.write(
|
||||||
|
`<link rel="stylesheet" title="ang" ${
|
||||||
|
theme !== 'ang' ? 'disabled' : ''
|
||||||
|
} href="${__uri('../../scss/themes/ang-ie11.scss')}" />`
|
||||||
|
);
|
||||||
|
document.write(
|
||||||
|
`<link rel="stylesheet" title="cxd" ${
|
||||||
|
theme !== 'cxd' ? 'disabled' : ''
|
||||||
|
} href="${__uri('../../scss/themes/cxd-ie11.scss')}" />`
|
||||||
|
);
|
||||||
|
document.write(
|
||||||
|
`<link rel="stylesheet" title="dark" ${
|
||||||
|
theme !== 'dark' ? 'disabled' : ''
|
||||||
|
} href="${__uri('../../scss/themes/dark-ie11.scss')}" />`
|
||||||
|
);
|
||||||
|
document.write(
|
||||||
|
`<link rel="stylesheet" title="antd" ${
|
||||||
|
theme !== 'antd' ? 'disabled' : ''
|
||||||
|
} href="${__uri('../../scss/themes/antd-ie11.scss')}" />`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<!--ignore-->
|
||||||
|
<link rel="stylesheet" href="../../scss/helper.scss" />
|
||||||
|
<!--ignore-->
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="root" class="app-wrapper"></div>
|
||||||
|
<script src="../mod.js"></script>
|
||||||
|
<script type="text/javascript">
|
||||||
|
/* @require ./index.jsx 标记为同步依赖,提前加载 */
|
||||||
|
amis.require(['./index.jsx'], function (app) {
|
||||||
|
var initialState = {};
|
||||||
|
app.bootstrap(document.getElementById('root'), initialState);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
140
examples/app/index.jsx
Normal file
140
examples/app/index.jsx
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
/**
|
||||||
|
* @file app 模式示例
|
||||||
|
*/
|
||||||
|
|
||||||
|
import APPSchema from './app';
|
||||||
|
import {createHashHistory} from 'history';
|
||||||
|
import {embed} from '../embed';
|
||||||
|
|
||||||
|
const history = createHashHistory({});
|
||||||
|
|
||||||
|
function normalizeLink(to, location = history.location) {
|
||||||
|
to = to || '';
|
||||||
|
|
||||||
|
if (to && to[0] === '#') {
|
||||||
|
to = location.pathname + location.search + to;
|
||||||
|
} else if (to && to[0] === '?') {
|
||||||
|
to = location.pathname + to;
|
||||||
|
}
|
||||||
|
|
||||||
|
const idx = to.indexOf('?');
|
||||||
|
const idx2 = to.indexOf('#');
|
||||||
|
let pathname = ~idx
|
||||||
|
? to.substring(0, idx)
|
||||||
|
: ~idx2
|
||||||
|
? to.substring(0, idx2)
|
||||||
|
: to;
|
||||||
|
let search = ~idx ? to.substring(idx, ~idx2 ? idx2 : undefined) : '';
|
||||||
|
let hash = ~idx2 ? to.substring(idx2) : location.hash;
|
||||||
|
|
||||||
|
if (!pathname) {
|
||||||
|
pathname = location.pathname;
|
||||||
|
} else if (pathname[0] != '/' && !/^https?\:\/\//.test(pathname)) {
|
||||||
|
let relativeBase = location.pathname;
|
||||||
|
const paths = relativeBase.split('/');
|
||||||
|
paths.pop();
|
||||||
|
let m;
|
||||||
|
while ((m = /^\.\.?\//.exec(pathname))) {
|
||||||
|
if (m[0] === '../') {
|
||||||
|
paths.pop();
|
||||||
|
}
|
||||||
|
pathname = pathname.substring(m[0].length);
|
||||||
|
}
|
||||||
|
pathname = paths.concat(pathname).join('/');
|
||||||
|
}
|
||||||
|
|
||||||
|
return pathname + search + hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isCurrentUrl(to, ctx) {
|
||||||
|
if (!to) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const pathname = history.location.pathname;
|
||||||
|
const link = normalizeLink(to, {
|
||||||
|
...location,
|
||||||
|
pathname,
|
||||||
|
hash: ''
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!~link.indexOf('http') && ~link.indexOf(':')) {
|
||||||
|
let strict = ctx && ctx.strict;
|
||||||
|
return match(link, {
|
||||||
|
decode: decodeURIComponent,
|
||||||
|
strict: typeof strict !== 'undefined' ? strict : true
|
||||||
|
})(pathname);
|
||||||
|
}
|
||||||
|
|
||||||
|
return decodeURI(pathname) === link;
|
||||||
|
}
|
||||||
|
|
||||||
|
const APPENV = {
|
||||||
|
updateLocation: (location, replace) => {
|
||||||
|
location = normalizeLink(location);
|
||||||
|
if (location === 'goBack') {
|
||||||
|
return history.goBack();
|
||||||
|
} else if (
|
||||||
|
(!/^https?\:\/\//.test(location) &&
|
||||||
|
location === history.location.pathname + history.location.search) ||
|
||||||
|
location === history.location.href
|
||||||
|
) {
|
||||||
|
// 目标地址和当前地址一样,不处理,免得重复刷新
|
||||||
|
return;
|
||||||
|
} else if (/^https?\:\/\//.test(location) || !history) {
|
||||||
|
return (window.location.href = location);
|
||||||
|
}
|
||||||
|
|
||||||
|
history[replace ? 'replace' : 'push'](location);
|
||||||
|
},
|
||||||
|
jumpTo: (to, action) => {
|
||||||
|
if (to === 'goBack') {
|
||||||
|
return history.goBack();
|
||||||
|
}
|
||||||
|
|
||||||
|
to = normalizeLink(to);
|
||||||
|
|
||||||
|
if (isCurrentUrl(to)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action && action.actionType === 'url') {
|
||||||
|
action.blank === false
|
||||||
|
? (window.location.href = to)
|
||||||
|
: window.open(to, '_blank');
|
||||||
|
return;
|
||||||
|
} else if (action && action.blank) {
|
||||||
|
window.open(to, '_blank');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (/^https?:\/\//.test(to)) {
|
||||||
|
window.location.href = to;
|
||||||
|
} else if (
|
||||||
|
(!/^https?\:\/\//.test(to) &&
|
||||||
|
to === history.pathname + history.location.search) ||
|
||||||
|
to === history.location.href
|
||||||
|
) {
|
||||||
|
// do nothing
|
||||||
|
} else {
|
||||||
|
history.push(to);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
isCurrentUrl: isCurrentUrl
|
||||||
|
};
|
||||||
|
|
||||||
|
export function bootstrap(mountTo) {
|
||||||
|
const amisInstance = embed(
|
||||||
|
mountTo,
|
||||||
|
APPSchema,
|
||||||
|
{
|
||||||
|
location: history.location
|
||||||
|
},
|
||||||
|
APPENV
|
||||||
|
);
|
||||||
|
|
||||||
|
history.listen(state => {
|
||||||
|
amisInstance.updateProps({
|
||||||
|
location: state.location || state
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
60
examples/components/CRUD/ItemAction.jsx
Normal file
60
examples/components/CRUD/ItemAction.jsx
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
export default {
|
||||||
|
title: '点击左侧 crud 会触发右侧 crud 刷新',
|
||||||
|
subTitle: '需要配置 syncLocation: false 避免左侧也刷新',
|
||||||
|
body: {
|
||||||
|
type: 'grid',
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
body: [
|
||||||
|
{
|
||||||
|
type: 'crud',
|
||||||
|
api: '/api/sample',
|
||||||
|
headerToolbar: [],
|
||||||
|
perPage: 10,
|
||||||
|
syncLocation: false,
|
||||||
|
itemAction: {
|
||||||
|
actionType: 'reload',
|
||||||
|
target: 'detailCRUD?id=${id}'
|
||||||
|
},
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
name: 'id',
|
||||||
|
label: 'ID',
|
||||||
|
width: 20,
|
||||||
|
type: 'text'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'platform',
|
||||||
|
label: 'Platform(s)',
|
||||||
|
type: 'text'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
body: [
|
||||||
|
{
|
||||||
|
type: 'crud',
|
||||||
|
name: 'detailCRUD',
|
||||||
|
headerToolbar: [],
|
||||||
|
syncLocation: false,
|
||||||
|
api: '/api/sample?perPage=10&id=${id}&waitSeconds=1',
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
name: 'engine',
|
||||||
|
label: 'Rendering engine',
|
||||||
|
type: 'text'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'version',
|
||||||
|
label: 'Engine version',
|
||||||
|
type: 'text'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
};
|
@ -54,7 +54,7 @@ export const components = [
|
|||||||
)
|
)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Grid 水平布局',
|
label: 'Grid 水平分栏',
|
||||||
path: '/zh-CN/components/grid',
|
path: '/zh-CN/components/grid',
|
||||||
getComponent: () =>
|
getComponent: () =>
|
||||||
import('../../docs/zh-CN/components/grid.md').then(
|
import('../../docs/zh-CN/components/grid.md').then(
|
||||||
@ -370,6 +370,14 @@ export const components = [
|
|||||||
makeMarkdownRenderer
|
makeMarkdownRenderer
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: 'InputFormula 公式编辑器',
|
||||||
|
path: '/zh-CN/components/form/input-formula',
|
||||||
|
getComponent: () =>
|
||||||
|
import('../../docs/zh-CN/components/form/input-formula.md').then(
|
||||||
|
makeMarkdownRenderer
|
||||||
|
)
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: 'DiffEditor 对比编辑器',
|
label: 'DiffEditor 对比编辑器',
|
||||||
path: '/zh-CN/components/form/diff-editor',
|
path: '/zh-CN/components/form/diff-editor',
|
||||||
@ -991,7 +999,15 @@ export const components = [
|
|||||||
import('../../docs/zh-CN/components/video.md').then(
|
import('../../docs/zh-CN/components/video.md').then(
|
||||||
makeMarkdownRenderer
|
makeMarkdownRenderer
|
||||||
)
|
)
|
||||||
}
|
},
|
||||||
|
{
|
||||||
|
label: 'Timeline 时间轴',
|
||||||
|
path: '/zh-CN/components/timeline',
|
||||||
|
getComponent: () =>
|
||||||
|
import('../../docs/zh-CN/components/timeline.md').then(
|
||||||
|
makeMarkdownRenderer
|
||||||
|
)
|
||||||
|
},
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -90,6 +90,7 @@ export default {
|
|||||||
label: '弹个表单',
|
label: '弹个表单',
|
||||||
actionType: 'dialog',
|
actionType: 'dialog',
|
||||||
dialog: {
|
dialog: {
|
||||||
|
size: 'lg',
|
||||||
title: '在弹框中的表单',
|
title: '在弹框中的表单',
|
||||||
closeOnEsc: true,
|
closeOnEsc: true,
|
||||||
actions: [
|
actions: [
|
||||||
|
@ -53,6 +53,7 @@ import HeaderHideSchema from './CRUD/HeaderHide';
|
|||||||
import LoadOnceTableCrudSchema from './CRUD/LoadOnce';
|
import LoadOnceTableCrudSchema from './CRUD/LoadOnce';
|
||||||
import ExportCSVExcelSchema from './CRUD/ExportCSVExcel';
|
import ExportCSVExcelSchema from './CRUD/ExportCSVExcel';
|
||||||
import CRUDDynamicSchema from './CRUD/Dynamic';
|
import CRUDDynamicSchema from './CRUD/Dynamic';
|
||||||
|
import ItemActionchema from './CRUD/ItemAction';
|
||||||
import SdkTest from './Sdk/Test';
|
import SdkTest from './Sdk/Test';
|
||||||
import JSONSchemaForm from './Form/Schem';
|
import JSONSchemaForm from './Form/Schem';
|
||||||
import SimpleDialogSchema from './Dialog/Simple';
|
import SimpleDialogSchema from './Dialog/Simple';
|
||||||
@ -84,7 +85,7 @@ import Tab1Schema from './Tabs/Tab1';
|
|||||||
import Tab2Schema from './Tabs/Tab2';
|
import Tab2Schema from './Tabs/Tab2';
|
||||||
import Tab3Schema from './Tabs/Tab3';
|
import Tab3Schema from './Tabs/Tab3';
|
||||||
import TestComponent from './Test';
|
import TestComponent from './Test';
|
||||||
import APP from './APP/index';
|
|
||||||
import {normalizeLink} from '../../src/utils/normalizeLink';
|
import {normalizeLink} from '../../src/utils/normalizeLink';
|
||||||
|
|
||||||
export const examples = [
|
export const examples = [
|
||||||
@ -376,6 +377,11 @@ export const examples = [
|
|||||||
path: '/examples/crud/load-once',
|
path: '/examples/crud/load-once',
|
||||||
component: makeSchemaRenderer(LoadOnceTableCrudSchema)
|
component: makeSchemaRenderer(LoadOnceTableCrudSchema)
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: '点击联动',
|
||||||
|
path: '/examples/crud/item-action',
|
||||||
|
component: makeSchemaRenderer(ItemActionchema)
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: '导出 Excel/CSV',
|
label: '导出 Excel/CSV',
|
||||||
path: '/examples/crud/export-excel-csv',
|
path: '/examples/crud/export-excel-csv',
|
||||||
@ -577,49 +583,17 @@ export const examples = [
|
|||||||
{
|
{
|
||||||
label: 'APP 多页应用',
|
label: 'APP 多页应用',
|
||||||
icon: 'fa fa-cubes',
|
icon: 'fa fa-cubes',
|
||||||
path: '/examples/app',
|
path: '/app/',
|
||||||
component: makeSchemaRenderer(APP, false, {
|
component: () => {
|
||||||
session: 'app',
|
// 如果在 gh-pages 里面
|
||||||
jumpTo: (to: string) => {
|
if (/^\/amis/.test(window.location.pathname)) {
|
||||||
location.hash = to;
|
window.open(`/amis/app/`, '_blank');
|
||||||
},
|
} else {
|
||||||
updateLocation: (to, replace) => {
|
window.open(`/examples/app/`, '_blank');
|
||||||
if (to === 'goBack') {
|
|
||||||
return window.history.back();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (replace && window.history.replaceState) {
|
|
||||||
window.history.replaceState(
|
|
||||||
'',
|
|
||||||
document.title,
|
|
||||||
normalizeLink(to)
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
window.history.pushState('', document.title, normalizeLink(to));
|
|
||||||
},
|
|
||||||
isCurrentUrl: (to: string, ctx: any) => {
|
|
||||||
if (!to) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const pathname = location.hash ? location.hash.substring(1) : '/';
|
|
||||||
const link = normalizeLink(to, {
|
|
||||||
...location,
|
|
||||||
pathname,
|
|
||||||
hash: ''
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!~link.indexOf('http') && ~link.indexOf(':')) {
|
|
||||||
return match(link, {
|
|
||||||
decode: decodeURIComponent,
|
|
||||||
strict: ctx?.strict ?? true
|
|
||||||
})(pathname);
|
|
||||||
}
|
|
||||||
|
|
||||||
return pathname === encodeURI(link);
|
|
||||||
}
|
}
|
||||||
})
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// {
|
// {
|
@ -2,6 +2,7 @@ import React from 'react';
|
|||||||
import {toast} from '../../src/components/Toast';
|
import {toast} from '../../src/components/Toast';
|
||||||
import {render, makeTranslator} from '../../src/index';
|
import {render, makeTranslator} from '../../src/index';
|
||||||
import {normalizeLink} from '../../src/utils/normalizeLink';
|
import {normalizeLink} from '../../src/utils/normalizeLink';
|
||||||
|
import {isMobile} from '../../src/utils/helper';
|
||||||
import attachmentAdpator from '../../src/utils/attachmentAdpator';
|
import attachmentAdpator from '../../src/utils/attachmentAdpator';
|
||||||
import {alert, confirm} from '../../src/components/Alert';
|
import {alert, confirm} from '../../src/components/Alert';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
@ -324,11 +325,10 @@ export default class PlayGround extends React.Component {
|
|||||||
theme: this.props.theme,
|
theme: this.props.theme,
|
||||||
locale: this.props.locale,
|
locale: this.props.locale,
|
||||||
affixHeader: false,
|
affixHeader: false,
|
||||||
affixFooter: false,
|
affixFooter: false
|
||||||
useMobileUI: true
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (this.props.viewMode === 'mobile') {
|
if (this.props.viewMode === 'mobile' && !isMobile()) {
|
||||||
return (
|
return (
|
||||||
<iframe
|
<iframe
|
||||||
width="375"
|
width="375"
|
||||||
|
@ -7,12 +7,15 @@ import './polyfills/index';
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {render} from 'react-dom';
|
import {render} from 'react-dom';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
|
import TouchEmulator from 'hammer-touchemulator';
|
||||||
import copy from 'copy-to-clipboard';
|
import copy from 'copy-to-clipboard';
|
||||||
import {toast} from '../src/components/Toast';
|
import {toast} from '../src/components/Toast';
|
||||||
import '../src/locale/en-US';
|
import '../src/locale/en-US';
|
||||||
|
|
||||||
import {render as renderAmis} from '../src/index';
|
import {render as renderAmis} from '../src/index';
|
||||||
|
|
||||||
|
TouchEmulator();
|
||||||
|
|
||||||
class AMISComponent extends React.Component {
|
class AMISComponent extends React.Component {
|
||||||
state = {
|
state = {
|
||||||
schema: null,
|
schema: null,
|
||||||
@ -23,7 +26,10 @@ class AMISComponent extends React.Component {
|
|||||||
window.addEventListener('message', event => {
|
window.addEventListener('message', event => {
|
||||||
const data = event.data;
|
const data = event.data;
|
||||||
if (data && data.schema) {
|
if (data && data.schema) {
|
||||||
this.setState({schema: data.schema, props: data.props});
|
this.setState({
|
||||||
|
schema: data.schema,
|
||||||
|
props: data.props
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
window.parent.postMessage('amisReady', '*');
|
window.parent.postMessage('amisReady', '*');
|
||||||
@ -41,7 +47,12 @@ class AMISComponent extends React.Component {
|
|||||||
headers // 请求头
|
headers // 请求头
|
||||||
}) => {
|
}) => {
|
||||||
config = {
|
config = {
|
||||||
|
url,
|
||||||
dataType: 'json',
|
dataType: 'json',
|
||||||
|
method,
|
||||||
|
data,
|
||||||
|
headers,
|
||||||
|
responseType,
|
||||||
...config
|
...config
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -55,12 +66,7 @@ class AMISComponent extends React.Component {
|
|||||||
config.validateStatus = function () {
|
config.validateStatus = function () {
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
const response = await axios(config);
|
||||||
const response = await axios[config.method](
|
|
||||||
config.url,
|
|
||||||
config.data,
|
|
||||||
config
|
|
||||||
);
|
|
||||||
|
|
||||||
if (response.status >= 400) {
|
if (response.status >= 400) {
|
||||||
if (response.data) {
|
if (response.data) {
|
||||||
|
@ -54,6 +54,7 @@ fis.set('project.files', [
|
|||||||
'/scss/helper.scss',
|
'/scss/helper.scss',
|
||||||
'/scss/themes/*.scss',
|
'/scss/themes/*.scss',
|
||||||
'/examples/*.html',
|
'/examples/*.html',
|
||||||
|
'/examples/app/*.html',
|
||||||
'/examples/*.tpl',
|
'/examples/*.tpl',
|
||||||
'/examples/static/*.png',
|
'/examples/static/*.png',
|
||||||
'/examples/static/*.svg',
|
'/examples/static/*.svg',
|
||||||
@ -495,6 +496,7 @@ if (fis.project.currentMedia() === 'publish') {
|
|||||||
'!mpegts.js/**',
|
'!mpegts.js/**',
|
||||||
'!hls.js/**',
|
'!hls.js/**',
|
||||||
'!froala-editor/**',
|
'!froala-editor/**',
|
||||||
|
'!codemirror/**',
|
||||||
|
|
||||||
'!tinymce/**',
|
'!tinymce/**',
|
||||||
'!zrender/**',
|
'!zrender/**',
|
||||||
@ -530,6 +532,7 @@ if (fis.project.currentMedia() === 'publish') {
|
|||||||
|
|
||||||
'tinymce.js': ['src/components/Tinymce.tsx', 'tinymce/**'],
|
'tinymce.js': ['src/components/Tinymce.tsx', 'tinymce/**'],
|
||||||
|
|
||||||
|
'codemirror.js': ['codemirror/**'],
|
||||||
'papaparse.js': ['papaparse/**'],
|
'papaparse.js': ['papaparse/**'],
|
||||||
|
|
||||||
'exceljs.js': ['exceljs/**'],
|
'exceljs.js': ['exceljs/**'],
|
||||||
@ -562,6 +565,7 @@ if (fis.project.currentMedia() === 'publish') {
|
|||||||
'rest.js': [
|
'rest.js': [
|
||||||
'*.js',
|
'*.js',
|
||||||
'!monaco-editor/**',
|
'!monaco-editor/**',
|
||||||
|
'!codemirror/**',
|
||||||
'!mpegts.js/**',
|
'!mpegts.js/**',
|
||||||
'!hls.js/**',
|
'!hls.js/**',
|
||||||
'!froala-editor/**',
|
'!froala-editor/**',
|
||||||
@ -770,6 +774,7 @@ if (fis.project.currentMedia() === 'publish') {
|
|||||||
'/examples/mod.js',
|
'/examples/mod.js',
|
||||||
'node_modules/**.js',
|
'node_modules/**.js',
|
||||||
'!monaco-editor/**',
|
'!monaco-editor/**',
|
||||||
|
'!codemirror/**',
|
||||||
'!mpegts.js/**',
|
'!mpegts.js/**',
|
||||||
'!hls.js/**',
|
'!hls.js/**',
|
||||||
'!froala-editor/**',
|
'!froala-editor/**',
|
||||||
@ -808,6 +813,8 @@ if (fis.project.currentMedia() === 'publish') {
|
|||||||
|
|
||||||
'pkg/tinymce.js': ['src/components/Tinymce.tsx', 'tinymce/**'],
|
'pkg/tinymce.js': ['src/components/Tinymce.tsx', 'tinymce/**'],
|
||||||
|
|
||||||
|
'pkg/codemirror.js': ['codemirror/**'],
|
||||||
|
|
||||||
'pkg/papaparse.js': ['papaparse/**'],
|
'pkg/papaparse.js': ['papaparse/**'],
|
||||||
|
|
||||||
'pkg/exceljs.js': ['exceljs/**'],
|
'pkg/exceljs.js': ['exceljs/**'],
|
||||||
@ -900,7 +907,7 @@ if (fis.project.currentMedia() === 'publish') {
|
|||||||
const DocNavCN = ret.src['/examples/components/DocNavCN.ts'];
|
const DocNavCN = ret.src['/examples/components/DocNavCN.ts'];
|
||||||
const Components = ret.src['/examples/components/Components.tsx'];
|
const Components = ret.src['/examples/components/Components.tsx'];
|
||||||
const DocCSS = ret.src['/examples/components/CssDocs.tsx'];
|
const DocCSS = ret.src['/examples/components/CssDocs.tsx'];
|
||||||
const ExampleJs = ret.src['/examples/components/Example.tsx'];
|
const ExampleJs = ret.src['/examples/components/Example.jsx'];
|
||||||
|
|
||||||
const pages = [];
|
const pages = [];
|
||||||
const source = [
|
const source = [
|
||||||
|
12
mock/cfc/mock/crud/empty.js
Normal file
12
mock/cfc/mock/crud/empty.js
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
module.exports = function (req, res) {
|
||||||
|
const ret = {
|
||||||
|
status: 0,
|
||||||
|
msg: '',
|
||||||
|
data: {
|
||||||
|
count: 0,
|
||||||
|
rows: []
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
res.json(ret);
|
||||||
|
};
|
6
mock/cfc/mock/form/formitemFailed.json
Normal file
6
mock/cfc/mock/form/formitemFailed.json
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"status": 422,
|
||||||
|
"msg": "",
|
||||||
|
"errors": "当前用户已存在",
|
||||||
|
"data": null
|
||||||
|
}
|
3
mock/cfc/mock/form/formitemSuccess.json
Normal file
3
mock/cfc/mock/form/formitemSuccess.json
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"status": 0
|
||||||
|
}
|
15
mock/cfc/mock/timeline/timelineItems.json
Normal file
15
mock/cfc/mock/timeline/timelineItems.json
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"status": 0,
|
||||||
|
"msg": "",
|
||||||
|
"data": {
|
||||||
|
"items": [
|
||||||
|
{"time": "2019-02-07", "title": "数据开发", "detail": "2019-02-07detail", "color":"#ffb200", "icon": "close"},
|
||||||
|
{"time": "2019-02-08", "title": "管理中心", "detail": "2019-02-08detail" },
|
||||||
|
{"time": "2019-02-09", "title": "SQL语句", "detail": "2019-02-09detail", "color":"warning"},
|
||||||
|
{"time": "2019-02-10", "title": "一键部署", "detail": "2019-02-10detail", "icon": "compress-alt"},
|
||||||
|
{"time": "2019-02-10", "title": "一键部署", "detail": "2019-02-11detail"},
|
||||||
|
{"time": "2019-02-10", "title": "一键部署", "detail": "2019-02-12detail", "icon": "close"},
|
||||||
|
{"time": "2019-02-10", "title": "一键部署", "detail": "2019-02-13detail"}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
@ -1,3 +1,5 @@
|
|||||||
|
rewrite ^\/examples\/app\/$ /examples/app/index.html
|
||||||
|
|
||||||
rewrite ^\/(?:zh-CN|en-US)?\/?(?:examples|docs|components|style)(?:\/[a-z0-9\-_\/]+)?$ /examples/index.html
|
rewrite ^\/(?:zh-CN|en-US)?\/?(?:examples|docs|components|style)(?:\/[a-z0-9\-_\/]+)?$ /examples/index.html
|
||||||
|
|
||||||
rewrite ^\/play$ /examples/index.html
|
rewrite ^\/play$ /examples/index.html
|
||||||
|
15
package.json
15
package.json
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "amis",
|
"name": "amis",
|
||||||
"version": "1.5.3",
|
"version": "1.5.7",
|
||||||
"description": "一种MIS页面生成工具",
|
"description": "一种MIS页面生成工具",
|
||||||
"main": "lib/index.js",
|
"main": "lib/index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@ -13,7 +13,7 @@
|
|||||||
"dev": "fis3 release -cwd ./public",
|
"dev": "fis3 release -cwd ./public",
|
||||||
"publish-to-internal": "sh build.sh && sh publish.sh",
|
"publish-to-internal": "sh build.sh && sh publish.sh",
|
||||||
"build": "sh build.sh",
|
"build": "sh build.sh",
|
||||||
"prettier": "prettier --write '{src,examples,scss}/**/*.{tsx,ts,jsx,scss}'",
|
"prettier": "prettier --write '{src,scss,examples}/**/**/*.{js,jsx,ts,tsx,scss,json}'",
|
||||||
"deploy-gh-page": "sh ./deploy-gh-pages.sh",
|
"deploy-gh-page": "sh ./deploy-gh-pages.sh",
|
||||||
"build-schemas": "ts-node -O '{\"target\":\"es6\"}' scripts/build-schemas.ts"
|
"build-schemas": "ts-node -O '{\"target\":\"es6\"}' scripts/build-schemas.ts"
|
||||||
},
|
},
|
||||||
@ -37,13 +37,19 @@
|
|||||||
"url": "http://www.apache.org/licenses/LICENSE-2.0"
|
"url": "http://www.apache.org/licenses/LICENSE-2.0"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"lint-staged": {
|
||||||
|
"{src,examples}/**/**/*.{tsx,jsx,ts}": [
|
||||||
|
"prettier --write"
|
||||||
|
]
|
||||||
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"amis-formula": "^1.2.7",
|
"amis-formula": "^1.3.5",
|
||||||
"ansi-to-react": "^6.1.6",
|
"ansi-to-react": "^6.1.6",
|
||||||
"async": "2.6.0",
|
"async": "2.6.0",
|
||||||
"attr-accept": "2.2.2",
|
"attr-accept": "2.2.2",
|
||||||
"blueimp-canvastoblob": "2.1.0",
|
"blueimp-canvastoblob": "2.1.0",
|
||||||
"classnames": "2.3.1",
|
"classnames": "2.3.1",
|
||||||
|
"codemirror": "^5.63.0",
|
||||||
"downshift": "6.1.7",
|
"downshift": "6.1.7",
|
||||||
"echarts": "5.2.2",
|
"echarts": "5.2.2",
|
||||||
"echarts-stat": "^1.2.0",
|
"echarts-stat": "^1.2.0",
|
||||||
@ -94,6 +100,7 @@
|
|||||||
"@fortawesome/fontawesome-free": "^5.15.4",
|
"@fortawesome/fontawesome-free": "^5.15.4",
|
||||||
"@testing-library/react": "^12.0.0",
|
"@testing-library/react": "^12.0.0",
|
||||||
"@types/async": "^2.0.45",
|
"@types/async": "^2.0.45",
|
||||||
|
"@types/codemirror": "^5.60.3",
|
||||||
"@types/echarts": "^4.9.2",
|
"@types/echarts": "^4.9.2",
|
||||||
"@types/file-saver": "^2.0.1",
|
"@types/file-saver": "^2.0.1",
|
||||||
"@types/history": "^4.6.0",
|
"@types/history": "^4.6.0",
|
||||||
@ -140,12 +147,14 @@
|
|||||||
"fis3-preprocessor-js-require-file": "^0.1.3",
|
"fis3-preprocessor-js-require-file": "^0.1.3",
|
||||||
"fs-walk": "0.0.2",
|
"fs-walk": "0.0.2",
|
||||||
"glob": "^7.2.0",
|
"glob": "^7.2.0",
|
||||||
|
"hammer-touchemulator": "^0.0.2",
|
||||||
"history": "^4.7.2",
|
"history": "^4.7.2",
|
||||||
"husky": "^7.0.4",
|
"husky": "^7.0.4",
|
||||||
"jest": "^27.4.2",
|
"jest": "^27.4.2",
|
||||||
"jest-canvas-mock": "^2.3.0",
|
"jest-canvas-mock": "^2.3.0",
|
||||||
"js-yaml": "^4.1.0",
|
"js-yaml": "^4.1.0",
|
||||||
"json5": "^2.2.0",
|
"json5": "^2.2.0",
|
||||||
|
"lint-staged": "^12.1.4",
|
||||||
"marked": "^3.0.4",
|
"marked": "^3.0.4",
|
||||||
"mkdirp": "^1.0.4",
|
"mkdirp": "^1.0.4",
|
||||||
"moment-timezone": "^0.5.33",
|
"moment-timezone": "^0.5.33",
|
||||||
|
@ -164,6 +164,14 @@
|
|||||||
--Audio-volumeControl-width: #{px2rem(110px)};
|
--Audio-volumeControl-width: #{px2rem(110px)};
|
||||||
--Avatar-bg: #{$gray300};
|
--Avatar-bg: #{$gray300};
|
||||||
--Avatar-width: #{px2rem(40px)};
|
--Avatar-width: #{px2rem(40px)};
|
||||||
|
--Avatar-size-large: #{px2rem(48px)};
|
||||||
|
// 兼容旧的size大小写法
|
||||||
|
--Avatar-size-default: var(--Avatar-width);
|
||||||
|
--Avatar-size-small: #{px2rem(32px)};
|
||||||
|
--Avatar-icon-size-large: #{px2rem(20px)};
|
||||||
|
// 兼容旧的icon大小写法
|
||||||
|
--Avatar-icon-size-default: var(--fontSizeLg);
|
||||||
|
--Avatar-icon-size-small: #{px2rem(12px)};
|
||||||
|
|
||||||
--Badge-size: var(--gap-md);
|
--Badge-size: var(--gap-md);
|
||||||
--Badge-color: var(--white);
|
--Badge-color: var(--white);
|
||||||
@ -1091,8 +1099,8 @@
|
|||||||
--PickerColumns-title-lineHeight: 1.5;
|
--PickerColumns-title-lineHeight: 1.5;
|
||||||
--PickerColumns-action-padding: 0 var(--gap-sm);
|
--PickerColumns-action-padding: 0 var(--gap-sm);
|
||||||
--PickerColumns-action-fontSize: var(--fontSizeMd);
|
--PickerColumns-action-fontSize: var(--fontSizeMd);
|
||||||
--PickerColumns-confirmAction-color: var(--Button--info-bg);
|
--PickerColumns-confirmAction-color: #{lighten($text-color, 25%)};
|
||||||
--PickerColumns-cancelAction-color: var(--Button--light-bg);
|
--PickerColumns-cancelAction-color: #{lighten($text-color, 50%)};
|
||||||
--PickerColumns-option-fontSize: var(--fontSizeLg);
|
--PickerColumns-option-fontSize: var(--fontSizeLg);
|
||||||
--PickerColumns-optionText-color: var(--text-color);
|
--PickerColumns-optionText-color: var(--text-color);
|
||||||
--PickerColumns-optionDisabled-opacity: 0.3;
|
--PickerColumns-optionDisabled-opacity: 0.3;
|
||||||
@ -1144,7 +1152,9 @@
|
|||||||
--ResultBox-value-bg: #f5f5f5;
|
--ResultBox-value-bg: #f5f5f5;
|
||||||
--ResultBox-value-color: #000;
|
--ResultBox-value-color: #000;
|
||||||
|
|
||||||
--Rating-onActive-color: var(--info);
|
--Rating-inactive-color: #e6e6e8;
|
||||||
|
--Rating-star-margin: #{px2rem(8px)};
|
||||||
|
--Rating-star-size: #{px2rem(24px)};
|
||||||
|
|
||||||
--Satus-icon-width: var(--gap-lg);
|
--Satus-icon-width: var(--gap-lg);
|
||||||
--Satus-icon-height: var(--Satus-icon-width);
|
--Satus-icon-height: var(--Satus-icon-width);
|
||||||
|
@ -32,7 +32,7 @@ $colors: (
|
|||||||
primary: #007bff,
|
primary: #007bff,
|
||||||
secondary: #6c757d,
|
secondary: #6c757d,
|
||||||
success: #28a745,
|
success: #28a745,
|
||||||
info: #17a2b8,
|
info: #007bff,
|
||||||
warning: #fad733,
|
warning: #fad733,
|
||||||
danger: #dc3545,
|
danger: #dc3545,
|
||||||
light: #f8f9fa,
|
light: #f8f9fa,
|
||||||
|
@ -1,14 +1,37 @@
|
|||||||
|
@mixin avatar-size($size, $fontSize) {
|
||||||
|
width: $size;
|
||||||
|
height: $size;
|
||||||
|
line-height: $size;
|
||||||
|
|
||||||
|
i {
|
||||||
|
font-size: $fontSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.#{$ns}Avatar {
|
.#{$ns}Avatar {
|
||||||
background: var(--Avatar-bg);
|
background: var(--Avatar-bg);
|
||||||
width: var(--Avatar-width);
|
@include avatar-size(var(--Avatar-size-default), var(--Avatar-icon-size-default));
|
||||||
height: var(--Avatar-width);
|
position: relative;
|
||||||
line-height: var(--Avatar-width);
|
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|
||||||
|
&--lg {
|
||||||
|
@include avatar-size(var(--Avatar-size-large), var(--Avatar-icon-size-large));
|
||||||
|
}
|
||||||
|
|
||||||
|
&--sm {
|
||||||
|
@include avatar-size(var(--Avatar-size-small), var(--Avatar-icon-size-small));
|
||||||
|
}
|
||||||
|
|
||||||
|
&--text {
|
||||||
|
position: absolute;
|
||||||
|
left: 50%;
|
||||||
|
transform-origin: 0 center;
|
||||||
|
}
|
||||||
|
|
||||||
&--square {
|
&--square {
|
||||||
border-radius: 0%;
|
border-radius: 0%;
|
||||||
}
|
}
|
||||||
@ -17,18 +40,13 @@
|
|||||||
border-radius: 10%;
|
border-radius: 10%;
|
||||||
}
|
}
|
||||||
|
|
||||||
i {
|
|
||||||
font-size: var(--fontSizeLg);
|
|
||||||
}
|
|
||||||
|
|
||||||
img {
|
img {
|
||||||
color: transparent;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
object-fit: cover;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
|
|
||||||
img,
|
img,
|
||||||
i {
|
i {
|
||||||
transform: scale(1.1);
|
transform: scale(1.1);
|
||||||
|
@ -79,3 +79,283 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.#{$ns}CalendarMobile {
|
||||||
|
height: 100%;
|
||||||
|
overflow: scroll;
|
||||||
|
|
||||||
|
&-pop {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100vw;
|
||||||
|
height: 90vh;
|
||||||
|
background: #fff;
|
||||||
|
border-radius: px2rem(16px) px2rem(16px) 0 0;
|
||||||
|
overflow: hidden;
|
||||||
|
border-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-wrap {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-header {
|
||||||
|
flex-shrink: 0;
|
||||||
|
box-shadow: 0 2px 10px rgb(125 126 128 / 16%);
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.subtitle-text {
|
||||||
|
display: inline-block;
|
||||||
|
width: px2rem(110px);
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rdtPrev {
|
||||||
|
width: px2rem(20px);
|
||||||
|
height: px2rem(44px);
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: px2rem(-20px);
|
||||||
|
}
|
||||||
|
.rdtNext {
|
||||||
|
width: px2rem(20px);
|
||||||
|
height: px2rem(44px);
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: px2rem(-20px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-title,
|
||||||
|
&-subtitle {
|
||||||
|
height: px2rem(44px);
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: px2rem(44px);
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-title {
|
||||||
|
font-size: var(--fontSizeLg);
|
||||||
|
}
|
||||||
|
|
||||||
|
&-weekdays {
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
.weekday {
|
||||||
|
flex: 1;
|
||||||
|
line-height: px2rem(30px);
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-close {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 1;
|
||||||
|
color: #c8c9cc;
|
||||||
|
font-size: px2rem(16px);
|
||||||
|
cursor: pointer;
|
||||||
|
top: px2rem(11px);
|
||||||
|
right: px2rem(16px);
|
||||||
|
}
|
||||||
|
|
||||||
|
&-body::-webkit-scrollbar {
|
||||||
|
width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-body {
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: scroll;
|
||||||
|
|
||||||
|
table {
|
||||||
|
border-spacing: 0 px2rem(4px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.rdt .rdtPicker td.rdtActive, .rdt .rdtPicker td.rdtActive:hover {
|
||||||
|
background: transparent;
|
||||||
|
color: var(--Calendar-color);
|
||||||
|
text-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rdt .rdtPicker tr td.rdtDisabled, .rdt .rdtPicker tr td.rdtDisabled:hover {
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rdtOldNone td.rdtOld {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rdtPicker td {
|
||||||
|
height: px2rem(56px);
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rdt .rdtPicker {
|
||||||
|
td.rdtDay,
|
||||||
|
td.rdtDay:hover,
|
||||||
|
td.rdtDisabled,
|
||||||
|
td.rdtDisabled:hover {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-wrap {
|
||||||
|
position: relative;
|
||||||
|
width: px2rem(50px);
|
||||||
|
height: 100%;
|
||||||
|
text-align: center;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border-radius: px2rem(4px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-embed &-body {
|
||||||
|
.calendar-wrap {
|
||||||
|
width: auto;
|
||||||
|
max-width: px2rem(50px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-footer {
|
||||||
|
flex-shrink: 0;
|
||||||
|
|
||||||
|
.date-range-confirm {
|
||||||
|
height: px2rem(36px);
|
||||||
|
margin: px2rem(7px) 0;
|
||||||
|
border-radius: var(--borderRadiusMd);
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.is-disabled {
|
||||||
|
opacity: 0.5;
|
||||||
|
color: var(--Button--primary-color);
|
||||||
|
background: var(--Button--primary-bg);
|
||||||
|
border-color: var(--Button--primary-bg);
|
||||||
|
filter: none;
|
||||||
|
}
|
||||||
|
&-toolbar {
|
||||||
|
padding: 0 px2rem(16px);
|
||||||
|
}
|
||||||
|
&-ranges {
|
||||||
|
background: #fff;
|
||||||
|
box-shadow: 0 0 2px 2px rgba(0,0,0,0.02);
|
||||||
|
border-radius: 24px;
|
||||||
|
overflow-x: scroll;
|
||||||
|
position: relative;
|
||||||
|
height: px2rem(48px);
|
||||||
|
line-height: px2rem(48px);
|
||||||
|
}
|
||||||
|
.#{$ns}DateRangePicker-rangers {
|
||||||
|
position: absolute;
|
||||||
|
white-space: nowrap;
|
||||||
|
.#{$ns}DateRangePicker-ranger {
|
||||||
|
margin: 0 px2rem(25px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-calendar-wrap {
|
||||||
|
padding: var(--gap-sm) 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
& &-calendar-wrap &-calendar .rdtPicker {
|
||||||
|
width: 100%;
|
||||||
|
padding: 0;
|
||||||
|
.rdtOld {
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
.rdtNew {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.rdtBetween {
|
||||||
|
background: var(--Calendar-cell-onBetween-bg) !important;
|
||||||
|
color: var(--Button--primary-bg);
|
||||||
|
}
|
||||||
|
.rdtRangeStart,
|
||||||
|
.rdtRangeStart:hover,
|
||||||
|
.rdtRangeEnd,
|
||||||
|
.rdtRangeEnd:hover {
|
||||||
|
.calendar-wrap {
|
||||||
|
background: var(--Calendar-cell-onActive-bg) !important;
|
||||||
|
color: #fff;
|
||||||
|
text-shadow: 0 -1px 0 rgb(0 0 0 / 25%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.rdtRangeHasEnd,
|
||||||
|
.rdtRangeHasEnd:hover {
|
||||||
|
background: linear-gradient(to right, transparent 0%, transparent 50%, var(--Calendar-cell-onBetween-bg) 51%, var(--Calendar-cell-onBetween-bg) 100%) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rdtRangeEnd,
|
||||||
|
.rdtRangeEnd:hover {
|
||||||
|
background: linear-gradient(to right, var(--Calendar-cell-onBetween-bg) 0%, var(--Calendar-cell-onBetween-bg) 50%, transparent 51%, transparent 100%) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-calendar-header {
|
||||||
|
height: px2rem(30px);
|
||||||
|
line-height: px2rem(30px);
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-range-text {
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
color: #fff;
|
||||||
|
font-size: var(--fontSizeSm);
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-calendar-wrap {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-calendar-mark {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
z-index: 0;
|
||||||
|
color: rgba(242, 243, 245, 0.8);
|
||||||
|
font-size: px2rem(160px);
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-toast {
|
||||||
|
position: fixed;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
width: px2rem(136px);
|
||||||
|
height: px2rem(36px);
|
||||||
|
background: rgba(0, 0, 0, .9);
|
||||||
|
border-radius: 4px;
|
||||||
|
color: #fff;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-time {
|
||||||
|
height: px2rem(200px);
|
||||||
|
&-title {
|
||||||
|
border: var(--Calendar-borderWidth) solid var(--borderColorDarken);
|
||||||
|
border-left: none;
|
||||||
|
border-right: none;
|
||||||
|
text-align: center;
|
||||||
|
height: px2rem(30px);
|
||||||
|
line-height: px2rem(30px);
|
||||||
|
}
|
||||||
|
.rdtPicker {
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -331,7 +331,7 @@
|
|||||||
}
|
}
|
||||||
&-multiMedia-img {
|
&-multiMedia-img {
|
||||||
display: block;
|
display: block;
|
||||||
width: 100%;
|
width: auto;
|
||||||
height: auto;
|
height: auto;
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
border-radius: var(--Card-borderRadius);
|
border-radius: var(--Card-borderRadius);
|
||||||
|
98
scss/components/_cascader.scss
Normal file
98
scss/components/_cascader.scss
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
.#{$ns}Cascader-tabs {
|
||||||
|
display: flex;
|
||||||
|
&.scrollable {
|
||||||
|
display: block;
|
||||||
|
overflow-x: auto;
|
||||||
|
white-space: nowrap;
|
||||||
|
&::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.#{$ns}Cascader-tab {
|
||||||
|
flex: 1;
|
||||||
|
width: calc((100vw - 20px) / 3);
|
||||||
|
height: px2rem(370px);
|
||||||
|
overflow-y: auto;
|
||||||
|
display: inline-block;
|
||||||
|
&::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.#{$ns}Cascader {
|
||||||
|
width: 100%;
|
||||||
|
padding: 0 10px;
|
||||||
|
&-Nav {
|
||||||
|
overflow-x: auto;
|
||||||
|
&Item {
|
||||||
|
display: inline-block;
|
||||||
|
margin-right: px2rem(10px);
|
||||||
|
list-style: none;
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
|
padding: 0 px2rem(6px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&-btnGroup {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
height: px2rem(60px);
|
||||||
|
}
|
||||||
|
&-options {
|
||||||
|
box-sizing: border-box;
|
||||||
|
height: var(--Cascader-option-height);
|
||||||
|
padding-top: px2rem(6px);
|
||||||
|
overflow-y: auto;
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
&-option {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: px2rem(6px) 0;
|
||||||
|
font-size: var(--fontSizeMd);
|
||||||
|
line-height: var(--Cascader-option-lineHeight);
|
||||||
|
cursor: pointer;
|
||||||
|
position: relative;
|
||||||
|
&.selected {
|
||||||
|
span {
|
||||||
|
color: var(--primary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.disabled {
|
||||||
|
span {
|
||||||
|
color: gray;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&--text {
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
&-selectedNum {
|
||||||
|
min-width: px2rem(16px);
|
||||||
|
height: px2rem(16px);
|
||||||
|
line-height: px2rem(16px);
|
||||||
|
border-radius: 100%;
|
||||||
|
text-align: center;
|
||||||
|
background: var(--Form-select-menu-onActive-color);
|
||||||
|
color: var(--white) !important;
|
||||||
|
font-size: var(--fontSizeSm);
|
||||||
|
display: inline-block;
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&-icon {
|
||||||
|
color: var(--primary);
|
||||||
|
}
|
||||||
|
&-tab {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,4 @@
|
|||||||
.#{$ns}CollapseGroup {
|
.#{$ns}CollapseGroup {
|
||||||
|
|
||||||
.#{$ns}Collapse:not(:last-child) {
|
.#{$ns}Collapse:not(:last-child) {
|
||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
}
|
}
|
||||||
@ -11,5 +10,4 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
|
||||||
|
146
scss/components/_formula.scss
Normal file
146
scss/components/_formula.scss
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
.#{$ns}FormulaEditor {
|
||||||
|
overflow: visible;
|
||||||
|
max-width: 100%;
|
||||||
|
box-sizing: content-box;
|
||||||
|
|
||||||
|
&-content {
|
||||||
|
border-radius: var(--borderRadius);
|
||||||
|
border: var(--Form-input-borderWidth) solid var(--Form-input-borderColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
&-header {
|
||||||
|
width: 100%;
|
||||||
|
height: px2rem(30px);
|
||||||
|
line-height: px2rem(30px);
|
||||||
|
padding: 0 #{px2rem(10px)};
|
||||||
|
box-sizing: border-box;
|
||||||
|
background: var(--Formula-header-bgColor);
|
||||||
|
border-radius: var(--borderRadius) var(--borderRadius) 0 0;
|
||||||
|
border-bottom: var(--Form-input-borderWidth) solid
|
||||||
|
var(--Form-input-borderColor);
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-editor {
|
||||||
|
min-height: px2rem(238px);
|
||||||
|
max-height: px2rem(320px);
|
||||||
|
height: auto;
|
||||||
|
padding: #{px2rem(10px)};
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-error &-editor {
|
||||||
|
border-color: var(--Form-input-onError-borderColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-focused &-editor {
|
||||||
|
border-color: var(--Form-input-onFocused-borderColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
&-settings {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: stretch;
|
||||||
|
justify-content: space-between;
|
||||||
|
max-height: px2rem(350px);
|
||||||
|
margin: 0 -5px;
|
||||||
|
|
||||||
|
> div {
|
||||||
|
flex: 1;
|
||||||
|
padding: 0 5px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
> h3 {
|
||||||
|
padding: 10px 0;
|
||||||
|
margin: 0;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
> div {
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.cm-field,
|
||||||
|
.cm-func {
|
||||||
|
border-radius: 2px;
|
||||||
|
color: #fff;
|
||||||
|
margin: 0 1px;
|
||||||
|
padding: 0 2px;
|
||||||
|
}
|
||||||
|
.cm-field {
|
||||||
|
background: #007bff;
|
||||||
|
}
|
||||||
|
.cm-func {
|
||||||
|
background: #17a2b8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.#{$ns}FormulaFuncList {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
& > &-searchBox {
|
||||||
|
display: flex;
|
||||||
|
width: auto;
|
||||||
|
flex-shrink: 0;
|
||||||
|
margin-bottom: px2rem(8px);
|
||||||
|
}
|
||||||
|
|
||||||
|
&-columns {
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
|
overflow: auto;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: flex-start;
|
||||||
|
|
||||||
|
> div:first-child {
|
||||||
|
min-width: 200px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&-funcItem {
|
||||||
|
padding: 0 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&.is-active {
|
||||||
|
background: var(--Formula-funcItem-bgColor-onActive);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&-groupTitle {
|
||||||
|
padding: 5px 0;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
&-groupBody {
|
||||||
|
> div {
|
||||||
|
padding: 5px 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&-funcDetail {
|
||||||
|
padding: 10px 20px;
|
||||||
|
|
||||||
|
pre {
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-wrap: break-word;
|
||||||
|
background: var(--Formula-header-bgColor);
|
||||||
|
padding: #{px2rem(10px)};
|
||||||
|
border-radius: var(--borderRadius);
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
div {
|
||||||
|
color: var(--text--loud-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.#{$ns}FormulaPicker {
|
||||||
|
&-icon {
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: #{px2rem(5px)};
|
||||||
|
}
|
||||||
|
}
|
@ -231,3 +231,21 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 移动端样式调整 */
|
||||||
|
@include media-breakpoint-down(sm) {
|
||||||
|
.#{$ns}Modal {
|
||||||
|
.#{$ns}Modal-footer {
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
> .#{$ns}Button {
|
||||||
|
flex: 1;
|
||||||
|
height: px2rem(44px);
|
||||||
|
|
||||||
|
&:first-child {
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -182,3 +182,48 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 移动端样式调整 */
|
||||||
|
@include media-breakpoint-down(sm) {
|
||||||
|
.#{$ns}Panel--form {
|
||||||
|
border: none;
|
||||||
|
box-shadow: none;
|
||||||
|
|
||||||
|
margin: 0 calc(var(--Panel-bodyPadding) * -1)
|
||||||
|
calc(var(--Panel-marginBottom) / 2);
|
||||||
|
|
||||||
|
.#{$ns}Panel-body {
|
||||||
|
padding: 0 var(--gap-md) var(--gap-md);
|
||||||
|
}
|
||||||
|
|
||||||
|
> .#{$ns}Panel-heading {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
border-radius: 0;
|
||||||
|
|
||||||
|
.#{$ns}Panel-title {
|
||||||
|
padding-left: var(--Panel-bodyPadding);
|
||||||
|
border-left: px2rem(3px) solid var(--primary);
|
||||||
|
font-size: var(--fontSizeLg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.#{$ns}Panel-footerWrap {
|
||||||
|
padding-bottom: var(--Panel-bodyPadding);
|
||||||
|
}
|
||||||
|
|
||||||
|
.#{$ns}Panel-footer {
|
||||||
|
border-top: none;
|
||||||
|
display: flex;
|
||||||
|
padding: 0 var(--Panel-bodyPadding);
|
||||||
|
|
||||||
|
> .#{$ns}Button {
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
&:first-child {
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -2,14 +2,9 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
background-color: var(--PickerColumns-bg);
|
background-color: var(--PickerColumns-bg);
|
||||||
user-select: none;
|
user-select: none;
|
||||||
|
flex: 1;
|
||||||
&-popOver {
|
overflow: hidden;
|
||||||
position: fixed;
|
font-size: var(--PickerColumns-option-fontSize);
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
padding: 0 var(--gap-sm);
|
|
||||||
}
|
|
||||||
|
|
||||||
&-toolbar {
|
&-toolbar {
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -30,6 +25,9 @@
|
|||||||
&:active {
|
&:active {
|
||||||
opacity: 0.7;
|
opacity: 0.7;
|
||||||
}
|
}
|
||||||
|
&:hover {
|
||||||
|
background-color: none !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&-confirm {
|
&-confirm {
|
||||||
@ -77,8 +75,22 @@
|
|||||||
z-index: 2;
|
z-index: 2;
|
||||||
transform: translateY(-50%);
|
transform: translateY(-50%);
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
position: absolute;
|
||||||
|
content: '';
|
||||||
|
border-bottom: 1px solid var(--borderColor);
|
||||||
|
border-top: 1px solid var(--borderColor);
|
||||||
|
top: -50%;
|
||||||
|
right: -50%;
|
||||||
|
left: -50%;
|
||||||
|
bottom: -50%;
|
||||||
|
transform: scale(0.5);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
&-mask {
|
&-mask {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
@ -98,12 +110,6 @@
|
|||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
&-column {
|
|
||||||
flex: 1;
|
|
||||||
overflow: hidden;
|
|
||||||
font-size: var(--PickerColumns-option-fontSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
&-columnWrapper {
|
&-columnWrapper {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
149
scss/components/_popup.scss
Normal file
149
scss/components/_popup.scss
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
@keyframes PopUpIn {
|
||||||
|
from {
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes PopUpOut {
|
||||||
|
to {
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes PopUpOpacityIn {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes PopUpOpacityOut {
|
||||||
|
to {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.#{$ns}PopUp {
|
||||||
|
width: 100%;
|
||||||
|
height: px2rem(400px);
|
||||||
|
position: fixed;
|
||||||
|
background: var(--PopOver-bg);
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
z-index: $zindex-popover;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
font-weight: var(--fontWeightNormal);
|
||||||
|
letter-spacing: normal;
|
||||||
|
line-height: var(--lineHeightBase);
|
||||||
|
text-align: left;
|
||||||
|
text-align: start;
|
||||||
|
text-decoration: none;
|
||||||
|
text-shadow: none;
|
||||||
|
text-transform: none;
|
||||||
|
white-space: normal;
|
||||||
|
word-break: normal;
|
||||||
|
word-spacing: normal;
|
||||||
|
word-wrap: normal;
|
||||||
|
font-size: var(--fontSizeBase);
|
||||||
|
box-shadow: var(--boxShadow);
|
||||||
|
border: var(--borderWidth) solid var(--borderColor);
|
||||||
|
border-radius: var(--borderRadius);
|
||||||
|
overflow: hidden;
|
||||||
|
&.in,
|
||||||
|
&.out {
|
||||||
|
animation-duration: var(--animation-duration);
|
||||||
|
animation-fill-mode: both;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.in {
|
||||||
|
animation-name: PopUpIn;
|
||||||
|
.#{$ns}PopUp-overlay {
|
||||||
|
animation-name: PopUpOpacityIn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.out {
|
||||||
|
animation-name: PopUpOut;
|
||||||
|
.#{$ns}PopUp-overlay {
|
||||||
|
animation-name: PopUpOpacityOut;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-inner {
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
height: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
background: $white;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-closeWrap {
|
||||||
|
position: relative;
|
||||||
|
text-align: center;
|
||||||
|
height: px2rem(48px);
|
||||||
|
line-height: px2rem(48px);
|
||||||
|
}
|
||||||
|
|
||||||
|
&-closeWrap &-close {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 1;
|
||||||
|
color: var(--icon-color);
|
||||||
|
cursor: pointer;
|
||||||
|
top: px2rem(15px);
|
||||||
|
right: px2rem(15px);
|
||||||
|
}
|
||||||
|
|
||||||
|
&-toolbar {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
height: px2rem(60px);
|
||||||
|
}
|
||||||
|
&-title {
|
||||||
|
font-size: var(--fontSizeMd);
|
||||||
|
}
|
||||||
|
|
||||||
|
&-cancel {
|
||||||
|
margin-left: var(--gap-sm);
|
||||||
|
}
|
||||||
|
|
||||||
|
&-confirm {
|
||||||
|
margin-right: var(--gap-sm);
|
||||||
|
}
|
||||||
|
|
||||||
|
&-content {
|
||||||
|
overflow-y: auto;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > * {
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-overlay {
|
||||||
|
position: fixed !important;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
z-index: 1;
|
||||||
|
bottom: 0;
|
||||||
|
background: rgba(0, 0, 0, 0.3);
|
||||||
|
opacity: 1;
|
||||||
|
animation-duration: var(--animation-duration);
|
||||||
|
animation-fill-mode: both;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--leftBottomLeftTop {
|
||||||
|
margin-top: px2rem(4px);
|
||||||
|
}
|
||||||
|
&--leftTopLeftBottom {
|
||||||
|
margin-top: px2rem(-4px);
|
||||||
|
}
|
||||||
|
&-safearea {
|
||||||
|
height: px2rem(16px);
|
||||||
|
}
|
||||||
|
}
|
@ -126,4 +126,35 @@
|
|||||||
padding-left: 8px;
|
padding-left: 8px;
|
||||||
min-height: 24px;
|
min-height: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.is-mobile {
|
||||||
|
min-height: calc(var(--Form-input-lineHeight) * var(--fontSizeLg));
|
||||||
|
border: none;
|
||||||
|
padding: 0;
|
||||||
|
font-size: var(--fontSizeLg);
|
||||||
|
border: none;
|
||||||
|
justify-content: flex-end;
|
||||||
|
|
||||||
|
.#{$ns}ResultBox-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;
|
||||||
|
margin-left: 4px;
|
||||||
|
|
||||||
|
> svg {
|
||||||
|
transition: transform var(--animation-duration);
|
||||||
|
display: inline-block;
|
||||||
|
color: var(--Form-select-caret-iconColor);
|
||||||
|
width: 10px;
|
||||||
|
height: 10px;
|
||||||
|
top: 0;
|
||||||
|
transform: rotate(-90deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -140,3 +140,63 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.#{$ns}Steps-mobile.#{$ns}Steps--horizontal {
|
||||||
|
.#{$ns}StepsItem {
|
||||||
|
|
||||||
|
&-container {
|
||||||
|
&Wrapper {
|
||||||
|
.#{$ns}StepsItem-body {
|
||||||
|
.#{$ns}StepsItem-title {
|
||||||
|
&::after {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.#{$ns}StepsItem-body{
|
||||||
|
.#{$ns}StepsItem-title {
|
||||||
|
.#{$ns}StepsItem-subTitle {
|
||||||
|
padding-left: px2rem(5px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&Icon {
|
||||||
|
position: relative;
|
||||||
|
display: block;
|
||||||
|
|
||||||
|
&:after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
top: px2rem(15px);
|
||||||
|
height: 1px;
|
||||||
|
left: px2rem(40px);
|
||||||
|
width: 99999px;
|
||||||
|
background-color: var(--Steps-line-bg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&Icon.is-success {
|
||||||
|
&:after {
|
||||||
|
background-color: var(--Steps-line-success-bg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
.#{$ns}StepsItem-container {
|
||||||
|
&Icon {
|
||||||
|
&:after {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
198
scss/components/_timeline.scss
Normal file
198
scss/components/_timeline.scss
Normal file
@ -0,0 +1,198 @@
|
|||||||
|
.#{$ns}Timeline-vertical {
|
||||||
|
display: flex;
|
||||||
|
flex-flow: column;
|
||||||
|
|
||||||
|
.#{$ns}TimelineItem {
|
||||||
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
|
flex-direction: row;
|
||||||
|
|
||||||
|
&:last-of-type {
|
||||||
|
.#{$ns}TimelineItem-axle .#{$ns}TimelineItem-line {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-axle {
|
||||||
|
position: relative;
|
||||||
|
flex: var(--TimelineItem--axle-flex);
|
||||||
|
|
||||||
|
.#{$ns}TimelineItem-line {
|
||||||
|
position: absolute;
|
||||||
|
height: calc(100% - var(--TimelineItem--left-line-top));
|
||||||
|
width: var(--TimelineItem--left-line-width);
|
||||||
|
left: var(--TimelineItem--left-line-left);
|
||||||
|
top: var(--TimelineItem--left-line-top);
|
||||||
|
background-color: var(--TimelineItem--line-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.#{$ns}TimelineItem-round {
|
||||||
|
position: absolute;
|
||||||
|
width: var(--TimelineItem--round-width);
|
||||||
|
height: var(--TimelineItem--round-height);
|
||||||
|
left: var(--TimelineItem--round-left);
|
||||||
|
top: var(--TimelineItem--round-top);
|
||||||
|
background: var(--TimelineItem-round-bg);
|
||||||
|
border-radius: var(--TimelineItem--round-radius);
|
||||||
|
|
||||||
|
&--danger {
|
||||||
|
background: var(--Timeline--danger-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
&--info {
|
||||||
|
background: var(--Timeline--info-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
&--success {
|
||||||
|
background: var(--Timeline--success-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
&--warning {
|
||||||
|
background: var(--Timeline--warning-bg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.#{$ns}TimelineItem-icon {
|
||||||
|
position: absolute;
|
||||||
|
width: var(--TimelineItem--icon-width);
|
||||||
|
height: var(--TimelineItem--icon-height);
|
||||||
|
left: var(--TimelineItem--icon-left);
|
||||||
|
border-radius: var(--TimelineItem--icon-radius);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-content {
|
||||||
|
padding-bottom: var(--TimelineItem--content-padding-bottom);
|
||||||
|
margin-left: var(--TimelineItem--content-margin-left);
|
||||||
|
|
||||||
|
.#{$ns}TimelineItem-time {
|
||||||
|
color: var(--TimelineItem--text-secondary-color);
|
||||||
|
font-size: var(--Timeline--font-size);
|
||||||
|
margin-bottom: var(--TimelineItem--content-time-margin-bottom);
|
||||||
|
}
|
||||||
|
|
||||||
|
.#{$ns}TimelineItem-title {
|
||||||
|
color: var(--TimelineItem--text-primary-color);
|
||||||
|
font-size: var(--Timeline--font-size);
|
||||||
|
margin-bottom: var(--TimelineItem--content-title-margin-bottom);
|
||||||
|
}
|
||||||
|
|
||||||
|
.#{$ns}TimelineItem-detail {
|
||||||
|
.#{$ns}TimelineItem-detail-button {
|
||||||
|
display: flex;
|
||||||
|
cursor: pointer;
|
||||||
|
align-items: center;
|
||||||
|
font-size: var(--Timeline--font-size);
|
||||||
|
color: var(--TimelineItem--detail-button-color);
|
||||||
|
margin-bottom: var(--TimelineItem--detail-button-margin-bottom);
|
||||||
|
}
|
||||||
|
|
||||||
|
.#{$ns}TimelineItem-detail-arrow {
|
||||||
|
width: var(--TimelineItem-detail-arrow-width);
|
||||||
|
height: var(--TimelineItem-detail-arrow-width);
|
||||||
|
}
|
||||||
|
|
||||||
|
.#{$ns}TimelineItem-detail-arrow-top {
|
||||||
|
transform: rotateX(180deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.#{$ns}TimelineItem-detail-visible {
|
||||||
|
display: block;
|
||||||
|
max-width: var(--TimelineItem-detail-visible-max-width);
|
||||||
|
font-size: var(--Timeline--font-size);
|
||||||
|
padding: var(--TimelineItem-detail-visible-padding);
|
||||||
|
box-shadow: var(--TimelineItem-detail-visible-shadow);
|
||||||
|
}
|
||||||
|
|
||||||
|
.#{$ns}TimelineItem-detail-invisible {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.#{$ns}Timeline-left {
|
||||||
|
.#{$ns}TimelineItem {
|
||||||
|
flex-direction: row-reverse;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.#{$ns}Timeline-alternate {
|
||||||
|
.#{$ns}TimelineItem:nth-child(odd) {
|
||||||
|
flex-direction: row-reverse;
|
||||||
|
max-width: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.#{$ns}TimelineItem:nth-child(even) {
|
||||||
|
margin-left: calc(50% - var(--Timeline-alternate-margin-left));
|
||||||
|
max-width: calc(50% + var(--Timeline-alternate-margin-left));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.#{$ns}Timeline-horizontal {
|
||||||
|
display: flex;
|
||||||
|
flex-flow: row;
|
||||||
|
margin-left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
|
||||||
|
.#{$ns}TimelineItem {
|
||||||
|
display: flex;
|
||||||
|
width: -webkit-fill-available;
|
||||||
|
flex-flow: column;
|
||||||
|
|
||||||
|
&:last-of-type {
|
||||||
|
.#{$ns}TimelineItem-axle .#{$ns}TimelineItem-line {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-axle {
|
||||||
|
position: relative;
|
||||||
|
flex: var(--TimelineItem--axle-flex);
|
||||||
|
|
||||||
|
.#{$ns}TimelineItem-line {
|
||||||
|
position: absolute;
|
||||||
|
height: var(--TimelineItem--left-line-width);
|
||||||
|
width: calc(100% - var(--TimelineItem--left-line-left));
|
||||||
|
left: var(--TimelineItem--left-line-top);
|
||||||
|
top: var(--TimelineItem--left-line-left);
|
||||||
|
background-color: var(--TimelineItem--line-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.#{$ns}TimelineItem-round {
|
||||||
|
position: absolute;
|
||||||
|
width: var(--TimelineItem--round-width);
|
||||||
|
height: var(--TimelineItem--round-height);
|
||||||
|
left: var(--TimelineItem--round-top);
|
||||||
|
top: var(--TimelineItem--round-left);
|
||||||
|
background: var(--TimelineItem-round-bg);
|
||||||
|
border-radius: var(--TimelineItem--round-radius);
|
||||||
|
|
||||||
|
&--danger {
|
||||||
|
background: var(--Timeline--danger-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
&--info {
|
||||||
|
background: var(--Timeline--info-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
&--success {
|
||||||
|
background: var(--Timeline--success-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
&--warning {
|
||||||
|
background: var(--Timeline--warning-bg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.#{$ns}TimelineItem-icon {
|
||||||
|
position: absolute;
|
||||||
|
width: var(--TimelineItem--icon-width);
|
||||||
|
height: var(--TimelineItem--icon-height);
|
||||||
|
left: var(--TimelineItem--icon-left);
|
||||||
|
border-radius: var(--TimelineItem--icon-radius);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -29,6 +29,8 @@
|
|||||||
pointer-events: all;
|
pointer-events: all;
|
||||||
margin-left: var(--Checkbox-gap);
|
margin-left: var(--Checkbox-gap);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
|
||||||
> a {
|
> a {
|
||||||
// float: right;
|
// float: right;
|
||||||
|
@ -10,6 +10,10 @@
|
|||||||
color: var(--ColorPicker-color);
|
color: var(--ColorPicker-color);
|
||||||
border-radius: var(--borderRadius);
|
border-radius: var(--borderRadius);
|
||||||
|
|
||||||
|
&-popup{
|
||||||
|
height: 80vh;
|
||||||
|
}
|
||||||
|
|
||||||
&:not(.is-disabled) {
|
&:not(.is-disabled) {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
|
@ -127,6 +127,10 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.#{$ns}DateRangePicker-popup {
|
||||||
|
height: 90vh;
|
||||||
|
}
|
||||||
|
|
||||||
@include media-breakpoint-up(sm) {
|
@include media-breakpoint-up(sm) {
|
||||||
.#{$ns}DateRangePicker-wrap {
|
.#{$ns}DateRangePicker-wrap {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
@ -122,6 +122,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.#{$ns}DatePicker-popup {
|
||||||
|
height: 80vh;
|
||||||
|
}
|
||||||
// override third-party styles
|
// override third-party styles
|
||||||
.rdt {
|
.rdt {
|
||||||
user-select: none;
|
user-select: none;
|
||||||
|
@ -15,8 +15,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&-selectBtn {
|
&-selectBtn {
|
||||||
display: flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
margin-right: 10px;
|
||||||
|
|
||||||
> svg {
|
> svg {
|
||||||
width: px2rem(14px);
|
width: px2rem(14px);
|
||||||
@ -40,7 +41,6 @@
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
&-description {
|
&-description {
|
||||||
margin-left: 10px;
|
|
||||||
color: #999;
|
color: #999;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
|
@ -406,3 +406,171 @@
|
|||||||
.#{$ns}Form-column-10 > .#{$ns}Form-item {
|
.#{$ns}Form-column-10 > .#{$ns}Form-item {
|
||||||
width: 10%;
|
width: 10%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 移动端样式调整 */
|
||||||
|
@include media-breakpoint-down(sm) {
|
||||||
|
.#{$ns}Form {
|
||||||
|
.#{$ns}Form-item {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
margin-bottom: 0;
|
||||||
|
padding: var(--Form-item-gap) 0;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
position: absolute;
|
||||||
|
box-sizing: border-box;
|
||||||
|
content: ' ';
|
||||||
|
pointer-events: none;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
border-bottom: var(--Form-input-borderWidth) solid var(--borderColor);
|
||||||
|
transform: scaleY(0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.#{$ns}InputGroup-addOn,
|
||||||
|
.#{$ns}TextControl-addOn {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .#{$ns}Form-label {
|
||||||
|
flex: 0 0 28%;
|
||||||
|
max-width: 28%;
|
||||||
|
min-height: 1px;
|
||||||
|
text-align: left;
|
||||||
|
padding-right: calc(var(--Form--horizontal-gutterWidth) / 2);
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
margin-right: 0;
|
||||||
|
margin-bottom: 0;
|
||||||
|
font-size: var(--fontSizeLg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.#{$ns}Form-description {
|
||||||
|
font-size: var(--fontSizeBase);
|
||||||
|
}
|
||||||
|
|
||||||
|
.#{$ns}TextControl-input {
|
||||||
|
font-size: var(--fontSizeLg);
|
||||||
|
|
||||||
|
input {
|
||||||
|
height: calc(var(--Form-input-lineHeight) * var(--fontSizeLg));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.#{$ns}Form-value,
|
||||||
|
.#{$ns}Form-control {
|
||||||
|
flex: 1;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
font-size: var(--fontSizeLg);
|
||||||
|
|
||||||
|
&.is-disabled > .#{$ns}TextControl-input {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.#{$ns}Form-hint,
|
||||||
|
.#{$ns}Form-remark,
|
||||||
|
.#{$ns}Form-static,
|
||||||
|
.#{$ns}Form-group--hor .#{$ns}Form-item,
|
||||||
|
.#{$ns}SwitchControl,
|
||||||
|
.#{$ns}CheckboxControl,
|
||||||
|
.#{$ns}RadiosControl,
|
||||||
|
.#{$ns}CheckboxesControl {
|
||||||
|
padding-top: 0;
|
||||||
|
padding-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.#{$ns}Form-group--horizontal .#{$ns}TextControl-input input {
|
||||||
|
height: var(--Form-input-height);
|
||||||
|
}
|
||||||
|
|
||||||
|
.#{$ns}Form-hint {
|
||||||
|
font-size: var(--fontSizeBase);
|
||||||
|
margin-left: 0;
|
||||||
|
color: var(--text--muted-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.#{$ns}TextControl-placeholder {
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.#{$ns}Form-static {
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.#{$ns}Form-description,
|
||||||
|
.#{$ns}Form-feedback {
|
||||||
|
font-size: var(--fontSizeBase);
|
||||||
|
}
|
||||||
|
|
||||||
|
.#{$ns}InputGroup {
|
||||||
|
.#{$ns}Select,
|
||||||
|
.#{$ns}InputGroup-btn .#{$ns}Button {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .#{$ns}TextControl-input input {
|
||||||
|
height: var(--Form-input-height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.#{$ns}ColorPicker {
|
||||||
|
padding: 0;
|
||||||
|
border: none;
|
||||||
|
|
||||||
|
.#{$ns}ColorPicker-arrow {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.#{$ns}Form-group--hor .#{$ns}Form-item .#{$ns}Button {
|
||||||
|
margin-bottom: var(--gap-xs);
|
||||||
|
}
|
||||||
|
|
||||||
|
.#{$ns}TextareaControl > textarea,
|
||||||
|
.#{$ns}Form-control > .#{$ns}TextControl-input,
|
||||||
|
.#{$ns}TextControl.is-focused > .#{$ns}TextControl-input {
|
||||||
|
border: none;
|
||||||
|
padding: 0 var(--Form-input-paddingX) 0 0;
|
||||||
|
box-shadow: none;
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&:focus,
|
||||||
|
&.active {
|
||||||
|
border: none;
|
||||||
|
outline: none;
|
||||||
|
outline-style: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.#{$ns}Form-control > .#{$ns}TextControl-input--multiple {
|
||||||
|
padding: 0;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.#{$ns}Form-groupColumn {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.#{$ns}Divider {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.#{$ns}Tabs-pane {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .#{$ns}Form-item {
|
||||||
|
&:last-child::after,
|
||||||
|
&:last-of-type::after {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.#{$ns}Form-item .#{$ns}Form-groupColumn > .#{$ns}Form-item {
|
||||||
|
padding-bottom: var(--Form-input-paddingX);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -87,4 +87,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
&-popup {
|
||||||
|
height: px2rem(460px);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,31 +1,70 @@
|
|||||||
.#{$ns}Rating {
|
.#{$ns}Rating {
|
||||||
|
display: flex;
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow: hidden;
|
align-items: center;
|
||||||
display: block;
|
justify-content: flex-start;
|
||||||
float: left;
|
flex-flow: row wrap;
|
||||||
font-size: px2rem(24px);
|
|
||||||
color: var(--dark);
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
&.is-active {
|
& > ul {
|
||||||
color: var(--Rating-onActive-color);
|
display: flex;
|
||||||
|
padding: unset;
|
||||||
|
margin: unset;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-start;
|
||||||
|
flex-flow: row wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.is-disabled {
|
&-star {
|
||||||
cursor: not-allowed;
|
position: relative;
|
||||||
pointer-events: none;
|
margin-right: var(--Rating-star-margin);
|
||||||
}
|
|
||||||
|
|
||||||
&-half:before {
|
|
||||||
position: absolute;
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
display: block;
|
display: block;
|
||||||
z-index: 1;
|
font-size: var(--Rating-star-size);
|
||||||
top: 0;
|
line-height: 1;
|
||||||
left: 0;
|
cursor: pointer;
|
||||||
width: 50%;
|
user-select: none;
|
||||||
content: attr(data-forhalf);
|
color: var(--Rating-inactive-color);
|
||||||
color: var(--Rating-onActive-color);
|
|
||||||
|
&-half > svg.icon,
|
||||||
|
& > svg.icon {
|
||||||
|
display: block;
|
||||||
|
width: px2rem(24px);
|
||||||
|
height: px2rem(24px);
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-of-type {
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-disabled {
|
||||||
|
cursor: not-allowed;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-half {
|
||||||
|
position: absolute;
|
||||||
|
overflow: hidden;
|
||||||
|
display: block;
|
||||||
|
z-index: 1;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-text {
|
||||||
|
font-size: var(--fontSizeSm);
|
||||||
|
color: var(--text-color);
|
||||||
|
font-weight: var(--fontWeightNormal);
|
||||||
|
|
||||||
|
&--left {
|
||||||
|
margin-right: var(--Rating-star-margin);
|
||||||
|
}
|
||||||
|
|
||||||
|
&--right {
|
||||||
|
margin-left: var(--Rating-star-margin);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,6 +40,7 @@
|
|||||||
height: var(--Form-input-height);
|
height: var(--Form-input-height);
|
||||||
line-height: var(--Form-input-lineHeight);
|
line-height: var(--Form-input-lineHeight);
|
||||||
font-size: var(--Form-input-fontSize);
|
font-size: var(--Form-input-fontSize);
|
||||||
|
align-items: center;
|
||||||
padding: calc(
|
padding: calc(
|
||||||
(
|
(
|
||||||
var(--Form-input-height) - var(--Form-input-lineHeight) *
|
var(--Form-input-height) - var(--Form-input-lineHeight) *
|
||||||
@ -54,6 +55,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
> .#{$ns}Selections-dragbar {
|
> .#{$ns}Selections-dragbar {
|
||||||
|
top: 0;
|
||||||
width: var(--gap-lg);
|
width: var(--gap-lg);
|
||||||
position: relative;
|
position: relative;
|
||||||
left: calc(var(--gap-xs) * -1);
|
left: calc(var(--gap-xs) * -1);
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
|
align-items: center;
|
||||||
outline: none;
|
outline: none;
|
||||||
position: relative;
|
position: relative;
|
||||||
font-size: var(--Form-input-fontSize);
|
font-size: var(--Form-input-fontSize);
|
||||||
@ -148,10 +149,27 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.is-opened &-arrow > svg {
|
&.is-opened:not(.is-mobile) &-arrow > svg {
|
||||||
transform: rotate(180deg);
|
transform: rotate(180deg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.is-mobile {
|
||||||
|
min-height: calc(var(--Form-input-lineHeight) * var(--fontSizeLg));
|
||||||
|
border: none;
|
||||||
|
padding: 0;
|
||||||
|
font-size: var(--fontSizeLg);
|
||||||
|
|
||||||
|
.#{$ns}Select-valueWrap {
|
||||||
|
text-align: right;
|
||||||
|
padding-right: 4px;
|
||||||
|
}
|
||||||
|
.#{$ns}Select-arrow {
|
||||||
|
> svg {
|
||||||
|
transform: rotate(-90deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&-menu {
|
&-menu {
|
||||||
max-height: px2rem(300px);
|
max-height: px2rem(300px);
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
@ -159,6 +177,13 @@
|
|||||||
.#{$ns}Checkbox--sm > i {
|
.#{$ns}Checkbox--sm > i {
|
||||||
margin-top: px2rem(-3px);
|
margin-top: px2rem(-3px);
|
||||||
}
|
}
|
||||||
|
&.is-mobile {
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
.#{$ns}Select-option {
|
||||||
|
line-height: px2rem(36px);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
&--longlist {
|
&--longlist {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
@ -282,8 +307,8 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.is-focused,
|
&.is-focused:not(.is-mobile),
|
||||||
&.is-opened {
|
&.is-opened:not(.is-mobile) {
|
||||||
border-color: var(--Form-input-onFocused-borderColor);
|
border-color: var(--Form-input-onFocused-borderColor);
|
||||||
color: var(--Form-select-onFocused-color);
|
color: var(--Form-select-onFocused-color);
|
||||||
}
|
}
|
||||||
@ -314,6 +339,10 @@
|
|||||||
fill: var(--Form-input-onHover-iconColor);
|
fill: var(--Form-input-onHover-iconColor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&-popup {
|
||||||
|
height: px2rem(320px);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.#{$ns}Select-popover {
|
.#{$ns}Select-popover {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
.#{$ns}Selection {
|
.#{$ns}Selection {
|
||||||
> .#{$ns}Checkbox {
|
> .#{$ns}Checkbox {
|
||||||
display: block;
|
display: block;
|
||||||
height: var(--Form-input-height);
|
// height: var(--Form-input-height);
|
||||||
line-height: var(--Form-input-lineHeight);
|
line-height: var(--Form-input-lineHeight);
|
||||||
font-size: var(--Form-input-fontSize);
|
font-size: var(--Form-input-fontSize);
|
||||||
padding: calc(
|
padding: calc(
|
||||||
@ -41,6 +41,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.#{$ns}GroupedSelection {
|
.#{$ns}GroupedSelection {
|
||||||
|
max-height: px2rem(300px);
|
||||||
|
overflow: auto;
|
||||||
|
user-select: none;
|
||||||
|
|
||||||
&-group:not(:first-child) > &-itemLabel {
|
&-group:not(:first-child) > &-itemLabel {
|
||||||
border-top: px2rem(1px) solid var(--ListMenu-divider-color);
|
border-top: px2rem(1px) solid var(--ListMenu-divider-color);
|
||||||
}
|
}
|
||||||
@ -53,7 +57,7 @@
|
|||||||
|
|
||||||
&-item {
|
&-item {
|
||||||
display: flex;
|
display: flex;
|
||||||
height: var(--Form-input-height);
|
// height: var(--Form-input-height);
|
||||||
line-height: var(--Form-input-lineHeight);
|
line-height: var(--Form-input-lineHeight);
|
||||||
font-size: var(--Form-input-fontSize);
|
font-size: var(--Form-input-fontSize);
|
||||||
padding: calc(
|
padding: calc(
|
||||||
@ -189,7 +193,7 @@
|
|||||||
&-itemInner {
|
&-itemInner {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
height: var(--Form-input-height);
|
// height: var(--Form-input-height);
|
||||||
line-height: var(--Form-input-lineHeight);
|
line-height: var(--Form-input-lineHeight);
|
||||||
font-size: var(--Form-input-fontSize);
|
font-size: var(--Form-input-fontSize);
|
||||||
padding: calc(
|
padding: calc(
|
||||||
@ -247,6 +251,7 @@
|
|||||||
.#{$ns}ChainedSelection {
|
.#{$ns}ChainedSelection {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
|
min-height: 100%;
|
||||||
|
|
||||||
&-col {
|
&-col {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
@ -265,7 +270,7 @@
|
|||||||
|
|
||||||
&-item {
|
&-item {
|
||||||
display: flex;
|
display: flex;
|
||||||
height: var(--Form-input-height);
|
// height: var(--Form-input-height);
|
||||||
line-height: var(--Form-input-lineHeight);
|
line-height: var(--Form-input-lineHeight);
|
||||||
font-size: var(--Form-input-fontSize);
|
font-size: var(--Form-input-fontSize);
|
||||||
padding: calc(
|
padding: calc(
|
||||||
|
@ -26,6 +26,12 @@
|
|||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
transition: all var(--animation-duration);
|
transition: all var(--animation-duration);
|
||||||
|
|
||||||
|
> svg {
|
||||||
|
width: var(--fontSizeSm);
|
||||||
|
height: var(--fontSizeSm);
|
||||||
|
margin-top: calc((var(--Switch-height) - var(--fontSizeSm)) / 2);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.slider {
|
.slider {
|
||||||
|
@ -235,6 +235,9 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
||||||
|
&.is-mobile {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
& > .#{$ns}Transfer-selection {
|
& > .#{$ns}Transfer-selection {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
max-height: 100%;
|
max-height: 100%;
|
||||||
|
@ -15,6 +15,10 @@
|
|||||||
box-shadow: var(--Form-input-boxShadow);
|
box-shadow: var(--Form-input-boxShadow);
|
||||||
background: var(--Form-input-onFocused-bg);
|
background: var(--Form-input-onFocused-bg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&-popup {
|
||||||
|
height: 80vh;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.#{$ns}TreeSelect-popover {
|
.#{$ns}TreeSelect-popover {
|
||||||
|
@ -15,7 +15,7 @@ title: 背景色
|
|||||||
| bg-primary | background: #007bff |
|
| bg-primary | background: #007bff |
|
||||||
| bg-secondary | background: #6c757d |
|
| bg-secondary | background: #6c757d |
|
||||||
| bg-success | background: #28a745 |
|
| bg-success | background: #28a745 |
|
||||||
| bg-info | background: #17a2b8 |
|
| bg-info | background: #007bff |
|
||||||
| bg-warning | background: #28a745 |
|
| bg-warning | background: #28a745 |
|
||||||
| bg-danger | background: #dc3545 |
|
| bg-danger | background: #dc3545 |
|
||||||
| bg-light | background: #f8f9fa |
|
| bg-light | background: #f8f9fa |
|
||||||
|
@ -11,7 +11,7 @@ title: 边框颜色
|
|||||||
| border-primary | border-color: #007bff |
|
| border-primary | border-color: #007bff |
|
||||||
| border-secondary | border-color: #6c757d |
|
| border-secondary | border-color: #6c757d |
|
||||||
| border-success | border-color: #28a745 |
|
| border-success | border-color: #28a745 |
|
||||||
| border-info | border-color: #17a2b8 |
|
| border-info | border-color: #007bff |
|
||||||
| border-warning | border-color: #28a745 |
|
| border-warning | border-color: #28a745 |
|
||||||
| border-danger | border-color: #dc3545 |
|
| border-danger | border-color: #dc3545 |
|
||||||
| border-light | border-color: #f8f9fa |
|
| border-light | border-color: #f8f9fa |
|
||||||
|
@ -13,7 +13,7 @@ title: Text Color
|
|||||||
| text-primary | color: #007bff |
|
| text-primary | color: #007bff |
|
||||||
| text-secondary | color: #6c757d |
|
| text-secondary | color: #6c757d |
|
||||||
| text-success | color: #28a745 |
|
| text-success | color: #28a745 |
|
||||||
| text-info | color: #17a2b8 |
|
| text-info | color: #007bff |
|
||||||
| text-warning | color: #fad733 |
|
| text-warning | color: #fad733 |
|
||||||
| text-danger | color: #dc3545 |
|
| text-danger | color: #dc3545 |
|
||||||
| text-light | color: #f8f9fa |
|
| text-light | color: #f8f9fa |
|
||||||
|
@ -166,4 +166,46 @@ $link-color: $info;
|
|||||||
--Table-onChecked-borderColor: var(--Table-borderColor);
|
--Table-onChecked-borderColor: var(--Table-borderColor);
|
||||||
|
|
||||||
--Switch-bgColor: #bfbfbf;
|
--Switch-bgColor: #bfbfbf;
|
||||||
|
|
||||||
|
// timeline
|
||||||
|
--TimelineItem--axle-flex: 0 0 #{px2rem(24px)};
|
||||||
|
--TimelineItem--left-line-width: #{px2rem(2px)};
|
||||||
|
--TimelineItem--left-line-left: #{px2rem(13px)};
|
||||||
|
--TimelineItem--left-line-top: #{px2rem(20px)};
|
||||||
|
--TimelineItem--round-width: #{px2rem(8px)};
|
||||||
|
--TimelineItem--round-height: #{px2rem(8px)};
|
||||||
|
--TimelineItem--round-left: #{px2rem(10px)};
|
||||||
|
--TimelineItem--round-top: #{px2rem(8px)};
|
||||||
|
--TimelineItem--icon-width: #{px2rem(16px)};
|
||||||
|
--TimelineItem--icon-height: #{px2rem(16px)};
|
||||||
|
--TimelineItem--icon-left: #{px2rem(6px)};
|
||||||
|
--TimelineItem--content-padding-bottom: #{px2rem(16px)};
|
||||||
|
--TimelineItem--content-margin-left: #{px2rem(8px)};
|
||||||
|
--TimelineItem--content-time-margin-bottom: #{px2rem(4px)};
|
||||||
|
--TimelineItem--content-title-margin-bottom: #{px2rem(4px)};
|
||||||
|
--TimelineItem--detail-button-margin-bottom: #{px2rem(8px)};
|
||||||
|
--TimelineItem-detail-arrow-width: #{px2rem(16px)};
|
||||||
|
--TimelineItem-detail-visible-padding: #{px2rem(10px)};
|
||||||
|
--TimelineItem-detail-visible-max-width: #{px2rem(300px)};
|
||||||
|
--Timeline-alternate-margin-left: #{px2rem(24px)};
|
||||||
|
|
||||||
|
--TimelineItem--icon-radius: 50%;
|
||||||
|
--TimelineItem--round-radius: 50%;
|
||||||
|
--TimelineItem--content-radius: #{px2rem(2px)};
|
||||||
|
|
||||||
|
--TimelineItem-detail-visible-shadow: 0 #{px2rem(1px)} #{px2rem(10px)} 0 rgba(0 0 0 / 10%);
|
||||||
|
|
||||||
|
--TimelineItem--font-size: #{px2rem(12px)};
|
||||||
|
|
||||||
|
--TimelineItem--text-primary-color: #151a26;
|
||||||
|
--TimelineItem--text-secondary-color: #83868c;
|
||||||
|
--TimelineItem--detail-button-color: var(--primary);
|
||||||
|
--TimelineItem--line-bg: #e6e6e8;
|
||||||
|
--TimelineItem--content-bg: #f2f2f4;
|
||||||
|
--TimelineItem-round-bg: #dadbdd;
|
||||||
|
|
||||||
|
--Timeline--success-bg: var(--success);
|
||||||
|
--Timeline--info-bg: var(--info);
|
||||||
|
--Timeline--warning-bg: var(--warning);
|
||||||
|
--Timeline--danger-bg: var(--danger);
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
@import '../components/tooltip';
|
@import '../components/tooltip';
|
||||||
@import '../components/tpl';
|
@import '../components/tpl';
|
||||||
@import '../components/popover';
|
@import '../components/popover';
|
||||||
|
@import '../components/popup';
|
||||||
@import '../components/picker-columns';
|
@import '../components/picker-columns';
|
||||||
@import '../components/toast';
|
@import '../components/toast';
|
||||||
@import '../components/alert';
|
@import '../components/alert';
|
||||||
@ -111,11 +112,14 @@
|
|||||||
@import '../components/form/rating';
|
@import '../components/form/rating';
|
||||||
@import '../components/form/transfer';
|
@import '../components/form/transfer';
|
||||||
@import '../components/form/nested-select';
|
@import '../components/form/nested-select';
|
||||||
|
@import '../components/cascader';
|
||||||
@import '../components/form/icon-picker';
|
@import '../components/form/icon-picker';
|
||||||
@import '../components/form/form';
|
@import '../components/form/form';
|
||||||
@import '../components/anchor-nav';
|
@import '../components/anchor-nav';
|
||||||
@import '../components/markdown';
|
@import '../components/markdown';
|
||||||
@import '../components/link';
|
@import '../components/link';
|
||||||
@import '../components/mapping';
|
@import '../components/mapping';
|
||||||
|
@import '../components/formula';
|
||||||
|
@import '../components/timeline';
|
||||||
|
|
||||||
@import '../utilities';
|
@import '../utilities';
|
||||||
|
@ -364,7 +364,7 @@ $L1: 0px 4px 6px 0px rgba(8, 14, 26, 0.06),
|
|||||||
--Table-onHover-bg: #f5f5f5;
|
--Table-onHover-bg: #f5f5f5;
|
||||||
--Table-onHover-bg-rgb: 245, 251, 255;
|
--Table-onHover-bg-rgb: 245, 251, 255;
|
||||||
--Table-onHover-borderColor: #eceff8;
|
--Table-onHover-borderColor: #eceff8;
|
||||||
--Table-onChecked-bg: transparent;
|
--Table-onChecked-bg: #f0faff;
|
||||||
--Table-onChecked-borderColor: #eceff8;
|
--Table-onChecked-borderColor: #eceff8;
|
||||||
--Table-onChecked-color: #333;
|
--Table-onChecked-color: #333;
|
||||||
--Table-onChecked-onHover-bg: #f5f5f5;
|
--Table-onChecked-onHover-bg: #f5f5f5;
|
||||||
@ -610,4 +610,53 @@ $L1: 0px 4px 6px 0px rgba(8, 14, 26, 0.06),
|
|||||||
--Tree-itemHeight: #{px2rem(32px)};
|
--Tree-itemHeight: #{px2rem(32px)};
|
||||||
|
|
||||||
--Progress-borderRadius: #{$R7};
|
--Progress-borderRadius: #{$R7};
|
||||||
|
|
||||||
|
--Rating-inactive-color: #{$G9};
|
||||||
|
|
||||||
|
// timeline
|
||||||
|
--TimelineItem--axle-flex: 0 0 #{px2rem(24px)};
|
||||||
|
--TimelineItem--left-line-width: #{px2rem(2px)};
|
||||||
|
--TimelineItem--left-line-left: #{px2rem(13px)};
|
||||||
|
--TimelineItem--left-line-top: #{px2rem(20px)};
|
||||||
|
--TimelineItem--round-width: #{px2rem(8px)};
|
||||||
|
--TimelineItem--round-height: #{px2rem(8px)};
|
||||||
|
--TimelineItem--round-left: #{px2rem(10px)};
|
||||||
|
--TimelineItem--round-top: #{px2rem(8px)};
|
||||||
|
--TimelineItem--icon-width: #{px2rem(16px)};
|
||||||
|
--TimelineItem--icon-height: #{px2rem(16px)};
|
||||||
|
--TimelineItem--icon-left: #{px2rem(6px)};
|
||||||
|
--TimelineItem--content-padding-bottom: #{px2rem(16px)};
|
||||||
|
--TimelineItem--content-margin-left: #{px2rem(8px)};
|
||||||
|
--TimelineItem--content-time-margin-bottom: #{px2rem(4px)};
|
||||||
|
--TimelineItem--content-title-margin-bottom: #{px2rem(4px)};
|
||||||
|
--TimelineItem--detail-button-margin-bottom: #{px2rem(8px)};
|
||||||
|
--TimelineItem-detail-arrow-width: #{px2rem(16px)};
|
||||||
|
--TimelineItem-detail-visible-padding: #{px2rem(10px)};
|
||||||
|
--TimelineItem-detail-visible-max-width: #{px2rem(300px)};
|
||||||
|
--Timeline-alternate-margin-left: #{px2rem(24px)};
|
||||||
|
|
||||||
|
--TimelineItem--icon-radius: #{$R8};
|
||||||
|
--TimelineItem--round-radius: #{$R8};
|
||||||
|
--TimelineItem--content-radius: #{$R2};
|
||||||
|
|
||||||
|
--TimelineItem-detail-visible-shadow: 0 #{px2rem(1px)} #{px2rem(10px)} 0 rgba(0 0 0 / 10%);
|
||||||
|
|
||||||
|
--TimelineItem--font-size: #{$T2};
|
||||||
|
|
||||||
|
--TimelineItem--text-primary-color: #{$text-color};
|
||||||
|
--TimelineItem--text-secondary-color: #{$G5};
|
||||||
|
--TimelineItem--detail-button-color: var(--primary);
|
||||||
|
--TimelineItem--line-bg: #{$G9};
|
||||||
|
--TimelineItem--content-bg: #{$G10};
|
||||||
|
--TimelineItem-round-bg: #{$G8};
|
||||||
|
|
||||||
|
--Timeline--success-bg: var(--success);
|
||||||
|
--Timeline--info-bg: var(--info);
|
||||||
|
--Timeline--warning-bg: var(--warning);
|
||||||
|
--Timeline--danger-bg: var(--danger);
|
||||||
|
|
||||||
|
|
||||||
|
// Formula
|
||||||
|
--Formula-header-bgColor: #{$G10};
|
||||||
|
--Formula-funcItem-bgColor-onActive: #{$light};
|
||||||
}
|
}
|
||||||
|
@ -57,6 +57,7 @@ $link-color: $info;
|
|||||||
--DatePicker-bg: var(--background);
|
--DatePicker-bg: var(--background);
|
||||||
--DatePicker-header-select-borderColor: var(--background);
|
--DatePicker-header-select-borderColor: var(--background);
|
||||||
--DropDown-menu-bg: var(--background);
|
--DropDown-menu-bg: var(--background);
|
||||||
|
--Drawer-header-bg: var(--background);
|
||||||
--Fieldset-legend-bgColor: var(--background);
|
--Fieldset-legend-bgColor: var(--background);
|
||||||
--Form-input-addOnBg: var(--Form-input-bg);
|
--Form-input-addOnBg: var(--Form-input-bg);
|
||||||
--Form-input-bg: #3c3c3c;
|
--Form-input-bg: #3c3c3c;
|
||||||
|
@ -150,6 +150,7 @@ export function renderChild(
|
|||||||
<SchemaRenderer
|
<SchemaRenderer
|
||||||
{...props}
|
{...props}
|
||||||
schema={schema}
|
schema={schema}
|
||||||
|
propKey={schema.key}
|
||||||
$path={`${prefix ? `${prefix}/` : ''}${(schema && schema.type) || ''}`}
|
$path={`${prefix ? `${prefix}/` : ''}${(schema && schema.type) || ''}`}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -184,12 +184,12 @@ export class RootRenderer extends React.Component<RootRendererProps> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
store.closeDialog();
|
store.closeDialog(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleDialogClose() {
|
handleDialogClose(confirmed = false) {
|
||||||
const store = this.store;
|
const store = this.store;
|
||||||
store.closeDialog();
|
store.closeDialog(confirmed);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleDrawerConfirm(values: object[], action: Action, ...args: Array<any>) {
|
handleDrawerConfirm(values: object[], action: Action, ...args: Array<any>) {
|
||||||
|
@ -56,6 +56,7 @@ import {PaginationSchema} from './renderers/Pagination';
|
|||||||
import {AnchorNavSchema} from './renderers/AnchorNav';
|
import {AnchorNavSchema} from './renderers/AnchorNav';
|
||||||
import {AvatarSchema} from './renderers/Avatar';
|
import {AvatarSchema} from './renderers/Avatar';
|
||||||
import {StepsSchema} from './renderers/Steps';
|
import {StepsSchema} from './renderers/Steps';
|
||||||
|
import {TimelineSchema} from './renderers/Timeline';
|
||||||
import {ArrayControlSchema} from './renderers/Form/InputArray';
|
import {ArrayControlSchema} from './renderers/Form/InputArray';
|
||||||
import {ButtonGroupControlSchema} from './renderers/Form/ButtonGroupSelect';
|
import {ButtonGroupControlSchema} from './renderers/Form/ButtonGroupSelect';
|
||||||
import {ChainedSelectControlSchema} from './renderers/Form/ChainedSelect';
|
import {ChainedSelectControlSchema} from './renderers/Form/ChainedSelect';
|
||||||
@ -201,6 +202,7 @@ export type SchemaType =
|
|||||||
| 'web-component'
|
| 'web-component'
|
||||||
| 'anchor-nav'
|
| 'anchor-nav'
|
||||||
| 'steps'
|
| 'steps'
|
||||||
|
| 'timeline'
|
||||||
| 'control'
|
| 'control'
|
||||||
| 'input-array'
|
| 'input-array'
|
||||||
| 'button'
|
| 'button'
|
||||||
@ -227,6 +229,7 @@ export type SchemaType =
|
|||||||
| 'input-time-range'
|
| 'input-time-range'
|
||||||
| 'input-datetime-range'
|
| 'input-datetime-range'
|
||||||
| 'input-excel'
|
| 'input-excel'
|
||||||
|
| 'input-formula'
|
||||||
| 'diff-editor'
|
| 'diff-editor'
|
||||||
|
|
||||||
// editor 系列
|
// editor 系列
|
||||||
@ -386,6 +389,7 @@ export type SchemaObject =
|
|||||||
| AnchorNavSchema
|
| AnchorNavSchema
|
||||||
| StepsSchema
|
| StepsSchema
|
||||||
| PortletSchema
|
| PortletSchema
|
||||||
|
| TimelineSchema
|
||||||
|
|
||||||
// 表单项
|
// 表单项
|
||||||
| FormControlSchema
|
| FormControlSchema
|
||||||
|
@ -242,6 +242,7 @@ export class SchemaRenderer extends React.Component<SchemaRendererProps, any> {
|
|||||||
data: defaultData,
|
data: defaultData,
|
||||||
value: defaultValue,
|
value: defaultValue,
|
||||||
activeKey: defaultActiveKey,
|
activeKey: defaultActiveKey,
|
||||||
|
key: propKey,
|
||||||
...restSchema
|
...restSchema
|
||||||
} = schema;
|
} = schema;
|
||||||
return rest.invisible
|
return rest.invisible
|
||||||
@ -253,6 +254,7 @@ export class SchemaRenderer extends React.Component<SchemaRendererProps, any> {
|
|||||||
defaultData,
|
defaultData,
|
||||||
defaultValue,
|
defaultValue,
|
||||||
defaultActiveKey,
|
defaultActiveKey,
|
||||||
|
propKey,
|
||||||
$path: $path,
|
$path: $path,
|
||||||
$schema: schema,
|
$schema: schema,
|
||||||
ref: isSFC ? undefined : this.refFn,
|
ref: isSFC ? undefined : this.refFn,
|
||||||
@ -293,6 +295,7 @@ export class SchemaRenderer extends React.Component<SchemaRendererProps, any> {
|
|||||||
const {
|
const {
|
||||||
data: defaultData,
|
data: defaultData,
|
||||||
value: defaultValue,
|
value: defaultValue,
|
||||||
|
key: propKey,
|
||||||
activeKey: defaultActiveKey,
|
activeKey: defaultActiveKey,
|
||||||
...restSchema
|
...restSchema
|
||||||
} = schema;
|
} = schema;
|
||||||
@ -321,6 +324,7 @@ export class SchemaRenderer extends React.Component<SchemaRendererProps, any> {
|
|||||||
defaultData={restSchema.defaultData ?? defaultData}
|
defaultData={restSchema.defaultData ?? defaultData}
|
||||||
defaultValue={restSchema.defaultValue ?? defaultValue}
|
defaultValue={restSchema.defaultValue ?? defaultValue}
|
||||||
defaultActiveKey={defaultActiveKey}
|
defaultActiveKey={defaultActiveKey}
|
||||||
|
propKey={propKey}
|
||||||
$path={$path}
|
$path={$path}
|
||||||
$schema={{...schema, ...exprProps}}
|
$schema={{...schema, ...exprProps}}
|
||||||
ref={this.refFn}
|
ref={this.refFn}
|
||||||
|
@ -24,7 +24,7 @@ interface LinkItemProps {
|
|||||||
|
|
||||||
export interface Navigation {
|
export interface Navigation {
|
||||||
label: string;
|
label: string;
|
||||||
children: Array<LinkItem>;
|
children?: Array<LinkItem>;
|
||||||
prefix?: JSX.Element;
|
prefix?: JSX.Element;
|
||||||
affix?: JSX.Element;
|
affix?: JSX.Element;
|
||||||
className?: string;
|
className?: string;
|
||||||
|
@ -106,7 +106,8 @@ export class AssociatedSelection extends BaseSelection<
|
|||||||
leftMode,
|
leftMode,
|
||||||
cellRender,
|
cellRender,
|
||||||
multiple,
|
multiple,
|
||||||
onDeferLoad
|
onDeferLoad,
|
||||||
|
itemRender
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const selectdOption = BaseSelection.resolveSelected(
|
const selectdOption = BaseSelection.resolveSelected(
|
||||||
@ -186,6 +187,7 @@ export class AssociatedSelection extends BaseSelection<
|
|||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
option2value={option2value}
|
option2value={option2value}
|
||||||
multiple={multiple}
|
multiple={multiple}
|
||||||
|
itemRender={itemRender}
|
||||||
/>
|
/>
|
||||||
) : rightMode === 'chained' ? (
|
) : rightMode === 'chained' ? (
|
||||||
<ChainedSelection
|
<ChainedSelection
|
||||||
@ -195,6 +197,7 @@ export class AssociatedSelection extends BaseSelection<
|
|||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
option2value={option2value}
|
option2value={option2value}
|
||||||
multiple={multiple}
|
multiple={multiple}
|
||||||
|
itemRender={itemRender}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<GroupedSelection
|
<GroupedSelection
|
||||||
@ -204,6 +207,7 @@ export class AssociatedSelection extends BaseSelection<
|
|||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
option2value={option2value}
|
option2value={option2value}
|
||||||
multiple={multiple}
|
multiple={multiple}
|
||||||
|
itemRender={itemRender}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
) : (
|
) : (
|
||||||
|
253
src/components/Avatar.tsx
Normal file
253
src/components/Avatar.tsx
Normal file
@ -0,0 +1,253 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import {ClassNamesFn, themeable, ThemeProps} from '../theme';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Avatar 属性
|
||||||
|
*/
|
||||||
|
interface AvatarCmptProps extends ThemeProps {
|
||||||
|
style?: {
|
||||||
|
[prop: string]: any
|
||||||
|
};
|
||||||
|
className?: string;
|
||||||
|
classnames: ClassNamesFn;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 图片地址
|
||||||
|
*/
|
||||||
|
src?: string | React.ReactNode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 图标
|
||||||
|
*/
|
||||||
|
icon?: string | React.ReactNode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 图片相对于容器的缩放方式
|
||||||
|
*/
|
||||||
|
fit?: 'fill' | 'contain' | 'cover' | 'none' | 'scale-down';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 形状
|
||||||
|
*/
|
||||||
|
shape?: 'circle' | 'square' | 'rounded';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 大小
|
||||||
|
*/
|
||||||
|
size?: number | 'small' | 'default' | 'large';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文本
|
||||||
|
*/
|
||||||
|
text?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 字符类型距离左右两侧边界单位像素
|
||||||
|
*/
|
||||||
|
gap?: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 图片无法显示时的替换文字地址
|
||||||
|
*/
|
||||||
|
alt?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 图片是否允许拖动
|
||||||
|
*/
|
||||||
|
draggable?: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 图片CORS属性
|
||||||
|
*/
|
||||||
|
crossOrigin?: 'anonymous' | 'use-credentials' | '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 图片加载失败的事件,返回 false 会关闭组件默认的
|
||||||
|
*/
|
||||||
|
onError?: (event: React.SyntheticEvent<HTMLImageElement, Event>) => boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
children?: JSX.Element | ((props?: any) => JSX.Element)
|
||||||
|
}
|
||||||
|
|
||||||
|
const prefix = 'Avatar--';
|
||||||
|
const childPrefix = prefix + 'text';
|
||||||
|
|
||||||
|
export interface AvatarState {
|
||||||
|
scale: number;
|
||||||
|
hasImg: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Avatar extends React.Component<AvatarCmptProps, AvatarState> {
|
||||||
|
static defaultProps: Partial<AvatarCmptProps> = {
|
||||||
|
shape: 'circle',
|
||||||
|
size: 'default',
|
||||||
|
fit: 'cover',
|
||||||
|
gap: 4
|
||||||
|
};
|
||||||
|
|
||||||
|
state: AvatarState = {
|
||||||
|
scale: 1,
|
||||||
|
hasImg: true
|
||||||
|
};
|
||||||
|
|
||||||
|
avatarChildrenRef: React.RefObject<HTMLElement>;
|
||||||
|
avatarRef: React.RefObject<HTMLElement>;
|
||||||
|
|
||||||
|
constructor(props: AvatarCmptProps) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.avatarChildrenRef = React.createRef();
|
||||||
|
this.avatarRef = React.createRef();
|
||||||
|
|
||||||
|
this.handleImageLoadError = this.handleImageLoadError.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.setScaleByGap();
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate(prevProps: AvatarCmptProps, prevState: AvatarState) {
|
||||||
|
const {src, gap, text, children} = this.props;
|
||||||
|
const {hasImg} = this.state;
|
||||||
|
if (prevProps.src !== src) {
|
||||||
|
this.setState({
|
||||||
|
hasImg: !!src
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if ((prevState.hasImg && !hasImg)
|
||||||
|
|| (prevProps.text !== text)
|
||||||
|
|| (prevProps.children !== children)
|
||||||
|
|| (prevProps.gap !== gap)) {
|
||||||
|
this.setScaleByGap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleImageLoadError(event: React.SyntheticEvent<HTMLImageElement, Event>) {
|
||||||
|
const {onError} = this.props;
|
||||||
|
this.setState({
|
||||||
|
hasImg: onError ? !onError(event) : false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
setScaleByGap() {
|
||||||
|
const {gap = 4} = this.props;
|
||||||
|
if (!this.avatarChildrenRef.current || !this.avatarRef.current) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const childrenWidth = this.avatarChildrenRef.current.offsetWidth;
|
||||||
|
const nodeWidth = this.avatarRef.current.offsetWidth;
|
||||||
|
if (childrenWidth && nodeWidth) {
|
||||||
|
if (gap * 2 < nodeWidth) {
|
||||||
|
const diff = nodeWidth - gap * 2;
|
||||||
|
this.setState({
|
||||||
|
scale: diff < childrenWidth ? diff / childrenWidth : 1
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
let {
|
||||||
|
style = {},
|
||||||
|
className,
|
||||||
|
shape,
|
||||||
|
size,
|
||||||
|
src,
|
||||||
|
icon,
|
||||||
|
alt,
|
||||||
|
draggable,
|
||||||
|
crossOrigin,
|
||||||
|
fit,
|
||||||
|
text,
|
||||||
|
children,
|
||||||
|
classnames: cx
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
const {scale, hasImg} = this.state;
|
||||||
|
|
||||||
|
const isImgRender = React.isValidElement(src);
|
||||||
|
const isIconRender = React.isValidElement(icon);
|
||||||
|
|
||||||
|
let childrenRender;
|
||||||
|
|
||||||
|
let sizeStyle = {};
|
||||||
|
let sizeClass = '';
|
||||||
|
|
||||||
|
if (typeof size === 'number') {
|
||||||
|
sizeStyle = {
|
||||||
|
height: size,
|
||||||
|
width: size,
|
||||||
|
lineHeight: size + 'px'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else if (typeof size === 'string') {
|
||||||
|
sizeClass = size === 'large'
|
||||||
|
? `${prefix}lg`
|
||||||
|
: size === 'small' ? `${prefix}sm` : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
const scaleX = `scale(${scale}) translateX(-50%)`;
|
||||||
|
const scaleStyle = {
|
||||||
|
msTransform: scaleX,
|
||||||
|
WebkitTransform: scaleX,
|
||||||
|
transform: scaleX
|
||||||
|
};
|
||||||
|
|
||||||
|
if (typeof src === 'string' && hasImg) {
|
||||||
|
const imgStyle = fit ? {objectFit: fit} : {};
|
||||||
|
childrenRender = (
|
||||||
|
<img
|
||||||
|
style={imgStyle}
|
||||||
|
src={src}
|
||||||
|
alt={alt}
|
||||||
|
draggable={draggable}
|
||||||
|
onError={this.handleImageLoadError}
|
||||||
|
crossOrigin={crossOrigin}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else if (isImgRender) {
|
||||||
|
childrenRender = src;
|
||||||
|
}
|
||||||
|
else if (typeof text === 'string' || typeof text === 'number') {
|
||||||
|
childrenRender = (
|
||||||
|
<span
|
||||||
|
className={cx(childPrefix)}
|
||||||
|
ref={this.avatarChildrenRef}
|
||||||
|
style={scaleStyle}>
|
||||||
|
{text}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else if (typeof icon === 'string') {
|
||||||
|
childrenRender = (<i className={icon} />);
|
||||||
|
}
|
||||||
|
else if (isIconRender) {
|
||||||
|
childrenRender = icon;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
childrenRender = (
|
||||||
|
<span
|
||||||
|
className={cx(childPrefix)}
|
||||||
|
ref={this.avatarChildrenRef}
|
||||||
|
style={scaleStyle}>
|
||||||
|
{children}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<span
|
||||||
|
className={cx(`Avatar`, className, prefix + shape, sizeClass)}
|
||||||
|
style={{...sizeStyle, ...style}}
|
||||||
|
ref={this.avatarRef}>
|
||||||
|
{childrenRender}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default themeable(Avatar);
|
@ -120,7 +120,11 @@ export class BaiduMapPicker extends React.Component<
|
|||||||
? new BMap.Point(value.lng, value.lat)
|
? new BMap.Point(value.lng, value.lat)
|
||||||
: new BMap.Point(116.404, 39.915);
|
: new BMap.Point(116.404, 39.915);
|
||||||
if (this.props.coordinatesType == 'gcj02') {
|
if (this.props.coordinatesType == 'gcj02') {
|
||||||
point = await this.covertPoint(point, COORDINATES_GCJ02, COORDINATES_BD09);
|
point = await this.covertPoint(
|
||||||
|
point,
|
||||||
|
COORDINATES_GCJ02,
|
||||||
|
COORDINATES_BD09
|
||||||
|
);
|
||||||
map.centerAndZoom(point, 15);
|
map.centerAndZoom(point, 15);
|
||||||
} else {
|
} else {
|
||||||
map.centerAndZoom(point, 15);
|
map.centerAndZoom(point, 15);
|
||||||
@ -269,27 +273,29 @@ export class BaiduMapPicker extends React.Component<
|
|||||||
|
|
||||||
covertPoint(point: any, from: number, to: number) {
|
covertPoint(point: any, from: number, to: number) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
this.convertor.translate([point], from, to, (res:any)=> {
|
this.convertor.translate([point], from, to, (res: any) => {
|
||||||
if (res.status === 0 && res.points.length) {
|
if (res.status === 0 && res.points.length) {
|
||||||
resolve(new BMap.Point(res.points[0].lng, res.points[0].lat));
|
resolve(new BMap.Point(res.points[0].lng, res.points[0].lat));
|
||||||
} else {
|
} else {
|
||||||
reject();
|
reject();
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
triggerOnChange(loc: LocationItem) {
|
triggerOnChange(loc: LocationItem) {
|
||||||
const point = new BMap.Point(loc.lng, loc.lat);
|
const point = new BMap.Point(loc.lng, loc.lat);
|
||||||
if (this.props.coordinatesType == 'gcj02') {
|
if (this.props.coordinatesType == 'gcj02') {
|
||||||
this.covertPoint(point, COORDINATES_BD09, COORDINATES_GCJ02).then((convertedPoint:any)=>{
|
this.covertPoint(point, COORDINATES_BD09, COORDINATES_GCJ02).then(
|
||||||
this.props?.onChange({
|
(convertedPoint: any) => {
|
||||||
address: loc.address.trim() || loc.title,
|
this.props?.onChange({
|
||||||
lat: convertedPoint.lat,
|
address: loc.address.trim() || loc.title,
|
||||||
lng: convertedPoint.lng,
|
lat: convertedPoint.lat,
|
||||||
city: loc.city
|
lng: convertedPoint.lng,
|
||||||
});
|
city: loc.city
|
||||||
})
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
this.props?.onChange({
|
this.props?.onChange({
|
||||||
address: loc.address.trim() || loc.title,
|
address: loc.address.trim() || loc.title,
|
||||||
@ -298,7 +304,6 @@ export class BaiduMapPicker extends React.Component<
|
|||||||
city: loc.city
|
city: loc.city
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@autobind
|
||||||
|
563
src/components/CalendarMobile.tsx
Normal file
563
src/components/CalendarMobile.tsx
Normal file
@ -0,0 +1,563 @@
|
|||||||
|
/**
|
||||||
|
* @file CalendarMobile
|
||||||
|
* @description 移动端日历组件
|
||||||
|
* @author hongyang03
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import moment from 'moment';
|
||||||
|
import Calendar from './calendar/Calendar';
|
||||||
|
import {themeable, ThemeProps} from '../theme';
|
||||||
|
import {LocaleProps, localeable} from '../locale';
|
||||||
|
import {autobind} from '../utils/helper';
|
||||||
|
|
||||||
|
export interface CalendarMobileProps extends ThemeProps, LocaleProps {
|
||||||
|
className?: string;
|
||||||
|
timeFormat?: string;
|
||||||
|
inputFormat?: string;
|
||||||
|
startDate?: moment.Moment;
|
||||||
|
endDate?: moment.Moment;
|
||||||
|
minDate?: moment.Moment;
|
||||||
|
maxDate?: moment.Moment;
|
||||||
|
minDuration?: moment.Duration;
|
||||||
|
maxDuration?: moment.Duration;
|
||||||
|
dateFormat?: string;
|
||||||
|
embed?: boolean;
|
||||||
|
viewMode?: 'days' | 'months' | 'years' | 'time' | 'quarters';
|
||||||
|
close?: () => void;
|
||||||
|
confirm?: () => void;
|
||||||
|
onChange?: (data: any, callback?: () => void) => void;
|
||||||
|
footerExtra?: JSX.Element | null;
|
||||||
|
showViewMode?: 'years' | 'months';
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CalendarMobileState {
|
||||||
|
startDate?: moment.Moment;
|
||||||
|
endDate?: moment.Moment;
|
||||||
|
monthHeights?: number[];
|
||||||
|
currentDate: moment.Moment;
|
||||||
|
showToast: boolean;
|
||||||
|
isScrollToBottom: boolean;
|
||||||
|
dateTime: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class CalendarMobile extends React.Component<
|
||||||
|
CalendarMobileProps,
|
||||||
|
CalendarMobileState
|
||||||
|
> {
|
||||||
|
|
||||||
|
mobileBody: any;
|
||||||
|
mobileHeader: any;
|
||||||
|
timer: any;
|
||||||
|
|
||||||
|
static defaultProps: Pick<CalendarMobileProps, 'showViewMode' | 'minDate' | 'maxDate'> = {
|
||||||
|
showViewMode: 'months',
|
||||||
|
minDate: moment().subtract(1, 'year').startOf('months'),
|
||||||
|
maxDate: moment().add(1, 'year').endOf('months'),
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor(props: CalendarMobileProps) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.mobileBody = React.createRef();
|
||||||
|
this.mobileHeader = React.createRef();
|
||||||
|
|
||||||
|
const {startDate, endDate, viewMode} = this.props;
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
startDate,
|
||||||
|
endDate,
|
||||||
|
showToast: false,
|
||||||
|
currentDate: moment(),
|
||||||
|
isScrollToBottom: false,
|
||||||
|
dateTime: endDate ? [endDate.hour(), endDate.minute()] : [0, 0]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.initMonths();
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate(prevProps: CalendarMobileProps) {
|
||||||
|
const props = this.props;
|
||||||
|
|
||||||
|
if (prevProps.minDate !== props.minDate || prevProps.maxDate !== props.maxDate) {
|
||||||
|
this.initMonths();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
this.setState({showToast: false});
|
||||||
|
clearTimeout(this.timer);
|
||||||
|
}
|
||||||
|
|
||||||
|
initMonths() {
|
||||||
|
if (this.mobileBody.current) {
|
||||||
|
const header = this.mobileHeader.current;
|
||||||
|
let monthHeights: number[] = [];
|
||||||
|
const monthCollection = this.mobileBody.current.children;
|
||||||
|
for (let i = 0; i < monthCollection.length; i++) {
|
||||||
|
monthHeights[i] = monthCollection[i].offsetTop - header.clientHeight;
|
||||||
|
}
|
||||||
|
this.setState({
|
||||||
|
monthHeights
|
||||||
|
});
|
||||||
|
this.scollToDate(moment());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
scollToDate(date: moment.Moment) {
|
||||||
|
const {minDate, showViewMode} = this.props;
|
||||||
|
const index = date.diff(minDate, showViewMode);
|
||||||
|
const currentEl = this.mobileBody.current.children[index];
|
||||||
|
const header = this.mobileHeader.current;
|
||||||
|
this.mobileBody.current.scrollBy(0, currentEl.offsetTop - this.mobileBody.current.scrollTop - header.clientHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
@autobind
|
||||||
|
onMobileBodyScroll(e: any) {
|
||||||
|
const {showViewMode} = this.props;
|
||||||
|
const {monthHeights} = this.state;
|
||||||
|
let minDate = this.props.minDate?.clone();
|
||||||
|
if (!this.mobileBody?.current || !monthHeights || !minDate) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const scrollTop = this.mobileBody.current.scrollTop;
|
||||||
|
const clientHeight = this.mobileBody.current.clientHeight;
|
||||||
|
const scrollHeight = this.mobileBody.current.scrollHeight;
|
||||||
|
|
||||||
|
let i = 0;
|
||||||
|
for(i; i < monthHeights.length; i++) {
|
||||||
|
if (scrollTop < monthHeights[i]) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i--;
|
||||||
|
i < 0 && (i = 0);
|
||||||
|
const currentDate = minDate.add(i, showViewMode);
|
||||||
|
this.setState({
|
||||||
|
currentDate,
|
||||||
|
isScrollToBottom: scrollTop + clientHeight === scrollHeight
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@autobind
|
||||||
|
scrollPreYear() {
|
||||||
|
if (!this.state.currentDate) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const {minDate} = this.props;
|
||||||
|
let {currentDate} = this.state;
|
||||||
|
currentDate = currentDate.clone().subtract(1, 'years');
|
||||||
|
if (minDate && currentDate.isBefore(minDate)) {
|
||||||
|
currentDate = minDate;
|
||||||
|
}
|
||||||
|
this.setState({
|
||||||
|
currentDate
|
||||||
|
});
|
||||||
|
this.scollToDate(currentDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
@autobind
|
||||||
|
scrollAfterYear() {
|
||||||
|
if (!this.state.currentDate) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const {maxDate} = this.props;
|
||||||
|
let {currentDate} = this.state;
|
||||||
|
currentDate = currentDate.clone().add(1, 'years');
|
||||||
|
if (maxDate && currentDate.isAfter(maxDate)) {
|
||||||
|
currentDate = maxDate;
|
||||||
|
}
|
||||||
|
this.setState({
|
||||||
|
currentDate
|
||||||
|
});
|
||||||
|
this.scollToDate(currentDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
getDaysOfWeek() {
|
||||||
|
const locale = moment().localeData();
|
||||||
|
const days = locale.weekdaysMin();
|
||||||
|
const first = locale.firstDayOfWeek();
|
||||||
|
const dow: string[] = [];
|
||||||
|
let i = 0;
|
||||||
|
|
||||||
|
days.forEach((day: string) => {
|
||||||
|
dow[ (7 + ( i++ ) - first) % 7 ] = day;
|
||||||
|
});
|
||||||
|
|
||||||
|
return dow;
|
||||||
|
}
|
||||||
|
|
||||||
|
@autobind
|
||||||
|
handleCalendarClick(isDisabled: boolean) {
|
||||||
|
if (isDisabled) {
|
||||||
|
this.setState({showToast: true});
|
||||||
|
this.timer = setTimeout(() => {
|
||||||
|
this.setState({showToast: false});
|
||||||
|
}, 2000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getRenderProps(props: any, currentDate: moment.Moment) {
|
||||||
|
let {startDate, endDate} = this.state;
|
||||||
|
const {translate: __, viewMode} = this.props;
|
||||||
|
const precision = viewMode === 'time' ? 'hours' : viewMode || 'day';
|
||||||
|
let footerText = '';
|
||||||
|
|
||||||
|
if (startDate &&
|
||||||
|
endDate &&
|
||||||
|
currentDate.isBetween(startDate, endDate, precision, '()')) {
|
||||||
|
props.className += ' rdtBetween';
|
||||||
|
}
|
||||||
|
else if (startDate
|
||||||
|
&& endDate
|
||||||
|
&& startDate.isSame(endDate, precision)
|
||||||
|
&& currentDate.isSame(startDate, precision)) {
|
||||||
|
props.className += ' rdtRangeStart';
|
||||||
|
footerText = __('Calendar.beginAndEnd');
|
||||||
|
}
|
||||||
|
else if (startDate && currentDate.isSame(startDate, precision)) {
|
||||||
|
props.className += ' rdtRangeStart';
|
||||||
|
footerText = __('Calendar.begin');
|
||||||
|
if (endDate) {
|
||||||
|
props.className += ' rdtRangeHasEnd';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (endDate && currentDate.isSame(endDate, precision)) {
|
||||||
|
props.className += ' rdtRangeEnd';
|
||||||
|
footerText = __('Calendar.end');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (precision === 'day' && currentDate.date() === 1 && currentDate.day() === 1) {
|
||||||
|
props.className += ' rdtOldNone';
|
||||||
|
}
|
||||||
|
|
||||||
|
const rdtDisabled = props.className.indexOf('rdtDisabled') > -1;
|
||||||
|
|
||||||
|
return {
|
||||||
|
props,
|
||||||
|
footerText,
|
||||||
|
rdtDisabled
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@autobind
|
||||||
|
handleTimeChange(newTime: any) {
|
||||||
|
if (!newTime) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const {onChange} = this.props;
|
||||||
|
let {startDate, endDate} = this.state;
|
||||||
|
if (startDate) {
|
||||||
|
let obj = {
|
||||||
|
dateTime: newTime,
|
||||||
|
startDate: endDate ? startDate : startDate?.clone().set({hour: newTime[0], minute: newTime[1], second: 0}),
|
||||||
|
endDate: !endDate ? endDate : endDate?.clone().set({hour: newTime[0], minute: newTime[1], second: 0})
|
||||||
|
};
|
||||||
|
this.setState(obj, () => {
|
||||||
|
onChange && onChange(this.state);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@autobind
|
||||||
|
checkIsValidDate(currentDate: moment.Moment) {
|
||||||
|
const {minDate, maxDate} = this.props;
|
||||||
|
let {startDate, endDate} = this.state;
|
||||||
|
let {minDuration, maxDuration, viewMode} = this.props;
|
||||||
|
const precision = viewMode === 'time' ? 'hours' : viewMode || 'day';
|
||||||
|
|
||||||
|
if (minDate && currentDate.isBefore(minDate, precision)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (maxDate && currentDate.isAfter(maxDate, precision)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (startDate && !endDate) {
|
||||||
|
if (minDuration
|
||||||
|
&& currentDate.isBefore(startDate.clone().add(minDuration))
|
||||||
|
&& currentDate.isSameOrAfter(startDate)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (maxDuration && currentDate.isAfter(startDate.clone().add(maxDuration))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@autobind
|
||||||
|
renderMobileDay(props: any, currentDate: moment.Moment) {
|
||||||
|
const cx = this.props.classnames;
|
||||||
|
const renderProps = this.getRenderProps(props, currentDate);
|
||||||
|
|
||||||
|
return <td {...renderProps.props}>
|
||||||
|
<div className="calendar-wrap" onClick={() => this.handleCalendarClick(renderProps.rdtDisabled)}>
|
||||||
|
{currentDate.date()}
|
||||||
|
<div className={cx('CalendarMobile-range-text')}>{renderProps.footerText}</div>
|
||||||
|
</div>
|
||||||
|
</td>;
|
||||||
|
}
|
||||||
|
|
||||||
|
@autobind
|
||||||
|
renderMonth(props: any, month: number, year: number) {
|
||||||
|
const cx = this.props.classnames;
|
||||||
|
const currentDate = moment().year(year).month(month);
|
||||||
|
const monthStr = currentDate
|
||||||
|
.localeData()
|
||||||
|
.monthsShort(currentDate.month(month));
|
||||||
|
const strLength = 3;
|
||||||
|
const monthStrFixedLength = monthStr.substring(0, strLength);
|
||||||
|
const renderProps = this.getRenderProps(props, currentDate);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<td {...renderProps.props}>
|
||||||
|
<div className="calendar-wrap" onClick={() => this.handleCalendarClick(renderProps.rdtDisabled)}>
|
||||||
|
{monthStrFixedLength}
|
||||||
|
<div className={cx('CalendarMobile-range-text')}>{renderProps.footerText}</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@autobind
|
||||||
|
renderQuarter(props: any, quarter: number, year: number) {
|
||||||
|
const cx = this.props.classnames;
|
||||||
|
const currentDate = moment().year(year).quarter(quarter);
|
||||||
|
const renderProps = this.getRenderProps(props, currentDate);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<td {...props}>
|
||||||
|
<div className="calendar-wrap" onClick={() => this.handleCalendarClick(renderProps.rdtDisabled)}>
|
||||||
|
Q{quarter}
|
||||||
|
<div className={cx('CalendarMobile-range-text')}>{renderProps.footerText}</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@autobind
|
||||||
|
handleMobileChange(newValue: moment.Moment) {
|
||||||
|
const {embed, minDuration, maxDuration, confirm, onChange, viewMode, minDate, maxDate} = this.props;
|
||||||
|
const {startDate, endDate, dateTime} = this.state;
|
||||||
|
const precision = viewMode === 'time' ? 'hours' : viewMode || 'day';
|
||||||
|
|
||||||
|
if (minDate && newValue && newValue.isBefore(minDate, 'second')) {
|
||||||
|
newValue = minDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (maxDate && newValue && newValue.isAfter(maxDate, 'second')) {
|
||||||
|
newValue = maxDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
startDate &&
|
||||||
|
!endDate &&
|
||||||
|
newValue.isSameOrAfter(startDate) &&
|
||||||
|
(!minDuration || newValue.isSameOrAfter(startDate.clone().add(minDuration))) &&
|
||||||
|
(!maxDuration || newValue.isSameOrBefore(startDate.clone().add(maxDuration)))
|
||||||
|
) {
|
||||||
|
return this.setState(
|
||||||
|
{
|
||||||
|
endDate: newValue.clone().endOf(precision).set({hour: dateTime[0], minute: dateTime[1], second: 0})
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
onChange && onChange(this.state, () => embed && confirm && confirm());
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState(
|
||||||
|
{
|
||||||
|
startDate: newValue.clone().startOf(precision).set({hour: dateTime[0], minute: dateTime[1], second: 0}),
|
||||||
|
endDate: undefined
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
onChange && onChange(this.state);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@autobind
|
||||||
|
renderMobileCalendarBody() {
|
||||||
|
const {
|
||||||
|
classnames: cx,
|
||||||
|
dateFormat,
|
||||||
|
timeFormat,
|
||||||
|
inputFormat,
|
||||||
|
locale,
|
||||||
|
viewMode = 'days',
|
||||||
|
close
|
||||||
|
} = this.props;
|
||||||
|
const __ = this.props.translate;
|
||||||
|
|
||||||
|
const {minDate, maxDate, showViewMode} = this.props;
|
||||||
|
if (!minDate || !maxDate) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let calendarDates: moment.Moment[] = [];
|
||||||
|
for(let minDateClone = minDate.clone(); minDateClone.isSameOrBefore(maxDate); minDateClone.add(1, showViewMode)) {
|
||||||
|
calendarDates.push(minDateClone.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={cx('CalendarMobile-body')} ref={this.mobileBody}
|
||||||
|
onScroll={this.onMobileBodyScroll}>
|
||||||
|
{calendarDates.map((calendarDate: moment.Moment, index: number) => {
|
||||||
|
const rdtOldNone = showViewMode === 'months'
|
||||||
|
&& calendarDate.clone().startOf('month').day() === 1
|
||||||
|
? 'rdtOldNone' : '';
|
||||||
|
|
||||||
|
return <div className={cx('CalendarMobile-calendar-wrap', rdtOldNone)} key={'calendar-wrap' + index}>
|
||||||
|
{showViewMode === 'months' && <div className={cx('CalendarMobile-calendar-mark')} key={'calendar-mark' + index}>
|
||||||
|
{calendarDate.month() + 1}
|
||||||
|
</div>}
|
||||||
|
<div className={cx('CalendarMobile-calendar-header')}>
|
||||||
|
<span className="rdtSwitch">
|
||||||
|
{calendarDate.format(__('dateformat.year'))}
|
||||||
|
</span>
|
||||||
|
{showViewMode === 'months' && <span className="rdtSwitch">
|
||||||
|
{calendarDate.format(__('MMM'))}
|
||||||
|
</span>}
|
||||||
|
</div>
|
||||||
|
<Calendar
|
||||||
|
className={cx('CalendarMobile-calendar', rdtOldNone)}
|
||||||
|
viewDate={calendarDate}
|
||||||
|
value={calendarDate}
|
||||||
|
onChange={this.handleMobileChange}
|
||||||
|
requiredConfirm={false}
|
||||||
|
dateFormat={dateFormat}
|
||||||
|
inputFormat={inputFormat}
|
||||||
|
timeFormat=''
|
||||||
|
isValidDate={this.checkIsValidDate}
|
||||||
|
viewMode={viewMode}
|
||||||
|
input={false}
|
||||||
|
onClose={close}
|
||||||
|
renderDay={this.renderMobileDay}
|
||||||
|
renderMonth={this.renderMonth}
|
||||||
|
renderQuarter={this.renderQuarter}
|
||||||
|
locale={locale}
|
||||||
|
hideHeader={true}
|
||||||
|
updateOn={viewMode}
|
||||||
|
key={'calendar' + index}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@autobind
|
||||||
|
renderMobileTimePicker() {
|
||||||
|
const {
|
||||||
|
classnames: cx,
|
||||||
|
timeFormat,
|
||||||
|
locale,
|
||||||
|
close
|
||||||
|
} = this.props;
|
||||||
|
const __ = this.props.translate;
|
||||||
|
|
||||||
|
const {startDate, endDate, dateTime} = this.state;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={cx('CalendarMobile-time')}>
|
||||||
|
<div className={cx('CalendarMobile-time-title')}>
|
||||||
|
{startDate && endDate ? __('Calendar.endPick') : __('Calendar.startPick')}
|
||||||
|
</div>
|
||||||
|
<Calendar
|
||||||
|
className={cx('CalendarMobile-time-calendar')}
|
||||||
|
onChange={this.handleTimeChange}
|
||||||
|
requiredConfirm={false}
|
||||||
|
timeFormat={timeFormat}
|
||||||
|
viewMode="time"
|
||||||
|
input={false}
|
||||||
|
onClose={close}
|
||||||
|
locale={locale}
|
||||||
|
useMobileUI={true}
|
||||||
|
showToolbar={false}
|
||||||
|
viewDate={moment().set({hour: dateTime[0], minute: dateTime[1], second: 0})}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const {
|
||||||
|
className,
|
||||||
|
classnames: cx,
|
||||||
|
embed,
|
||||||
|
confirm,
|
||||||
|
footerExtra,
|
||||||
|
timeFormat,
|
||||||
|
minDate,
|
||||||
|
maxDate,
|
||||||
|
showViewMode
|
||||||
|
} = this.props;
|
||||||
|
const __ = this.props.translate;
|
||||||
|
|
||||||
|
const {startDate, endDate, currentDate, showToast, isScrollToBottom} = this.state;
|
||||||
|
let dateNow = currentDate
|
||||||
|
? currentDate.format(__(`Calendar.${showViewMode === 'months' ? 'yearmonth' : 'year'}`))
|
||||||
|
: moment().format(__(`Calendar.${showViewMode === 'months' ? 'yearmonth' : 'year'}`));
|
||||||
|
|
||||||
|
const header = (
|
||||||
|
<div className={cx('CalendarMobile-header')} ref={this.mobileHeader}>
|
||||||
|
<div className={cx('CalendarMobile-subtitle')}>
|
||||||
|
<span className="subtitle-text">
|
||||||
|
{currentDate && currentDate.isSameOrBefore(minDate, showViewMode)
|
||||||
|
? null
|
||||||
|
: <a className="rdtPrev" onClick={this.scrollPreYear}>‹</a>}
|
||||||
|
{dateNow}
|
||||||
|
{currentDate && currentDate.isSameOrAfter(maxDate, showViewMode) || isScrollToBottom
|
||||||
|
? null
|
||||||
|
: <a className="rdtNext" onClick={this.scrollAfterYear}>›</a>}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{showViewMode === 'months' ? <div className={cx('CalendarMobile-weekdays')}>
|
||||||
|
{this.getDaysOfWeek().map((day: string, index: number) => (
|
||||||
|
<span key={day + index} className="weekday">
|
||||||
|
{day}
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
|
</div> : null}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
const footer = (
|
||||||
|
<div className={cx('CalendarMobile-footer')}>
|
||||||
|
{timeFormat && startDate && this.renderMobileTimePicker()}
|
||||||
|
<div className={cx('CalendarMobile-footer-toolbar')}>
|
||||||
|
<div className={cx('CalendarMobile-footer-ranges')}>
|
||||||
|
{footerExtra}
|
||||||
|
</div>
|
||||||
|
{confirm && !embed && <a
|
||||||
|
className={cx('Button', 'Button--primary', 'date-range-confirm', {
|
||||||
|
'is-disabled': !startDate || !endDate
|
||||||
|
})}
|
||||||
|
onClick={confirm}
|
||||||
|
>
|
||||||
|
{__('confirm')}
|
||||||
|
</a>}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={cx('CalendarMobile',
|
||||||
|
embed ? 'CalendarMobile-embed' : '',
|
||||||
|
className)}>
|
||||||
|
<div className={cx('CalendarMobile-wrap')}>
|
||||||
|
{header}
|
||||||
|
{this.renderMobileCalendarBody()}
|
||||||
|
{footer}
|
||||||
|
</div>
|
||||||
|
{showToast? <div className={cx('CalendarMobile-toast')}>{__('Calendar.toast')}</div> : null}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default themeable(localeable(CalendarMobile));
|
@ -176,7 +176,7 @@ export class Card extends React.Component<CardProps> {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div>
|
<>
|
||||||
{heading}
|
{heading}
|
||||||
{body ? (
|
{body ? (
|
||||||
<div className={cx('Card-body', bodyClassName)}>{body}</div>
|
<div className={cx('Card-body', bodyClassName)}>{body}</div>
|
||||||
@ -193,7 +193,7 @@ export class Card extends React.Component<CardProps> {
|
|||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
565
src/components/Cascader.tsx
Normal file
565
src/components/Cascader.tsx
Normal file
@ -0,0 +1,565 @@
|
|||||||
|
/**
|
||||||
|
* @file Cascader
|
||||||
|
* @author fex
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import {autobind, getTreeAncestors} from '../utils/helper';
|
||||||
|
import {themeable} from '../theme';
|
||||||
|
import {NestedSelectProps} from '../renderers/Form/NestedSelect';
|
||||||
|
import {Option, Options} from './Select';
|
||||||
|
import intersectionBy from 'lodash/intersectionBy';
|
||||||
|
import compact from 'lodash/compact';
|
||||||
|
import find from 'lodash/find';
|
||||||
|
import uniqBy from 'lodash/uniqBy';
|
||||||
|
import Button from './Button';
|
||||||
|
import {flattenTree, findTree, getTreeDepth} from '../utils/helper';
|
||||||
|
|
||||||
|
export type CascaderOption = {
|
||||||
|
text?: string;
|
||||||
|
value?: string | number;
|
||||||
|
color?: string;
|
||||||
|
disabled?: boolean;
|
||||||
|
children?: Options;
|
||||||
|
className?: string;
|
||||||
|
[key: string]: any;
|
||||||
|
};
|
||||||
|
export interface CascaderProps extends NestedSelectProps {
|
||||||
|
value?: (number | string)[];
|
||||||
|
activeColor?: string;
|
||||||
|
optionRender?: ({
|
||||||
|
option,
|
||||||
|
selected
|
||||||
|
}: {
|
||||||
|
option: CascaderOption;
|
||||||
|
selected: boolean;
|
||||||
|
}) => React.ReactNode;
|
||||||
|
onClose?: () => void;
|
||||||
|
onConfirm?: (param: any) => void;
|
||||||
|
multiple?: boolean;
|
||||||
|
}
|
||||||
|
export type CascaderTab = {
|
||||||
|
options: Options;
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface CascaderState {
|
||||||
|
selectedOptions: Options;
|
||||||
|
activeTab: number;
|
||||||
|
tabs: Array<{
|
||||||
|
options: Options;
|
||||||
|
}>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Cascader extends React.Component<CascaderProps, CascaderState> {
|
||||||
|
static defaultProps = {
|
||||||
|
labelField: 'label',
|
||||||
|
valueField: 'value'
|
||||||
|
};
|
||||||
|
tabsRef: React.RefObject<HTMLDivElement> = React.createRef();
|
||||||
|
tabRef: React.RefObject<HTMLDivElement> = React.createRef();
|
||||||
|
constructor(props: CascaderProps) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
selectedOptions: this.props.selectedOptions || [],
|
||||||
|
activeTab: 0,
|
||||||
|
tabs: [
|
||||||
|
{
|
||||||
|
options: this.props.options.slice() || []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
componentDidMount() {
|
||||||
|
const {multiple, options, valueField = 'value', cascade} = this.props;
|
||||||
|
let selectedOptions = this.props.selectedOptions.slice();
|
||||||
|
let parentsCount = 0;
|
||||||
|
let parentTree: Options = [];
|
||||||
|
selectedOptions.forEach((item: Option) => {
|
||||||
|
const parents = getTreeAncestors(options, item as any);
|
||||||
|
// 获取最长路径
|
||||||
|
if (parents && parents?.length > parentsCount) {
|
||||||
|
parentTree = parents;
|
||||||
|
parentsCount = parentTree.length;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const selectedValues = selectedOptions.map(
|
||||||
|
(option: Option) => option[valueField]
|
||||||
|
);
|
||||||
|
const tabs = parentTree.map((option: Option) => {
|
||||||
|
if (multiple && !cascade) {
|
||||||
|
if (
|
||||||
|
selectedValues.includes(option[valueField]) &&
|
||||||
|
option?.children?.length
|
||||||
|
) {
|
||||||
|
option.children.forEach((option: Option) => (option.disabled = true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return multiple
|
||||||
|
? {
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
...option,
|
||||||
|
isCheckAll: true
|
||||||
|
},
|
||||||
|
...(option.children ? option.children : [])
|
||||||
|
]
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
options: option.children ? option.children : []
|
||||||
|
};
|
||||||
|
});
|
||||||
|
this.setState({
|
||||||
|
selectedOptions,
|
||||||
|
tabs: [...this.state.tabs, ...tabs]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@autobind
|
||||||
|
handleTabSelect(index: number) {
|
||||||
|
const tabs = this.state.tabs.slice(0, index + 1);
|
||||||
|
this.setState({
|
||||||
|
activeTab: index,
|
||||||
|
tabs
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@autobind
|
||||||
|
getOptionParent(option: Option) {
|
||||||
|
const {options, valueField = 'value'} = this.props;
|
||||||
|
let ancestors: any[] = [];
|
||||||
|
findTree(options, (item, index, level, paths) => {
|
||||||
|
if (item[valueField] === option[valueField]) {
|
||||||
|
ancestors = paths;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
return ancestors.length ? ancestors[ancestors.length - 1] : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@autobind
|
||||||
|
dealParentSelect(option: Option, selectedOptions: Options): Options {
|
||||||
|
const {valueField = 'value'} = this.props;
|
||||||
|
const parentOption = this.getOptionParent(option);
|
||||||
|
if (parentOption) {
|
||||||
|
const parentChildren = parentOption?.children;
|
||||||
|
const equalOption = intersectionBy(
|
||||||
|
selectedOptions,
|
||||||
|
parentChildren,
|
||||||
|
valueField
|
||||||
|
);
|
||||||
|
// 包含则选中父节点
|
||||||
|
const isParentSelected = find(selectedOptions, {
|
||||||
|
[valueField]: parentOption[valueField]
|
||||||
|
});
|
||||||
|
if (equalOption.length === parentChildren?.length && !isParentSelected) {
|
||||||
|
selectedOptions.push(parentOption);
|
||||||
|
}
|
||||||
|
if (equalOption.length !== parentChildren?.length && isParentSelected) {
|
||||||
|
const index = selectedOptions.findIndex(
|
||||||
|
(item: Option) => item[valueField] === parentOption[valueField]
|
||||||
|
);
|
||||||
|
selectedOptions.splice(index, 1);
|
||||||
|
}
|
||||||
|
return this.dealParentSelect(parentOption, selectedOptions);
|
||||||
|
} else {
|
||||||
|
return selectedOptions;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@autobind
|
||||||
|
flattenTreeWithLeafNodes(option: Option) {
|
||||||
|
return compact(
|
||||||
|
flattenTree(Array.isArray(option) ? option : [option], node => node)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@autobind
|
||||||
|
adjustOptionSelect(option: Option): boolean {
|
||||||
|
const {valueField = 'value'} = this.props;
|
||||||
|
const {selectedOptions} = this.state;
|
||||||
|
function loop(arr: any[]): boolean {
|
||||||
|
if (!arr.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return arr.some((item: any) => item[valueField] === option[valueField]);
|
||||||
|
}
|
||||||
|
return loop(selectedOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
@autobind
|
||||||
|
getSelectedChildNum(option: Option): number {
|
||||||
|
let count = 0;
|
||||||
|
const loop = (arr: any[]) => {
|
||||||
|
if (!arr || !arr.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (let item of arr) {
|
||||||
|
if (item.children) {
|
||||||
|
loop(item.children || []);
|
||||||
|
} else {
|
||||||
|
if (this.adjustOptionSelect(item)) {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
loop(option.children || []);
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
@autobind
|
||||||
|
dealOptionDisable(selectedOptions: Options) {
|
||||||
|
const {
|
||||||
|
valueField = 'value',
|
||||||
|
options,
|
||||||
|
cascade,
|
||||||
|
multiple,
|
||||||
|
onlyChildren // 子节点可点击
|
||||||
|
} = this.props;
|
||||||
|
if (!multiple || cascade || onlyChildren) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const selectedValues = selectedOptions.map(
|
||||||
|
(option: Option) => option[valueField]
|
||||||
|
);
|
||||||
|
const loop = (option: Option) => {
|
||||||
|
if (!option.children) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
option.children &&
|
||||||
|
option.children.forEach((childOption: Option) => {
|
||||||
|
if (
|
||||||
|
!selectedValues.includes(option[valueField]) &&
|
||||||
|
!option.disabled
|
||||||
|
) {
|
||||||
|
childOption.disabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selectedValues.includes(option[valueField]) || option.disabled) {
|
||||||
|
childOption.disabled = true;
|
||||||
|
}
|
||||||
|
loop(childOption);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
options.forEach((option: Option) => loop(option));
|
||||||
|
}
|
||||||
|
|
||||||
|
@autobind
|
||||||
|
dealChildrenSelect(option: Option, selectedOptions: Options) {
|
||||||
|
const {valueField = 'value'} = this.props;
|
||||||
|
let index = selectedOptions.findIndex(
|
||||||
|
(item: Option) => item[valueField] === option[valueField]
|
||||||
|
);
|
||||||
|
if (index !== -1) {
|
||||||
|
selectedOptions.splice(index, 1);
|
||||||
|
} else {
|
||||||
|
selectedOptions.push(option);
|
||||||
|
}
|
||||||
|
function loop(option: Option) {
|
||||||
|
if (!option.children) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
option.children.forEach((item: Option) => {
|
||||||
|
if (index !== -1) {
|
||||||
|
// 删除选中节点及其子节点
|
||||||
|
selectedOptions = selectedOptions.filter(
|
||||||
|
(sItem: Option) => sItem[valueField] !== item[valueField]
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// 添加节点及其子节点
|
||||||
|
selectedOptions.push(item);
|
||||||
|
}
|
||||||
|
loop(item);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
loop(option);
|
||||||
|
return selectedOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
getParentTree = (option: Option, arr: Options): Options => {
|
||||||
|
const parentOption = this.getOptionParent(option);
|
||||||
|
if (parentOption) {
|
||||||
|
arr.push(parentOption);
|
||||||
|
return this.getParentTree(parentOption, arr);
|
||||||
|
}
|
||||||
|
return arr;
|
||||||
|
};
|
||||||
|
|
||||||
|
@autobind
|
||||||
|
onSelect(option: CascaderOption, tabIndex: number) {
|
||||||
|
const {multiple, valueField = 'value', cascade} = this.props;
|
||||||
|
|
||||||
|
let tabs = this.state.tabs.slice();
|
||||||
|
let {activeTab} = this.state;
|
||||||
|
let selectedOptions = this.state.selectedOptions;
|
||||||
|
const isDisable = option.disabled;
|
||||||
|
if (!isDisable) {
|
||||||
|
if (multiple) {
|
||||||
|
// 父子级分离
|
||||||
|
if (cascade) {
|
||||||
|
if (
|
||||||
|
option.isCheckAll ||
|
||||||
|
!option.children ||
|
||||||
|
!option.children.length
|
||||||
|
) {
|
||||||
|
let index = selectedOptions.findIndex(
|
||||||
|
(item: Option) => item[valueField] === option[valueField]
|
||||||
|
);
|
||||||
|
if (index !== -1) {
|
||||||
|
selectedOptions.splice(index, 1);
|
||||||
|
} else {
|
||||||
|
selectedOptions.push(option);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (
|
||||||
|
option.isCheckAll ||
|
||||||
|
!option.children ||
|
||||||
|
!option.children.length
|
||||||
|
) {
|
||||||
|
selectedOptions = this.dealChildrenSelect(option, selectedOptions);
|
||||||
|
selectedOptions = this.dealParentSelect(option, selectedOptions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 单选
|
||||||
|
selectedOptions = this.getParentTree(option, [option]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.dealOptionDisable(selectedOptions);
|
||||||
|
|
||||||
|
if (tabs.length > tabIndex + 1) {
|
||||||
|
tabs = tabs.slice(0, tabIndex + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
const tabWidth = this.tabRef.current?.offsetWidth || 1;
|
||||||
|
const parentTree = this.getParentTree(option, [option]);
|
||||||
|
const scrollLeft = (parentTree.length - 2) * tabWidth;
|
||||||
|
if (scrollLeft !== 0) {
|
||||||
|
(this.tabsRef.current as HTMLElement).scrollTo(scrollLeft, 0);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (option?.children && !option.isCheckAll) {
|
||||||
|
const nextTab = multiple
|
||||||
|
? {
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
...option,
|
||||||
|
isCheckAll: true
|
||||||
|
},
|
||||||
|
...option.children
|
||||||
|
]
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
options: option.children
|
||||||
|
};
|
||||||
|
|
||||||
|
if (tabs[tabIndex + 1]) {
|
||||||
|
tabs[tabIndex + 1] = nextTab;
|
||||||
|
} else {
|
||||||
|
tabs.push(nextTab);
|
||||||
|
}
|
||||||
|
activeTab += 1;
|
||||||
|
}
|
||||||
|
this.setState({
|
||||||
|
tabs,
|
||||||
|
activeTab,
|
||||||
|
selectedOptions
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@autobind
|
||||||
|
onNextClick(option: CascaderOption, tabIndex: number) {
|
||||||
|
let {activeTab} = this.state;
|
||||||
|
let tabs = this.state.tabs.slice();
|
||||||
|
if (option.c)
|
||||||
|
if (option?.children) {
|
||||||
|
const nextTab = {
|
||||||
|
options: option.children
|
||||||
|
};
|
||||||
|
if (tabs[tabIndex + 1]) {
|
||||||
|
tabs[tabIndex + 1] = nextTab;
|
||||||
|
} else {
|
||||||
|
tabs.push(nextTab);
|
||||||
|
}
|
||||||
|
activeTab += 1;
|
||||||
|
}
|
||||||
|
this.setState({
|
||||||
|
tabs,
|
||||||
|
activeTab
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@autobind
|
||||||
|
getSubmitOptions(selectedOptions: Options): Options {
|
||||||
|
const _selectedOptions: Options = [];
|
||||||
|
const {
|
||||||
|
multiple,
|
||||||
|
options,
|
||||||
|
valueField = 'value',
|
||||||
|
cascade,
|
||||||
|
onlyChildren,
|
||||||
|
withChildren
|
||||||
|
} = this.props;
|
||||||
|
if (cascade || onlyChildren || withChildren || !multiple) {
|
||||||
|
return selectedOptions;
|
||||||
|
}
|
||||||
|
const selectedValues = selectedOptions.map(
|
||||||
|
(option: Option) => option[valueField]
|
||||||
|
);
|
||||||
|
function loop(options: Options) {
|
||||||
|
if (!options || !options.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
options.forEach((option: Option) => {
|
||||||
|
if (selectedValues.includes(option[valueField])) {
|
||||||
|
_selectedOptions.push(option);
|
||||||
|
} else {
|
||||||
|
loop(option.children ? option.children : []);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
loop(options);
|
||||||
|
return _selectedOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
@autobind
|
||||||
|
confirm() {
|
||||||
|
const {onChange, joinValues, delimiter, extractValue, valueField, onClose} =
|
||||||
|
this.props;
|
||||||
|
let {selectedOptions} = this.state;
|
||||||
|
let _selectedOptions = this.getSubmitOptions(selectedOptions);
|
||||||
|
_selectedOptions = uniqBy(_selectedOptions, valueField);
|
||||||
|
onChange(
|
||||||
|
joinValues
|
||||||
|
? _selectedOptions
|
||||||
|
.map(item => item[valueField as string])
|
||||||
|
.join(delimiter)
|
||||||
|
: extractValue
|
||||||
|
? _selectedOptions.map(item => item[valueField as string])
|
||||||
|
: _selectedOptions
|
||||||
|
);
|
||||||
|
onClose && onClose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@autobind
|
||||||
|
renderOption(option: CascaderOption, tabIndex: number) {
|
||||||
|
const {
|
||||||
|
activeColor,
|
||||||
|
optionRender,
|
||||||
|
labelField,
|
||||||
|
valueField = 'value',
|
||||||
|
classnames: cx,
|
||||||
|
cascade,
|
||||||
|
multiple
|
||||||
|
} = this.props;
|
||||||
|
const {selectedOptions} = this.state;
|
||||||
|
const selectedValueArr = selectedOptions.map(item => item[valueField]);
|
||||||
|
|
||||||
|
let selfChecked = selectedValueArr.includes(option[valueField]);
|
||||||
|
const color = option.color || (selfChecked ? activeColor : undefined);
|
||||||
|
const Text = optionRender ? (
|
||||||
|
optionRender({option, selected: selfChecked})
|
||||||
|
) : (
|
||||||
|
<span>{option[labelField]}</span>
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
<li
|
||||||
|
className={cx(
|
||||||
|
'Cascader-option',
|
||||||
|
{
|
||||||
|
selected: selfChecked,
|
||||||
|
disabled: option.disabled
|
||||||
|
},
|
||||||
|
option.className
|
||||||
|
)}
|
||||||
|
style={{color}}
|
||||||
|
onClick={() => this.onSelect(option, tabIndex)}
|
||||||
|
key={tabIndex + '-' + option[valueField]}
|
||||||
|
>
|
||||||
|
<span className={cx('Cascader-option--text')}>{Text}</span>
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@autobind
|
||||||
|
renderOptions(options: Options, tabIndex: number) {
|
||||||
|
const {classnames: cx} = this.props;
|
||||||
|
return (
|
||||||
|
<ul key={tabIndex} className={cx('Cascader-options')}>
|
||||||
|
{options.map(option => this.renderOption(option, tabIndex))}
|
||||||
|
</ul>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@autobind
|
||||||
|
renderTabs() {
|
||||||
|
const {classnames: cx, options} = this.props;
|
||||||
|
const {tabs} = this.state;
|
||||||
|
const depth = getTreeDepth(options);
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={cx(`Cascader-tabs`, depth > 3 ? 'scrollable' : '')}
|
||||||
|
ref={this.tabsRef}
|
||||||
|
>
|
||||||
|
{tabs.map((tab: CascaderTab, tabIndex: number) => {
|
||||||
|
const {options} = tab;
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={cx(`Cascader-tab`)}
|
||||||
|
ref={this.tabRef}
|
||||||
|
key={tabIndex}
|
||||||
|
>
|
||||||
|
{this.renderOptions(options, tabIndex)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
{depth <= 3 && options.length
|
||||||
|
? Array(getTreeDepth(options) - tabs.length)
|
||||||
|
.fill(1)
|
||||||
|
.map((item: number, index: number) => (
|
||||||
|
<div className={cx(`Cascader-tab`)} key={index}></div>
|
||||||
|
))
|
||||||
|
: null}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const {
|
||||||
|
classPrefix: ns,
|
||||||
|
classnames: cx,
|
||||||
|
className,
|
||||||
|
onClose,
|
||||||
|
translate: __
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={cx(`Cascader`, className)}>
|
||||||
|
<div className={cx(`Cascader-btnGroup`)}>
|
||||||
|
<Button
|
||||||
|
className={cx(`Cascader-btnCancel`)}
|
||||||
|
level="default"
|
||||||
|
onClick={onClose}
|
||||||
|
>
|
||||||
|
{__('cancel')}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
className={cx(`Cascader-btnConfirm`)}
|
||||||
|
level="primary"
|
||||||
|
onClick={this.confirm}
|
||||||
|
>
|
||||||
|
{__('confirm')}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
{this.renderTabs()}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default themeable(Cascader);
|
@ -78,7 +78,13 @@ export class ChainedSelection extends BaseSelection<
|
|||||||
onClick={() => this.toggleOption(option)}
|
onClick={() => this.toggleOption(option)}
|
||||||
>
|
>
|
||||||
<div className={cx('ChainedSelection-itemLabel')}>
|
<div className={cx('ChainedSelection-itemLabel')}>
|
||||||
{itemRender(option)}
|
{itemRender(option, {
|
||||||
|
index: index,
|
||||||
|
multiple: multiple,
|
||||||
|
checked: !!~valueArray.indexOf(option),
|
||||||
|
onChange: () => this.toggleOption(option),
|
||||||
|
disabled: disabled || option.disabled
|
||||||
|
})}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{multiple ? (
|
{multiple ? (
|
||||||
@ -100,7 +106,8 @@ export class ChainedSelection extends BaseSelection<
|
|||||||
disabled,
|
disabled,
|
||||||
classnames: cx,
|
classnames: cx,
|
||||||
itemClassName,
|
itemClassName,
|
||||||
itemRender
|
itemRender,
|
||||||
|
multiple
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const valueArray = this.valueArray;
|
const valueArray = this.valueArray;
|
||||||
|
|
||||||
@ -118,7 +125,13 @@ export class ChainedSelection extends BaseSelection<
|
|||||||
onClick={() => this.selectOption(option, depth, id)}
|
onClick={() => this.selectOption(option, depth, id)}
|
||||||
>
|
>
|
||||||
<div className={cx('ChainedSelection-itemLabel')}>
|
<div className={cx('ChainedSelection-itemLabel')}>
|
||||||
{itemRender(option)}
|
{itemRender(option, {
|
||||||
|
index: index,
|
||||||
|
multiple: multiple,
|
||||||
|
checked: !!~this.state.selected.indexOf(id),
|
||||||
|
onChange: () => this.selectOption(option, depth, id),
|
||||||
|
disabled: disabled || option.disabled
|
||||||
|
})}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{option.defer && option.loading ? <Spinner size="sm" show /> : null}
|
{option.defer && option.loading ? <Spinner size="sm" show /> : null}
|
||||||
|
99
src/components/CodeMirror.tsx
Normal file
99
src/components/CodeMirror.tsx
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import 'codemirror/lib/codemirror.css';
|
||||||
|
import type CodeMirror from 'codemirror';
|
||||||
|
import {autobind} from '../utils/helper';
|
||||||
|
import {resizeSensor} from '../utils/resize-sensor';
|
||||||
|
|
||||||
|
export interface CodeMirrorEditorProps {
|
||||||
|
className?: string;
|
||||||
|
value?: string;
|
||||||
|
onChange?: (value: string) => void;
|
||||||
|
onFocus?: (e: any) => void;
|
||||||
|
onBlur?: (e: any) => void;
|
||||||
|
editorFactory?: (
|
||||||
|
dom: HTMLElement,
|
||||||
|
cm: typeof CodeMirror,
|
||||||
|
props?: any
|
||||||
|
) => CodeMirror.Editor;
|
||||||
|
editorDidMount?: (cm: typeof CodeMirror, editor: CodeMirror.Editor) => void;
|
||||||
|
editorWillUnMount?: (
|
||||||
|
cm: typeof CodeMirror,
|
||||||
|
editor: CodeMirror.Editor
|
||||||
|
) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class CodeMirrorEditor extends React.Component<CodeMirrorEditorProps> {
|
||||||
|
dom = React.createRef<HTMLDivElement>();
|
||||||
|
|
||||||
|
editor?: CodeMirror.Editor;
|
||||||
|
toDispose: Array<() => void> = [];
|
||||||
|
unmounted = false;
|
||||||
|
async componentDidMount() {
|
||||||
|
const cm = (await import('codemirror')).default;
|
||||||
|
// @ts-ignore
|
||||||
|
await import('codemirror/mode/javascript/javascript');
|
||||||
|
// @ts-ignore
|
||||||
|
await import('codemirror/mode/htmlmixed/htmlmixed');
|
||||||
|
await import('codemirror/addon/mode/simple');
|
||||||
|
await import('codemirror/addon/mode/multiplex');
|
||||||
|
if (this.unmounted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.editor =
|
||||||
|
this.props.editorFactory?.(this.dom.current!, cm, this.props) ??
|
||||||
|
cm(this.dom.current!, {
|
||||||
|
value: this.props.value || ''
|
||||||
|
});
|
||||||
|
|
||||||
|
this.props.editorDidMount?.(cm, this.editor);
|
||||||
|
this.editor.on('change', this.handleChange);
|
||||||
|
|
||||||
|
this.toDispose.push(
|
||||||
|
resizeSensor(this.dom.current as HTMLElement, () =>
|
||||||
|
this.editor?.refresh()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
// todo 以后优化这个,解决弹窗里面默认光标太小的问题
|
||||||
|
setTimeout(() => this.editor?.refresh(), 350);
|
||||||
|
this.toDispose.push(() => {
|
||||||
|
this.props.editorWillUnMount?.(cm, this.editor!);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate(prevProps: CodeMirrorEditorProps) {
|
||||||
|
const props = this.props;
|
||||||
|
|
||||||
|
if (props.value !== prevProps.value) {
|
||||||
|
this.editor && this.setValue(props.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
this.unmounted = true;
|
||||||
|
this.editor?.off('change', this.handleChange);
|
||||||
|
this.toDispose.forEach(fn => fn());
|
||||||
|
this.toDispose = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
@autobind
|
||||||
|
handleChange(editor: any) {
|
||||||
|
this.props.onChange?.(editor.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
setValue(value?: string) {
|
||||||
|
const doc = this.editor!.getDoc();
|
||||||
|
if (value && value !== doc.getValue()) {
|
||||||
|
const cursor = doc.getCursor();
|
||||||
|
doc.setValue(value);
|
||||||
|
doc.setCursor(cursor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const {className} = this.props;
|
||||||
|
return <div className={className} ref={this.dom}></div>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CodeMirrorEditor;
|
@ -26,13 +26,14 @@ const collapseStyles: {
|
|||||||
export interface CollapseProps {
|
export interface CollapseProps {
|
||||||
key?: string;
|
key?: string;
|
||||||
id?: string;
|
id?: string;
|
||||||
|
propKey?: string;
|
||||||
mountOnEnter?: boolean;
|
mountOnEnter?: boolean;
|
||||||
unmountOnExit?: boolean;
|
unmountOnExit?: boolean;
|
||||||
className?: string;
|
className?: string;
|
||||||
classPrefix: string;
|
classPrefix: string;
|
||||||
classnames: ClassNamesFn;
|
classnames: ClassNamesFn;
|
||||||
headerPosition?: 'top' | 'bottom';
|
headerPosition?: 'top' | 'bottom';
|
||||||
header?: React.ReactElement;
|
header?: React.ReactNode;
|
||||||
body: any;
|
body: any;
|
||||||
bodyClassName?: string;
|
bodyClassName?: string;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
@ -55,7 +56,6 @@ export interface CollapseState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class Collapse extends React.Component<CollapseProps, CollapseState> {
|
export class Collapse extends React.Component<CollapseProps, CollapseState> {
|
||||||
|
|
||||||
static defaultProps: Partial<CollapseProps> = {
|
static defaultProps: Partial<CollapseProps> = {
|
||||||
mountOnEnter: false,
|
mountOnEnter: false,
|
||||||
unmountOnExit: false,
|
unmountOnExit: false,
|
||||||
@ -79,10 +79,13 @@ export class Collapse extends React.Component<CollapseProps, CollapseState> {
|
|||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.toggleCollapsed = this.toggleCollapsed.bind(this);
|
this.toggleCollapsed = this.toggleCollapsed.bind(this);
|
||||||
this.state.collapsed = !!props.collapsed;
|
this.state.collapsed = props.collapsable ? !!props.collapsed : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static getDerivedStateFromProps(nextProps: CollapseProps, preState: CollapseState) {
|
static getDerivedStateFromProps(
|
||||||
|
nextProps: CollapseProps,
|
||||||
|
preState: CollapseState
|
||||||
|
) {
|
||||||
if (nextProps.propsUpdate && nextProps.collapsed !== preState.collapsed) {
|
if (nextProps.propsUpdate && nextProps.collapsed !== preState.collapsed) {
|
||||||
return {
|
return {
|
||||||
collapsed: !!nextProps.collapsed
|
collapsed: !!nextProps.collapsed
|
||||||
@ -164,7 +167,9 @@ export class Collapse extends React.Component<CollapseProps, CollapseState> {
|
|||||||
children
|
children
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const finalHeader = this.state.collapsed ? header : collapseHeader || header;
|
const finalHeader = this.state.collapsed
|
||||||
|
? header
|
||||||
|
: collapseHeader || header;
|
||||||
|
|
||||||
let dom = [
|
let dom = [
|
||||||
finalHeader ? (
|
finalHeader ? (
|
||||||
@ -173,14 +178,18 @@ export class Collapse extends React.Component<CollapseProps, CollapseState> {
|
|||||||
onClick={this.toggleCollapsed}
|
onClick={this.toggleCollapsed}
|
||||||
className={cx(`Collapse-header`, headingClassName)}
|
className={cx(`Collapse-header`, headingClassName)}
|
||||||
>
|
>
|
||||||
{showArrow && collapsable
|
{showArrow && collapsable ? (
|
||||||
? expandIcon
|
expandIcon ? (
|
||||||
? React.cloneElement(expandIcon, {
|
React.cloneElement(expandIcon, {
|
||||||
...expandIcon.props,
|
...expandIcon.props,
|
||||||
className: cx('Collapse-icon-tranform')
|
className: cx('Collapse-icon-tranform')
|
||||||
})
|
})
|
||||||
: <span className={cx('Collapse-arrow')} />
|
) : (
|
||||||
: ''}
|
<span className={cx('Collapse-arrow')} />
|
||||||
|
)
|
||||||
|
) : (
|
||||||
|
''
|
||||||
|
)}
|
||||||
{finalHeader}
|
{finalHeader}
|
||||||
</HeadingComponent>
|
</HeadingComponent>
|
||||||
) : null,
|
) : null,
|
||||||
@ -213,7 +222,6 @@ export class Collapse extends React.Component<CollapseProps, CollapseState> {
|
|||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
</Transition>
|
</Transition>
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|
||||||
if (headerPosition === 'bottom') {
|
if (headerPosition === 'bottom') {
|
||||||
|
@ -28,7 +28,6 @@ class CollapseGroup extends React.Component<
|
|||||||
CollapseGroupProps,
|
CollapseGroupProps,
|
||||||
CollapseGroupState
|
CollapseGroupState
|
||||||
> {
|
> {
|
||||||
|
|
||||||
static defaultProps: Partial<CollapseGroupProps> = {
|
static defaultProps: Partial<CollapseGroupProps> = {
|
||||||
className: '',
|
className: '',
|
||||||
accordion: false,
|
accordion: false,
|
||||||
@ -58,21 +57,18 @@ class CollapseGroup extends React.Component<
|
|||||||
if (collapsed) {
|
if (collapsed) {
|
||||||
if (this.props.accordion) {
|
if (this.props.accordion) {
|
||||||
activeKey = [];
|
activeKey = [];
|
||||||
}
|
} else {
|
||||||
else {
|
for (let i = 0; i < activeKey.length; i++) {
|
||||||
for(let i = 0; i < activeKey.length; i++) {
|
|
||||||
if (activeKey[i] === item.id) {
|
if (activeKey[i] === item.id) {
|
||||||
activeKey.splice(i, 1);
|
activeKey.splice(i, 1);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
if (this.props.accordion) {
|
if (this.props.accordion) {
|
||||||
activeKey = [item.id];
|
activeKey = [item.id];
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
activeKey.push(item.id);
|
activeKey.push(item.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -88,7 +84,8 @@ class CollapseGroup extends React.Component<
|
|||||||
|
|
||||||
return children.map((child: React.ReactElement, index: number) => {
|
return children.map((child: React.ReactElement, index: number) => {
|
||||||
let props = child.props;
|
let props = child.props;
|
||||||
const id = props.schema.key || String(index);
|
|
||||||
|
const id = props.propKey || String(index);
|
||||||
const collapsed = this.state.activeKey.indexOf(id) === -1;
|
const collapsed = this.state.activeKey.indexOf(id) === -1;
|
||||||
|
|
||||||
return React.cloneElement(child as any, {
|
return React.cloneElement(child as any, {
|
||||||
@ -98,7 +95,8 @@ class CollapseGroup extends React.Component<
|
|||||||
collapsed,
|
collapsed,
|
||||||
expandIcon: this.props.expandIcon,
|
expandIcon: this.props.expandIcon,
|
||||||
propsUpdate: true,
|
propsUpdate: true,
|
||||||
onCollapse: (item: CollapseProps, collapsed: boolean) => this.collapseChange(item, collapsed)
|
onCollapse: (item: CollapseProps, collapsed: boolean) =>
|
||||||
|
this.collapseChange(item, collapsed)
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@ -116,7 +114,7 @@ class CollapseGroup extends React.Component<
|
|||||||
className={cx(
|
className={cx(
|
||||||
`CollapseGroup`,
|
`CollapseGroup`,
|
||||||
{
|
{
|
||||||
'icon-position-right': expandIconPosition === 'right',
|
'icon-position-right': expandIconPosition === 'right'
|
||||||
},
|
},
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
|
@ -11,8 +11,9 @@ import {Icon} from './icons';
|
|||||||
import Overlay from './Overlay';
|
import Overlay from './Overlay';
|
||||||
import {uncontrollable} from 'uncontrollable';
|
import {uncontrollable} from 'uncontrollable';
|
||||||
import PopOver from './PopOver';
|
import PopOver from './PopOver';
|
||||||
|
import PopUp from './PopUp';
|
||||||
import {ClassNamesFn, themeable, ThemeProps} from '../theme';
|
import {ClassNamesFn, themeable, ThemeProps} from '../theme';
|
||||||
import {autobind, isObject} from '../utils/helper';
|
import {autobind, isMobile, isObject} from '../utils/helper';
|
||||||
import {localeable, LocaleProps} from '../locale';
|
import {localeable, LocaleProps} from '../locale';
|
||||||
|
|
||||||
export type PresetColor = {color: string; title: string} | string;
|
export type PresetColor = {color: string; title: string} | string;
|
||||||
@ -32,6 +33,7 @@ export interface ColorProps extends LocaleProps, ThemeProps {
|
|||||||
presetColors?: PresetColor[];
|
presetColors?: PresetColor[];
|
||||||
resetValue?: string;
|
resetValue?: string;
|
||||||
allowCustomColor?: boolean;
|
allowCustomColor?: boolean;
|
||||||
|
useMobileUI?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ColorControlState {
|
export interface ColorControlState {
|
||||||
@ -218,7 +220,8 @@ export class ColorControl extends React.PureComponent<
|
|||||||
placement,
|
placement,
|
||||||
classnames: cx,
|
classnames: cx,
|
||||||
presetColors,
|
presetColors,
|
||||||
allowCustomColor
|
allowCustomColor,
|
||||||
|
useMobileUI
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const __ = this.props.translate;
|
const __ = this.props.translate;
|
||||||
@ -270,7 +273,7 @@ export class ColorControl extends React.PureComponent<
|
|||||||
<Icon icon="caret" className="icon" onClick={this.handleClick} />
|
<Icon icon="caret" className="icon" onClick={this.handleClick} />
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
{isOpened ? (
|
{!(useMobileUI && isMobile()) && isOpened ? (
|
||||||
<Overlay
|
<Overlay
|
||||||
placement={placement || 'auto'}
|
placement={placement || 'auto'}
|
||||||
target={() => findDOMNode(this)}
|
target={() => findDOMNode(this)}
|
||||||
@ -317,6 +320,43 @@ export class ColorControl extends React.PureComponent<
|
|||||||
</PopOver>
|
</PopOver>
|
||||||
</Overlay>
|
</Overlay>
|
||||||
) : null}
|
) : null}
|
||||||
|
{useMobileUI && isMobile() && (
|
||||||
|
<PopUp
|
||||||
|
className={cx(`${ns}ColorPicker-popup`)}
|
||||||
|
isShow={isOpened}
|
||||||
|
onHide={this.handleClick}
|
||||||
|
>
|
||||||
|
{allowCustomColor ? (
|
||||||
|
<SketchPicker
|
||||||
|
styles={{}}
|
||||||
|
disableAlpha={!!~['rgb', 'hex'].indexOf(format as string)}
|
||||||
|
color={value}
|
||||||
|
presetColors={presetColors}
|
||||||
|
onChangeComplete={this.handleChange}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<GithubPicker
|
||||||
|
color={value}
|
||||||
|
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}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</PopUp>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -9,13 +9,14 @@ import moment from 'moment';
|
|||||||
import 'moment/locale/zh-cn';
|
import 'moment/locale/zh-cn';
|
||||||
import {Icon} from './icons';
|
import {Icon} from './icons';
|
||||||
import PopOver from './PopOver';
|
import PopOver from './PopOver';
|
||||||
|
import PopUp from './PopUp';
|
||||||
import Overlay from './Overlay';
|
import Overlay from './Overlay';
|
||||||
import {ClassNamesFn, themeable, ThemeProps} from '../theme';
|
import {ClassNamesFn, themeable, ThemeProps} from '../theme';
|
||||||
import {PlainObject} from '../types';
|
import {PlainObject} from '../types';
|
||||||
import Calendar from './calendar/Calendar';
|
import Calendar from './calendar/Calendar';
|
||||||
import 'react-datetime/css/react-datetime.css';
|
import 'react-datetime/css/react-datetime.css';
|
||||||
import {localeable, LocaleProps, TranslateFn} from '../locale';
|
import {localeable, LocaleProps, TranslateFn} from '../locale';
|
||||||
import {ucFirst} from '../utils/helper';
|
import {isMobile, ucFirst} from '../utils/helper';
|
||||||
|
|
||||||
const availableShortcuts: {[propName: string]: any} = {
|
const availableShortcuts: {[propName: string]: any} = {
|
||||||
now: {
|
now: {
|
||||||
@ -279,15 +280,17 @@ export interface DateProps extends LocaleProps, ThemeProps {
|
|||||||
// 是否为内嵌模式,如果开启就不是 picker 了,直接页面点选。
|
// 是否为内嵌模式,如果开启就不是 picker 了,直接页面点选。
|
||||||
embed?: boolean;
|
embed?: boolean;
|
||||||
schedules?: Array<{
|
schedules?: Array<{
|
||||||
startTime: Date,
|
startTime: Date;
|
||||||
endTime: Date,
|
endTime: Date;
|
||||||
content: any,
|
content: any;
|
||||||
className?: string
|
className?: string;
|
||||||
}>;
|
}>;
|
||||||
scheduleClassNames?: Array<string>;
|
scheduleClassNames?: Array<string>;
|
||||||
largeMode?: boolean;
|
largeMode?: boolean;
|
||||||
onScheduleClick?: (scheduleData: any) => void;
|
onScheduleClick?: (scheduleData: any) => void;
|
||||||
|
|
||||||
|
useMobileUI?: boolean;
|
||||||
|
|
||||||
// 下面那个千万不要写,写了就会导致 keyof DateProps 得到的结果是 string | number;
|
// 下面那个千万不要写,写了就会导致 keyof DateProps 得到的结果是 string | number;
|
||||||
// [propName: string]: any;
|
// [propName: string]: any;
|
||||||
}
|
}
|
||||||
@ -312,7 +315,13 @@ export class DatePicker extends React.Component<DateProps, DatePickerState> {
|
|||||||
shortcuts: '',
|
shortcuts: '',
|
||||||
closeOnSelect: true,
|
closeOnSelect: true,
|
||||||
overlayPlacement: 'auto',
|
overlayPlacement: 'auto',
|
||||||
scheduleClassNames: ['bg-warning', 'bg-danger', 'bg-success', 'bg-info', 'bg-secondary']
|
scheduleClassNames: [
|
||||||
|
'bg-warning',
|
||||||
|
'bg-danger',
|
||||||
|
'bg-success',
|
||||||
|
'bg-info',
|
||||||
|
'bg-secondary'
|
||||||
|
]
|
||||||
};
|
};
|
||||||
state: DatePickerState = {
|
state: DatePickerState = {
|
||||||
isOpened: false,
|
isOpened: false,
|
||||||
@ -557,6 +566,8 @@ export class DatePicker extends React.Component<DateProps, DatePickerState> {
|
|||||||
borderMode,
|
borderMode,
|
||||||
embed,
|
embed,
|
||||||
minDate,
|
minDate,
|
||||||
|
useMobileUI,
|
||||||
|
maxDate,
|
||||||
schedules,
|
schedules,
|
||||||
largeMode,
|
largeMode,
|
||||||
scheduleClassNames,
|
scheduleClassNames,
|
||||||
@ -612,6 +623,7 @@ export class DatePicker extends React.Component<DateProps, DatePickerState> {
|
|||||||
onClose={this.close}
|
onClose={this.close}
|
||||||
locale={locale}
|
locale={locale}
|
||||||
minDate={minDate}
|
minDate={minDate}
|
||||||
|
maxDate={maxDate}
|
||||||
// utc={utc}
|
// utc={utc}
|
||||||
schedules={schedulesData}
|
schedules={schedulesData}
|
||||||
largeMode={largeMode}
|
largeMode={largeMode}
|
||||||
@ -659,7 +671,7 @@ export class DatePicker extends React.Component<DateProps, DatePickerState> {
|
|||||||
<Icon icon="clock" className="icon" />
|
<Icon icon="clock" className="icon" />
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
{isOpened ? (
|
{!(useMobileUI && isMobile()) && isOpened ? (
|
||||||
<Overlay
|
<Overlay
|
||||||
target={this.getTarget}
|
target={this.getTarget}
|
||||||
container={popOverContainer || this.getParent}
|
container={popOverContainer || this.getParent}
|
||||||
@ -690,11 +702,38 @@ export class DatePicker extends React.Component<DateProps, DatePickerState> {
|
|||||||
onClose={this.close}
|
onClose={this.close}
|
||||||
locale={locale}
|
locale={locale}
|
||||||
minDate={minDate}
|
minDate={minDate}
|
||||||
|
maxDate={maxDate}
|
||||||
// utc={utc}
|
// utc={utc}
|
||||||
/>
|
/>
|
||||||
</PopOver>
|
</PopOver>
|
||||||
</Overlay>
|
</Overlay>
|
||||||
) : null}
|
) : null}
|
||||||
|
{useMobileUI && isMobile() ? (
|
||||||
|
<PopUp
|
||||||
|
className={cx(`${ns}DatePicker-popup`)}
|
||||||
|
isShow={isOpened}
|
||||||
|
onHide={this.handleClick}
|
||||||
|
>
|
||||||
|
{this.renderShortCuts(shortcuts)}
|
||||||
|
|
||||||
|
<Calendar
|
||||||
|
value={date}
|
||||||
|
onChange={this.handleChange}
|
||||||
|
requiredConfirm={!!(dateFormat && timeFormat)}
|
||||||
|
dateFormat={dateFormat}
|
||||||
|
inputFormat={inputFormat}
|
||||||
|
timeFormat={timeFormat}
|
||||||
|
isValidDate={this.checkIsValidDate}
|
||||||
|
viewMode={viewMode}
|
||||||
|
timeConstraints={timeConstraints}
|
||||||
|
input={false}
|
||||||
|
onClose={this.close}
|
||||||
|
locale={locale}
|
||||||
|
minDate={minDate}
|
||||||
|
// utc={utc}
|
||||||
|
/>
|
||||||
|
</PopUp>
|
||||||
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -13,10 +13,12 @@ import Overlay from './Overlay';
|
|||||||
import {ShortCuts, ShortCutDateRange} from './DatePicker';
|
import {ShortCuts, ShortCutDateRange} from './DatePicker';
|
||||||
import Calendar from './calendar/Calendar';
|
import Calendar from './calendar/Calendar';
|
||||||
import PopOver from './PopOver';
|
import PopOver from './PopOver';
|
||||||
|
import PopUp from './PopUp';
|
||||||
import {ClassNamesFn, themeable, ThemeProps} from '../theme';
|
import {ClassNamesFn, themeable, ThemeProps} from '../theme';
|
||||||
import {PlainObject} from '../types';
|
import {PlainObject} from '../types';
|
||||||
import {noop, ucFirst} from '../utils/helper';
|
import {isMobile, noop, ucFirst} from '../utils/helper';
|
||||||
import {LocaleProps, localeable} from '../locale';
|
import {LocaleProps, localeable} from '../locale';
|
||||||
|
import CalendarMobile from './CalendarMobile';
|
||||||
|
|
||||||
export interface DateRangePickerProps extends ThemeProps, LocaleProps {
|
export interface DateRangePickerProps extends ThemeProps, LocaleProps {
|
||||||
className?: string;
|
className?: string;
|
||||||
@ -47,6 +49,7 @@ export interface DateRangePickerProps extends ThemeProps, LocaleProps {
|
|||||||
embed?: boolean;
|
embed?: boolean;
|
||||||
viewMode?: 'days' | 'months' | 'years' | 'time' | 'quarters';
|
viewMode?: 'days' | 'months' | 'years' | 'time' | 'quarters';
|
||||||
borderMode?: 'full' | 'half' | 'none';
|
borderMode?: 'full' | 'half' | 'none';
|
||||||
|
useMobileUI?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DateRangePickerState {
|
export interface DateRangePickerState {
|
||||||
@ -285,6 +288,7 @@ export class DateRangePicker extends React.Component<
|
|||||||
this.handlePopOverClick = this.handlePopOverClick.bind(this);
|
this.handlePopOverClick = this.handlePopOverClick.bind(this);
|
||||||
this.renderDay = this.renderDay.bind(this);
|
this.renderDay = this.renderDay.bind(this);
|
||||||
this.renderQuarter = this.renderQuarter.bind(this);
|
this.renderQuarter = this.renderQuarter.bind(this);
|
||||||
|
this.handleMobileChange = this.handleMobileChange.bind(this);
|
||||||
const {format, joinValues, delimiter, value} = this.props;
|
const {format, joinValues, delimiter, value} = this.props;
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
@ -497,6 +501,16 @@ export class DateRangePicker extends React.Component<
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleMobileChange(data: any, callback?: () => void) {
|
||||||
|
this.setState(
|
||||||
|
{
|
||||||
|
startDate: data.startDate,
|
||||||
|
endDate: data.endDate
|
||||||
|
},
|
||||||
|
callback
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
selectRannge(range: PlainObject) {
|
selectRannge(range: PlainObject) {
|
||||||
const {closeOnSelect, minDate, maxDate} = this.props;
|
const {closeOnSelect, minDate, maxDate} = this.props;
|
||||||
const now = moment();
|
const now = moment();
|
||||||
@ -760,10 +774,20 @@ export class DateRangePicker extends React.Component<
|
|||||||
disabled,
|
disabled,
|
||||||
embed,
|
embed,
|
||||||
overlayPlacement,
|
overlayPlacement,
|
||||||
borderMode
|
borderMode,
|
||||||
|
useMobileUI,
|
||||||
|
timeFormat,
|
||||||
|
minDate,
|
||||||
|
maxDate,
|
||||||
|
minDuration,
|
||||||
|
maxDuration,
|
||||||
|
dateFormat,
|
||||||
|
viewMode = 'days',
|
||||||
|
ranges
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
const useCalendarMobile = useMobileUI && isMobile() && ['days', 'months', 'quarters'].indexOf(viewMode) > -1;
|
||||||
|
|
||||||
const {isOpened, isFocused} = this.state;
|
const {isOpened, isFocused, startDate, endDate} = this.state;
|
||||||
|
|
||||||
const selectedDate = DateRangePicker.unFormatValue(
|
const selectedDate = DateRangePicker.unFormatValue(
|
||||||
value,
|
value,
|
||||||
@ -782,6 +806,25 @@ export class DateRangePicker extends React.Component<
|
|||||||
endViewValue && arr.push(endViewValue);
|
endViewValue && arr.push(endViewValue);
|
||||||
const __ = this.props.translate;
|
const __ = this.props.translate;
|
||||||
|
|
||||||
|
const calendarMobile = <CalendarMobile
|
||||||
|
timeFormat={timeFormat}
|
||||||
|
inputFormat={inputFormat}
|
||||||
|
startDate={startDate}
|
||||||
|
endDate={endDate}
|
||||||
|
minDate={minDate}
|
||||||
|
maxDate={maxDate}
|
||||||
|
minDuration={minDuration}
|
||||||
|
maxDuration={maxDuration}
|
||||||
|
dateFormat={dateFormat}
|
||||||
|
embed={embed}
|
||||||
|
viewMode={viewMode}
|
||||||
|
close={this.close}
|
||||||
|
confirm={this.confirm}
|
||||||
|
onChange={this.handleMobileChange}
|
||||||
|
footerExtra={this.renderRanges(ranges)}
|
||||||
|
showViewMode={viewMode === 'quarters' || viewMode === 'months' ? 'years' : 'months'}
|
||||||
|
/>;
|
||||||
|
|
||||||
if (embed) {
|
if (embed) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@ -793,11 +836,15 @@ export class DateRangePicker extends React.Component<
|
|||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{this.renderCalendar()}
|
{useCalendarMobile
|
||||||
|
? calendarMobile
|
||||||
|
: this.renderCalendar()}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const CalendarMobileTitle = <div className={`${ns}CalendarMobile-title`}>{__('Calendar.datepicker')}</div>;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
@ -837,7 +884,19 @@ export class DateRangePicker extends React.Component<
|
|||||||
</a>
|
</a>
|
||||||
|
|
||||||
{isOpened ? (
|
{isOpened ? (
|
||||||
<Overlay
|
useMobileUI && isMobile() ? (
|
||||||
|
<PopUp
|
||||||
|
isShow={isOpened}
|
||||||
|
className={cx(`${ns}CalendarMobile-pop`)}
|
||||||
|
onHide={this.close}
|
||||||
|
header={CalendarMobileTitle}
|
||||||
|
>
|
||||||
|
{useCalendarMobile
|
||||||
|
? calendarMobile
|
||||||
|
: this.renderCalendar()}
|
||||||
|
</PopUp>
|
||||||
|
)
|
||||||
|
: <Overlay
|
||||||
target={() => this.dom.current}
|
target={() => this.dom.current}
|
||||||
onHide={this.close}
|
onHide={this.close}
|
||||||
container={popOverContainer || (() => findDOMNode(this))}
|
container={popOverContainer || (() => findDOMNode(this))}
|
||||||
|
@ -27,7 +27,13 @@ export class GroupedSelection extends BaseSelection {
|
|||||||
className={cx('GroupedSelection-group', option.className)}
|
className={cx('GroupedSelection-group', option.className)}
|
||||||
>
|
>
|
||||||
<div className={cx('GroupedSelection-itemLabel')}>
|
<div className={cx('GroupedSelection-itemLabel')}>
|
||||||
{itemRender(option)}
|
{itemRender(option, {
|
||||||
|
index: index,
|
||||||
|
multiple: multiple,
|
||||||
|
checked: false,
|
||||||
|
onChange: () => undefined,
|
||||||
|
disabled: disabled || option.disabled
|
||||||
|
})}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={cx('GroupedSelection-items', option.className)}>
|
<div className={cx('GroupedSelection-items', option.className)}>
|
||||||
@ -52,7 +58,13 @@ export class GroupedSelection extends BaseSelection {
|
|||||||
onClick={() => this.toggleOption(option)}
|
onClick={() => this.toggleOption(option)}
|
||||||
>
|
>
|
||||||
<div className={cx('GroupedSelection-itemLabel')}>
|
<div className={cx('GroupedSelection-itemLabel')}>
|
||||||
{itemRender(option)}
|
{itemRender(option, {
|
||||||
|
index: index,
|
||||||
|
multiple: multiple,
|
||||||
|
checked: !!~valueArray.indexOf(option),
|
||||||
|
onChange: () => this.toggleOption(option),
|
||||||
|
disabled: disabled || option.disabled
|
||||||
|
})}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{multiple ? (
|
{multiple ? (
|
||||||
|
@ -12,14 +12,16 @@ import {Icon} from './icons';
|
|||||||
import Overlay from './Overlay';
|
import Overlay from './Overlay';
|
||||||
import Calendar from './calendar/Calendar';
|
import Calendar from './calendar/Calendar';
|
||||||
import PopOver from './PopOver';
|
import PopOver from './PopOver';
|
||||||
|
import PopUp from './PopUp';
|
||||||
import {themeable, ThemeProps} from '../theme';
|
import {themeable, ThemeProps} from '../theme';
|
||||||
import {PlainObject} from '../types';
|
import {PlainObject} from '../types';
|
||||||
import {noop} from '../utils/helper';
|
import {isMobile, noop} from '../utils/helper';
|
||||||
import {LocaleProps, localeable} from '../locale';
|
import {LocaleProps, localeable} from '../locale';
|
||||||
import {DateRangePicker} from './DateRangePicker';
|
import {DateRangePicker} from './DateRangePicker';
|
||||||
import capitalize from 'lodash/capitalize';
|
import capitalize from 'lodash/capitalize';
|
||||||
import {ShortCuts, ShortCutDateRange} from './DatePicker';
|
import {ShortCuts, ShortCutDateRange} from './DatePicker';
|
||||||
import {availableRanges} from './DateRangePicker';
|
import {availableRanges} from './DateRangePicker';
|
||||||
|
import CalendarMobile from './CalendarMobile';
|
||||||
|
|
||||||
export interface MonthRangePickerProps extends ThemeProps, LocaleProps {
|
export interface MonthRangePickerProps extends ThemeProps, LocaleProps {
|
||||||
className?: string;
|
className?: string;
|
||||||
@ -47,6 +49,7 @@ export interface MonthRangePickerProps extends ThemeProps, LocaleProps {
|
|||||||
resetValue?: any;
|
resetValue?: any;
|
||||||
popOverContainer?: any;
|
popOverContainer?: any;
|
||||||
embed?: boolean;
|
embed?: boolean;
|
||||||
|
useMobileUI?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MonthRangePickerState {
|
export interface MonthRangePickerState {
|
||||||
@ -97,6 +100,7 @@ export class MonthRangePicker extends React.Component<
|
|||||||
this.handleKeyPress = this.handleKeyPress.bind(this);
|
this.handleKeyPress = this.handleKeyPress.bind(this);
|
||||||
this.handlePopOverClick = this.handlePopOverClick.bind(this);
|
this.handlePopOverClick = this.handlePopOverClick.bind(this);
|
||||||
this.renderMonth = this.renderMonth.bind(this);
|
this.renderMonth = this.renderMonth.bind(this);
|
||||||
|
this.handleMobileChange = this.handleMobileChange.bind(this);
|
||||||
const {format, joinValues, delimiter, value} = this.props;
|
const {format, joinValues, delimiter, value} = this.props;
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
@ -278,6 +282,16 @@ export class MonthRangePicker extends React.Component<
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleMobileChange(data: any, callback?: () => void) {
|
||||||
|
this.setState(
|
||||||
|
{
|
||||||
|
startDate: data.startDate,
|
||||||
|
endDate: data.endDate
|
||||||
|
},
|
||||||
|
callback
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
selectRannge(range: PlainObject) {
|
selectRannge(range: PlainObject) {
|
||||||
const {closeOnSelect, minDate, maxDate} = this.props;
|
const {closeOnSelect, minDate, maxDate} = this.props;
|
||||||
this.setState(
|
this.setState(
|
||||||
@ -528,10 +542,18 @@ export class MonthRangePicker extends React.Component<
|
|||||||
clearable,
|
clearable,
|
||||||
disabled,
|
disabled,
|
||||||
embed,
|
embed,
|
||||||
overlayPlacement
|
overlayPlacement,
|
||||||
|
useMobileUI,
|
||||||
|
timeFormat,
|
||||||
|
minDate,
|
||||||
|
maxDate,
|
||||||
|
minDuration,
|
||||||
|
maxDuration,
|
||||||
|
ranges
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
const mobileUI = isMobile() && useMobileUI;
|
||||||
|
|
||||||
const {isOpened, isFocused} = this.state;
|
const {isOpened, isFocused, startDate, endDate} = this.state;
|
||||||
|
|
||||||
const selectedDate = DateRangePicker.unFormatValue(
|
const selectedDate = DateRangePicker.unFormatValue(
|
||||||
value,
|
value,
|
||||||
@ -550,6 +572,24 @@ export class MonthRangePicker extends React.Component<
|
|||||||
endViewValue && arr.push(endViewValue);
|
endViewValue && arr.push(endViewValue);
|
||||||
const __ = this.props.translate;
|
const __ = this.props.translate;
|
||||||
|
|
||||||
|
const calendarMobile = <CalendarMobile
|
||||||
|
timeFormat={timeFormat}
|
||||||
|
inputFormat={inputFormat}
|
||||||
|
startDate={startDate}
|
||||||
|
endDate={endDate}
|
||||||
|
minDate={minDate}
|
||||||
|
maxDate={maxDate}
|
||||||
|
minDuration={minDuration}
|
||||||
|
maxDuration={maxDuration}
|
||||||
|
embed={embed}
|
||||||
|
viewMode="months"
|
||||||
|
close={this.close}
|
||||||
|
confirm={this.confirm}
|
||||||
|
onChange={this.handleMobileChange}
|
||||||
|
footerExtra={this.renderRanges(ranges)}
|
||||||
|
showViewMode="years"
|
||||||
|
/>;
|
||||||
|
|
||||||
if (embed) {
|
if (embed) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@ -561,11 +601,15 @@ export class MonthRangePicker extends React.Component<
|
|||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{this.renderCalendar()}
|
{mobileUI
|
||||||
|
? calendarMobile
|
||||||
|
: this.renderCalendar()}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const CalendarMobileTitle = <div className={`${ns}CalendarMobile-title`}>{__('Calendar.datepicker')}</div>;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
@ -604,7 +648,17 @@ export class MonthRangePicker extends React.Component<
|
|||||||
</a>
|
</a>
|
||||||
|
|
||||||
{isOpened ? (
|
{isOpened ? (
|
||||||
<Overlay
|
mobileUI ? (
|
||||||
|
<PopUp
|
||||||
|
isShow={isOpened}
|
||||||
|
className={cx(`${ns}CalendarMobile-pop`)}
|
||||||
|
onHide={this.close}
|
||||||
|
header={CalendarMobileTitle}
|
||||||
|
>
|
||||||
|
{calendarMobile}
|
||||||
|
</PopUp>
|
||||||
|
)
|
||||||
|
: <Overlay
|
||||||
target={() => this.dom.current}
|
target={() => this.dom.current}
|
||||||
onHide={this.close}
|
onHide={this.close}
|
||||||
container={popOverContainer || (() => findDOMNode(this))}
|
container={popOverContainer || (() => findDOMNode(this))}
|
||||||
|
@ -165,7 +165,7 @@ class Position extends React.Component<any, any> {
|
|||||||
interface OverlayProps {
|
interface OverlayProps {
|
||||||
placement?: string;
|
placement?: string;
|
||||||
show?: boolean;
|
show?: boolean;
|
||||||
transition?: React.ReactType;
|
transition?: React.ElementType;
|
||||||
containerPadding?: number;
|
containerPadding?: number;
|
||||||
shouldUpdatePosition?: boolean;
|
shouldUpdatePosition?: boolean;
|
||||||
rootClose?: boolean;
|
rootClose?: boolean;
|
||||||
|
150
src/components/Picker.tsx
Normal file
150
src/components/Picker.tsx
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
/**
|
||||||
|
* @file Picker
|
||||||
|
* @description 移动端列滚动选择器
|
||||||
|
*/
|
||||||
|
import React, {memo, ReactNode, useState, useEffect} from 'react';
|
||||||
|
import {uncontrollable} from 'uncontrollable';
|
||||||
|
|
||||||
|
import {themeable, ThemeProps} from '../theme';
|
||||||
|
import {localeable, LocaleProps} from '../locale';
|
||||||
|
|
||||||
|
import Button from './Button';
|
||||||
|
import {PickerColumnItem, default as Column} from './PickerColumn';
|
||||||
|
|
||||||
|
export type PickerValue = string | number;
|
||||||
|
|
||||||
|
export interface PickerProps extends ThemeProps, LocaleProps {
|
||||||
|
title?: String | ReactNode;
|
||||||
|
labelField?: string;
|
||||||
|
className?: string;
|
||||||
|
showToolbar?: boolean;
|
||||||
|
defaultValue?: PickerValue[];
|
||||||
|
value?: PickerValue[];
|
||||||
|
swipeDuration?: number;
|
||||||
|
visibleItemCount?: number;
|
||||||
|
itemHeight?: number;
|
||||||
|
columns: PickerColumnItem[] | PickerColumnItem;
|
||||||
|
onChange?: (value?: PickerValue[], index?: number, confirm?: boolean) => void;
|
||||||
|
onClose?: (value?: PickerValue[]) => void;
|
||||||
|
onConfirm?: (value?: PickerValue[]) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
function fixToArray(data: any) {
|
||||||
|
if (!Array.isArray(data)) {
|
||||||
|
return [data];
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Picker = memo<PickerProps>(props => {
|
||||||
|
const {
|
||||||
|
labelField,
|
||||||
|
visibleItemCount = 5,
|
||||||
|
value = [],
|
||||||
|
swipeDuration = 1000,
|
||||||
|
columns = [],
|
||||||
|
itemHeight = 30,
|
||||||
|
showToolbar = true,
|
||||||
|
className = '',
|
||||||
|
classnames: cx,
|
||||||
|
classPrefix: ns,
|
||||||
|
translate: __
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
const _columns = fixToArray(columns);
|
||||||
|
const [innerValue, setInnerValue] = useState<PickerValue[]>(
|
||||||
|
fixToArray(props.value === undefined ? props.defaultValue || [] : value)
|
||||||
|
);
|
||||||
|
useEffect(() => {
|
||||||
|
setInnerValue(value);
|
||||||
|
}, [value]);
|
||||||
|
|
||||||
|
const close = () => {
|
||||||
|
if (props.onClose) {
|
||||||
|
props.onClose(innerValue);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const confirm = () => {
|
||||||
|
if (props.onConfirm) {
|
||||||
|
props.onConfirm(innerValue);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onChange = (
|
||||||
|
itemValue: PickerValue,
|
||||||
|
columnIndex: number,
|
||||||
|
confirm?: boolean
|
||||||
|
) => {
|
||||||
|
const nextInnerValue = [...innerValue];
|
||||||
|
nextInnerValue[columnIndex] = itemValue;
|
||||||
|
setInnerValue(nextInnerValue);
|
||||||
|
if (props.onChange) {
|
||||||
|
props.onChange(nextInnerValue, columnIndex, confirm);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderColumnItem = (item: PickerColumnItem, index: number) => {
|
||||||
|
return (
|
||||||
|
<Column
|
||||||
|
{...item}
|
||||||
|
classnames={cx}
|
||||||
|
classPrefix={ns}
|
||||||
|
labelField={labelField}
|
||||||
|
itemHeight={itemHeight}
|
||||||
|
swipeDuration={swipeDuration}
|
||||||
|
visibleItemCount={visibleItemCount}
|
||||||
|
value={innerValue[index]}
|
||||||
|
onChange={(val: string | number, i, confirm) => {
|
||||||
|
onChange(val, index, confirm);
|
||||||
|
}}
|
||||||
|
key={`column${index}`}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const wrapHeight = itemHeight * +visibleItemCount;
|
||||||
|
const frameStyle = {height: `${itemHeight}px`};
|
||||||
|
const columnsStyle = {height: `${wrapHeight}px`};
|
||||||
|
const maskStyle = {
|
||||||
|
backgroundSize: `100% ${(wrapHeight - itemHeight) / 2}px`
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={cx(className, 'PickerColumns', 'PickerColumns-popOver')}>
|
||||||
|
{showToolbar && (
|
||||||
|
<div className={cx('PickerColumns-toolbar')}>
|
||||||
|
<Button
|
||||||
|
className="PickerColumns-cancel"
|
||||||
|
level="default"
|
||||||
|
onClick={close}
|
||||||
|
>
|
||||||
|
{__('cancel')}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
className="PickerColumns-confirm"
|
||||||
|
level="primary"
|
||||||
|
onClick={confirm}
|
||||||
|
>
|
||||||
|
{__('confirm')}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className={cx('PickerColumns-columns')} style={columnsStyle}>
|
||||||
|
{_columns.map((column: PickerColumnItem, index: number) =>
|
||||||
|
renderColumnItem(column, index)
|
||||||
|
)}
|
||||||
|
<div className={cx('PickerColumns-mask')} style={maskStyle}></div>
|
||||||
|
<div className={cx('PickerColumns-frame')} style={frameStyle}></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
export default themeable(
|
||||||
|
localeable(
|
||||||
|
uncontrollable(Picker, {
|
||||||
|
value: 'onChange'
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user