zlisen 4 anos atrás
pai
commit
ebc1878fda
46 arquivos alterados com 5048 adições e 3 exclusões
  1. 60
    0
      config/routes.js
  2. 115
    0
      src/components/EditIcon/index.jsx
  3. 34
    0
      src/components/Navigate/index.jsx
  4. 11
    0
      src/components/Navigate/style.less
  5. 3
    3
      src/components/TableList/index.jsx
  6. 84
    0
      src/components/XForm/FileUpload.jsx
  7. 98
    0
      src/components/XForm/ImageListUpload.jsx
  8. 77
    0
      src/components/XForm/ImageUpload.jsx
  9. 31
    0
      src/components/XForm/README.md
  10. 81
    0
      src/components/XForm/WrapperForm.jsx
  11. 151
    0
      src/components/XForm/WrapperItem.jsx
  12. 9
    0
      src/components/XForm/index.jsx
  13. 13
    0
      src/components/XForm/style.css
  14. 20
    0
      src/components/XForm/style.less
  15. 13
    0
      src/components/XForm/style.wxss
  16. 148
    0
      src/pages/building/Developers/Edit.jsx
  17. 56
    0
      src/pages/building/Developers/index.jsx
  18. 69
    0
      src/pages/building/Developers/tableColumns.js
  19. 112
    0
      src/pages/building/type/edi.jsx
  20. 152
    0
      src/pages/building/type/index.jsx
  21. 48
    0
      src/pages/building/type/style.css
  22. 49
    0
      src/pages/building/type/style.less
  23. 47
    0
      src/pages/building/type/style.wxss
  24. 165
    0
      src/pages/customer/Customer/PrivateCustomer/index.jsx
  25. 136
    0
      src/pages/customer/Customer/PrivateCustomer/tableColumns.js
  26. 29
    0
      src/pages/customer/Customer/index.jsx
  27. 170
    0
      src/pages/customer/customerlist/components/BatchAssistConsultant.jsx
  28. 160
    0
      src/pages/customer/customerlist/components/assistConsultant.jsx
  29. 146
    0
      src/pages/customer/customerlist/components/attribution.jsx
  30. 81
    0
      src/pages/customer/customerlist/components/changeStatus.jsx
  31. 160
    0
      src/pages/customer/customerlist/components/integralRecord.jsx
  32. 146
    0
      src/pages/customer/customerlist/components/recommend.jsx
  33. 217
    0
      src/pages/customer/customerlist/customerDetail.jsx
  34. 786
    0
      src/pages/customer/customerlist/index copy.jsx
  35. 35
    0
      src/pages/customer/customerlist/index.jsx
  36. 183
    0
      src/pages/customer/customerlist/publicCustomerDetail.jsx
  37. 209
    0
      src/pages/customer/customerlist/publicStyle.less
  38. 69
    0
      src/pages/customer/customerlist/style.css
  39. 239
    0
      src/pages/customer/customerlist/style.less
  40. 69
    0
      src/pages/customer/customerlist/style.wxss
  41. 115
    0
      src/pages/customer/drift/index.jsx
  42. 58
    0
      src/pages/customer/drift/style.less
  43. 173
    0
      src/pages/customer/visiting/index.jsx
  44. 151
    0
      src/pages/customer/visiting/style.less
  45. 31
    0
      src/utils/mixStr copy.js
  46. 39
    0
      src/utils/upload.js

+ 60
- 0
config/routes.js Ver arquivo

@@ -56,6 +56,66 @@ export default [
56 56
                 name: '项目列表',
57 57
                 component: './building/List',
58 58
               },
59
+              {
60
+                path: '/building/type',
61
+                name: '项目类型',
62
+                component: './building/type/index',
63
+              },
64
+              {
65
+                path: '/building/type/edi',
66
+                name: '项目类型编辑',
67
+                hideInMenu: true,
68
+                component: './building/type/edi',
69
+              },
70
+              {
71
+                path: '/building/Developers',
72
+                name: '品牌开发商',
73
+                component: './building/Developers',
74
+              },
75
+              {
76
+                path: '/building/Developers/Edit',
77
+                name: '品牌开发商编辑',
78
+                hideInMenu: true,
79
+                component: './building/Developers/Edit',
80
+              },
81
+            ],
82
+          },
83
+          {
84
+            path: '/customer',
85
+            name: '客户管理',
86
+            component: '../layouts/BlankLayout',
87
+            routes: [
88
+              {
89
+                path: '/customer/customer/list',
90
+                name: '客户列表',
91
+                component: './customer/customer/index',
92
+              },
93
+
94
+
95
+              {
96
+                path: '/customer/customerlist/list',
97
+                name: '客户列表old',
98
+                component: './customer/customerlist/index copy.jsx',
99
+              },
100
+              {
101
+                path: '/customer/customerlist/customerDetail',
102
+                name: '私客详情',
103
+                hideInMenu: true,
104
+                component: './customer/customerlist/customerDetail',
105
+              },
106
+              {
107
+                path: '/customer/drift/list',
108
+                name: '游客列表',
109
+                component: './customer/drift/index',
110
+              },
111
+              {
112
+                path: '/customer/customerlist/publicCustomerDetail',
113
+                name: '公客详情',
114
+                hideInMenu: true,
115
+                component: './customer/customerlist/publicCustomerDetail',
116
+              },
117
+
118
+              
59 119
             ],
60 120
           },
