lu-lowcode-package-form 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.
@@ -0,0 +1,191 @@
1
+ import React, { forwardRef, useEffect } from "react";
2
+
3
+ import { Form, Row, Col } from "antd";
4
+
5
+ function debounce(func, wait) {
6
+ let timeout;
7
+ return function(...args) {
8
+ clearTimeout(timeout);
9
+ timeout = setTimeout(() => func.apply(this, args), wait);
10
+ }
11
+ }
12
+ function groupArray(array, groupLength) {
13
+ console.log("groupArray array", array.length)
14
+ const newArray = [];
15
+ let tmpArray = [];
16
+
17
+ const fillReactElement = (groupLength, array) => {
18
+ const diff = groupLength - array.length;
19
+ array.push(...new Array(diff).fill(React.createElement('div')));
20
+ };
21
+
22
+ for (const currNode of array) {
23
+ const { cols = 1 } = currNode?.props || {};
24
+ tmpArray.push(currNode);
25
+
26
+ if (tmpArray.length === groupLength) {
27
+ newArray.push(tmpArray);
28
+ tmpArray = [];
29
+ } else if (tmpArray.length > groupLength) {
30
+ fillReactElement(groupLength, tmpArray);
31
+ newArray.push(tmpArray);
32
+ tmpArray = [currNode];
33
+ }
34
+ }
35
+
36
+ if (tmpArray.length > 0) {
37
+ fillReactElement(groupLength, tmpArray);
38
+ newArray.push(tmpArray);
39
+ }
40
+
41
+ return newArray;
42
+ }
43
+
44
+
45
+ const FormContainer = forwardRef(({ cols, children }, ref) => {
46
+ cols = cols ? cols : 1
47
+ const [form] = Form.useForm();
48
+ const [formContent, setFormContent] = React.useState(null);
49
+ const withMap = React.useRef(null);
50
+ React.useImperativeHandle(ref, () => {
51
+ return {
52
+ formRef: form,
53
+ initFieldsShow: initFieldsShow,
54
+ }
55
+ }, [])
56
+
57
+ useEffect(() => {
58
+ },[form])
59
+
60
+ useEffect(() => {
61
+ initWithMap()
62
+ setFormContent(getChildren())
63
+ }, [])
64
+
65
+
66
+ const initWithMap = () => {
67
+ withMap.current = new Map();
68
+ let childrenArray = React.Children.toArray(children);
69
+ for (let i = 0; i < childrenArray.length; i++) {
70
+ const { componentId, __id, _componentName, ...props } = childrenArray[i].props;
71
+ const name = componentId || __id;
72
+ withMap.current.set(name, {
73
+ children: childrenArray.filter((item) => {
74
+ return item.props.__withId == name || item.props.__withIds?.includes(name)
75
+ }),
76
+ show: true
77
+ });
78
+ }
79
+
80
+ initFieldsShow();
81
+
82
+ }
83
+
84
+ // 初始化字段的显示状态
85
+ const initFieldsShow = (reloadFields = false) => {
86
+ const fieldsValues = form.getFieldsValue();
87
+ withMap.current.keys().forEach(function (key) {
88
+ computeNeedShow(key,fieldsValues)
89
+ });
90
+
91
+ if (reloadFields) setFormContent(getChildren())
92
+ }
93
+
94
+ // 计算是否显示
95
+ const computeNeedShow = (name,fieldsValues) => {
96
+ let needRefresh = false;
97
+ if (withMap.current.has(name)) {
98
+ let withChildrens = withMap.current.get(name).children
99
+ withChildrens.forEach((item) => {
100
+ if (item.props.__withFunc && typeof item.props.__withFunc === 'function') {
101
+ let childrenShow = item.props.__withFunc(fieldsValues);
102
+ const name = item.props.componentId || item.props.__id;
103
+ if (withMap.current.has(name)) {
104
+ let childrenItem = withMap.current.get(name)
105
+ if (childrenItem.show != childrenShow) {
106
+ childrenItem.show = childrenShow
107
+ withMap.current.set(name, childrenItem)
108
+ needRefresh = true;
109
+ }
110
+ }
111
+ }
112
+ })
113
+ }
114
+ return needRefresh
115
+ }
116
+
117
+ const handleFieldsChange = debounce((changedFields, allFields) => {
118
+ // 获得所有字段值
119
+ const fieldsValues = form.getFieldsValue();
120
+ let needRefresh = false;
121
+ for (let index = 0; index < changedFields.length; index++) {
122
+ const element = changedFields[index];
123
+ if (element.name && element.name.length > 0 )
124
+ needRefresh = computeNeedShow(element.name[0],fieldsValues)
125
+ }
126
+ if (needRefresh) {
127
+ setFormContent(getChildren())
128
+ }
129
+ }, 100); // 字段变化时会调用三次此函数,使用 100 毫秒的防抖时间
130
+
131
+ const getChildren = () => {
132
+ let childrenArray = React.Children.toArray(children);
133
+ const newArray = groupArray(childrenArray.filter((item) => {
134
+ const name = item.props.componentId || item.props.__id
135
+ return withMap.current.has(name) && withMap.current.get(name).show
136
+ }), cols);
137
+ console.log("newArray", newArray)
138
+ const result = newArray.map((childs) => {
139
+ return (
140
+ <Row
141
+ key={childs?.[0]?.key}
142
+ gutter={[24, 24]}
143
+ >
144
+ {childs.map((child) => {
145
+ const { componentId, __id, _componentName, onChange, ...props } = child.props;
146
+ const name = componentId || __id;
147
+ const componentName = _componentName;
148
+
149
+
150
+ // 占整行内容
151
+ if (['FieldLayout', 'FieldSubTable'].includes(componentName)) {
152
+ // console.log('q=>child', child);
153
+ return (
154
+ <Col key={name} span={24} style={{ marginBottom: 0 }} >
155
+ {child}
156
+ </Col>
157
+ );
158
+ }
159
+ // console.log('child', child)
160
+ return (
161
+ <Col key={name} span={24 / childs.length} >
162
+ {name && (
163
+ <Form.Item
164
+ style={{ marginBottom: 0 }}
165
+ label=""
166
+ name={name}
167
+ rules={[{ required: props.isRequired, message: `${props.title}不能为空!` }]}
168
+ >
169
+
170
+ {child}
171
+ </Form.Item>
172
+ )}
173
+ </Col>
174
+ );
175
+ })}
176
+ </Row>
177
+ );
178
+ });
179
+
180
+ console.log("result", newArray)
181
+ return result;
182
+ };
183
+ return (
184
+ <Form form={form} className="form-container p-6 w-full h-full box-border overflow-auto" onFieldsChange={handleFieldsChange}>
185
+ {formContent}
186
+ </Form>
187
+ )
188
+ });
189
+
190
+ export default FormContainer;
191
+
@@ -0,0 +1,21 @@
1
+ import { Input, TextArea, Password, Search ,CodeMachine} from './field/input/index.jsx'
2
+ import { TreeSelect, Select } from './field/select/index.jsx'
3
+ import Custom from './field/custom/index.jsx'
4
+ import { default as FormContainer } from './form-container/index.jsx'
5
+ import { Checkbox ,CheckboxTree } from './field/checkbox/index.jsx'
6
+ import { default as RadioGrop } from './field/radio/index.jsx'
7
+ const Field = {
8
+ Input,
9
+ TextArea,
10
+ Password,
11
+ Search,
12
+ TreeSelect,
13
+ Select,
14
+ CodeMachine,
15
+ Checkbox,
16
+ CheckboxTree,
17
+ RadioGrop,
18
+ Custom,
19
+ }
20
+
21
+ export { FormContainer, Field }
package/src/index.css ADDED
@@ -0,0 +1,13 @@
1
+ body {
2
+ margin: 0;
3
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
4
+ 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
5
+ sans-serif;
6
+ -webkit-font-smoothing: antialiased;
7
+ -moz-osx-font-smoothing: grayscale;
8
+ }
9
+
10
+ code {
11
+ font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
12
+ monospace;
13
+ }
package/src/index.js ADDED
@@ -0,0 +1,17 @@
1
+ import React from 'react';
2
+ import ReactDOM from 'react-dom/client';
3
+ import './index.css';
4
+ import App from './App';
5
+ import reportWebVitals from './reportWebVitals';
6
+
7
+ const root = ReactDOM.createRoot(document.getElementById('root'));
8
+ root.render(
9
+ <React.StrictMode>
10
+ <App />
11
+ </React.StrictMode>
12
+ );
13
+
14
+ // If you want to start measuring performance in your app, pass a function
15
+ // to log results (for example: reportWebVitals(console.log))
16
+ // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
17
+ reportWebVitals();
package/src/logo.svg ADDED
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 841.9 595.3"><g fill="#61DAFB"><path d="M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 29.4-19.6-4.8-41-8.5-63.5-10.9-13.5-18.5-27.5-35.3-41.6-50 32.6-30.3 63.2-46.9 84-46.9V78c-27.5 0-63.5 19.6-99.9 53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7 0 51.4 16.5 84 46.6-14 14.7-28 31.4-41.3 49.9-22.6 2.4-44 6.1-63.6 11-2.3-10-4-19.7-5.2-29-4.7-38.2 1.1-67.9 14.6-75.8 3-1.8 6.9-2.6 11.5-2.6V78.5c-8.4 0-16 1.8-22.6 5.6-28.1 16.2-34.4 66.7-19.9 130.1-62.2 19.2-102.7 49.9-102.7 82.3 0 32.5 40.7 63.3 103.1 82.4-14.4 63.6-8 114.2 20.2 130.4 6.5 3.8 14.1 5.6 22.5 5.6 27.5 0 63.5-19.6 99.9-53.6 36.4 33.8 72.4 53.2 99.9 53.2 8.4 0 16-1.8 22.6-5.6 28.1-16.2 34.4-66.7 19.9-130.1 62-19.1 102.5-49.9 102.5-82.3zm-130.2-66.7c-3.7 12.9-8.3 26.2-13.5 39.5-4.1-8-8.4-16-13.1-24-4.6-8-9.5-15.8-14.4-23.4 14.2 2.1 27.9 4.7 41 7.9zm-45.8 106.5c-7.8 13.5-15.8 26.3-24.1 38.2-14.9 1.3-30 2-45.2 2-15.1 0-30.2-.7-45-1.9-8.3-11.9-16.4-24.6-24.2-38-7.6-13.1-14.5-26.4-20.8-39.8 6.2-13.4 13.2-26.8 20.7-39.9 7.8-13.5 15.8-26.3 24.1-38.2 14.9-1.3 30-2 45.2-2 15.1 0 30.2.7 45 1.9 8.3 11.9 16.4 24.6 24.2 38 7.6 13.1 14.5 26.4 20.8 39.8-6.3 13.4-13.2 26.8-20.7 39.9zm32.3-13c5.4 13.4 10 26.8 13.8 39.8-13.1 3.2-26.9 5.9-41.2 8 4.9-7.7 9.8-15.6 14.4-23.7 4.6-8 8.9-16.1 13-24.1zM421.2 430c-9.3-9.6-18.6-20.3-27.8-32 9 .4 18.2.7 27.5.7 9.4 0 18.7-.2 27.8-.7-9 11.7-18.3 22.4-27.5 32zm-74.4-58.9c-14.2-2.1-27.9-4.7-41-7.9 3.7-12.9 8.3-26.2 13.5-39.5 4.1 8 8.4 16 13.1 24 4.7 8 9.5 15.8 14.4 23.4zM420.7 163c9.3 9.6 18.6 20.3 27.8 32-9-.4-18.2-.7-27.5-.7-9.4 0-18.7.2-27.8.7 9-11.7 18.3-22.4 27.5-32zm-74 58.9c-4.9 7.7-9.8 15.6-14.4 23.7-4.6 8-8.9 16-13 24-5.4-13.4-10-26.8-13.8-39.8 13.1-3.1 26.9-5.8 41.2-7.9zm-90.5 125.2c-35.4-15.1-58.3-34.9-58.3-50.6 0-15.7 22.9-35.6 58.3-50.6 8.6-3.7 18-7 27.7-10.1 5.7 19.6 13.2 40 22.5 60.9-9.2 20.8-16.6 41.1-22.2 60.6-9.9-3.1-19.3-6.5-28-10.2zM310 490c-13.6-7.8-19.5-37.5-14.9-75.7 1.1-9.4 2.9-19.3 5.1-29.4 19.6 4.8 41 8.5 63.5 10.9 13.5 18.5 27.5 35.3 41.6 50-32.6 30.3-63.2 46.9-84 46.9-4.5-.1-8.3-1-11.3-2.7zm237.2-76.2c4.7 38.2-1.1 67.9-14.6 75.8-3 1.8-6.9 2.6-11.5 2.6-20.7 0-51.4-16.5-84-46.6 14-14.7 28-31.4 41.3-49.9 22.6-2.4 44-6.1 63.6-11 2.3 10.1 4.1 19.8 5.2 29.1zm38.5-66.7c-8.6 3.7-18 7-27.7 10.1-5.7-19.6-13.2-40-22.5-60.9 9.2-20.8 16.6-41.1 22.2-60.6 9.9 3.1 19.3 6.5 28.1 10.2 35.4 15.1 58.3 34.9 58.3 50.6-.1 15.7-23 35.6-58.4 50.6zM320.8 78.4z"/><circle cx="420.9" cy="296.5" r="45.7"/><path d="M520.5 78.1z"/></g></svg>
@@ -0,0 +1,13 @@
1
+ const reportWebVitals = onPerfEntry => {
2
+ if (onPerfEntry && onPerfEntry instanceof Function) {
3
+ import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
4
+ getCLS(onPerfEntry);
5
+ getFID(onPerfEntry);
6
+ getFCP(onPerfEntry);
7
+ getLCP(onPerfEntry);
8
+ getTTFB(onPerfEntry);
9
+ });
10
+ }
11
+ };
12
+
13
+ export default reportWebVitals;
@@ -0,0 +1,5 @@
1
+ // jest-dom adds custom jest matchers for asserting on DOM nodes.
2
+ // allows you to do things like:
3
+ // expect(element).toHaveTextContent(/react/i)
4
+ // learn more: https://github.com/testing-library/jest-dom
5
+ import '@testing-library/jest-dom';
@@ -0,0 +1,27 @@
1
+ const columnMapping = {
2
+ "varchar" : {
3
+ valueType : "text",
4
+ searchForm: "input"
5
+ }
6
+ };
7
+
8
+ const findColumnType = (columnType, property) => {
9
+ for (const key in columnMapping) {
10
+ if (columnType.startsWith(key)) {
11
+ return columnMapping[key][property];
12
+ }
13
+ }
14
+ return null;
15
+ };
16
+
17
+ const GetValueType = (columnType) => {
18
+ const valueType = columnMapping[columnType]?.valueType || findColumnType(columnType, "valueType");
19
+ return valueType || "text";
20
+ };
21
+
22
+ const GetSearchForm = (columnType) => {
23
+ const searchForm = columnMapping[columnType]?.searchForm || findColumnType(columnType, "searchForm");
24
+ return searchForm || "input";
25
+ };
26
+
27
+ export { GetValueType, GetSearchForm };
@@ -0,0 +1,186 @@
1
+ import { DeleteFilled, EditFilled, FileAddFilled } from "@ant-design/icons";
2
+ import { Tooltip } from "antd";
3
+
4
+ export const createPromiseWrapper = () => {
5
+ let resolve, reject;
6
+
7
+ const promise = new Promise((res, rej) => {
8
+ resolve = res;
9
+ reject = rej;
10
+ });
11
+
12
+ return { promise, resolve, reject };
13
+ };
14
+
15
+ /**
16
+ * 构造树型结构的菜单
17
+ * @param {*} data 数据源
18
+ */
19
+ export function handleTreeMenu(data) {
20
+ return constructTree(
21
+ data,
22
+ "id",
23
+ "title",
24
+ "parentId",
25
+ "routes",
26
+ 0,
27
+ (item) => ({
28
+ path: item.route || "/",
29
+ name: item.title,
30
+ icon: item.icon,
31
+ })
32
+ );
33
+ }
34
+
35
+ /**
36
+ * 构造树型结构数据
37
+ * @param {*} data 数据源
38
+ * @param {*} id id字段 默认 'id'
39
+ * @param {*} title 显示字段 默认 'title'
40
+ * @param {*} parentId 父节点字段 默认 'parentId'
41
+ * @param {*} children 孩子节点字段 默认 'children'
42
+ * @param {*} rootId 根Id 默认 0
43
+ * @param {*} disabledValue 禁用的节点值 默认 0
44
+ */
45
+ export function handleTree(
46
+ data,
47
+ id = "id",
48
+ title = "title",
49
+ parentId = "parentId",
50
+ children = "children",
51
+ rootId = 0,
52
+ disabledValue = 0
53
+ ) {
54
+ return constructTree(
55
+ data,
56
+ id,
57
+ title,
58
+ parentId,
59
+ children,
60
+ rootId,
61
+ (item) => ({
62
+ value: item[id],
63
+ title: item[title],
64
+ key: item[id],
65
+ disabled: item[id] === disabledValue,
66
+ })
67
+ );
68
+ }
69
+
70
+ /**
71
+ * 构造树型结构编辑数据
72
+ * @param {*} data 数据源
73
+ * @param {*} id id字段
74
+ * @param {*} parentId 父节点字段
75
+ * @param {*} children 孩子节点字段
76
+ * @param {*} title 显示字段
77
+ * @param {*} rootId 根Id
78
+ * @param {*} handle 处理函数
79
+ * @param {*} moduleName 模块名称
80
+ */
81
+ export function handleTreeEdit(
82
+ data,
83
+ id,
84
+ parentId,
85
+ children,
86
+ title,
87
+ rootId,
88
+ handle = { New: () => {}, Edit: () => {}, Del: () => {} },
89
+ moduleName = "节点"
90
+ ) {
91
+ const newNode = (node) => ({
92
+ title: (
93
+ <div
94
+ className="flex items-center text-[#1890ff]"
95
+ onClick={(event) => handle.New(event, node)}
96
+ >
97
+ <FileAddFilled className="mr-2" /> 添加子{moduleName}
98
+ </div>
99
+ ),
100
+ key: `${node ? node[id] : 0}-add`,
101
+ selectable: false,
102
+ });
103
+
104
+ const processNode = (item) => {
105
+ item.__original = { ...item };
106
+ item.__label = item[title];
107
+ item.__value = item[id];
108
+ return item;
109
+ };
110
+
111
+ const renderTitle = (child) => (
112
+ <div className="flex items-center">
113
+ {child.__label}
114
+ <Tooltip title={`编辑${moduleName}`} color="#666">
115
+ <EditFilled
116
+ className="ml-4 text-gray-200 hover:text-[#1890ff]"
117
+ onClick={(event) => handle.Edit(event, child)}
118
+ />
119
+ </Tooltip>
120
+ <Tooltip title={`删除${moduleName}`} color="red">
121
+ <DeleteFilled
122
+ className="ml-2 text-gray-200 hover:text-red-500"
123
+ onClick={(event) => handle.Del(event, child)}
124
+ />
125
+ </Tooltip>
126
+ <Tooltip title={`添加子${moduleName}`} color="#666">
127
+ <FileAddFilled
128
+ className="ml-2 text-gray-200 hover:text-[#1890ff]"
129
+ onClick={(event) => handle.New(event, child)}
130
+ />
131
+ </Tooltip>
132
+ </div>
133
+ );
134
+
135
+ const treeData = constructTree(
136
+ data,
137
+ id,
138
+ title,
139
+ parentId,
140
+ children,
141
+ rootId,
142
+ processNode
143
+ ).map((item) => {
144
+ item.title = renderTitle(item);
145
+ item.key = `node-${item[id]}`;
146
+ return item;
147
+ });
148
+
149
+ treeData.push(newNode());
150
+ return treeData;
151
+ }
152
+
153
+ /**
154
+ * 构造树型结构的通用函数
155
+ * @param {*} data 数据源
156
+ * @param {*} id id字段
157
+ * @param {*} title 显示字段
158
+ * @param {*} parentId 父节点字段
159
+ * @param {*} children 孩子节点字段
160
+ * @param {*} rootId 根Id
161
+ * @param {*} processItem 处理每个节点的函数
162
+ */
163
+ function constructTree(
164
+ data,
165
+ id,
166
+ title,
167
+ parentId,
168
+ children,
169
+ rootId,
170
+ processItem
171
+ ) {
172
+ if (!Array.isArray(data) || data.length === 0) return [];
173
+
174
+ const cloneData = data.map((item) => ({
175
+ ...item,
176
+ ...processItem(item),
177
+ }));
178
+
179
+ return cloneData.filter((father) => {
180
+ const branchArr = cloneData.filter(
181
+ (child) => father[id] === child[parentId]
182
+ );
183
+ father[children] = branchArr.length > 0 ? branchArr : [];
184
+ return father[parentId] === rootId;
185
+ });
186
+ }
@@ -0,0 +1,17 @@
1
+ module.exports = {
2
+ purge: [
3
+ './src/**/*.html',
4
+ './src/**/*.tsx',
5
+ './src/**/*.jsx',
6
+ './src/**/*.js',
7
+ ],
8
+ darkMode: false, // or 'media' or 'class'
9
+ theme: {
10
+ extend: {},
11
+ },
12
+ variants: {
13
+ extend: {},
14
+ },
15
+ plugins: [],
16
+ }
17
+
@@ -0,0 +1,41 @@
1
+ const path = require('path');
2
+
3
+ module.exports = {
4
+ mode: 'production',
5
+ entry: './src/components/index.jsx',
6
+ output: {
7
+ path: path.resolve(__dirname, 'dist'),
8
+ filename: 'index.js',
9
+ library: 'luLowcodePackageForm',
10
+ libraryTarget: 'umd',
11
+ globalObject: 'this',
12
+ },
13
+ resolve: {
14
+ extensions: ['.js', '.jsx'],
15
+ },
16
+ module: {
17
+ rules: [
18
+ {
19
+ test: /\.(js|jsx)$/,
20
+ exclude: /node_modules/,
21
+ use: {
22
+ loader: 'babel-loader',
23
+ },
24
+ },
25
+ ],
26
+ },
27
+ externals: {
28
+ react: {
29
+ commonjs: 'react',
30
+ commonjs2: 'react',
31
+ amd: 'react',
32
+ root: 'React',
33
+ },
34
+ 'react-dom': {
35
+ commonjs: 'react-dom',
36
+ commonjs2: 'react-dom',
37
+ amd: 'react-dom',
38
+ root: 'ReactDOM',
39
+ },
40
+ },
41
+ };