form-driver 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +37 -0
- package/dist/m3.css +310 -0
- package/dist/m3.js +1 -0
- package/es/m3.css +310 -0
- package/es/m3.js +20919 -0
- package/lib/m3.css +310 -0
- package/lib/m3.js +20959 -0
- package/package.json +132 -0
- package/src/.DS_Store +0 -0
- package/src/framework/Ajax.ts +96 -0
- package/src/framework/Assembly.tsx +165 -0
- package/src/framework/Init.tsx +196 -0
- package/src/framework/M3.tsx +94 -0
- package/src/framework/MContext.ts +15 -0
- package/src/framework/MFieldViewer.tsx +32 -0
- package/src/framework/MUtil.tsx +653 -0
- package/src/framework/MViewer.less +128 -0
- package/src/framework/MViewer.tsx +180 -0
- package/src/framework/MViewerDebug.tsx +95 -0
- package/src/framework/Persistant.ts +90 -0
- package/src/framework/Schema.ts +386 -0
- package/src/framework/SchemaFunc.ts +30 -0
- package/src/framework/Validator.ts +160 -0
- package/src/framework/editorMap.ts +109 -0
- package/src/index.ts +33 -0
- package/src/types/MArrayType.ts +73 -0
- package/src/types/MCascadeType.ts +35 -0
- package/src/types/MCnAddressType.ts +54 -0
- package/src/types/MDateRangeType.ts +52 -0
- package/src/types/MDateTimeType.ts +53 -0
- package/src/types/MDecorationType.ts +6 -0
- package/src/types/MEnumType.ts +65 -0
- package/src/types/MExperienceType.ts +81 -0
- package/src/types/MFloatType.ts +10 -0
- package/src/types/MGB2260Type.ts +56 -0
- package/src/types/MIntDiffType.ts +6 -0
- package/src/types/MIntType.ts +44 -0
- package/src/types/MKvSetType.ts +50 -0
- package/src/types/MMatrixType.ts +52 -0
- package/src/types/MObjectType.ts +89 -0
- package/src/types/MSetType.ts +220 -0
- package/src/types/MStringType.ts +27 -0
- package/src/types/MTelType.ts +14 -0
- package/src/types/MType.ts +77 -0
- package/src/types/MVLPairType.ts +35 -0
- package/src/types/gb2260.json +1 -0
- package/src/ui/BaseViewer.tsx +110 -0
- package/src/ui/editor/.DS_Store +0 -0
- package/src/ui/editor/basic/.DS_Store +0 -0
- package/src/ui/editor/basic/ACascadePicker.tsx +114 -0
- package/src/ui/editor/basic/ACheckBox.tsx +104 -0
- package/src/ui/editor/basic/ADatetimePicker.tsx +76 -0
- package/src/ui/editor/basic/AGB2260.tsx +52 -0
- package/src/ui/editor/basic/AInputBox.tsx +59 -0
- package/src/ui/editor/basic/AIntBox.tsx +39 -0
- package/src/ui/editor/basic/AKvSet.less +9 -0
- package/src/ui/editor/basic/AKvSet.tsx +90 -0
- package/src/ui/editor/basic/ARadio.tsx +86 -0
- package/src/ui/editor/basic/ARangePicker.tsx +129 -0
- package/src/ui/editor/basic/ARate.less +8 -0
- package/src/ui/editor/basic/ARate.tsx +37 -0
- package/src/ui/editor/basic/ARemoteSelector.tsx +116 -0
- package/src/ui/editor/basic/ASelector.tsx +88 -0
- package/src/ui/editor/basic/ASetSelector.tsx +65 -0
- package/src/ui/editor/basic/ASpecInputBox.tsx +20 -0
- package/src/ui/editor/basic/ATreeSelect.tsx +41 -0
- package/src/ui/editor/basic/AUpload.tsx +119 -0
- package/src/ui/editor/basic/NPS.less +21 -0
- package/src/ui/editor/basic/NPS.tsx +47 -0
- package/src/ui/editor/complex/AArray.less +10 -0
- package/src/ui/editor/complex/AArray.tsx +104 -0
- package/src/ui/editor/complex/AArrayGrid.tsx +115 -0
- package/src/ui/editor/complex/ACnAddress.less +15 -0
- package/src/ui/editor/complex/ACnAddress.tsx +61 -0
- package/src/ui/editor/complex/ADialogForm.tsx +45 -0
- package/src/ui/editor/complex/AExperience.tsx +85 -0
- package/src/ui/editor/complex/AForm.less +35 -0
- package/src/ui/editor/complex/AForm.tsx +340 -0
- package/src/ui/editor/complex/AIntDiff.tsx +77 -0
- package/src/ui/editor/complex/AMatrix.less +18 -0
- package/src/ui/editor/complex/AMatrix.tsx +242 -0
- package/src/ui/editor/complex/ATable.less +4 -0
- package/src/ui/editor/complex/ATable.tsx +33 -0
- package/src/ui/editor/complex/JsonEditor.tsx +37 -0
- package/src/ui/readable/A.tsx +33 -0
- package/src/ui/readable/ArrayViewer.tsx +46 -0
- package/src/ui/readable/DecorationViewer.tsx +76 -0
- package/src/ui/readable/DivViewer.tsx +11 -0
- package/src/ui/widget/Collapsible.tsx +156 -0
- package/src/ui/widget/Segment.less +39 -0
- package/src/ui/widget/Segment.tsx +40 -0
- package/src/ui/widget/SegmentEditSwitch.tsx +46 -0
- package/src/ui/widget/SelectBox.tsx +43 -0
- package/src/ui/widget/UnderlineInputBox.less +47 -0
- package/src/ui/widget/UnderlineInputBox.tsx +10 -0
- package/types/framework/Ajax.d.ts +5 -0
- package/types/framework/Assembly.d.ts +59 -0
- package/types/framework/Init.d.ts +4 -0
- package/types/framework/M3.d.ts +6 -0
- package/types/framework/MContext.d.ts +11 -0
- package/types/framework/MFieldViewer.d.ts +8 -0
- package/types/framework/MUtil.d.ts +180 -0
- package/types/framework/MViewer.d.ts +75 -0
- package/types/framework/MViewerDebug.d.ts +11 -0
- package/types/framework/Persistant.d.ts +17 -0
- package/types/framework/Schema.d.ts +306 -0
- package/types/framework/SchemaFunc.d.ts +14 -0
- package/types/framework/Validator.d.ts +53 -0
- package/types/framework/editorMap.d.ts +107 -0
- package/types/index.d.ts +21 -0
- package/types/types/MArrayType.d.ts +2 -0
- package/types/types/MCascadeType.d.ts +11 -0
- package/types/types/MCnAddressType.d.ts +2 -0
- package/types/types/MDateRangeType.d.ts +7 -0
- package/types/types/MDateTimeType.d.ts +11 -0
- package/types/types/MDecorationType.d.ts +7 -0
- package/types/types/MEnumType.d.ts +2 -0
- package/types/types/MExperienceType.d.ts +5 -0
- package/types/types/MFloatType.d.ts +2 -0
- package/types/types/MGB2260Type.d.ts +9 -0
- package/types/types/MIntDiffType.d.ts +2 -0
- package/types/types/MIntType.d.ts +2 -0
- package/types/types/MKvSetType.d.ts +11 -0
- package/types/types/MMatrixType.d.ts +5 -0
- package/types/types/MObjectType.d.ts +11 -0
- package/types/types/MSetType.d.ts +7 -0
- package/types/types/MStringType.d.ts +2 -0
- package/types/types/MTelType.d.ts +4 -0
- package/types/types/MType.d.ts +46 -0
- package/types/types/MVLPairType.d.ts +5 -0
- package/types/ui/BaseViewer.d.ts +45 -0
- package/types/ui/editor/basic/ACascadePicker.d.ts +11 -0
- package/types/ui/editor/basic/ACheckBox.d.ts +17 -0
- package/types/ui/editor/basic/ADatetimePicker.d.ts +15 -0
- package/types/ui/editor/basic/AGB2260.d.ts +10 -0
- package/types/ui/editor/basic/AInputBox.d.ts +8 -0
- package/types/ui/editor/basic/AIntBox.d.ts +9 -0
- package/types/ui/editor/basic/AKvSet.d.ts +19 -0
- package/types/ui/editor/basic/ARadio.d.ts +14 -0
- package/types/ui/editor/basic/ARangePicker.d.ts +30 -0
- package/types/ui/editor/basic/ARate.d.ts +13 -0
- package/types/ui/editor/basic/ARemoteSelector.d.ts +15 -0
- package/types/ui/editor/basic/ASelector.d.ts +14 -0
- package/types/ui/editor/basic/ASetSelector.d.ts +10 -0
- package/types/ui/editor/basic/ASpecInputBox.d.ts +9 -0
- package/types/ui/editor/basic/ATreeSelect.d.ts +18 -0
- package/types/ui/editor/basic/AUpload.d.ts +33 -0
- package/types/ui/editor/basic/NPS.d.ts +13 -0
- package/types/ui/editor/complex/AArray.d.ts +11 -0
- package/types/ui/editor/complex/AArrayGrid.d.ts +13 -0
- package/types/ui/editor/complex/ACnAddress.d.ts +10 -0
- package/types/ui/editor/complex/ADialogForm.d.ts +11 -0
- package/types/ui/editor/complex/AExperience.d.ts +16 -0
- package/types/ui/editor/complex/AForm.d.ts +46 -0
- package/types/ui/editor/complex/AIntDiff.d.ts +14 -0
- package/types/ui/editor/complex/AMatrix.d.ts +48 -0
- package/types/ui/editor/complex/ATable.d.ts +9 -0
- package/types/ui/editor/complex/JsonEditor.d.ts +9 -0
- package/types/ui/readable/A.d.ts +5 -0
- package/types/ui/readable/ArrayViewer.d.ts +5 -0
- package/types/ui/readable/DecorationViewer.d.ts +3 -0
- package/types/ui/readable/DivViewer.d.ts +5 -0
- package/types/ui/widget/Collapsible.d.ts +46 -0
- package/types/ui/widget/Segment.d.ts +18 -0
- package/types/ui/widget/SegmentEditSwitch.d.ts +20 -0
- package/types/ui/widget/SelectBox.d.ts +13 -0
- package/types/ui/widget/UnderlineInputBox.d.ts +6 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { BaseViewer } from '../../BaseViewer';
|
|
4
|
+
import { MUtil } from "../../../framework/MUtil";
|
|
5
|
+
import { Table } from 'antd';
|
|
6
|
+
import { MFieldViewer } from "../../../framework/MFieldViewer";
|
|
7
|
+
import "./ATable.less";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* antd的表格,用于展示对象数组
|
|
11
|
+
*/
|
|
12
|
+
export class ATable extends BaseViewer {
|
|
13
|
+
element() {
|
|
14
|
+
if(this.props.schema.type !== "array" || this.props.schema.arrayMember.type !== "object"){
|
|
15
|
+
return MUtil.error("ATable只适用于对象数组", this.props.schema);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const columns = this.props.schema.arrayMember?.objectFields?.map(f=>{
|
|
19
|
+
return {
|
|
20
|
+
title: f.label,
|
|
21
|
+
dataIndex: f.name,
|
|
22
|
+
key: f.name,
|
|
23
|
+
render: (value, row, idx) => {
|
|
24
|
+
return <MFieldViewer schema={f} database={row} path={f.name} morph={this.props.morph}/>
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
let nextKey = 0;
|
|
30
|
+
return <Table rowKey={()=>nextKey++} key={this.props.path} className="ATable"
|
|
31
|
+
columns={columns} dataSource={super.getValue()} pagination={false} loading={!this.props.database}/>
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import React, { CSSProperties } from "react";
|
|
2
|
+
import _ from "lodash";
|
|
3
|
+
import { BaseViewer } from '../../BaseViewer';
|
|
4
|
+
import TextArea from 'antd/lib/input/TextArea';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* 用文本框编辑json object或者json array
|
|
8
|
+
*/
|
|
9
|
+
export class JsonEditor extends BaseViewer {
|
|
10
|
+
error:string;
|
|
11
|
+
|
|
12
|
+
element() {
|
|
13
|
+
const value = super.getValue();
|
|
14
|
+
let styles:CSSProperties = _.merge({minHeight: "10em"}, this.props.style, this.props.schema.style);
|
|
15
|
+
if(this.props.hideBorder){
|
|
16
|
+
styles.border = "1px solid transparent";
|
|
17
|
+
}
|
|
18
|
+
if(this.error) {
|
|
19
|
+
styles.border = "1px solid red";
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return <TextArea style={styles} key={this.props.path} defaultValue={JSON.stringify(value,null,2)} onBlur={(v)=>{
|
|
23
|
+
try{
|
|
24
|
+
this.changeValue(JSON.parse(v.target.value));
|
|
25
|
+
}catch(e){
|
|
26
|
+
}
|
|
27
|
+
}} onChange={(v)=>{
|
|
28
|
+
try{
|
|
29
|
+
JSON.parse(v.target.value);
|
|
30
|
+
this.error = undefined;
|
|
31
|
+
}catch(e){
|
|
32
|
+
this.error = e.message;
|
|
33
|
+
}
|
|
34
|
+
this.setState({});
|
|
35
|
+
}}/>
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import _ from "lodash";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { MFieldSchema } from "../..";
|
|
4
|
+
import { assembly } from "../../framework/Assembly";
|
|
5
|
+
import { MUtil } from "../../framework/MUtil";
|
|
6
|
+
import { BaseViewer } from '../BaseViewer';
|
|
7
|
+
|
|
8
|
+
export class A extends BaseViewer {
|
|
9
|
+
element() {
|
|
10
|
+
const schema = this.props.schema as MFieldSchema;
|
|
11
|
+
const { urlExpr, labelExpr, onClick } = schema.a;
|
|
12
|
+
const value = super.getValue();
|
|
13
|
+
const parent = super.getParentValue();
|
|
14
|
+
|
|
15
|
+
let label;
|
|
16
|
+
let href;
|
|
17
|
+
if (_.isNil(value)) {
|
|
18
|
+
label = assembly.theme.READABLE_BLANK
|
|
19
|
+
} else {
|
|
20
|
+
label = labelExpr
|
|
21
|
+
? MUtil.evalExprOrFunction(labelExpr, ["value", schema.name, "parent", "_"], [value, value, parent, _], value?.toString())
|
|
22
|
+
: assembly.toReadable(schema, value, parent);
|
|
23
|
+
href = MUtil.evalExprOrFunction(urlExpr, ["value", schema.name, "parent", "_"], [value, value, parent, _], undefined);
|
|
24
|
+
}
|
|
25
|
+
const target = schema.a.currentPage ? undefined : "_blank";
|
|
26
|
+
return <a
|
|
27
|
+
style={this.props.schema.style}
|
|
28
|
+
className="wrap"
|
|
29
|
+
onClick={() => onClick?.(value, parent)}
|
|
30
|
+
href={href}
|
|
31
|
+
target={target}>{label}</a>
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
import _ from "lodash";
|
|
4
|
+
import React from "react";
|
|
5
|
+
import { assembly } from '../../framework/Assembly';
|
|
6
|
+
import { MFieldViewer } from "../../framework/MFieldViewer";
|
|
7
|
+
import { BaseViewer } from "../BaseViewer";
|
|
8
|
+
|
|
9
|
+
export class ArrayViewer extends BaseViewer {
|
|
10
|
+
element(ctx) {
|
|
11
|
+
const vs = this.getValue();
|
|
12
|
+
if (_.isNil(vs)) {
|
|
13
|
+
return <div>{assembly.theme.READABLE_BLANK}</div>
|
|
14
|
+
} else if (!_.isArray(vs)) {
|
|
15
|
+
return <div>{assembly.theme.READABLE_INVALID}</div>
|
|
16
|
+
} else if (vs.length === 0) {
|
|
17
|
+
return <div>{assembly.theme.READABLE_BLANK}</div>
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (this.props.schema.toReadable) {
|
|
21
|
+
return <div>{assembly.toReadable(this.props.schema, vs, super.getParentValue())}</div>
|
|
22
|
+
} else if (this.props.schema.remote) {
|
|
23
|
+
return <div> {vs.map(v => v.label).join(", ")} </div>
|
|
24
|
+
} else if (!this.props.schema.arrayMember) {
|
|
25
|
+
return <div>{vs.join(', ')}</div>
|
|
26
|
+
} else {
|
|
27
|
+
if (this.props.schema.layoutHint === "h") {
|
|
28
|
+
return <div style={{ display: 'flex' }}>
|
|
29
|
+
{
|
|
30
|
+
vs.map((v, index) => {
|
|
31
|
+
const itemPath = this.props.path + "[" + index + "]";
|
|
32
|
+
return <div style={{flex: 1}}>
|
|
33
|
+
<MFieldViewer style={this.props.schema.arrayMember.style} morph={this.props.morph} key={itemPath} schema={this.props.schema.arrayMember} database={this.props.database} path={itemPath} />
|
|
34
|
+
</div>
|
|
35
|
+
})
|
|
36
|
+
}
|
|
37
|
+
</div>
|
|
38
|
+
} else {
|
|
39
|
+
return vs.map((v, index) => {
|
|
40
|
+
const itemPath = this.props.path + "[" + index + "]";
|
|
41
|
+
return <MFieldViewer style={this.props.schema.arrayMember.style} morph={this.props.morph} key={itemPath} schema={this.props.schema.arrayMember} database={this.props.database} path={itemPath} />;
|
|
42
|
+
})
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
import { MProp } from '../../framework/Schema';
|
|
4
|
+
import { Button, Modal, message, Space} from 'antd';
|
|
5
|
+
import { MUtil } from '../../framework/MUtil';
|
|
6
|
+
import { assembly } from '../../framework/Assembly';
|
|
7
|
+
import { MContext } from '../../framework/MContext';
|
|
8
|
+
import _ from "lodash";
|
|
9
|
+
|
|
10
|
+
export function DecorationViewer(props:MProp){
|
|
11
|
+
let subType = props.schema?.decoration?.subType;
|
|
12
|
+
|
|
13
|
+
if (!subType) {
|
|
14
|
+
if(_.isString(props.schema?.decoration?.HTML)) {
|
|
15
|
+
subType = "rich";
|
|
16
|
+
} else if ( _.isString(props.schema?.decoration?.segmentLabel)) {
|
|
17
|
+
subType = "segmentLabel";
|
|
18
|
+
} else if (_.isString(props.schema?.decoration?.submitLabel)) {
|
|
19
|
+
subType = "submitBar";
|
|
20
|
+
} else if (props.schema?.decoration?.operations) {
|
|
21
|
+
subType = "operations";
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if(subType === 'rich') {
|
|
26
|
+
// HTML 片段
|
|
27
|
+
return <div dangerouslySetInnerHTML={{ __html: props.schema.decoration.HTML}}></div>
|
|
28
|
+
} else if(subType === 'submitBar') {
|
|
29
|
+
// 提交按钮
|
|
30
|
+
const [loading, setLoading] = useState(false);
|
|
31
|
+
return <MContext.Consumer>
|
|
32
|
+
{
|
|
33
|
+
ctx => <div style={{textAlign: "center"}}>
|
|
34
|
+
<Button type="primary" loading={loading} onClick={()=>{
|
|
35
|
+
const finalData = MUtil.filterHide(ctx.rootProps.schema, props.database)
|
|
36
|
+
const r = assembly.validate(ctx.rootProps.schema, finalData);
|
|
37
|
+
ctx.setForceValid(true);
|
|
38
|
+
if(r){ // 校验有问题
|
|
39
|
+
Modal.warning({
|
|
40
|
+
content: '还没填完呢',
|
|
41
|
+
onOk:()=> document.getElementById(r.path)?.scrollIntoView({behavior: 'smooth', block: 'start'})
|
|
42
|
+
});
|
|
43
|
+
} else {
|
|
44
|
+
if(ctx.rootProps.onSubmit){
|
|
45
|
+
setLoading(true);
|
|
46
|
+
ctx.rootProps.onSubmit(finalData).finally(() => {
|
|
47
|
+
setLoading(false);
|
|
48
|
+
})
|
|
49
|
+
} else {
|
|
50
|
+
message.success("填完了,但提交功能还在开发中,请联系技术支持人员");
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}}>{props.schema.decoration.submitLabel}</Button>
|
|
54
|
+
</div>
|
|
55
|
+
}
|
|
56
|
+
</MContext.Consumer>
|
|
57
|
+
} else if(subType === 'segmentLabel'){
|
|
58
|
+
// 分段标题
|
|
59
|
+
return <div style={{borderBottom: "1px solid #d9d9d9", padding: "0 0 10px", margin: "20px 0 15px", fontSize: 19, fontWeight: "bold"}}>
|
|
60
|
+
{props.schema.decoration.segmentLabel}
|
|
61
|
+
</div>
|
|
62
|
+
} else if(subType === 'operations'){
|
|
63
|
+
return <Space size="middle">
|
|
64
|
+
{props.schema.decoration.operations.map((o,index)=>
|
|
65
|
+
<a key={index} onClick={()=>{
|
|
66
|
+
const ppath = MUtil.parentPath(props.path);
|
|
67
|
+
o.handler(ppath ? _.get(props.database, ppath): props.database)
|
|
68
|
+
}}>
|
|
69
|
+
{o.label}
|
|
70
|
+
</a>
|
|
71
|
+
)}
|
|
72
|
+
</Space>
|
|
73
|
+
} else {
|
|
74
|
+
return MUtil.error("无效的Decoration", props.schema);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { BaseViewer } from '../BaseViewer';
|
|
3
|
+
import { assembly } from '../../framework/Assembly';
|
|
4
|
+
|
|
5
|
+
export class DivViewer extends BaseViewer {
|
|
6
|
+
element() {
|
|
7
|
+
return <pre style={{marginBottom:0, color: "rgba(0,0,0,0.6)", overflow: "initial", wordBreak: "break-all", whiteSpace: "pre-wrap", ...this.props.schema.style}}>
|
|
8
|
+
{assembly.toReadable(this.props.schema, super.getValue(), super.getParentValue())}
|
|
9
|
+
</pre>
|
|
10
|
+
}
|
|
11
|
+
}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import _ from "lodash";
|
|
2
|
+
import React, { CSSProperties } from "react";
|
|
3
|
+
interface Prop {
|
|
4
|
+
/** 单测需要用到的属性 */
|
|
5
|
+
name: string;
|
|
6
|
+
/** 是否展开状态 */
|
|
7
|
+
open: boolean;
|
|
8
|
+
/** 动画时长,默认0.5秒,nil表示无动画效果 */
|
|
9
|
+
ms?: number;
|
|
10
|
+
/** Collapsible也能定义样式 */
|
|
11
|
+
style?: CSSProperties;
|
|
12
|
+
/** html元素id */
|
|
13
|
+
id?:string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
interface State {
|
|
17
|
+
css: CSSProperties,
|
|
18
|
+
|
|
19
|
+
// 为了让getDerivedStateFromProps能读到this,把this放在state里
|
|
20
|
+
self?: Collapsible
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* 可以折叠的元素,折叠时可带动画效果
|
|
25
|
+
*
|
|
26
|
+
* 以下几点帮助理解这个组件:
|
|
27
|
+
* 1. 初始元素隐藏时,需要隐藏又不能占用空间,否则无法获取其高度,用position="absolute"让其脱离文档流来实现
|
|
28
|
+
* 2. 折叠效果通过max-height来实现
|
|
29
|
+
* 3. 用state来控制元素的css,state可能由props变化和定时器自己触发,所以要用getDerivedStateFromProps
|
|
30
|
+
*/
|
|
31
|
+
export class Collapsible extends React.Component<Prop, State> {
|
|
32
|
+
div:HTMLDivElement;
|
|
33
|
+
height: number;
|
|
34
|
+
action: any;
|
|
35
|
+
|
|
36
|
+
/** props.style去掉margin。如果不去掉,隐藏后margin会占用空间出来 */
|
|
37
|
+
closeStyle: CSSProperties
|
|
38
|
+
|
|
39
|
+
constructor(p: Prop){
|
|
40
|
+
super(p);
|
|
41
|
+
|
|
42
|
+
// 这段根据props创造一个初始的state
|
|
43
|
+
let st = Collapsible.genNextState(this, this.props.open, false);
|
|
44
|
+
if(!this.props.open){ // 初始折叠的话,不能占用空间,又有实际大小供componentDidMount读取。
|
|
45
|
+
st.css.position = "absolute"
|
|
46
|
+
st.css.display = this.props.style.display;
|
|
47
|
+
delete st.css.maxHeight;
|
|
48
|
+
}
|
|
49
|
+
st.self = this;
|
|
50
|
+
this.state = st;
|
|
51
|
+
|
|
52
|
+
this.closeStyle = {};
|
|
53
|
+
for(let k of Object.keys(this.props.style)){
|
|
54
|
+
if(k.indexOf("margin") != 0){
|
|
55
|
+
this.closeStyle[k] = this.props.style[k];
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
componentDidMount(){ // dom初次加载时记录元素高度,假如起始状态是隐藏的,这个高度动画会用得上
|
|
61
|
+
if(!this.height) {
|
|
62
|
+
this.height = this.div.offsetHeight;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* 产生state对象
|
|
68
|
+
* @param self
|
|
69
|
+
* @param open 是否展开
|
|
70
|
+
* @param changing 是否是切换中
|
|
71
|
+
* @returns 新的state对象
|
|
72
|
+
*/
|
|
73
|
+
static genNextState(self: Collapsible, open:boolean, changing:boolean): State {
|
|
74
|
+
const transition = self.props.ms ? 'all ' + self.props.ms + 'ms' : undefined;
|
|
75
|
+
|
|
76
|
+
if(open) {
|
|
77
|
+
return { css: {
|
|
78
|
+
...self.props.style,
|
|
79
|
+
maxHeight: changing ? self.height + "px" : undefined,
|
|
80
|
+
visibility: "visible",
|
|
81
|
+
opacity: "1",
|
|
82
|
+
transition
|
|
83
|
+
} }
|
|
84
|
+
} else {
|
|
85
|
+
const r: State = { css: {
|
|
86
|
+
... self.closeStyle,
|
|
87
|
+
maxHeight: "0px",
|
|
88
|
+
visibility: "hidden",
|
|
89
|
+
opacity: "0",
|
|
90
|
+
transition
|
|
91
|
+
} }
|
|
92
|
+
|
|
93
|
+
if(!changing) {
|
|
94
|
+
r.css.display = "none";
|
|
95
|
+
}
|
|
96
|
+
return r;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** Collapsible的state既可以被props影响,也可能自发(延时)改变 */
|
|
101
|
+
static getDerivedStateFromProps(nextProps:Prop, prevState:State): State {
|
|
102
|
+
const self = prevState.self;
|
|
103
|
+
|
|
104
|
+
// open状态不变,直接返回。这个判断是要的,否则初始隐藏的元素高度获得不了,因为下面的代码会把maxHeight设置为0
|
|
105
|
+
if(self.state.css.visibility == "hidden" && !nextProps.open || self.state.css.visibility == "visible" && nextProps.open){
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if(_.isNil(nextProps.ms)) { // 没有动画
|
|
110
|
+
return Collapsible.genNextState(self, nextProps.open, false);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
let nextState = null;
|
|
114
|
+
|
|
115
|
+
if(prevState.css.visibility === "visible" && !nextProps.open) { // 折叠
|
|
116
|
+
// 折叠前,重新获取元素高度,虽然componentDidMount里记录过,但渲染过以后,万一元素高度又变了呢?
|
|
117
|
+
const heightNow = prevState.self.div.offsetHeight;
|
|
118
|
+
|
|
119
|
+
nextState = Collapsible.genNextState(self, true, true); // maxHeight设置为元素被折叠前的高度,以启动动画
|
|
120
|
+
|
|
121
|
+
clearTimeout(self.action)
|
|
122
|
+
self.action = setTimeout(()=>{
|
|
123
|
+
self.setState(Collapsible.genNextState(self, false, true)); // maxHeight设置为0,以完成动画
|
|
124
|
+
|
|
125
|
+
self.action =setTimeout(()=>{
|
|
126
|
+
self.setState(Collapsible.genNextState(self, false, false)); // 去掉maxHeight,这样元素高度仍然是可变的
|
|
127
|
+
}, self.props.ms)
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
// 计入height,展开时用
|
|
131
|
+
self.height = _.max([heightNow, self.height]);
|
|
132
|
+
} else if(prevState.css.visibility === "hidden" && nextProps.open) { // 展开
|
|
133
|
+
// 原来没有maxHeight,需要给个初始的,等下再改掉。因为maxHeight必须有变化,才能触发动画
|
|
134
|
+
nextState = Collapsible.genNextState(self, false, true); // maxHeight设置为0,以启动动画
|
|
135
|
+
|
|
136
|
+
clearTimeout(self.action)
|
|
137
|
+
self.action = setTimeout(()=>{
|
|
138
|
+
self.setState(Collapsible.genNextState(self, true, true)); // maxHeight设置为折叠前的高度,以完成动画
|
|
139
|
+
|
|
140
|
+
self.action = setTimeout(()=>{
|
|
141
|
+
self.setState(Collapsible.genNextState(self, true, false)); // 去掉maxHeight/display,这样元素高度仍然是可变的
|
|
142
|
+
}, self.props.ms)
|
|
143
|
+
})
|
|
144
|
+
}
|
|
145
|
+
// else { // 状态不变,不用动
|
|
146
|
+
// }
|
|
147
|
+
return nextState;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
render(){
|
|
151
|
+
// @ts-ignore
|
|
152
|
+
return <div id={this.props.id} name={this.props.name} ref={d => {this.div = d}} style={this.state.css}>
|
|
153
|
+
{this.props.children}
|
|
154
|
+
</div>
|
|
155
|
+
}
|
|
156
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
.AForm_segment {
|
|
4
|
+
background: white;
|
|
5
|
+
box-shadow: 0 0 10px #d0cfcf;
|
|
6
|
+
padding: 30px;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/* 只有第一个segment需要留上边距 */
|
|
10
|
+
* .AForm_segment:not(:first-child){ margin: 25px 0 25px 0;}
|
|
11
|
+
* .AForm_segment:first-child{ margin: 0px 0 25px 0;}
|
|
12
|
+
|
|
13
|
+
.AForm_segmentTitleBar {
|
|
14
|
+
border-bottom: 1px solid #d9d9d9;
|
|
15
|
+
padding: 0 0 10px;
|
|
16
|
+
margin: 0 0 15px;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.AForm_segmentMainTitle {
|
|
20
|
+
font-size: 19px;
|
|
21
|
+
font-weight:bold;
|
|
22
|
+
margin-right: 15px;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.AForm_segmentSubTitle {
|
|
26
|
+
font-size: 10px;
|
|
27
|
+
display: block;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.AForm_segmentTopRight {
|
|
31
|
+
margin-left: 10px;
|
|
32
|
+
float: right;
|
|
33
|
+
display: inline;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
.MEditor_p .AForm_segment{
|
|
38
|
+
padding: 20px;
|
|
39
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import "./Segment.less";
|
|
3
|
+
|
|
4
|
+
interface Prop {
|
|
5
|
+
style?: React.CSSProperties,
|
|
6
|
+
mainTitle?: React.ReactNode,
|
|
7
|
+
/** 副标题 */
|
|
8
|
+
subTitle?: React.ReactNode,
|
|
9
|
+
/** 右上角的按钮 */
|
|
10
|
+
topRight?: React.ReactNode,
|
|
11
|
+
|
|
12
|
+
/** label的name,可以用于query到dom定位 */
|
|
13
|
+
labelName?: string;
|
|
14
|
+
|
|
15
|
+
/** 分栏数 */
|
|
16
|
+
column?: number;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export class Segment extends React.Component<Prop, any> {
|
|
20
|
+
render() {
|
|
21
|
+
return <div style={this.props.style} className="AForm_segment">
|
|
22
|
+
<div className="AForm_segmentTitleBar" key="分段标题" {... { name: this.props.labelName }}>
|
|
23
|
+
{this.props.topRight ? <span className="AForm_segmentTopRight">{this.props.topRight}</span> : undefined}
|
|
24
|
+
{this.props.mainTitle ? <span className="AForm_segmentMainTitle">{this.props.mainTitle}</span> : undefined}
|
|
25
|
+
{this.props.subTitle ? <span className="AForm_segmentSubTitle">{this.props.subTitle}</span> : undefined}
|
|
26
|
+
</div>
|
|
27
|
+
{
|
|
28
|
+
this.props.column > 1 ?
|
|
29
|
+
<div className="AForm" style={{
|
|
30
|
+
display: 'flex',
|
|
31
|
+
flexFlow: 'row wrap',
|
|
32
|
+
alignContent: 'flex-start',
|
|
33
|
+
}}>
|
|
34
|
+
{this.props.children}
|
|
35
|
+
</div>
|
|
36
|
+
: this.props.children
|
|
37
|
+
}
|
|
38
|
+
</div>
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Button, Radio } from 'antd';
|
|
3
|
+
import { LoadingOutlined } from '@ant-design/icons';
|
|
4
|
+
|
|
5
|
+
export type SegmentEditSwitchState =
|
|
6
|
+
/** 只有一个可以按的【编辑】按钮 */
|
|
7
|
+
"readable" |
|
|
8
|
+
/** 只有一个disable的编辑按钮 */
|
|
9
|
+
"disable" |
|
|
10
|
+
|
|
11
|
+
/** 有【取消】和【编辑】按钮,都能按 */
|
|
12
|
+
"editor" |
|
|
13
|
+
/** 有【取消】和【编辑】按钮,但编辑按钮展示为loading且disable */
|
|
14
|
+
"saving"
|
|
15
|
+
|
|
16
|
+
export interface SegmentEditSwitchProps {
|
|
17
|
+
state: SegmentEditSwitchState
|
|
18
|
+
/** edit=true表示按【提交】或者【编辑】按钮 edit=false表示按了【取消】按钮*/
|
|
19
|
+
onClick: (edit:boolean)=>void
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* 展示一个【编辑】按钮,点下以后变成【提交】和【取消】两个按钮
|
|
24
|
+
* @param props
|
|
25
|
+
*/
|
|
26
|
+
export function SegmentEditSwitch(props:SegmentEditSwitchProps): JSX.Element{
|
|
27
|
+
let disable = false, saving = false;
|
|
28
|
+
switch(props.state){
|
|
29
|
+
// @ts-ignore
|
|
30
|
+
case "disable":
|
|
31
|
+
disable = true;
|
|
32
|
+
// eslint-disable-next-line no-fallthrough
|
|
33
|
+
case "readable":
|
|
34
|
+
return <Button value="edit" disabled={disable} onClick={()=>props.onClick(true)}>编辑</Button>
|
|
35
|
+
// @ts-ignore
|
|
36
|
+
case "saving":
|
|
37
|
+
saving = true;
|
|
38
|
+
// eslint-disable-next-line no-fallthrough
|
|
39
|
+
case "editor":
|
|
40
|
+
// @ts-ignore
|
|
41
|
+
return <Radio.Group>
|
|
42
|
+
<Radio.Button disabled={saving} value="save" onClick={()=>props.onClick(true)}>{saving?<LoadingOutlined />:undefined} 提交</Radio.Button>
|
|
43
|
+
<Radio.Button key="c" value="cancel" onClick={()=>props.onClick(false)}>取消</Radio.Button>
|
|
44
|
+
</Radio.Group>
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// 统一移动端的Picker和PC的Selector
|
|
2
|
+
|
|
3
|
+
import { Select } from "antd";
|
|
4
|
+
import { Picker } from "antd-mobile";
|
|
5
|
+
import { PickerData } from "antd-mobile/lib/picker/PropsType";
|
|
6
|
+
import _ from "lodash";
|
|
7
|
+
import React from "react";
|
|
8
|
+
//import { InputHTMLAttributes } from "react";
|
|
9
|
+
import { MUtil } from '../../framework/MUtil';
|
|
10
|
+
|
|
11
|
+
interface Prop { // extends InputHTMLAttributes<HTMLInputElement> {
|
|
12
|
+
data: string;
|
|
13
|
+
options: PickerData[],
|
|
14
|
+
onChange: (newValue?:string)=>void;
|
|
15
|
+
onBlur?:()=>void;
|
|
16
|
+
|
|
17
|
+
// 是否允许开放选项
|
|
18
|
+
openLabel?: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export class SelectBox extends React.Component<Prop, any> {
|
|
22
|
+
render(){
|
|
23
|
+
if(MUtil.phoneLike()) {
|
|
24
|
+
const looked = this.props.options.find(o=>o.value === this.props.data);
|
|
25
|
+
const backfillClass = looked ? "backfill" : "backfill_empty";
|
|
26
|
+
|
|
27
|
+
return <Picker extra="请选择(可选)"
|
|
28
|
+
cols={1}
|
|
29
|
+
data={this.props.options}
|
|
30
|
+
value={[this.props.data]}
|
|
31
|
+
onOk={e => this.props.onChange(_.last(e))}
|
|
32
|
+
onDismiss={() => {
|
|
33
|
+
if(this.props.onBlur) { this.props.onBlur() }
|
|
34
|
+
}}>
|
|
35
|
+
<div className={backfillClass}>{looked?.label ?? "点击选择"}</div>
|
|
36
|
+
</Picker>
|
|
37
|
+
} else {
|
|
38
|
+
return <Select defaultValue={this.props.data}>
|
|
39
|
+
{this.props.options.map(o=><Select.Option value={o.value}>{o.label ?? o.value}</Select.Option>)}
|
|
40
|
+
</Select>;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
.paperInputBox {
|
|
2
|
+
border-style: none none solid none;
|
|
3
|
+
border-bottom: 1px solid #d9d9d9;
|
|
4
|
+
background: white;
|
|
5
|
+
border-radius: 0;
|
|
6
|
+
font-size: 16px;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.paperInputBox:disabled {
|
|
10
|
+
border-bottom: 1px dotted #f0f0f0;
|
|
11
|
+
color: #d9d9d9;
|
|
12
|
+
background: white;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.antMiddleInputBox {
|
|
16
|
+
font-size: 16px;
|
|
17
|
+
|
|
18
|
+
/** 以下都是ant的 */
|
|
19
|
+
border: 1px solid #d9d9d9;
|
|
20
|
+
border-radius: 2px;
|
|
21
|
+
background-color: #fff;
|
|
22
|
+
box-sizing: border-box;
|
|
23
|
+
margin: 0;
|
|
24
|
+
padding: 0;
|
|
25
|
+
font-variant: tabular-nums;
|
|
26
|
+
list-style: none;
|
|
27
|
+
-webkit-font-feature-settings: 'tnum';
|
|
28
|
+
font-feature-settings: 'tnum', "tnum";
|
|
29
|
+
position: relative;
|
|
30
|
+
display: inline-block;
|
|
31
|
+
padding: 4px 11px;
|
|
32
|
+
color: rgba(0, 0, 0, 0.85);
|
|
33
|
+
font-size: 14px;
|
|
34
|
+
line-height: 1.5715;
|
|
35
|
+
-webkit-transition: all 0.3s;
|
|
36
|
+
transition: all 0.3s;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.antMiddleInputBox:disabled {
|
|
40
|
+
border: 1px dotted #f0f0f0;
|
|
41
|
+
color: #d9d9d9;
|
|
42
|
+
background: white;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.antMiddleInputBox::placeholder {
|
|
46
|
+
color: #d9d9d9;
|
|
47
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { InputHTMLAttributes } from "react";
|
|
3
|
+
import { assembly } from "../../framework/Assembly";
|
|
4
|
+
import "./UnderlineInputBox.less";
|
|
5
|
+
|
|
6
|
+
export class UnderlineInputBox extends React.Component<InputHTMLAttributes<HTMLInputElement>, any> {
|
|
7
|
+
render(){
|
|
8
|
+
return <input className={assembly.theme.themeName + "InputBox"} {...this.props}></input>
|
|
9
|
+
}
|
|
10
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare const Ajax: {
|
|
2
|
+
req(method: 'GET' | 'POST' | 'PUT', apiPath: string, urlArgs: any, body: any, headers: any, prompt?: any): Promise<any>;
|
|
3
|
+
post(apiPath: string, data: any, succMsg?: string): Promise<any>;
|
|
4
|
+
get(apiPath: string, data?: object): Promise<any>;
|
|
5
|
+
};
|