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,119 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import _ from "lodash";
|
|
3
|
+
import { BaseViewer } from "../../BaseViewer";
|
|
4
|
+
import { Upload, message, Button } from 'antd';
|
|
5
|
+
import { MUtil } from "../../..";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* antd 的上传组件 https://ant.design/components/upload-cn/
|
|
9
|
+
* {
|
|
10
|
+
"type": "array", arrayMember: {
|
|
11
|
+
type: 'object',
|
|
12
|
+
readable: 'A',
|
|
13
|
+
style: {display: 'block'},
|
|
14
|
+
a: {
|
|
15
|
+
urlExpr: "value.url",
|
|
16
|
+
labelExpr: "value.name",
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
"editor": "AUpload", "name": "upload_sec", "label": "文件上传_HP_SECOBJ",
|
|
20
|
+
ossFile: {
|
|
21
|
+
type: "HP_SECOBJ",
|
|
22
|
+
arguments: { genName: true, ossKeyPath: "course/" }
|
|
23
|
+
}, props: {
|
|
24
|
+
beforeUpload(file) {
|
|
25
|
+
const isLt200M = file.size / 1024 / 1024 < 200;
|
|
26
|
+
if (!isLt200M) {
|
|
27
|
+
message.error('File must smaller than 200MB!');
|
|
28
|
+
}
|
|
29
|
+
return isLt200M;
|
|
30
|
+
},
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
*/
|
|
34
|
+
export class AUpload extends BaseViewer {
|
|
35
|
+
loading?: number = undefined;
|
|
36
|
+
|
|
37
|
+
element() {
|
|
38
|
+
const p = this.props.schema.props ?? {};
|
|
39
|
+
const { type } = this.props.schema.ossFile
|
|
40
|
+
let a = {
|
|
41
|
+
name: "file",
|
|
42
|
+
data: (file: any) => (this.props.schema.ossFile?.arguments),
|
|
43
|
+
}
|
|
44
|
+
if (type === 'HP_GO') {
|
|
45
|
+
Object.assign(a, {
|
|
46
|
+
action: "/academy/go/upload",
|
|
47
|
+
})
|
|
48
|
+
} else if (type === 'HP_SECOBJ') {
|
|
49
|
+
Object.assign(a, {
|
|
50
|
+
action: "/academy/oss/secObject",
|
|
51
|
+
})
|
|
52
|
+
} else {
|
|
53
|
+
return MUtil.error(`ossFile.type=${type}无效`, this.props.schema)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const prevData = super.getValue();
|
|
57
|
+
if (prevData) {
|
|
58
|
+
Object.assign(a, {
|
|
59
|
+
defaultFileList: prevData.map((item, index) => {
|
|
60
|
+
item.uid = index
|
|
61
|
+
return item
|
|
62
|
+
})
|
|
63
|
+
})
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const props = {
|
|
67
|
+
...a,
|
|
68
|
+
onChange: info => {
|
|
69
|
+
if (p.onChange) p.onChange(info)
|
|
70
|
+
console.log(info)
|
|
71
|
+
const { file, fileList = [] } = info
|
|
72
|
+
|
|
73
|
+
switch (file.status) {
|
|
74
|
+
case 'uploading':
|
|
75
|
+
this.loading = Math.floor(file.percent || 0);
|
|
76
|
+
this.setState({});
|
|
77
|
+
break;
|
|
78
|
+
case "done":
|
|
79
|
+
case 'success':
|
|
80
|
+
this.loading = undefined;
|
|
81
|
+
if (type === 'HP_GO') {
|
|
82
|
+
fileList[fileList.length - 1] = {
|
|
83
|
+
name: file.name,
|
|
84
|
+
url: file.response.content.url
|
|
85
|
+
}
|
|
86
|
+
super.changeValue(fileList);
|
|
87
|
+
} else if (type === 'HP_SECOBJ') {
|
|
88
|
+
const newValue = `/academy/oss/secObject/${file.response.data?.ossKey}`;
|
|
89
|
+
if (file.response.errorCode) {
|
|
90
|
+
message.error(file.response.message);
|
|
91
|
+
} else {
|
|
92
|
+
fileList[fileList.length - 1] = {
|
|
93
|
+
name: file.name,
|
|
94
|
+
url: newValue
|
|
95
|
+
}
|
|
96
|
+
super.changeValue(fileList);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
break;
|
|
100
|
+
case 'error':
|
|
101
|
+
this.loading = undefined;
|
|
102
|
+
this.setState({});
|
|
103
|
+
message.error("上传失败了");
|
|
104
|
+
break;
|
|
105
|
+
case 'removed':
|
|
106
|
+
super.changeValue(fileList);
|
|
107
|
+
break;
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
...p,
|
|
111
|
+
}
|
|
112
|
+
return (
|
|
113
|
+
<Upload key={this.props.path}
|
|
114
|
+
disabled={this.loading !== undefined} {...props}>
|
|
115
|
+
<Button disabled={this.loading !== undefined}>点击上传</Button>
|
|
116
|
+
</Upload>
|
|
117
|
+
)
|
|
118
|
+
}
|
|
119
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
.m3-nps-wrapper {
|
|
2
|
+
.m3-nps-tip {
|
|
3
|
+
overflow: hidden;
|
|
4
|
+
span:first-child {
|
|
5
|
+
float: left;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
span:last-child {
|
|
9
|
+
float: right;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.ant-rate {
|
|
14
|
+
display: flex !important;
|
|
15
|
+
width : 100%;
|
|
16
|
+
|
|
17
|
+
.ant-rate-star {
|
|
18
|
+
flex: 1
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import _ from "lodash";
|
|
3
|
+
import { BaseViewer } from "../../BaseViewer";
|
|
4
|
+
import { Rate } from 'antd';
|
|
5
|
+
import "./NPS.less";
|
|
6
|
+
import { MProp } from "../../../framework/Schema";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* NPS 打分组件,基于 antd 的评分组件 https://ant.design/components/rate-cn/
|
|
10
|
+
* 定义示例:{ editor: 'NPS', name: 'possibility', label: "您向朋友或同事推荐本堂课程的可能性有多大?", required: true },
|
|
11
|
+
*/
|
|
12
|
+
export class NPS extends BaseViewer {
|
|
13
|
+
value: string;
|
|
14
|
+
|
|
15
|
+
constructor(p:MProp){
|
|
16
|
+
super(p);
|
|
17
|
+
// rate 组件从 1 开始计算
|
|
18
|
+
this.value = super.getValue() == -1 ? null : super.getValue() + 1;
|
|
19
|
+
}
|
|
20
|
+
element() {
|
|
21
|
+
const p = this.props.schema.props ?? {};
|
|
22
|
+
const props = {
|
|
23
|
+
count: 11,
|
|
24
|
+
character: ({ index }) => index,
|
|
25
|
+
defaultValue: this.value,
|
|
26
|
+
onChange: (value, label, extra)=>{
|
|
27
|
+
// nps 从 0 开始计算
|
|
28
|
+
let v = value - 1
|
|
29
|
+
if (v == -1) v = null
|
|
30
|
+
if (p.onChange) p.onChange(v, label, extra)
|
|
31
|
+
super.changeValue(v);
|
|
32
|
+
},
|
|
33
|
+
onBlur: () =>{
|
|
34
|
+
if (p.onBlur) p.onBlur()
|
|
35
|
+
super.changeValue(super.getValue())
|
|
36
|
+
},
|
|
37
|
+
...p,
|
|
38
|
+
}
|
|
39
|
+
return <div className="m3-nps-wrapper">
|
|
40
|
+
<div className="m3-nps-tip">
|
|
41
|
+
<span>{p.leftTip ?? '不可能'}</span>
|
|
42
|
+
<span>{p.rightTip ?? '极有可能'}</span>
|
|
43
|
+
</div>
|
|
44
|
+
<Rate className="m3-nps-rate" {...props} />
|
|
45
|
+
</div>
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { Button, List } from "antd";
|
|
4
|
+
import { CloseCircleFilled } from '@ant-design/icons';
|
|
5
|
+
import "./AArray.less";
|
|
6
|
+
import { BaseViewer } from '../../BaseViewer';
|
|
7
|
+
import { MFieldSchemaAnonymity } from "../../../framework/Schema";
|
|
8
|
+
import { MFieldViewer } from "../../../framework/MFieldViewer";
|
|
9
|
+
import { MUtil } from "../../../framework/MUtil";
|
|
10
|
+
import { assembly } from "../../../framework/Assembly";
|
|
11
|
+
|
|
12
|
+
const NO_REMOVE_BTN: {[type:string]:boolean} = {"string":true};
|
|
13
|
+
|
|
14
|
+
/** @deprecated Ant的list,也不好看 */
|
|
15
|
+
export class AArray extends BaseViewer {
|
|
16
|
+
_removeClicked(index: number) {
|
|
17
|
+
const value:any[] = super.getValue() ?? [];
|
|
18
|
+
let nv:any[]|undefined = value.filter((v,idx)=>idx !== index);
|
|
19
|
+
if(nv.length === 0) {
|
|
20
|
+
nv = undefined;
|
|
21
|
+
}
|
|
22
|
+
super.changeValueEx(nv, true, true);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** 不同的数组成员类型,有不同的渲染方式 */
|
|
26
|
+
_renderItem(memberSchema:MFieldSchemaAnonymity, itemData: any, index: number, removeButton?: boolean){
|
|
27
|
+
return [
|
|
28
|
+
removeButton && !NO_REMOVE_BTN[memberSchema.type]
|
|
29
|
+
? <CloseCircleFilled key={"closebtn:" + index} style={{marginLeft: 10}} size={32} onClick={()=>{ // FIXME 删除按钮都移到元素上
|
|
30
|
+
this._removeClicked(index);
|
|
31
|
+
}}/>
|
|
32
|
+
:undefined,
|
|
33
|
+
|
|
34
|
+
<div key={index + ":" + this.state.ctrlVersion} className="AArray_item">
|
|
35
|
+
<MFieldViewer morph={this.props.morph} key={index} schema={memberSchema} database={this.props.database} path={this.props.path + "[" + index + "]"} afterChange={(path:string, newVal:any, blur:boolean) => {
|
|
36
|
+
if(newVal === undefined) { // 删除
|
|
37
|
+
this._removeClicked(index);
|
|
38
|
+
} else { // 修改
|
|
39
|
+
this.props.afterChange?.(path, newVal, blur);
|
|
40
|
+
this.setState({}); // 子元素变化,可能影响校验状态,所以刷新下
|
|
41
|
+
}
|
|
42
|
+
}} parent={this.props.schema} forceValid={this.props.forceValid} removeButton={removeButton}/>
|
|
43
|
+
</div>
|
|
44
|
+
]
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
element() {
|
|
48
|
+
const schema = this.props.schema;
|
|
49
|
+
const memberSchema = schema.arrayMember;
|
|
50
|
+
if(!memberSchema) {
|
|
51
|
+
return MUtil.error(`缺少arrayMember属性`, schema);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
if(memberSchema.type === "object" || memberSchema.type==="string"){
|
|
56
|
+
const data:any[] = super.getValue() ?? [];
|
|
57
|
+
const isMax = data.length >= (schema.max ?? Number.MAX_VALUE);
|
|
58
|
+
const min = schema.min ?? 0;
|
|
59
|
+
const max = schema.max ?? Number.MAX_VALUE;
|
|
60
|
+
|
|
61
|
+
if(memberSchema.type === "string") {
|
|
62
|
+
const n = Math.max(min, data.length);
|
|
63
|
+
const removeButton = max === min ? undefined : n > min;
|
|
64
|
+
let arr = [];
|
|
65
|
+
for(let i = 0; i < n; i ++) {
|
|
66
|
+
arr.push(this._renderItem(memberSchema, data[i], i, removeButton));
|
|
67
|
+
}
|
|
68
|
+
if(n < max) {
|
|
69
|
+
arr.push(<div key=":add" className="AArray_addBtn">
|
|
70
|
+
<Button key=":加一项按钮" onClick={() => {
|
|
71
|
+
const d = super.getValue() ?? [];
|
|
72
|
+
while(d.length < n+1){
|
|
73
|
+
d.push(undefined);
|
|
74
|
+
}
|
|
75
|
+
super.changeValue(d);
|
|
76
|
+
}}>{schema.arrayAddLabel ?? "+"}</Button>
|
|
77
|
+
</div>)
|
|
78
|
+
}
|
|
79
|
+
return arr;
|
|
80
|
+
} else {
|
|
81
|
+
return <List
|
|
82
|
+
bordered
|
|
83
|
+
footer={<Button disabled={isMax} key=":加一项按钮" onClick={()=>{
|
|
84
|
+
data.push(
|
|
85
|
+
assembly.types[schema.arrayMember.type]?.createDefaultValue(assembly, schema.arrayMember)
|
|
86
|
+
);
|
|
87
|
+
super.changeValue(data);
|
|
88
|
+
}} >增加一项</Button>}
|
|
89
|
+
dataSource={data}
|
|
90
|
+
renderItem={ (valueItem,index) => {
|
|
91
|
+
let noRemoveBtn = NO_REMOVE_BTN[memberSchema.type] // 为了美观,某些数组元素不需要关闭按钮
|
|
92
|
+
if(schema.min && schema.min === schema.max){ // 最大项数=最小项数时,不要删除按钮
|
|
93
|
+
noRemoveBtn = true;
|
|
94
|
+
}
|
|
95
|
+
return <List.Item key={index}>
|
|
96
|
+
{this._renderItem(memberSchema, valueItem, index, !noRemoveBtn)}
|
|
97
|
+
</List.Item>
|
|
98
|
+
}}/>;
|
|
99
|
+
}
|
|
100
|
+
} else {
|
|
101
|
+
return MUtil.error(`成员类型${memberSchema.type},无法编辑`, schema);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
|
|
2
|
+
import _ from "lodash";
|
|
3
|
+
import { Button, message, Popconfirm } from 'antd';
|
|
4
|
+
import { CaretDownOutlined, CaretUpOutlined, CloseOutlined } from '@ant-design/icons';
|
|
5
|
+
import { BaseViewer } from '../../BaseViewer';
|
|
6
|
+
import { MUtil } from "../../../framework/MUtil";
|
|
7
|
+
import { MFieldViewer } from "../../../framework/MFieldViewer";
|
|
8
|
+
import React from "react";
|
|
9
|
+
import { assembly } from '../../../framework/Assembly';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* 数据表格
|
|
13
|
+
* 数据是这样的数组:
|
|
14
|
+
* [
|
|
15
|
+
* {from:"1606997544611",to:"", tillNow:true, company:"阿里巴巴", position:"CEO"},
|
|
16
|
+
* {from:"1606997544611",to:"", tillNow:true, company:"阿里巴巴", position:"CEO"},
|
|
17
|
+
* ]
|
|
18
|
+
*/
|
|
19
|
+
export class AArrayGrid extends BaseViewer {
|
|
20
|
+
element() {
|
|
21
|
+
const schema = this.props.schema;
|
|
22
|
+
|
|
23
|
+
if(!schema.arrayMember) {
|
|
24
|
+
return MUtil.error("arrayMember未定义", schema);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const members = schema.arrayMember.objectFields // 成员是复杂结构
|
|
28
|
+
|| [{name:undefined, ...schema.arrayMember}]; // 成员是简单结构
|
|
29
|
+
// if(!members) {
|
|
30
|
+
// return MUtil.error("AArrayGrid只适用于对象数组", schema);
|
|
31
|
+
// }
|
|
32
|
+
|
|
33
|
+
let data = super.getValue();
|
|
34
|
+
if(!_.isArray(data)){ // 只接受数组
|
|
35
|
+
data = [];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const cols = 1 + members.length;
|
|
39
|
+
|
|
40
|
+
//let headTh = [<td key=":操作栏" width="40px" align="center" style={{backgroundImage: "linear-gradient(to bottom left, transparent calc(50% - 1px), #d3d3d3, transparent calc(50% + 1px))"}}></td>]
|
|
41
|
+
|
|
42
|
+
let rows = [];
|
|
43
|
+
for(let idx = 0; idx < data.length; idx ++){
|
|
44
|
+
const i = idx;
|
|
45
|
+
rows.push(<tr key={i}>
|
|
46
|
+
{/* 各个字段 */}
|
|
47
|
+
{
|
|
48
|
+
members.map((f,idx)=>
|
|
49
|
+
<td key={f.name + idx}>
|
|
50
|
+
<MFieldViewer key={this.state.ctrlVersion + "." + f.name} parent={schema} morph={this.props.morph} schema={f} database={data} path={MUtil.jsonPath("[" + i + "]", f.name)} hideBorder={true} afterChange={(path, v, final):void => {
|
|
51
|
+
super.changeValueEx(data, false, final);
|
|
52
|
+
}}/>
|
|
53
|
+
</td>)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
{/* 操作栏 */}
|
|
57
|
+
<td key=":option" align="center">
|
|
58
|
+
<CaretUpOutlined style={{display: "block"}} hidden={data.length <= 1} onClick={()=>{
|
|
59
|
+
if(i === 0){
|
|
60
|
+
message.warn("已经到顶了");
|
|
61
|
+
} else {
|
|
62
|
+
const prev = data[i-1];
|
|
63
|
+
data[i-1] = data[i];
|
|
64
|
+
data[i] = prev;
|
|
65
|
+
super.changeValueEx(data, true, true);
|
|
66
|
+
}
|
|
67
|
+
}}/>
|
|
68
|
+
<Popconfirm
|
|
69
|
+
title="确定要删除吗这一项吗?"
|
|
70
|
+
onConfirm={()=>{
|
|
71
|
+
data.splice(i,1);
|
|
72
|
+
super.changeValueEx(data, true, true);
|
|
73
|
+
}}
|
|
74
|
+
okText="删除"
|
|
75
|
+
cancelText="不删">
|
|
76
|
+
<CloseOutlined style={{display: "block"}} hidden={ data.length == (schema.min ?? 0) }/>
|
|
77
|
+
</Popconfirm>
|
|
78
|
+
<CaretDownOutlined style={{display: "block"}} hidden={data.length <= 1} onClick={()=>{
|
|
79
|
+
if(i === data.length - 1){
|
|
80
|
+
message.warn("已经到底了");
|
|
81
|
+
} else {
|
|
82
|
+
const prev = data[i+1];
|
|
83
|
+
data[i+1] = data[i];
|
|
84
|
+
data[i] = prev;
|
|
85
|
+
super.changeValueEx(data, true, true);
|
|
86
|
+
}
|
|
87
|
+
}}/>
|
|
88
|
+
</td>
|
|
89
|
+
</tr>);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const isMax = data.length >= (schema.max ?? Number.MAX_VALUE);
|
|
93
|
+
return (
|
|
94
|
+
<table key={this.props.path} className="AExperience M3_table" style={{width: "100%"}}><tbody>
|
|
95
|
+
<tr key=":header">
|
|
96
|
+
{members.map((f,i)=><th key={f.name + i + ":first"}>{f.label ?? f.name}</th>)}
|
|
97
|
+
<td key=":操作栏" width="40px" align="center"></td>
|
|
98
|
+
</tr>
|
|
99
|
+
{rows}
|
|
100
|
+
<tr key=":footer">
|
|
101
|
+
{/* 增加按钮 */}
|
|
102
|
+
<th key=":add" colSpan={cols}>
|
|
103
|
+
<Button disabled={isMax} key=":add" onClick={()=>{
|
|
104
|
+
data.push(
|
|
105
|
+
assembly.types[schema.arrayMember.type]?.createDefaultValue(assembly, schema.arrayMember)
|
|
106
|
+
);
|
|
107
|
+
super.changeValue(data);
|
|
108
|
+
}}>增加一项</Button>
|
|
109
|
+
{this.props.extra}
|
|
110
|
+
</th>
|
|
111
|
+
</tr>
|
|
112
|
+
</tbody></table>
|
|
113
|
+
)
|
|
114
|
+
}
|
|
115
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { Input } from 'antd';
|
|
4
|
+
import { AGB2260 } from "../basic/AGB2260";
|
|
5
|
+
import './ACnAddress.less';
|
|
6
|
+
import { AInputBox } from "../basic/AInputBox";
|
|
7
|
+
import _ from "lodash";
|
|
8
|
+
import { BaseViewer } from '../../BaseViewer';
|
|
9
|
+
import { MUtil } from '../../../framework/MUtil';
|
|
10
|
+
import { MGB2260Type } from '../../../types/MGB2260Type';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* 国内地址,用以编辑省市区和详细地址
|
|
14
|
+
* 数据类似{province:"陕西省",city:"西安市",district:"长安区", address:"紫薇田园都市A区", code:610116}
|
|
15
|
+
*/
|
|
16
|
+
export class ACnAddress extends BaseViewer {
|
|
17
|
+
element() {
|
|
18
|
+
const gb2260 = super.getValue() ?? {};
|
|
19
|
+
// 当传入的 database,没有 code,但有 province 时,反查 code
|
|
20
|
+
if (gb2260.province && !gb2260.code) {
|
|
21
|
+
for (const p of MGB2260Type.gb2260) {
|
|
22
|
+
if (p.label === gb2260.province) {
|
|
23
|
+
if (gb2260.city && p.children) {
|
|
24
|
+
for (const c of p.children) {
|
|
25
|
+
if (c.label === gb2260.city) {
|
|
26
|
+
if (gb2260.district && c.children) {
|
|
27
|
+
for (const d of c.children) {
|
|
28
|
+
if (d.label === gb2260.district) {
|
|
29
|
+
gb2260.code = d.value
|
|
30
|
+
break
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
} else {
|
|
34
|
+
gb2260.code = c.value
|
|
35
|
+
break
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
} else {
|
|
40
|
+
gb2260.code = p.value
|
|
41
|
+
break
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return <Input.Group compact key={this.props.path} className={MUtil.phoneLike() ? "ACnAddress_p" : "ACnAddress"}>
|
|
47
|
+
<AGB2260 morph="editor" schema={{ type: "gb2260", placeholder: super.getPlaceholder(0) }} database={gb2260} path={"code"} disable={this.props.disable} afterChange={(path, code, final):void => {
|
|
48
|
+
const info = MGB2260Type.lookup(code);
|
|
49
|
+
if (info) {
|
|
50
|
+
_.set(gb2260, "province", info.label[0]);
|
|
51
|
+
_.set(gb2260, "city", info.label[1]);
|
|
52
|
+
_.set(gb2260, "district", info.label[2]);
|
|
53
|
+
}
|
|
54
|
+
super.changeValueEx(gb2260, false, final);
|
|
55
|
+
}} />
|
|
56
|
+
<AInputBox morph="editor" schema={{ type: "string", placeholder: super.getPlaceholder(1) }} database={this.props.database} path={this.props.path + ".address"} disable={this.props.disable} afterChange={(path, v, final)=>{
|
|
57
|
+
super.changeValueEx(gb2260, false, final);
|
|
58
|
+
}} />
|
|
59
|
+
</Input.Group>
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { BaseViewer } from "../../BaseViewer";
|
|
4
|
+
import { MUtil } from "../../../framework/MUtil";
|
|
5
|
+
import { assembly } from '../../../framework/Assembly';
|
|
6
|
+
import { Button, Modal } from "antd";
|
|
7
|
+
import { AForm } from "./AForm";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* 展示为一个按钮,点击后弹出对话框编辑
|
|
11
|
+
*/
|
|
12
|
+
export class ADialogForm extends BaseViewer {
|
|
13
|
+
pop:boolean;
|
|
14
|
+
|
|
15
|
+
constructor(p){
|
|
16
|
+
super(p);
|
|
17
|
+
this.close = this.close.bind(this);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
close(){
|
|
21
|
+
this.pop = false;
|
|
22
|
+
this.setState({})
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
element() {
|
|
26
|
+
const value = super.getValue();
|
|
27
|
+
const readable = assembly.toReadable(this.props.schema, value, super.getParentValue());
|
|
28
|
+
return <>
|
|
29
|
+
<Modal
|
|
30
|
+
style={this.props.style}
|
|
31
|
+
closable={false}
|
|
32
|
+
keyboard={true}
|
|
33
|
+
width="70%"
|
|
34
|
+
className={MUtil.phoneLike() ? "MEditor_p" : "MEditor"} maskClosable={false} title="编辑" visible={this.pop}
|
|
35
|
+
footer={<Button onClick={this.close}>确定</Button>}>
|
|
36
|
+
<AForm {...this.props}/>
|
|
37
|
+
</Modal>
|
|
38
|
+
{readable}
|
|
39
|
+
<Button style={{marginLeft: 15}} onClick={()=>{
|
|
40
|
+
this.pop = true;
|
|
41
|
+
this.setState({});
|
|
42
|
+
}}>编辑</Button>
|
|
43
|
+
</>
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
import _ from "lodash";
|
|
5
|
+
import { AArrayGrid } from './AArrayGrid';
|
|
6
|
+
import React from 'react';
|
|
7
|
+
import Button from 'antd/lib/button';
|
|
8
|
+
import { BaseViewer } from '../../BaseViewer';
|
|
9
|
+
import { MUtil } from "../../../framework/MUtil";
|
|
10
|
+
import { MFieldSchemaAnonymity } from "../../../framework/Schema";
|
|
11
|
+
|
|
12
|
+
const RANGE_FNAME = "_range";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* 经历,可以用于教育经历、工作经历等
|
|
16
|
+
* 数据类似以下数组:
|
|
17
|
+
* [
|
|
18
|
+
* {from:"1606997544611",to:"", tillNow:true, company:"阿里巴巴", position:"CEO"},
|
|
19
|
+
* {from:"1606997544611",to:"", tillNow:true, company:"阿里巴巴", position:"CEO"},
|
|
20
|
+
* ...
|
|
21
|
+
* ]
|
|
22
|
+
*
|
|
23
|
+
*/
|
|
24
|
+
export class AExperience extends BaseViewer {
|
|
25
|
+
_submitData(arrayGridData: any, sort:boolean){
|
|
26
|
+
// 修改后,把数据转换回来再提交
|
|
27
|
+
let experienceData = arrayGridData.map((e: any) => {
|
|
28
|
+
let item = {
|
|
29
|
+
...e,
|
|
30
|
+
from: _.get(e, RANGE_FNAME + "[0]"),
|
|
31
|
+
to: _.get(e, RANGE_FNAME + "[1]"),
|
|
32
|
+
tillNow: _.get(e, RANGE_FNAME + "[2]"),
|
|
33
|
+
}
|
|
34
|
+
delete item[RANGE_FNAME];
|
|
35
|
+
return item;
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
if(sort){
|
|
39
|
+
experienceData.sort((a:any,b:any) => a.from - b.from)
|
|
40
|
+
}
|
|
41
|
+
super.changeValueEx(experienceData, !!sort, true);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
element() {
|
|
45
|
+
if(!this.props.schema.experience) {
|
|
46
|
+
return MUtil.error("experience未定义", this.props.schema);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// 复用AArrayGrid,下面构造个schema给它
|
|
50
|
+
const arrayGridSchema:MFieldSchemaAnonymity = {
|
|
51
|
+
type:"array",
|
|
52
|
+
dataFormat: this.props.schema.dataFormat,
|
|
53
|
+
arrayMember:{
|
|
54
|
+
type:"object",
|
|
55
|
+
objectFields:[
|
|
56
|
+
{name: RANGE_FNAME, type: "dateRange", label:"起止时间", dateRange: this.props.schema.dateRange },
|
|
57
|
+
...this.props.schema.experience.members
|
|
58
|
+
]
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
// 转换一套数据给arrayGrid
|
|
62
|
+
let data = super.getValue();
|
|
63
|
+
if(!_.isArray(data)){ // 只接受数组
|
|
64
|
+
data = [];
|
|
65
|
+
}
|
|
66
|
+
const arrayGridData = data.map((e: any)=>{
|
|
67
|
+
let a = _.clone(e);
|
|
68
|
+
a[RANGE_FNAME] = [e.from, e.to, e.tillNow]
|
|
69
|
+
delete a.from;
|
|
70
|
+
delete a.to;
|
|
71
|
+
delete a.tillNow;
|
|
72
|
+
return a
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
return <AArrayGrid key={this.state.ctrlVersion} schema={arrayGridSchema} database={arrayGridData} path=""
|
|
76
|
+
morph={this.props.morph}
|
|
77
|
+
extra={<Button onClick={()=>{
|
|
78
|
+
this._submitData(arrayGridData, true);
|
|
79
|
+
}} >自动排序</Button>}
|
|
80
|
+
afterChange={(path,v, blur) => {
|
|
81
|
+
this._submitData(arrayGridData, false);
|
|
82
|
+
}
|
|
83
|
+
}/>;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
/* 背景 */
|
|
4
|
+
/* .AForm::after{
|
|
5
|
+
content: "";
|
|
6
|
+
background: url(//hupan-web.oss-cn-hangzhou.aliyuncs.com/static/image/headImg.jpg);
|
|
7
|
+
opacity: 0.2;
|
|
8
|
+
top: 0;
|
|
9
|
+
left: 0;
|
|
10
|
+
bottom: 0;
|
|
11
|
+
right: 0;
|
|
12
|
+
position: absolute;
|
|
13
|
+
z-index: -1;
|
|
14
|
+
} */
|
|
15
|
+
|
|
16
|
+
.AForm .AForm { /** 嵌套的子表单 */
|
|
17
|
+
/* box-shadow: 0 0 10px #d0cfcf; */
|
|
18
|
+
border: 1px solid #d9d9d9;
|
|
19
|
+
padding: 15px;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.AForm .ItemLabel {
|
|
23
|
+
font-weight: bold;
|
|
24
|
+
margin-bottom: 5px;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.AForm .Item {
|
|
28
|
+
margin-bottom:20px;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.MEditor, .MEditor_p {
|
|
32
|
+
.ant-form-item-explain.ant-form-item-explain-error {
|
|
33
|
+
color: #ff4d4f
|
|
34
|
+
}
|
|
35
|
+
}
|