@hzab/form-render-mobile 0.0.8 → 0.0.10

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hzab/form-render-mobile",
3
- "version": "0.0.8",
3
+ "version": "0.0.10",
4
4
  "description": "formily-form-render-mobile",
5
5
  "main": "lib",
6
6
  "scripts": {
@@ -43,6 +43,7 @@
43
43
  "@formily/antd-mobile": "1.0.0-beta.3",
44
44
  "@formily/core": "2.2.29",
45
45
  "@formily/react": "2.2.29",
46
+ "@hzab/data-model": "^0.0.3",
46
47
  "antd-mobile": "^5.32.0",
47
48
  "antd-mobile-icons": "^0.2.2",
48
49
  "nanoid": "^5.0.3"
@@ -1,11 +1,13 @@
1
1
  import { useState, useEffect } from "react";
2
2
  import { useField, observer } from "@formily/react";
3
3
  import { CascadePicker } from "antd-mobile";
4
- import { RightOutline } from "antd-mobile-icons";
4
+
5
+ import PickerRightOutline from "../PickerRightOutline";
6
+ import Placeholder from "../Placeholder";
5
7
 
6
8
  import "./index.less";
7
9
 
8
- function Cascader(props) {
10
+ export const Cascader = observer((props) => {
9
11
  const { disabled, readOnly, placeholder = "请选择", onChange } = props;
10
12
 
11
13
  const field = useField();
@@ -39,7 +41,7 @@ function Cascader(props) {
39
41
  }
40
42
 
41
43
  return (
42
- <div className="formily-cascader picker-box" onClick={onClick}>
44
+ <PickerRightOutline className="formily-cascader picker-box" onClick={onClick}>
43
45
  <CascadePicker
44
46
  {...props}
45
47
  style={{ pointerEvents: readOnly || disabled ? "none" : undefined }}
@@ -50,7 +52,7 @@ function Cascader(props) {
50
52
  >
51
53
  {(value) => {
52
54
  if (!value || value.length <= 0) {
53
- return <div className="placeholder">{placeholder}</div>;
55
+ return <Placeholder {...props} placeholder={placeholder} isPicker />;
54
56
  }
55
57
  return value?.reduce((pre, cur) => {
56
58
  if (!cur) {
@@ -63,9 +65,8 @@ function Cascader(props) {
63
65
  }, "");
64
66
  }}
65
67
  </CascadePicker>
66
- <RightOutline className="picker-right-icon" />
67
- </div>
68
+ </PickerRightOutline>
68
69
  );
69
- }
70
+ });
70
71
 
71
- export default observer(Cascader);
72
+ export default Cascader;
@@ -1,6 +1,7 @@
1
1
  import { useState, useEffect, useMemo } from "react";
2
2
  import { useField, observer } from "@formily/react";
3
3
  import { DatePicker as ADataPicker } from "antd-mobile";
4
+ import { Precision } from "antd-mobile/2x/es/components/date-picker/date-picker-utils";
4
5
  import { RightOutline } from "antd-mobile-icons";
5
6
  import dayjs from "dayjs";
6
7
 
@@ -9,6 +10,9 @@ import weekOfYear from "dayjs/plugin/weekOfYear";
9
10
 
10
11
  import { handleValue } from "./common/utils";
11
12
 
13
+ import PickerOutline from "../PickerRightOutline";
14
+ import Placeholder from "../Placeholder";
15
+
12
16
  import "./index.less";
13
17
 
14
18
  dayjs.extend(advancedFormat);
@@ -30,7 +34,18 @@ type fieldT = {
30
34
  dataSource: Array<Object>;
31
35
  };
32
36
 
33
- function DatePicker(props) {
37
+ type DatePickerPropsT = {
38
+ value: string | number | Object;
39
+ disabled: boolean;
40
+ readOnly: boolean;
41
+ placeholder: string;
42
+ format: string;
43
+ picker: string;
44
+ precision: Precision;
45
+ showTime: boolean;
46
+ onChange: (date: string | number) => void;
47
+ };
48
+ export const DatePicker = observer((props: DatePickerPropsT) => {
34
49
  const { value, disabled, readOnly, placeholder = "请选择", format, onChange } = props;
35
50
 
36
51
  const field: fieldT = useField();
@@ -40,25 +55,27 @@ function DatePicker(props) {
40
55
 
41
56
  const [visible, setVisible] = useState(false);
42
57
 
43
- const precision = useMemo(() => {
44
- if (props.picker) {
45
- return props.picker;
46
- }
58
+ const precision: Precision = useMemo(() => {
47
59
  if (props.precision) {
48
60
  return props.precision;
49
61
  }
50
62
  const {
51
- // time date month year
63
+ // time date month year decade quarter
52
64
  picker,
53
65
  showTime,
54
66
  } = props;
67
+ // TODO: 处理 季度、财年、时间 quarter decade time
55
68
  // 'year' | 'month' | 'day' | 'hour' | 'minute' | 'second' | 'week' | 'week-day'
56
- let _precision = "day";
69
+ // @ts-ignore 兼容 PC 端配置
70
+ let _precision: Precision = picker || "day";
71
+ if (picker === "date") {
72
+ _precision = "day";
73
+ }
57
74
  if ((!picker || picker === "date") && showTime) {
58
75
  _precision = "second";
59
76
  }
60
77
  return _precision;
61
- }, [props.precision, props.picker, props.mode, props.showTime]);
78
+ }, [props.precision, props.picker, props.showTime]);
62
79
 
63
80
  useEffect(() => {
64
81
  if (dataSource) {
@@ -88,7 +105,7 @@ function DatePicker(props) {
88
105
  const _value = handleValue(value, formatStr, precision);
89
106
 
90
107
  return (
91
- <div className="picker-box formily-date-picker" onClick={onClick}>
108
+ <PickerOutline className="picker-box formily-date-picker" onClick={onClick}>
92
109
  <ADataPicker
93
110
  {...props}
94
111
  value={_value}
@@ -98,11 +115,10 @@ function DatePicker(props) {
98
115
  precision={precision}
99
116
  onConfirm={onConfirm}
100
117
  >
101
- {(val) => (val ? dayjs(val).format(formatStr) : <div className="placeholder">{placeholder}</div>)}
118
+ {(val) => (val ? dayjs(val).format(formatStr) : <Placeholder {...props} placeholder={placeholder} isPicker />)}
102
119
  </ADataPicker>
103
- <RightOutline className="picker-right-icon" />
104
- </div>
120
+ </PickerOutline>
105
121
  );
106
- }
122
+ });
107
123
 
108
- export default observer(DatePicker);
124
+ export default DatePicker;
@@ -1,19 +1,13 @@
1
1
  import React from "react";
2
- import { connect, mapProps } from "@formily/react";
3
2
  import { Stepper } from "antd-mobile";
4
3
 
5
4
  import "./index.less";
6
5
 
7
- function NumberPicker({ precision, className, ...props }) {
6
+ export const NumberPicker = ({ precision, className, ...props }) => {
8
7
  if (props.readOnly) {
9
8
  return props.value || "";
10
9
  }
11
10
  return <Stepper className={`number-picker ${className}`} {...props} digits={precision} />;
12
- }
11
+ };
13
12
 
14
- export default connect(
15
- NumberPicker,
16
- mapProps((props, field) => {
17
- return { ...props, form: field.form, field };
18
- }),
19
- );
13
+ export default NumberPicker;
@@ -1,14 +1,8 @@
1
1
  import React from "react";
2
- import { connect, mapProps } from "@formily/react";
3
2
  import { Input } from "antd-mobile";
4
3
 
5
- function Password(props) {
6
- return <Input {...props} type="password" placeholder={props.placeholder || "请输入"} />;
7
- }
4
+ export const Password = (props) => {
5
+ return <Input {...props} type="password" placeholder={!props.readOnly ? props.placeholder || "请输入" : ""} />;
6
+ };
8
7
 
9
- export default connect(
10
- Password,
11
- mapProps((props, field) => {
12
- return { ...props, form: field.form, field };
13
- }),
14
- );
8
+ export default Password;
@@ -0,0 +1,7 @@
1
+ .form-item-picker-right-outline {
2
+ display: flex;
3
+ justify-content: space-between;
4
+ align-items: center;
5
+ .picker-right-icon {
6
+ }
7
+ }
@@ -0,0 +1,14 @@
1
+ import { RightOutline } from "antd-mobile-icons";
2
+
3
+ import "./index.less";
4
+
5
+ export const PickerOutline = (props) => {
6
+ return (
7
+ <div {...props} className={`form-item-picker-right-outline ${props.className}`}>
8
+ {props.children}
9
+ <RightOutline className="picker-right-icon" />
10
+ </div>
11
+ );
12
+ };
13
+
14
+ export default PickerOutline;
@@ -0,0 +1,3 @@
1
+ .form-item-placeholder {
2
+ color: #ccc;
3
+ }
@@ -0,0 +1,18 @@
1
+ import "./index.less";
2
+
3
+ export const Placeholder = (props) => {
4
+ const { value, placeholder, className, isPicker } = props;
5
+ if (value !== null && value !== undefined && value !== "") {
6
+ return value;
7
+ }
8
+ if (props.readOnly) {
9
+ return <span></span>;
10
+ }
11
+ return (
12
+ <div {...props} className={`form-item-placeholder ${className}`}>
13
+ {placeholder || isPicker ? "请选择" : "请输入"}
14
+ </div>
15
+ );
16
+ };
17
+
18
+ export default Placeholder;
@@ -3,13 +3,23 @@ import { useField, observer } from "@formily/react";
3
3
  import { Picker } from "antd-mobile";
4
4
  import { RightOutline } from "antd-mobile-icons";
5
5
 
6
+ import PickerRightOutline from "../PickerRightOutline";
7
+ import Placeholder from "../Placeholder";
8
+
9
+ import { formItemProps } from "../form-item";
10
+
6
11
  import "./index.less";
7
12
 
8
13
  type fieldT = {
9
14
  dataSource: Array<Object>;
10
15
  };
11
16
 
12
- function Select(props) {
17
+ type SelectPropsT = {
18
+ value: number | string;
19
+ onChange: (val: any) => void;
20
+ } & formItemProps;
21
+
22
+ export const Select = observer((props: SelectPropsT) => {
13
23
  const { value, disabled, readOnly, placeholder = "请选择", onChange } = props;
14
24
 
15
25
  const field: fieldT = useField();
@@ -45,11 +55,12 @@ function Select(props) {
45
55
  }
46
56
 
47
57
  return (
48
- <div className="formily-select picker-box" onClick={onClick}>
58
+ <PickerRightOutline className="formily-select picker-box" onClick={onClick}>
49
59
  <Picker
50
60
  {...props}
51
61
  value={Array.isArray(value) ? value : [value]}
52
62
  style={{ pointerEvents: readOnly || disabled ? "none" : undefined }}
63
+ // @ts-ignore
53
64
  columns={_options}
54
65
  visible={visible}
55
66
  onClose={onClose}
@@ -59,13 +70,12 @@ function Select(props) {
59
70
  return items && items.length > 0 && items[0] ? (
60
71
  items[0]?.label
61
72
  ) : (
62
- <div className="placeholder">{placeholder || "请选择"}</div>
73
+ <Placeholder {...props} placeholder={placeholder} isPicker />
63
74
  );
64
75
  }}
65
76
  </Picker>
66
- <RightOutline className="picker-right-icon" />
67
- </div>
77
+ </PickerRightOutline>
68
78
  );
69
- }
79
+ });
70
80
 
71
- export default observer(Select);
81
+ export default Select;
@@ -1,7 +1,6 @@
1
1
  import React from "react";
2
- import { connect, mapProps } from "@formily/react";
3
2
 
4
- function Text({ value, mode, content, className, ...props }) {
3
+ export const Text = ({ value, mode, content, className, ...props }) => {
5
4
  const tagName = mode === "normal" || !mode ? "div" : mode;
6
5
  return React.createElement(
7
6
  tagName,
@@ -11,11 +10,6 @@ function Text({ value, mode, content, className, ...props }) {
11
10
  },
12
11
  value || content,
13
12
  );
14
- }
13
+ };
15
14
 
16
- export default connect(
17
- Text,
18
- mapProps((props, field) => {
19
- return { ...props, form: field.form, field };
20
- }),
21
- );
15
+ export default Text;
@@ -0,0 +1,63 @@
1
+ export function getTimeColumns(opt: any = {}) {
2
+ const { use12Hours, isPM } = opt || {};
3
+ const configList = [
4
+ {
5
+ stepKey: "hourStep",
6
+ max: 23,
7
+ },
8
+ {
9
+ stepKey: "minuteStep",
10
+ max: 59,
11
+ },
12
+ {
13
+ stepKey: "secondStep",
14
+ max: 59,
15
+ },
16
+ ];
17
+
18
+ const columns = [[], [], []];
19
+
20
+ if (use12Hours) {
21
+ columns.push([
22
+ {
23
+ label: "AM",
24
+ value: "AM",
25
+ },
26
+ {
27
+ label: "PM",
28
+ value: "PM",
29
+ },
30
+ ]);
31
+ }
32
+
33
+ configList?.forEach((conf, idx) => {
34
+ let i = 0;
35
+ let { max, stepKey } = conf;
36
+ let step = opt[stepKey] || 1;
37
+ if (step <= 0 || step > max) {
38
+ step = 1;
39
+ }
40
+
41
+ if (use12Hours && stepKey === "hourStep") {
42
+ max = 11;
43
+ if (isPM) {
44
+ columns[idx].push({
45
+ label: "12",
46
+ value: "00",
47
+ });
48
+ i = step;
49
+ }
50
+ }
51
+
52
+ while (i <= max) {
53
+ let _val = `${i > 9 ? i : "0" + i}`;
54
+ columns[idx].push({
55
+ label: _val,
56
+ value: _val,
57
+ });
58
+ i += step;
59
+ }
60
+ });
61
+
62
+ return columns;
63
+ }
@@ -0,0 +1,79 @@
1
+ import { useState, useMemo } from "react";
2
+ import { useField, observer } from "@formily/react";
3
+ import { Picker } from "antd-mobile";
4
+ import PickerRightOutline from "../PickerRightOutline";
5
+ import Placeholder from "../Placeholder";
6
+ import { getTimeColumns } from "./common/utils";
7
+ import { formItemProps } from "../form-item";
8
+
9
+ type TimePickerPropsT = {
10
+ use12Hours: boolean;
11
+ } & formItemProps;
12
+
13
+ export const TimePicker = observer((props: TimePickerPropsT) => {
14
+ const { value, disabled, readOnly, placeholder = "请选择", use12Hours, onChange } = props;
15
+ const field: any = useField();
16
+ const {} = field;
17
+ const [visible, setVisible] = useState(false);
18
+ const [columns, setColumns] = useState(getTimeColumns(props));
19
+
20
+ function onConfirm(val) {
21
+ // 数据进行合并
22
+ let resVal = val?.join(":");
23
+ if (use12Hours) {
24
+ if (val[3] === "PM" && val[0] == 0) {
25
+ val[0] = 12;
26
+ }
27
+ resVal = [...val]?.slice(0, 3)?.join(":");
28
+ resVal += ` ${val[3] || ""}`;
29
+ }
30
+ onChange && onChange(resVal);
31
+ }
32
+
33
+ function onSelect(v, ...args) {
34
+ if (use12Hours && v?.[3] == "PM") {
35
+ setColumns(getTimeColumns({ ...props, isPM: true }));
36
+ } else {
37
+ setColumns(getTimeColumns(props));
38
+ }
39
+ }
40
+
41
+ function onClick() {
42
+ if (readOnly || disabled) {
43
+ return;
44
+ }
45
+ setVisible((val) => {
46
+ return !val;
47
+ });
48
+ }
49
+
50
+ // 拆分字符串数据并转为数字格式
51
+ let pickerVal = value?.split(":");
52
+ if (use12Hours && pickerVal && pickerVal.length > 0) {
53
+ const t = pickerVal[2];
54
+ pickerVal.splice(2, 1, t.split(" ")?.[0]);
55
+ pickerVal.push(t.split(" ")?.[1]);
56
+ if (pickerVal[3] === "PM" && pickerVal[0] == 12) {
57
+ pickerVal[0] = "00";
58
+ }
59
+ }
60
+
61
+ return (
62
+ <PickerRightOutline className="picker-box formily-time-picker" onClick={onClick}>
63
+ <Picker
64
+ {...props}
65
+ value={pickerVal}
66
+ visible={visible}
67
+ columns={columns}
68
+ onSelect={onSelect}
69
+ onClose={() => {
70
+ setVisible(false);
71
+ }}
72
+ onConfirm={onConfirm}
73
+ />
74
+ <Placeholder {...props} value={value} placeholder={placeholder} />
75
+ </PickerRightOutline>
76
+ );
77
+ });
78
+
79
+ export default TimePicker;
@@ -1,4 +1,4 @@
1
- import axios from "axios";
1
+ import { axios } from "@hzab/data-model";
2
2
 
3
3
  export function getSignature(opt = {}) {
4
4
  const { serverUrl = "/api/v1/user/oss/getWebOssConfig" } = opt;
@@ -89,3 +89,15 @@ export function checkUrlSuffix(url, types = [], caseSensitive) {
89
89
  return true;
90
90
  }
91
91
  }
92
+
93
+ function getArr(value) {
94
+ return Array.isArray(value) ? value : (value && [value]) || [];
95
+ }
96
+
97
+ export function handleMaxCount(fileList, maxCount) {
98
+ let list = getArr(fileList);
99
+ if (maxCount > 0 && list.length > maxCount) {
100
+ list = list.slice(maxCount);
101
+ }
102
+ return list;
103
+ }
@@ -1,8 +1,6 @@
1
- import { connect, mapProps } from "@formily/react";
1
+ import UploaderCom from "./uploader";
2
2
 
3
- import Uploader from "./uploader";
4
-
5
- function UploaderCom(props) {
3
+ export const Uploader = (props) => {
6
4
  const { field = {}, onChange, value } = props;
7
5
  const { name, mode, componentProps = {} } = field;
8
6
  const { multiple } = componentProps;
@@ -29,12 +27,7 @@ function UploaderCom(props) {
29
27
  ...componentProps,
30
28
  };
31
29
 
32
- return <Uploader {..._props} onChange={onUploadChange} />;
33
- }
30
+ return <UploaderCom {..._props} onChange={onUploadChange} />;
31
+ };
34
32
 
35
- export default connect(
36
- UploaderCom,
37
- mapProps((props, field) => {
38
- return { ...props, form: field.form, field };
39
- }),
40
- );
33
+ export default Uploader;
@@ -7,7 +7,7 @@ import Video from "./video";
7
7
  import Image from "./Image";
8
8
 
9
9
  import { getImage, getVideo, getAudio } from "./common/cordova-camera";
10
- import { getFileURL, checkImageUrl, checkVideoUrl, checkAudioUrl } from "./common/utils";
10
+ import { getFileURL, checkImageUrl, checkVideoUrl, checkAudioUrl, handleMaxCount } from "./common/utils";
11
11
  import { handleOssUpload } from "./common/ossUpload";
12
12
 
13
13
  import "./uploader.less";
@@ -39,16 +39,16 @@ function Uploader(props) {
39
39
  onCountExceed,
40
40
  } = props;
41
41
  const [curAccept, setCurAccept] = useState(accept);
42
- const [fileList, setFileList] = useState(Array.isArray(value) ? value : (value && [value]) || []);
42
+ const [fileList, setFileList] = useState(handleMaxCount(handleMaxCount(value)));
43
43
  const [actionSheetVisible, setActionSheetVisible] = useState(false);
44
44
  useEffect(() => {
45
- setFileList(Array.isArray(value) ? value : (value && [value]) || []);
45
+ setFileList(handleMaxCount(value));
46
46
  }, [value]);
47
47
 
48
48
  async function onFileChange(e) {
49
49
  e.persist();
50
50
  const { files: rawFiles } = e.target;
51
- let files = [].slice.call(rawFiles);
51
+ let files = [...fileList, ...[].slice.call(rawFiles)];
52
52
  // 超出个数限制只保留前面的 maxCount 个
53
53
  if (maxCount > 0 && files.length > maxCount) {
54
54
  onCountExceed && onCountExceed(files.length - maxCount);
@@ -85,6 +85,7 @@ function Uploader(props) {
85
85
  if (type !== "del" && multiple) {
86
86
  _files = [...fileList, ..._files];
87
87
  }
88
+ _files = handleMaxCount(files);
88
89
  setFileList(_files);
89
90
  onChange && onChange(_files && _files.length > 0 ? _files : undefined);
90
91
  }
@@ -194,6 +195,9 @@ function Uploader(props) {
194
195
  capture={props.capture}
195
196
  ></input>
196
197
  {fileList.map((_it, idx) => {
198
+ if (!_it) {
199
+ return null;
200
+ }
197
201
  let it = _it;
198
202
  // 数据兼容处理
199
203
  if (typeof it === "string") {
@@ -203,7 +207,7 @@ function Uploader(props) {
203
207
  name: it,
204
208
  };
205
209
  }
206
- const { type, name, url = it?.ossUrl } = it;
210
+ const { type, name, url = it?.ossUrl } = it || {};
207
211
  let src = "";
208
212
  let fileType = "";
209
213
  let downLoadUrl = undefined;
@@ -227,19 +231,19 @@ function Uploader(props) {
227
231
  } else {
228
232
  // 图片
229
233
  if (type?.startsWith("image/")) {
230
- src = getFileURL(src);
234
+ src = getFileURL(it);
231
235
  fileType = TYPE_IMG;
232
236
  }
233
237
 
234
238
  // 视频
235
239
  if (type?.startsWith("video/")) {
236
- src = getFileURL(src);
240
+ src = getFileURL(it);
237
241
  fileType = TYPE_VIDEO;
238
242
  // TODO: 确认下载逻辑
239
243
  downLoadUrl = src;
240
244
  }
241
245
  if (type?.startsWith("audio/")) {
242
- src = getFileURL(src);
246
+ src = getFileURL(it);
243
247
  fileType = TYPE_AUDIO;
244
248
  // TODO: 确认下载逻辑
245
249
  downLoadUrl = src;
@@ -268,7 +272,7 @@ function Uploader(props) {
268
272
  return <audio src={src} controls></audio>;
269
273
  }
270
274
 
271
- return it.name;
275
+ return it?.name;
272
276
  })}
273
277
  {disabled ||
274
278
  readOnly ||
@@ -0,0 +1,7 @@
1
+ export type formItemProps = {
2
+ value: any;
3
+ disabled: boolean;
4
+ readOnly: boolean;
5
+ placeholder: string;
6
+ onChange: (val: any) => void;
7
+ };
@@ -1,13 +1,11 @@
1
- import Text from "./Text";
2
- import Select from "./Select";
3
- import Cascader from "./Cascader";
4
- import NumberPicker from "./NumberPicker";
5
- import DatePicker from "./DatePicker";
6
- import Password from "./Password";
7
- import Uploader from "./Uploader";
8
-
9
1
  export * from "./ArrayCards";
10
2
  export * from "./ArrayTable";
3
+ export * from "./Cascader";
4
+ export * from "./DatePicker";
11
5
  export * from "./FormCollapse";
12
-
13
- export { NumberPicker, Password, Select, Cascader, DatePicker, Text, Uploader };
6
+ export * from "./NumberPicker";
7
+ export * from "./Password";
8
+ export * from "./Select";
9
+ export * from "./Text";
10
+ export * from "./TimePicker";
11
+ export * from "./Uploader";
package/src/index.less CHANGED
@@ -16,4 +16,13 @@
16
16
  align-items: center;
17
17
  }
18
18
  }
19
+
20
+ &.form-render-detail {
21
+ .adm-formily-item-asterisk {
22
+ display: none;
23
+ }
24
+ .adm-selector-item-disabled {
25
+ opacity: 1;
26
+ }
27
+ }
19
28
  }