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,86 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Radio } from "antd";
|
|
3
|
+
import _ from "lodash";
|
|
4
|
+
import { MFieldViewer } from "../../../framework/MFieldViewer";
|
|
5
|
+
import { MUtil } from "../../../framework/MUtil";
|
|
6
|
+
import { BaseViewer } from '../../BaseViewer';
|
|
7
|
+
import { MEnumType } from "../../../types/MEnumType";
|
|
8
|
+
import { assembly } from "../../../framework/Assembly";
|
|
9
|
+
|
|
10
|
+
// "其他(请填写)"这样的开放选项Radio的value
|
|
11
|
+
const OPEN_ENUM_RADIO_VALUE = -1;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* 单选框,用于enum
|
|
15
|
+
* 支持开放选项。
|
|
16
|
+
* 定义示例:
|
|
17
|
+
* {label:"1.1 您的性别是", name:"gendar", type:"enum", option: [{label:"男", value:"M"},{label:"女", value:"F"}] },
|
|
18
|
+
* {label:"1.1 您的性别是", name:"gendar", type:"enum", option:"男 女"},
|
|
19
|
+
*/
|
|
20
|
+
export class ARadio extends BaseViewer {
|
|
21
|
+
/** 开放输入框的值 */
|
|
22
|
+
_inputBoxValue: string = "";
|
|
23
|
+
|
|
24
|
+
element(ctx) {
|
|
25
|
+
const value = super.getValue();
|
|
26
|
+
const option = MUtil.option(this.props.schema);
|
|
27
|
+
const style = (this.props.schema.layoutHint ?? "v") === "v" ? { display: "block" } : undefined;
|
|
28
|
+
|
|
29
|
+
let initIndex = undefined;
|
|
30
|
+
const options = option.map((m: any, index) => {
|
|
31
|
+
const isShow = MUtil.isShow(this.props.database, ctx.rootProps.schema?.objectFields, m.showIf)
|
|
32
|
+
if (!isShow) return null;
|
|
33
|
+
if (value === m.value) {
|
|
34
|
+
initIndex = index;
|
|
35
|
+
}
|
|
36
|
+
return <Radio disabled={this.props.disable} key={index} value={index} style={style}>{m.label ?? m.value}</Radio>;
|
|
37
|
+
});
|
|
38
|
+
if (initIndex === undefined) {
|
|
39
|
+
this._inputBoxValue = value;
|
|
40
|
+
if (!_.isNil(value)) {
|
|
41
|
+
initIndex = OPEN_ENUM_RADIO_VALUE;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (this.props.schema.openOption) {
|
|
46
|
+
const editor = <MFieldViewer morph={this.props.morph} schema={this.props.schema.openOption} database={this} path="_inputBoxValue"
|
|
47
|
+
afterChange={(path: string, str: any, final: boolean) => {
|
|
48
|
+
const matchEnum = option.find(e => e.value === str);
|
|
49
|
+
if (matchEnum) { // 不能让用户输入了某个候选值
|
|
50
|
+
this._inputBoxValue = "";
|
|
51
|
+
super.changeValueEx(str, true, final);
|
|
52
|
+
} else {
|
|
53
|
+
this._inputBoxValue = str;
|
|
54
|
+
super.changeValueEx(this._inputBoxValue, false, final);
|
|
55
|
+
}
|
|
56
|
+
}}
|
|
57
|
+
parent={this.props.schema} forceValid={false} disable={initIndex !== OPEN_ENUM_RADIO_VALUE}
|
|
58
|
+
style={{ width: "inherit" }} />;
|
|
59
|
+
|
|
60
|
+
options.push(<Radio disabled={this.props.disable} key={"_open_"} value={OPEN_ENUM_RADIO_VALUE}>
|
|
61
|
+
<span style={{ marginRight: "10px" }}>{this.props.schema.openOption.label ?? "其他"}</span>
|
|
62
|
+
{editor}
|
|
63
|
+
</Radio>);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const p = this.props.schema.props ?? {};
|
|
67
|
+
return <Radio.Group
|
|
68
|
+
key={this.state.ctrlVersion}
|
|
69
|
+
defaultValue={initIndex}
|
|
70
|
+
onChange={(vv) => {
|
|
71
|
+
let v = vv.target.value;
|
|
72
|
+
if (v !== OPEN_ENUM_RADIO_VALUE) {
|
|
73
|
+
super.changeValue(option[v].value);
|
|
74
|
+
} else {
|
|
75
|
+
if (!this._inputBoxValue) {
|
|
76
|
+
this._inputBoxValue = "";
|
|
77
|
+
}
|
|
78
|
+
super.changeValue(this._inputBoxValue);
|
|
79
|
+
}
|
|
80
|
+
}}
|
|
81
|
+
{...p}
|
|
82
|
+
>
|
|
83
|
+
{options}
|
|
84
|
+
</Radio.Group>
|
|
85
|
+
}
|
|
86
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import React, { Component, RefObject } from "react";
|
|
2
|
+
import { DatePicker } from "antd";
|
|
3
|
+
import zhCN from 'antd/lib/date-picker/locale/zh_CN';
|
|
4
|
+
import moment from "moment";
|
|
5
|
+
import { Button, Calendar } from "antd-mobile";
|
|
6
|
+
import ReactDOM from "react-dom";
|
|
7
|
+
import { RangePickerProps } from "antd/lib/date-picker";
|
|
8
|
+
import _ from "lodash";
|
|
9
|
+
import { Viewer, ViewerState } from '../../BaseViewer';
|
|
10
|
+
import { MUtil } from '../../../framework/MUtil';
|
|
11
|
+
import { MProp } from "../../../framework/Schema";
|
|
12
|
+
import { MDateRangeType } from '../../../types/MDateRangeType';
|
|
13
|
+
import { assembly } from '../../../framework/Assembly';
|
|
14
|
+
|
|
15
|
+
export type ARangePickerData = [
|
|
16
|
+
// 开始时间
|
|
17
|
+
string | null | undefined,
|
|
18
|
+
// 结束时间
|
|
19
|
+
string | null | undefined,
|
|
20
|
+
// 是否至今,如果true,结束时间是无效的
|
|
21
|
+
boolean | null | undefined
|
|
22
|
+
];
|
|
23
|
+
|
|
24
|
+
type AntData = [moment.Moment | null, moment.Moment | null];
|
|
25
|
+
|
|
26
|
+
interface State extends ViewerState {
|
|
27
|
+
mobileDlg: boolean;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export class ARangePicker extends Viewer<State> {
|
|
31
|
+
_pickerRef: RefObject<Component<RangePickerProps, any, any>> = React.createRef();
|
|
32
|
+
_onCalendarChangeValue?: AntData | null;
|
|
33
|
+
|
|
34
|
+
constructor(p: MProp) {
|
|
35
|
+
super(p);
|
|
36
|
+
this.state = { ctrlVersion: 1, noValidate: false, mobileDlg: false };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
componentDidUpdate() {
|
|
40
|
+
this._patchTillnow();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
componentDidMount() {
|
|
44
|
+
this._patchTillnow();
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
_patchTillnow() {
|
|
48
|
+
const v = super.getValue();
|
|
49
|
+
if (_.get(v, "[2]")) { // tillnow
|
|
50
|
+
const dom = ReactDOM.findDOMNode(this._pickerRef.current);
|
|
51
|
+
if (dom) {
|
|
52
|
+
// @ts-ignore
|
|
53
|
+
let r = dom.querySelector(":nth-child(3)");
|
|
54
|
+
r.innerHTML = "<input readonly disabled size='12' autocomplete='off' value='至今' style='color: black'>";
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* RangePicker的数据转换成json上的数据类型
|
|
61
|
+
* @param r
|
|
62
|
+
*/
|
|
63
|
+
_rangePicker2Data(v: AntData | null | undefined, tillNow: boolean): ARangePickerData | undefined {
|
|
64
|
+
if (!v) {
|
|
65
|
+
return undefined;
|
|
66
|
+
}
|
|
67
|
+
const dataFormat = this.props.schema.dataFormat ?? "x";
|
|
68
|
+
return [v[0]?.format(dataFormat), v[1]?.format(dataFormat), tillNow];
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* json上的数据转换成RangePicker的数据
|
|
73
|
+
* @param d
|
|
74
|
+
*/
|
|
75
|
+
_data2rangePicker(d: ARangePickerData): AntData {
|
|
76
|
+
const dataFormat = this.props.schema.dataFormat ?? "x";
|
|
77
|
+
return [d[0] ? moment(d[0], dataFormat) : null, d[1] ? moment(d[1], dataFormat) : null];
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
element() {
|
|
81
|
+
const p = this.props.schema.props ?? {};
|
|
82
|
+
let rangePickerData = this._data2rangePicker(this.getValue() ?? []);
|
|
83
|
+
if (MUtil.phoneLike()) {
|
|
84
|
+
let show = MDateRangeType.toReadableN(assembly, this.props.schema, super.getValue());
|
|
85
|
+
|
|
86
|
+
return <>
|
|
87
|
+
<div className="backfill" onClick={() => this.setState({ mobileDlg: true })}> {show ?? '请点击选择'} </div>
|
|
88
|
+
<Calendar
|
|
89
|
+
visible={this.state.mobileDlg}
|
|
90
|
+
pickTime={this.props.schema.dateRange?.showTime}
|
|
91
|
+
minDate={this.props.schema.min ? new Date(this.props.schema.min) : undefined}
|
|
92
|
+
maxDate={this.props.schema.min ? new Date(this.props.schema.max) : undefined}
|
|
93
|
+
onCancel={() => this.setState({ mobileDlg: false })}
|
|
94
|
+
onConfirm={(start, end) => {
|
|
95
|
+
super.changeValueEx(this._rangePicker2Data([moment(start), moment(end)], false), true, true)
|
|
96
|
+
this.setState({ mobileDlg: false })
|
|
97
|
+
}}
|
|
98
|
+
{...p}
|
|
99
|
+
/>
|
|
100
|
+
</>
|
|
101
|
+
} else {
|
|
102
|
+
// 构造元素
|
|
103
|
+
return <DatePicker.RangePicker
|
|
104
|
+
ref={this._pickerRef}
|
|
105
|
+
showTime={this.props.schema.dateRange?.showTime}
|
|
106
|
+
key={this.state.ctrlVersion}
|
|
107
|
+
renderExtraFooter={this.props.schema.dateRange?.hideTillNow || this.props.schema.dateRange?.showTime // TODO 显示时间时,“至今”无法支持
|
|
108
|
+
? undefined
|
|
109
|
+
: (mode) => <div style={{ textAlign: "right" }} {...p}>
|
|
110
|
+
<Button
|
|
111
|
+
size="small" style={{ width: "100px", display: "inline-block", marginTop: "5px" }}
|
|
112
|
+
onClick={() => {
|
|
113
|
+
super.changeValueEx(this._rangePicker2Data(this._onCalendarChangeValue, true), true, true);
|
|
114
|
+
}}>至今</Button>
|
|
115
|
+
</div>
|
|
116
|
+
}
|
|
117
|
+
bordered={this.props.hideBorder ? false : true}
|
|
118
|
+
style={{ minWidth: "240px" }}
|
|
119
|
+
locale={zhCN}
|
|
120
|
+
defaultValue={rangePickerData}
|
|
121
|
+
onCalendarChange={(d) => {
|
|
122
|
+
this._onCalendarChangeValue = d;
|
|
123
|
+
}}
|
|
124
|
+
onChange={(vv) => {
|
|
125
|
+
super.changeValueEx(this._rangePicker2Data(vv, false), true, true)
|
|
126
|
+
}} />;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import _ from "lodash";
|
|
3
|
+
import { BaseViewer } from "../../BaseViewer";
|
|
4
|
+
import { Rate } from 'antd';
|
|
5
|
+
import './ARate.less';
|
|
6
|
+
import { MProp } from "../../../framework/Schema";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* antd 的评分组件 https://ant.design/components/rate-cn/
|
|
10
|
+
* { editor: 'ARate', name: 'score', label: "评分", required: true, props: {count: 8} }
|
|
11
|
+
*/
|
|
12
|
+
export class ARate extends BaseViewer {
|
|
13
|
+
value: string;
|
|
14
|
+
|
|
15
|
+
constructor(p:MProp){
|
|
16
|
+
super(p);
|
|
17
|
+
this.value = super.getValue() == 0 ? null : super.getValue();
|
|
18
|
+
}
|
|
19
|
+
element() {
|
|
20
|
+
const p = this.props.schema.props ?? {};
|
|
21
|
+
const props = {
|
|
22
|
+
count: this.props.schema.max,
|
|
23
|
+
defaultValue: this.value,
|
|
24
|
+
onChange: (value, label, extra)=>{
|
|
25
|
+
if (value == 0) value = null
|
|
26
|
+
if (p.onChange) p.onChange(value, label, extra)
|
|
27
|
+
super.changeValue(value);
|
|
28
|
+
},
|
|
29
|
+
onBlur: () =>{
|
|
30
|
+
if (p.onBlur) p.onBlur()
|
|
31
|
+
super.changeValue(super.getValue())
|
|
32
|
+
},
|
|
33
|
+
...p,
|
|
34
|
+
}
|
|
35
|
+
return <Rate {...props} />
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { Select, Spin } from 'antd';
|
|
4
|
+
import debounce from 'lodash/debounce';
|
|
5
|
+
import { Viewer, ViewerState } from '../../BaseViewer';
|
|
6
|
+
import { Ajax } from '../../../framework/Ajax';
|
|
7
|
+
import _ from "lodash";
|
|
8
|
+
import { MUtil } from '../../../framework/MUtil';
|
|
9
|
+
import { MFieldSchema } from '../../../framework/Schema';
|
|
10
|
+
|
|
11
|
+
interface State extends ViewerState {
|
|
12
|
+
candidate: any[],
|
|
13
|
+
fetching: boolean,
|
|
14
|
+
q: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// 这个Viewer可用的数据类型
|
|
18
|
+
const VALID_TYPES = {
|
|
19
|
+
"vl":true,
|
|
20
|
+
"array":true,
|
|
21
|
+
"string":true // 仅保存value
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export function labelExpr(d: any, remote: MFieldSchema["remote"]){
|
|
25
|
+
let script = "const {" + _.keys(d).join(',') + "} = d; return " + remote.labelExpr;
|
|
26
|
+
return new Function("d", "_", script)(d, _);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export class ARemoteSelector extends Viewer<State> {
|
|
30
|
+
fetchCandidate: (key:string)=>void;
|
|
31
|
+
|
|
32
|
+
constructor(props) {
|
|
33
|
+
super(props);
|
|
34
|
+
this.state = {candidate: undefined, fetching: false, q:""};
|
|
35
|
+
this.fetchCandidate = debounce(q => {
|
|
36
|
+
this.setState({ fetching: true });
|
|
37
|
+
Ajax.get(this.props.schema.remote.url.replace("${q}", q))
|
|
38
|
+
.then(
|
|
39
|
+
d => {
|
|
40
|
+
_.assign(this.props.schema, {
|
|
41
|
+
_options: d
|
|
42
|
+
})
|
|
43
|
+
this.setState({ candidate: MUtil.get(d, this.props.schema.remote.dataPath), fetching: false })
|
|
44
|
+
}
|
|
45
|
+
);
|
|
46
|
+
}, 500);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
element() {
|
|
50
|
+
const type = this.props.schema.type;
|
|
51
|
+
if(!VALID_TYPES[type]){
|
|
52
|
+
return MUtil.error("只适用类型" + Object.keys(VALID_TYPES).join(","), this.props.schema);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
let defaultValue = super.getValue();
|
|
56
|
+
if(type === "string"){
|
|
57
|
+
defaultValue = {value: defaultValue, label: defaultValue};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
let mode = undefined;
|
|
61
|
+
if(type === "array") {
|
|
62
|
+
if(!_.isArray(defaultValue)){ // 数据不对
|
|
63
|
+
defaultValue = []; // 干掉
|
|
64
|
+
}
|
|
65
|
+
mode = "multiple"
|
|
66
|
+
} else {
|
|
67
|
+
if(_.isNil(defaultValue?.value)){
|
|
68
|
+
defaultValue = undefined;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const p = this.props.schema.props ?? {};
|
|
73
|
+
return <Select
|
|
74
|
+
key={this.props.path}
|
|
75
|
+
showSearch
|
|
76
|
+
mode={mode}
|
|
77
|
+
// mode={"multiple"}
|
|
78
|
+
labelInValue
|
|
79
|
+
defaultValue={defaultValue}
|
|
80
|
+
placeholder={this.props.schema.placeholder}
|
|
81
|
+
notFoundContent={this.state.fetching ? <Spin size="small" /> : null}
|
|
82
|
+
filterOption={false}
|
|
83
|
+
onSearch={this.fetchCandidate}
|
|
84
|
+
style={{ width: '100%' }}
|
|
85
|
+
onFocus={()=>{
|
|
86
|
+
if(!this.state.candidate) {
|
|
87
|
+
this.fetchCandidate("");
|
|
88
|
+
}
|
|
89
|
+
}}
|
|
90
|
+
onChange={(v: any) => {
|
|
91
|
+
switch(type){
|
|
92
|
+
case "vl":
|
|
93
|
+
if (v) super.changeValue({value:v.value, label:v.label});
|
|
94
|
+
else super.changeValue(null);
|
|
95
|
+
break;
|
|
96
|
+
case "array":
|
|
97
|
+
if (v) super.changeValue(v.map(i=>({value:i.value, label:i.label })));
|
|
98
|
+
else super.changeValue(null);
|
|
99
|
+
break;
|
|
100
|
+
case "string":
|
|
101
|
+
if (v) super.changeValue(v.value);
|
|
102
|
+
else super.changeValue(null);
|
|
103
|
+
break;
|
|
104
|
+
}
|
|
105
|
+
}}
|
|
106
|
+
{...p}
|
|
107
|
+
>
|
|
108
|
+
{
|
|
109
|
+
this.state.candidate?.map(d => {
|
|
110
|
+
const v = MUtil.get(d, this.props.schema.remote.valuePath);
|
|
111
|
+
return <Select.Option key={v} value={v}>{labelExpr(d, this.props.schema.remote)}</Select.Option>
|
|
112
|
+
})
|
|
113
|
+
}
|
|
114
|
+
</Select>
|
|
115
|
+
}
|
|
116
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { PlusOutlined } from "@ant-design/icons";
|
|
3
|
+
import { Button, Divider } from "antd";
|
|
4
|
+
import Select from "antd/lib/select";
|
|
5
|
+
import { MUtil } from "../../../framework/MUtil";
|
|
6
|
+
import { MEnumField, MProp } from '../../../framework/Schema';
|
|
7
|
+
import { BaseViewer } from '../../BaseViewer';
|
|
8
|
+
import { SelectBox } from "../../widget/SelectBox";
|
|
9
|
+
import _ from "lodash";
|
|
10
|
+
import { MFieldViewer } from '../../../framework/MFieldViewer';
|
|
11
|
+
import { MEnumType } from "../../../types/MEnumType";
|
|
12
|
+
import { assembly } from "../../../framework/Assembly";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* 下拉选择框
|
|
16
|
+
* 定义示例: {label:"1.1 您的性别是", name:"gendar", type:"enum", typeArg: [{label:"男", value:"M"},{label:"女", value:"F"}] },
|
|
17
|
+
*/
|
|
18
|
+
export class ASelector extends BaseViewer {
|
|
19
|
+
/** 开放输入框的值 */
|
|
20
|
+
_inputBoxValue: string = "";
|
|
21
|
+
_enums: MEnumField[];
|
|
22
|
+
|
|
23
|
+
constructor(p: MProp) {
|
|
24
|
+
super(p);
|
|
25
|
+
this._enums = _.cloneDeep(MUtil.option(this.props.schema));
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
element() {
|
|
29
|
+
const value =super.getValue();
|
|
30
|
+
|
|
31
|
+
const p = this.props.schema.props ?? {};
|
|
32
|
+
if (MUtil.phoneLike()) {
|
|
33
|
+
if (this.props.schema.openOption) {
|
|
34
|
+
return MUtil.error("手机上的ASelector还不支持openOption");
|
|
35
|
+
}
|
|
36
|
+
return <SelectBox
|
|
37
|
+
key={this.props.path}
|
|
38
|
+
data={value}
|
|
39
|
+
// @ts-ignore
|
|
40
|
+
options={this._enums.map(e => { return { label: e.label ?? e.value?.toString(), value: e.value } })}
|
|
41
|
+
{...p}
|
|
42
|
+
onChange={(newValue?: string) => { super.changeValue(newValue) }} />
|
|
43
|
+
} else {
|
|
44
|
+
let initIndex = undefined;
|
|
45
|
+
const options = this._enums.map((m, index) => {
|
|
46
|
+
if (value === m.value) {
|
|
47
|
+
initIndex = index;
|
|
48
|
+
}
|
|
49
|
+
return <Select.Option key={index} value={index}>{m.label ?? m.value}</Select.Option>
|
|
50
|
+
});
|
|
51
|
+
return <Select
|
|
52
|
+
key={this.props.path}
|
|
53
|
+
defaultValue={initIndex}
|
|
54
|
+
placeholder={super.getPlaceholder()}
|
|
55
|
+
bordered={this.props.hideBorder ? false : true}
|
|
56
|
+
//style={{minWidth: width + 20}}
|
|
57
|
+
dropdownRender={menu => {
|
|
58
|
+
return this.props.schema.openOption
|
|
59
|
+
? <>
|
|
60
|
+
{menu}
|
|
61
|
+
<Divider key="分割线" style={{ margin: '4px 0' }} />
|
|
62
|
+
<div key="开放选择" style={{ display: 'flex', flexWrap: 'nowrap', padding: 8 }}>
|
|
63
|
+
<MFieldViewer morph={this.props.morph} schema={this.props.schema.openOption} database={this} path="_inputBoxValue" parent={this.props.schema} forceValid={false} style={{ width: "inherit" }} />
|
|
64
|
+
<Button shape="circle" icon={<PlusOutlined />} onClick={() => {
|
|
65
|
+
let matchEnum = this._enums.findIndex(e => e.value === this._inputBoxValue);
|
|
66
|
+
if (matchEnum < 0) {
|
|
67
|
+
matchEnum = this._enums.findIndex(e => e.label === this._inputBoxValue);
|
|
68
|
+
}
|
|
69
|
+
if (matchEnum >= 0) { // 不能让用户输入了某个候选值
|
|
70
|
+
super.changeValue(this._enums[matchEnum].value);
|
|
71
|
+
} else {
|
|
72
|
+
this._enums.unshift({ value: this._inputBoxValue, label: this._inputBoxValue });
|
|
73
|
+
this.setState({});
|
|
74
|
+
}
|
|
75
|
+
}} />
|
|
76
|
+
</div>
|
|
77
|
+
</>
|
|
78
|
+
: menu
|
|
79
|
+
}}
|
|
80
|
+
style={{ width: "100%" }}
|
|
81
|
+
onChange={(vv: any) => super.changeValue(this._enums[vv].value)}
|
|
82
|
+
{...p}
|
|
83
|
+
>
|
|
84
|
+
{options}
|
|
85
|
+
</Select>;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Select } from "antd";
|
|
3
|
+
import { MUtil } from "../../../framework/MUtil";
|
|
4
|
+
import { BaseViewer } from "../../BaseViewer";
|
|
5
|
+
import _ from "lodash";
|
|
6
|
+
import { MSetType } from '../../../types/MSetType';
|
|
7
|
+
import { assembly } from '../../../framework/Assembly';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* 下拉框多选
|
|
11
|
+
* 示例:{label:"1.13 除爱人/对象之外,目前和您一起生活的家庭成员包括(多选):",name:"familyAccompany",type:"set", editor:"ASetSelector", option: "父亲 母亲 孩子 爱人/对象的父亲 爱人/对象的母亲 兄弟姐妹"},
|
|
12
|
+
* 值:["孩子", "父亲"]
|
|
13
|
+
*/
|
|
14
|
+
export class ASetSelector extends BaseViewer {
|
|
15
|
+
element() {
|
|
16
|
+
if(this.props.schema.openOption){
|
|
17
|
+
return MUtil.error("尚不支持openOption", this.props.schema);
|
|
18
|
+
}
|
|
19
|
+
let initValues = super.getValue();
|
|
20
|
+
|
|
21
|
+
// 给字段定义加上序号
|
|
22
|
+
const option:any[] = MUtil.option(this.props.schema);
|
|
23
|
+
option.forEach((e,index)=> e.index =index)
|
|
24
|
+
|
|
25
|
+
// 把value数组转换成序号数组
|
|
26
|
+
const value2Fields = _.keyBy(option, "value");
|
|
27
|
+
const initIndexes = initValues?.map(v => value2Fields[v].index);
|
|
28
|
+
|
|
29
|
+
const p = this.props.schema.props ?? {};
|
|
30
|
+
return <Select
|
|
31
|
+
key={this.props.path + "/" + this.state.ctrlVersion}
|
|
32
|
+
mode="multiple"
|
|
33
|
+
placeholder={this.props.schema.placeholder}
|
|
34
|
+
optionFilterProp="children"
|
|
35
|
+
style={this.props.style}
|
|
36
|
+
defaultValue={initIndexes}
|
|
37
|
+
onChange={(newIndexes: any)=>{
|
|
38
|
+
let add = []; // 新加进去的
|
|
39
|
+
let baseValues = [] // 原来就有的
|
|
40
|
+
newIndexes.forEach(idx => {
|
|
41
|
+
const v = option[idx].value;
|
|
42
|
+
if(initValues?.indexOf(v) >= 0){
|
|
43
|
+
baseValues.push(v);
|
|
44
|
+
} else {
|
|
45
|
+
add.push(v);
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
if(add.length > 0){
|
|
50
|
+
add.forEach(newOne => {
|
|
51
|
+
baseValues = MSetType.change(true, newOne, baseValues, this.props.schema);
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
super.changeValueEx(baseValues, newIndexes?.length != baseValues.length, true);
|
|
55
|
+
}}
|
|
56
|
+
{...p}
|
|
57
|
+
>
|
|
58
|
+
{option.map((item,index) => (
|
|
59
|
+
<Select.Option key={index} value={index}>
|
|
60
|
+
{item.label ?? item.value}
|
|
61
|
+
</Select.Option>
|
|
62
|
+
))}
|
|
63
|
+
</Select>;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Input } from "antd";
|
|
3
|
+
import { BaseViewer } from '../../BaseViewer';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* 手机号/邮箱等输入框
|
|
7
|
+
* cnPhone/tel/email
|
|
8
|
+
*/
|
|
9
|
+
export class ASpecInputBox extends BaseViewer {
|
|
10
|
+
element() {
|
|
11
|
+
const p = this.props.schema.props ?? {};
|
|
12
|
+
return <Input
|
|
13
|
+
{...super.antProp()}
|
|
14
|
+
defaultValue={super.getValue()}
|
|
15
|
+
onBlur={() => this.changeValueEx(super.getValue(), false, true)}
|
|
16
|
+
onChange={(v) => this.changeValueEx(v.target.value, false, false)}
|
|
17
|
+
{...p}
|
|
18
|
+
/>;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import _ from "lodash";
|
|
3
|
+
import { BaseViewer } from "../../BaseViewer";
|
|
4
|
+
import { TreeSelect } from 'antd';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* antd 的树型选择控件 https://ant.design/components/tree-select-cn/
|
|
8
|
+
* {
|
|
9
|
+
* "type": "array", arrayMember: {type: 'vl'}, toReadable: 'value.map(i=>i.label).join(",")',
|
|
10
|
+
* "editor": "ATreeSelect", "name": "tree1", "label": "多选树选择", props: {
|
|
11
|
+
* treeData: treeData,
|
|
12
|
+
* labelInValue: true,
|
|
13
|
+
* multiple: true
|
|
14
|
+
* }
|
|
15
|
+
* }
|
|
16
|
+
*/
|
|
17
|
+
export class ATreeSelect extends BaseViewer {
|
|
18
|
+
value: string;
|
|
19
|
+
|
|
20
|
+
constructor(p){
|
|
21
|
+
super(p);
|
|
22
|
+
this.value = super.getValue();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
element() {
|
|
26
|
+
const p = this.props.schema.props ?? {};
|
|
27
|
+
const props = {
|
|
28
|
+
defaultValue: this.value,
|
|
29
|
+
onChange: (value, label, extra) => {
|
|
30
|
+
if (p.onChange) p.onChange(value, label, extra)
|
|
31
|
+
super.changeValue(value);
|
|
32
|
+
},
|
|
33
|
+
onBlur: () => {
|
|
34
|
+
if (p.onBlur) p.onBlur()
|
|
35
|
+
super.changeValue(super.getValue())
|
|
36
|
+
},
|
|
37
|
+
...p,
|
|
38
|
+
}
|
|
39
|
+
return <TreeSelect style={{ width: "100%" }} {...props} />
|
|
40
|
+
}
|
|
41
|
+
}
|