소스 검색

员工管理和楼盘选择下拉框改造

傅行帆 5 년 전
부모
커밋
1f8042c496

+ 2
- 2
src/components/SelectButton/BuildSelect.jsx 파일 보기

@@ -23,11 +23,11 @@ const BuildingSelect = props => {
23 23
   const [data, setData] = useState([])
24 24
   const [value, setValue] = useState([])
25 25
   useEffect(() => {
26
-    getCityList();
26
+    getBuildList();
27 27
   }, [props.value])
28 28
 
29 29
 
30
-  const getCityList = e => {
30
+  const getBuildList = e => {
31 31
     request({ ...apis.building.buildingSelect, params: { pageNum: 1, pageSize: 999 } }).then(data => {
32 32
         setData(data.records)
33 33
         checkValue(data.records)

+ 82
- 0
src/components/XForm2/Form.jsx 파일 보기

@@ -0,0 +1,82 @@
1
+import React from 'react';
2
+import PropTypes from 'prop-types';
3
+import { Form, Button, Select } from 'antd';
4
+import XItem2 from './Item';
5
+import { isFunction, isEmpty } from './utils';
6
+
7
+const formItemLayout = {
8
+  labelCol: {
9
+    xs: { span: 24 },
10
+    sm: { span: 8 },
11
+  },
12
+  wrapperCol: {
13
+    xs: { span: 24 },
14
+    sm: { span: 16 },
15
+  },
16
+};
17
+
18
+class Form2 extends React.PureComponent {
19
+  state = {}
20
+
21
+  handleSubmit = e => {
22
+    e.preventDefault();
23
+    const { form, onError, onSubmit } = this.props
24
+
25
+    form.validateFieldsAndScroll((err, values) => {
26
+      if (err) {
27
+        if (isFunction(onError)) {
28
+          onError(err)
29
+        } else {
30
+          window.console.error(err)
31
+        }
32
+      } else {
33
+        if (isFunction(onSubmit)) {
34
+          onSubmit(values)
35
+        }
36
+      }
37
+    });
38
+  }
39
+
40
+  handleCancel = e => {
41
+    e.preventDefault();
42
+
43
+    if (isFunction(this.props.onCancel)) {
44
+      this.props.onCancel()
45
+    }
46
+  }
47
+
48
+  renderDefaultAction = (submitBtn, cancelBtn) => {
49
+    let FieldSubmit = null
50
+    let FieldCancel = null
51
+    if (submitBtn !== false) {
52
+      FieldSubmit = <Button htmlType="submit" type="primary">提交</Button>
53
+    }
54
+    if (cancelBtn !== false) {
55
+      FieldCancel = <Button htmlType="button" onClick={this.handleCancel} style={{ marginLeft: '48px' }}>取消</Button>
56
+    }
57
+
58
+    return FieldSubmit || FieldCancel ? <XItem2 action render={() => (<>{FieldSubmit}{FieldCancel}</>)} /> : null
59
+  }
60
+
61
+  render () {
62
+    const {fields, form, children, submitBtn, cancelBtn, ...formProps} = this.props;
63
+
64
+    console.log('---------render parent------------')
65
+
66
+    return (
67
+      <Form {...formItemLayout} {...formProps} onSubmit={this.handleSubmit}>
68
+        {children}
69
+        {/* {FeildItems} */}
70
+        {this.renderDefaultAction(submitBtn, cancelBtn)}
71
+      </Form>
72
+    );
73
+  }
74
+}
75
+
76
+Form2.propTypes = {
77
+  fields: PropTypes.array.isRequired,
78
+  form: PropTypes.object.isRequired,
79
+  dataset: PropTypes.object,
80
+}
81
+
82
+export default Form2;

+ 185
- 0
src/components/XForm2/Item.jsx 파일 보기

@@ -0,0 +1,185 @@
1
+import React from 'react';
2
+import { Form, Select } from 'antd';
3
+import PropTypes from 'prop-types'; 
4
+import { hasOwnProperty, isFunction, isBoolean } from './utils';
5
+
6
+const AntFormItem = Form.Item
7
+
8
+const tailFormItemLayout = {
9
+  wrapperCol: {
10
+    xs: {
11
+      span: 24,
12
+      offset: 0,
13
+    },
14
+    sm: {
15
+      span: 16,
16
+      offset: 8,
17
+    },
18
+  },
19
+};
20
+
21
+class Item extends React.PureComponent {
22
+  state = {
23
+    allFieldValues: this.getFormValues(),
24
+  }
25
+
26
+  componentDidMount() {
27
+    if (this.props.formChange) {
28
+      this.props.formChange((p, c, values) => {
29
+        this.setState({ allFieldValues: values || this.getFormValues()})
30
+      })
31
+    }
32
+  }
33
+
34
+  getFormValues() {
35
+    if (this.props.form) {
36
+      return this.props.form.getFieldsValue() || {}
37
+    }
38
+
39
+    return {}
40
+  }
41
+
42
+  getActualValue(fn, defVal) {
43
+    const val = isFunction(fn) ? fn(this.state.allFieldValues, this.props) : fn
44
+    return val === undefined ? defVal : val
45
+  }
46
+
47
+  getValueFromEvent = e => {
48
+    if (hasOwnProperty(e, 'checked')) {
49
+      return e.checked;
50
+    } else if (hasOwnProperty(e, 'target')) {
51
+      return e.target.value;
52
+    } else {
53
+      return e;
54
+    }
55
+  }
56
+
57
+  renderSelectOptions (dict) {
58
+    return (dict || []).map(x => <Select.Option key={x.value} value={x.value}>{x.label}</Select.Option>)
59
+  }
60
+
61
+  renderField() {
62
+    const {
63
+      form,
64
+      name,
65
+      node,
66
+      children,
67
+      placeholder,
68
+      render,
69
+      rules,
70
+      value,
71
+      action,
72
+      props,
73
+      options,
74
+    } = this.props || {}
75
+
76
+    const config = {
77
+      rules,
78
+      initialValue: value,
79
+      getValueFromEvent: this.getValueFromEvent,
80
+    }
81
+
82
+    const fieldProps = {
83
+      placeholder: Array.isArray(placeholder) ? placeholder(this.state.allFieldValues, this.props) : placeholder,
84
+      style: { width: '100%' },
85
+      ...(props || {})
86
+    }
87
+
88
+    const Childs = Array.isArray(options) ? this.renderSelectOptions(options) : (isFunction(children) ? children() : (children || null));
89
+    const Field = isFunction(render) ? render(this.props) : React.createElement(node, fieldProps, Childs);
90
+
91
+    return action ? Field : form.getFieldDecorator(name, config)(Field)
92
+  }
93
+
94
+  render () {
95
+    const {
96
+      form,
97
+      label,
98
+      name,
99
+      node,
100
+      children,
101
+      placeholder,
102
+      render,
103
+      rules,
104
+      value,
105
+      action,
106
+      props = {},
107
+      hidden = false,
108
+      reportChange,
109
+      listenChange,
110
+      options,
111
+      ...restProps
112
+    } = this.props;
113
+
114
+    const initialValue = value === undefined ? {} : { initialValue: value }
115
+    
116
+    const config = {
117
+      rules,
118
+      getValueFromEvent: this.getValueFromEvent,
119
+      ...initialValue,
120
+    }
121
+    
122
+    // 以下支持可变内容
123
+    // 暂时只支持 hidden, disable, label 的可变
124
+    const hiddenActual = isFunction(hidden) ? hidden(this.state.allFieldValues, this.props) : (
125
+      isBoolean(hidden) ? hidden : false
126
+    );
127
+    const labelActual = isFunction(label) ? label(this.state.allFieldValues, this.props) : label
128
+    const disabledActual = isFunction(props.disabled) ? props.disabled(this.state.allFieldValues, this.props) : (
129
+      isBoolean(props.disabled) ? props.disabled : false
130
+    )
131
+
132
+    // 没有类型与组件, 生成隐藏字段
133
+    if (name && hiddenActual === true) {
134
+      if (form && form.getFieldDecorator) {
135
+        form.getFieldDecorator(name, config);
136
+      }
137
+      return null;
138
+    }
139
+
140
+    const Field = this.renderField()
141
+
142
+    if (!labelActual && !name && !action) return !hiddenActual ? Field : null;
143
+    
144
+    const itemProps = action ? { ...restProps, ...tailFormItemLayout, disabled: disabledActual } : { ...restProps, disabled: disabledActual }
145
+
146
+    return !hiddenActual ? (
147
+      <AntFormItem label={labelActual} {...itemProps}>{Field}</AntFormItem>
148
+    ) : null
149
+  }
150
+};
151
+
152
+Item.propTypes = {
153
+  formChange: PropTypes.func,
154
+  form: PropTypes.object,
155
+  label: PropTypes.oneOfType([
156
+    PropTypes.node,
157
+    PropTypes.func,
158
+  ]),
159
+  placeholder: PropTypes.oneOfType([
160
+    PropTypes.string,
161
+    PropTypes.func,
162
+  ]),
163
+  name: PropTypes.string,
164
+  node: PropTypes.oneOfType([
165
+    PropTypes.elementType,
166
+    PropTypes.func,
167
+  ]),
168
+  children: PropTypes.oneOfType([
169
+    PropTypes.element,
170
+    PropTypes.func,
171
+    PropTypes.arrayOf(PropTypes.element),
172
+  ]),
173
+  render: PropTypes.func,
174
+  rules: PropTypes.array,
175
+  value: PropTypes.any,
176
+  action: PropTypes.bool,
177
+  props: PropTypes.object,
178
+  options: PropTypes.array,
179
+  hidden: PropTypes.oneOfType([
180
+    PropTypes.bool,
181
+    PropTypes.func,
182
+  ]),
183
+}
184
+
185
+export default Item;

+ 101
- 0
src/components/XForm2/README.md 파일 보기

@@ -0,0 +1,101 @@
1
+# XForm2
2
+基于原来的 XForm 思想, 重新封装了一个表单。主要解决以下几个问题
3
+
4
+* 支持任意的表单组件, 不再受限
5
+* 支持组件的动态显示
6
+
7
+## 引用
8
+```jsx
9
+import createForm from 'path/to/xform2';
10
+```
11
+
12
+## 使用
13
+### 创建 Form
14
+
15
+```jsx
16
+// https://ant.design/components/form-cn/#Form.create(options)
17
+const options = {}
18
+
19
+// options 也可以不传
20
+const Form = createForm(options)
21
+
22
+// 这样就可以使用 Form 了
23
+class MyComponent extends React.Component {
24
+  render() {
25
+    return (
26
+      <Form onSubmit={xxx}></Form>
27
+    );
28
+  }
29
+}
30
+
31
+```
32
+
33
+生成的 Form 有如下 Props 设置
34
+
35
+| prop | 说明 | 类型 |
36
+|---|---|---|
37
+| onSubmit | 表单提交 | function(allFieldValues) {} |
38
+| onCancel | 表单取消 | function() {} |
39
+| submitBtn | 是否显示提交按钮 | 布尔值: 默认 true |
40
+| cancelBtn | 是否显示取消按钮 | 布尔值: 默认 true |
41
+| ... | 其他 [Antd Form](https://ant.design/components/form-cn/#Form) 支持的属性 |  |
42
+
43
+### 添加表单字段
44
+
45
+```jsx
46
+import React from 'react';
47
+import { Input, Select } from 'antd';
48
+
49
+const fields = [
50
+        {
51
+          label: '标题',
52
+          name: 'title',
53
+          placeholder: '填写标题',
54
+          node: Input,
55
+          value: data.title,
56
+        },
57
+        {
58
+          label: '发布位置',
59
+          name: 'showPosition',
60
+          node: Select,
61
+          options: [
62
+            { 
63
+              label: '首页',
64
+              value: 'index',
65
+            },
66
+            {
67
+              label: '积分商城',
68
+              value: 'mall',
69
+            }
70
+          ],
71
+          value: data.showPosition,
72
+          hidden: values => values.title === 'foobar',
73
+        },
74
+      ];
75
+
76
+
77
+// some codes
78
+// <Form onSubmit={xxx} fields={fields}></Form>
79
+
80
+```
81
+
82
+表单字段是通过 `json` 配置实现的。支持的属性有
83
+
84
+| 属性 | 说明 | 类型 |
85
+|---|---|---|
86
+| label | 字段文字标题 | string 或者 function(allFieldValues, props), 非必填 |
87
+| name | 字段 name | string, 非必填 |
88
+| node | 字段节点, 注意不是 `React.Element`。也就是不能是 `<Input />`, 而是 `Input` | react elementType 或者 可以生成 `React.Element` 的函数, 非必填 |
89
+| children | 字段如果复杂, 需要子元素。 子元素不能是节点, 必须是 `React.Element` | react element 或者 函数, 非必填 |
90
+| options | 如果 node 是 `antd.Select`, 这里提供了一个快捷方式自定义 `option` 列表 | array, 元素格式 `{ label: xxx, value: xxx }`, 非必填 |
91
+| render | 自定义的字段渲染逻辑 | function(props) {}, 非必填 |
92
+| action | 是否为功能类组件, 比如按钮 | boolean: 默认 false |
93
+| value | 字段默认值 | any: 非必填 |
94
+| placeholder | 字段 placeholder | string 或者 function(allFieldValues, props), 非必填 |
95
+| rules | 字段校验规则, 与 [antd.Form](https://ant.design/components/form-cn/#%E6%A0%A1%E9%AA%8C%E8%A7%84%E5%88%99) 的约定一致 | 类型 array: 非必填 |
96
+| props | 其余需要传入字段组件的属性或者事件 | object: 非必填 |
97
+| hidden | 如果有 `name`, 此值又是 `true`. 那么该字段则为隐藏字段放入 form 中。另外, 则为设置组件的显示, 隐藏 | boolean 或者 function(allFieldValues, props) : 非必填, 默认 false |
98
+
99
+## 注意事项
100
+* 字段组件支持自定义封装, 但是封装组件必须满足2个条件。1、值传递通过 `value` 属性, 2、提供 `onChange` 事件
101
+* 字段组件必须支持 `ref` 传递. 因此函数式组件, 请使用 `React.forwardRef` 包裹起来

+ 53
- 0
src/components/XForm2/index.jsx 파일 보기

@@ -0,0 +1,53 @@
1
+import React from 'react';
2
+import { Form } from 'antd';
3
+import Form2 from './Form';
4
+import Item from './Item';
5
+import { isFunction, isEmpty } from './utils';
6
+
7
+function withForm(option = {}) {
8
+
9
+  const createOption = {
10
+    ...option,
11
+    onValuesChange: (...args) => {
12
+
13
+      if (isFunction(option.onValuesChange)) {
14
+        option.onValuesChange(...args)
15
+      }
16
+      defaultListener(...args)
17
+    }
18
+  }
19
+
20
+  let listeners = []
21
+
22
+  function defaultListener(...args) {
23
+    listeners.map(fn => fn(...args))
24
+  }
25
+
26
+  function handleFormChange (fn) {
27
+    if (isFunction(fn)) {
28
+      listeners.push(fn)
29
+    }
30
+  }
31
+
32
+  class WrapperForm extends React.Component {
33
+    renderChildren() {
34
+      const { fields, form } = this.props;
35
+  
36
+      return (fields || []).filter(x => x).map((props, inx) => {  
37
+        return (<Item key={inx} {...props} form={form} formChange={handleFormChange} />)
38
+      })
39
+    }
40
+
41
+    render() {
42
+      return (
43
+        <Form2 {...this.props}>
44
+          {this.renderChildren()}
45
+        </Form2>
46
+      )
47
+    }
48
+  }
49
+
50
+  return Form.create(createOption)(WrapperForm)
51
+}
52
+
53
+export default withForm;

+ 30
- 0
src/components/XForm2/utils.js 파일 보기

@@ -0,0 +1,30 @@
1
+
2
+export function hasOwnProperty(o, p) {
3
+  return Object.prototype.hasOwnProperty.call(o, p);
4
+}
5
+
6
+export function isFunction(fn) {
7
+  return typeof fn === 'function'
8
+}
9
+
10
+export function isBoolean(b) {
11
+  return typeof b === 'boolean'
12
+}
13
+
14
+export function isEmpty(t) {
15
+  if (t === undefined || t === null) {
16
+    return true
17
+  }
18
+
19
+  if (typeof t === 'object') {
20
+    return !Object.keys(t).length
21
+  }
22
+  
23
+  if (Array.isArray(t)) {
24
+    return t.length
25
+  }
26
+
27
+  return false
28
+}
29
+
30
+export function noop() {}

+ 41
- 28
src/pages/staff/list/editStaff.jsx 파일 보기

@@ -5,7 +5,7 @@ import { FormattedMessage } from 'umi-plugin-react/locale';
5 5
 import BuildSelect from '../../../components/SelectButton/BuildSelect'
6 6
 import router from 'umi/router';
7 7
 import styles from '../../style/GoodsList.less';
8
-import XForm, { FieldTypes } from '../../../components/XForm';
8
+import { FieldTypes, createForm } from '../../../components/XForm';
9 9
 import Wangedit from '../../../components/Wangedit/Wangedit'
10 10
 import channels from './channelList.less';
11 11
 import Tagss from '../components/Tagss.jsx';
@@ -14,7 +14,17 @@ import request from '../../../utils/request'
14 14
 
15 15
 const { TextArea } = Input;
16 16
 const { Option } = Select;
17
+let consultantChecked = false;
17 18
 
19
+const setExtraData = (data) => {
20
+  consultantChecked = data.isConsultant
21
+}
22
+
23
+const handleFormValueChange = (props, changedValues, allValues) => {
24
+    setExtraData(allValues)
25
+}
26
+
27
+const XForm = createForm({ onValuesChange: handleFormValueChange })
18 28
 
19 29
 /**
20 30
  *
@@ -27,6 +37,7 @@ const Edit = (props) => {
27 37
   const [userData, setUserData] = useState({})
28 38
   const [tagData, setTagData] = useState([])
29 39
   const [roleData, setRoleData] = useState([])
40
+  const [buildData, setBuildData] = useState([])
30 41
 
31 42
   const getTagList = () => {
32 43
     request({ ...apis.staff.taTags, params: { pageNum: 1, pageSize: 999 } }).then((data) => {
@@ -41,10 +52,17 @@ const Edit = (props) => {
41 52
     })
42 53
   }
43 54
 
55
+  //获取项目列表
56
+  const getBuildList = e => {
57
+    request({ ...apis.building.buildingSelect, params: { pageNum: 1, pageSize: 999 } }).then(data => {
58
+        setBuildData(data.records)
59
+    })
60
+  }
61
+
44 62
   // 查询列表
45 63
   const getUserData = (userId) => {
46 64
     request({ ...apis.staff.getTaUser, urlData: { id: userId } }).then((data) => {
47
-      console.log(data, "tauser")
65
+      consultantChecked = data.isConsultant
48 66
       setUserData(data)
49 67
     })
50 68
   }
@@ -52,6 +70,7 @@ const Edit = (props) => {
52 70
   useEffect(() => {
53 71
     getTagList();
54 72
     getRoleList();
73
+    getBuildList();
55 74
     if (userId) {
56 75
       getUserData(userId);
57 76
     }
@@ -171,37 +190,31 @@ const Edit = (props) => {
171 190
       </Select>,
172 191
       value: userData.roleIds,
173 192
     },
174
-    // {
175
-    //   label: '标签',
176
-    //   name: 'taTags',
177
-    //   render: <Select
178
-    //     mode="multiple"
179
-    //     style={{ width: '100%' }}
180
-    //     placeholder="请选择标签"
181
-    //     onChange={tagsChange} >
182
-    //     {tagData.map(item => (
183
-    //       <Select.Option key={item.tagId} value={item.tagId}>
184
-    //         {item.tagName}
185
-    //       </Select.Option>
186
-    //     ))}
187
-    //   </Select>,
188
-    //   value: userData.taTags,
189
-    // },
190
-    // {
191
-    //   label: '地址',
192
-    //   name: 'address',
193
-    //   type: FieldTypes.Text,
194
-    //   placeholder: '请输入地址',
195
-    //   value: userData.address,
196
-    //   rules: [
197
-    //     { required: true, message: '请输入地址' },
198
-    //   ]
199
-    // },
200 193
     {
201 194
       label: '授权项目',
202 195
       name: 'buildingId',
203 196
       render: <BuildSelect />,
204 197
       value: userData.buildingId,
198
+      hidden: () => !consultantChecked,
199
+      rules: [
200
+        { required: true, message: '请选择授权项目' },
201
+      ]
202
+    },
203
+    {
204
+      label: '授权项目',
205
+      name: 'buildingIds',
206
+      render: <Select
207
+        mode="multiple"
208
+        style={{ width: '100%' }}
209
+        placeholder="请选择授权项目">
210
+        {buildData.map(item => (
211
+          <Select.Option key={item.buildingId} value={item.buildingId}>
212
+            {item.buildingName}
213
+          </Select.Option>
214
+        ))}
215
+      </Select>,
216
+      value: userData.buildingIds,
217
+      hidden: () => consultantChecked,
205 218
       rules: [
206 219
         { required: true, message: '请选择授权项目' },
207 220
       ]