Quellcode durchsuchen

Merge branch 'dev' of http://git.ycjcjy.com/zhiyuxing/estateagents-admin-manager into dev

张延森 vor 5 Jahren
Ursprung
Commit
0ee82a22f7
36 geänderte Dateien mit 1545 neuen und 140 gelöschten Zeilen
  1. 1
    1
      config/routes.js
  2. 1
    1
      package.json
  3. 1
    1
      src/components/GlobalHeader/AvatarDropdown.jsx
  4. 45
    0
      src/components/SelectButton/WxDictSelect.jsx
  5. 1
    1
      src/pages/activity/SignList.jsx
  6. 23
    0
      src/pages/activity/drainage/DrainageList.jsx
  7. 32
    8
      src/pages/building/list/add/components/base.jsx
  8. 1
    1
      src/pages/building/list/add/components/buildingImage.jsx
  9. 26
    8
      src/pages/building/list/add/components/buildingProjectType.jsx
  10. 29
    0
      src/pages/building/list/add/components/imageSet.jsx
  11. 37
    18
      src/pages/building/list/add/components/modalImage.jsx
  12. 250
    0
      src/pages/building/list/add/components/modalPanoramaImage.jsx
  13. 182
    0
      src/pages/building/list/add/components/panoramaImage.jsx
  14. 3
    0
      src/pages/building/list/add/index.jsx
  15. 4
    3
      src/pages/building/list/index.jsx
  16. 1
    1
      src/pages/carouselFigure/editCarousel.jsx
  17. 158
    0
      src/pages/customer/customerlist/components/assistConsultant.jsx
  18. 10
    1
      src/pages/customer/customerlist/customerDetail.jsx
  19. 26
    0
      src/pages/customer/customerlist/index.jsx
  20. 10
    1
      src/pages/customer/customerlist/publicCustomerDetail.jsx
  21. 53
    5
      src/pages/customer/drift/index.jsx
  22. 11
    4
      src/pages/indexEcharts/components/IntentionalCustomers.jsx
  23. 289
    0
      src/pages/indexEcharts/components/UserBehaviorIndex.jsx
  24. 1
    7
      src/pages/indexEcharts/components/UserConversion.jsx
  25. 3
    0
      src/pages/indexEcharts/components/UserSex.jsx
  26. 20
    12
      src/pages/indexEcharts/components/UserSource.jsx
  27. 139
    0
      src/pages/indexEcharts/components/UserSourceDetail.jsx
  28. 9
    14
      src/pages/indexEcharts/components/UserSourcepie.jsx
  29. 2
    2
      src/pages/indexEcharts/index.jsx
  30. 55
    38
      src/pages/indexEcharts/userSource.jsx
  31. 58
    6
      src/pages/news/list/editNewsList.jsx
  32. 1
    1
      src/pages/record/drainage/DrainageVisitRecordList.jsx
  33. 8
    0
      src/pages/staff/list/editStaff.jsx
  34. 1
    1
      src/pages/style/GoodsList.less
  35. 49
    0
      src/services/apis.js
  36. 5
    5
      src/utils/request.js

+ 1
- 1
config/routes.js Datei anzeigen

@@ -296,7 +296,7 @@ export default [
296 296
               },
297 297
               {
298 298
                 path: '/activity/helpActivity/signList',
299
-                name: '报名列表',
299
+                name: '助力记录',
300 300
                 hideInMenu: true,
301 301
                 component: './activity/helpActivity/signList',
302 302
               },

+ 1
- 1
package.json Datei anzeigen

@@ -64,7 +64,7 @@
64 64
     "react-dom": "^16.8.6",
65 65
     "react-zmage": "^0.8.5",
66 66
     "redux": "^4.0.1",
67
-    "umi": "^2.8.7",
67
+    "umi": "^2.13.3",
68 68
     "umi-plugin-pro-block": "^1.3.2",
69 69
     "umi-plugin-react": "^1.9.5",
70 70
     "umi-request": "^1.0.8",

+ 1
- 1
src/components/GlobalHeader/AvatarDropdown.jsx Datei anzeigen

@@ -79,7 +79,7 @@ class AvatarDropdown extends React.Component {
79 79
       <>
80 80
         <HeaderDropdown overlay={menuHeaderDropdown}>
81 81
             <span className={`${styles.action} ${styles.account}`}>
82
-              <Avatar size="small" className={styles.avatar} src={currentUser.avatar} alt="avatar" />
82
+              <Avatar size="small" className={styles.avatar} src={currentUser.avatar === null ? currentUser.photo:currentUser.avatar} alt="avatar" />
83 83
               <span className={styles.name}>{currentUser.userName}</span>
84 84
             </span>
85 85
         </HeaderDropdown>

+ 45
- 0
src/components/SelectButton/WxDictSelect.jsx Datei anzeigen

@@ -0,0 +1,45 @@
1
+import React, { useState, useEffect, useRef } from 'react';
2
+import { Select } from 'antd';
3
+import apis from '../../services/apis';
4
+import request from '../../utils/request'
5
+
6
+const { Option } = Select;
7
+
8
+/**
9
+ *
10
+ *
11
+ * @param {*} props
12
+ * @returns
13
+ */
14
+const WxDictSelect = props => {
15
+  const [data, setData] = useState([])
16
+  const [value, setValue] = useState([])
17
+  useEffect(() => {
18
+    getWxDictList();
19
+  }, [props.value])
20
+
21
+
22
+  const getWxDictList = e => {
23
+    request({ ...apis.wxDict.list, params: { pageNumber: 1, pageSize: 999 } }).then(data => {
24
+        setData(data.records)
25
+        // 默认选中第一个
26
+    })
27
+  }
28
+
29
+  return (
30
+      <Select
31
+      showSearch
32
+      value={props.value}
33
+      style={{ width: '250px' }}
34
+      placeholder="请选择用户来源"
35
+      onChange={props.onChange}
36
+      filterOption={(input, option) =>
37
+        option.props.children && option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0
38
+      }>
39
+          {data.map(wxDict => (
40
+            <Option key={wxDict.sceneType} value={wxDict.sceneType}>{wxDict.sceneAlias}</Option>
41
+          ))}
42
+      </Select>
43
+  )
44
+}
45
+export default WxDictSelect

+ 1
- 1
src/pages/activity/SignList.jsx Datei anzeigen

@@ -121,7 +121,7 @@ const handleSubmit = (e, props) => {
121 121
         const link = document.createElement('a')
122 122
         link.style.display = 'none'
123 123
         link.href = url
124
-        link.setAttribute('download', '助力者记录.xlsx')
124
+        link.setAttribute('download', '报名列表.xlsx')
125 125
         document.body.append(link)
126 126
         link.click()
127 127
       }).catch(() => {

+ 23
- 0
src/pages/activity/drainage/DrainageList.jsx Datei anzeigen

@@ -73,6 +73,23 @@ const header = (props) => {
73 73
     });
74 74
   }
75 75
 
