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,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
|
+
}
|