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.
Files changed (168) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +37 -0
  3. package/dist/m3.css +310 -0
  4. package/dist/m3.js +1 -0
  5. package/es/m3.css +310 -0
  6. package/es/m3.js +20919 -0
  7. package/lib/m3.css +310 -0
  8. package/lib/m3.js +20959 -0
  9. package/package.json +132 -0
  10. package/src/.DS_Store +0 -0
  11. package/src/framework/Ajax.ts +96 -0
  12. package/src/framework/Assembly.tsx +165 -0
  13. package/src/framework/Init.tsx +196 -0
  14. package/src/framework/M3.tsx +94 -0
  15. package/src/framework/MContext.ts +15 -0
  16. package/src/framework/MFieldViewer.tsx +32 -0
  17. package/src/framework/MUtil.tsx +653 -0
  18. package/src/framework/MViewer.less +128 -0
  19. package/src/framework/MViewer.tsx +180 -0
  20. package/src/framework/MViewerDebug.tsx +95 -0
  21. package/src/framework/Persistant.ts +90 -0
  22. package/src/framework/Schema.ts +386 -0
  23. package/src/framework/SchemaFunc.ts +30 -0
  24. package/src/framework/Validator.ts +160 -0
  25. package/src/framework/editorMap.ts +109 -0
  26. package/src/index.ts +33 -0
  27. package/src/types/MArrayType.ts +73 -0
  28. package/src/types/MCascadeType.ts +35 -0
  29. package/src/types/MCnAddressType.ts +54 -0
  30. package/src/types/MDateRangeType.ts +52 -0
  31. package/src/types/MDateTimeType.ts +53 -0
  32. package/src/types/MDecorationType.ts +6 -0
  33. package/src/types/MEnumType.ts +65 -0
  34. package/src/types/MExperienceType.ts +81 -0
  35. package/src/types/MFloatType.ts +10 -0
  36. package/src/types/MGB2260Type.ts +56 -0
  37. package/src/types/MIntDiffType.ts +6 -0
  38. package/src/types/MIntType.ts +44 -0
  39. package/src/types/MKvSetType.ts +50 -0
  40. package/src/types/MMatrixType.ts +52 -0
  41. package/src/types/MObjectType.ts +89 -0
  42. package/src/types/MSetType.ts +220 -0
  43. package/src/types/MStringType.ts +27 -0
  44. package/src/types/MTelType.ts +14 -0
  45. package/src/types/MType.ts +77 -0
  46. package/src/types/MVLPairType.ts +35 -0
  47. package/src/types/gb2260.json +1 -0
  48. package/src/ui/BaseViewer.tsx +110 -0
  49. package/src/ui/editor/.DS_Store +0 -0
  50. package/src/ui/editor/basic/.DS_Store +0 -0
  51. package/src/ui/editor/basic/ACascadePicker.tsx +114 -0
  52. package/src/ui/editor/basic/ACheckBox.tsx +104 -0
  53. package/src/ui/editor/basic/ADatetimePicker.tsx +76 -0
  54. package/src/ui/editor/basic/AGB2260.tsx +52 -0
  55. package/src/ui/editor/basic/AInputBox.tsx +59 -0
  56. package/src/ui/editor/basic/AIntBox.tsx +39 -0
  57. package/src/ui/editor/basic/AKvSet.less +9 -0
  58. package/src/ui/editor/basic/AKvSet.tsx +90 -0
  59. package/src/ui/editor/basic/ARadio.tsx +86 -0
  60. package/src/ui/editor/basic/ARangePicker.tsx +129 -0
  61. package/src/ui/editor/basic/ARate.less +8 -0
  62. package/src/ui/editor/basic/ARate.tsx +37 -0
  63. package/src/ui/editor/basic/ARemoteSelector.tsx +116 -0
  64. package/src/ui/editor/basic/ASelector.tsx +88 -0
  65. package/src/ui/editor/basic/ASetSelector.tsx +65 -0
  66. package/src/ui/editor/basic/ASpecInputBox.tsx +20 -0
  67. package/src/ui/editor/basic/ATreeSelect.tsx +41 -0
  68. package/src/ui/editor/basic/AUpload.tsx +119 -0
  69. package/src/ui/editor/basic/NPS.less +21 -0
  70. package/src/ui/editor/basic/NPS.tsx +47 -0
  71. package/src/ui/editor/complex/AArray.less +10 -0
  72. package/src/ui/editor/complex/AArray.tsx +104 -0
  73. package/src/ui/editor/complex/AArrayGrid.tsx +115 -0
  74. package/src/ui/editor/complex/ACnAddress.less +15 -0
  75. package/src/ui/editor/complex/ACnAddress.tsx +61 -0
  76. package/src/ui/editor/complex/ADialogForm.tsx +45 -0
  77. package/src/ui/editor/complex/AExperience.tsx +85 -0
  78. package/src/ui/editor/complex/AForm.less +35 -0
  79. package/src/ui/editor/complex/AForm.tsx +340 -0
  80. package/src/ui/editor/complex/AIntDiff.tsx +77 -0
  81. package/src/ui/editor/complex/AMatrix.less +18 -0
  82. package/src/ui/editor/complex/AMatrix.tsx +242 -0
  83. package/src/ui/editor/complex/ATable.less +4 -0
  84. package/src/ui/editor/complex/ATable.tsx +33 -0
  85. package/src/ui/editor/complex/JsonEditor.tsx +37 -0
  86. package/src/ui/readable/A.tsx +33 -0
  87. package/src/ui/readable/ArrayViewer.tsx +46 -0
  88. package/src/ui/readable/DecorationViewer.tsx +76 -0
  89. package/src/ui/readable/DivViewer.tsx +11 -0
  90. package/src/ui/widget/Collapsible.tsx +156 -0
  91. package/src/ui/widget/Segment.less +39 -0
  92. package/src/ui/widget/Segment.tsx +40 -0
  93. package/src/ui/widget/SegmentEditSwitch.tsx +46 -0
  94. package/src/ui/widget/SelectBox.tsx +43 -0
  95. package/src/ui/widget/UnderlineInputBox.less +47 -0
  96. package/src/ui/widget/UnderlineInputBox.tsx +10 -0
  97. package/types/framework/Ajax.d.ts +5 -0
  98. package/types/framework/Assembly.d.ts +59 -0
  99. package/types/framework/Init.d.ts +4 -0
  100. package/types/framework/M3.d.ts +6 -0
  101. package/types/framework/MContext.d.ts +11 -0
  102. package/types/framework/MFieldViewer.d.ts +8 -0
  103. package/types/framework/MUtil.d.ts +180 -0
  104. package/types/framework/MViewer.d.ts +75 -0
  105. package/types/framework/MViewerDebug.d.ts +11 -0
  106. package/types/framework/Persistant.d.ts +17 -0
  107. package/types/framework/Schema.d.ts +306 -0
  108. package/types/framework/SchemaFunc.d.ts +14 -0
  109. package/types/framework/Validator.d.ts +53 -0
  110. package/types/framework/editorMap.d.ts +107 -0
  111. package/types/index.d.ts +21 -0
  112. package/types/types/MArrayType.d.ts +2 -0
  113. package/types/types/MCascadeType.d.ts +11 -0
  114. package/types/types/MCnAddressType.d.ts +2 -0
  115. package/types/types/MDateRangeType.d.ts +7 -0
  116. package/types/types/MDateTimeType.d.ts +11 -0
  117. package/types/types/MDecorationType.d.ts +7 -0
  118. package/types/types/MEnumType.d.ts +2 -0
  119. package/types/types/MExperienceType.d.ts +5 -0
  120. package/types/types/MFloatType.d.ts +2 -0
  121. package/types/types/MGB2260Type.d.ts +9 -0
  122. package/types/types/MIntDiffType.d.ts +2 -0
  123. package/types/types/MIntType.d.ts +2 -0
  124. package/types/types/MKvSetType.d.ts +11 -0
  125. package/types/types/MMatrixType.d.ts +5 -0
  126. package/types/types/MObjectType.d.ts +11 -0
  127. package/types/types/MSetType.d.ts +7 -0
  128. package/types/types/MStringType.d.ts +2 -0
  129. package/types/types/MTelType.d.ts +4 -0
  130. package/types/types/MType.d.ts +46 -0
  131. package/types/types/MVLPairType.d.ts +5 -0
  132. package/types/ui/BaseViewer.d.ts +45 -0
  133. package/types/ui/editor/basic/ACascadePicker.d.ts +11 -0
  134. package/types/ui/editor/basic/ACheckBox.d.ts +17 -0
  135. package/types/ui/editor/basic/ADatetimePicker.d.ts +15 -0
  136. package/types/ui/editor/basic/AGB2260.d.ts +10 -0
  137. package/types/ui/editor/basic/AInputBox.d.ts +8 -0
  138. package/types/ui/editor/basic/AIntBox.d.ts +9 -0
  139. package/types/ui/editor/basic/AKvSet.d.ts +19 -0
  140. package/types/ui/editor/basic/ARadio.d.ts +14 -0
  141. package/types/ui/editor/basic/ARangePicker.d.ts +30 -0
  142. package/types/ui/editor/basic/ARate.d.ts +13 -0
  143. package/types/ui/editor/basic/ARemoteSelector.d.ts +15 -0
  144. package/types/ui/editor/basic/ASelector.d.ts +14 -0
  145. package/types/ui/editor/basic/ASetSelector.d.ts +10 -0
  146. package/types/ui/editor/basic/ASpecInputBox.d.ts +9 -0
  147. package/types/ui/editor/basic/ATreeSelect.d.ts +18 -0
  148. package/types/ui/editor/basic/AUpload.d.ts +33 -0
  149. package/types/ui/editor/basic/NPS.d.ts +13 -0
  150. package/types/ui/editor/complex/AArray.d.ts +11 -0
  151. package/types/ui/editor/complex/AArrayGrid.d.ts +13 -0
  152. package/types/ui/editor/complex/ACnAddress.d.ts +10 -0
  153. package/types/ui/editor/complex/ADialogForm.d.ts +11 -0
  154. package/types/ui/editor/complex/AExperience.d.ts +16 -0
  155. package/types/ui/editor/complex/AForm.d.ts +46 -0
  156. package/types/ui/editor/complex/AIntDiff.d.ts +14 -0
  157. package/types/ui/editor/complex/AMatrix.d.ts +48 -0
  158. package/types/ui/editor/complex/ATable.d.ts +9 -0
  159. package/types/ui/editor/complex/JsonEditor.d.ts +9 -0
  160. package/types/ui/readable/A.d.ts +5 -0
  161. package/types/ui/readable/ArrayViewer.d.ts +5 -0
  162. package/types/ui/readable/DecorationViewer.d.ts +3 -0
  163. package/types/ui/readable/DivViewer.d.ts +5 -0
  164. package/types/ui/widget/Collapsible.d.ts +46 -0
  165. package/types/ui/widget/Segment.d.ts +18 -0
  166. package/types/ui/widget/SegmentEditSwitch.d.ts +20 -0
  167. package/types/ui/widget/SelectBox.d.ts +13 -0
  168. 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,10 @@
1
+ .AArray_addBtn{
2
+ margin-top: 10px;
3
+ text-align: center;
4
+ }
5
+
6
+
7
+ .AArray_item {
8
+ flex: 1;
9
+ margin-bottom: 5px;
10
+ }
@@ -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,15 @@
1
+ .ACnAddress_p div{
2
+ width: 50%
3
+ }
4
+
5
+ .ACnAddress_p .backfill{
6
+ width: 100%;
7
+ }
8
+
9
+ .ACnAddress .AInputBox{
10
+ width: 50% !important;
11
+ }
12
+
13
+ .ACnAddress .AGB2260{
14
+ width: 50% !important;
15
+ }
@@ -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
+ }