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,128 @@
1
+
2
+ .MEditor {
3
+ /* max-width:900px; */
4
+ /* margin:auto; */
5
+ --MViewerBorder: 1px solid #d9d9d9; /** TODO 在这里扩展吧 */
6
+ }
7
+
8
+ .MEditor .submitBar {
9
+ background: white;
10
+ box-shadow: 0 0 10px #d0cfcf;
11
+ padding: 25px;
12
+ margin: 25px 0 25px 0;
13
+
14
+ text-align: center;
15
+ }
16
+
17
+ .MEditor .AForm_removeBtn:focus, .MEditor .AForm_removeBtn:hover {
18
+ color: #40a9ff;
19
+ }
20
+
21
+ .MEditor .AForm_removeBtn_disabled {
22
+ color: lightgray;
23
+ }
24
+
25
+
26
+
27
+ .MEditor_p {
28
+ max-width:900px;
29
+ margin:auto;
30
+ --MViewerBorder: 1px solid #d9d9d9; /** TODO 在这里扩展吧 */
31
+ }
32
+
33
+ .MEditor_p .submitBar {
34
+ background: white;
35
+ box-shadow: 0 0 10px #d0cfcf;
36
+ padding: 15px;
37
+ margin: 25px 0 25px 0;
38
+
39
+ text-align: center;
40
+ }
41
+
42
+ .MEditor_p .AForm_removeBtn:focus, .MEditor .AForm_removeBtn:hover {
43
+ color: #40a9ff;
44
+ }
45
+
46
+ .MEditor_p .AForm_removeBtn_disabled {
47
+ color: lightgray;
48
+ }
49
+
50
+ /** 回填字段的样式,例如日期选择/gb2260选项下,带下划线的,显示被选中值的一行字 */
51
+ .MEditor_p .backfill {
52
+ border-bottom: 1px solid #d9d9d9;
53
+ }
54
+
55
+ .MEditor_p .backfill_empty {
56
+ border-bottom: 1px solid #d9d9d9;
57
+ color: gray;
58
+ }
59
+
60
+ .MEditor_p input, .MEditor_p textarea {
61
+ font-size: 16px; /** ios上,字小于16px的输入框,输入时页面会放大,这个体验及其糟糕,只能通过改大字体解决 */
62
+ }
63
+
64
+ table.M3_table {
65
+ border: var(--MViewerBorder);
66
+ border-spacing: 0;
67
+ }
68
+ table.M3_table tr,table.M3_table th {border-bottom: var(--MViewerBorder);}
69
+ table.M3_table td,table.M3_table th{
70
+ border-right: var(--MViewerBorder);
71
+ border-bottom: var(--MViewerBorder);
72
+ padding: 5px 0 5px 0;
73
+ .ant-select-selector {
74
+ border: none !important;
75
+ }
76
+ }
77
+ table.M3_table th {
78
+ font-weight: normal;
79
+ text-align: center;
80
+ }
81
+ table.M3_table tr td:last-child{ border-right: 0 }
82
+ table.M3_table tr:last-child td{ border-bottom: 0 }
83
+
84
+ /** antd 样式调整 */
85
+ .MEditor {
86
+ /** 减少字段之间的垂直距离,表单紧凑些 */
87
+ .ant-row {
88
+ margin-bottom: 10px;
89
+ }
90
+
91
+ /** ant表单项的间距太大了,浪费空间 */
92
+ .ant-form-item, .ant-form-item- {
93
+ margin-bottom: 0px !important;
94
+ }
95
+
96
+ }
97
+
98
+ .MEditor_p, .MEditor {
99
+ .wrap {
100
+ word-break: break-all;
101
+ white-space: pre-line;
102
+ }
103
+
104
+ /** 选项文字折行展示 */
105
+ .ant-radio-wrapper, .ant-checkbox-wrapper{
106
+ white-space: normal;
107
+
108
+ span {
109
+ word-break: break-all;
110
+ white-space: pre-line;
111
+ }
112
+ }
113
+
114
+ .am-calendar {
115
+ .content { /** TODO:antd mobile的日历缩回去的时候有bug,动画不正常,这里把动画禁掉,以后如果升级antd mobile了可以试试去掉 */
116
+ animation-duration: 0s;
117
+ -webkit-animation-duration: 0s;
118
+ }
119
+ }
120
+ }
121
+
122
+ /** antd的Tabs不能100%高,加上FullHeightTab这个class就可以了 */
123
+ .FullHeightTab .ant-tabs-content{
124
+ height: 100%
125
+ }
126
+ .FullHeightTab .ant-tabs {
127
+ height: 100%
128
+ }
@@ -0,0 +1,180 @@
1
+ import React, { useState } from "react";
2
+ import { AFTER_CHANGE_CALLBACK, MFieldSchema, M3UISpec } from "../framework/Schema";
3
+ import { Button, message, Modal } from "antd";
4
+ import "./MViewer.less";
5
+ import { MFieldViewer } from "./MFieldViewer";
6
+ import { MUtil } from './MUtil';
7
+ import { assembly, MORPH } from './Assembly';
8
+ import _ from "lodash";
9
+ import { ensureM3 } from './Init';
10
+ import { MContext } from './MContext';
11
+ import { PersistantTool, PersistantConf } from "./Persistant";
12
+
13
+ export interface MViewerProp {
14
+ schema: MFieldSchema,
15
+ database: any,
16
+ layout?: M3UISpec,
17
+ style?: React.CSSProperties,
18
+ morph: MORPH,
19
+ onSubmit?: (finalData: any) => Promise<any>;
20
+ afterChange?: AFTER_CHANGE_CALLBACK,
21
+ wrapper?: (elem: React.ReactElement, schema: Partial<MFieldSchema>) => React.ReactElement,
22
+ formItemWrapper?: (elem: React.ReactElement, schema: Partial<MFieldSchema>) => React.ReactElement,
23
+ /** 持久存储选项,nil表示不持久存储 */
24
+ persistant?: PersistantConf
25
+ }
26
+
27
+ export interface M3Prop {
28
+ schema: MFieldSchema | MFieldSchema[],
29
+ database: any,
30
+ layout?: M3UISpec,
31
+ style?: React.CSSProperties,
32
+ morph: MORPH,
33
+ onSubmit?: (finalData: any) => Promise<any>;
34
+ afterChange?: AFTER_CHANGE_CALLBACK,
35
+ wrapper?: (elem: React.ReactElement, schema: Partial<MFieldSchema>) => React.ReactElement,
36
+ formItemWrapper?: (elem: React.ReactElement, schema: Partial<MFieldSchema>) => React.ReactElement,
37
+ /** 持久存储选项,nil表示不持久存储 */
38
+ persistant?: PersistantConf
39
+ }
40
+
41
+ interface State {
42
+ forceValid: boolean;
43
+ ctrlVersion: number;
44
+ }
45
+ /**
46
+ * 一个完整的表单
47
+ */
48
+ export class MViewer extends React.Component<MViewerProp, State> {
49
+ database: any;
50
+
51
+ constructor(p: MViewerProp) {
52
+ super(p);
53
+ this.state = {
54
+ forceValid: false,
55
+ ctrlVersion: 1,
56
+ }
57
+
58
+ ensureM3();
59
+
60
+ const props = this.props;
61
+
62
+ // 值类型兼容预处理
63
+ this.database = assembly.types[props.schema.type]?.standardValue(assembly, props.schema, props.database, false);
64
+
65
+ // 填入默认值
66
+ MUtil.applyDefaultValue(props.schema, props.database, "");
67
+ // 填入本地缓存值
68
+ this.recover()
69
+ }
70
+
71
+ recover() {
72
+ const { ctrlVersion } = this.state
73
+ const { persistant } = this.props
74
+ PersistantTool.load(this.database, persistant, () => {
75
+ this.setState({
76
+ ctrlVersion: ctrlVersion + 1
77
+ })
78
+ });
79
+ }
80
+
81
+ render() {
82
+ const props = this.props;
83
+ const database = this.database;
84
+ const { ctrlVersion, forceValid } = this.state
85
+
86
+ return <MContext.Provider value={{
87
+ rootProps: props,
88
+ forceValid, setForceValid: (b) => { this.setState({ forceValid: true }) }
89
+ }}>
90
+ <div key={ctrlVersion} className={MUtil.phoneLike() ? "MEditor_p" : "MEditor"} style={props.style}>
91
+ <MFieldViewer schema={props.schema} database={database} path="" morph={props.morph} afterChange={PersistantTool.patchAfterChange(props.afterChange, props.persistant)} />
92
+ {props.children}
93
+ </div>
94
+ </MContext.Provider>
95
+ }
96
+ }
97
+
98
+ /**
99
+ * 提交按钮
100
+ 示例1,使用默认的提交样式:
101
+ <MViewer schema={schema} database={database} morph="editor">
102
+ <SubmitBar onSubmit={(d:any) => ...返回一个Promise} />
103
+ </MViewer>
104
+
105
+ 示例2,自定义样式:
106
+ <MViewer schema={schema} database={database} morph="editor">
107
+ <SubmitBar onSubmit={(d:any) => ...返回一个Promise} >
108
+ <Button>提交</Button>
109
+ </SubmitBar>
110
+ </MViewer>
111
+
112
+ 示例3,提交完成前,禁用提交按钮:
113
+ <MViewer schema={schema} database={database} morph="editor">
114
+ <SubmitBar onSubmit={(d:any) => ...返回一个Promise} >
115
+ {
116
+ loading => <Button loading={loading}>提交</Button>
117
+ }
118
+ </SubmitBar>
119
+ </MViewer>
120
+ * @param props
121
+ */
122
+ export function SubmitBar(props: {
123
+ style?: React.CSSProperties,
124
+ onSubmit?: (finalData: any) => Promise<any>,
125
+ children?: React.ReactNode | ((loading: boolean) => React.ReactNode)
126
+ }): JSX.Element {
127
+
128
+ const [loading, setLoading] = useState(false);
129
+ let style: React.CSSProperties = props.children ? undefined : { textAlign: "center", ...props.style }
130
+
131
+ return <MContext.Consumer>{
132
+ ctx => {
133
+ const onClick = () => {
134
+ if (loading) {
135
+ message.warn("正在提交,请稍候");
136
+ return;
137
+ }
138
+ const r = assembly.validate(ctx.rootProps.schema, ctx.rootProps.database);
139
+ const submit = props.onSubmit ?? ctx.rootProps.onSubmit;
140
+ ctx.setForceValid(true);
141
+ if (r) {
142
+ Modal.warning({
143
+ content: '还没填完呢',
144
+ onOk: () => {
145
+ const viewer = document.getElementById(r.path);
146
+ if (viewer) {
147
+ viewer.scrollIntoView({ behavior: 'smooth', block: 'start' });
148
+ } else {
149
+ console.error("viewer not found", r);
150
+ }
151
+ }
152
+ });
153
+ } else {
154
+ if (submit) {
155
+ setLoading(true);
156
+ const finalData = MUtil.filterHide(ctx.rootProps.schema, ctx.rootProps.database)
157
+ submit(finalData).then(() => {
158
+ PersistantTool.clear(ctx.rootProps.persistant)
159
+ }).finally(() => {
160
+ setLoading(false);
161
+ });
162
+ } else {
163
+ message.success("填是填完了,但提交功能还在开发中,请联系程序员解决");
164
+ }
165
+ }
166
+ };
167
+ return <div style={style} onClick={props.children ? onClick : undefined}>
168
+ {
169
+ props.children
170
+ ? (_.isFunction(props.children) ? props.children(loading) : props.children)
171
+ : <Button style={{ width: "40%" }} type="primary" loading={loading} onClick={props.children ? undefined : onClick}>提交</Button>
172
+ }
173
+ </div>
174
+ }
175
+ }</MContext.Consumer>
176
+ }
177
+
178
+ export function useM3Database(initValue) {
179
+ return useState(initValue);
180
+ }
@@ -0,0 +1,95 @@
1
+ import React from "react";
2
+ import { Tabs, Button, message } from 'antd';
3
+ import _ from 'lodash';
4
+ import { useState } from "react";
5
+ import { MViewerProp, MViewer } from './MViewer';
6
+ import { MUtil } from './MUtil';
7
+
8
+ const TEXTAREAKEY = "tk";
9
+ const FINALDATAKEY = "fd";
10
+ const OTHERMORPH = "om";
11
+
12
+ /**
13
+ * 调试和理解MViewer,使用时除了多一个debug props,完全和MViewer一样
14
+ * debug=true时,通过tab同时展示viewer及其database和schema
15
+ * debug=false时,仅展示viewer
16
+ * @param props 用来调试viewer
17
+ */
18
+ export function MViewerDebug(props: React.PropsWithChildren<MViewerProp & {debug?: boolean}>):JSX.Element {
19
+ const [dataVersion, setDataVersion] = useState(0);
20
+ const debug = props.debug ?? (window.location.search.indexOf("debug")>=0 || window.location.hash.indexOf("debug")>=0);
21
+ if(!debug){
22
+ return <MViewer {...props}/>;
23
+ }
24
+
25
+ function finalData(){
26
+ return MUtil.filterHide(props.schema, props.database);
27
+ }
28
+
29
+ return <Tabs style={props.style} onTabClick={(key:string)=>{
30
+ if(key === TEXTAREAKEY){
31
+ setTimeout(() => { // 让编辑器满屏
32
+ const textarea = document.querySelector("#_data");
33
+ // @ts-ignore
34
+ const h = document.body.clientHeight - textarea.offsetTop;
35
+ // @ts-ignore
36
+ textarea.style.height=(h - 10) + "px";
37
+ // @ts-ignore
38
+ textarea.value = JSON.stringify(props.database, null, 2);
39
+ })
40
+ } else if(key === FINALDATAKEY) {
41
+ setTimeout(() => {
42
+ let myContainer = document.getElementById('finalData') as HTMLInputElement;
43
+ myContainer.innerHTML = JSON.stringify(finalData() , null, 2);
44
+ });
45
+ } else if(key == OTHERMORPH) {
46
+ setDataVersion(dataVersion + 1);
47
+ }
48
+ }}>
49
+ <Tabs.TabPane tab="页面" key="fk">
50
+ <MViewer {..._.omit(props,"style")} key={dataVersion} />
51
+ </Tabs.TabPane>
52
+
53
+ {
54
+ props.morph === "readable"
55
+ ? <Tabs.TabPane tab="编辑模式" key={OTHERMORPH}>
56
+ <MViewer {..._.omit(props,"style")} morph="editor" key={dataVersion} />
57
+ </Tabs.TabPane>
58
+ : <Tabs.TabPane tab="阅读模式" key={OTHERMORPH}>
59
+ <MViewer {..._.omit(props,"style")} morph="readable" key={dataVersion} />
60
+ </Tabs.TabPane>
61
+ }
62
+
63
+ <Tabs.TabPane tab="数据" key={TEXTAREAKEY}>
64
+ <Button onClick={()=>{ // 刷新数据到表单
65
+ try{
66
+ // @ts-ignore
67
+ const json = JSON.parse(document.querySelector("#_data").value);
68
+ for(let k in props.database){
69
+ delete props.database[k];
70
+ }
71
+ _.assign(props.database, json);
72
+ message.success('成了');
73
+ setDataVersion(dataVersion+1);
74
+ } catch(e){
75
+ message.error(<pre>{'json解析失败了\n' + e}</pre>);
76
+ }
77
+ }}>修改</Button>
78
+ <textarea id="_data" style={{width:"100%", padding:"0"}}/>
79
+ </Tabs.TabPane>
80
+ <Tabs.TabPane tab="最终数据" key={FINALDATAKEY}>
81
+ <pre id="finalData" className="ant-input" style={{"width": "100%"}}/>
82
+ </Tabs.TabPane>
83
+ <Tabs.TabPane tab="Schema" key="schema">
84
+ <pre className="ant-input" style={{"width": "100%"}}>
85
+ {MUtil.validateSchema(props.schema).map(r=><div key="warn" style={{color: "red"}}>{r.message}</div>)}
86
+ {JSON.stringify(props.schema, null, 2)}
87
+ </pre>
88
+ </Tabs.TabPane>
89
+ <Tabs.TabPane tab="UT" key="ut">
90
+ <div key="ut" className="ant-input" style={{"width": "100%"}}>
91
+ {JSON.stringify({name:`debug-${Date.now()}`, schema: props.schema, database: props.database})},
92
+ </div>
93
+ </Tabs.TabPane>
94
+ </Tabs>
95
+ }
@@ -0,0 +1,90 @@
1
+ // 在本地持久保存表单,防止丢失
2
+ import _ from "lodash";
3
+ import { AFTER_CHANGE_CALLBACK } from "./Schema";
4
+ import { Modal } from "antd"
5
+ /**
6
+ * MViewer的配置
7
+ */
8
+ export interface PersistantConf {
9
+ /** 持久存储的localStorageKey前缀 */
10
+ localStorageKeyPrefix: string;
11
+ /** 是否询问恢复至缓存 */
12
+ useConfirm?: Boolean;
13
+ /** 如何合并database和persistant数据,默认是local覆盖database */
14
+ override?: (database:any, local:any) => void;
15
+ }
16
+
17
+ let confirming = false
18
+
19
+ export const PersistantTool = {
20
+ load: function initPersistant(database:any, persistant?: PersistantConf, updateViewer?: any){
21
+ // 填入本地缓存值
22
+ const prefix = persistant?.localStorageKeyPrefix;
23
+ const useConfirm = persistant?.useConfirm;
24
+ if(prefix) {
25
+ const successCb = () => {
26
+ let localDb = {};
27
+ for(let key in localStorage) {
28
+ try {
29
+ if(_.startsWith(key, prefix)) {
30
+ _.set(localDb, key.substr(prefix.length), JSON.parse(localStorage[key]))
31
+ }
32
+ } catch(e){ // 加载值错了不能影响表单填写
33
+ console.log(e);
34
+ }
35
+ }
36
+ (persistant.override ?? _.assign)(database, localDb);
37
+ }
38
+ if(useConfirm && Object.keys(localStorage).find(f=> f.indexOf(prefix) === 0) && !confirming) {
39
+ // 防止同时多次弹框
40
+ confirming = true
41
+ Modal.confirm({
42
+ content: '是否恢复您上次填写过但未提交的内容?',
43
+ okText: '恢复',
44
+ cancelText: '取消',
45
+ onOk: () => {
46
+ confirming = false
47
+ successCb()
48
+ updateViewer()
49
+ },
50
+ onCancel: () => {
51
+ confirming = false
52
+ this.clear(persistant)
53
+ }
54
+ });
55
+ } else {
56
+ successCb()
57
+ }
58
+ }
59
+ },
60
+
61
+ patchAfterChange: function(afterChange: AFTER_CHANGE_CALLBACK, persistant?: PersistantConf){
62
+ const prefix = persistant?.localStorageKeyPrefix;
63
+ return prefix ? (path, v, final) => {
64
+ if(final){
65
+ if(_.isNil(v)){
66
+ localStorage.removeItem(prefix + path)
67
+ } else {
68
+ localStorage.setItem(prefix + path, JSON.stringify(v))
69
+ }
70
+ }
71
+ afterChange?.(path, v, final);
72
+ }
73
+ : afterChange;
74
+ },
75
+
76
+ clear: function(persistant?: PersistantConf){
77
+ const pprefix = persistant?.localStorageKeyPrefix;
78
+ if(pprefix) { // 提交成功,删掉所有的本地存储
79
+ for(let key of Object.keys(localStorage)){
80
+ try {
81
+ if(_.startsWith(key, pprefix)) {
82
+ localStorage.removeItem(key);
83
+ }
84
+ } catch(e) { // 出错不能影响主流程
85
+ console.log(e);
86
+ }
87
+ }
88
+ }
89
+ }
90
+ }