76
+  const newQrcode = row => {
77
+    const x = new XMLHttpRequest();
78
+    const resourceUrl = row.qrCode
79
+    console.log(resourceUrl);
80
+    x.open('GET', resourceUrl, true);
81
+    x.responseType = 'blob';
82
+    x.onload = function (e) {
83
+      const url = window.URL.createObjectURL(x.response)
84
+      const a = document.createElement('a');
85
+      a.href = url;
86
+      a.style.display = 'none'
87
+      a.download = 'H5二维码.png';
88
+      a.click();
89
+    }
90
+    x.send();
91
+  }
92
+
76 93
   const exchangeDrainage = drainage => () => {
77 94
     if(drainage.status === 1){
78 95
       if(drainage.isMiniapp){
@@ -206,6 +223,12 @@ const header = (props) => {
206 223
               数据
207 224
             </span>
208 225
           </AuthButton>
226
+          <AuthButton name="admin.taDrainage.id.delete" noRight={null}>
227
+            <span>{datas.isMiniapp === false ? "" : <span style={{ right: '20px', bottom: ' 10px', fontSize: ' 0.106rem', color: '#FF7E48', cursor: 'pointer', marginLeft: 20 }} onClick={newQrcode.bind(this, datas)}>
228
+              下载二维码
229
+            </span>}</span>
230
+            
231
+          </AuthButton >
209 232
         </>
210 233
       ),
211 234
     },

+ 32
- 8
src/pages/building/list/add/components/base.jsx Datei anzeigen

@@ -45,6 +45,7 @@ function AddBuilding(props) {
45 45
 
46 46
   const [poi, setPoi] = useState([])
47 47
   const [videoImage, setVideoImage] = useState(false)
48
+  const [priceTypeList, setPriceTypeList] = useState([])
48 49
 
49 50
   // 存放所以 buildingData 基础信息
50 51
   const [buildingData, setBuildingData] = useState({})
@@ -83,9 +84,11 @@ function AddBuilding(props) {
83 84
    // 获取详情信息
84 85
    function getById(currentId) {
85 86
     request({ ...apis.building.buildingGetById, urlData: { id: currentId } }).then(res => {
86
-      if (res.openingDate !== null) {
87
-        res.openingDate = moment(res.openingDate)
88
-      }
87
+      // if (res.openingDate !== null) {
88
+      //   res.openingDate = moment(res.openingDate)
89
+      // }
90
+      //构造下拉框
91
+      setPriceTypeList(res.buildingProjectType)
89 92
       if (res.receivedDate !== null) {
90 93
         res.receivedDate = moment(res.receivedDate)
91 94
       }
@@ -161,7 +164,7 @@ function AddBuilding(props) {
161 164
       data.mapJson = poi
162 165
     }
163 166
 
164
-    data.openingDate = moment(data.openingDate, 'yyyy-MM-dd HH:mm:ss')
167
+    // data.openingDate = moment(data.openingDate, 'yyyy-MM-dd HH:mm:ss')
165 168
     data.receivedDate = moment(data.receivedDate, 'yyyy-MM-dd HH:mm:ss')
166 169
     // 项目主图
167 170
     data.img = data.avatarImage && data.avatarImage.map((item, index) => ({ imgType: 'banner', url: item, orderNo: index + 1 }))
@@ -304,6 +307,12 @@ function AddBuilding(props) {
304 307
     return newPoi
305 308
   }
306 309
 
310
+  //组装项目类型
311
+  function getBuildingProjectTypeList(e) {
312
+    console.log(e,"项目类型------")
313
+    setPriceTypeList(e)
314
+  }
315
+
307 316
   function setFormMapScopeTagValue(keyType, item) {
308 317
     const tag = item.data.map(t => ({ tagName: t.name, delete: true, automatic: true }))
309 318
     switch (keyType) {
@@ -357,13 +366,19 @@ function AddBuilding(props) {
357 366
           <Form.Item label="项目类型">
358 367
             {getFieldDecorator('buildingProjectType', {
359 368
               rules: [{ required: true, message: '请选择项目类型' }],
360
-            })(<BudildingProjectType />)}
369
+            })(<BudildingProjectType onChange={e => getBuildingProjectTypeList(e)}/>)}
361 370
           </Form.Item>
362
-          <Form.Item label="均价" >
363
-            {getFieldDecorator('price')(<Input type="number" style={{ width: '210px' }}/>)}元/m²
371
+          <Form.Item label="列表展示均价" >
372
+            {/* {getFieldDecorator('price')(<Input type="number" style={{ width: '210px' }}/>)}元/m² */}
373
+            {getFieldDecorator('buildingTypeId')(
374
+              <Select placeholder="请选择项目类型的价格">
375
+                    {
376
+                      priceTypeList.map((item, _) => <Option value={item.buildingTypeId}>{item.buildingTypeName}</Option>)
377
+                    }
378
+              </Select>)}
364 379
           </Form.Item>
365 380
           <Form.Item label="开盘时间" >
366
-            {getFieldDecorator('openingDate')(<DatePicker format="YYYY/MM/DD" />)}
381
+            {getFieldDecorator('openingDate')(<Input placeholder="预计xxxx年xx月开盘" maxLength = "15"/>)}
367 382
           </Form.Item>
368 383
           <Form.Item label="电话" >
369 384
             {getFieldDecorator('tel', {
@@ -549,6 +564,15 @@ function AddBuilding(props) {
549 564
           <Form.Item label="装修标准" >
550 565
             {getFieldDecorator('decoration')(<Input />)}
551 566
           </Form.Item>
567
+          <Form.Item label="开发商" >
568
+            {getFieldDecorator('propertyDeveloper')(<Input placeholder="不超过15个字" maxLength = "15"/>)}
569
+          </Form.Item>
570
+          <Form.Item label="备案名" >
571
+            {getFieldDecorator('recordName')(<Input placeholder="不超过15个字" maxLength = "15"/>)}
572
+          </Form.Item>
573
+          <Form.Item label="楼栋总数" >
574
+            {getFieldDecorator('buildingNum')(<Input min={0} type="number" style={{ width: 80}}/>)}
575
+          </Form.Item>
552 576
           {/* <Form.Item label="交房时间" >
553 577
             {getFieldDecorator('receivedDate')(<DatePicker />)}
554 578
           </Form.Item> */}

+ 1
- 1
src/pages/building/list/add/components/buildingImage.jsx Datei anzeigen

@@ -123,7 +123,7 @@ function ModalEdi(props) {
123 123
             })(<Input />)}
124 124
           </Form.Item>
125 125
           {
126
-            !props.noImage && <Form.Item label="选择图片">
126
+            !props.noImage && <Form.Item label="选择图片" help="建议图片尺寸256px*252px,比例4:3,格式:jpg">
127 127
             {getFieldDecorator('img', {
128 128
               rules: [
129 129
                 {

+ 26
- 8
src/pages/building/list/add/components/buildingProjectType.jsx Datei anzeigen

@@ -1,5 +1,5 @@
1 1
 import React, { useEffect, useState } from 'react'
2
-import { Button, Radio, Icon, Form, Input, Row, Col, Modal, notification, Checkbox, InputNumber } from 'antd';
2
+import { Button, Radio, Icon, Form, Input, Row, Col, Modal, notification, Checkbox, Select, InputNumber } from 'antd';
3 3
 import { render } from 'react-dom';
4 4
 import request from '../../../../../utils/request';
5 5
 import apis from '../../../../../services/apis';
@@ -28,11 +28,11 @@ class TypeForm extends React.Component {
28 28
   }
29 29
 
30 30
   onChange(e, name) {
31
-    // console.log(e.target.value)
31
+    // console.log(e)
32 32
     this.props.form.validateFieldsAndScroll((err, values) => {
33 33
       if (!err) {
34 34
         // console.log('Received values of form: ', values);
35
-        const currentValue = e.target.value
35
+        const currentValue = name === 'marketStatus' ? e : e.target.value
36 36
         if (currentValue === '') {
37 37
           values[name] = null
38 38
         } else {
@@ -54,7 +54,7 @@ class TypeForm extends React.Component {
54 54
     const { getFieldDecorator } = this.props.form;
55 55
 
56 56
     // this.props.form.setFieldsValue(this.props.type)
57
-    console.log(this.props.type)
57
+    // console.log(this.props.type,"33333333")
58 58
     return (
59 59
       <>
60 60
         <Col span={11} style={{
@@ -66,16 +66,34 @@ class TypeForm extends React.Component {
66 66
 
67 67
           <p style={{ padding: '20px', borderBottom: '1px solid #eee' }}>{this.props.type.buildingTypeName || ''}  <Button type="link" style={{ position: 'absolute', right: '16px' }} icon="close" onClick={() => this.close()} /></p>
68 68
 
69
-          <Form {...formItemLayout} style={{ padding: '10px 0px 10px 10px' }}>
69
+          <Form {...formItemLayout} style={{ padding: '5px 0px 5px 5px' }}>
70 70
             <Form.Item label="类型编号" style={{ display: 'none' }}>
71 71
               {getFieldDecorator('buildingTypeId')(<Input disabled />)}
72 72
             </Form.Item>
73 73
             <Form.Item label="状态" style={{ display: 'none' }}>
74 74
               {getFieldDecorator('status')(<Input disabled />)}
75 75
             </Form.Item>
76
-            <Form.Item label="价格">
77
-              {getFieldDecorator('price')(<Input type="number" placeholder="元/㎡" onChange={e => this.onChange(e, 'price')} />)}
76
+            <Form.Item label="价格类型">
77
+                {getFieldDecorator('priceType')(
78
+                  <Radio.Group onChange={e => this.onChange(e, 'priceType')} >
79
+                    <Radio value="average">均价</Radio>
80
+                    <Radio value="total">总价</Radio>
81
+                  </Radio.Group>
82
+                )}
83
+              </Form.Item>
84
+            <Form.Item label="价格区间" help='最高价与最低价一致时,只展示一个'>
85
+              {getFieldDecorator('startPrice')(<Input placeholder="最低价" style={{width: '60px'}} maxLength="9" onChange={e => this.onChange(e, 'startPrice')} />)} --
86
+              {getFieldDecorator('endPrice')(<Input placeholder="最高价" style={{width: '60px'}} maxLength="9" onChange={e => this.onChange(e, 'endPrice')} />)}{this.props.type.priceType === "average" ? "元/㎡" : "万元/套"}
78 87
             </Form.Item>
88
+            <Form.Item label="销售状态" >
89
+            {getFieldDecorator('marketStatus')(
90
+              <Select placeholder="销售状态" onChange={e => this.onChange(e, 'marketStatus')} >
91
+                <Option value="待售">待售</Option>
92
+                <Option value="在售">在售</Option>
93
+                <Option value="售罄">售罄</Option>
94
+              </Select>,
95
+            )}
96
+          </Form.Item>
79 97
             <Form.Item label="装修标准">
80 98
               {getFieldDecorator('decoration')(<Input onChange={e => this.onChange(e, 'decoration')} />)}
81 99
             </Form.Item>
@@ -136,7 +154,7 @@ class ProjectTypeBody extends React.Component {
136 154
     const { projectType } = this.state
137 155
     const buildingType = projectType.filter(item => checked.includes(`${item.buildingTypeId}`))
138 156
 
139
-    const tempDate = buildingType.map(item => ({ buildingTypeId: item.buildingTypeId, buildingTypeName: item.buildingTypeName, price: null, decoration: null, rightsYear: null, status: '1' }))
157
+    const tempDate = buildingType.map(item => ({ buildingTypeId: item.buildingTypeId, buildingTypeName: item.buildingTypeName, startPrice: null,endPrice: null,priceType: "average", decoration: null, rightsYear: null, status: '1' }))
140 158
     const updateProjectDate = this.updateProjectType(tempDate)
141 159
     console.log('updateProjectDate: ', updateProjectDate)
142 160
     this.setState({ data: updateProjectDate })

+ 29
- 0
src/pages/building/list/add/components/imageSet.jsx Datei anzeigen

@@ -25,6 +25,29 @@ const saleType = [
25 25
   },
26 26
 ]
27 27
 
28
+const houseType = [
29
+  {
30
+    id: 1,
31
+    name: '1室',
32
+  },
33
+  {
34
+    id: 2,
35
+    name: '2室',
36
+  },
37
+  {
38
+    id: 3,
39
+    name: '3室',
40
+  },
41
+  {
42
+    id: 4,
43
+    name: '4室',
44
+  },
45
+  {
46
+    id: 5,
47
+    name: '5室及以上',
48
+  },
49
+]
50
+
28 51
 /**
29 52
  *图片设置
30 53
  *
@@ -115,6 +138,12 @@ function imageSet(props) {
115 138
       key: 'marketStatus',
116 139
       render: (_, record) => <span>{ (saleType.filter(x => x.id == record.marketStatus)[0] || {}).name }</span>,
117 140
     },
141
+    {
142
+      title: '户型',
143
+      dataIndex: 'houseType',
144
+      key: 'houseType',
145
+      render: (_, record) => <span>{ (houseType.filter(x => x.id == record.houseType)[0] || {}).name }</span>,
146
+    },
118 147
     {
119 148
       title: '面积',
120 149
       dataIndex: 'buildingArea',

+ 37
- 18
src/pages/building/list/add/components/modalImage.jsx Datei anzeigen

@@ -42,6 +42,29 @@ const saleType = [
42 42
   },
43 43
 ]
44 44
 
45
+const houseType = [
46
+  {
47
+    id: '1',
48
+    name: '1室',
49
+  },
50
+  {
51
+    id: '2',
52
+    name: '2室',
53
+  },
54
+  {
55
+    id: '3',
56
+    name: '3室',
57
+  },
58
+  {
59
+    id: '4',
60
+    name: '4室',
61
+  },
62
+  {
63
+    id: '5',
64
+    name: '5室及以上',
65
+  },
66
+]
67
+
45 68
 /**
46 69
  * 图片信息
47 70
  *
@@ -186,7 +209,7 @@ class ModalImage extends React.Component {
186 209
                   </Select>,
187 210
                 )}
188 211
               </Form.Item>
189
-              <Form.Item label="图片">
212
+              <Form.Item label="图片" help="建议图片尺寸336px*336px,比例1:1,格式:jpg">
190 213
               {getFieldDecorator('img', {
191 214
                   rules: [{ required: true, message: '请上传户型图片' }],
192 215
               })(
@@ -194,26 +217,22 @@ class ModalImage extends React.Component {
194 217
                 <ImageUpload />,
195 218
               )}
196 219
               </Form.Item>
220
+              <Form.Item label="户型">
221
+                {getFieldDecorator('houseType', {
222
+                  rules: [{ required: true, message: '请选择户型' }],
223
+                })(
224
+                  <Select placeholder="户型">
225
+                    {
226
+                      houseType.map((item, _) => <Option value={item.id}>{item.name}</Option>)
227
+                    }
228
+                  </Select>,
229
+                )}
230
+              </Form.Item>
197 231
               <Form.Item label="面积" help="单位 ㎡">
198
-                {getFieldDecorator('buildingArea', {
199
-                  rules: [{ message: '请填写面积' },
200
-                          {
201
-                            pattern: new RegExp('^[1-9][0-9]*(\.[0-9]{1,2})?$'),
202
-                            message: '只允许输入数字',
203
-                          },
204
-                  ],
205
-                })(<Input type="number" />)}
232
+                {getFieldDecorator('buildingArea')(<Input type="number" precision = '2' min='0.00' step='0.01'/>)}
206 233
               </Form.Item>
207 234
               <Form.Item label="套内面积" help="单位 ㎡">
208
-                {getFieldDecorator('insideArea', {
209
-                  rules: [
210
-                    // { required: true, message: '请填写套内面积' },
211
-                          {
212
-                            pattern: new RegExp('^[1-9][0-9]*(\.[0-9]{1,2})?$'),
213
-                            message: '只允许输入数字',
214
-                          },
215
-                  ],
216
-                })(<Input type="number" />)}
235
+                {getFieldDecorator('insideArea')(<Input type="number" precision = '2' min='0.00' step='0.01'/>)}
217 236
               </Form.Item>
218 237
               <Form.Item style={{ width: '400px', margin: 'auto', display: 'flex', justifyContent: 'space-between' }}>
219 238
                 <Button type="primary" htmlType="submit">保存</Button>

+ 250
- 0
src/pages/building/list/add/components/modalPanoramaImage.jsx Datei anzeigen

@@ -0,0 +1,250 @@
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 ImageUpload from '../../../../../components/XForm/ImageUpload'
8
+import ImageListUpload from '../../../../../components/XForm/ImageListUpload'
9
+import Wangedit from '../../../../../components/Wangedit/Wangedit'
10
+
11
+
12
+const { Option } = Select;
13
+// eslint-disable-next-line @typescript-eslint/no-unused-vars
14
+const { Meta } = Card;
15
+
16
+const { TextArea } = Input;
17
+
18
+const formItemLayout = {
19
+  labelCol: {
20
+    xs: { span: 24 },
21
+    sm: { span: 2 },
22
+  },
23
+  wrapperCol: {
24
+    xs: { span: 24 },
25
+    sm: { span: 16 },
26
+  },
27
+};
28
+
29
+const saleType = [
30
+  {
31
+    id: 1,
32
+    name: '待售',
33
+  },
34
+  {
35
+    id: 2,
36
+    name: '售罄',
37
+  },
38
+  {
39
+    id: 3,
40
+    name: '在售',
41
+  },
42
+]
43
+
44
+const houseType = [
45
+  {
46
+    id: '1',
47
+    name: '1室',
48
+  },
49
+  {
50
+    id: '2',
51
+    name: '2室',
52
+  },
53
+  {
54
+    id: '3',
55
+    name: '3室',
56
+  },
57
+  {
58
+    id: '4',
59
+    name: '4室',
60
+  },
61
+  {
62
+    id: '5',
63
+    name: '5室及以上',
64
+  },
65
+]
66
+  
67
+/**
68
+ * 图片信息
69
+ *
70
+ * @param {*} props
71
+ * @returns
72
+ */
73
+class ModalPanoramaImage extends React.Component {
74
+  constructor(props) {
75
+    super(props);
76
+    this.state = {
77
+       visibleData: { visible: false, apartmentId: '', buildingId: '',panoramaType: "apartment" },
78
+    }
79
+  }
80
+
81
+  // 挂载之后
82
+  // componentDidMount() {
83
+  //
84
+  // }
85
+
86
+  componentDidUpdate(preProps, preState) {
87
+    if (this.props.visibleData.visible !== preState.visibleData.visible) {
88
+      console.log(this.props.visibleData)
89
+      this.getById()
90
+      this.setState({ visibleData: this.props.visibleData });
91
+    }
92
+  }
93
+
94
+  // 弹框确定按钮
95
+  // eslint-disable-next-line react/sort-comp
96
+  handleOk() {
97
+    this.setState({ visibleData: { visible: false, apartmentId: '', buildingId: '' } })
98
+  }
99
+
100
+  // 弹框取消按钮
101
+  handleCancel() {
102
+    // this.setState({ visibleData: { visible: false, apartmentId: '', buildingId: '' } })
103
+    this.props.onSuccess()
104
+  }
105
+
106
+  getById(params) {
107
+    const { apartmentId } = this.props.visibleData
108
+    if (apartmentId === '' || apartmentId === undefined) {
109
+      return
110
+    }
111
+
112
+    // 网路请求
113
+    request({ ...apis.building.buildingApartmentGetById, urlData: { id: apartmentId }, params: { ...params } }).then(res => {
114
+      // res.img = res.buildingImgList.map(item => item.url)
115
+      if (res.buildingImgList) {
116
+        res.img = res.buildingImgList[0].url
117
+      }
118
+      
119
+      this.props.form.setFieldsValue(res)
120
+    }).catch(err => {
121
+     this.openNotificationWithIcon('error', err)
122
+    })
123
+  }
124
+
125
+  openNotificationWithIcon = (type, message) => {
126
+    notification[type]({
127
+      message,
128
+      description:
129
+        '',
130
+    });
131
+  };
132
+
133
+  // 提交
134
+  handleSubmit(e) {
135
+    e.preventDefault();
136
+    this.props.form.validateFields((err, values) => {
137
+      if (!err) {
138
+        this.submitData(values)
139
+      }
140
+    });
141
+  }
142
+
143
+  submitData(data) {
144
+    data.buildingId = this.props.visibleData.buildingId;
145
+    const api = apis.paorama.add;
146
+
147
+    // 网路请求
148
+    request({ ...api, data: { ...data } }).then(() => {
149
+      // eslint-disable-next-line no-unused-expressions
150
+      this.openNotificationWithIcon('success', '操作成功')
151
+
152
+      // 传递父组件事件
153
+      // onSuccess() 是自定义
154
+      this.props.onSuccess()
155
+
156
+      // this.setState({ visibleData: { visible: false, apartmentId: '', buildingId: '' } }, () => console.log('回调:', this.state.visibleData))
157
+    }).catch(err => {
158
+      // eslint-disable-next-line no-unused-expressions
159
+      this.openNotificationWithIcon('error', err)
160
+    })
161
+  }
162
+
163
+  radioOnChange(e) {
164
+    this.setState({ visibleData: { ...this.state.visibleData, panoramaType: e.target.value } })
165
+  }
166
+
167
+  /**
168
+   * 取消按钮
169
+   *
170
+   * @memberof ModalImage
171
+   */
172
+  closeModal() {
173
+    this.setState({ visibleData: { visible: false, apartmentId: '', buildingId: '' } })
174
+  }
175
+
176
+  render() {
177
+    const { getFieldDecorator } = this.props.form;
178
+    return (
179
+      <>
180
+        <Modal
181
+            title="新增全景图"
182
+            width={1100}
183
+            destroyOnClose="true"
184
+            footer={null}
185
+            visible={this.state.visibleData.visible}
186
+            onOk={() => this.handleOk()}
187
+            onCancel={e => this.handleCancel(e)}
188
+          >
189
+            <Form {...formItemLayout} onSubmit={e => this.handleSubmit(e)}>
190
+              <Form.Item label="全景类型">
191
+                {getFieldDecorator('panoramaType', {
192
+                  rules: [
193
+                    {
194
+                      required: true,
195
+                      message: '请选择全景类型',
196
+                    },
197
+                  ],
198
+                })(
199
+                  <Radio.Group onChange={(e) => this.radioOnChange(e)}>
200
+                    <Radio value="apartment">户型</Radio>
201
+                    <Radio value="building">项目</Radio>
202
+                  </Radio.Group>,
203
+                )}
204
+              </Form.Item>
205
+              { this.state.visibleData.panoramaType == "apartment" && <Form.Item label="全景内容">
206
+                {getFieldDecorator('apartmentId', {
207
+                  rules: [{ required: true, message: '请选择全景内容' }],
208
+                })(
209
+                  <Select placeholder="户型">
210
+                    {
211
+                      (this.state.visibleData.panoramaList || []).map((item, _) => <Option value={item.apartmentId}>{item.apartmentName}</Option>)
212
+                    }
213
+                  </Select>,
214
+                )}
215
+              </Form.Item>}
216
+              { this.state.visibleData.panoramaType == "building" && <Form.Item label="全景内容">
217
+                {getFieldDecorator('content', {
218
+                  rules: [{ required: true, message: '请输入全景内容' }],
219
+                })(
220
+                  <Input />,
221
+                )}
222
+              </Form.Item>}
223
+              <Form.Item label="选择封面" help="建议图片尺寸750px*600px,比例5:4,格式:jpg">
224
+              {getFieldDecorator('coverImg', {
225
+                  rules: [{ required: true, message: '请上传封面图片' }],
226
+              })(
227
+                // <ImageListUpload />,
228
+                <ImageUpload />,
229
+              )}
230
+              </Form.Item>
231
+              <Form.Item label="链接地址">
232
+                {getFieldDecorator('panoramaLink', {
233
+                  rules: [{ required: true, message: '请输入链接地址' }],
234
+                })(<Input />)}
235
+              </Form.Item>
236
+              <Form.Item style={{ width: '400px', margin: 'auto', display: 'flex', justifyContent: 'space-between' }}>
237
+                <Button type="primary" htmlType="submit">保存</Button>
238
+                &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
239
+                <Button onClick={() => this.handleCancel()}>取消</Button>
240
+              </Form.Item>
241
+            </Form>
242
+        </Modal>
243
+      </>
244
+    );
245
+  }
246
+}
247
+
248
+const WrappedModalPanoramaImageForm = Form.create({ name: 'modalPanoramaImage' })(ModalPanoramaImage);
249
+
250
+export default WrappedModalPanoramaImageForm

+ 182
- 0
src/pages/building/list/add/components/panoramaImage.jsx Datei anzeigen

@@ -0,0 +1,182 @@
1
+import React, { useState, useEffect } from 'react';
2
+import { Form, Icon, Input, Button, DatePicker, Select, Card, Row, Col, Pagination, Alert, Radio, Tag, Tooltip, Tabs, Table, notification, Modal } 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';
8
+import ModalPanoramaImage from './modalPanoramaImage';
9
+
10
+const { confirm } = Modal;
11
+
12
+
13
+const saleType = [
14
+  {
15
+    id: 1,
16
+    name: '待售',
17
+  },
18
+  {
19
+    id: 2,
20
+    name: '售罄',
21
+  },
22
+  {
23
+    id: 3,
24
+    name: '在售',
25
+  },
26
+]
27
+
28
+const houseType = [
29
+  {
30
+    id: 1,
31
+    name: '1室',
32
+  },
33
+  {
34
+    id: 2,
35
+    name: '2室',
36
+  },
37
+  {
38
+    id: 3,
39
+    name: '3室',
40
+  },
41
+  {
42
+    id: 4,
43
+    name: '4室',
44
+  },
45
+  {
46
+    id: 5,
47
+    name: '5室及以上',
48
+  },
49
+]
50
+
51
+/**
52
+ *图片设置
53
+ *
54
+ * @param {*} props
55
+ * @returns
56
+ */
57
+function panoramaImage(props) {
58
+  // eslint-disable-next-line react-hooks/rules-of-hooks
59
+  const [data, setData] = useState([])
60
+
61
+  // eslint-disable-next-line react-hooks/rules-of-hooks
62
+  const [visibleData, setVisibleData] = useState({ visible: false, apartmentId: '', buildingId: '' })
63
+
64
+  // eslint-disable-next-line react-hooks/rules-of-hooks
65
+  useEffect(() => {
66
+    getList()
67
+  }, [])
68
+
69
+  function openNotificationWithIcon(type, message) {
70
+    notification[type]({
71
+      message,
72
+      description:
73
+        '',
74
+    });
75
+  }
76
+
77
+  function getList(params) {
78
+    // 网路请求
79
+    request({ ...apis.paorama.list, urlData: { id: props.building.buildingId }, params: { ...params, apartmentType: 'apart' } }).then(res => {
80
+      setData(res)
81
+    }).catch(err => {
82
+      openNotificationWithIcon('error', err.message)
83
+    })
84
+  }
85
+
86
+  /**
87
+   *回调事件
88
+   *
89
+   */
90
+  function onModalChange() {
91
+    getList()
92
+    setVisibleData({ visible: false, apartmentId: '', buildingId: '' })
93
+  }
94
+
95
+  /**
96
+   *打开编辑页
97
+   *
98
+   * @param {*} record
99
+   */
100
+  function showEdi(record) {
101
+    // 网路请求
102
+    request({ ...apis.paorama.panoramaApartList, params: { buildingId:  props.building.buildingId } }).then(res => {
103
+      setVisibleData({ visible: true, apartmentId: record === undefined ? '' : record.apartmentId, buildingId: props.building.buildingId, panoramaType: "apartment", panoramaList: res })
104
+     }).catch(err => {
105
+      this.openNotificationWithIcon('error', err)
106
+     })
107
+  }
108
+
109
+  /**
110
+   * 删除
111
+   *
112
+   * @param {*} record
113
+   */
114
+  function deleteApartment(record) {
115
+    confirm({
116
+      title: '确认删除当前数据?',
117
+      content: '确定后成功删除,点击取消则放弃当前操作',
118
+      okText: '确定',
119
+      cancelText: '取消',
120
+      onOk() {
121
+        // 网路请求
122
+        request({ ...apis.paorama.delete, urlData: { id: record.panoramaId } }).then(res => {
123
+          getList()
124
+          openNotificationWithIcon('success', '操作成功')
125
+        }).catch(err => {
126
+          // openNotificationWithIcon('error', err.message)
127
+        })
128
+      },
129
+      onCancel() {},
130
+    });
131
+  }
132
+
133
+
134
+  const columns = [
135
+    {
136
+      title: '封面图',
137
+      dataIndex: 'coverImg',
138
+      key: 'coverImg',
139
+      render: (x, record) =>  <img style={{ width: '150px', height: '120px' }} src={x} alt="" />,
140
+    },
141
+    {
142
+      title: '类型',
143
+      dataIndex: 'panoramaType',
144
+      key: 'panoramaType',
145
+      render: (x, record) => <span>{x === "apartment" ? "户型" : "项目"}</span>,
146
+    },
147
+    {
148
+      title: '内容',
149
+      dataIndex: 'content',
150
+      key: 'content',
151
+    },
152
+    {
153
+      title: '链接地址',
154
+      dataIndex: 'panoramaLink',
155
+      key: 'panoramaLink',
156
+    },
157
+    {
158
+      title: '操作',
159
+      dataIndex: 'apartmentId',
160
+      key: 'apartmentId',
161
+      render: (_, record) => (
162
+        <>
163
+          <Button type="link" style={{ color: 'red' }} onClick={() => deleteApartment(record)}>删除</Button>
164
+        </>
165
+      ),
166
+    },
167
+  ]
168
+
169
+  return (
170
+    <>
171
+      <Button type="primary" onClick={() => showEdi()}>新增全景图</Button>
172
+      <Button type="danger" style={{ marginLeft: '18px'}} onClick={() => router.go(-1)}>返回</Button>
173
+      <Table dataSource={data} columns={columns} pagination={false} rowKey="imageSet" />
174
+
175
+      {/* 编辑页 */}
176
+      {/*  onSuccess是子组件传递事件信息  */}
177
+      <ModalPanoramaImage visibleData={visibleData} key="ModalImage" onSuccess={() => onModalChange()}/>
178
+    </>
179
+  )
180
+}
181
+
182
+export default panoramaImage

+ 3
- 0
src/pages/building/list/add/index.jsx Datei anzeigen

@@ -12,6 +12,7 @@ import Wangedit from '../../../../components/Wangedit/Wangedit'
12 12
 import TagGroup from './components/tags'
13 13
 import Base from './components/base'
14 14
 import Apartment from './components/imageSet'
15
+import PanoramaImage from './components/panoramaImage'
15 16
 import Poster from './components/poster'
16 17
 import Share from './components/share'
17 18
 import BuildingImage from './components/buildingImage';
@@ -64,6 +65,7 @@ function AddBuilding(props) {
64 65
         <Radio.Button value="base">基本信息</Radio.Button>
65 66
         <Radio.Button value="apartment">户型库</Radio.Button>
66 67
         <Radio.Button value="image">项目相册</Radio.Button>
68
+        <Radio.Button value="panorama">全景图片</Radio.Button>
67 69
         <Radio.Button value="poster">海报图片</Radio.Button>
68 70
         <Radio.Button value="share">分享设置</Radio.Button>
69 71
       </Radio.Group>
@@ -71,6 +73,7 @@ function AddBuilding(props) {
71 73
         { tab === 'base' && <Base building={{ buildingId: buildingData.buildingId || (props.location.query && props.location.query.id) }} onSuccess={e => buildingOnSuccess(e)}/> }
72 74
         { (tab === 'apartment' && <Apartment building={buildingData} />)}
73 75
         { (tab === 'image' && <BuildingImage building={buildingData} />)}
76
+        { (tab === 'panorama' && <PanoramaImage building={buildingData} />)}
74 77
         { (tab === 'poster' && <Poster building={buildingData} />)}
75 78
         { (tab === 'share' && <Share building={buildingData} />)}
76 79
       </div>

+ 4
- 3
src/pages/building/list/index.jsx Datei anzeigen

@@ -1,5 +1,5 @@
1 1
 import React, { useState, useEffect } from 'react';
2
-import { Form, Icon, Input, Button, DatePicker, Select, Card, Row, Col, Pagination, Alert, notification, Modal } from 'antd';
2
+import { Form, Icon, Input, Button, DatePicker, Select, Card, Row, message, Col, Pagination, Alert, notification, Modal } from 'antd';
3 3
 import moment from 'moment';
4 4
 import request from '../../../utils/request';
5 5
 import apis from '../../../services/apis';
@@ -59,7 +59,8 @@ function CartBody(props) {
59 59
           openNotificationWithIcon('success', '操作成功')
60 60
           props.onSuccess()
61 61
         }).catch(err => {
62
-          openNotificationWithIcon('error', err.message)
62
+          // openNotificationWithIcon('error', err.message)
63
+          message.info(err.msg || err.message)
63 64
         })
64 65
 
65 66
         modal.destroy();
@@ -174,7 +175,7 @@ function body(props) {
174 175
   const { getFieldDecorator } = props.form
175 176
 
176 177
   // eslint-disable-next-line react-hooks/rules-of-hooks
177
-  const [dataSource, setDataSource] = useState({ records: [] })
178
+  const [dataSource, setDataSource] = useState({ records: [],size: 0 })
178 179
 
179 180
   // eslint-disable-next-line react-hooks/rules-of-hooks
180 181
   useEffect(() => {

+ 1
- 1
src/pages/carouselFigure/editCarousel.jsx Datei anzeigen

@@ -164,7 +164,7 @@ const { MonthPicker, RangePicker, WeekPicker } = DatePicker;
164 164
           name: 'image',
165 165
           type: FieldTypes.ImageUploader,
166 166
           value: data.image,
167
-          help: () => (locationType ? '建议图片尺寸:750*420px,比例16:9,格式:jpg,用于积分商城banner轮播' : '建议图片尺寸:750*464px,比例1.:0.618,格式:jpg,用于:首页顶部banner轮播'),
167
+          help: () => (locationType ? '建议图片尺寸:750*250px,比例3:1,格式:jpg,用于积分商城banner轮播' : '建议图片尺寸:750*464px,比例1.:0.618,格式:jpg,用于:首页顶部banner轮播'),
168 168
           rules: [
169 169
             { required: true, message: '请上传轮播图' },
170 170
           ],

+ 158
- 0
src/pages/customer/customerlist/components/assistConsultant.jsx Datei anzeigen

@@ -0,0 +1,158 @@
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 })
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, buildingId: this.props.visibleData.buildingId })
91
+  }
92
+
93
+  // 提交
94
+  submitGm(record) {
95
+    // 网路请求
96
+    request({ ...apis.customer.consultantAssist, urlData: { id: this.state.visibleData.customerId }, data: { userId: record.userId } }).then(res => {
97
+      // eslint-disable-next-line no-unused-expressions
98
+      this.openNotificationWithIcon('success', '操作成功')
99
+      this.handleCancel()
100
+    }).catch(err => {
101
+      // eslint-disable-next-line no-unused-expressions
102
+      this.openNotificationWithIcon('error', err)
103
+    })
104
+  }
105
+
106
+  render() {
107
+    const columns = [
108
+      // {
109
+      //   title: '编号',
110
+      //   dataIndex: 'userId',
111
+      //   key: 'userId',
112
+      // },
113
+      {
114
+        title: '姓名',
115
+        dataIndex: 'userName',
116
+        key: 'userName',
117
+      },
118
+      {
119
+        title: '电话',
120
+        dataIndex: 'phone',
121
+        key: 'phone',
122
+      },
123
+      {
124
+        title: '部门',
125
+        dataIndex: 'department',
126
+        key: 'department',
127
+      },
128
+      {
129
+        title: '岗位',
130
+        dataIndex: 'position',
131
+        key: 'position',
132
+      },
133
+      {
134
+        title: '操作',
135
+        dataIndex: 'personId',
136
+        key: 'personId',
137
+        // eslint-disable-next-line no-nested-ternary
138
+        render: (_, record) => <>{ <Button type="danger" onClick={() => this.submitGm(record)}>确定</Button>}</>, },
139
+    ]
140
+    return (
141
+      <>
142
+        <Modal
143
+            title="分配置业顾问"
144
+            width={800}
145
+            destroyOnClose="true"
146
+            footer={null}
147
+            visible={this.state.visibleData.visible}
148
+            onCancel={(e) => this.handleCancel(e)}
149
+          >
150
+            <BuildSelect onChange={this.changBuilding.bind(this)} value={this.state.visibleData.buildingName} />
151
+            <Table rowKey="assistConsultant" dataSource={this.state.dataSource.records} columns={columns} pagination={{ total: this.state.dataSource.total, onChange: e => this.onChange(e) }} />
152
+          </Modal>
153
+      </>
154
+    );
155
+  }
156
+}
157
+
158
+export default ModalAttribution

+ 10
- 1
src/pages/customer/customerlist/customerDetail.jsx Datei anzeigen

@@ -5,6 +5,7 @@ import styles from './style.less';
5 5
 import apis from '../../../services/apis';
6 6
 import request from '../../../utils/request';
7 7
 import moment from 'moment';
8
+import BuildSelect from '../../../components/SelectButton/BuildSelect'
8 9
 
9 10
 import router from 'umi/router';
10 11
 
@@ -19,6 +20,7 @@ function header(props) {
19 20
   const [tableData, setTableDataData] = useState([{}])
20 21
   const [dataConsultant, setDataonsultant] = useState({})
21 22
   const [intentionData, setIntentionData] = useState([])
23
+  const [buildingIdValue, setBuildingIdData] = useState()
22 24
 
23 25
   // eslint-disable-next-line react-hooks/rules-of-hooks
24 26
   useEffect(() => {
@@ -71,6 +73,12 @@ function header(props) {
71 73
       setDataonsultant(res.geoInfo)
72 74
     })
73 75
   }
76
+
77
+  function changBuilding(buildingId) {
78
+    setBuildingIdData(buildingId)
79
+    getById({ pageNumber: 1, pageSize: 10, buildingId: buildingId })
80
+  }
81
+
74 82
   const columns = [
75 83
     {
76 84
       title: '访问事件',
@@ -190,7 +198,8 @@ function header(props) {
190 198
       </div>
191 199
       <div className={styles.recordBox}>
192 200
         <p className={styles.tableName}>访问记录</p>
193
-        <Table dataSource={tableData.records} columns={columns} pagination={{ pageSize: 10, total: tableData.total, onChange }} />
201
+        <BuildSelect onChange={changBuilding} value={buildingIdValue} />
202
+        <Table dataSource={tableData.records} columns={columns} style={{marginTop: '15px'}} pagination={{ pageSize: 10, total: tableData.total, onChange }} />
194 203
         {/* <Pagination showQuickJumper defaultCurrent={1} total={data.records} onChange={e => changePageNum(e)} current={data.visitRecords.total}/> */}
195 204
       </div>
196 205
 

+ 26
- 0
src/pages/customer/customerlist/index.jsx Datei anzeigen

@@ -7,11 +7,13 @@ import Styles from './style.less';
7 7
 import router from 'umi/router';
8 8
 
9 9
 import Attribution from './components/attribution'
10
+import AssistConsultant from './components/assistConsultant'
10 11
 import IntegralRecord from './components/integralRecord'
11 12
 import ModalRecommendRecord from './components/recommend'
12 13
 import ChangeStatus from './components/changeStatus'
13 14
 import BuildSelect from '../../../components/SelectButton/BuildSelect'
14 15
 import AuthButton from '@/components/AuthButton';
16
+import WxDictSelect from '@/components/SelectButton/WxDictSelect';
15 17
 
16 18
 
17 19
 const { Option } = Select;
@@ -54,6 +56,8 @@ function body(props) {
54 56
   // 推荐客户
55 57
   const [recommendVisibleData, setRecommendVisibleData] = useState({ visible: false, customerId: '' })
56 58
 
59
+  const [assistVisibleData, setAssistVisibleData] = useState({visible: false, customerId: ''})
60
+
57 61
   // eslint-disable-next-line react-hooks/rules-of-hooks
58 62
   useEffect(() => {
59 63
     getList({ pageNumber: 1, pageSize: 10, customerType })
@@ -159,6 +163,16 @@ function body(props) {
159 163
     setStatusVisibleData({ visible: false, customerId: '', status: '' })
160 164
     setRecommendVisibleData({ visible: true, customerId: personId })
161 165
   }
166
+
167
+  //分配置业顾问
168
+  function assistConsultant(personId) {
169
+    setGVisibleData({ visible: false, customerId: '', realtyConsultant: '', buildingId: '' })
170
+    setRecordVisibleData({ visible: false, customerId: '' })
171
+    setStatusVisibleData({ visible: false, customerId: '' })
172
+    setRecommendVisibleData({ visible: false, customerId: '' })
173
+    setAssistVisibleData({ visible: true, customerId: personId })
174
+  }
175
+
162 176
   function toCustomerDateil(record) {
163 177
     router.push({
164 178
       pathname: '/customer/customerlist/customerDetail',
@@ -211,6 +225,7 @@ function body(props) {
211 225
     setGVisibleData({ visible: false, customerId: '', realtyConsultant: '', buildingId: '' })
212 226
     setStatusVisibleData({ visible: false, customerId: '', status: '' })
213 227
     setRecommendVisibleData({ visible: false, customerId: '' })
228
+    setAssistVisibleData({ visible: false, customerId: ''})
214 229
     getList({ pageNum: 1, pageSize: 10, customerType, ...props.form.getFieldsValue() })
215 230
   }
216 231
 
@@ -271,6 +286,9 @@ function body(props) {
271 286
           <AuthButton name="admin.customer.recommend" noRight={null}>
272 287
             <Button className={customerType === 'private' ? Styles.displayS : Styles.text } type="link" onClick={() => showRecommend(record.personId)}>推荐客户</Button>
273 288
           </AuthButton>
289
+          <AuthButton name="admin.customer.assign" noRight={null}>
290
+            <Button className={customerType === 'private' ? Styles.displayS : Styles.text } type="link" onClick={() => assistConsultant(record.personId)}>分配置业顾问</Button>
291
+          </AuthButton>
274 292
         </>
275 293
       ),
276 294
     },
@@ -425,6 +443,11 @@ function body(props) {
425 443
             <Input placeholder="置业顾问电话" />,
426 444
           )}
427 445
         </Form.Item>}
446
+        <Form.Item>
447
+          {getFieldDecorator('sceneType')(
448
+            <WxDictSelect />,
449
+          )}
450
+        </Form.Item>
428 451
         <Form.Item>
429 452
             <AuthButton name="admin.customer.recommend.search" noRight={null}>
430 453
               <Button type="primary" htmlType="submit" >
@@ -476,6 +499,9 @@ function body(props) {
476 499
 
477 500
       {/* 推荐客户 */}
478 501
       <ModalRecommendRecord visibleData={recommendVisibleData} onCancel={() => closeAll()}/>
502
+
503
+      {/* 分配置业顾问 */}
504
+      <AssistConsultant visibleData={assistVisibleData} onCancel={() => closeAll()}/>
479 505
     </>
480 506
   );
481 507
 }

+ 10
- 1
src/pages/customer/customerlist/publicCustomerDetail.jsx Datei anzeigen

@@ -5,6 +5,7 @@ import publicStyle from './publicStyle.less';
5 5
 import apis from '../../../services/apis';
6 6
 import request from '../../../utils/request';
7 7
 import moment from 'moment';
8
+import BuildSelect from '../../../components/SelectButton/BuildSelect'
8 9
 
9 10
 import router from 'umi/router';
10 11
 
@@ -19,6 +20,7 @@ function header(props) {
19 20
   const [tableData, setTableDataData] = useState([{ }])
20 21
   const [dataConsultant, setDataonsultant] = useState({})
21 22
   const [intentionData, setIntentionData] = useState([])
23
+  const [buildingIdValue, setBuildingIdData] = useState()
22 24
 
23 25
   // eslint-disable-next-line react-hooks/rules-of-hooks
24 26
   useEffect(() => {
@@ -72,6 +74,12 @@ function onChange(number) {
72 74
       setDataonsultant(res.geoInfo)
73 75
     })
74 76
   }
77
+
78
+  function changBuilding(buildingId) {
79
+    setBuildingIdData(buildingId)
80
+    getById({ pageNumber: 1, pageSize: 10, buildingId: buildingId })
81
+  }
82
+
75 83
   const columns = [
76 84
     {
77 85
       title: '访问事件',
@@ -158,7 +166,8 @@ function onChange(number) {
158 166
       </div>
159 167
       <div className={publicStyle.recordBox}>
160 168
         <p className={publicStyle.tableName}>访问记录</p>
161
-        <Table dataSource={tableData.records} columns={columns} pagination={{ pageSize: 10, total: tableData.total, onChange }} />
169
+        <BuildSelect onChange={changBuilding} value={buildingIdValue} />
170
+        <Table dataSource={tableData.records} columns={columns} style={{marginTop: '15px'}} pagination={{ pageSize: 10, total: tableData.total, onChange }} />
162 171
       </div>
163 172
     </>
164 173
   )

+ 53
- 5
src/pages/customer/drift/index.jsx Datei anzeigen

@@ -1,11 +1,13 @@
1 1
 import React, { useState, useEffect } from 'react';
2
-import { Table, Avatar, Alert } from 'antd';
2
+import { Form, Table, Avatar, Alert, Select, Button } from 'antd';
3 3
 
4 4
 import request from '../../../utils/request';
5 5
 import apis from '../../../services/apis';
6 6
 import Styles from './style.less'
7
+import WxDictSelect from '@/components/SelectButton/WxDictSelect';
7 8
 
8
-function costomerDrift() {
9
+function costomerDrift(props) {
10
+  const { getFieldDecorator } = props.form
9 11
   // eslint-disable-next-line react-hooks/rules-of-hooks
10 12
   const [dataSources, setDataSources] = useState({ records: [] })
11 13
 
@@ -37,6 +39,24 @@ function costomerDrift() {
37 39
     getList({ pageNumber: pageNum, pageSize: 10 })
38 40
   }
39 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
+
40 60
   const columns = [
41 61
     {
42 62
       title: '头像',
@@ -56,12 +76,40 @@ function costomerDrift() {
56 76
       // eslint-disable-next-line no-nested-ternary
57 77
       render: (_, record) => <><span>{ record.gender === '1' ? '男' : record.gender === '2' ? '女' : '未知' }</span></>,
58 78
     },
79
+    {
80
+      title: '用户来源',
81
+      dataIndex: 'sceneAlias',
82
+      key: 'sceneAlias',
83
+      render: (_, record) => <><span>{ record.sceneAlias ? record.sceneAlias : '其他'}</span></>,
84
+    },
59 85
   ];
60
-
86
+  
61 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 dataSource={dataSources.records} rowKey="drift" columns={columns} pagination={{ total: dataSources.total, onChange }} />
108
+    </>
62 109
     // eslint-disable-next-line max-len
63
-    <Table dataSource={dataSources.records} rowKey="drift" columns={columns} pagination={{ total: dataSources.total, onChange }} />
110
+    
64 111
   )
65 112
 }
66 113
 
67
-export default costomerDrift
114
+const WrappedBody = Form.create()(costomerDrift);
115
+export default WrappedBody

+ 11
- 4
src/pages/indexEcharts/components/IntentionalCustomers.jsx Datei anzeigen

@@ -10,19 +10,26 @@ const UserBehavior = (props) => {
10 10
   const [data, setData] = useState({ records: [] })
11 11
   const [buildingId, setBuildingId] = useState('')
12 12
   useEffect(() => {
13
-    IntentionUsers()
13
+    IntentionUsers({ pageNum: 1, pageSize: 5 })
14 14
     // getUserBehaviorProfile(formatDate(props.startDate, props.endDate))
15 15
 
16 16
   }, [])
17 17
   function IntentionUsers (params) {
18 18
     request({
19 19
       ...apis.indexEcharts.intentionUsers,
20
-      params: { pageSize: 9999, ...params }
20
+      params: { ...params }
21 21
     }).then((data) => {
22
+      console.log(data,"datadata")
22 23
       setData(data)
23 24
     })
24 25
   }
25 26
 
27
+  // 分页
28
+  function onChange(pageNum) {
29
+    // eslint-disable-next-line react-hooks/rules-of-hooks
30
+    IntentionUsers({ pageNum: pageNum, pageSize: 5, buildingId })
31
+  }
32
+
26 33
   // getIntentionUsers({ commit }, payload) {
27 34
   //   return new Promise((resolve, reject) => {
28 35
   //     request({
@@ -78,7 +85,7 @@ const UserBehavior = (props) => {
78 85
   ];
79 86
 
80 87
   function handleBuildingChange (e) {
81
-    IntentionUsers({ buildingId: e })
88
+    IntentionUsers({  pageNum: 1, pageSize: 5, buildingId: e })
82 89
     setBuildingId(e)
83 90
   }
84 91
   return (
@@ -88,7 +95,7 @@ const UserBehavior = (props) => {
88 95
 
89 96
         <BuildSelect slot='action' onChange={(e => handleBuildingChange(e))} value={buildingId}></BuildSelect>
90 97
 
91
-        <Table rowKey="IntentionalCustomers" dataSource={data.records} columns={columns} style={{marginTop:'15px'}} pagination={false} scroll={{ y: 500 }} />
98
+        <Table rowKey="IntentionalCustomers" dataSource={data.records} columns={columns} style={{marginTop:'15px'}} pagination={{ total: data.total, defaultPageSize: 5, onChange: e => onChange(e) }} scroll={{ y: 500 }} />
92 99
 
93 100
       </div>
94 101
 

+ 289
- 0
src/pages/indexEcharts/components/UserBehaviorIndex.jsx Datei anzeigen

@@ -0,0 +1,289 @@
1
+import React, { Component, useState, useEffect } from 'react';
2
+import EChart from '../../../components/EchartsTest/EChart';
3
+import request from '../../../utils/request';
4
+import apis from '../../../services/apis';
5
+import moment from 'moment';
6
+import router from 'umi/router';
7
+import 'echarts/lib/component/dataZoom'
8
+import { Table, Select, Row, Col, Menu, Dropdown, Button, Icon, message, Modal } from 'antd';
9
+import BuildSelect from '../../../components/SelectButton/BuildSelect'
10
+
11
+const { Option } = Select;
12
+
13
+
14
+const eventcolumns = [
15
+  {
16
+    title: '编号',
17
+    dataIndex: 'recordId',
18
+    key: 'recordId',
19
+    align: 'center',
20
+    width: '16%',
21
+  },
22
+  {
23
+    title: '访问时间',
24
+    dataIndex: 'visitTime',
25
+    key: 'visitTime',
26
+    align: 'center',
27
+    width: '18%',
28
+    render: (x, row) => (
29
+      <>
30
+        <span>{ moment(row.visitTime).format('YYYY-MM-DD hh:mm:ss') }</span>
31
+      </>
32
+    ),
33
+  },
34
+  {
35
+    title: '离开时间',
36
+    dataIndex: 'leaveTime',
37
+    key: 'leaveTime',
38
+    align: 'center',
39
+    width: '18%',
40
+    render: (x, row) => (
41
+      <>
42
+        <span>{ row.leaveTime && moment(row.leaveTime).format('YYYY-MM-DD hh:mm:ss') }</span>
43
+      </>
44
+    ),
45
+  },
46
+]
47
+
48
+
49
+class EventcoModal extends React.Component {
50
+  constructor(props) {
51
+    super(props)
52
+    this.state = {
53
+      eventList: [],
54
+      visibleData: { visible: false, row: {} },
55
+    }
56
+  }
57
+
58
+  componentDidUpdate(prevProps) {
59
+    if (this.props.visibleData.visible !== prevProps.visibleData.visible) {
60
+      // eslint-disable-next-line react/no-did-update-set-state
61
+      this.setState({ visibleData: this.props.visibleData }, () => {
62
+        this.showDetails(this.props.visibleData.row)
63
+      })
64
+    }
65
+  }
66
+
67
+  showDetails = row => {
68
+    request({
69
+      ...apis.indexEcharts.userBehavior.profile,
70
+      params: { event: row.event, personId: row.personId },
71
+    }).then(data => {
72
+        this.setState({ eventList: data })
73
+    })
74
+  }
75
+
76
+  handleOk = e => {
77
+    this.props.onSuccess()
78
+  };
79
+
80
+  handleCancel = e => {
81
+    this.props.onSuccess()
82
+  };
83
+
84
+  render() {
85
+    return (
86
+      <>
87
+        <Modal
88
+          title="访问详情"
89
+          visible={this.state.visibleData.visible}
90
+          onOk={() => this.handleOk()}
91
+          onCancel={() => this.handleCancel()}
92
+          footer={null}
93
+        >
94
+         <Table rowKey="UserBehavior" dataSource={this.state.eventList} columns={eventcolumns} pagination={false} rowKey="eventall" scroll={{ y: 400 }} />
95
+        </Modal>
96
+      </>
97
+    )
98
+  }
99
+}
100
+
101
+
102
+const formatDate = (start, end) => {
103
+  const startDate = `${moment(start).format('YYYY-MM-DDT00:00:00.000')}Z`
104
+  const endDate = `${moment(end).format('YYYY-MM-DDT23:59:59.999')}Z`
105
+  return { startDate, endDate }
106
+}
107
+const UserBehaviorIndex = props => {
108
+  const [data, setData] = useState({ records: [] })
109
+  const [visibleData, setVisibleData] = useState({ visible: false, row: {} })
110
+  const [buildingId, setBuildingId] = useState('')
111
+
112
+  // 柱图
113
+  useEffect(() => {
114
+    const date = formatDate(props.startDate, props.endDate)
115
+    getUserBehaviorSummary({
116
+      ...date,
117
+      activity: props.activity,
118
+      event: props.event,
119
+      eventType: props.eventType,
120
+      buildingId: props.buildingId,
121
+    })
122
+    // getUserBehaviorProfile(formatDate(props.startDate, props.endDate))
123
+  }, [props.startDate, props.endDate, props.activity, props.event, props.eventType, props.buildingId])
124
+
125
+  const [recordList, setList] = useState([])
126
+  function getUserBehaviorSummary(params) {
127
+    request({
128
+      ...apis.indexEcharts.userBehavior.tsUserBehavior,
129
+      params,
130
+    }).then(data => {
131
+      setData(data || {})
132
+      // setList((data.data.records || []).filter(e => e.activity !== '客户'))
133
+    })
134
+  }
135
+
136
+
137
+  const seriesMaker = (data.selectUserBehavior || []).filter(e => e.activity !== '客户' && e.activity !== '首页').reduce((series, item) => {
138
+    let { date, activityCount, activity } = item
139
+    date = moment(date).format('YYYY-MM-DD')
140
+    if (!activityCount) activityCount = 0
141
+
142
+    // 使用对象, 可以去重
143
+    series[`${activity}`] = (series[`${activity}`] || []).concat([[date, activityCount]])
144
+
145
+    return series;
146
+  }, {})
147
+
148
+
149
+  const dataZoom = props.dataZoom ? [
150
+    {
151
+      type: 'inside',
152
+      start: 0,
153
+      end: 100,
154
+    },
155
+    {
156
+      type: 'slider',
157
+      start: 0,
158
+      end: 100,
159
+    },
160
+  ] : undefined
161
+
162
+
163
+  const options = {
164
+    title: {},
165
+    icon: 'rect',
166
+    legend: {
167
+      show: true,
168
+      zlevel: 10,
169
+      itemGap: 100,
170
+    },
171
+    color: ['#F12B3E', '#FE929C', '#647CE1', '#A2B9FF', '#FF844F', '#FFBB9D'],
172
+    tooltip: {
173
+      trigger: 'axis',
174
+    },
175
+    xAxis: { type: 'category' },
176
+    yAxis: {},
177
+    dataZoom,
178
+    series: Object.keys(seriesMaker).map(x => ({
179
+        type: 'line',
180
+        smooth: true,
181
+        name: x,
182
+        data: seriesMaker[x],
183
+      })),
184
+  }
185
+  function handleBuildingChange(e) {
186
+    const date = formatDate(props.startDate, props.endDate)
187
+    getUserBehaviorSummary({
188
+      ...date,
189
+      activity: props.activity,
190
+      event: props.event,
191
+      eventType: props.eventType,
192
+      buildingId: e,
193
+    })
194
+    setBuildingId(e)
195
+  }
196
+
197
+  function showDetails(row) {
198
+    setVisibleData({ visible: true, row })
199
+  }
200
+
201
+  function onSuccess() {
202
+    setVisibleData({ visible: false, row: {} })
203
+  }
204
+
205
+
206
+  const style = {
207
+    width: '100%',
208
+    height: '400px',
209
+
210
+  }
211
+
212
+  const columns = [
213
+    {
214
+      title: '访问事件',
215
+      dataIndex: 'eventName',
216
+      key: 'eventName',
217
+      align: 'center',
218
+      width: '16%',
219
+
220
+    },
221
+    {
222
+      title: '访问用户',
223
+      dataIndex: 'userName',
224
+      key: 'userName',
225
+      align: 'center',
226
+      width: '15%',
227
+
228
+    },
229
+    {
230
+      title: '访问次数',
231
+      dataIndex: 'accessCount',
232
+      key: 'accessCount',
233
+      align: 'center',
234
+      width: '17%',
235
+
236
+      render: (x, row) => (
237
+        <>
238
+          <span style={{ color: '#EF273A', cursor: 'pointer' }} onClick={() => showDetails(row)}> {row.accessCount}</span>
239
+        </>
240
+      ),
241
+    },
242
+    {
243
+      title: '首次访问时间',
244
+      dataIndex: 'visitTime',
245
+      key: 'visitTime',
246
+      align: 'center',
247
+      width: '18%',
248
+      render: (x, row) => (
249
+        <>
250
+          <span>{ row.visitTime && moment(row.visitTime).format('YYYY-MM-DD hh:mm:ss') }</span>
251
+        </>
252
+      ),
253
+    },
254
+    {
255
+      title: '离开时间',
256
+      dataIndex: 'leaveTime',
257
+      key: 'leaveTime',
258
+      width: '18%',
259
+      align: 'center',
260
+      render: (x, row) => (
261
+        <>
262
+          <span>{ row.leaveTime && moment(row.leaveTime).format('YYYY-MM-DD hh:mm:ss') }</span>
263
+        </>
264
+      ),
265
+    },
266
+  ]
267
+
268
+
269
+  return (
270
+    <>
271
+      <div>
272
+        {/* <p onClick={() => router.push('/indexEcharts/userBehavior')} style={{cursor: 'pointer'}}> */}
273
+          <span style={{ borderBottom: '1px solid #f02d40', color: '#333', fontSize: '0.12rem', fontWeight: '600' }}>用户行为</span>
274
+          {!props.BuildSelectHide && <span style={{ fontSize: '0.09rem', color: '#888', marginLeft: '0.06rem' }}>最近七天</span>}
275
+        {/* </p> */}
276
+        <div style={{ float: 'right', marginTop: '-40px', marginBottom: '20px' }}>
277
+          {!props.BuildSelectHide && <BuildSelect slot="action" onChange={(e => handleBuildingChange(e))} value={buildingId}></BuildSelect>}
278
+        </div>
279
+        <EChart options={options} style={style} />
280
+        {false &&
281
+          <Table rowKey="UserBehaviorTwo" dataSource={recordList} columns={columns} pagination={false} rowKey="userbehavior" scroll={{ y: 500 }} />
282
+        }
283
+      </div>
284
+      <EventcoModal visibleData={visibleData} onSuccess={() => onSuccess()} />
285
+    </>
286
+  )
287
+}
288
+
289
+export default UserBehaviorIndex

+ 1
- 7
src/pages/indexEcharts/components/UserConversion.jsx Datei anzeigen

@@ -79,13 +79,7 @@ const UserSource = props => {
79 79
       radius: ['34%', '52%'],
80 80
       avoidLabelOverlap: false,
81 81
       label: {
82
-        normal: {
83
-          show: false,
84
-          position: 'center',
85
-          color: '#666666',
86
-          fontSize: 22,
87
-          formatter: '{d}%'
88
-        },
82
+        formatter: '{b} {@value}',
89 83
         emphasis: {
90 84
           show: true,
91 85
           formatter: '{d}%',

+ 3
- 0
src/pages/indexEcharts/components/UserSex.jsx Datei anzeigen

@@ -51,6 +51,9 @@ const UserSource = (props) => {
51 51
       name: '性别比例',
52 52
       center: ['44%', '65%'],
53 53
       radius: ['34%', '52%'],
54
+      label: {
55
+        formatter: '{b} {@value}',
56
+      },
54 57
       data:
55 58
         dataset,
56 59
     }

+ 20
- 12
src/pages/indexEcharts/components/UserSource.jsx Datei anzeigen

@@ -16,7 +16,9 @@ const formatDate = (start, end) => {
16 16
 }
17 17
 
18 18
 const UserSource = (props) => {
19
-  const [data, setData] = useState({ records: [] })
19
+  const [xData, setxData] = useState([])
20
+  const [fromData, setFromData] = useState([])
21
+  const [registerData, setRegisterData] = useState([])
20 22
   //柱图
21 23
   useEffect(() => {
22 24
     userResource(formatDate(props.startDate, props.endDate))
@@ -24,21 +26,25 @@ const UserSource = (props) => {
24 26
 
25 27
   function userResource(params) {
26 28
     request({
27
-      ...apis.indexEcharts.userResource,
29
+      ...apis.indexEcharts.selectPersonFrom,
28 30
       params,
29 31
     }).then((data) => {
30
-      setData(data)
31
-      console.log(data,"datadatadatadatavvvdatadatav")
32
+      console.log(data,"222222")
33
+      setxData(data.tdWxDicts.map(x =>  x.sceneAlias))
34
+      setFromData(data.tdWxDicts.map(x => x.fromData))
35
+      setRegisterData(data.tdWxDicts.map(x => x.registerSum))
32 36
       props.onSuccess(data)
33 37
     })
34 38
   }
35 39
 
36
-  const dataset = data || {};
37
-  const source = dataset.columnar || [];
38 40
   const subtitle = '最近7天';
39 41
   const baroptions = {
40 42
     title: { },
41
-    xAxis: { type: 'category' },
43
+    xAxis: { 
44
+              type: 'category',
45
+              data: xData,
46
+              axisLabel: {rotate: 45}
47
+            },
42 48
     legend: {
43 49
       left: '20%',
44 50
       data: ['所有用户', '注册用户']
@@ -51,6 +57,7 @@ const UserSource = (props) => {
51 57
       {
52 58
         type: 'bar', name: '所有用户', datasetIndex: 0,
53 59
         barWidth: 20,
60
+        data: fromData,
54 61
         itemStyle: {
55 62
           normal: {
56 63
             barBorderRadius: [20, 20, 0, 0],
@@ -70,6 +77,7 @@ const UserSource = (props) => {
70 77
         type: 'bar',
71 78
         name: '注册用户',
72 79
         barWidth: 20,
80
+        data: registerData,
73 81
         itemStyle: {
74 82
           normal: {
75 83
             barBorderRadius: [20, 20, 0, 0],
@@ -87,11 +95,11 @@ const UserSource = (props) => {
87 95
 
88 96
       },
89 97
     ],
90
-    dataset: {
91
-      id: 'bar',
92
-      dimensions: ['fromName', 'userCount', 'registered'],
93
-      source: source,
94
-    },
98
+    // dataset: {
99
+    //   id: 'bar',
100
+    //   dimensions: ['fromName', 'userCount', 'registered'],
101
+    //   source: source,
102
+    // },
95 103
   }
96 104
 
97 105
 

+ 139
- 0
src/pages/indexEcharts/components/UserSourceDetail.jsx Datei anzeigen

@@ -0,0 +1,139 @@
1
+import React, { Component, useState, useEffect } from 'react';
2
+import echarts from 'echarts/lib/echarts';
3
+import EChart from '../../../components/EchartsTest/EChart';
4
+import request from '../../../utils/request';
5
+import apis from '../../../services/apis';
6
+import moment from 'moment';
7
+import router from 'umi/router';
8
+import { Table, Select, Row, Col, Menu, Dropdown, Button, Icon, message } from 'antd';
9
+
10
+import styles from '../styles.less'
11
+
12
+const formatDate = (start, end) => {
13
+  const startDate = moment(start).format('YYYY-MM-DDT00:00:00.000') + 'Z'
14
+  const endDate = moment(end).format('YYYY-MM-DDT23:59:59.999') + 'Z'
15
+  return { startDate, endDate }
16
+}
17
+
18
+const UserSourceDetail = (props) => {
19
+  const [xData, setxData] = useState([])
20
+  const [fromData, setFromData] = useState([])
21
+  const [registerData, setRegisterData] = useState([])
22
+  //柱图
23
+  useEffect(() => {
24
+    userResource(formatDate(props.startDate, props.endDate))
25
+  }, [props.startDate, props.endDate])
26
+
27
+  function userResource(params) {
28
+    request({
29
+      ...apis.indexEcharts.selectPersonFromGroupByDay,
30
+      params,
31
+    }).then((data) => {
32
+      setxData(data.map(x =>  x.createTime))
33
+      setFromData(data.map(x => x.fromNum))
34
+      setRegisterData(data.map(x => x.registeredNum))
35
+      props.onSuccess(data)
36
+    })
37
+  }
38
+
39
+  const subtitle = '最近7天';
40
+  const baroptions = {
41
+    title: {
42
+    },
43
+    tooltip: {
44
+        trigger: 'axis'
45
+    },
46
+    legend: {
47
+        data: ['新用户数', '授权注册']
48
+    },
49
+    toolbox: {
50
+        show: true,
51
+        feature: {
52
+            dataZoom: {
53
+                yAxisIndex: 'none'
54
+            },
55
+            dataView: {readOnly: false},
56
+            magicType: {type: ['line', 'bar']},
57
+            restore: {},
58
+            saveAsImage: {}
59
+        }
60
+    },
61
+    xAxis: {
62
+        type: 'category',
63
+        boundaryGap: false,
64
+        data: xData
65
+    },
66
+    yAxis: {
67
+        type: 'value',
68
+        axisLabel: {
69
+            formatter: '{value} °C'
70
+        }
71
+    },
72
+    series: [
73
+        {
74
+            name: '新用户数',
75
+            type: 'line',
76
+            data: fromData,
77
+            markPoint: {
78
+                data: [
79
+                    {type: 'max', name: '最大值'},
80
+                    {type: 'min', name: '最小值'}
81
+                ]
82
+            },
83
+            markLine: {
84
+                data: [
85
+                    {type: 'average', name: '平均值'}
86
+                ]
87
+            }
88
+        },
89
+        {
90
+            name: '授权注册',
91
+            type: 'line',
92
+            data: registerData,
93
+            markPoint: {
94
+                data: [
95
+                    {name: '周最低', value: -2, xAxis: 1, yAxis: -1.5}
96
+                ]
97
+            },
98
+            markLine: {
99
+                data: [
100
+                    {type: 'average', name: '平均值'},
101
+                    [{
102
+                        symbol: 'none',
103
+                        x: '90%',
104
+                        yAxis: 'max'
105
+                    }, {
106
+                        symbol: 'circle',
107
+                        label: {
108
+                            position: 'start',
109
+                            formatter: '最大值'
110
+                        },
111
+                        type: 'max',
112
+                        name: '最高点'
113
+                    }]
114
+                ]
115
+            }
116
+        }
117
+    ]
118
+}
119
+
120
+  const barstyle = {
121
+    width: '100%',
122
+    height: '480px',
123
+    minWidth: '400px'
124
+  }
125
+
126
+  return (
127
+    <>
128
+      <div>
129
+      <p onClick={()=>router.push('/indexEcharts/userSource')}><span style={{borderBottom:'1px solid #f02d40',cursor: 'pointer',color:'#333',fontSize:'0.12rem',fontWeight:'600'}}>用户来源</span> {!props.BuildSelectHide && <span style={{ fontSize: '0.09rem', color: '#888', marginLeft: '0.06rem' }}>最近七天</span>}</p>
130
+
131
+        <EChart options={baroptions} style={barstyle} />
132
+
133
+      </div>
134
+
135
+    </>
136
+  )
137
+}
138
+
139
+export default UserSourceDetail;

+ 9
- 14
src/pages/indexEcharts/components/UserSourcepie.jsx Datei anzeigen

@@ -45,7 +45,7 @@ const UserSource = (props) => {
45 45
     legend: {
46 46
       orient: 'vertical',
47 47
       x: 'left',
48
-      data: ['来源置业顾问', '来源全民经纪人', '自主进入'],
48
+      data: ['来源置业', '来源客户', '其他'],
49 49
     },
50 50
     tooltip: {
51 51
       // trigger: 'item',
@@ -53,17 +53,12 @@ const UserSource = (props) => {
53 53
     },
54 54
     series: [
55 55
       {
56
+        name: '用户来源',
56 57
         type: 'pie',
57
-        center: ['40%', '65%'],
58
-        radius: ['34%', '52%'],
58
+        center: ['50%', '65%'],
59
+        radius: ['25%', '40%'],
59 60
         label: {
60
-          normal: {
61
-            show: false,
62
-            position: 'center',
63
-            color: '#666666',
64
-            fontSize: 22,
65
-            formatter: '{d}%'
66
-          },
61
+          formatter: '{b} {@value}',
67 62
           emphasis: {
68 63
             show: true,
69 64
             formatter: '{d}%',
@@ -71,7 +66,7 @@ const UserSource = (props) => {
71 66
               fontSize: '22',
72 67
               fontWeight: 'bold'
73 68
             }
74
-          }
69
+          },
75 70
         },
76 71
       },
77 72
 
@@ -80,9 +75,9 @@ const UserSource = (props) => {
80 75
     dataset: {
81 76
       id: 'pie',
82 77
       source: [
83
-        { '用户来源': '来源置业顾问', value: person_realty_consultant, },
84
-        { '用户来源': '来源全民经纪人', value: person_estate_agent },
85
-        { '用户来源': '自主进入', value: person_null },
78
+        { '用户来源': '来源置业', value: person_realty_consultant, },
79
+        { '用户来源': '来源客户', value: person_estate_agent },
80
+        { '用户来源': '其他', value: person_null },
86 81
       ]
87 82
     },
88 83
   }

+ 2
- 2
src/pages/indexEcharts/index.jsx Datei anzeigen

@@ -6,7 +6,7 @@ import request from '../../utils/request'
6 6
 import apis from '../../services/apis';
7 7
 import UserSource from './components/UserSource.jsx';
8 8
 import UserSourcepie from './components/UserSourcepie.jsx';
9
-import UserBehavior from './components/UserBehavior.jsx';
9
+import UserBehaviorIndex from './components/UserBehaviorIndex.jsx';
10 10
 import UserActive from './components/UserActive';
11 11
 import UserSex from './components/UserSex';
12 12
 import NewUsers from './components/NewUsers'
@@ -56,7 +56,7 @@ const indexEcharts = props => {
56 56
       }
57 57
       {checkData.includes('user_behavior') &&
58 58
         <div className={styles.behavior}>
59
-          <UserBehavior tableShow={false} endDate={endDate} startDate={startDate} dataZoom={false}></UserBehavior>
59
+          <UserBehaviorIndex tableShow={false} endDate={endDate} startDate={startDate} dataZoom={false}></UserBehaviorIndex>
60 60
         </div>
61 61
       }
62 62
       <div style={{ display: 'flex' }}>

+ 55
- 38
src/pages/indexEcharts/userSource.jsx Datei anzeigen

@@ -21,40 +21,44 @@ const { Option } = Select
21 21
 // }
22 22
 
23 23
 let daterange = []
24
-let tableTitle = ['日期']
24
+let tableTitle = ['类型']
25 25
 
26
-const columns = [
26
+let columns = [
27 27
   {
28 28
     title: '日期',
29
-    dataIndex: 'label',
30
-    key: 'label',
31
-    width:'20%'
29
+    dataIndex: 'createTime',
30
+    key: 'createTime',
32 31
   },
33
-  {
34
-    title: '名片分享',
35
-    dataIndex: '名片分享',
36
-    key: '名片分享',
37
-  },
38
-  {
39
-    title: '生成海报',
40
-    dataIndex: '生成海报',
41
-    key: '生成海报',
42
-  },
43
-  {
44
-    title: '小程序搜索',
45
-    dataIndex: '小程序搜索',
46
-    key: '小程序搜索',
47
-  },
48
-  {
49
-    title: '好友分享',
50
-    dataIndex: '好友分享',
51
-    key: '好友分享',
52
-
53
-  },
54
-
55
-
32
+  // {
33
+  //   title: '名片分享',
34
+  //   dataIndex: '名片分享',
35
+  //   key: '名片分享',
36
+  // },
37
+  // {
38
+  //   title: '生成海报',
39
+  //   dataIndex: '生成海报',
40
+  //   key: '生成海报',
41
+  // },
42
+  // {
43
+  //   title: '小程序搜索',
44
+  //   dataIndex: '小程序搜索',
45
+  //   key: '小程序搜索',
46
+  // },
47
+  // {
48
+  //   title: '好友分享',
49
+  //   dataIndex: '好友分享',
50
+  //   key: '好友分享',
51
+  // },
56 52
 ]
57 53
 
54
+// let columns = [
55
+//   {
56
+//     title: '类型',
57
+//     dataIndex: 'value',
58
+//     key: 'value',
59
+//   }
60
+// ]
61
+
58 62
 class Header extends React.Component {
59 63
 
60 64
   constructor(props) {
@@ -72,8 +76,6 @@ class Header extends React.Component {
72 76
     this.setState({ endDate: new Date(), startDate: moment().subtract(7, 'day').toDate() })
73 77
   }
74 78
 
75
-
76
-
77 79
   formatDate = (start, end) => {
78 80
     const tempStartDate = `${moment(start).format('YYYY-MM-DDT00:00:00.000')}Z`
79 81
     const tempEndDate = `${moment(end).format('YYYY-MM-DDT23:59:59.999')}Z`
@@ -111,17 +113,32 @@ class Header extends React.Component {
111 113
 
112 114
   onTabledatas = (e) => {
113 115
     console.log('this.state.userType: ', this.state.userType)
114
-    const data = (e.data || []).reduce((acc, item) => {
115
-      const { date, fromName, count, registered } = item
116
-      const num = this.state.userType === 'registered' ? registered : count
116
+    e.tdWxDicts.map(x => {
117
+        columns = columns.filter(y => y.key != x.sceneType).concat({
118
+          title: x.sceneAlias,
119
+          dataIndex: x.sceneType,
120
+          key: x.sceneType,
121
+        })
122
+    })
123
+    const data = (e.list || []).reduce((acc, item) => {
124
+      const { sceneType, fromNum, registeredNum, createTime } = item
125
+      const num = this.state.userType === 'registered' ? registeredNum : fromNum
126
+      acc[createTime] = { ...acc[createTime], ...item, [`${sceneType}`]: !num ? 0 : num }
127
+      return acc
128
+    }, {})
117 129
 
118
-      tableTitle = tableTitle.indexOf(fromName) > -1 ? tableTitle : tableTitle.concat(fromName) // eslint-disable-line
119
-      acc[date] = { ...acc[date], [`${fromName}`]: !num ? 0 : num }
130
+    const dictData =  e.tdWxDicts.reduce((acc,item,index) => {
131
+      const { sceneType } = item
132
+      acc[sceneType] = 0
133
+      return acc
134
+    },{})
120 135
 
136
+    const tableData = Object.keys(data).map(k => data[k]).reduce((acc,item,index) => {
137
+      acc[index] = { ...dictData, ...item }
121 138
       return acc
122
-    }, {})
139
+    },[])
123 140
 
124
-    this.setState({ tableData: Object.keys(data).map(x => ({ label: x, ...data[x] })) })
141
+    this.setState({ tableData })
125 142
 
126 143
   }
127 144
 
@@ -152,7 +169,7 @@ class Header extends React.Component {
152 169
             Today: [moment(), moment()],
153 170
             'This Month': [moment().startOf('month'), moment().endOf('month')],
154 171
           }}
155
-          showTime
172
+          // showTime
156 173
           // format="YYYY/MM/DD HH:mm:ss"
157 174
           onChange={(_dates, dateStrings) => this.onChangetime(_dates, dateStrings)}
158 175
         />

+ 58
- 6
src/pages/news/list/editNewsList.jsx Datei anzeigen

@@ -7,7 +7,7 @@ import moment from 'moment';
7 7
 import router from 'umi/router';
8 8
 import BuildSelect from '../../../components/SelectButton/BuildSelect'
9 9
 import NewsTypeSelect from '../../../components/SelectButton/NewTypeSelect'
10
-import XForm, { FieldTypes } from '../../../components/XForm';
10
+import  { FieldTypes, createForm } from '../../../components/XForm';
11 11
 import Wangedit from '../../../components/Wangedit/Wangedit'
12 12
 import request from '../../../utils/request'
13 13
 import ImageUploader from '../../../components/XForm/ImageUpload';
@@ -20,6 +20,21 @@ import xiaochengxu from '../../../assets/xiaochengxu.png'
20 20
 
21 21
 const { MonthPicker, RangePicker, WeekPicker } = DatePicker;
22 22
 const { TextArea } = Input;
23
+
24
+let detailVisible = false;
25
+let urlVisible = false;
26
+
27
+const setExtraData = (data) => {
28
+  detailVisible = data.newsDetailType == '1'
29
+  urlVisible = data.newsDetailType == '0'
30
+}
31
+
32
+const handleFormValueChange = (props, changedValues, allValues) => {
33
+    setExtraData(allValues)
34
+}
35
+
36
+const XForm = createForm({ onValuesChange: handleFormValueChange })
37
+
23 38
 /**
24 39
  *
25 40
  *
@@ -30,8 +45,7 @@ const { TextArea } = Input;
30 45
   const [ tab, changeTab ] = useState('basic')
31 46
   const newsId = props.location.query.newsId
32 47
   const [ dynamicData, setDynamicData ] = useState({})
33
-  
34
-  
48
+
35 49
     useEffect(() => {
36 50
       if(newsId){
37 51
         getDynamicData(newsId);
@@ -41,14 +55,26 @@ const { TextArea } = Input;
41 55
   // 查询列表
42 56
   const getDynamicData = (newsId) => {
43 57
     request({ ...apis.news.get, urlData: { id: newsId },}).then((data) => {
44
-        setDynamicData(data)
58
+      setDynamicData(data)
59
+      detailVisible = data.newsDetailType == '1'
60
+      urlVisible = data.newsDetailType == '0'
45 61
     }).catch((err) => {
46 62
       console.log(err)
47 63
       message.info(err.msg || err.message)
48 64
     })
49 65
     }
50 66
 
51
-  
67
+  // const changeRadio = (e) => {
68
+  //   console.log(e.target.value,'eeee')
69
+  //   if(e.target.value == "1"){
70
+  //     detailVisible = true
71
+  //     urlVisible = false
72
+  //   }
73
+  //   if(e.target.value == "0"){
74
+  //     detailVisible = false
75
+  //     urlVisible = true
76
+  //   }
77
+  // }
52 78
 
53 79
   const cancelPage = () =>{
54 80
     router.push({
@@ -103,9 +129,35 @@ const { TextArea } = Input;
103 129
       },
104 130
       {
105 131
         label: '资讯详情',
132
+        name: 'newsDetailType',
133
+        render: <Radio.Group> 
134
+                <Radio value={1}>自定义</Radio>
135
+                <Radio value={0}>公众号链接</Radio>
136
+                </Radio.Group>,
137
+        value: dynamicData.newsDetailType,
138
+        rules: [
139
+          {required: true, message: '请选择资讯类型'},
140
+        ]
141
+      },
142
+      {
143
+        label: '详情内容',
106 144
         name: 'newsDetail',
107 145
         render: <Wangedit />,
146
+        hidden: () => !detailVisible,
108 147
         value: dynamicData.newsDetail,
148
+        rules: [
149
+          {required: true, message: '请输入详情内容'},
150
+        ]
151
+      },
152
+      {
153
+        label: '公众号链接',
154
+        name: 'newsDetail',
155
+        type: FieldTypes.Text,
156
+        hidden: () => !urlVisible,
157
+        value: dynamicData.newsDetail,
158
+        rules: [
159
+          {required: true, message: '请输入公众号链接'},
160
+        ]
109 161
       },
110 162
     ]
111 163
   
@@ -138,7 +190,7 @@ const { TextArea } = Input;
138 190
       }
139 191
     }
140 192
     
141
-    return <XForm onSubmit={handleSubmit} onCancel={cancelPage} fields={fields}></XForm>
193
+    return <XForm onSubmit={handleSubmit} onCancel={cancelPage}  fields={fields}></XForm>
142 194
   }
143 195
   
144 196
   const Poster = (props) => {

+ 1
- 1
src/pages/record/drainage/DrainageVisitRecordList.jsx Datei anzeigen

@@ -83,7 +83,7 @@ const header = props => {
83 83
       dataIndex: 'personType',
84 84
       key: 'personType',
85 85
       align: 'center',
86
-      render: (text, record) => <span>{record.shareName ? record.consultantId === null || record.consultantId === '' ? '普通客户' : '置业顾问' : ''}</span>,
86
+      render: (text, record) => <span>{record.personType === 'Realty Consultant' ? '置业顾问' : '普通客户' }</span>,
87 87
     },
88 88
   ];
89 89
   

+ 8
- 0
src/pages/staff/list/editStaff.jsx Datei anzeigen

@@ -256,6 +256,14 @@ const Edit = (props) => {
256 256
       </Radio.Group>,
257 257
       value: userData.status != null ? userData.status.toString() : "1"
258 258
     },
259
+    {
260
+      label: '权重',
261
+      name: 'weight',
262
+      type: FieldTypes.Number,
263
+      render: <Input type="number" style={{ width: 150}} />,
264
+      value: userData.weight,
265
+      help: '数字越大越靠前',
266
+    },
259 267
   ]
260 268
 
261 269
   console.log('--------->', fields)

+ 1
- 1
src/pages/style/GoodsList.less Datei anzeigen

@@ -11,7 +11,7 @@
11 11
 
12 12
 .imgPerfect {
13 13
   width: 120px;
14
-  height: 67.5px;
14
+  height: 40px;
15 15
 }
16 16
 
17 17
 .imgIndex {

+ 49
- 0
src/services/apis.js Datei anzeigen

@@ -253,6 +253,11 @@ export default {
253 253
       url: `${prefix}/customer/recommend/edit/:id`,
254 254
       action: 'admin.customer.recommend.edit.id.put',
255 255
     },
256
+    consultantAssist: {
257
+      method: 'POST',
258
+      url: `${prefix}/customer/consultant/assist/:id`,
259
+      action: 'admin.customer.consultant.assist.id.put',
260
+    },
256 261
     taPointsRecords: {
257 262
       method: 'GET',
258 263
       url: `${prefix}/mine/taPointsRecords/:id`,
@@ -320,6 +325,16 @@ export default {
320 325
       url: `${prefix}/selectUserResource`,
321 326
       action: 'userStatistics',
322 327
     },
328
+    selectPersonFrom: {
329
+      method: 'GET',
330
+      url: `${prefix}/selectPersonFrom`,
331
+      action: 'userStatistics',
332
+    },
333
+    selectPersonFromGroupByDay: {
334
+      method: 'GET',
335
+      url: `${prefix}/selectPersonFromGroupByDay`,
336
+      action: 'userStatistics',
337
+    },
323 338
     list:{
324 339
       method:'Get',
325 340
       url: `${prefix}/indexStatistical`,
@@ -336,6 +351,11 @@ export default {
336 351
         url: `${prefix}/selectEventAll`,
337 352
         action: 'behaviorIncident',
338 353
       },
354
+      tsUserBehavior: {
355
+        method: 'GET',
356
+        url: `${prefix}/tsUserBehavior`,
357
+        action: 'tsUserBehavior',
358
+      },
339 359
     },
340 360
     bizEvent: {
341 361
       dict: {
@@ -999,6 +1019,13 @@ export default {
999 1019
     action: 'admin.thirdPartyMiniapp.id.get',
1000 1020
   },
1001 1021
  },
1022
+ wxDict: {
1023
+  list: {
1024
+    url: `${prefix}/tdWxDict`,
1025
+    method: 'GET',
1026
+    action: 'admin.tdWxDict.get',
1027
+  },
1028
+ },
1002 1029
  poster: {
1003 1030
   posterTemplate: {
1004 1031
     url: `${prefix}/posterTemplate`,
@@ -1006,6 +1033,28 @@ export default {
1006 1033
     action: 'admin.posterTemplate.get',
1007 1034
   },
1008 1035
  },
1036
+ paorama: {
1037
+  panoramaApartList: {
1038
+    url: `${prefix}/panorama/apartment`,
1039
+    method: 'GET',
1040
+    action: 'admin.panoramaApartList.get',
1041
+  },
1042
+  add: {
1043
+    url: `${prefix}/panorama`,
1044
+    method: 'POST',
1045
+    action: 'admin.panorama.post',
1046
+  },
1047
+  list: {
1048
+    url: `${prefix}/panorama/:id`,
1049
+    method: 'GET',
1050
+    action: 'admin.panorama.id.get',
1051
+  },
1052
+  delete: {
1053
+    method: 'DELETE',
1054
+    url: `${prefix}/panorama/:id`,
1055
+    action: 'admin.panorama.id.delete',
1056
+  }
1057
+ },
1009 1058
  amap: { // 高德地图
1010 1059
    webService: { // Web服务Api ---- 周边搜索
1011 1060
      url: `${amapPrefix}/place/around`,

+ 5
- 5
src/utils/request.js Datei anzeigen

@@ -89,11 +89,11 @@ request.interceptors.response.use(async (response, options) => {
89 89
       const { code, data, message } = await response.clone().json();
90 90
       if (code != 1000) {
91 91
         if (code === 1001) {
92
-          notification.error({
93
-            message: `请登录系统`,
94
-            // description: '请登录系统',
95
-          });
96
-          throw new Error('请登录系统');
92
+          // notification.error({
93
+          //   message: `请登录系统`,
94
+          //   // description: '请登录系统',
95
+          // });
96
+          // throw new Error('请登录系统');
97 97
         } else {
98 98
           notification.error({
99 99
             message: message || '请求错误',