61 121
           {

+ 115
- 0
src/components/EditIcon/index.jsx Ver arquivo

@@ -0,0 +1,115 @@
1
+import React, { useMemo } from 'react';
2
+import spriteImg from '../../assets/sprite.png';
3
+
4
+const spriteInfo = [
5
+    {
6
+        name: '启用',
7
+        type: 'start',
8
+        pos: 0,
9
+        color: '#ff925c',
10
+    },
11
+    {
12
+        name: '发布',
13
+        type: 'publish',
14
+        pos: -126,
15
+        color: '#ff925c',
16
+    },
17
+    {
18
+        name: '上架',
19
+        type: 'up',
20
+        pos: -18,
21
+        color: '#ff925c',
22
+    },
23
+    {
24
+        name: '编辑',
25
+        type: 'edit',
26
+        pos: -144,
27
+        color: '#ff925c',
28
+    },
29
+    {
30
+        name: '取消',
31
+        type: 'cancel',
32
+        pos: -36,
33
+        color: '#FF4A4A',
34
+    },
35
+    {
36
+        name: '停用',
37
+        type: 'stop',
38
+        pos: -162,
39
+        color: '#FF4A4A',
40
+    },
41
+    {
42
+        name: '结束',
43
+        type: 'end',
44
+        pos: -54,
45
+        color: '#FF4A4A',
46
+    },
47
+    {
48
+        name: '删除',
49
+        type: 'delete',
50
+        pos: -180,
51
+        color: '#FF4A4A',
52
+    },
53
+    {
54
+        name: '查看',
55
+        type: 'look',
56
+        pos: -72,
57
+        color: '#FF4A4A',
58
+    },
59
+    {
60
+        name: '添加',
61
+        type: 'add',
62
+        pos: -198,
63
+        color: '#FF4A4A',
64
+    },
65
+    {
66
+        name: '前置',
67
+        type: 'top',
68
+        pos: -90,
69
+        color: '#FF4A4A',
70
+    },
71
+    {
72
+        name: '下架',
73
+        type: 'down',
74
+        pos: -216,
75
+        color: '#FF4A4A',
76
+    },
77
+    {
78
+        name: '记录',
79
+        type: 'record',
80
+        pos: -108,
81
+        color: '#FF4A4A',
82
+    },
83
+    {
84
+        name: '数据',
85
+        type: 'data',
86
+        pos: -234,
87
+        color: '#FF4A4A',
88
+    },
89
+    {
90
+        name: '下载二维码',
91
+        type: 'download',
92
+        pos: -252,
93
+        color: '#ff925c',
94
+    },
95
+]
96
+
97
+function noop() { }
98
+
99
+const EditIcon = ({ text, type, color, position, onClick }) => {
100
+    const icon = spriteInfo.filter(x => x.type === type)[0] || {};
101
+    const color2 = color || icon.color || '#ff925c';
102
+    const position2 = position || icon.pos;
103
+
104
+    const wrappedStyle = useMemo(() => ({ color: `${color2}`, display: 'flex', alignItems: 'center', cursor: 'pointer' }), [type, color]);
105
+    const iconStyle = useMemo(() => ({ display: 'inline-block', marginLeft: '6px', background: `url(${spriteImg}) 0 ${position2}px / 100% 1500% no-repeat`, width: '18px', height: '18px' }), [type, position])
106
+
107
+    return (
108
+        <span style={wrappedStyle} onClick={onClick || noop} >
109
+            {text}
110
+            {type && <span style={iconStyle}></span>}
111
+        </span>
112
+    )
113
+};
114
+
115
+export default EditIcon;

+ 34
- 0
src/components/Navigate/index.jsx Ver arquivo

@@ -0,0 +1,34 @@
1
+import React from 'react';
2
+import Link from 'umi/link';
3
+
4
+import styles from './style.less';
5
+
6
+const Navigate = ({ children, to, onClick }) => {
7
+    // to 包含协议 用 a
8
+    const useA = (to || '').indexOf('://') > -1 ? 'a' : '';
9
+    // 否则用 Link
10
+    const useLink = to && to.indexOf('://') === -1 ? Link : '';
11
+    // 没有 to 则使用 span
12
+    const element = useA || useLink || 'span';
13
+
14
+    const events = onClick ? { onClick } : {};
15
+    const style = { color: '#1D74D9', cursor: 'pointer' };
16
+
17
+    let redirect = {};
18
+    if (useA) {
19
+        redirect = { href: to }
20
+    } else if (useLink) {
21
+        redirect = { to }
22
+    }
23
+
24
+    const props = {
25
+        className: 'cust-navi',
26
+        style,
27
+        ...redirect,
28
+        ...events,
29
+    }
30
+
31
+    return React.createElement(element, props, children);
32
+};
33
+
34
+export default Navigate;

+ 11
- 0
src/components/Navigate/style.less Ver arquivo

@@ -0,0 +1,11 @@
1
+
2
+:global {
3
+  .cust-navi {
4
+    color: #1D74D9;
5
+    cursor: pointer;
6
+  
7
+    * {
8
+      color: #1D74D9 !important;
9
+    }
10
+  }
11
+}

+ 3
- 3
src/components/TableList/index.jsx Ver arquivo

@@ -12,12 +12,12 @@ export default React.forwardRef((props, ref) => {
12 12
   const [pageTotal, setPageTotal] = useState(0)
13 13
   const [pageConfig, setPageConfig] = useState({ current: 1, pageSize: 10 })
14 14
 
15
-  const { api, params = {}, onPageChange, ...tableProps } = props
15
+  const { api, params, onPageChange, ...tableProps } = props
16 16
 
17 17
   // 更新分页
18 18
   const updatePage = useCallback(
19 19
     (current, pageSize) => {
20
-      const config = pageSize ? { current, pageSize } : { current }
20
+      const config = pageSize ? { current, pageSize } : { current, pageSize:pageConfig.pageSize }
21 21
       const newPage = {
22 22
         ...pageConfig,
23 23
         ...config,
@@ -36,7 +36,7 @@ export default React.forwardRef((props, ref) => {
36 36
     () => {
37 37
       setLoading(true)
38 38
       const queryParams = {
39
-        ...params,
39
+        ...params || {},
40 40
         pageNum: pageConfig.current,
41 41
         pageSize: pageConfig.pageSize,
42 42
       }

+ 84
- 0
src/components/XForm/FileUpload.jsx Ver arquivo

@@ -0,0 +1,84 @@
1
+import React, { useState, useEffect } from 'react'
2
+import { Upload, Button, Icon } from 'antd';
3
+import { uploaderProps } from '../../utils/upload';
4
+
5
+/**
6
+ * value 数据的接收格式 [{ url: xxxxx.mp4 }]
7
+ * size  参数限制可以上传多少个文件
8
+ * @param { value, size } props
9
+ */
10
+function fileUpload(props) {
11
+
12
+  const { value } = props
13
+  // console.log('fileUploadProps: ', props)
14
+  // eslint-disable-next-line react-hooks/rules-of-hooks
15
+  const [defaultFileList, setDefaultFileList] = useState([])
16
+
17
+  // eslint-disable-next-line react-hooks/rules-of-hooks
18
+  useEffect(() => {
19
+    setDefaultValue()
20
+  }, [props.value]);
21
+
22
+
23
+  function getFileList() {
24
+    console.log('fileUpload: ', value)
25
+    // value 数据的接收格式 [{ url: xxxxx.mp4 }, { url: xxxxx.jpg }]
26
+    return (value || []).filter(f => f !== undefined).map((img, inx) => ({ uid: inx, url: img, name: img.substring(img.lastIndexOf('/') + 1, img.length), status: 'done' }))
27
+  }
28
+
29
+  function setDefaultValue() {
30
+    if (!value) {
31
+      return;
32
+    }
33
+
34
+    setDefaultFileList(getFileList())
35
+  }
36
+
37
+  function onFileChange({ file, fileList }) {
38
+    console.log(file, fileList)
39
+    setDefaultFileList(fileList)
40
+    if (file.status === 'uploading') {
41
+      return
42
+    }
43
+
44
+    if (file.status === 'done' || file.status === 'removed') {
45
+
46
+      // 因为这个控件本身返回的数据格式 [{ ..., response: '服务器返回的数据' }]
47
+      // 但是 我这里自己用的时候 传入的数据是 [ 'xxxx.mp4', 'xxxx.jpg' ], 然后通过 getFileList() 转换成了 [{ url: 'xxx.mp4' }] 这样的格式
48
+
49
+      // 原因是因为 控件返回的数据 和 fileList 展示已经上传的数据 的格式字段不一样
50
+
51
+      const resFileList = fileList.filter(f => f.response !== undefined).map(i => i.response)
52
+      const tempFileList = fileList.filter(f => f.url !== undefined).map(i => i.url)
53
+      const resultList = tempFileList.concat(resFileList || [])
54
+      props.onChange(resultList)
55
+    }
56
+  }
57
+
58
+  return (
59
+    <>
60
+      <Upload
61
+        { ...uploaderProps }
62
+        {...props}
63
+        onChange={onFileChange}
64
+        fileList={defaultFileList}
65
+      >
66
+        {
67
+          props.size ?
68
+          (props.size > defaultFileList.length
69
+          && (
70
+            <Button>
71
+                <Icon type="upload" /> {props.label}
72
+              </Button>
73
+          )) : (
74
+            <Button>
75
+              <Icon type="upload" /> {props.label}
76
+            </Button>
77
+          )
78
+        }
79
+      </Upload>
80
+    </>
81
+  )
82
+}
83
+
84
+export default fileUpload

+ 98
- 0
src/components/XForm/ImageListUpload.jsx Ver arquivo

@@ -0,0 +1,98 @@
1
+import React from 'react';
2
+import { Upload, Icon, Modal } from 'antd';
3
+import './style.less';
4
+import { uploaderProps } from '../../utils/upload';
5
+
6
+/**
7
+ * unlimited  属性表示上传图片无限制
8
+ * 例子: <ImageListUpload unlimited/>
9
+ */
10
+class ImageListUpload extends React.Component {
11
+  state = {
12
+    previewVisible: false,
13
+    previewImage: '',
14
+    loadding: false,
15
+  };
16
+
17
+  getFileList = () => {
18
+    return (this.props.value || []).map((img, inx) => ({ uid: inx, url: img, status: 'done' }))
19
+  }
20
+
21
+  handleCancel = () => this.setState({ previewVisible: false });
22
+
23
+  handlePreview = async file => {
24
+    this.setState({
25
+      previewImage: file.url ,
26
+      previewVisible: true,
27
+    });
28
+  };
29
+
30
+  handleChange = (e) => {
31
+    if (e.file.status === "uploading") {
32
+      this.setState({ loading: true });
33
+      return;
34
+    }
35
+
36
+    const fileList = (this.props.value || []).filter(x => x != e.file.url);
37
+    this.props.onChange(fileList);
38
+
39
+    // console.log('删除图片触发了', e.file)
40
+    // if (e.file.state === 'removed') {
41
+    //   const fileList = (this.props.value || []).filter(x => x != e.file.url);
42
+    //   this.props.onChange(fileList);
43
+    // }
44
+
45
+    // if (e.file.status === "done") {
46
+    //   this.setState({
47
+    //     loading: false,
48
+    //   })
49
+
50
+    //   if (e.file.response && e.file.response.url) {
51
+    //     if (typeof this.props.onChange === 'function') {
52
+    //       const fileList = this.getFileList()
53
+    //       this.props.onChange([...fileList || [], e.file.response.url]);
54
+    //     }
55
+    //   }
56
+    // }
57
+  };
58
+
59
+  handleUploadSucess = (url) => {
60
+    this.setState({ loading: false });
61
+    if (typeof this.props.onChange === 'function') {
62
+      const fileList = this.props.value || [];
63
+      this.props.onChange([...fileList, url]);
64
+    }
65
+  }
66
+
67
+  render() {
68
+    const { previewVisible, previewImage } = this.state;
69
+    const fileList = this.getFileList();
70
+
71
+    const uploadButton = (
72
+      <div>
73
+        <Icon style={{ fontSize: '2em', color: '#aaa' }} type={this.state.loading ? "loading" : "plus"}  />
74
+      </div>
75
+    );
76
+    return (
77
+      <div className="clearfix">
78
+        <Upload
79
+          listType="picture-card"
80
+          multiple={true}
81
+          fileList={fileList}
82
+          onPreview={this.handlePreview}
83
+          onChange={this.handleChange}
84
+          { ...uploaderProps }
85
+          onSuccess={this.handleUploadSucess}
86
+        >
87
+          {/* unlimited 表示上传无限制数量 */}
88
+          {(this.props.unlimited && uploadButton) || ((fileList || images).length >= 8 ? null : uploadButton)}
89
+        </Upload>
90
+        <Modal visible={previewVisible} footer={null} onCancel={this.handleCancel}>
91
+          <img alt="example" style={{ width: '100%' }} src={previewImage} />
92
+        </Modal>
93
+      </div>
94
+    );
95
+  }
96
+}
97
+
98
+export default ImageListUpload;

+ 77
- 0
src/components/XForm/ImageUpload.jsx Ver arquivo

@@ -0,0 +1,77 @@
1
+import React from 'react';
2
+import { Upload, Icon, message } from 'antd';
3
+import './style.less';
4
+import { uploaderProps } from '../../utils/upload';
5
+
6
+
7
+
8
+class ImageUpload extends React.Component {
9
+  state = {
10
+    loading: false,
11
+    imageUrl: undefined,
12
+  };
13
+
14
+  handleChange = info => {
15
+    if (info.file.status === "uploading") {
16
+      this.setState({ loading: true });
17
+      return;
18
+    }
19
+    
20
+    if (info.file.status === 'removed') {
21
+      this.props.onChange();
22
+    }
23
+
24
+    // if (info.file.status === "done") {
25
+    //   this.setState({
26
+    //     loading: false,
27
+    //   })
28
+
29
+    //   if (info.file.response && info.file.response.url) {
30
+    //     this.setState({
31
+    //       imageUrl: info.file.response.thumbUrl,
32
+    //     });
33
+
34
+    //     if (typeof this.props.onChange === 'function') {
35
+    //       this.props.onChange(info.file.response.url);
36
+    //     }
37
+    //   }
38
+    // }
39
+  };
40
+
41
+  handleUploadSucess = (url) => {
42
+    this.setState({ loading: false });
43
+    if (typeof this.props.onChange === 'function') {
44
+      this.props.onChange(url);
45
+    }
46
+  }
47
+
48
+  render() {
49
+    const uploadButton = (
50
+      <div>
51
+        <Icon style={{ fontSize: '2em', color: '#aaa' }} type={this.state.loading ? "loading" : "plus"} />
52
+      </div>
53
+    );
54
+
55
+    const value = this.props.value;
56
+    return (
57
+      <Upload
58
+        listType="picture-card"
59
+        className="avatar-uploader"
60
+        showUploadList={false}
61
+        beforeUpload={this.props.beforeUpload}
62
+        onChange={this.handleChange}
63
+        {...uploaderProps}
64
+        disabled={this.props.disabled}
65
+        onSuccess={this.handleUploadSucess}
66
+      >
67
+        {(this.state.imageUrl || value) ? (
68
+          <img src={this.state.imageUrl || value} alt="avatar" style={{ width: "100%" }} />
69
+        ) : (
70
+            uploadButton
71
+          )}
72
+      </Upload>
73
+    );
74
+  }
75
+}
76
+
77
+export default ImageUpload;

+ 31
- 0
src/components/XForm/README.md Ver arquivo

@@ -0,0 +1,31 @@
1
+# XForm
2
+Antd Form 扩展
3
+
4
+## 使用说明
5
+
6
+```jsx
7
+import XForm, { FieldTypes } from '/path/to/XForm';
8
+
9
+const tplJSON = [
10
+  {},
11
+  {}
12
+]
13
+
14
+```
15
+
16
+通过 `json` 对象来实现自动构造 form 表单
17
+
18
+计划支持的属性
19
+
20
+[] label      显示的文字
21
+
22
+[] name       form name, 用于表单提交
23
+
24
+[] type       组件类型, 参见 FieldTypes
25
+
26
+[] rules      校验规则
27
+
28
+[] element    函数, 用于生成组件. (props) => <></> . 其中 props 包含 ...
29
+
30
+[] value      绑定的数据
31
+

+ 81
- 0
src/components/XForm/WrapperForm.jsx Ver arquivo

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

+ 151
- 0
src/components/XForm/WrapperItem.jsx Ver arquivo

@@ -0,0 +1,151 @@
1
+import React from 'react';
2
+import {
3
+  Form,
4
+  Input,
5
+  InputNumber,
6
+  Checkbox,
7
+  Radio,
8
+  DatePicker,
9
+  TimePicker,
10
+  Select,
11
+  Switch,
12
+} from 'antd';
13
+import ImageUploader from './ImageUpload';
14
+
15
+const {Item} = Form
16
+const { Option } = Select;
17
+const { MonthPicker, RangePicker } = DatePicker
18
+
19
+const FieldTypes = {
20
+  Text: 'Text',
21
+  Password: 'Password',
22
+  Number: 'Number',
23
+  Switch: 'Switch',
24
+  TimePicker: 'TimePicker',
25
+  DatePicker: 'DatePicker',
26
+  RangePicker: 'RangePicker',
27
+  MonthPicker: 'MonthPicker',
28
+  Select: 'Select',
29
+  ImageUploader: 'ImageUploader',
30
+}
31
+
32
+const tailFormItemLayout = {
33
+  wrapperCol: {
34
+    xs: {
35
+      span: 24,
36
+      offset: 0,
37
+    },
38
+    sm: {
39
+      span: 16,
40
+      offset: 8,
41
+    },
42
+  },
43
+};
44
+
45
+const WrapperItem = props => {
46
+  const {
47
+    form,
48
+    label,
49
+    name,
50
+    type,
51
+    placeholder,
52
+    render,
53
+    rules,
54
+    value,
55
+    dict,
56
+    action,
57
+    props: fieldProps,
58
+    hidden = false,
59
+    help,
60
+    ...restProps
61
+  } = props;
62
+
63
+  const { getFieldDecorator } = form || {};
64
+
65
+  let config = {
66
+    rules,
67
+    initialValue: value,
68
+  }
69
+
70
+  if (type === FieldTypes.Switch) {
71
+    config = {
72
+      ...config,
73
+      valuePropName: 'checked',
74
+    };
75
+  }
76
+
77
+  if (type === FieldTypes.ImageUploader) {
78
+    config = {
79
+      ...config,
80
+      getValueFromEvent: x => x,
81
+    };
82
+  }
83
+
84
+  // 没有类型与组件, 生成隐藏字段
85
+  if (!type && !render) {
86
+    getFieldDecorator(name, config);
87
+    return null;
88
+  }
89
+
90
+  const SelectOpts = (dict || []).map((item, index) => (<Option value={item.value}>{item.label}</Option>))
91
+
92
+  let Field = <></>;
93
+  if (render) {
94
+    Field = typeof render === 'function' ? render(props) : render
95
+  } else {
96
+    switch (type) {
97
+      case FieldTypes.Text:
98
+        Field = <Input placeholder={placeholder} style={{ width: '100%' }} {...fieldProps}/>;
99
+        break;
100
+      case FieldTypes.Password:
101
+        Field = <Input.Password placeholder={placeholder} style={{ width: '100%' }} {...fieldProps}/>;
102
+        break;
103
+      case FieldTypes.Number:
104
+        Field = <InputNumber placeholder={placeholder} style={{ width: '100%' }} {...fieldProps}/>;
105
+        break;
106
+      case FieldTypes.Switch:
107
+        Field = <Switch {...fieldProps} />;
108
+        break;
109
+      case FieldTypes.TimePicker:
110
+        Field = <TimePicker {...fieldProps} style={{ width: '100%' }}/>;
111
+        break;
112
+      case FieldTypes.DatePicker:
113
+        Field = <DatePicker {...fieldProps} style={{ width: '100%' }}/>;
114
+        break;
115
+      case FieldTypes.RangePicker:
116
+        Field = <RangePicker {...fieldProps} style={{ width: '100%' }}/>;
117
+        break;
118
+      case FieldTypes.MonthPicker:
119
+        Field = <MonthPicker {...fieldProps} style={{ width: '100%' }}/>;
120
+        break;
121
+      case FieldTypes.Select:
122
+        Field = <Select placeholder={placeholder} style={{ width: '100%' }} {...fieldProps}>{SelectOpts}</Select>
123
+        break;
124
+      case FieldTypes.ImageUploader:
125
+        const beforeUpload = { beforeUpload: props.beforeUpload }
126
+        Field = <ImageUploader {...fieldProps} { ...beforeUpload}/>
127
+        break;
128
+      default:
129
+        throw new Error(`暂时不支持的组件类型: ${type}`)
130
+    }
131
+  }
132
+
133
+  const visible = typeof hidden === 'function' ? !hidden() : !hidden;
134
+
135
+  if (!label && !name && !action && !help) return visible ? Field : null;
136
+
137
+  const itemProps = action ? { ...restProps, ...tailFormItemLayout } : restProps
138
+  const labelNode = typeof label === 'function' ? label() : label;
139
+  const helpNode = typeof help === 'function' ? help() : help;
140
+
141
+  const newItemProps = { ...itemProps, help: helpNode }
142
+
143
+  return visible && (
144
+    <Item label={labelNode} { ...newItemProps }>
145
+      {action ? Field : getFieldDecorator(name, config)(Field)}
146
+    </Item>
147
+  )
148
+};
149
+
150
+export default WrapperItem;
151
+export { FieldTypes };

+ 9
- 0
src/components/XForm/index.jsx Ver arquivo

@@ -0,0 +1,9 @@
1
+import React from 'react';
2
+import { Form } from 'antd';
3
+import WrapperForm from './WrapperForm';
4
+import { FieldTypes } from './WrapperItem';
5
+
6
+const createForm = option => Form.create(option)(WrapperForm);
7
+
8
+export default Form.create()(WrapperForm);
9
+export { FieldTypes, createForm };

+ 13
- 0
src/components/XForm/style.css Ver arquivo

@@ -0,0 +1,13 @@
1
+:global .avatar-uploader > .ant-upload {
2
+  width: 128px;
3
+  height: 128px;
4
+}
5
+/* you can make up upload button and sample style by using stylesheets */
6
+.ant-upload-select-picture-card i {
7
+  font-size: 32px;
8
+  color: #999;
9
+}
10
+.ant-upload-select-picture-card .ant-upload-text {
11
+  margin-top: 8px;
12
+  color: #666;
13
+}

+ 20
- 0
src/components/XForm/style.less Ver arquivo

@@ -0,0 +1,20 @@
1
+:global {
2
+  .avatar-uploader {
3
+    & > .ant-upload {
4
+      width: 128px;
5
+      height: 128px;
6
+    }
7
+  }
8
+}
9
+
10
+
11
+/* you can make up upload button and sample style by using stylesheets */
12
+.ant-upload-select-picture-card i {
13
+  font-size: 32px;
14
+  color: #999;
15
+}
16
+
17
+.ant-upload-select-picture-card .ant-upload-text {
18
+  margin-top: 8px;
19
+  color: #666;
20
+}

+ 13
- 0
src/components/XForm/style.wxss Ver arquivo

@@ -0,0 +1,13 @@
1
+:global .avatar-uploader > .ant-upload {
2
+  width: 128px;
3
+  height: 128px;
4
+}
5
+/* you can make up upload button and sample style by using stylesheets */
6
+.ant-upload-select-picture-card i {
7
+  font-size: 32px;
8
+  color: #999;
9
+}
10
+.ant-upload-select-picture-card .ant-upload-text {
11
+  margin-top: 8px;
12
+  color: #666;
13
+}

+ 148
- 0
src/pages/building/Developers/Edit.jsx Ver arquivo

@@ -0,0 +1,148 @@
1
+import React, { useState, useEffect } from 'react';
2
+import { Input, Button, Card, Form,  notification } from 'antd';
3
+import router from 'umi/router';
4
+// import Styles from './style.less';
5
+import ImageUploader from '../../../components/XForm/ImageUpload';
6
+import request from '../../../utils/request';
7
+import apis from '../../../services/apis';
8
+
9
+const formItemLayout = {
10
+    labelCol: {
11
+      xs: { span: 24 },
12
+      sm: { span: 8 },
13
+    },
14
+    wrapperCol: {
15
+      xs: { span: 24 },
16
+      sm: { span: 16 },
17
+    },
18
+  };
19
+  const tailFormItemLayout = {
20
+    wrapperCol: {
21
+      xs: {
22
+        span: 24,
23
+        offset: 0,
24
+      },
25
+      sm: {
26
+        span: 16,
27
+        offset: 8,
28
+      },
29
+    },
30
+  };
31
+
32
+function Edit(props) {
33
+  const { getFieldDecorator } = props.form;
34
+
35
+  // eslint-disable-next-line react-hooks/rules-of-hooks
36
+  let data = {};
37
+  const { id } = props.location.query;
38
+
39
+  useEffect(() => {
40
+    if (id) {
41
+      getById(id);
42
+    }
43
+  }, [id]);
44
+
45
+  function handleSubmit(e) {
46
+    e.preventDefault();
47
+    props.form.validateFields((err, values) => {
48
+      if (!err) {
49
+        // 提交数据
50
+        submitData(values);
51
+      }
52
+    });
53
+  }
54
+
55
+  // 获取详情信息
56
+  function getById(currentId) {
57
+    request({ ...apis.buildingType.getById, urlData: { id: currentId } }).then(res => {
58
+      data = res;
59
+      props.form.setFieldsValue(res);
60
+    });
61
+  }
62
+
63
+  const openNotificationWithIcon = (type, message) => {
64
+    notification[type]({
65
+      message,
66
+      description: '',
67
+    });
68
+  };
69
+
70
+  function submitData(dataSources) {
71
+    if (id !== '') {
72
+      // 修改
73
+      updateType(dataSources);
74
+      return;
75
+    }
76
+
77
+    dataSources.status = 1;
78
+    dataSources.createDate = new Date();
79
+    request({ ...apis.buildingType.add, data: { ...dataSources } })
80
+      .then(() => {
81
+        // eslint-disable-next-line no-unused-expressions
82
+        openNotificationWithIcon('success', '操作成功');
83
+        router.go(-1);
84
+      })
85
+      .catch(err => {});
86
+  }
87
+
88
+  // 修改
89
+  function updateType(row) {
90
+    // const { url, method } = apis.buildingType.update
91
+    // const tempUrl = url.substring(0, url.lastIndexOf('id')).concat(row.buildingTypeId)
92
+
93
+    // request({ url: tempUrl, method, data: { ...row } }).then(() => {
94
+    //   router.go(-1)
95
+    // })
96
+    request({ ...apis.buildingType.update, urlData: { id: row.buildingTypeId }, data: { ...row } })
97
+      .then(() => {
98
+        // eslint-disable-next-line no-unused-expressions
99
+        openNotificationWithIcon('success', '操作成功');
100
+        router.go(-1);
101
+      })
102
+      .catch(err => {});
103
+  }
104
+
105
+  return (
106
+    <Card>
107
+      <Form {...formItemLayout} onSubmit={handleSubmit}>
108
+      <Form.Item label="开发商名称">
109
+          {getFieldDecorator('name', {
110
+            rules: [
111
+              {
112
+                required: true,
113
+                message: '请输入开发商名称!',
114
+              },
115
+            ],
116
+          })(<Input style={{width:200}}/>)}
117
+        </Form.Item>
118
+        <Form.Item label="开发商索引">
119
+          {getFieldDecorator('ind', {
120
+            rules: [{ required: true, message: '请输入开发商索引' }],
121
+          })(<Input style={{width:200}}/>)}
122
+        </Form.Item>
123
+        <Form.Item label="品牌宣传图">
124
+          {getFieldDecorator('img', {
125
+            rules: [{ required: true, message: '请上传品牌宣传图' }],
126
+        })(<ImageUploader />)}
127
+        </Form.Item>
128
+        <Form.Item label="开发商简介">
129
+          {getFieldDecorator('buildingTypeName', {
130
+            rules: [{ required: true, message: '请输入开发商简介' }],
131
+          })(<Input.TextArea style={{width:400}} placeholder="120字内的简介"/>)}
132
+        </Form.Item>
133
+       
134
+        <Form.Item  {...tailFormItemLayout} style={{ display: 'flex', justifyContent: 'space-between' }}>
135
+          <Button type="primary" htmlType="submit" style={{ margin: '0' }}>
136
+            确定
137
+          </Button>
138
+          &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
139
+          <Button onClick={() => router.go(-1)}>取消</Button>
140
+        </Form.Item>
141
+      </Form>
142
+    </Card>
143
+  );
144
+}
145
+
146
+const WrappedBuidingTypeForm = Form.create({ name: 'Edit' })(Edit);
147
+
148
+export default WrappedBuidingTypeForm;

+ 56
- 0
src/pages/building/Developers/index.jsx Ver arquivo

@@ -0,0 +1,56 @@
1
+import React, { useMemo, useRef, useCallback, useState } from 'react'
2
+import {Button} from 'antd'
3
+import apis from '@/services/apis'
4
+import AuthButton from '@/components/AuthButton';
5
+// import QueryTable from '@/components/QueryTable'
6
+import TableList from '@/components/TableList'
7
+import getTableColumns from './tableColumns'
8
+import { router } from 'umi';
9
+
10
+export default (props) => {
11
+  const ref = useRef()
12
+  const [page, setPage] = useState({current: 1, pageSize: 10})
13
+  const toEdit = useCallback((row) => {
14
+    //
15
+    router.push({
16
+      pathname: '/building/Developers/Edit',
17
+      query: {
18
+        id: row?.id || undefined,
19
+      },
20
+    });
21
+  }, [])
22
+
23
+  const onDelete = useCallback((row) => {
24
+
25
+  }, [])
26
+
27
+
28
+  const tableColumns = useMemo(() => {
29
+    return getTableColumns({
30
+      page,
31
+      onEdit:(e)=>toEdit(e),
32
+      onDelete,
33
+    })
34
+  }, [page])
35
+
36
+  const actionRender = () => {
37
+    return (
38
+      <AuthButton name="admin.tdBuildingType.add" noRight={null}>
39
+        <Button type="primary" icon="plus" onClick={() => toEdit()} >
40
+          新增
41
+        </Button>
42
+      </AuthButton>
43
+    );
44
+  };
45
+
46
+  return (
47
+    <>
48
+      <TableList
49
+        rowKey="buildingId"
50
+        api={apis.building.getList}
51
+        columns={tableColumns}
52
+        actionRender={actionRender}
53
+      />
54
+    </>
55
+  )
56
+}

+ 69
- 0
src/pages/building/Developers/tableColumns.js Ver arquivo

@@ -0,0 +1,69 @@
1
+import { router } from 'umi'
2
+import { Button, Badge } from 'antd'
3
+import moment from 'moment'
4
+import OperButton from '@/components/OperButton'
5
+import AuthButton from '@/components/AuthButton'
6
+import withActions from '@/components/ActionList'
7
+
8
+export default ({page, onEdit, onDelete}) => [
9
+  {
10
+    title: '开发商索引字母',
11
+    key: '#',
12
+    align: 'center',
13
+    render: (t, r, index) => (page.current - 1) * page.pageSize + index + 1,
14
+  },
15
+  {
16
+    title: '开发商名称',
17
+    dataIndex: 'name',
18
+    key: 'name',
19
+    align: 'center',
20
+  },
21
+  {
22
+    title: '品牌宣传图',
23
+    dataIndex: 'image',
24
+    key: 'image',
25
+    align: 'center',
26
+    render: (_, row) => {
27
+      if (row.image) {
28
+        return <img src={row.image} width={128} height={72} style={{borderRadius: '4px'}} alt="" />
29
+      }
30
+      return null
31
+    }
32
+  },
33
+  {
34
+    title: '简介',
35
+    dataIndex: 'buildingName',
36
+    key: 'buildingName',
37
+    align: 'center',
38
+    // render: (t, row) => (
39
+    //   <AuthButton name="admin.building.update" noRight={t}>
40
+    //     <Button
41
+    //       type="link"
42
+    //       onClick={() => router.push({
43
+    //         pathname: '/building/list/add',
44
+    //         query: {
45
+    //           id: row.buildingId,
46
+    //         },
47
+    //       })}
48
+    //     >{t}</Button>
49
+    //   </AuthButton>
50
+    // )
51
+  },
52
+  {
53
+    title: '操作',
54
+    key: 'options',
55
+    align: 'center',
56
+    render: withActions((_, row) => [
57
+      <AuthButton name="admin.building.public" noRight={null}>
58
+        <OperButton onClick={() => onEdit(row)}>编辑</OperButton>
59
+      </AuthButton>,
60
+      <AuthButton name="admin.building.delete" noRight={null}>
61
+        <OperButton.Confirm
62
+          title="确认删除?"
63
+          content="删除之后不可恢复"
64
+          onClick={() => onDelete(row)}
65
+        >删除</OperButton.Confirm>
66
+      </AuthButton>
67
+    ], {noMargin: true})
68
+  },
69
+]

+ 112
- 0
src/pages/building/type/edi.jsx Ver arquivo

@@ -0,0 +1,112 @@
1
+import React, {  useEffect } from 'react';
2
+import { Input, Button, Form, Card, notification } from 'antd';
3
+import router from 'umi/router';
4
+import Styles from './style.less';
5
+import request from '../../../utils/request';
6
+import apis from '../../../services/apis';
7
+
8
+function body(props) {
9
+  const { getFieldDecorator } = props.form;
10
+
11
+  // eslint-disable-next-line react-hooks/rules-of-hooks
12
+  let data = {};
13
+  const { id } = props.location.query;
14
+
15
+  if (id !== '') {
16
+    // eslint-disable-next-line react-hooks/rules-of-hooks
17
+    useEffect(() => {
18
+      getById(id);
19
+    }, []);
20
+  }
21
+
22
+  function handleSubmit(e) {
23
+    e.preventDefault();
24
+    props.form.validateFields((err, values) => {
25
+      if (!err) {
26
+        // 提交数据
27
+        submitData(values);
28
+      }
29
+    });
30
+  }
31
+
32
+  // 获取详情信息
33
+  function getById(currentId) {
34
+    request({ ...apis.buildingType.getById, urlData: { id: currentId } }).then(res => {
35
+      data = res;
36
+      props.form.setFieldsValue(res);
37
+    });
38
+  }
39
+
40
+  const openNotificationWithIcon = (type, message) => {
41
+    notification[type]({
42
+      message,
43
+      description: '',
44
+    });
45
+  };
46
+
47
+  function submitData(dataSources) {
48
+    if (id !== '') {
49
+      // 修改
50
+      updateType(dataSources);
51
+      return;
52
+    }
53
+
54
+    dataSources.status = 1;
55
+    dataSources.createDate = new Date();
56
+    request({ ...apis.buildingType.add, data: { ...dataSources } })
57
+      .then(() => {
58
+        // eslint-disable-next-line no-unused-expressions
59
+        openNotificationWithIcon('success', '操作成功');
60
+        router.go(-1);
61
+      })
62
+      .catch(err => {});
63
+  }
64
+
65
+  // 修改
66
+  function updateType(row) {
67
+    // const { url, method } = apis.buildingType.update
68
+    // const tempUrl = url.substring(0, url.lastIndexOf('id')).concat(row.buildingTypeId)
69
+
70
+    // request({ url: tempUrl, method, data: { ...row } }).then(() => {
71
+    //   router.go(-1)
72
+    // })
73
+    request({ ...apis.buildingType.update, urlData: { id: row.buildingTypeId }, data: { ...row } })
74
+      .then(() => {
75
+        // eslint-disable-next-line no-unused-expressions
76
+        openNotificationWithIcon('success', '操作成功');
77
+        router.go(-1);
78
+      })
79
+      .catch(err => {});
80
+  }
81
+
82
+  return (
83
+    <Card>
84
+      <Form onSubmit={handleSubmit} style={{ width: '500px', margin: 'auto' }}>
85
+        <Form.Item style={{ display: 'none' }}>
86
+          {getFieldDecorator('buildingTypeId')(<Input placeholder="buildingTtypeId" />)}
87
+        </Form.Item>
88
+        <Form.Item>
89
+          {getFieldDecorator('buildingTypeName', {
90
+            rules: [{ required: true, message: '请输入名称' }],
91
+          })(<Input placeholder="名称" />)}
92
+        </Form.Item>
93
+        <Form.Item style={{ display: 'flex', justifyContent: 'space-between' }}>
94
+          <Button
95
+            type="primary"
96
+            htmlType="submit"
97
+            className={Styles.addButton}
98
+            style={{ margin: '0' }}
99
+          >
100
+            确定
101
+          </Button>
102
+          &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
103
+          <Button onClick={() => router.go(-1)}>取消</Button>
104
+        </Form.Item>
105
+      </Form>
106
+    </Card>
107
+  );
108
+}
109
+
110
+const WrappedBuidingTypeForm = Form.create({ name: 'body' })(body);
111
+
112
+export default WrappedBuidingTypeForm;

+ 152
- 0
src/pages/building/type/index.jsx Ver arquivo

@@ -0,0 +1,152 @@
1
+import React, { useState, useEffect } from 'react';
2
+import { Button, Tag, notification, Modal } from 'antd';
3
+
4
+import router from 'umi/router';
5
+import moment from 'moment';
6
+import withActions from '@/components/ActionList';
7
+import EditIcon from '@/components/EditIcon';
8
+import AuthButton from '@/components/AuthButton';
9
+import TableList from '@/components/TableList';
10
+import request from '../../../utils/request';
11
+import apis from '../../../services/apis';
12
+
13
+export default function BuildType() {
14
+  // eslint-disable-next-line react-hooks/rules-of-hooks
15
+  const [data, setData] = useState([{ records: [] }]);
16
+  const [page, setPage] = useState({ current: 1, pageSize: 10 });
17
+
18
+  const columns = [
19
+    {
20
+      title: '编号',
21
+      dataIndex: 'index',
22
+      key: 'index',
23
+      align: 'center',
24
+      render: (text, record, index) => <span>{index + 1}</span>,
25
+    },
26
+    {
27
+      title: '类型名称',
28
+      dataIndex: 'buildingTypeName',
29
+      key: 'buildingTypeName',
30
+      align: 'center',
31
+      render: (_, record) => (
32
+        <Tag color="blue" onClick={() => toEdi(record.buildingTypeId)}>
33
+          {record.buildingTypeName}
34
+        </Tag>
35
+      ),
36
+    },
37
+    {
38
+      title: '创建时间',
39
+      dataIndex: 'createDate',
40
+      key: 'createDate',
41
+      align: 'center',
42
+      render: (_, record) => <span>{moment(record.createDate).format('YYYY-MM-DD')}</span>,
43
+    },
44
+    {
45
+      title: '操作',
46
+      dataIndex: 'row',
47
+      key: 'row',
48
+      align: 'center',
49
+      render: withActions((_, record) => [
50
+        <AuthButton name="admin.tdBuildingType.delete" noRight={null}>
51
+          <EditIcon text="删除" type="delete" onClick={() => deleteType(record)} />
52
+        </AuthButton>,
53
+        <AuthButton name="admin.tdBuildingType.update" noRight={null}>
54
+          <EditIcon text="编辑" type="edit" onClick={() => toEdi(record.buildingTypeId)} />
55
+        </AuthButton>,
56
+      ]),
57
+    },
58
+  ];
59
+
60
+  // eslint-disable-next-line react-hooks/rules-of-hooks
61
+  useEffect(() => {
62
+    getList({ pageNum: 1, pageSize: 10 });
63
+  }, []);
64
+
65
+  const openNotificationWithIcon = (type, message) => {
66
+    notification[type]({
67
+      message,
68
+      description: '',
69
+    });
70
+  };
71
+
72
+  function getList(params) {
73
+    request({ ...apis.buildingType.getList, params: { ...params } })
74
+      .then(res => {
75
+        setData(res);
76
+      })
77
+      .catch(err => {
78
+        // eslint-disable-next-line no-unused-expressions
79
+        // openNotificationWithIcon('error', err)
80
+      });
81
+  }
82
+
83
+  // 分页
84
+  function onChange(pageNumber) {
85
+    // eslint-disable-next-line no-console
86
+    console.log('Page: ', pageNumber);
87
+    getList({ pageNum: pageNumber, pageSize: 10 });
88
+  }
89
+
90
+  function deleteType(row) {
91
+    row.status = -1;
92
+    const modal = Modal.confirm();
93
+    modal.update({
94
+      content: '确认删除?',
95
+      okText: '确认',
96
+      cancelText: '关闭',
97
+      onOk: () => {
98
+        request({
99
+          ...apis.buildingType.update,
100
+          urlData: { id: row.buildingTypeId },
101
+          data: { ...row },
102
+        })
103
+          .then(() => {
104
+            getList({ pageNum: data.current, pageSize: 10 });
105
+          })
106
+          .then(() => {
107
+            openNotificationWithIcon('success', '操作成功');
108
+          })
109
+          .catch(err => {
110
+            // openNotificationWithIcon('error', err.message)
111
+          });
112
+
113
+        modal.destroy();
114
+      },
115
+      onCancel: () => {
116
+        modal.destroy();
117
+      },
118
+    });
119
+  }
120
+
121
+  // 跳转修改页
122
+  function toEdi(currentId) {
123
+    router.push({
124
+      pathname: '/building/type/edi',
125
+      query: {
126
+        id: currentId || '',
127
+      },
128
+    });
129
+  }
130
+  const actionRender = () => {
131
+    return (
132
+      <AuthButton name="admin.tdBuildingType.add" noRight={null}>
133
+        <Button type="danger" onClick={() => toEdi()} style={{ marginBottom: '20px' }}>
134
+          新增类型
135
+        </Button>
136
+      </AuthButton>
137
+    );
138
+  };
139
+
140
+  return (
141
+    <>
142
+      <TableList
143
+        // ref={ref}
144
+        rowKey="buildingType"
145
+        api={apis.buildingType.getList}
146
+        // searchFields={searchFields}
147
+        actionRender={actionRender}
148
+        columns={columns}
149
+      />
150
+    </>
151
+  );
152
+}

+ 48
- 0
src/pages/building/type/style.css Ver arquivo

@@ -0,0 +1,48 @@
1
+.SubmitButton {
2
+  background: #3a91d5;
3
+  border-radius: 7px;
4
+  border: 0px;
5
+}
6
+.SelectFrom {
7
+  width: 180px;
8
+  background: #ffffff;
9
+  border-radius: 7px;
10
+  border: 1px solid #dbdbdb;
11
+}
12
+.addButton {
13
+  background: #50be00;
14
+  border-radius: 4px;
15
+  border: 0px;
16
+  margin: 10px 0px;
17
+}
18
+.cardText {
19
+  font-size:  0.106rem;
20
+  color: #333;
21
+  line-height: 24px;
22
+  display: flex;
23
+  align-items: center;
24
+  position: relative;
25
+}
26
+.cardItem {
27
+
28
+  color: #666;
29
+  display: flex;
30
+  align-items: center;
31
+  line-height: 1.5;
32
+  font-size: 0.106rem;
33
+  margin-bottom: 0.08rem;
34
+}
35
+.ediText {
36
+  font-size: 0.106rem;
37
+  color: #ff925c;
38
+  line-height: 24px;
39
+  position: absolute;
40
+  right: 0;
41
+}
42
+.title {
43
+  display: inline-block;
44
+  width: 0.54rem;
45
+  justify-content: space-between;
46
+  text-align: justify;
47
+  text-align-last: justify;
48
+}

+ 49
- 0
src/pages/building/type/style.less Ver arquivo

@@ -0,0 +1,49 @@
1
+.SubmitButton {
2
+  background: #3a91d5;
3
+  border-radius: 7px;
4
+  border: 0px;
5
+}
6
+.SelectFrom {
7
+  width: 180px;
8
+  background: #ffffff;
9
+  border-radius: 7px;
10
+  border: 1px solid #dbdbdb;
11
+}
12
+.addButton {
13
+  background: #FF4A4A;
14
+  border-radius: 4px;
15
+  border: 0px;
16
+  margin: 10px 0px;
17
+}
18
+.cardText {
19
+  color: #333;
20
+  display: flex;
21
+  align-items: center;
22
+  position: relative;
23
+  line-height: 1.5;
24
+  font-size: 0.106rem;
25
+  margin-bottom: 0.08rem;
26
+
27
+}
28
+.cardItem{
29
+  color: #666;
30
+  display: flex;
31
+  align-items: center;  
32
+  line-height: 1.5;
33
+  font-size: 0.106rem;
34
+  margin-bottom: 0.08rem;
35
+}
36
+.ediText {
37
+  font-size: 0.106rem;
38
+  color: #ff925c;
39
+  line-height: 24px;
40
+  position: absolute;
41
+  right: 0;
42
+}
43
+.title{
44
+  display: inline-block;
45
+  width: 0.54rem;
46
+  justify-content: space-between;
47
+  text-align: justify;
48
+  text-align-last:justify
49
+}

+ 47
- 0
src/pages/building/type/style.wxss Ver arquivo

@@ -0,0 +1,47 @@
1
+.SubmitButton {
2
+  background: #3a91d5;
3
+  border-radius: 7px;
4
+  border: 0px;
5
+}
6
+.SelectFrom {
7
+  width: 180px;
8
+  background: #ffffff;
9
+  border-radius: 7px;
10
+  border: 1px solid #dbdbdb;
11
+}
12
+.addButton {
13
+  background: #50be00;
14
+  border-radius: 4px;
15
+  border: 0px;
16
+  margin: 10px 0px;
17
+}
18
+.cardText {
19
+  font-size:  0.106rem;
20
+  color: #333;
21
+  line-height: 24px;
22
+  display: flex;
23
+  align-items: center;
24
+  position: relative;
25
+}
26
+.cardItem {
27
+  font-size:  0.106rem;
28
+  font-weight: 400;
29
+  color: #666;
30
+  line-height: 24px;
31
+  display: flex;
32
+  align-items: center;
33
+}
34
+.ediText {
35
+  font-size:  0.106rem;
36
+  color: #ff925c;
37
+  line-height: 24px;
38
+  position: absolute;
39
+  right: 0;
40
+}
41
+.title {
42
+  display: inline-block;
43
+  width:  0.54rem;
44
+  justify-content: space-between;
45
+  text-align: justify;
46
+  text-align-last: justify;
47
+}

+ 165
- 0
src/pages/customer/Customer/PrivateCustomer/index.jsx Ver arquivo

@@ -0,0 +1,165 @@
1
+import React, { useState, useEffect, useMemo, useRef } from 'react';
2
+import { Avatar } from 'antd';
3
+
4
+import QueryTable from '@/components/QueryTable';
5
+import request from '@/utils/request';
6
+import apis from '@/services/apis';
7
+import Navigate from '@/components/Navigate';
8
+import getTableColumns from './tableColumns'
9
+
10
+function PrivateCustomer(props) {
11
+    const ref = useRef()
12
+    const [page, setPage] = useState({current: 1, pageSize: 10})
13
+  const columns = [
14
+    {
15
+      title: '头像',
16
+      dataIndex: 'picture',
17
+      key: 'picture',
18
+      align: 'center',
19
+      width: '10%',
20
+      render: (_, record) => (
21
+        <Avatar
22
+          shape="square"
23
+          style={{ color: 'blue', cursor: 'pointer' }}
24
+          onClick={() => toCustomerDateil(record)}
25
+          src={record.picture}
26
+          size={64}
27
+          icon="user"
28
+        />
29
+      ),
30
+    },
31
+    {
32
+      title: '姓名',
33
+      dataIndex: 'name',
34
+      key: 'name',
35
+      align: 'center',
36
+      width: '10%',
37
+      // eslint-disable-next-line no-nested-ternary
38
+      render: (_, record) => (
39
+        <>
40
+          <Navigate onClick={() => toCustomerDateil(record)}>{record.name}</Navigate>
41
+        </>
42
+      ),
43
+    },
44
+    {
45
+      title: '电话',
46
+      dataIndex: 'phone',
47
+      key: 'phone',
48
+      align: 'center',
49
+      width: '10%',
50
+    },
51
+    {
52
+      title: '性别',
53
+      dataIndex: 'sex',
54
+      key: 'sex',
55
+      align: 'center',
56
+      width: '10%',
57
+      // eslint-disable-next-line no-nested-ternary
58
+      render: (_, record) => (
59
+        <>
60
+          <span>{record.sex === 1 ? '男' : record.sex === 2 ? '女' : '未知'}</span>
61
+        </>
62
+      ),
63
+    },
64
+    {
65
+      title: '置业顾问',
66
+      dataIndex: 'consultantName',
67
+      key: 'consultantName',
68
+      align: 'center',
69
+      width: '10%',
70
+      // eslint-disable-next-line no-nested-ternary
71
+      render: (_, record) => (
72
+        <>
73
+          <span>{record.consultantName}</span>
74
+          <br />
75
+          <span>{record.consultTel}</span>
76
+        </>
77
+      ),
78
+    },
79
+    {
80
+      title: '归属项目',
81
+      dataIndex: 'buildingName',
82
+      key: 'buildingName',
83
+      align: 'center',
84
+      width: '10%',
85
+    },
86
+    {
87
+      title: '推广人员',
88
+      dataIndex: 'sharePersonName',
89
+      key: 'sharePersonName',
90
+      align: 'center',
91
+      width: '10%',
92
+    },
93
+    {
94
+      title: '客户状态',
95
+      dataIndex: 'reportRecommendStatus',
96
+      key: 'reportRecommendStatus',
97
+      align: 'center',
98
+      width: '10%',
99
+      // eslint-disable-next-line no-nested-ternary
100
+      render: (text, records) => {
101
+        if (records.status === 1) {
102
+          return '报备';
103
+        }
104
+        if (records.status === 2) {
105
+          return '到访';
106
+        }
107
+        if (records.status === 3) {
108
+          return '认购';
109
+        }
110
+        if (records.status === 4) {
111
+          return '签约';
112
+        }
113
+      },
114
+    },
115
+    {
116
+      title: '操作',
117
+      dataIndex: 'customerId',
118
+      key: 'customerId',
119
+      align: 'center',
120
+      width: '20%',
121
+      //   render: withActions((text, record) => [
122
+      //     <EditIcon color="#FF4A4A" text="查看详情" onClick={() => toCustomerDateil(record)} />,
123
+      //     <AuthButton name="admin.customer.recommend.edit.id.put" noRight={null}>
124
+      //       <Button className={Styles.text} type="link" onClick={() => showStatus(record)}>变更状态</Button>
125
+      //     </AuthButton>,
126
+
127
+      //     <AuthButton name="admin.customer.recommend.belong" noRight={null}>
128
+      //       <Button className={Styles.text} type="link" onClick={() => showGM(record)}>调整归属</Button>
129
+      //     </AuthButton>,
130
+
131
+      //     <AuthButton name="admin.mine.taPointsRecords.point.record" noRight={null}>
132
+      //       <Button className={ Styles.text } type="link" onClick={() => showRecord(record)}>积分记录</Button>
133
+      //     </AuthButton>,
134
+
135
+      //     <AuthButton name="admin.mine.taPointsRecords.id.get" noRight={null}>
136
+      //       <Button className={ Styles.text} type="link" onClick={() => showRecommend(record.personId)}>推荐客户</Button>
137
+      //     </AuthButton>
138
+      //   ]),
139
+    },
140
+  ];
141
+
142
+  const tableColumns = useMemo(() => {
143
+    return getTableColumns()
144
+  }, [page])
145
+
146
+
147
+//   const tableColumns = useMemo(()=>columns, []);
148
+
149
+  return (
150
+    <>
151
+      <QueryTable
152
+          ref={ref}
153
+        rowKey="customerId"
154
+        api={apis.customer.customerRecommend}
155
+        // searchFields={{}}
156
+        params={{ customerType: 'private' }}
157
+        columns={tableColumns}
158
+        //   actionRender={actionRender}
159
+        //   onPageChange={(pg) => setPage(pg)}
160
+      />
161
+    </>
162
+  );
163
+}
164
+
165
+export default PrivateCustomer;

+ 136
- 0
src/pages/customer/Customer/PrivateCustomer/tableColumns.js Ver arquivo

@@ -0,0 +1,136 @@
1
+import { router } from 'umi'
2
+import { Button, Avatar } from 'antd'
3
+import moment from 'moment'
4
+import OperButton from '@/components/OperButton'
5
+import AuthButton from '@/components/AuthButton'
6
+import withActions from '@/components/ActionList'
7
+import Navigate from '@/components/Navigate';
8
+
9
+export default () => [
10
+  {
11
+    title: '头像',
12
+    dataIndex: 'picture',
13
+    key: 'picture',
14
+    align: 'center',
15
+    width: '10%',
16
+    render: (_, record) => (
17
+      <Avatar
18
+        shape="square"
19
+        style={{ color: 'blue', cursor: 'pointer' }}
20
+        onClick={() => toCustomerDateil(record)}
21
+        src={record.picture}
22
+        size={64}
23
+        icon="user"
24
+      />
25
+    ),
26
+  },
27
+  {
28
+    title: '姓名',
29
+    dataIndex: 'name',
30
+    key: 'name',
31
+    align: 'center',
32
+    width: '10%',
33
+    // eslint-disable-next-line no-nested-ternary
34
+    render: (_, record) => (
35
+      <>
36
+        <Navigate onClick={() => toCustomerDateil(record)}>{record.name}</Navigate>
37
+      </>
38
+    ),
39
+  },
40
+  {
41
+    title: '电话',
42
+    dataIndex: 'phone',
43
+    key: 'phone',
44
+    align: 'center',
45
+    width: '10%',
46
+  },
47
+  {
48
+    title: '性别',
49
+    dataIndex: 'sex',
50
+    key: 'sex',
51
+    align: 'center',
52
+    width: '10%',
53
+    // eslint-disable-next-line no-nested-ternary
54
+    render: (_, record) => (
55
+      <>
56
+        <span>{record.sex === 1 ? '男' : record.sex === 2 ? '女' : '未知'}</span>
57
+      </>
58
+    ),
59
+  },
60
+  {
61
+    title: '置业顾问',
62
+    dataIndex: 'consultantName',
63
+    key: 'consultantName',
64
+    align: 'center',
65
+    width: '10%',
66
+    // eslint-disable-next-line no-nested-ternary
67
+    render: (_, record) => (
68
+      <>
69
+        <span>{record.consultantName}</span>
70
+        <br />
71
+        <span>{record.consultTel}</span>
72
+      </>
73
+    ),
74
+  },
75
+  {
76
+    title: '归属项目',
77
+    dataIndex: 'buildingName',
78
+    key: 'buildingName',
79
+    align: 'center',
80
+    width: '10%',
81
+  },
82
+  {
83
+    title: '推广人员',
84
+    dataIndex: 'sharePersonName',
85
+    key: 'sharePersonName',
86
+    align: 'center',
87
+    width: '10%',
88
+  },
89
+  {
90
+    title: '客户状态',
91
+    dataIndex: 'reportRecommendStatus',
92
+    key: 'reportRecommendStatus',
93
+    align: 'center',
94
+    width: '10%',
95
+    // eslint-disable-next-line no-nested-ternary
96
+    render: (text, records) => {
97
+      if (records.status === 1) {
98
+        return '报备';
99
+      }
100
+      if (records.status === 2) {
101
+        return '到访';
102
+      }
103
+      if (records.status === 3) {
104
+        return '认购';
105
+      }
106
+      if (records.status === 4) {
107
+        return '签约';
108
+      }
109
+    },
110
+  },
111
+  {
112
+    title: '操作',
113
+    dataIndex: 'customerId',
114
+    key: 'customerId',
115
+    align: 'center',
116
+    width: '20%',
117
+    //   render: withActions((text, record) => [
118
+    //     <EditIcon color="#FF4A4A" text="查看详情" onClick={() => toCustomerDateil(record)} />,
119
+    //     <AuthButton name="admin.customer.recommend.edit.id.put" noRight={null}>
120
+    //       <Button className={Styles.text} type="link" onClick={() => showStatus(record)}>变更状态</Button>
121
+    //     </AuthButton>,
122
+
123
+    //     <AuthButton name="admin.customer.recommend.belong" noRight={null}>
124
+    //       <Button className={Styles.text} type="link" onClick={() => showGM(record)}>调整归属</Button>
125
+    //     </AuthButton>,
126
+
127
+    //     <AuthButton name="admin.mine.taPointsRecords.point.record" noRight={null}>
128
+    //       <Button className={ Styles.text } type="link" onClick={() => showRecord(record)}>积分记录</Button>
129
+    //     </AuthButton>,
130
+
131
+    //     <AuthButton name="admin.mine.taPointsRecords.id.get" noRight={null}>
132
+    //       <Button className={ Styles.text} type="link" onClick={() => showRecommend(record.personId)}>推荐客户</Button>
133
+    //     </AuthButton>
134
+    //   ]),
135
+  },
136
+];

+ 29
- 0
src/pages/customer/Customer/index.jsx Ver arquivo

@@ -0,0 +1,29 @@
1
+import React, { useState, useEffect } from 'react';
2
+import { Radio } from 'antd';
3
+import PrivateCustomer from './PrivateCustomer';
4
+
5
+function Customer(props) {
6
+  const [customerType, setCustomerType] = useState('private');
7
+
8
+  return (
9
+    <>
10
+      <div style={{ margin: '20px 0' }}>
11
+        <Radio.Group
12
+          value={customerType}
13
+          onChange={e => {
14
+            setCustomerType(e.target.value);
15
+          }}
16
+          buttonStyle="solid"
17
+        >
18
+          <Radio.Button value="private">私客</Radio.Button>
19
+          <Radio.Button value="public">公客</Radio.Button>
20
+        </Radio.Group>
21
+      </div>
22
+      <div>
23
+        <PrivateCustomer></PrivateCustomer>
24
+      </div>
25
+    </>
26
+  );
27
+}
28
+
29
+export default Customer;

+ 170
- 0
src/pages/customer/customerlist/components/BatchAssistConsultant.jsx Ver arquivo

@@ -0,0 +1,170 @@
1
+import React, { useState, useEffect } from 'react';
2
+import { Form, Icon, Input, Button, DatePicker, Select, Card, Row, Col, Pagination, Alert, Table, Avatar, Radio, Modal, Descriptions, notification } from 'antd';
3
+import moment from 'moment';
4
+import request from '../../../../utils/request';
5
+import apis from '../../../../services/apis';
6
+import Styles from '../style.less';
7
+import BuildSelect from '../../../../components/SelectButton/BuildSelect'
8
+
9
+
10
+const { Option } = Select;
11
+// eslint-disable-next-line @typescript-eslint/no-unused-vars
12
+const { Meta } = Card;
13
+
14
+/**
15
+ * 分配置业顾问
16
+ *
17
+ * @param {*} props
18
+ * @returns
19
+ */
20
+class ModalAttribution extends React.Component {
21
+  constructor(props) {
22
+    super(props);
23
+    console.log(props, 'props')
24
+    this.state = {
25
+       dataSource: { records: [] },
26
+       visibleData: { visible: false, customerId: [], buildingName: '' },
27
+    }
28
+  }
29
+
30
+  // 挂载之后
31
+  componentDidMount() {
32
+    // this.getList({ pageNumber: 1, pageSize: 5 })
33
+  }
34
+
35
+  componentDidUpdate(preProps, preState) {
36
+    console.log(this.props.visibleData)
37
+    if (this.props.visibleData.visible !== preState.visibleData.visible) {
38
+      this.getList({ pageNumber: 1, pageSize: 5,buildingId: this.props.visibleData.buildingId})
39
+      this.setState({ visibleData: this.props.visibleData });
40
+    }
41
+  }
42
+
43
+  // 弹框确定按钮
44
+  // eslint-disable-next-line react/sort-comp
45
+  handleOk() {
46
+    this.props.onCancel()
47
+  }
48
+
49
+  // 弹框取消按钮
50
+  handleCancel() {
51
+    this.props.onCancel()
52
+  }
53
+
54
+  changBuilding(buildingId){
55
+    this.getUserList({ pageNumber: 1, pageSize: 5, buildingId: buildingId })
56
+    this.setState({ visibleData: { visible: this.props.visibleData.visible, customerId: this.props.visibleData.customerId, buildingName: buildingId } });
57
+  }
58
+
59
+  getUserList(params){
60
+    console.log('params: ', params)
61
+    if (params.buildingId === '' || params.buildingId === null || params.buildingId === undefined) {
62
+      return
63
+    }
64
+    // 网路请求
65
+    request({ ...apis.customer.buildingConsultant, params: { ...params } }).then(res => {
66
+      this.setState({ dataSource: res })
67
+    }).catch(err => {
68
+      message.err(err)
69
+    })
70
+  }
71
+
72
+  getList(params) {
73
+    // 网路请求
74
+    console.log('params: ', params)
75
+    request({ ...apis.customer.buildingConsultant , params: { ...params }}).then(res => {
76
+      this.setState({ dataSource: res })
77
+    }).catch(err => {
78
+      
79
+    })
80
+  }
81
+
82
+  openNotificationWithIcon = (type, message) => {
83
+    notification[type]({
84
+      message,
85
+      description:
86
+        '',
87
+    });
88
+  };
89
+
90
+   // 分页
91
+  onChange(pageNum) {
92
+    this.getList({ pageNumber: pageNum, pageSize: 5, buildingId: this.state.visibleData.buildingName ||this.props.visibleData.buildingId})
93
+
94
+    // this.getUserList({ pageNumber: pageNum, pageSize: 5, buildingId: this.state.visibleData.buildingName })
95
+  }
96
+
97
+  // 提交
98
+  submitGm(record) {
99
+    // 网路请求
100
+    request({ ...apis.customer.batchConsultantAssist, data: { userId: record.userId
101
+      ,buildingId:this.state.visibleData.buildingName  || this.props.visibleData.buildingId
102
+      , customerIds: this.state.visibleData.customerId } }).then(res => {
103
+      // eslint-disable-next-line no-unused-expressions
104
+      debugger
105
+      if(res!= null && res.failNum > 0 ){
106
+        this.openNotificationWithIcon('success', '共'+res.totalNum+'条数据,失败操作'+res.failNum+'条')
107
+      }else{
108
+        this.openNotificationWithIcon('success', '操作成功')
109
+      }
110
+      this.handleCancel()
111
+    }).catch(err => {
112
+      // eslint-disable-next-line no-unused-expressions
113
+  
114
+    })
115
+  }
116
+
117
+  render() {
118
+    const columns = [
119
+      // {
120
+      //   title: '编号',
121
+      //   dataIndex: 'userId',
122
+      //   key: 'userId',
123
+      // },
124
+      {
125
+        title: '姓名',
126
+        dataIndex: 'userName',
127
+        key: 'userName',
128
+      },
129
+      {
130
+        title: '电话',
131
+        dataIndex: 'phone',
132
+        key: 'phone',
133
+      },
134
+      {
135
+        title: '部门',
136
+        dataIndex: 'department',
137
+        key: 'department',
138
+      },
139
+      {
140
+        title: '岗位',
141
+        dataIndex: 'position',
142
+        key: 'position',
143
+      },
144
+      {
145
+        title: '操作',
146
+        dataIndex: 'personId',
147
+        key: 'personId',
148
+        // eslint-disable-next-line no-nested-ternary
149
+        render: (_, record) => <>{ <Button type="danger" onClick={() => this.submitGm(record)}>确定</Button>}</>, },
150
+    ]
151
+    return (
152
+      <>
153
+        <Modal
154
+            title="分配置业顾问"
155
+            width={800}
156
+            destroyOnClose="true"
157
+            footer={null}
158
+            visible={this.state.visibleData.visible}
159
+            onCancel={(e) => this.handleCancel(e)}
160
+          >
161
+            <span>你正在为{this.props.visibleData.customerId.length}位公客分配置业顾问</span><br/><br/>
162
+            {this.props.visibleData.buildingId == null && <BuildSelect onChange={this.changBuilding.bind(this)} value={this.state.visibleData.buildingName} />}
163
+            <Table rowKey="BatchAssistConsultant" dataSource={this.state.dataSource.records} columns={columns} pagination={{pageSize: 5, total: this.state.dataSource.total, onChange: e => this.onChange(e) }} />
164
+          </Modal>
165
+      </>
166
+    );
167
+  }
168
+}
169
+
170
+export default ModalAttribution

+ 160
- 0
src/pages/customer/customerlist/components/assistConsultant.jsx Ver arquivo

@@ -0,0 +1,160 @@
1
+import React, { useState, useEffect } from 'react';
2
+import { Form, Icon, Input, Button, DatePicker, Select, Card, Row, Col, Pagination, Alert, Table, Avatar, Radio, Modal, Descriptions, notification } from 'antd';
3
+import moment from 'moment';
4
+import request from '../../../../utils/request';
5
+import apis from '../../../../services/apis';
6
+import Styles from '../style.less';
7
+import BuildSelect from '../../../../components/SelectButton/BuildSelect'
8
+
9
+
10
+const { Option } = Select;
11
+// eslint-disable-next-line @typescript-eslint/no-unused-vars
12
+const { Meta } = Card;
13
+
14
+/**
15
+ * 分配置业顾问
16
+ *
17
+ * @param {*} props
18
+ * @returns
19
+ */
20
+class ModalAttribution extends React.Component {
21
+  constructor(props) {
22
+    super(props);
23
+    this.state = {
24
+       dataSource: { records: [] },
25
+       visibleData: { visible: false, customerId: '', buildingName: '' },
26
+    }
27
+  }
28
+
29
+  // 挂载之后
30
+  componentDidMount() {
31
+    // this.getList({ pageNumber: 1, pageSize: 5 })
32
+  }
33
+
34
+  componentDidUpdate(preProps, preState) {
35
+    console.log(this.props.visibleData)
36
+    if (this.props.visibleData.visible !== preState.visibleData.visible) {
37
+      this.getList({ pageNumber: 1, pageSize: 5, customerId: this.props.visibleData.customerId,buildingId:this.props.visibleData.buildingId })
38
+      this.setState({ visibleData: this.props.visibleData });
39
+    }
40
+  }
41
+
42
+  // 弹框确定按钮
43
+  // eslint-disable-next-line react/sort-comp
44
+  handleOk() {
45
+    this.props.onCancel()
46
+  }
47
+
48
+  // 弹框取消按钮
49
+  handleCancel() {
50
+    this.props.onCancel()
51
+  }
52
+
53
+  changBuilding(buildingId){
54
+    this.getUserList({ pageNumber: 1, pageSize: 5, buildingId: buildingId })
55
+    this.setState({ visibleData: { visible: this.props.visibleData.visible, customerId: this.props.visibleData.customerId, buildingName: buildingId } });
56
+  }
57
+
58
+  getUserList(params){
59
+    console.log('params: ', params)
60
+    if (params.buildingId === '' || params.buildingId === null || params.buildingId === undefined) {
61
+      return
62
+    }
63
+    // 网路请求
64
+    request({ ...apis.customer.buildingConsultant, params: { ...params } }).then(res => {
65
+      this.setState({ dataSource: res })
66
+    }).catch(err => {
67
+      
68
+    })
69
+  }
70
+
71
+  getList(params) {
72
+    // 网路请求
73
+    request({ ...apis.customer.buildingConsultant, params: { ...params } }).then(res => {
74
+      this.setState({ dataSource: res })
75
+    }).catch(err => {
76
+      
77
+    })
78
+  }
79
+
80
+  openNotificationWithIcon = (type, message) => {
81
+    notification[type]({
82
+      message,
83
+      description:
84
+        '',
85
+    });
86
+  };
87
+
88
+   // 分页
89
+  onChange(pageNum) {
90
+    this.getList({ pageNumber: pageNum, pageSize: 5, customerId: this.props.visibleData.customerId, buildingId: this.state.visibleData.buildingName ||this.props.visibleData.buildingId})
91
+  }
92
+
93
+  // 提交
94
+  submitGm(record) {
95
+    debugger
96
+    // 网路请求
97
+    request({ ...apis.customer.consultantAssist, urlData: { id: this.state.visibleData.customerId }, 
98
+      data: { userId: record.userId,buildingId:this.state.visibleData.buildingName  || this.props.visibleData.buildingId } }).then(res => {
99
+      // eslint-disable-next-line no-unused-expressions
100
+      
101
+      this.openNotificationWithIcon('success', '操作成功')
102
+      this.handleCancel()
103
+    }).catch(err => {
104
+      // eslint-disable-next-line no-unused-expressions
105
+    })
106
+  }
107
+
108
+  render() {
109
+    const columns = [
110
+      // {
111
+      //   title: '编号',
112
+      //   dataIndex: 'userId',
113
+      //   key: 'userId',
114
+      // },
115
+      {
116
+        title: '姓名',
117
+        dataIndex: 'userName',
118
+        key: 'userName',
119
+      },
120
+      {
121
+        title: '电话',
122
+        dataIndex: 'phone',
123
+        key: 'phone',
124
+      },
125
+      {
126
+        title: '部门',
127
+        dataIndex: 'department',
128
+        key: 'department',
129
+      },
130
+      {
131
+        title: '岗位',
132
+        dataIndex: 'position',
133
+        key: 'position',
134
+      },
135
+      {
136
+        title: '操作',
137
+        dataIndex: 'personId',
138
+        key: 'personId',
139
+        // eslint-disable-next-line no-nested-ternary
140
+        render: (_, record) => <>{ <Button type="danger" onClick={() => this.submitGm(record)}>确定</Button>}</>, },
141
+    ]
142
+    return (
143
+      <>
144
+        <Modal
145
+            title="分配置业顾问"
146
+            width={800}
147
+            destroyOnClose="true"
148
+            footer={null}
149
+            visible={this.state.visibleData.visible}
150
+            onCancel={(e) => this.handleCancel(e)}
151
+          >
152
+            {this.props.visibleData.buildingId == null && <BuildSelect  onChange={this.changBuilding.bind(this)} value={this.state.visibleData.buildingName} />}
153
+            <Table rowKey="assistConsultant" dataSource={this.state.dataSource.records} columns={columns} pagination={{pageSize: 5, total: this.state.dataSource.total, onChange: e => this.onChange(e) }} />
154
+          </Modal>
155
+      </>
156
+    );
157
+  }
158
+}
159
+
160
+export default ModalAttribution

+ 146
- 0
src/pages/customer/customerlist/components/attribution.jsx Ver arquivo

@@ -0,0 +1,146 @@
1
+import React, { useState, useEffect } from 'react';
2
+import { Form, Icon, Input, Button, DatePicker, Select, Card, Row, Col, Pagination, Alert, Table, Avatar, Radio, Modal, Descriptions, notification } from 'antd';
3
+import moment from 'moment';
4
+import request from '../../../../utils/request';
5
+import apis from '../../../../services/apis';
6
+import Styles from '../style.less';
7
+
8
+
9
+const { Option } = Select;
10
+// eslint-disable-next-line @typescript-eslint/no-unused-vars
11
+const { Meta } = Card;
12
+
13
+/**
14
+ * 调整归属
15
+ *
16
+ * @param {*} props
17
+ * @returns
18
+ */
19
+class ModalAttribution extends React.Component {
20
+  constructor(props) {
21
+    super(props);
22
+    this.state = {
23
+       dataSource: { records: [] },
24
+       visibleData: { visible: false, customerId: '', realtyConsultant: '', buildingId: '' },
25
+    }
26
+  }
27
+
28
+  // 挂载之后
29
+  componentDidMount() {
30
+    // this.getList({ pageNumber: 1, pageSize: 5 })
31
+  }
32
+
33
+  componentDidUpdate(preProps, preState) {
34
+    console.log(this.props.visibleData)
35
+    if (this.props.visibleData.visible !== preState.visibleData.visible) {
36
+      this.getList({ pageNumber: 1, pageSize: 5, buildingId: this.props.visibleData.buildingId })
37
+      this.setState({ visibleData: this.props.visibleData });
38
+    }
39
+  }
40
+
41
+  // 弹框确定按钮
42
+  // eslint-disable-next-line react/sort-comp
43
+  handleOk() {
44
+    this.props.onCancel()
45
+  }
46
+
47
+  // 弹框取消按钮
48
+  handleCancel() {
49
+    this.props.onCancel()
50
+  }
51
+
52
+  getList(params) {
53
+    console.log('params: ', params)
54
+    if (params.buildingId === '' || params.buildingId === null || params.buildingId === undefined) {
55
+      return
56
+    }
57
+    // 网路请求
58
+    request({ ...apis.customer.buildingConsultant, params: { ...params } }).then(res => {
59
+      console.log('res',res);
60
+      this.setState({ dataSource: res })
61
+    }).catch(err => {
62
+      // eslint-disable-next-line no-unused-expressions
63
+      
64
+    })
65
+  }
66
+
67
+  openNotificationWithIcon = (type, message) => {
68
+    notification[type]({
69
+      message,
70
+      description:
71
+        '',
72
+    });
73
+  };
74
+
75
+   // 分页
76
+  onChange(pageNum) {
77
+    this.getList({ pageNumber: pageNum, pageSize: 5, buildingId: this.props.visibleData.buildingId })
78
+  }
79
+
80
+  // 提交
81
+  submitGm(record) {
82
+    // 网路请求
83
+    request({ ...apis.customer.recommendEdit, urlData: { id: this.state.visibleData.customerId }, data: { customerId: this.state.visibleData.customerId, realtyConsultant: record.userId } }).then(res => {
84
+      // eslint-disable-next-line no-unused-expressions
85
+      this.openNotificationWithIcon('success', '操作成功')
86
+      this.handleCancel()
87
+    }).catch(err => {
88
+      // eslint-disable-next-line no-unused-expressions
89
+      this.openNotificationWithIcon('error', err)
90
+    })
91
+  }
92
+
93
+  render() {
94
+    const columns = [
95
+      // {
96
+      //   title: '编号',
97
+      //   dataIndex: 'userId',
98
+      //   key: 'userId',
99
+      // },
100
+      {
101
+        title: '姓名',
102
+        dataIndex: 'userName',
103
+        key: 'userName',
104
+      },
105
+      {
106
+        title: '电话',
107
+        dataIndex: 'phone',
108
+        key: 'phone',
109
+      },
110
+      {
111
+        title: '部门',
112
+        dataIndex: 'department',
113
+        key: 'department',
114
+      },
115
+      {
116
+        title: '岗位',
117
+        dataIndex: 'position',
118
+        key: 'position',
119
+      },
120
+      {
121
+        title: '操作',
122
+        dataIndex: 'personId',
123
+        key: 'personId',
124
+        // eslint-disable-next-line no-nested-ternary
125
+        render: (_, record) => <>{ this.props.visibleData.realtyConsultant != record.userId && <Button type="danger" onClick={() => this.submitGm(record)}>确定</Button>}</>,
126
+      },
127
+    ]
128
+    return (
129
+      <>
130
+        <Modal
131
+            title="选择置业顾问"
132
+            width={800}
133
+            destroyOnClose="true"
134
+            footer={null}
135
+            visible={this.state.visibleData.visible}
136
+            // onOk={() => this.handleOk()}
137
+            onCancel={(e) => this.handleCancel(e)}
138
+          >
139
+            <Table rowKey="attribution" dataSource={this.state.dataSource.records} columns={columns} pagination={{ total: this.state.dataSource.total, pageSize: this.state.dataSource.size, onChange: e => this.onChange(e) }} />
140
+          </Modal>
141
+      </>
142
+    );
143
+  }
144
+}
145
+
146
+export default ModalAttribution

+ 81
- 0
src/pages/customer/customerlist/components/changeStatus.jsx Ver arquivo

@@ -0,0 +1,81 @@
1
+import React, { useState, useEffect } from 'react';
2
+import { Form, Icon, Input, Button, DatePicker, Select, Card, Row, Col, Pagination, Alert, Table, Avatar, Radio, Modal, Descriptions, notification } from 'antd';
3
+import moment from 'moment';
4
+import request from '../../../../utils/request';
5
+import apis from '../../../../services/apis';
6
+import Styles from '../style.less';
7
+
8
+class ChangeStatus extends React.Component {
9
+  constructor(props) {
10
+    super(props)
11
+    this.state = {
12
+      value: '',
13
+      visibleData: { visible: false, customerId: '' },
14
+    }
15
+  }
16
+
17
+  componentDidUpdate(preProps, preState) {
18
+    if (this.props.visibleData.customerId !== preState.visibleData.customerId) {
19
+      this.setState({ visibleData: { ...this.props.visibleData } })
20
+      this.setState({ value: this.props.visibleData.status })
21
+    }
22
+  }
23
+
24
+  // 弹框取消按钮
25
+  handleCancel() {
26
+    this.props.onCancel()
27
+  }
28
+
29
+  onChange = e => {
30
+    this.setState({
31
+      value: e.target.value,
32
+    });
33
+  };
34
+
35
+  openNotificationWithIcon = (type, message) => {
36
+    notification[type]({
37
+      message,
38
+      description:
39
+        '',
40
+    });
41
+  };
42
+
43
+  submitButton = e => {
44
+    // 网路请求
45
+    request({ ...apis.customer.recommendEdit, urlData: { id: this.state.visibleData.customerId }, data: { customerId: this.state.visibleData.customerId, status: this.state.value } }).then(res => {
46
+      // eslint-disable-next-line no-unused-expressions
47
+      this.openNotificationWithIcon('success', '操作成功')
48
+      this.handleCancel()
49
+    }).catch(err => {
50
+      // eslint-disable-next-line no-unused-expressions
51
+      this.openNotificationWithIcon('error', err)
52
+    })
53
+  }
54
+
55
+  render() {
56
+    return (
57
+      <>
58
+          <Modal
59
+            title="变更状态"
60
+            width={800}
61
+            destroyOnClose="true"
62
+            footer={null}
63
+            visible={this.state.visibleData.visible}
64
+            // onOk={() => this.handleOk()}
65
+            onCancel={(e) => this.handleCancel(e)}
66
+          >
67
+            <Radio.Group onChange={this.onChange} value={this.state.value}>
68
+              <Radio value={1}>客户报备</Radio>
69
+              <Radio value={2}>客户到访</Radio>
70
+              <Radio value={3}>客户认购</Radio>
71
+              <Radio value={4}>客户签约</Radio>
72
+            </Radio.Group>
73
+
74
+            <Button type="danger" onClick={this.submitButton}>确定</Button>
75
+          </Modal>
76
+      </>
77
+    )
78
+  }
79
+}
80
+
81
+export default ChangeStatus

+ 160
- 0
src/pages/customer/customerlist/components/integralRecord.jsx Ver arquivo

@@ -0,0 +1,160 @@
1
+import React, { useState, useEffect } from 'react';
2
+import { Form, Icon, Input, Button, DatePicker, Select, Card, Row, Col, Pagination, Alert, Table, Avatar, Radio, Modal, Descriptions, notification } from 'antd';
3
+import moment from 'moment';
4
+import request from '../../../../utils/request';
5
+import apis from '../../../../services/apis';
6
+import Styles from '../style.less';
7
+
8
+
9
+const { Option } = Select;
10
+// eslint-disable-next-line @typescript-eslint/no-unused-vars
11
+const { Meta } = Card;
12
+
13
+/**
14
+ * 积分记录
15
+ *
16
+ * @param {*} props
17
+ * @returns
18
+ */
19
+class ModalIntegralRecord extends React.Component {
20
+  constructor(props) {
21
+    super(props);
22
+    this.state = {
23
+       dataSource: { result: { records: [] }, totalPoints: 0 },
24
+       visibleData: { visible: false, customerId: '' },
25
+    }
26
+  }
27
+
28
+  // 挂载之后
29
+  componentDidMount() {
30
+    this.getList({ pageNumber: 1, pageSize: 5 })
31
+  }
32
+
33
+  componentDidUpdate(preProps, preState) {
34
+    if (this.props.visibleData.customerId !== preState.visibleData.customerId) {
35
+      this.getList({ pageNumber: 1, pageSize: 5 })
36
+      this.setState({ visibleData: this.props.visibleData });
37
+    }
38
+  }
39
+
40
+  // 弹框确定按钮
41
+  // eslint-disable-next-line react/sort-comp
42
+  handleOk() {
43
+    this.props.onCancel()
44
+  }
45
+
46
+  // 弹框取消按钮
47
+  handleCancel() {
48
+    this.props.onCancel()
49
+  }
50
+
51
+  openNotificationWithIcon = (type, message) => {
52
+    notification[type]({
53
+      message,
54
+      description:
55
+        '',
56
+    });
57
+  }
58
+
59
+  getList(params) {
60
+    const { customerId } = this.state.visibleData
61
+    if (customerId === '' || customerId === undefined) {
62
+      return
63
+    }
64
+    // 网路请求
65
+    // 网路请求
66
+    request({ ...apis.customer.taPointsRecords, urlData: { id: customerId }, params: { ...params } }).then(res => {
67
+      this.setState({ dataSource: res })
68
+    }).catch(err => {
69
+      this.openNotificationWithIcon('error', err)
70
+    })
71
+  }
72
+
73
+   // 分页
74
+  onChange(pageNum) {
75
+    this.getList({ pageNumber: pageNum, pageSize: 5 })
76
+  }
77
+
78
+  // 积分类型
79
+  showChangeType(str) {
80
+    switch (str) {
81
+      case 'goods':
82
+        return '兑换商品'
83
+      case 'checkin':
84
+        return '签到'
85
+      case 'share-poster':
86
+        return '分享'
87
+      case 'signup-agent':
88
+        return '授权手机号'
89
+      case 'activity_checkin':
90
+        return '活动签到'
91
+      case 'document_verify':
92
+          return '资料审核'  
93
+      case 'recommend-customer':
94
+        return '推荐客户'
95
+        case 'group':
96
+        return '拼团'
97
+        case 'recommend-customer':
98
+        return '推荐客户'
99
+      default:
100
+        return '未知类型'
101
+    }
102
+  }
103
+
104
+  // 判断是否是 - 开头
105
+  subPoints(points) {
106
+    const subStr = points.toString().substring(0, 1)
107
+    if (subStr === '-') {
108
+      return true
109
+    }
110
+    return false
111
+  }
112
+
113
+// eslint-disable-next-line class-methods-use-this
114
+  render() {
115
+    const columns = [
116
+      {
117
+        title: '序号',
118
+        dataIndex: 'index',
119
+        key: 'index',
120
+        render: (text, record, index) => <span>{index + 1}</span>,
121
+        // render: (text, record, index) => <span>{(this.state.dataSource.result.total - this.state.dataSource.result.records.length) * 1 + index + 1}</span>,
122
+      },
123
+      {
124
+        title: '积分类型',
125
+        dataIndex: 'changeType',
126
+        key: 'changeType',
127
+        render: (_, record) => <span>{this.showChangeType(record.changeType) }</span>,
128
+      },
129
+      {
130
+        title: '积分变化',
131
+        dataIndex: 'pointsAmount',
132
+        key: 'pointsAmount',
133
+        // render: (pointsAmount, record) => { <span style={this.subPoints(pointsAmount) && {color: 'red'}}>{ pointsAmount }</span> },
134
+      },
135
+      {
136
+        title: '发生时间',
137
+        dataIndex: 'createDate',
138
+        key: 'createDate',
139
+        render: (createDate, records) => <span> {moment(createDate).format('YYYY-MM-DD')}</span>,
140
+      },
141
+    ]
142
+    return (
143
+      <>
144
+        <Modal
145
+            title={ '当前可用积分:' + (this.state.dataSource.totalPoints  || 0 )}
146
+            width={800}
147
+            destroyOnClose="true"
148
+            footer={null}
149
+            visible={this.state.visibleData.visible}
150
+            // onOk={() => this.handleOk()}
151
+            onCancel={e => this.handleCancel(e)}
152
+          >
153
+            <Table dataSource={this.state.dataSource.result.records} columns={columns} pagination={{pageSize: 5, total: this.state.dataSource.result.total, onChange: e => this.onChange(e) }} />
154
+          </Modal>
155
+      </>
156
+    );
157
+  }
158
+}
159
+
160
+export default ModalIntegralRecord

+ 146
- 0
src/pages/customer/customerlist/components/recommend.jsx Ver arquivo

@@ -0,0 +1,146 @@
1
+import React, { useState, useEffect } from 'react';
2
+import { Form, Icon, Input, Button, DatePicker, Select, Card, Row, Col, Pagination, Alert, Table, Avatar, Radio, Modal, Descriptions, notification } from 'antd';
3
+import moment from 'moment';
4
+import request from '../../../../utils/request';
5
+import apis from '../../../../services/apis';
6
+import Styles from '../style.less';
7
+
8
+
9
+const { Option } = Select;
10
+// eslint-disable-next-line @typescript-eslint/no-unused-vars
11
+const { Meta } = Card;
12
+
13
+/**
14
+ * 推荐客户
15
+ *
16
+ * @param {*} props
17
+ * @returns
18
+ */
19
+class ModalRecommendRecord extends React.Component {
20
+  constructor(props) {
21
+    super(props);
22
+    this.state = {
23
+       dataSource: [],
24
+       visibleData: { visible: false, customerId: '' },
25
+    }
26
+  }
27
+
28
+  // 挂载之后
29
+  componentDidMount() {
30
+    this.getList({ pageNumber: 1, pageSize: 5 })
31
+  }
32
+
33
+  componentDidUpdate(preProps, preState) {
34
+    // console.log('this.props.visibleData', this.props.visibleData)
35
+    if (this.props.visibleData.customerId !== preState.visibleData.customerId) {
36
+      this.getList({ pageNumber: 1, pageSize: 5 })
37
+      this.setState({ visibleData: this.props.visibleData });
38
+    }
39
+  }
40
+
41
+  // 弹框确定按钮
42
+  // eslint-disable-next-line react/sort-comp
43
+  handleOk() {
44
+    this.props.onCancel()
45
+  }
46
+
47
+  // 弹框取消按钮
48
+  handleCancel() {
49
+    this.props.onCancel()
50
+  }
51
+
52
+  openNotificationWithIcon = (type, message) => {
53
+    notification[type]({
54
+      message,
55
+      description:
56
+        '',
57
+    });
58
+  }
59
+
60
+  getList(params) {
61
+    const { customerId } = this.state.visibleData
62
+    if (customerId === '' || customerId === undefined) {
63
+      return
64
+    }
65
+    console.log('customerId', customerId)
66
+    // 网路请求
67
+    // 网路请求
68
+    request({ ...apis.customer.recommendClient, urlData: { id: customerId }, params: { ...params } }).then(res => {
69
+      this.setState({ dataSource: res })
70
+    }).catch(err => {
71
+      this.openNotificationWithIcon('error', err)
72
+    })
73
+  }
74
+
75
+   // 分页
76
+  onChange(pageNum) {
77
+    this.getList({ pageNumber: pageNum, pageSize: 5 })
78
+  }
79
+
80
+  render() {
81
+    const columns = [
82
+      {
83
+        title: '头像',
84
+        // eslint-disable-next-line jsx-a11y/alt-text
85
+        render: (text, records) => <img src={records.picture} width={50} height={50} />,
86
+      },
87
+      {
88
+        title: '用户名',
89
+        dataIndex: 'name',
90
+        key: 'name',
91
+      },
92
+      {
93
+        title: '电话',
94
+        dataIndex: 'phone',
95
+        key: 'phone',
96
+      },
97
+      {
98
+        title: '性别',
99
+        dataIndex: 'sex',
100
+        key: 'sex',
101
+        render: (text, records) => <span>{records.sex === 1 ? '男' : '女'}</span>,
102
+      },
103
+      {
104
+        title: '意向项目',
105
+        dataIndex: 'intention',
106
+        key: 'intention',
107
+      },
108
+      {
109
+        title: '推荐时间',
110
+        dataIndex: 'createDate',
111
+        key: 'createDate',
112
+        render: (_, record) => <><span>{ record.createDate && moment(record.createDate).format('YYYY-MM-DD HH:mm:ss') }</span></>,
113
+      },
114
+      {
115
+        title: '状态',
116
+        // eslint-disable-next-line consistent-return
117
+        render: (text, records) => {
118
+          if (records.verifyStatus === 0) { return '未审核' }
119
+          if (records.verifyStatus === 1) {
120
+            if (records.reportRecommendStatus === 1) { return '报备' }
121
+            if (records.reportRecommendStatus === 2) { return '推荐' }
122
+           }
123
+          if (records.verifyStatus === 2) { return '审核不通过' }
124
+        },
125
+      },
126
+    ]
127
+    return (
128
+      <>
129
+        <Modal
130
+            title="推荐客户"
131
+            width={800}
132
+            destroyOnClose="true"
133
+            footer={null}
134
+            visible={this.state.visibleData.visible}
135
+            // onOk={() => this.handleOk()}
136
+            onCancel={(e) => this.handleCancel(e)}
137
+          >
138
+            {console.log('this.state.dataSource11111111111', this.state.dataSource.total)}
139
+            <Table dataSource={this.state.dataSource.records} rowKey="integralrecord" columns={columns} pagination={{ total: this.state.dataSource.total, pageSize: this.state.dataSource.size, onChange: e => this.onChange(e) }} />
140
+          </Modal>
141
+      </>
142
+    );
143
+  }
144
+}
145
+
146
+export default ModalRecommendRecord

+ 217
- 0
src/pages/customer/customerlist/customerDetail.jsx Ver arquivo

@@ -0,0 +1,217 @@
1
+import React, { useState, useEffect } from 'react';
2
+import { Form, Table, Pagination, Button } from 'antd';
3
+import { FormattedMessage } from 'umi-plugin-react/locale';
4
+import styles from './style.less';
5
+import apis from '../../../services/apis';
6
+import request from '../../../utils/request';
7
+import moment from 'moment';
8
+import BuildSelect from '../../../components/SelectButton/BuildSelect'
9
+
10
+import router from 'umi/router';
11
+import Prompt from 'umi/prompt';
12
+
13
+function header(props) {
14
+  /**
15
+   * @param {*} props
16
+   * @returns
17
+   */
18
+  // eslint-disable-next-line react-hooks/rules-of-hooks
19
+  const [data, setData] = useState([{}])
20
+  const [tableData, setTableDataData] = useState([{}])
21
+  const [dataConsultant, setDataonsultant] = useState({})
22
+  const [intentionData, setIntentionData] = useState([])
23
+  const [buildingIdValue, setBuildingIdData] = useState()
24
+
25
+  // eslint-disable-next-line react-hooks/rules-of-hooks
26
+  useEffect(() => {
27
+    getById()
28
+    toLoadIntention()
29
+  }, [])
30
+
31
+  function toLoadIntention() {
32
+    const arr = []
33
+    // 意向值
34
+    for (let i = 0; i < intentionData.length; i++) {
35
+      const a = [].concat(
36
+        intentionData.concat([{
37
+          key: i,
38
+          name: `Edward King ${i}`,
39
+          age: 32,
40
+          address: `London, Park Lane no. ${i}`,
41
+        }]),
42
+      )
43
+      arr.push(a)
44
+    }
45
+    setIntentionData(arr)
46
+  }
47
+
48
+  // 查询
49
+  const { id } = props.location.query;
50
+  function getById(params) {
51
+    if (id === '' || id === undefined) {
52
+      return
53
+    }
54
+    // eslint-disable-next-line max-len
55
+    request({ ...apis.customer.CustomerRecommendGet, urlData: { id }, params: { ...params, pageNumber: 1, pageSize: 10 } }).then(res => {
56
+      setData(res)
57
+      if (res.visitRecords) {
58
+        setTableDataData(res.visitRecords)
59
+      }
60
+      setDataonsultant(res.geoInfo)
61
+      setIntentionData(res.intentions)
62
+    })
63
+  }
64
+  // 分页
65
+  function onChange(number) {
66
+    if (id === '' || id === undefined) {
67
+      return
68
+    }
69
+    // eslint-disable-next-line max-len
70
+    request({ ...apis.customer.CustomerRecommendGet, urlData: { id }, params: { pageNumber: number, pageSize: 10 } }).then(res => {
71
+      setData(res)
72
+      setTableDataData(res.visitRecords)
73
+      setDataonsultant(res.geoInfo)
74
+    })
75
+  }
76
+
77
+  function changBuilding(buildingId) {
78
+    setBuildingIdData(buildingId)
79
+    getById({ pageNumber: 1, pageSize: 10, buildingId: buildingId })
80
+  }
81
+
82
+  const columns = [
83
+    {
84
+      title: '访问事件',
85
+      dataIndex: 'activity',
86
+      key: 'activity',
87
+      align: 'center',
88
+    },
89
+    {
90
+      title: '访问内容',
91
+      dataIndex: 'activityName',
92
+      key: 'activityName',
93
+      align: 'center',
94
+    },
95
+    {
96
+      title: '访问时间',
97
+      dataIndex: 'visitTime',
98
+      key: 'visitTime',
99
+      align: 'center',
100
+      render: (_, record) => <span> {moment(record.visitTime).format('YYYY-MM-DD HH:mm:ss')}</span>,
101
+    },
102
+    {
103
+      title: '停留时间',
104
+      dataIndex: 'visitDuration',
105
+      key: 'visitDuration',
106
+      align: 'center',
107
+      render: (_, record) => <span>{record.visitDuration === null ? 0 : record.visitDuration}秒</span>,
108
+    },
109
+  ]
110
+  // 意向
111
+  const intention = [
112
+    {
113
+      title: <span styles={{ color: '#222', fontSize: '24px' }}>项目名称</span>,
114
+      dataIndex: 'name',
115
+      width: '64%',
116
+      render: (_, record) => <span>{record.buildingName}</span>,
117
+    },
118
+    {
119
+      title: <span styles={{ color: '#222', fontSize: '24px' }}>意向值</span>,
120
+      dataIndex: 'address',
121
+      render: (_, record) => <span >{record.intention}</span>,
122
+    },
123
+  ];
124
+  return (
125
+    <>
126
+      <div style={{ textAlign: 'right', marginBottom:'16px'}}>
127
+        <Button onClick={() => router.go(-1)}>返回</Button>
128
+      </div>
129
+
130
+      <div className={styles.cardBox}>
131
+        <div className={styles.rightBox}>
132
+          <p className={styles.tit}>客户信息</p>
133
+          <div className={styles.flexBox}>
134
+            <img className={styles.touxiang} src={data.picture && data.picture} />
135
+            <div className={styles.right}>
136
+              <p className={styles.rightItem}>用户名称:{data.name}</p>
137
+              <p className={styles.rightItem}>手机号码:{data.phone}</p>
138
+              <p className={styles.rightItem}>首次访问时间:{data.visitTime && moment(data.visitTime).format('YYYY-MM-DD')}</p>
139
+            </div>
140
+            <div className={styles.Centered}>
141
+              <p>访问时长:{data.duration}秒</p>
142
+              <p>访问次数:{data.visitTimes}次</p>
143
+              <p className={styles.rightItem}>客户描述:{data.describe}</p>
144
+              {/* <p>预约人数:{ data.visiteNum }</p> */}
145
+            </div>
146
+            <div className={styles.rightCentered}>
147
+              <p >需求类型:{data.demandType}</p>
148
+              <p >价格区间:{data.priceRange}</p>
149
+              <p >物业类型:{data.realtyManageType}</p>
150
+              {/* <p>预约人数:{ data.visiteNum }</p> */}
151
+            </div>
152
+            {/* <p className={styles.rightItem}>预约到访时间:{data.appointmentTime && moment(data.appointmentTime).format('YYYY-MM-DD') }</p> */}
153
+            <div className={styles.rightInfo}>
154
+              <p className={styles.rightItem}>国家:{dataConsultant && dataConsultant.country}</p>
155
+              <p className={styles.rightItem}>省份:{dataConsultant && dataConsultant.provience}</p>
156
+              <p className={styles.rightItem}>城市:{dataConsultant && dataConsultant.city}</p>
157
+              <p className={styles.rightItem}>来源渠道:{data.personFrom ? data.personFrom : "其他"}</p>
158
+              {/* <p className={styles.rightItem}>详细信息:</p> */}
159
+              {/* <p className={styles.rightItem}>意向项目:{data.intention }</p> */}
160
+              {/* <p className={styles.rightItem}>客户说明:{ data.verifyRemark }</p> */}
161
+            </div>
162
+          </div>
163
+        </div>
164
+      </div>
165
+      <br></br>
166
+      <div className={styles.cardBox}>
167
+        <div className={styles.rightBoxCentre}>
168
+          <p className={styles.tit}>置业顾问信息</p>
169
+          <div>
170
+            <img className={styles.touxiangphoto} src={data.consultant && data.consultant.photo} />
171
+            <p >{data.consultant && data.consultant.userName}</p>
172
+          </div>
173
+          <div className={styles.rightphone}>
174
+            {/* <p className={styles.infoItem}>姓名:{ data.consultant && data.consultant.name }</p>
175
+          <p className={styles.infoItem}>部门:{ data.consultant && data.consultant.department }</p> */}
176
+            <p className={styles.infoItem}>号码:{data.consultant && data.consultant.phone} </p>
177
+            <p className={styles.infoItem}>岗位:{data.consultant && data.consultant.position}</p>
178
+          </div>
179
+          <div className={styles.left}>
180
+            <p className={styles.infoItem}>公司:{data.consultant && data.consultant.orgName} </p>
181
+            <p className={styles.infoItem}>
182
+              所属项目:
183
+            {
184
+                data.consultant && data.consultant.projects.map((item, _) => <span>{item}</span>)
185
+              }
186
+            </p>
187
+          </div>
188
+        </div>
189
+        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
190
+        <div className={styles.leftBoxCentre}>
191
+          {/* <div className={styles.tit}>
192
+                <span>项目名称</span>
193
+                <span style={{ marginLeft: '50%' }}>意向值</span>
194
+          </div> */}
195
+          {/* <img className={styles.touxiang} src={ data.picture && data.picture } /> */}
196
+          {/* <div className={styles.infoItem}> */}
197
+          <Table bordered={false} columnWidth={10} columns={intention} dataSource={intentionData} pagination={false} scroll={intentionData && intentionData.length >= 3 ? { y: 240 } : false} width={500} />
198
+          {/* </div> */}
199
+        </div>
200
+
201
+      </div>
202
+      <div className={styles.recordBox}>
203
+        <p className={styles.tableName}>访问记录</p>
204
+        <BuildSelect onChange={changBuilding} value={buildingIdValue} />
205
+        <Table dataSource={tableData.records} columns={columns} style={{ marginTop: '15px' }} pagination={{ pageSize: 10, total: tableData.total, onChange }} />
206
+        {/* <Pagination showQuickJumper defaultCurrent={1} total={data.records} onChange={e => changePageNum(e)} current={data.visitRecords.total}/> */}
207
+      </div>
208
+      <Prompt message={location =>
209
+        location.pathname.startsWith("/customer/customerlist")
210
+          ? true
211
+          : (localStorage.removeItem("priPageParams"), localStorage.removeItem("pubPageParams"), localStorage.removeItem("state"))} />
212
+    </>
213
+  )
214
+}
215
+const WrappedHeader = Form.create({ name: 'header' })(header);
216
+
217
+export default WrappedHeader

+ 786
- 0
src/pages/customer/customerlist/index copy.jsx Ver arquivo

@@ -0,0 +1,786 @@
1
+import React, { useState, useEffect } from 'react';
2
+import { Form, Icon, Input, Button, DatePicker, Select, Card, Row, Col, Pagination, Alert, Table, Avatar, Radio, Modal, Descriptions, message } from 'antd';
3
+import moment from 'moment';
4
+import request from '../../../utils/request';
5
+import apis from '../../../services/apis';
6
+import Styles from './style.less';
7
+import router from 'umi/router';
8
+import withActions from '@/components/ActionList';
9
+import Attribution from './components/attribution'
10
+import AssistConsultant from './components/assistConsultant'
11
+import BatchAssistConsultant from './components/BatchAssistConsultant'
12
+import IntegralRecord from './components/integralRecord'
13
+import ModalRecommendRecord from './components/recommend'
14
+import ChangeStatus from './components/changeStatus'
15
+import BuildSelect from '../../../components/SelectButton/BuildSelect'
16
+import AuthButton from '@/components/AuthButton';
17
+import Navigate from '@/components/Navigate';
18
+import WxDictSelect from '@/components/SelectButton/WxDictSelect';
19
+import Prompt from 'umi/prompt';
20
+import EditIcon from '@/components/EditIcon';
21
+
22
+
23
+const { Option } = Select;
24
+// eslint-disable-next-line @typescript-eslint/no-unused-vars
25
+const { Meta } = Card;
26
+
27
+/**
28
+ *
29
+ *
30
+ * @param {*} props
31
+ * @returns
32
+ */
33
+function body(props) {
34
+  const { getFieldDecorator, getFieldsValue } = props.form
35
+
36
+  // eslint-disable-next-line react-hooks/rules-of-hooks
37
+  const [dataSource, setDataSource] = useState({ records: [] })
38
+  // eslint-disable-next-line react-hooks/rules-of-hooks
39
+  const [dataPub, setDataPub] = useState({ records: [] })
40
+  // 默认私客
41
+  // eslint-disable-next-line react-hooks/rules-of-hooks
42
+  const [customerType, setCustomerType] = useState('private')
43
+
44
+  // 调整归属 ============  start
45
+  // eslint-disable-next-line react-hooks/rules-of-hooks
46
+  const [gVisibleData, setGVisibleData] = useState({ visible: false, customerId: '', realtyConsultant: '', buildingId: '' })
47
+  // 调整归属 ============= end
48
+
49
+  // 积分记录 ============  start
50
+  // eslint-disable-next-line react-hooks/rules-of-hooks
51
+  const [recordVisibleData, setRecordVisibleData] = useState({ visible: false, customerId: '' })
52
+  // 积分记录 ============= end
53
+
54
+  // 变更状态 ============  start
55
+  // eslint-disable-next-line react-hooks/rules-of-hooks
56
+  const [statusVisibleData, setStatusVisibleData] = useState({ visible: false, customerId: '', status: '' })
57
+  // 变更状态 ============= end
58
+
59
+  // 推荐客户
60
+  const [recommendVisibleData, setRecommendVisibleData] = useState({ visible: false, customerId: '' })
61
+
62
+  const [assistVisibleData, setAssistVisibleData] = useState({ visible: false, customerId: '', buildingId: '' })
63
+
64
+  const [loadingStatus, setLoadingStatus] = useState(false)
65
+
66
+  // 选中的公客信息
67
+  const [personInfo, setPersonInfo] = useState([])
68
+
69
+  const [batchAssistVisibleData, setBatchAssistVisibleData] = useState({ visible: false, customerId: [], buildingId: '' })
70
+
71
+  const [currentSelectedRows, setSelectedRows] = useState([])
72
+
73
+  const [selectedRowKeys, setSelectedRowKeys] = useState([])
74
+
75
+  // eslint-disable-next-line react-hooks/rules-of-hooks
76
+  useEffect(() => {
77
+    // localStorage.setItem("state", JSON.stringify({ value }))
78
+    if (localStorage.getItem("state")) {
79
+      console.log(localStorage.getItem("state"), 'state')
80
+      if (JSON.parse(localStorage.getItem("state")) == 'private') {
81
+        const localStorageValue = JSON.parse(localStorage.getItem('priPageParams'))
82
+        localStorageValue.startCreateDate = localStorageDate(localStorageValue.startCreateDate)
83
+        localStorageValue.endCreateDate = localStorageDate(localStorageValue.endCreateDate)
84
+        props.form.setFieldsValue(localStorageValue);
85
+        getList({ pageNumber: 1, pageSize: 10, ...JSON.parse(localStorage.getItem("priPageParams")) })
86
+      } else {
87
+        setCustomerType('public')
88
+        const localStorageValue = JSON.parse(localStorage.getItem("pubPageParams"))
89
+        localStorageValue.startCreateDate = localStorageDate(localStorageValue.startCreateDate)
90
+        localStorageValue.endCreateDate = localStorageDate(localStorageValue.endCreateDate)
91
+        props.form.setFieldsValue(localStorageValue);
92
+        getList({ pageNumber: 1, pageSize: 10, ...JSON.parse(localStorage.getItem("pubPageParams")) })
93
+      }
94
+
95
+    } else {
96
+      localStorage.setItem("priPageParams", JSON.stringify({ pageNumber: 1, pageSize: 10, customerType }))
97
+      getList({ pageNumber: 1, pageSize: 10, customerType })
98
+    }
99
+  }, [])
100
+
101
+  function getList(params) {
102
+    // 网路请求
103
+    if (params.startCreateDate) {
104
+      params.startCreateDate = moment(params.startCreateDate).format('YYYY-MM-DD HH:mm:ss');
105
+    }
106
+    if (params.endCreateDate) {
107
+      params.endCreateDate = moment(params.endCreateDate).format('YYYY-MM-DD HH:mm:ss');
108
+    }
109
+    request({ ...apis.customer.customerRecommend, params: { ...params } }).then(res => {
110
+      if (params.customerType === 'private') {
111
+        setDataSource(res)
112
+      } else {
113
+        setDataPub(res)
114
+      }
115
+    }).catch(err => {
116
+      // eslint-disable-next-line no-unused-expressions
117
+      <Alert
118
+        style={{
119
+          marginBottom: 24,
120
+        }}
121
+        message={err}
122
+        type="error"
123
+        showIcon
124
+      />
125
+    })
126
+  }
127
+
128
+  function displayNone() {
129
+    setRecordVisibleData({ visible: false, customerId: '' })
130
+    setGVisibleData({ visible: false, customerId: '', realtyConsultant: '' })
131
+    setStatusVisibleData({ visible: false, customerId: '', status: '' })
132
+    setRecommendVisibleData({ visible: false, customerId: '' })
133
+  }
134
+
135
+  // 提交事件
136
+  function handleSubmit(e) {
137
+    displayNone()
138
+
139
+    setPersonInfo([])
140
+    setSelectedRowKeys([])
141
+
142
+    e.preventDefault();
143
+    props.form.validateFields((err, values) => {
144
+      if (!err) {
145
+        if (values.startCreateDate) {
146
+          values.startCreateDate = moment(values.startCreateDate).format('YYYY-MM-DD HH:mm:ss');
147
+        }
148
+        if (values.endCreateDate) {
149
+          values.endCreateDate = moment(values.endCreateDate).format('YYYY-MM-DD HH:mm:ss');
150
+        }
151
+        if (customerType === 'private') {
152
+          localStorage.setItem("priPageParams", JSON.stringify({ customerType: 'private', ...values }))
153
+          getList(JSON.parse(localStorage.getItem("priPageParams")))
154
+        } else {
155
+          localStorage.setItem("pubPageParams", JSON.stringify({ customerType: 'public', ...values }))
156
+          getList(JSON.parse(localStorage.getItem("pubPageParams")))
157
+        }
158
+        // getList({ pageNum: 1, pageSize: 10, customerType, ...values })
159
+
160
+
161
+      }
162
+    });
163
+  }
164
+
165
+  // Change 事件
166
+  function handleSelectChange(e) {
167
+    // eslint-disable-next-line no-console
168
+    console.log(e)
169
+  }
170
+
171
+  const localStorageDate = (date) => date ? moment(date) : ''
172
+
173
+  // 分页
174
+  function onChange(pageNum) {
175
+    // eslint-disable-next-line react-hooks/rules-of-hooks
176
+    props.form.validateFields((err, values) => {
177
+      if (!err) {
178
+        localStorage.setItem("state", JSON.stringify(customerType))
179
+        if (customerType === 'private') {
180
+          localStorage.setItem("priPageParams", JSON.stringify({ pageNumber: pageNum, pageSize: 10, customerType: 'private', ...props.form.getFieldsValue() }))
181
+          getList(JSON.parse(localStorage.getItem("priPageParams")))
182
+        } else {
183
+          localStorage.setItem("pubPageParams", JSON.stringify({ pageNumber: pageNum, pageSize: 10, customerType: 'public', ...props.form.getFieldsValue() }))
184
+          getList(JSON.parse(localStorage.getItem("pubPageParams")))
185
+        }
186
+
187
+
188
+      }
189
+    });
190
+  }
191
+
192
+  // 私客/公客切换
193
+  function radioButtonHandleSizeChange(e) {
194
+
195
+    displayNone()
196
+
197
+    props.form.resetFields();
198
+    const { value } = e.target
199
+    setCustomerType(value)
200
+    localStorage.setItem("state", JSON.stringify(value))
201
+    if (value === 'private') {
202
+      if (localStorage.getItem("priPageParams")) {
203
+        const localStorageValue = JSON.parse(localStorage.getItem("priPageParams"))
204
+        localStorageValue.startCreateDate = localStorageDate(localStorageValue.startCreateDate)
205
+        localStorageValue.endCreateDate = localStorageDate(localStorageValue.endCreateDate)
206
+        props.form.setFieldsValue(localStorageValue);
207
+        getList(JSON.parse(localStorage.getItem("priPageParams")))
208
+      } else {
209
+        localStorage.setItem("priPageParams", JSON.stringify({ pageNumber: 1, pageSize: 10, customerType: value }))
210
+        getList({ pageNumber: 1, pageSize: 10, customerType: value })
211
+      }
212
+
213
+    } else {
214
+
215
+      if (localStorage.getItem("pubPageParams")) {
216
+        const localStorageValue = JSON.parse(localStorage.getItem("pubPageParams"))
217
+        localStorageValue.startCreateDate = localStorageDate(localStorageValue.startCreateDate)
218
+        localStorageValue.endCreateDate = localStorageDate(localStorageValue.endCreateDate)
219
+        props.form.setFieldsValue(localStorageValue);
220
+
221
+        getList({ pageNumber: 1, pageSize: 10, customerType, ...JSON.parse(localStorage.getItem("pubPageParams")) })
222
+      } else {
223
+        localStorage.setItem("pubPageParams", JSON.stringify({ pageNumber: 1, pageSize: 10, customerType: 'public' }))
224
+        getList({ pageNumber: 1, pageSize: 10, customerType: 'public' })
225
+      }
226
+    }
227
+
228
+    //setColumns(value === 'private' ? privateColumns : publicColumns)
229
+
230
+  }
231
+
232
+  function handleReset() {
233
+    props.form.resetFields();
234
+    if (customerType === 'private') {
235
+      localStorage.setItem("priPageParams", JSON.stringify({ pageNumber: 1, pageSize: 10, customerType: 'private' }));
236
+      getList(JSON.parse(localStorage.getItem("priPageParams")))
237
+    } else {
238
+      localStorage.setItem("pubPageParams", JSON.stringify({ pageNumber: 1, pageSize: 10, customerType: 'public' }))
239
+      getList(JSON.parse(localStorage.getItem("pubPageParams")))
240
+    }
241
+
242
+  }
243
+
244
+
245
+  // 这里有个 Bug, 就是 Modal 弹框,会联动出现, 比如 我点击 调整归属的Model弹框, 那么 积分记录的Model弹框莫名其妙的也显示了
246
+  // 所有这里临时解决方法是,弹出一个Modal对话框的时候,把其他的对话框给隐藏
247
+
248
+  function showGM(record) {
249
+    setGVisibleData({ visible: true, customerId: record.customerId, realtyConsultant: record.realtyConsultant, buildingId: record.buildingId })
250
+    setRecordVisibleData({ visible: false, customerId: '' })
251
+    setStatusVisibleData({ visible: false, customerId: '' })
252
+    setRecommendVisibleData({ visible: false, customerId: '' })
253
+    setBatchAssistVisibleData({ visible: false, customerId: '', buildingId: '' })
254
+  }
255
+
256
+  function showRecord(record) {
257
+    setRecordVisibleData({ visible: true, customerId: record.personId })
258
+    setGVisibleData({ visible: false, customerId: '', realtyConsultant: '', buildingId: '' })
259
+    setStatusVisibleData({ visible: false, customerId: '' })
260
+    setRecommendVisibleData({ visible: false, customerId: '' })
261
+    setBatchAssistVisibleData({ visible: false, customerId: '', buildingId: '' })
262
+  }
263
+
264
+  function showStatus(record) {
265
+    setRecordVisibleData({ visible: false, customerId: '' })
266
+    setGVisibleData({ visible: false, customerId: '', realtyConsultant: '', buildingId: '' })
267
+    setRecommendVisibleData({ visible: false, customerId: '' })
268
+    setStatusVisibleData({ visible: true, customerId: record.customerId, status: record.status })
269
+    setBatchAssistVisibleData({ visible: false, customerId: '', buildingId: '' })
270
+  }
271
+
272
+  // 推荐客户
273
+  function showRecommend(personId) {
274
+    setRecordVisibleData({ visible: false, customerId: '' })
275
+    setGVisibleData({ visible: false, customerId: '', realtyConsultant: '', buildingId: '' })
276
+    setStatusVisibleData({ visible: false, customerId: '', status: '' })
277
+    setRecommendVisibleData({ visible: true, customerId: personId })
278
+    setBatchAssistVisibleData({ visible: false, customerId: '' })
279
+  }
280
+
281
+  //分配置业顾问
282
+  function assistConsultant(personId, buildingId) {
283
+    setGVisibleData({ visible: false, customerId: '', realtyConsultant: '', buildingId: '' })
284
+    setRecordVisibleData({ visible: false, customerId: '' })
285
+    setStatusVisibleData({ visible: false, customerId: '' })
286
+    setRecommendVisibleData({ visible: false, customerId: '' })
287
+    setAssistVisibleData({ visible: true, customerId: personId, buildingId: buildingId })
288
+    setBatchAssistVisibleData({ visible: false, customerId: '', buildingId: '' })
289
+  }
290
+
291
+  function batchAssistConsultant() {
292
+    console.log(personInfo, 'personInfo')
293
+    console.log(personInfo.length)
294
+    if (personInfo.length <= 0) {
295
+      return message.info('请至少选择一条数据');
296
+    }
297
+
298
+    const compareSet = new Set();
299
+    personInfo.filter(record => {
300
+      compareSet.add(record.buildingName)
301
+    })
302
+
303
+    if (compareSet.size != 1) {
304
+      return message.info('选中的公客存在于不同项目中,请分开进行分配置业顾问操作');
305
+    }
306
+
307
+    // const newSelectedRows = personInfo.filter()  
308
+
309
+    // console.log(newSelectedRows,'newSelectedRows')
310
+
311
+    setGVisibleData({ visible: false, customerId: '', realtyConsultant: '', buildingId: '' })
312
+    setRecordVisibleData({ visible: false, customerId: '' })
313
+    setStatusVisibleData({ visible: false, customerId: '' })
314
+    setRecommendVisibleData({ visible: false, customerId: '' })
315
+    setAssistVisibleData({ visible: false, customerId: '', buildingId: '' })
316
+    setBatchAssistVisibleData({ visible: true, customerId: personInfo, buildingId: personInfo[0].buildingId })
317
+  }
318
+
319
+  function toCustomerDateil(record) {
320
+    router.push({
321
+      pathname: '/customer/customerlist/customerDetail',
322
+      query: {
323
+        id: record.customerId,
324
+      },
325
+    });
326
+  }
327
+  // 私客详情
328
+  function publicCustomerDetail(record) {
329
+    router.push({
330
+      pathname: '/customer/customerlist/publicCustomerDetail',
331
+      query: {
332
+        id: record.personId,
333
+      },
334
+    });
335
+  }
336
+
337
+  function exportCustomer() {
338
+    setLoadingStatus(true)
339
+    const fieldsValue = getFieldsValue()
340
+    if (fieldsValue.startCreateDate) {
341
+      fieldsValue.startCreateDate = moment(fieldsValue.startCreateDate).format('YYYY-MM-DD HH:mm:ss');
342
+    }
343
+    if (fieldsValue.endCreateDate) {
344
+      fieldsValue.endCreateDate = moment(fieldsValue.endCreateDate).format('YYYY-MM-DD HH:mm:ss');
345
+    }
346
+    request({ ...apis.customer.customerRecommendExport, responseType: 'blob', params: { ...fieldsValue, customerType } })
347
+      .then(response => {
348
+        download(response)
349
+        setLoadingStatus(false)
350
+      }).catch(error => {
351
+        message.err('连接超时');
352
+        setLoadingStatus(false)
353
+      })
354
+  }
355
+
356
+  function download(data) {
357
+    if (!data) {
358
+      return
359
+    }
360
+    const url = window.URL.createObjectURL(new Blob([data]))
361
+    const link = document.createElement('a')
362
+    link.style.display = 'none'
363
+    link.href = url
364
+    link.setAttribute('download', '客户列表.xlsx')
365
+    document.body.append(link)
366
+    link.click()
367
+  }
368
+
369
+  // 关闭调整归属的窗口
370
+  function closeAttribution() {
371
+    setGVisibleData({ visible: false, customerId: '', realtyConsultant: '', buildingId: '' })
372
+    if (customerType === 'private') {
373
+      getList(JSON.parse(localStorage.getItem('priPageParams')))
374
+    } else {
375
+      getList(JSON.parse(localStorage.getItem('pubPageParams')))
376
+    }
377
+  }
378
+  // 回调关闭所有弹框
379
+  function closeAll() {
380
+    setRecordVisibleData({ visible: false, customerId: '' })
381
+    setGVisibleData({ visible: false, customerId: '', realtyConsultant: '', buildingId: '' })
382
+    setStatusVisibleData({ visible: false, customerId: '', status: '' })
383
+    setRecommendVisibleData({ visible: false, customerId: '' })
384
+    setAssistVisibleData({ visible: false, customerId: '', buildingId: '' })
385
+    setBatchAssistVisibleData({ visible: false, customerId: '' })
386
+    if (customerType === 'private') {
387
+
388
+      getList(JSON.parse(localStorage.getItem("priPageParams")))
389
+    } else {
390
+
391
+      getList(JSON.parse(localStorage.getItem("pubPageParams")))
392
+    }
393
+    // getList({ pageNum: 1, pageSize: 10, customerType, ...props.form.getFieldsValue() })
394
+  }
395
+
396
+  const rowSelection = {
397
+    selectedRowKeys,
398
+    onChange: (selectedRowKeys, selectedRows) => {
399
+      console.log('selectedRowKeys:', selectedRowKeys, 'selectedRows: ', selectedRows);
400
+      setSelectedRowKeys(selectedRowKeys)
401
+      const newSelectedRows = personInfo.filter(x => !selectedRows.some(y => x.customerId === y.customerId))     // 去重
402
+        .concat(selectedRows)                                                                 // 新增选择
403
+        .filter(x => selectedRowKeys.some(y => y === x.customerId))                             // 去掉未选的数据
404
+
405
+      // setSelectedRows(newSelectedRows)
406
+      console.log(newSelectedRows, "33333333333333333333")
407
+      setPersonInfo(newSelectedRows)
408
+    },
409
+  };
410
+
411
+  const publicColumns = [
412
+    {
413
+      title: '头像',
414
+      dataIndex: 'picture',
415
+      key: 'picture',
416
+      align: 'center',
417
+      width: '10%',
418
+      // render: (_, record) => <Avatar shape="square" style={{ color: 'blue', cursor: 'pointer' }} onClick={() => publicCustomerDetail(record)} src={customerType === 'private' ? record.picture : record.avatarurl} size={64} icon="user" />,
419
+      render: (_, record) => <Avatar shape="square" style={{ color: 'blue', cursor: 'pointer' }} onClick={() => publicCustomerDetail(record)} src={record.picture} size={64} icon="user" />,
420
+    },
421
+    {
422
+      title: '姓名',
423
+      dataIndex: 'name',
424
+      key: 'name',
425
+      align: 'center',
426
+      width: '10%',
427
+      // eslint-disable-next-line no-nested-ternary
428
+      render: (_, record) => <><Navigate onClick={() => publicCustomerDetail(record)}>{record.name || record.nickname}</Navigate></>,
429
+    },
430
+    {
431
+      title: '电话',
432
+      dataIndex: 'phone',
433
+      key: 'phone',
434
+      align: 'center',
435
+      width: '15%',
436
+    },
437
+    {
438
+      title: '是否归属项目',
439
+      dataIndex: '',
440
+      key: '',
441
+      align: 'center',
442
+      width: '10%',
443
+      render: (_, record) => <><span>{record.buildingId != null ? '是' : '否'}</span></>,
444
+    },
445
+    {
446
+      title: '归属项目',
447
+      dataIndex: 'buildingName',
448
+      key: 'buildingName',
449
+      align: 'center',
450
+      width: '15%',
451
+    },
452
+    {
453
+      title: '推广人员',
454
+      dataIndex: 'sharePersonName',
455
+      key: 'sharePersonName',
456
+      align: 'center',
457
+      width: '10%',
458
+    },
459
+    {
460
+      title: '性别',
461
+      dataIndex: 'sex',
462
+      key: 'sex',
463
+      align: 'center',
464
+      width: '10%',
465
+      // eslint-disable-next-line no-nested-ternary
466
+      render: (_, record) => <><span>{record.sex === 1 ? '男' : record.sex === 2 ? '女' : '未知'}</span></>,
467
+    },
468
+    {
469
+      title: '操作',
470
+      dataIndex: 'customerId',
471
+      key: 'customerId',
472
+      align: 'center',
473
+      width: '20%',
474
+      render: withActions((text, record) => [
475
+        <EditIcon color="#FF4A4A" text="查看详情" onClick={() => publicCustomerDetail(record)} />,
476
+        <AuthButton name="admin.customer.recommend" noRight={null}>
477
+          <Button className={customerType === 'private' ? Styles.displayS : Styles.text} type="link" onClick={() => showRecommend(record.personId)}>推荐客户</Button>
478
+        </AuthButton>,
479
+        <AuthButton name="admin.customer.assign" noRight={null}>
480
+          <Button className={customerType === 'private' ? Styles.displayS : Styles.text} type="link" onClick={() => assistConsultant(record.customerId, record.buildingId)}>分配置业顾问</Button>
481
+        </AuthButton>,
482
+        <AuthButton name="admin.mine.taPointsRecords.point.record" noRight={null}>
483
+          <Button className={Styles.text} type="link" onClick={() => showRecord(record)}>积分记录</Button>
484
+        </AuthButton>
485
+      ]),
486
+      // render: (_, record) => (
487
+      //   <>
488
+      //     {/* <AuthButton name="admin.customer.public.detail" noRight={null}>
489
+      //       <Button className={customerType === 'private' ? Styles.displayS : Styles.text } type="link" onClick={() => publicCustomerDetail(record)}>查看详情</Button>
490
+      //     </AuthButton>
491
+      //     &nbsp;&nbsp; */}
492
+      //     <AuthButton name="admin.customer.recommend" noRight={null}>
493
+      //       <Button className={customerType === 'private' ? Styles.displayS : Styles.text} type="link" onClick={() => showRecommend(record.personId)}>推荐客户</Button>
494
+      //     </AuthButton>
495
+      //     <AuthButton name="admin.customer.assign" noRight={null}>
496
+      //       <Button className={customerType === 'private' ? Styles.displayS : Styles.text} type="link" onClick={() => assistConsultant(record.customerId, record.buildingId)}>分配置业顾问</Button>
497
+      //     </AuthButton>
498
+      //     <AuthButton name="admin.mine.taPointsRecords.point.record" noRight={null}>
499
+      //       <Button className={Styles.text} type="link" onClick={() => showRecord(record)}>积分记录</Button>
500
+      //     </AuthButton>
501
+      //   </>
502
+      // ),
503
+    },
504
+  ]
505
+
506
+  const privateColumns = [
507
+    {
508
+      title: '头像',
509
+      dataIndex: 'picture',
510
+      key: 'picture',
511
+      align: 'center',
512
+      width: '10%',
513
+      render: (_, record) => <Avatar shape="square" style={{ color: 'blue', cursor: 'pointer' }} onClick={() => toCustomerDateil(record)} src={customerType === 'private' ? record.picture : record.avatarurl} size={64} icon="user" />,
514
+    },
515
+    {
516
+      title: '姓名',
517
+      dataIndex: 'name',
518
+      key: 'name',
519
+      align: 'center',
520
+      width: '10%',
521
+      // eslint-disable-next-line no-nested-ternary
522
+      render: (_, record) => <><Navigate onClick={() => toCustomerDateil(record)}>{customerType === 'private' ? record.name : record.nickname}</Navigate></>,
523
+    },
524
+    {
525
+      title: '电话',
526
+      dataIndex: 'phone',
527
+      key: 'phone',
528
+      align: 'center',
529
+      width: '10%',
530
+    },
531
+    {
532
+      title: '性别',
533
+      dataIndex: 'sex',
534
+      key: 'sex',
535
+      align: 'center',
536
+      width: '10%',
537
+      // eslint-disable-next-line no-nested-ternary
538
+      render: (_, record) => <><span>{record.sex === 1 ? '男' : record.sex === 2 ? '女' : '未知'}</span></>,
539
+    },
540
+    {
541
+      title: '置业顾问',
542
+      dataIndex: 'consultantName',
543
+      key: 'consultantName',
544
+      align: 'center',
545
+      width: '10%',
546
+      // eslint-disable-next-line no-nested-ternary
547
+      render: (_, record) => (
548
+        <>
549
+          <span>{record.consultantName}</span>
550
+          <br />
551
+          <span>{record.consultTel}</span>
552
+        </>
553
+      ),
554
+    },
555
+    {
556
+      title: '归属项目',
557
+      dataIndex: 'buildingName',
558
+      key: 'buildingName',
559
+      align: 'center',
560
+      width: '10%',
561
+    },
562
+    {
563
+      title: '推广人员',
564
+      dataIndex: 'sharePersonName',
565
+      key: 'sharePersonName',
566
+      align: 'center',
567
+      width: '10%',
568
+    },
569
+    {
570
+      title: '客户状态',
571
+      dataIndex: 'reportRecommendStatus',
572
+      key: 'reportRecommendStatus',
573
+      align: 'center',
574
+      width: '10%',
575
+      // eslint-disable-next-line no-nested-ternary
576
+      render: (text, records) => {
577
+        if (records.status === 1) { return '报备' }
578
+        if (records.status === 2) { return '到访' }
579
+        if (records.status === 3) { return '认购' }
580
+        if (records.status === 4) { return '签约' }
581
+      },
582
+    },
583
+    {
584
+      title: '操作',
585
+      dataIndex: 'customerId',
586
+      key: 'customerId',
587
+      align: 'center',
588
+      width: '20%',
589
+      render: withActions((text, record) => [
590
+        <EditIcon color="#FF4A4A" text="查看详情" onClick={() => toCustomerDateil(record)} />,
591
+        <AuthButton name="admin.customer.recommend.edit.id.put" noRight={null}>
592
+          <Button className={customerType === 'private' ? Styles.text : Styles.displayS} type="link" onClick={() => showStatus(record)}>变更状态</Button>
593
+        </AuthButton>,
594
+
595
+        <AuthButton name="admin.customer.recommend.belong" noRight={null}>
596
+          <Button className={customerType === 'private' ? Styles.text : Styles.displayS} type="link" onClick={() => showGM(record)}>调整归属</Button>
597
+        </AuthButton>,
598
+
599
+        <AuthButton name="admin.mine.taPointsRecords.point.record" noRight={null}>
600
+          <Button className={customerType === 'private' ? Styles.text : Styles.displayS} type="link" onClick={() => showRecord(record)}>积分记录</Button>
601
+        </AuthButton>,
602
+
603
+        <AuthButton name="admin.mine.taPointsRecords.id.get" noRight={null}>
604
+          <Button className={customerType === 'private' ? Styles.text : Styles.displayS} type="link" onClick={() => showRecommend(record.personId)}>推荐客户</Button>
605
+        </AuthButton>
606
+      ]),
607
+      // render: (_, record) => (
608
+      //   <>
609
+      //     <AuthButton name="admin.customer.recommend.edit.id.put" noRight={null}>
610
+      //       <Button className={customerType === 'private' ? Styles.text : Styles.displayS} type="link" onClick={() => showStatus(record)}>变更状态</Button>
611
+      //     </AuthButton>
612
+      //     <AuthButton name="admin.customer.recommend.belong" noRight={null}>
613
+      //       <Button className={customerType === 'private' ? Styles.text : Styles.displayS} type="link" onClick={() => showGM(record)}>调整归属</Button>
614
+      //     </AuthButton>
615
+
616
+      //     {/* <AuthButton name="admin.customer.recommend.get" noRight={null}>
617
+      //       <Button className={customerType === 'private' ? Styles.text : Styles.displayS} type="link" onClick={() => toCustomerDateil(record)}>查看详情</Button>
618
+      //     </AuthButton>
619
+      //       &nbsp;&nbsp; */}
620
+      //     <AuthButton name="admin.mine.taPointsRecords.point.record" noRight={null}>
621
+      //       <Button className={customerType === 'private' ? Styles.text : Styles.displayS} type="link" onClick={() => showRecord(record)}>积分记录</Button>
622
+      //     </AuthButton>
623
+      //     <AuthButton name="admin.mine.taPointsRecords.id.get" noRight={null}>
624
+      //       <Button className={customerType === 'private' ? Styles.text : Styles.displayS} type="link" onClick={() => showRecommend(record.personId)}>推荐客户</Button>
625
+      //     </AuthButton>
626
+
627
+      //   </>
628
+      // ),
629
+    },
630
+  ]
631
+
632
+  return (
633
+    <>
634
+      <Form layout="inline" onSubmit={e => handleSubmit(e, props)}>
635
+        {customerType === 'public' && <Form.Item>
636
+          {getFieldDecorator('belongStatus')(
637
+            <Select style={{ width: '180px' }} placeholder="是否归属项目" onChange={handleSelectChange}>
638
+              <Option value={1}>是</Option>
639
+              <Option value={0}>否</Option>
640
+            </Select>,
641
+          )}
642
+        </Form.Item>}
643
+        <Form.Item>
644
+          {getFieldDecorator('buildingId')(
645
+            <BuildSelect />,
646
+          )}
647
+        </Form.Item>
648
+        {customerType === 'private' && <Form.Item>
649
+          {getFieldDecorator('status')(
650
+            <Select style={{ width: '180px' }} placeholder="状态" onChange={handleSelectChange}>
651
+              <Option value={1}>报备</Option>
652
+              <Option value={2}>到访</Option>
653
+              <Option value={3}>认筹</Option>
654
+              <Option value={4}>签约</Option>
655
+            </Select>,
656
+          )}
657
+        </Form.Item>}
658
+        <Form.Item>
659
+          {getFieldDecorator('name')(
660
+            <Input
661
+              prefix={<Icon type="text" style={{ color: 'rgba(0,0,0,.25)' }} />}
662
+              placeholder="姓名"
663
+            />,
664
+          )}
665
+        </Form.Item>
666
+        <Form.Item>
667
+          {getFieldDecorator('tel')(
668
+            <Input
669
+              prefix={<Icon type="text" style={{ color: 'rgba(0,0,0,.25)' }} />}
670
+              placeholder="电话"
671
+            />,
672
+          )}
673
+        </Form.Item>
674
+        {customerType === 'private' && <Form.Item>
675
+          {getFieldDecorator('consultName')(
676
+            <Input placeholder="置业顾问" />,
677
+          )}
678
+        </Form.Item>}
679
+        {customerType === 'private' && <Form.Item>
680
+          {getFieldDecorator('consultTel')(
681
+            <Input placeholder="置业顾问电话" />,
682
+          )}
683
+        </Form.Item>}
684
+        <Form.Item >
685
+          {getFieldDecorator('startCreateDate')(
686
+            <DatePicker placeholder="注册开始时间" format="YYYY-MM-DD HH:mm:ss" showTime={{ format: 'HH:mm:ss' }} />)}
687
+        </Form.Item>
688
+        <Form.Item >
689
+          {getFieldDecorator('endCreateDate')(
690
+            <DatePicker placeholder="注册结束时间" format="YYYY-MM-DD HH:mm:ss" showTime={{ format: 'HH:mm:ss' }} />)}
691
+        </Form.Item>
692
+        <Form.Item>
693
+          {getFieldDecorator('sceneType')(
694
+            <WxDictSelect />,
695
+          )}
696
+        </Form.Item>
697
+        <Form.Item>
698
+          {getFieldDecorator('sharePersonName')(
699
+            <Input placeholder="推广人" />,
700
+          )}
701
+        </Form.Item>
702
+        <Form.Item>
703
+          <AuthButton name="admin.customer.recommend.search" noRight={null}>
704
+            <Button type="primary" htmlType="submit" >
705
+              搜索
706
+              </Button>
707
+          </AuthButton>
708
+          <Button style={{ marginLeft: 8 }} onClick={handleReset}>
709
+            重置
710
+            </Button>
711
+        </Form.Item>
712
+      </Form>
713
+      {
714
+        customerType === 'private' ?
715
+          <AuthButton name="admin.customer.import" noRight={null}>
716
+            <Button type="primary" loading={loadingStatus} onClick={() => exportCustomer()} style={{ float: 'right', margin: '20px 0', zIndex: 1 }}>
717
+              导出
718
+          </Button>
719
+          </AuthButton> :
720
+          <>
721
+            <AuthButton name="admin.customer.import" noRight={null}>
722
+              <Button type="primary" onClick={() => batchAssistConsultant()} style={{ float: 'right', margin: '20px 0', marginLeft: '20px', zIndex: 1 }}>
723
+                批量分配置业顾问
724
+          </Button>
725
+            </AuthButton>
726
+            <AuthButton name="admin.customer.import" noRight={null}>
727
+              <Button type="primary" loading={loadingStatus} onClick={() => exportCustomer()} style={{ float: 'right', margin: '20px 0', zIndex: 1 }}>
728
+                导出
729
+          </Button>
730
+            </AuthButton>
731
+          </>
732
+      }
733
+
734
+
735
+      <div style={{ margin: '20px 0' }}>
736
+        <Radio.Group value={customerType} onChange={radioButtonHandleSizeChange} buttonStyle="solid">
737
+          <Radio.Button value="private">私客</Radio.Button>
738
+          <Radio.Button value="public">公客</Radio.Button>
739
+        </Radio.Group>
740
+      </div>
741
+      {customerType === 'private' ?
742
+        <Table
743
+          // onRow={record => {
744
+          //   return {
745
+          //     onClick: () => toCustomerDateil(record),
746
+          //   };
747
+          // }}
748
+          dataSource={dataSource.records} columns={privateColumns} pagination={{ current: dataSource.current, total: dataSource.total, pageSize: dataSource.size, onChange }} rowKey={r => r.customerId} /> :
749
+        <Table rowSelection={rowSelection}
750
+          // onRow={record => {
751
+          //   return {
752
+          //     onClick: () => publicCustomerDetail(record),
753
+          //   };
754
+          // }}
755
+          dataSource={dataPub.records} columns={publicColumns} pagination={{ current: dataPub.current, total: dataPub.total, pageSize: dataPub.size, onChange }} rowKey={r => r.customerId} ></Table>
756
+      }
757
+
758
+      {/* 调整归属 */}
759
+      <Attribution visibleData={gVisibleData} onCancel={() => closeAttribution()} />
760
+
761
+      {/* 积分记录 */}
762
+      <IntegralRecord visibleData={recordVisibleData} onCancel={() => closeAll()} />
763
+
764
+      {/* 变更状态 */}
765
+      <ChangeStatus visibleData={statusVisibleData} onCancel={() => closeAll()} />
766
+
767
+      {/* 推荐客户 */}
768
+      <ModalRecommendRecord visibleData={recommendVisibleData} onCancel={() => closeAll()} />
769
+
770
+      {/* 分配置业顾问 */}
771
+      <AssistConsultant visibleData={assistVisibleData} onCancel={() => closeAll()} />
772
+
773
+      {/* 批量分配置业顾问 */}
774
+      <BatchAssistConsultant visibleData={batchAssistVisibleData} onCancel={() => closeAll()} />
775
+
776
+      <Prompt message={location =>
777
+        location.pathname.startsWith("/customer/customerlist")
778
+          ? true
779
+          : (localStorage.removeItem("priPageParams"), localStorage.removeItem("pubPageParams"), localStorage.removeItem("state"))} />
780
+    </>
781
+  );
782
+}
783
+
784
+const WrappedBody = Form.create({ name: 'body' })(body);
785
+
786
+export default WrappedBody

+ 35
- 0
src/pages/customer/customerlist/index.jsx Ver arquivo

@@ -0,0 +1,35 @@
1
+import React, { useState, useEffect } from 'react';
2
+import { Form, Icon, Input, Button, DatePicker, Select, Card, Row, Col, Pagination, Alert, Table, Avatar, Radio, Modal, Descriptions, message } from 'antd';
3
+import moment from 'moment';
4
+import request from '../../../utils/request';
5
+import apis from '../../../services/apis';
6
+import Styles from './style.less';
7
+import router from 'umi/router';
8
+import withActions from '@/components/ActionList';
9
+import Attribution from './components/attribution'
10
+import AssistConsultant from './components/assistConsultant'
11
+import BatchAssistConsultant from './components/BatchAssistConsultant'
12
+import IntegralRecord from './components/integralRecord'
13
+import ModalRecommendRecord from './components/recommend'
14
+import ChangeStatus from './components/changeStatus'
15
+import BuildSelect from '../../../components/SelectButton/BuildSelect'
16
+import AuthButton from '@/components/AuthButton';
17
+import Navigate from '@/components/Navigate';
18
+import WxDictSelect from '@/components/SelectButton/WxDictSelect';
19
+import Prompt from 'umi/prompt';
20
+import EditIcon from '@/components/EditIcon';
21
+
22
+
23
+const { Option } = Select;
24
+
25
+function customerlist(props) {
26
+
27
+
28
+  return (
29
+    <>1234
30
+    </>
31
+  );
32
+}
33
+
34
+
35
+export default customerlist

+ 183
- 0
src/pages/customer/customerlist/publicCustomerDetail.jsx Ver arquivo

@@ -0,0 +1,183 @@
1
+import React, { useState, useEffect } from 'react';
2
+import { Form, Table, Pagination, Button } from 'antd';
3
+import { FormattedMessage } from 'umi-plugin-react/locale';
4
+import publicStyle from './publicStyle.less';
5
+import apis from '../../../services/apis';
6
+import request from '../../../utils/request';
7
+import moment from 'moment';
8
+import BuildSelect from '../../../components/SelectButton/BuildSelect'
9
+
10
+import router from 'umi/router';
11
+import Prompt from 'umi/prompt';
12
+
13
+function header(props) {
14
+  /**
15
+   * @param {*} props
16
+   * @returns
17
+   */
18
+  // eslint-disable-next-line react-hooks/rules-of-hooks
19
+  const [data, setData] = useState([{}])
20
+  const [tableData, setTableDataData] = useState([{}])
21
+  const [dataConsultant, setDataonsultant] = useState({})
22
+  const [intentionData, setIntentionData] = useState([])
23
+  const [buildingIdValue, setBuildingIdData] = useState()
24
+
25
+  // eslint-disable-next-line react-hooks/rules-of-hooks
26
+  useEffect(() => {
27
+    getById()
28
+    toLoadIntention()
29
+  }, [])
30
+
31
+  function toLoadIntention() {
32
+    if (intentionData) {
33
+      return
34
+    }
35
+    const arr = []
36
+    // 意向值
37
+    for (let i = 0; i < intentionData.length; i++) {
38
+      const a = [].concat(
39
+        intentionData.concat([{
40
+          key: i,
41
+          name: `Edward King ${i}`,
42
+          age: 32,
43
+          address: `London, Park Lane no. ${i}`,
44
+        }]),
45
+      )
46
+      arr.push(a)
47
+    }
48
+    setIntentionData(arr)
49
+  }
50
+
51
+  // 查询
52
+  const { id } = props.location.query;
53
+  function getById(params) {
54
+    if (id === '' || id === undefined) {
55
+      return
56
+    }
57
+
58
+    request({ ...apis.customer.cecommendPublic, urlData: { id }, params: { ...params } }).then(res => {
59
+      setTableDataData(res.visitRecords)
60
+      setData(res)
61
+      setDataonsultant(res.geoInfo)
62
+      setIntentionData(res.intentions)
63
+    })
64
+  }
65
+  // 分页
66
+  // 分页
67
+  function onChange(number) {
68
+    if (id === '' || id === undefined) {
69
+      return
70
+    }
71
+    request({ ...apis.customer.cecommendPublic, urlData: { id }, params: { pageNumber: number, pageSize: 10 } }).then(res => {
72
+      setData(res)
73
+      setTableDataData(res.visitRecords)
74
+      setDataonsultant(res.geoInfo)
75
+    })
76
+  }
77
+
78
+  function changBuilding(buildingId) {
79
+    setBuildingIdData(buildingId)
80
+    getById({ pageNumber: 1, pageSize: 10, buildingId: buildingId })
81
+  }
82
+
83
+  const columns = [
84
+    {
85
+      title: '访问事件',
86
+      dataIndex: 'activity',
87
+      key: 'activity',
88
+      align: 'center',
89
+    },
90
+    {
91
+      title: '访问内容',
92
+      dataIndex: 'activityName',
93
+      key: 'activityName',
94
+      align: 'center',
95
+    },
96
+    {
97
+      title: '访问时间',
98
+      dataIndex: 'visitTime',
99
+      key: 'visitTime',
100
+      align: 'center',
101
+      render: (_, record) => <span> {moment(record.visitTime).format('YYYY-MM-DD HH:mm:ss')}</span>,
102
+    },
103
+    {
104
+      title: '停留时间',
105
+      dataIndex: 'visitDuration',
106
+      key: 'visitDuration',
107
+      align: 'center',
108
+      render: (_, record) => <span>{record.visitDuration === null ? 0 : record.visitDuration}秒</span>,
109
+    },
110
+  ]
111
+  // 意向
112
+  const intention = [
113
+    {
114
+      title: 'Name',
115
+      dataIndex: 'name',
116
+      width: 200,
117
+      render: (_, record) => <span>{record.buildingName}</span>,
118
+    },
119
+    {
120
+      title: 'Address',
121
+      dataIndex: 'address',
122
+      render: (_, record) => <span style={{ marginLeft: '50%' }}>{record.intention}</span>,
123
+    },
124
+  ];
125
+
126
+  return (
127
+    <>
128
+      <div style={{ textAlign: 'right', marginBottom: '16px' }}>
129
+        <Button onClick={() => router.go(-1)}>返回</Button>
130
+      </div>
131
+      <div className={publicStyle.cardBox}>
132
+        {/* { console.log("data:",data),console.log("data:",dataConsultant)} */}
133
+        <div className={publicStyle.rightBox}>
134
+          <p className={publicStyle.tit}>客户信息</p>
135
+          <img className={publicStyle.touxiang} src={data.avatarurl && data.avatarurl} />
136
+          <div className={publicStyle.right}>
137
+            <p className={publicStyle.rightItem}>用户名称:{data.name === null ? data.nickname : data.name}</p>
138
+            <p className={publicStyle.rightItem}>手机号码:{data.phone}</p>
139
+            <p className={publicStyle.rightItem}>首次访问时间:{data.visitTime && moment(data.visitTime).format('YYYY-MM-DD')}</p>
140
+          </div>
141
+          <div className={publicStyle.Centered}>
142
+            <p>访问时长:{data.duration}秒</p>
143
+            <p>访问次数:{data.visitTimes}次</p>
144
+            <p className={publicStyle.rightItem}>来源渠道:{data.personFrom ? data.personFrom : '其他'}</p>
145
+            {/* <p>预约人数:{ data.visiteNum }</p> */}
146
+          </div>
147
+          {/* <p className={styles.rightItem}>预约到访时间:{data.appointmentTime && moment(data.appointmentTime).format('YYYY-MM-DD') }</p> */}
148
+          <div className={publicStyle.rightInfo}>
149
+            <p className={publicStyle.rightItem}>国家:{data && data.country}</p>
150
+            <p className={publicStyle.rightItem}>省份:{data && data.province}</p>
151
+            <p className={publicStyle.rightItem}>城市:{data && data.city}</p>
152
+            {/* <p className={styles.rightItem}>详细信息:</p> */}
153
+            {/* <p className={styles.rightItem}>意向项目:{data.intention }</p> */}
154
+            {/* <p className={styles.rightItem}>客户说明:{ data.verifyRemark }</p> */}
155
+          </div>
156
+        </div>
157
+        {/* { console.log("data:",data),console.log("data:",dataConsultant)} */}
158
+        <div className={publicStyle.leftBoxCentre}>
159
+          <div className={publicStyle.tit}>
160
+            <span>项目名称</span>
161
+            <span style={{ float: 'right' }}>意向值</span>
162
+          </div>
163
+          {/* <img className={styles.touxiang} src={ data.picture && data.picture } /> */}
164
+          <div className={publicStyle.infoItem}>
165
+            <Table columnWidth={10} showHeader={false} columns={intention} dataSource={intentionData} pagination={false} scroll={intentionData.length >= 3 ? { y: 240 } : false} />
166
+          </div>
167
+        </div>
168
+      </div>
169
+      <div className={publicStyle.recordBox}>
170
+        <p className={publicStyle.tableName}>访问记录</p>
171
+        <BuildSelect onChange={changBuilding} value={buildingIdValue} />
172
+        <Table dataSource={tableData.records} columns={columns} style={{ marginTop: '15px' }} pagination={{ pageSize: 10, total: tableData.total, onChange }} />
173
+      </div>
174
+      <Prompt message={location =>
175
+        location.pathname.startsWith("/customer/customerlist")
176
+          ? true
177
+          : (localStorage.removeItem("priPageParams"), localStorage.removeItem("pubPageParams"), localStorage.removeItem("state"))} />
178
+    </>
179
+  )
180
+}
181
+const WrappedHeader = Form.create({ name: 'header' })(header);
182
+
183
+export default WrappedHeader

+ 209
- 0
src/pages/customer/customerlist/publicStyle.less Ver arquivo

@@ -0,0 +1,209 @@
1
+.SubmitButton {
2
+  background: rgba(239,39,58,1);
3
+  border-radius: 7px;
4
+  border: 0px;
5
+}
6
+.text {
7
+  color: rgba(239,39,58,1);
8
+}
9
+.SelectFrom {
10
+  width: 180px;
11
+  background: #ffffff;
12
+  border-radius: 7px;
13
+  border: 1px solid #dbdbdb;
14
+}
15
+.addButton {
16
+  background: #50be00;
17
+  border-radius: 4px;
18
+  border: 0px;
19
+  margin: 10px 0px;
20
+}
21
+.cardText {
22
+  color: #333;
23
+  display: flex;
24
+  align-items: center;
25
+  position: relative;
26
+  line-height: 1.5;
27
+  font-size: 0.106rem;
28
+  margin-bottom: 0.08rem;
29
+
30
+}
31
+.cardItem{
32
+  color: #666;
33
+  display: flex;
34
+  align-items: center; 
35
+  line-height: 1.5;
36
+  font-size: 0.106rem;
37
+  margin-bottom: 0.08rem; 
38
+}
39
+.ediText {
40
+  font-size: 0.106rem;
41
+  color: #ff925c;
42
+  line-height: 24px;
43
+  position: absolute;
44
+  right: 0;
45
+}
46
+.title{
47
+  display: inline-block;
48
+  width:  0.54rem;
49
+  justify-content: space-between;
50
+  text-align: justify;
51
+  text-align-last:justify
52
+}
53
+
54
+.address { 
55
+  width: 400px;
56
+  height: 24px; 
57
+  text-overflow: ellipsis; 
58
+  white-space: nowrap;
59
+  overflow: hidden;
60
+}
61
+
62
+.pitchButton { 
63
+  border-color: rgba(255,126,72,1);
64
+  background-color: rgba(255,126,72,1);
65
+  color: rgba(255,255,255,1); 
66
+}
67
+.noButton {
68
+  border-color: rgba(255,126,72,1);
69
+  color: rgba(255,126,72,1);
70
+}
71
+.displayS {
72
+  display: none;
73
+}
74
+
75
+
76
+// 客户详情样式
77
+.cardBox{
78
+  display: flex;
79
+  .leftBox{
80
+    width:90%;
81
+    min-width:28.5%;
82
+    height:315px;
83
+    background:rgba(255,255,255,1);
84
+    box-shadow:0px 0px 16px 2px rgba(0,0,0,0.12);
85
+    border-radius:8px;
86
+    display: inline-block;
87
+    margin-right: 30px;
88
+    padding: 30px;
89
+    overflow: hidden;
90
+  }
91
+  .rightBox{
92
+    width:865px;
93
+    min-width:342px;
94
+    height:290px;
95
+    background:rgba(255,255,255,1);
96
+    box-shadow:0px 0px 16px 2px rgba(0,0,0,0.12);
97
+    border-radius:8px;
98
+    display: inline-block;
99
+    margin-right: 30px;
100
+    padding: 30px;
101
+    overflow: hidden;
102
+  }
103
+  .rightBox{
104
+    width:-webkit-fill-available;
105
+    height:315px;
106
+    min-width: 70%;
107
+    background:rgba(255,255,255,1);
108
+    box-shadow:0px 0px 16px 2px rgba(0,0,0,0.12);
109
+    border-radius:8px;
110
+    display: inline-block;
111
+    padding: 30px;
112
+    overflow: hidden;
113
+    position: relative;
114
+  }
115
+  .rightBoxCentre{
116
+    width:865px;
117
+    height:315px;
118
+    min-width: 60%;
119
+    background:rgba(255,255,255,1);
120
+    box-shadow:0px 0px 16px 2px rgba(0,0,0,0.12);
121
+    border-radius:8px;
122
+    display: inline-block;
123
+    padding: 30px;
124
+    overflow: hidden;
125
+    position: relative;
126
+  }
127
+  .leftBoxCentre{
128
+    width:100%;
129
+    height:315px;
130
+    background:rgba(255,255,255,1);
131
+    box-shadow:0px 0px 16px 2px rgba(0,0,0,0.12);
132
+    border-radius:8px;
133
+    display: inline-block;
134
+    padding: 30px;
135
+    overflow: hidden;
136
+    position: relative;
137
+  }
138
+  .tit{
139
+    font-size:0.15rem;
140
+    font-weight:600;
141
+    color:#222;
142
+    margin: 10px 0 0 0;
143
+  }
144
+  .touxiang{
145
+    width: 120px;
146
+    width: 120px;
147
+    border-radius: 6px;
148
+    margin: 30px 0 20px 0;
149
+  }
150
+  .infoItem{
151
+    color:#666;
152
+    font-size: 0.1rem;
153
+    margin: 0 0 10px 0;
154
+    
155
+  }
156
+  .rightItem{
157
+    color:#666;
158
+    font-size: 0.1rem;
159
+    margin: 0 0 15px 0;
160
+  }
161
+  .right{
162
+    position: absolute;
163
+    top:108px;
164
+    left:170px;
165
+  }
166
+  .left{
167
+    position: absolute;
168
+    top:108px;
169
+    left:60%;
170
+  }
171
+  .rightInfo{
172
+    position: absolute;
173
+    top:108px;
174
+    left:80%;
175
+  }
176
+
177
+  .Centered{
178
+    position: absolute;
179
+    top:108px;
180
+    left:50%;
181
+    margin: 0 0 15px 0;
182
+    color:#666;
183
+    font-size: 0.1rem;
184
+  }
185
+
186
+  .rightCentered{
187
+    position: absolute;
188
+    top:108px;
189
+    left:55%;
190
+    margin: 0 0 15px 0;
191
+    color:#666;
192
+    font-size: 0.1rem;
193
+  }
194
+  
195
+}
196
+.recordBox{
197
+  width:100%;
198
+  background:rgba(255,255,255,1);
199
+  box-shadow:0px 0px 16px 2px rgba(0,0,0,0.12);
200
+  border-radius:8px;
201
+  margin-top: 30px;
202
+  padding: 30px;
203
+  .tableName{
204
+    font-size:24px;
205
+    font-weight:600;
206
+    color:#222;
207
+  }
208
+}
209
+

+ 69
- 0
src/pages/customer/customerlist/style.css Ver arquivo

@@ -0,0 +1,69 @@
1
+.SubmitButton {
2
+  background: #ef273a;
3
+  border-radius: 7px;
4
+  border: 0px;
5
+}
6
+.text {
7
+  color: #ef273a;
8
+}
9
+.SelectFrom {
10
+  width: 180px;
11
+  background: #ffffff;
12
+  border-radius: 7px;
13
+  border: 1px solid #dbdbdb;
14
+}
15
+.addButton {
16
+  background: #50be00;
17
+  border-radius: 4px;
18
+  border: 0px;
19
+  margin: 10px 0px;
20
+}
21
+.cardText {
22
+  font-size:  0.106rem;
23
+  color: #333;
24
+  line-height: 24px;
25
+  display: flex;
26
+  align-items: center;
27
+  position: relative;
28
+}
29
+.cardItem {
30
+  font-size:  0.106rem;
31
+  font-weight: 400;
32
+  color: #666;
33
+  line-height: 24px;
34
+  display: flex;
35
+  align-items: center;
36
+}
37
+.ediText {
38
+  font-size:  0.106rem;
39
+  color: #ff925c;
40
+  line-height: 24px;
41
+  position: absolute;
42
+  right: 0;
43
+}
44
+.title {
45
+  display: inline-block;
46
+  width:  0.54rem;
47
+  justify-content: space-between;
48
+  text-align: justify;
49
+  text-align-last: justify;
50
+}
51
+.address {
52
+  width: 400px;
53
+  height: 24px;
54
+  text-overflow: ellipsis;
55
+  white-space: nowrap;
56
+  overflow: hidden;
57
+}
58
+.pitchButton {
59
+  border-color: #ff7e48;
60
+  background-color: #ff7e48;
61
+  color: #ffffff;
62
+}
63
+.noButton {
64
+  border-color: #ff7e48;
65
+  color: #ff7e48;
66
+}
67
+.displayS {
68
+  display: none;
69
+}

+ 239
- 0
src/pages/customer/customerlist/style.less Ver arquivo

@@ -0,0 +1,239 @@
1
+.SubmitButton {
2
+  background: rgba(239,39,58,1);
3
+  border-radius: 7px;
4
+  border: 0px;
5
+}
6
+.text {
7
+  color: rgba(239,39,58,1);
8
+}
9
+.SelectFrom {
10
+  width: 180px;
11
+  background: #ffffff;
12
+  border-radius: 7px;
13
+  border: 1px solid #dbdbdb;
14
+}
15
+.addButton {
16
+  background: #50be00;
17
+  border-radius: 4px;
18
+  border: 0px;
19
+  margin: 10px 0px;
20
+}
21
+.cardText {
22
+  color: #333;
23
+  display: flex;
24
+  align-items: center;
25
+  position: relative;
26
+  line-height: 1.5;
27
+  font-size: 0.106rem;
28
+  margin-bottom: 0.08rem;
29
+
30
+}
31
+.cardItem{
32
+  color: #666;
33
+  display: flex;
34
+  align-items: center; 
35
+  line-height: 1.5;
36
+  font-size: 0.106rem;
37
+  margin-bottom: 0.08rem; 
38
+}
39
+.ediText {
40
+  font-size: 0.106rem;
41
+  color: #ff925c;
42
+  line-height: 24px;
43
+  position: absolute;
44
+  right: 0;
45
+}
46
+.title{
47
+  display: inline-block;
48
+  width:  0.54rem;
49
+  justify-content: space-between;
50
+  text-align: justify;
51
+  text-align-last:justify
52
+}
53
+
54
+.address { 
55
+  width: 400px;
56
+  height: 24px; 
57
+  text-overflow: ellipsis; 
58
+  white-space: nowrap;
59
+  overflow: hidden;
60
+}
61
+
62
+.pitchButton { 
63
+  border-color: rgba(255,126,72,1);
64
+  background-color: rgba(255,126,72,1);
65
+  color: rgba(255,255,255,1); 
66
+}
67
+.noButton {
68
+  border-color: rgba(255,126,72,1);
69
+  color: rgba(255,126,72,1);
70
+}
71
+.displayS {
72
+  display: none;
73
+}
74
+
75
+
76
+// 客户详情样式
77
+.cardBox{
78
+  display: flex;
79
+  .leftBox{
80
+    width:1000px;
81
+    min-width:350px;
82
+    height:1000px;
83
+    background:rgba(255,255,255,1);
84
+    box-shadow:0px 0px 16px 2px rgba(0,0,0,0.12);
85
+    border-radius:8px;
86
+    display: inline-block;
87
+    margin-right: 30px;
88
+    padding: 30px;
89
+    overflow: hidden;
90
+  }
91
+  .rightBox{
92
+    width:865px;
93
+    min-width:342px;
94
+    height:290px;
95
+    background:rgba(255,255,255,1);
96
+    box-shadow:0px 0px 16px 2px rgba(0,0,0,0.12);
97
+    border-radius:8px;
98
+    display: inline-block;
99
+    margin-right: 30px;
100
+    padding: 30px;
101
+    overflow: hidden;
102
+  }
103
+  .rightBox{
104
+    width:-webkit-fill-available;
105
+    height:315px;
106
+    min-width: 100%;
107
+    background:rgba(255,255,255,1);
108
+    box-shadow:0px 0px 16px 2px rgba(0,0,0,0.12);
109
+    border-radius:8px;
110
+    display: inline-block;
111
+    padding: 30px;
112
+    overflow: hidden;
113
+    position: relative;
114
+  }
115
+  .rightBoxCentre{
116
+    width:865px;
117
+    height:345px;
118
+    min-width: 60%;
119
+    background:rgba(255,255,255,1);
120
+    box-shadow:0px 0px 16px 2px rgba(0,0,0,0.12);
121
+    border-radius:8px;
122
+    display: inline-block;
123
+    padding: 30px;
124
+    overflow: hidden;
125
+    position: relative;
126
+  }
127
+  .leftBoxCentre{
128
+    width:100%;
129
+    height:345px;
130
+    background:rgba(255,255,255,1);
131
+    box-shadow:0px 0px 16px 2px rgba(0,0,0,0.12);
132
+    border-radius:8px;
133
+    display: inline-block;
134
+    padding: 30px;
135
+    overflow: hidden;
136
+    position: relative;
137
+  }
138
+  .tit{
139
+    font-size:0.15rem;
140
+    font-weight:600;
141
+    color:#222;
142
+    margin: 10px 0 0 0;
143
+  }
144
+  .flexBox{
145
+    display: flex;
146
+    align-items: end;
147
+  }
148
+  .touxiang{
149
+    width: 120px;
150
+    width: 120px;
151
+    border-radius: 6px;
152
+    margin: 30px 0 20px 0;
153
+  }
154
+  .touxiangphoto{
155
+    width: 80px;
156
+    height: 80px;
157
+    margin: 38px 0 0px 0;
158
+  }
159
+  .infoItem{
160
+    color:#666;
161
+    font-size: 0.1rem;
162
+    margin: 0 0 30px 0;
163
+    
164
+  }
165
+  .rightItem{
166
+    color:#666;
167
+    font-size: 0.1rem;
168
+    margin: 0 0 15px 0;
169
+  }
170
+  .right{
171
+    width: 25%;
172
+    min-width: 1.3rem;
173
+    margin-left: 0.1rem;
174
+    padding-top: 0.3rem;
175
+    // position: absolute;
176
+    // top:108px;
177
+    // left:170px;
178
+  }
179
+  .rightphone{
180
+  
181
+    position: absolute;
182
+    top:108px;
183
+    left:170px;
184
+  }
185
+  .left{
186
+    position: absolute;
187
+    top:108px;
188
+    left:60%;
189
+  
190
+  }
191
+  .rightInfo{
192
+    width: 25%;
193
+    min-width: 1.3rem;
194
+    margin-right: 0.1rem;
195
+    padding-top: 0.3rem;
196
+    // position: absolute;
197
+    // top:108px;
198
+    // left:80%;
199
+  }
200
+
201
+  .Centered{
202
+    width: 25%;
203
+    // position: absolute;
204
+    // top:108px;
205
+    // left:30%;
206
+    min-width: 1.3rem;
207
+    margin: 0 0 15px 0;
208
+    color:#666;
209
+    font-size: 0.1rem;
210
+    padding-top: 0.3rem;
211
+  }
212
+
213
+  .rightCentered{
214
+    // position: absolute;
215
+    // top:108px;
216
+    // left:55%;
217
+    width: 25%;
218
+    min-width: 1.3rem;
219
+    margin: 0 0 15px 0;
220
+    padding-top: 0.3rem;
221
+    color:#666;
222
+    font-size: 0.1rem;
223
+  }
224
+  
225
+}
226
+.recordBox{
227
+  width:100%;
228
+  background:rgba(255,255,255,1);
229
+  box-shadow:0px 0px 16px 2px rgba(0,0,0,0.12);
230
+  border-radius:8px;
231
+  margin-top: 30px;
232
+  padding: 30px;
233
+  .tableName{
234
+    font-size:24px;
235
+    font-weight:600;
236
+    color:#222;
237
+  }
238
+}
239
+

+ 69
- 0
src/pages/customer/customerlist/style.wxss Ver arquivo

@@ -0,0 +1,69 @@
1
+.SubmitButton {
2
+  background: #ef273a;
3
+  border-radius: 7px;
4
+  border: 0px;
5
+}
6
+.text {
7
+  color: #ef273a;
8
+}
9
+.SelectFrom {
10
+  width: 180px;
11
+  background: #ffffff;
12
+  border-radius: 7px;
13
+  border: 1px solid #dbdbdb;
14
+}
15
+.addButton {
16
+  background: #50be00;
17
+  border-radius: 4px;
18
+  border: 0px;
19
+  margin: 10px 0px;
20
+}
21
+.cardText {
22
+  font-size:  0.106rem;
23
+  color: #333;
24
+  line-height: 24px;
25
+  display: flex;
26
+  align-items: center;
27
+  position: relative;
28
+}
29
+.cardItem {
30
+  font-size:  0.106rem;
31
+  font-weight: 400;
32
+  color: #666;
33
+  line-height: 24px;
34
+  display: flex;
35
+  align-items: center;
36
+}
37
+.ediText {
38
+  font-size:  0.106rem;
39
+  color: #ff925c;
40
+  line-height: 24px;
41
+  position: absolute;
42
+  right: 0;
43
+}
44
+.title {
45
+  display: inline-block;
46
+  width:  0.54rem;
47
+  justify-content: space-between;
48
+  text-align: justify;
49
+  text-align-last: justify;
50
+}
51
+.address {
52
+  width: 400px;
53
+  height: 24px;
54
+  text-overflow: ellipsis;
55
+  white-space: nowrap;
56
+  overflow: hidden;
57
+}
58
+.pitchButton {
59
+  border-color: #ff7e48;
60
+  background-color: #ff7e48;
61
+  color: #ffffff;
62
+}
63
+.noButton {
64
+  border-color: #ff7e48;
65
+  color: #ff7e48;
66
+}
67
+.displayS {
68
+  display: none;
69
+}

+ 115
- 0
src/pages/customer/drift/index.jsx Ver arquivo

@@ -0,0 +1,115 @@
1
+import React, { useState, useEffect } from 'react';
2
+import { Form, Table, Avatar, Alert, Select, Button } from 'antd';
3
+
4
+import request from '../../../utils/request';
5
+import apis from '../../../services/apis';
6
+import Styles from './style.less'
7
+import WxDictSelect from '@/components/SelectButton/WxDictSelect';
8
+
9
+function costomerDrift(props) {
10
+  const { getFieldDecorator } = props.form
11
+  // eslint-disable-next-line react-hooks/rules-of-hooks
12
+  const [dataSources, setDataSources] = useState({ records: [] })
13
+
14
+  // eslint-disable-next-line react-hooks/rules-of-hooks
15
+  useEffect(() => {
16
+   getList({ pageNumber: '1', pageSize: '10' })
17
+  }, [])
18
+
19
+  function getList(params) {
20
+    request({ ...apis.customer.drift, params: { ...params } }).then(res => {
21
+      setDataSources(res)
22
+    }).catch(err => {
23
+      // eslint-disable-next-line no-unused-expressions
24
+      <Alert
25
+        style={{
26
+          marginBottom: 24,
27
+        }}
28
+        message={err}
29
+        type="error"
30
+        showIcon
31
+      />
32
+    })
33
+  }
34
+
35
+  // 分页
36
+  function onChange(pageNum) {
37
+    // eslint-disable-next-line no-console
38
+    console.log('Page: ', pageNum);
39
+    getList({ pageNumber: pageNum, pageSize: 10 })
40
+  }
41
+
42
+  //重置搜索
43
+  function handleReset() {
44
+    props.form.resetFields();
45
+    getList({ pageNumber: '1', pageSize: '10' })
46
+  }
47
+
48
+  // 提交事件
49
+  function handleSubmit(e) {
50
+    e.preventDefault();
51
+    props.form.validateFields((err, values) => {
52
+      if (!err) {
53
+        console.log('提交数据: ', values)
54
+        const { startDate } = values
55
+        getList({ pageNum: 1, pageSize: 10, ...values })
56
+      }
57
+    });
58
+  }
59
+
60
+  const columns = [
61
+    {
62
+      title: '头像',
63
+      dataIndex: 'avatarurl',
64
+      key: 'avatarurl',
65
+      render: (_, record) => <Avatar shape="square" src={record.avatarurl} size={64} icon="user" />,
66
+    },
67
+    {
68
+      title: '姓名',
69
+      dataIndex: 'nickname',
70
+      key: 'nickname',
71
+    },
72
+    {
73
+      title: '性别',
74
+      dataIndex: 'gender',
75
+      key: 'gender',
76
+      // eslint-disable-next-line no-nested-ternary
77
+      render: (_, record) => <><span>{ record.gender === '1' ? '男' : record.gender === '2' ? '女' : '未知' }</span></>,
78
+    },
79
+    {
80
+      title: '用户来源',
81
+      dataIndex: 'sceneAlias',
82
+      key: 'sceneAlias',
83
+      render: (_, record) => <><span>{ record.sceneAlias ? record.sceneAlias : '其他'}</span></>,
84
+    },
85
+  ];
86
+  
87
+  return (
88
+    <>
89
+      <Form layout="inline" onSubmit={e => handleSubmit(e, props)}>
90
+        <Form.Item>
91
+          {getFieldDecorator('sceneType')(
92
+            <WxDictSelect />,
93
+          )}
94
+        </Form.Item>
95
+
96
+        <Form.Item>
97
+            {/* <AuthButton name="admin.customer.recommend.search" noRight={null}> */}
98
+            <Button type="primary" htmlType="submit" >
99
+              搜索
100
+            </Button>
101
+            {/* </AuthButton> */}
102
+            <Button style={{ marginLeft: 8 }} onClick={handleReset}>
103
+              重置
104
+            </Button>
105
+        </Form.Item>
106
+      </Form>
107
+      <Table style={{marginTop:'10px'}} dataSource={dataSources.records} rowKey="drift" columns={columns} pagination={{ total: dataSources.total, onChange }} />
108
+    </>
109
+    // eslint-disable-next-line max-len
110
+    
111
+  )
112
+}
113
+
114
+const WrappedBody = Form.create()(costomerDrift);
115
+export default WrappedBody

+ 58
- 0
src/pages/customer/drift/style.less Ver arquivo

@@ -0,0 +1,58 @@
1
+.SubmitButton {
2
+  background: rgba(239,39,58,1);
3
+  border-radius: 7px;
4
+  border: 0px;
5
+}
6
+.SelectFrom {
7
+  width: 180px;
8
+  background: #ffffff;
9
+  border-radius: 7px;
10
+  border: 1px solid #dbdbdb;
11
+}
12
+.addButton {
13
+  background: #50be00;
14
+  border-radius: 4px;
15
+  border: 0px;
16
+  margin: 10px 0px;
17
+}
18
+.cardText {
19
+  color: #333;
20
+  display: flex;
21
+  align-items: center;
22
+  position: relative;
23
+  line-height: 1.5;
24
+  font-size: 0.106rem;
25
+  margin-bottom: 0.08rem;
26
+
27
+}
28
+.cardItem{
29
+  color: #666;
30
+  display: flex;
31
+  align-items: center; 
32
+  line-height: 1.5;
33
+  font-size: 0.106rem;
34
+  margin-bottom: 0.08rem; 
35
+}
36
+.ediText {
37
+  font-size: 0.106rem;
38
+  color: #ff925c;
39
+  line-height: 24px;
40
+  position: absolute;
41
+  right: 0;
42
+}
43
+.title{
44
+  display: inline-block;
45
+  width:  0.54rem;
46
+  justify-content: space-between;
47
+  text-align: justify;
48
+  text-align-last:justify
49
+}
50
+
51
+.address { 
52
+  width: 400px;
53
+  height: 24px; 
54
+  text-overflow: ellipsis; 
55
+  white-space: nowrap;
56
+  overflow: hidden;
57
+}
58
+

+ 173
- 0
src/pages/customer/visiting/index.jsx Ver arquivo

@@ -0,0 +1,173 @@
1
+import React, { useState, useEffect } from 'react';
2
+import { Form, Icon, Input, Button, DatePicker, Select, Card, Row, Col, Pagination, Alert, Table, Avatar } from 'antd';
3
+import moment from 'moment';
4
+import request from '../../../utils/request';
5
+import apis from '../../../services/apis';
6
+import Styles from './style.less';
7
+import BuildSelect from '../../../components/SelectButton/BuildSelect'
8
+import AuthButton from '@/components/AuthButton';
9
+
10
+const columns = [
11
+  {
12
+    title: '头像',
13
+    dataIndex: 'picture',
14
+    key: 'picture',
15
+    align: 'center',
16
+    width: '15%',
17
+    render: (_, record) => <Avatar shape="square"  size={64} icon="user" />,
18
+  },
19
+  {
20
+    title: '姓名',
21
+    dataIndex: 'name',
22
+    key: 'name',
23
+    align: 'center',
24
+    width: '10%',
25
+    // eslint-disable-next-line no-nested-ternary
26
+    // render: (_, record) => <><span>{customerType === 'private' ? record.name : record.nickname}</span></>,
27
+  },
28
+  {
29
+    title: '电话',
30
+    dataIndex: 'phone',
31
+    key: 'phone',
32
+    align: 'center',
33
+    width: '15%',
34
+  },
35
+  {
36
+    title: '性别',
37
+    dataIndex: 'sex',
38
+    key: 'sex',
39
+    align: 'center',
40
+    width: '15%',
41
+  },
42
+  {
43
+    title: '置业顾问',
44
+    dataIndex: 'asdf',
45
+    key: 'asdf',
46
+    align: 'center',
47
+    width: '15%',
48
+  },
49
+  {
50
+    title: '置业顾问电话',
51
+    dataIndex: 'tel',
52
+    key: 'tel',
53
+    align: 'center',
54
+    width: '15%',
55
+  },
56
+  {
57
+    title: '到访时间',
58
+    dataIndex: 'time',
59
+    key: 'time',
60
+    align: 'center',
61
+    width: '15%',
62
+  },
63
+]
64
+
65
+const data = [
66
+
67
+]
68
+
69
+function onChangetime(dates, dateStrings) {
70
+ 
71
+  // setEndDate(dateStrings[1])
72
+  // setStartDate(dateStrings[0])
73
+  
74
+}
75
+
76
+function exportReport() {
77
+  // request({ ...apis.customer.customerRecommendReportExport, responseType: 'blob' })
78
+  //   .then(response => {
79
+  //     download(response)
80
+  //   })
81
+}
82
+
83
+
84
+
85
+
86
+
87
+
88
+
89
+function body (props) {
90
+  const { getFieldDecorator, getFieldsValue } = props.form
91
+  const { RangePicker } = DatePicker;
92
+
93
+  function handleReset() {
94
+    props.form.resetFields();
95
+    // getList({ pageNumber: 1, pageSize: 10, customerType })
96
+  }
97
+
98
+  function handleSubmit(e) {
99
+ 
100
+  }
101
+
102
+  // 分页
103
+  function onChange(pageNum) {
104
+    props.form.validateFields((err, values) => {
105
+      if (!err) {
106
+        getList({ pageNumber: pageNum, pageSize: 5, ...values })
107
+      }
108
+    });
109
+    // getList({ pageNumber: pageNum, pageSize: 9 })
110
+  }
111
+
112
+  return (
113
+    <>
114
+    <Form layout="inline" onSubmit={e => handleSubmit(e, props)}>
115
+       <Form.Item>
116
+          {getFieldDecorator('buildingId')(
117
+            <BuildSelect />,
118
+          )}
119
+        </Form.Item>
120
+       
121
+        <Form.Item>
122
+          {getFieldDecorator('name')(
123
+            <Input
124
+              prefix={<Icon type="text" style={{ color: 'rgba(0,0,0,.25)' }} />}
125
+              placeholder="姓名"
126
+            />,
127
+          )}
128
+        </Form.Item>
129
+        <Form.Item>
130
+          {getFieldDecorator('tel')(
131
+            <Input
132
+              prefix={<Icon type="text" style={{ color: 'rgba(0,0,0,.25)' }} />}
133
+              placeholder="电话"
134
+            />,
135
+          )}
136
+        </Form.Item>
137
+        
138
+         <Form.Item label="到访时间">
139
+          { getFieldDecorator('time')(
140
+            <RangePicker
141
+            style={{ width: '400px' }}
142
+            // placeholder="到访时间"
143
+            // ranges={{
144
+            //   Today: [moment(), moment()],
145
+            //   'This Month': [moment().startOf('month'), moment().endOf('month')],
146
+            // }}
147
+            // defaultValue={[moment(new Date(new Date().setDate((new Date().getDate() - 6))), 'YYYY-MM-DD HH:MM:SS'), moment(new Date(), 'YYYY-MM-DD HH:MM:SS')]}
148
+            showTime
149
+            onChange={onChangetime}
150
+          />
151
+          )}
152
+        </Form.Item>
153
+        <Form.Item>
154
+            <Button type="primary" htmlType="submit" >
155
+              搜索
156
+            </Button>
157
+            <Button style={{ marginLeft: 8 }} onClick={handleReset}>
158
+              重置
159
+            </Button>
160
+        </Form.Item>
161
+      </Form>
162
+      <Button type="primary" onClick={() => exportReport()} style={{ float: 'right', margin: '20px 0', zIndex: 1 }}>
163
+        导出
164
+      </Button>
165
+
166
+      <Table dataSource={data} columns={columns} pagination={{ total: data.total, onChange }} />
167
+      {/* pagination={{ total: dataSource.total, onChange }} */}
168
+    </>
169
+  );
170
+}
171
+const WrappedBody = Form.create({ name: 'body' })(body);
172
+
173
+export default WrappedBody

+ 151
- 0
src/pages/customer/visiting/style.less Ver arquivo

@@ -0,0 +1,151 @@
1
+.SubmitButton {
2
+  background: rgba(239,39,58,1);
3
+  border-radius: 7px;
4
+  border: 0px;
5
+}
6
+.text {
7
+  color: rgba(239,39,58,1);
8
+}
9
+.SelectFrom {
10
+  width: 180px;
11
+  background: #ffffff;
12
+  border-radius: 7px;
13
+  border: 1px solid #dbdbdb;
14
+}
15
+.addButton {
16
+  background: #50be00;
17
+  border-radius: 4px;
18
+  border: 0px;
19
+  margin: 10px 0px;
20
+}
21
+.cardText {
22
+  color: #333;
23
+  display: flex;
24
+  align-items: center;
25
+  position: relative;
26
+  line-height: 1.5;
27
+  font-size: 0.106rem;
28
+  margin-bottom: 0.08rem;
29
+
30
+}
31
+.cardItem{
32
+  color: #666;
33
+  display: flex;
34
+  align-items: center; 
35
+  line-height: 1.5;
36
+  font-size: 0.106rem;
37
+  margin-bottom: 0.08rem; 
38
+}
39
+.ediText {
40
+  font-size: 0.106rem;
41
+  color: #ff925c;
42
+  line-height: 24px;
43
+  position: absolute;
44
+  right: 0;
45
+}
46
+.title{
47
+  display: inline-block;
48
+  width:  0.54rem;
49
+  justify-content: space-between;
50
+  text-align: justify;
51
+  text-align-last:justify
52
+}
53
+
54
+.address { 
55
+  width: 400px;
56
+  height: 24px; 
57
+  text-overflow: ellipsis; 
58
+  white-space: nowrap;
59
+  overflow: hidden;
60
+}
61
+
62
+.pitchButton { 
63
+  border-color: rgba(255,126,72,1);
64
+  background-color: rgba(255,126,72,1);
65
+  color: rgba(255,255,255,1); 
66
+}
67
+.noButton {
68
+  border-color: rgba(255,126,72,1);
69
+  color: rgba(255,126,72,1);
70
+}
71
+.displayS {
72
+  display: none;
73
+}
74
+
75
+
76
+// 客户详情样式
77
+.cardBox{
78
+  display: flex;
79
+  .leftBox{
80
+    width:342px;
81
+    min-width:342px;
82
+    height:511px;
83
+    background:rgba(255,255,255,1);
84
+    box-shadow:0px 0px 16px 2px rgba(0,0,0,0.12);
85
+    border-radius:8px;
86
+    display: inline-block;
87
+    margin-right: 30px;
88
+    padding: 30px;
89
+    overflow: hidden;
90
+  }
91
+  .rightBox{
92
+    width:-webkit-fill-available;
93
+    height:511px;
94
+    min-width: 800px;
95
+    background:rgba(255,255,255,1);
96
+    box-shadow:0px 0px 16px 2px rgba(0,0,0,0.12);
97
+    border-radius:8px;
98
+    display: inline-block;
99
+    padding: 30px;
100
+    overflow: hidden;
101
+    position: relative;
102
+  }
103
+  .tit{
104
+    font-size:24px;
105
+    font-weight:600;
106
+    color:#222;
107
+    margin: 10px 0 0 0;
108
+  }
109
+  .touxiang{
110
+    width: 120px;
111
+    width: 120px;
112
+    border-radius: 6px;
113
+    margin: 30px 0 20px 0;
114
+  }
115
+  .infoItem{
116
+    color:#666;
117
+    font-size: 19px;
118
+    margin: 0 0 10px 0;
119
+    
120
+  }
121
+  .rightItem{
122
+    color:#666;
123
+    font-size: 19px;
124
+    margin: 0 0 15px 0;
125
+  }
126
+  .right{
127
+    position: absolute;
128
+    top:108px;
129
+    left:170px;
130
+  }
131
+  .rightInfo{
132
+    position: absolute;
133
+    top:50px;
134
+    left:50%;
135
+  }
136
+  
137
+}
138
+.recordBox{
139
+  width:100%;
140
+  background:rgba(255,255,255,1);
141
+  box-shadow:0px 0px 16px 2px rgba(0,0,0,0.12);
142
+  border-radius:8px;
143
+  margin-top: 30px;
144
+  padding: 30px;
145
+  .tableName{
146
+    font-size:24px;
147
+    font-weight:600;
148
+    color:#222;
149
+  }
150
+}
151
+

+ 31
- 0
src/utils/mixStr copy.js Ver arquivo

@@ -0,0 +1,31 @@
1
+
2
+const fill2Len = (str, len) => {
3
+  if (!str) {
4
+    return '*'.repeat(len)
5
+  }
6
+
7
+  const orginLen = str.length
8
+  if (len <= orginLen) {
9
+    return str.substr(0, len)
10
+  }
11
+
12
+  return str.repeat(Math.floor(len / orginLen)) + str.substr(0, len % orginLen)
13
+}
14
+
15
+const mixChars = window.navigator.userAgent // + (new Date).toUTCString().replace(/\d{2}:\d{2}:\d{2}/, '')
16
+
17
+const strXOR = (str, mix) => {
18
+  if (!str) return str
19
+
20
+  const strLen = str.length
21
+  const mixStr = fill2Len(mix, strLen)
22
+
23
+  const result = []
24
+  for (let i = 0; i < strLen; i ++) {
25
+    result.push(String.fromCharCode(str.charCodeAt(i) ^ mixStr.charCodeAt(i)))
26
+  }
27
+
28
+  return result.join('')
29
+}
30
+
31
+export default str => strXOR(str, mixChars)

+ 39
- 0
src/utils/upload.js Ver arquivo

@@ -0,0 +1,39 @@
1
+import { fetch, apis } from './request';
2
+// import mixStr from './mixStr';
3
+
4
+// const getToken = () => mixStr(window.localStorage.getItem('test-foobar'))
5
+
6
+const uploadImage = fetch(apis.image.upload)
7
+
8
+const uploaderProps = {
9
+  name: 'file',
10
+  // action: apis.image.uploadForAnt.url,
11
+  accept: '.png, .jpg, .jpeg, .gif',
12
+  // headers: {
13
+  //   Authorization: `Bearer ${getToken()}`
14
+  // },
15
+  customRequest({
16
+    // action,
17
+    file,
18
+    // headers,
19
+    onError,
20
+    // onProgress,
21
+    onSuccess,
22
+    // withCredentials,
23
+  }) {
24
+    const data = new FormData()
25
+    data.append('file', file)
26
+
27
+    uploadImage({ data }).then((img) => {
28
+      onSuccess(img, file);
29
+    }).catch(onError);
30
+
31
+    return {
32
+      abort() {
33
+        console.log('upload progress is aborted.');
34
+      },
35
+    };
36
+  },
37
+}
38
+
39
+export { uploaderProps }