zy-react-library 1.0.1 → 1.0.3
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/components/FormBuilder/FormBuilder.d.ts +15 -3
- package/components/FormBuilder/FormBuilder.js +36 -1
- package/components/Map/MapSelector.d.ts +18 -0
- package/components/Map/MapSelector.js +204 -0
- package/components/Map/index.d.ts +16 -0
- package/components/Map/index.js +61 -0
- package/components/PreviewPdf/index.d.ts +1 -1
- package/components/Table/index.js +1 -1
- package/hooks/useTable/index.d.ts +1 -1
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { FormInstance, FormProps } from "antd/es/form";
|
|
2
2
|
import type { Gutter } from "antd/es/grid/row";
|
|
3
|
-
import type { FC } from "react";
|
|
3
|
+
import type { FC, ReactNode } from "react";
|
|
4
4
|
import type { FormOption, FormValues } from "./FormItemsRenderer";
|
|
5
5
|
|
|
6
6
|
/**
|
|
@@ -16,11 +16,23 @@ export interface FormBuilderProps extends Omit<FormProps, "form"> {
|
|
|
16
16
|
/** 占据栅格列数,默认 12 */
|
|
17
17
|
span?: number | string;
|
|
18
18
|
/** 表单实例(通过 Form.useForm() 创建) */
|
|
19
|
-
form
|
|
19
|
+
form?: FormInstance;
|
|
20
20
|
/** 自动生成必填规则,默认 true */
|
|
21
21
|
useAutoGenerateRequired?: boolean;
|
|
22
22
|
/** 表单提交回调 */
|
|
23
|
-
onFinish?: (values:
|
|
23
|
+
onFinish?: (values: FormValues) => void;
|
|
24
|
+
/** 是否显示操作按钮区域,默认 true */
|
|
25
|
+
showActionButtons?: boolean;
|
|
26
|
+
/** 提交按钮文字,默认为"提交" */
|
|
27
|
+
submitButtonText?: string;
|
|
28
|
+
/** 重置按钮文字,默认为"重置" */
|
|
29
|
+
resetButtonText?: string;
|
|
30
|
+
/** 是否显示提交按钮,默认 true */
|
|
31
|
+
showSubmitButton?: boolean;
|
|
32
|
+
/** 是否显示重置按钮,默认 true */
|
|
33
|
+
showResetButton?: boolean;
|
|
34
|
+
/** 自定义操作按钮组 */
|
|
35
|
+
customActionButtons?: ReactNode;
|
|
24
36
|
}
|
|
25
37
|
|
|
26
38
|
/**
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Form, Row } from "antd";
|
|
1
|
+
import { Button, Col, Form, Row, Space } from "antd";
|
|
2
2
|
import FormItemsRenderer from "./FormItemsRenderer";
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -13,9 +13,23 @@ const FormBuilder = (props) => {
|
|
|
13
13
|
labelCol = { span: 4 },
|
|
14
14
|
onFinish,
|
|
15
15
|
useAutoGenerateRequired = true,
|
|
16
|
+
showActionButtons = true,
|
|
17
|
+
submitButtonText = "提交",
|
|
18
|
+
resetButtonText = "重置",
|
|
19
|
+
showSubmitButton = true,
|
|
20
|
+
showResetButton = true,
|
|
21
|
+
customActionButtons,
|
|
22
|
+
form: externalForm,
|
|
16
23
|
...restProps
|
|
17
24
|
} = props;
|
|
18
25
|
|
|
26
|
+
const [internalForm] = Form.useForm();
|
|
27
|
+
const form = externalForm || internalForm;
|
|
28
|
+
|
|
29
|
+
const handleReset = () => {
|
|
30
|
+
form.resetFields();
|
|
31
|
+
};
|
|
32
|
+
|
|
19
33
|
return (
|
|
20
34
|
<Form
|
|
21
35
|
labelCol={labelCol}
|
|
@@ -23,6 +37,7 @@ const FormBuilder = (props) => {
|
|
|
23
37
|
wrapperCol={{ span: 24 - labelCol.span }}
|
|
24
38
|
onFinish={onFinish}
|
|
25
39
|
initialValues={values}
|
|
40
|
+
form={form}
|
|
26
41
|
{...restProps}
|
|
27
42
|
>
|
|
28
43
|
<Row gutter={gutter}>
|
|
@@ -32,6 +47,26 @@ const FormBuilder = (props) => {
|
|
|
32
47
|
useAutoGenerateRequired={useAutoGenerateRequired}
|
|
33
48
|
/>
|
|
34
49
|
</Row>
|
|
50
|
+
{showActionButtons && (
|
|
51
|
+
<Row gutter={gutter} style={{ marginTop: 24 }}>
|
|
52
|
+
<Col span={24} style={{ textAlign: "center" }}>
|
|
53
|
+
{customActionButtons || (
|
|
54
|
+
<Space>
|
|
55
|
+
{showSubmitButton && (
|
|
56
|
+
<Button type="primary" htmlType="submit">
|
|
57
|
+
{submitButtonText}
|
|
58
|
+
</Button>
|
|
59
|
+
)}
|
|
60
|
+
{showResetButton && (
|
|
61
|
+
<Button onClick={handleReset} style={{ marginRight: 8 }}>
|
|
62
|
+
{resetButtonText}
|
|
63
|
+
</Button>
|
|
64
|
+
)}
|
|
65
|
+
</Space>
|
|
66
|
+
)}
|
|
67
|
+
</Col>
|
|
68
|
+
</Row>
|
|
69
|
+
)}
|
|
35
70
|
</Form>
|
|
36
71
|
);
|
|
37
72
|
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { FC } from "react";
|
|
2
|
+
|
|
3
|
+
export interface MapSelectorProps {
|
|
4
|
+
/** 是否显示弹窗 */
|
|
5
|
+
visible: boolean;
|
|
6
|
+
/** 关闭弹窗回调 */
|
|
7
|
+
onClose: () => void;
|
|
8
|
+
/** 经度值 */
|
|
9
|
+
longitude?: number | string;
|
|
10
|
+
/** 纬度值 */
|
|
11
|
+
latitude?: number | string;
|
|
12
|
+
/** 确认选择回调 */
|
|
13
|
+
onConfirm?: (longitude: number | string, latitude: number | string) => void;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
declare const MapSelector: FC<MapSelectorProps>;
|
|
17
|
+
|
|
18
|
+
export default MapSelector;
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import { Button, Col, Form, Input, Modal, Row, Spin } from "antd";
|
|
2
|
+
import { useEffect, useRef, useState } from "react";
|
|
3
|
+
|
|
4
|
+
const MapSelector = ({
|
|
5
|
+
visible,
|
|
6
|
+
onClose,
|
|
7
|
+
longitude,
|
|
8
|
+
latitude,
|
|
9
|
+
onConfirm, // 新增确认回调
|
|
10
|
+
}) => {
|
|
11
|
+
const mapContainerRef = useRef(null);
|
|
12
|
+
const mapInstanceRef = useRef(null);
|
|
13
|
+
const [loading, setLoading] = useState(false);
|
|
14
|
+
const [currentLongitude, setCurrentLongitude] = useState(longitude || "");
|
|
15
|
+
const [currentLatitude, setCurrentLatitude] = useState(latitude || "");
|
|
16
|
+
const [localSearch, setLocalSearch] = useState("");
|
|
17
|
+
|
|
18
|
+
// 当外部经纬度变化时,更新内部状态
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
setCurrentLongitude(longitude || "");
|
|
21
|
+
setCurrentLatitude(latitude || "");
|
|
22
|
+
}, [longitude, latitude]);
|
|
23
|
+
|
|
24
|
+
// 初始化地图
|
|
25
|
+
const initMap = async () => {
|
|
26
|
+
if (!window.BMapGL) {
|
|
27
|
+
console.error("BMapGL is not loaded");
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
setLoading(true);
|
|
32
|
+
|
|
33
|
+
// 确保DOM已经渲染
|
|
34
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
35
|
+
|
|
36
|
+
if (mapContainerRef.current) {
|
|
37
|
+
// 只有在没有地图实例时才创建新地图
|
|
38
|
+
if (!mapInstanceRef.current) {
|
|
39
|
+
const map = new window.BMapGL.Map(mapContainerRef.current);
|
|
40
|
+
mapInstanceRef.current = map;
|
|
41
|
+
|
|
42
|
+
map.centerAndZoom(
|
|
43
|
+
new window.BMapGL.Point(
|
|
44
|
+
longitude || "119.69457721306945",
|
|
45
|
+
latitude || "39.940504336846665",
|
|
46
|
+
),
|
|
47
|
+
16,
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
map.enableScrollWheelZoom(true);
|
|
51
|
+
|
|
52
|
+
// 如果有初始坐标,添加标记
|
|
53
|
+
if (longitude && latitude) {
|
|
54
|
+
const point = new window.BMapGL.Point(longitude, latitude);
|
|
55
|
+
const marker = new window.BMapGL.Marker(point);
|
|
56
|
+
map.addOverlay(marker);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// 添加点击事件
|
|
60
|
+
map.addEventListener("click", (event) => {
|
|
61
|
+
map.clearOverlays();
|
|
62
|
+
const point = new window.BMapGL.Point(event.latlng.lng, event.latlng.lat);
|
|
63
|
+
const marker = new window.BMapGL.Marker(point);
|
|
64
|
+
map.addOverlay(marker);
|
|
65
|
+
setCurrentLatitude(event.latlng.lat);
|
|
66
|
+
setCurrentLongitude(event.latlng.lng);
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
setLoading(false);
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
// 搜索功能
|
|
75
|
+
const handleLocalSearch = () => {
|
|
76
|
+
if (localSearch && mapInstanceRef.current) {
|
|
77
|
+
const local = new window.BMapGL.LocalSearch(mapInstanceRef.current, {
|
|
78
|
+
renderOptions: { map: mapInstanceRef.current },
|
|
79
|
+
});
|
|
80
|
+
local.search(localSearch);
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
// 关闭弹窗
|
|
85
|
+
const handleClose = () => {
|
|
86
|
+
setLocalSearch("");
|
|
87
|
+
if (onClose)
|
|
88
|
+
onClose();
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
// 确认选择
|
|
92
|
+
const handleConfirm = () => {
|
|
93
|
+
if (onConfirm) {
|
|
94
|
+
onConfirm(currentLongitude, currentLatitude);
|
|
95
|
+
}
|
|
96
|
+
handleClose();
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
// 监听visible变化
|
|
100
|
+
useEffect(() => {
|
|
101
|
+
let initTimer;
|
|
102
|
+
|
|
103
|
+
if (visible) {
|
|
104
|
+
// 延迟初始化地图,确保DOM完全渲染
|
|
105
|
+
initTimer = setTimeout(() => {
|
|
106
|
+
initMap();
|
|
107
|
+
}, 100);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return () => {
|
|
111
|
+
if (initTimer) {
|
|
112
|
+
clearTimeout(initTimer);
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
}, [visible]);
|
|
116
|
+
|
|
117
|
+
const handleAfterClose = () => {
|
|
118
|
+
if (mapInstanceRef.current) {
|
|
119
|
+
try {
|
|
120
|
+
mapInstanceRef.current.clearOverlays();
|
|
121
|
+
mapInstanceRef.current.destroy();
|
|
122
|
+
mapInstanceRef.current = null;
|
|
123
|
+
}
|
|
124
|
+
catch (e) {
|
|
125
|
+
console.warn("Error destroying map on unmount:", e);
|
|
126
|
+
}
|
|
127
|
+
mapInstanceRef.current = null;
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
// 组件卸载时清理地图
|
|
132
|
+
useEffect(() => {
|
|
133
|
+
return () => {
|
|
134
|
+
handleAfterClose();
|
|
135
|
+
};
|
|
136
|
+
}, []);
|
|
137
|
+
|
|
138
|
+
return (
|
|
139
|
+
<Modal
|
|
140
|
+
open={visible}
|
|
141
|
+
title="坐标"
|
|
142
|
+
onCancel={handleClose}
|
|
143
|
+
onOk={handleConfirm}
|
|
144
|
+
width={800}
|
|
145
|
+
destroyOnHidden={false}
|
|
146
|
+
afterClose={handleAfterClose}
|
|
147
|
+
>
|
|
148
|
+
<Form labelAlign="right" labelCol={{ span: 6 }} wrapperCol={{ span: 18 }}>
|
|
149
|
+
<Row gutter={24}>
|
|
150
|
+
<Col span={12}>
|
|
151
|
+
<Form.Item label="关键字搜索">
|
|
152
|
+
<Input
|
|
153
|
+
value={localSearch}
|
|
154
|
+
onChange={e => setLocalSearch(e.target.value)}
|
|
155
|
+
allowClear
|
|
156
|
+
/>
|
|
157
|
+
</Form.Item>
|
|
158
|
+
</Col>
|
|
159
|
+
<Col span={12}>
|
|
160
|
+
<Form.Item label=" " colon={false}>
|
|
161
|
+
<Button type="primary" onClick={handleLocalSearch}>
|
|
162
|
+
搜索
|
|
163
|
+
</Button>
|
|
164
|
+
</Form.Item>
|
|
165
|
+
</Col>
|
|
166
|
+
</Row>
|
|
167
|
+
<Row gutter={24}>
|
|
168
|
+
<Col span={12}>
|
|
169
|
+
<Form.Item label="经度">
|
|
170
|
+
<Input disabled value={currentLongitude} />
|
|
171
|
+
</Form.Item>
|
|
172
|
+
</Col>
|
|
173
|
+
<Col span={12}>
|
|
174
|
+
<Form.Item label="纬度">
|
|
175
|
+
<Input disabled value={currentLatitude} />
|
|
176
|
+
</Form.Item>
|
|
177
|
+
</Col>
|
|
178
|
+
</Row>
|
|
179
|
+
</Form>
|
|
180
|
+
<div
|
|
181
|
+
ref={mapContainerRef}
|
|
182
|
+
style={{ width: "100%", height: "500px", position: "relative" }}
|
|
183
|
+
>
|
|
184
|
+
<Spin size="large" tip="地图正在加载中..." spinning={loading}>
|
|
185
|
+
<div style={{
|
|
186
|
+
position: "absolute",
|
|
187
|
+
top: 0,
|
|
188
|
+
left: 0,
|
|
189
|
+
right: 0,
|
|
190
|
+
bottom: 0,
|
|
191
|
+
display: "flex",
|
|
192
|
+
justifyContent: "center",
|
|
193
|
+
alignItems: "center",
|
|
194
|
+
backgroundColor: "rgba(0, 0, 0, 0.5)",
|
|
195
|
+
zIndex: 1000,
|
|
196
|
+
}}
|
|
197
|
+
/>
|
|
198
|
+
</Spin>
|
|
199
|
+
</div>
|
|
200
|
+
</Modal>
|
|
201
|
+
);
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
export default MapSelector;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { FC } from "react";
|
|
2
|
+
|
|
3
|
+
export interface MapProps {
|
|
4
|
+
/** 经度属性名,默认 longitude */
|
|
5
|
+
longitudeProps?: string;
|
|
6
|
+
/** 纬度属性名,默认 latitude */
|
|
7
|
+
latitudeProps?: string;
|
|
8
|
+
/** 经纬度变化回调 */
|
|
9
|
+
onConfirm?: (longitude: number | string, latitude: number | string) => void;
|
|
10
|
+
/** 经纬度是否必填,默认 true */
|
|
11
|
+
required?: boolean;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
declare const Map: FC<MapProps>;
|
|
15
|
+
|
|
16
|
+
export default Map;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { Button, Col, Form, Input, Row } from "antd";
|
|
2
|
+
import { useState } from "react";
|
|
3
|
+
import MapSelector from "./MapSelector";
|
|
4
|
+
|
|
5
|
+
const Map = (props) => {
|
|
6
|
+
const {
|
|
7
|
+
longitudeProps = "longitude",
|
|
8
|
+
latitudeProps = "latitude",
|
|
9
|
+
onConfirm,
|
|
10
|
+
required = true,
|
|
11
|
+
} = props;
|
|
12
|
+
|
|
13
|
+
const form = Form.useFormInstance();
|
|
14
|
+
const [mapVisible, setMapVisible] = useState(false);
|
|
15
|
+
const [currentLongitude, setCurrentLongitude] = useState(form.getFieldValue(longitudeProps) || "");
|
|
16
|
+
const [currentLatitude, setCurrentLatitude] = useState(form.getFieldValue(latitudeProps) || "");
|
|
17
|
+
|
|
18
|
+
const handleMapConfirm = (longitudeValue, latitudeValue) => {
|
|
19
|
+
setCurrentLongitude(longitudeValue);
|
|
20
|
+
setCurrentLatitude(latitudeValue);
|
|
21
|
+
form.setFieldsValue({
|
|
22
|
+
[longitudeProps]: longitudeValue,
|
|
23
|
+
[latitudeProps]: latitudeValue,
|
|
24
|
+
});
|
|
25
|
+
onConfirm?.(longitudeValue, latitudeValue);
|
|
26
|
+
setMapVisible(false);
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<>
|
|
31
|
+
<Row gutter={24}>
|
|
32
|
+
<Col span={12}>
|
|
33
|
+
<Form.Item label="经度" name={longitudeProps} style={{ flex: 1 }} rules={[{ required, message: "请选择经度" }]}>
|
|
34
|
+
<Input disabled />
|
|
35
|
+
</Form.Item>
|
|
36
|
+
</Col>
|
|
37
|
+
<Col span={12}>
|
|
38
|
+
<div style={{ display: "flex" }}>
|
|
39
|
+
<Form.Item label="纬度" name={latitudeProps} style={{ flex: 1 }} rules={[{ required, message: "请选择纬度" }]}>
|
|
40
|
+
<Input disabled />
|
|
41
|
+
</Form.Item>
|
|
42
|
+
<Form.Item label=" " colon={false}>
|
|
43
|
+
<Button type="primary" onClick={() => setMapVisible(true)}>
|
|
44
|
+
点击定位
|
|
45
|
+
</Button>
|
|
46
|
+
</Form.Item>
|
|
47
|
+
</div>
|
|
48
|
+
</Col>
|
|
49
|
+
</Row>
|
|
50
|
+
<MapSelector
|
|
51
|
+
visible={mapVisible}
|
|
52
|
+
onClose={() => setMapVisible(false)}
|
|
53
|
+
longitude={currentLongitude}
|
|
54
|
+
latitude={currentLatitude}
|
|
55
|
+
onConfirm={handleMapConfirm}
|
|
56
|
+
/>
|
|
57
|
+
</>
|
|
58
|
+
);
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export default Map;
|
|
@@ -2,7 +2,7 @@ import type { FC } from "react";
|
|
|
2
2
|
|
|
3
3
|
export interface PreviewPdfProps {
|
|
4
4
|
/** 文件列表,和 name、url 冲突 */
|
|
5
|
-
files?:
|
|
5
|
+
files?: Record<string, any>[];
|
|
6
6
|
/** 文件名字段名,传入 files 时会优先查找是否存在 name、fileName */
|
|
7
7
|
nameKey?: string;
|
|
8
8
|
/** 文件路径字段名,传入 files 时会优先查找是否存在 filePath */
|
|
@@ -11,7 +11,7 @@ function TablePro(props) {
|
|
|
11
11
|
const storeIndex = props.storeIndex || `${window.process.env.app.antd["ant-prefix"]}_${Math.random().toString(36).substring(2)}`;
|
|
12
12
|
function calcColumns() {
|
|
13
13
|
showIndex && columns.unshift(getIndexColumn(props.pagination));
|
|
14
|
-
return columns.map(item => ({
|
|
14
|
+
return columns.map(item => ({ align: useAlignCenter ? "center" : "left", ...item }));
|
|
15
15
|
}
|
|
16
16
|
return <Table storeIndex={storeIndex} columns={calcColumns()} {...restProps} />;
|
|
17
17
|
}
|
|
@@ -13,7 +13,7 @@ export interface UseTableOptions<TData extends Data, TParams extends Params> ext
|
|
|
13
13
|
/** 是否使用存储查询条件,默认是 */
|
|
14
14
|
useStorageQueryCriteria?: boolean;
|
|
15
15
|
/** 额外参数 */
|
|
16
|
-
params?:
|
|
16
|
+
params?: Record<string, any> | (() => Record<string, any>);
|
|
17
17
|
/** 表单数据转换函数,在每次请求之前调用,接收当前搜索的表单项,要求返回一个对象 */
|
|
18
18
|
transform?: (formData: FormValues) => FormValues;
|
|
19
19
|
/** 回调函数 */
|