张延森 5 anni fa
parent
commit
ddc4ee9ca6

+ 12
- 2
src/actions/house.js Vedi File

@@ -1,7 +1,17 @@
1
-import { createActionNormal } from '@/utils/redux'
2
-import { ADD_CART, SUB_CART } from '@/constants/house'
1
+import { createAction, createActionNormal } from '@/utils/redux'
2
+import { API_CARDS_LIST } from '@/constants/api'
3
+import { ADD_CART, SUB_CART, ADD_CONSULTANT, CHOOSE_CONSULTANT } from '@/constants/house'
3 4
 
4 5
 // 加入临时车
5 6
 export const dispatchAddCart = payload => createActionNormal({ type: ADD_CART, payload })
6 7
 // 删减临时车
7 8
 export const dispatchSubCart = payload => createActionNormal({ type: SUB_CART, payload })
9
+// 获取置业顾问
10
+export const dispatchGetConsultants = payload => createAction({
11
+  type: ADD_CONSULTANT,
12
+  url: API_CARDS_LIST,
13
+  payload
14
+})
15
+
16
+// 临时选择置业
17
+export const dispatchChooseConsultant = payload => createActionNormal({ type: CHOOSE_CONSULTANT, payload })

+ 6
- 0
src/constants/house.js Vedi File

@@ -4,3 +4,9 @@ export const ADD_CART = 'ADD_CART'
4 4
 
5 5
 // 取消选择-暂存
6 6
 export const SUB_CART = 'SUB_CART'
7
+
8
+// 置业顾问列表
9
+export const ADD_CONSULTANT = 'ADD_CONSULTANT'
10
+
11
+// 临时选择置业
12
+export const CHOOSE_CONSULTANT = 'CHOOSE_CONSULTANT'

+ 31
- 13
src/onlineSelling/components/ConsultantItem/index.js Vedi File

@@ -1,18 +1,36 @@
1
-import { View } from "@tarojs/components";
1
+import { View, Block } from "@tarojs/components";
2 2
 import './index.scss'
3 3
 
4
+export default function ConsultantItem(props) {
4 5
 
5
-export default function CConsultantItem(props) {
6
-
7
-  const { data, style, type } = props
6
+  const { data = {}, style, type } = props
8 7
 
9 8
   return (
10
-    <View className="card-item" style={style}>
11
-      <Image src={data.photo || data.avatar} className='avatar' ></Image>
12
-      <View className="name">{data.name}<Image src={require('@/assets/person/card.png')} className='card' ></Image>{type == 'raiseProfile' && <View className="tip">您的专属置业顾问</View>}</View>
13
-      <View className="phone">{data.phone}</View>
14
-      {!type && <View className="home">主页</View>}
15
-      {type == 'raiseProfile' && <View className="contact">联系我<Text className="right-icon"></Text></View>}
16
-    </View >
17
-  );
18
-}
9
+    <Block>
10
+      {(!data || !data.id) && (<View style="margin-top: 20px; text-align: center">暂无数据</View>)}
11
+      {
12
+        (data && data.id) &&
13
+        (
14
+          <View className="card-item" style={style}>
15
+            <View onClick={() => props.onClick && props.onClick(data)}>
16
+              <Image src={data.photo || data.avatar} className='avatar' ></Image>
17
+              {
18
+                data.name &&
19
+                (
20
+                  <View className="name">
21
+                    {data.name}
22
+                    <Image src={require('@/assets/person/card.png')} className='card' ></Image>
23
+                    {type == 'raiseProfile' && <View className="tip">您的专属置业顾问</View>}
24
+                  </View>
25
+                )
26
+              }
27
+              <View className="phone">{data.phone}</View>
28
+            </View>
29
+            {!type && <View className="home">主页</View>}
30
+            {type == 'raiseProfile' && <View className="contact">联系我<Text className="right-icon"></Text></View>}
31
+          </View >
32
+        )
33
+      }
34
+    </Block>
35
+  )
36
+}

+ 2
- 2
src/onlineSelling/components/RaiseCard/index.js Vedi File

@@ -48,7 +48,7 @@ export default function HouseCard(props) {
48 48
     }
49 49
   }
50 50
   return (
51
-    <View className="housecard" style={props.style} onClick={props.handleCard}>
51
+    <View className="housecard" style={props.style}>
52 52
       <View className="head">
53 53
         {thumb ? <Image className="thumb" src={transferImage((buildingImgList)[0].url)} mode="aspectFit" /> :
54 54
           <Text style="font-size:26rpx;color:#666">暂无户型图</Text>
@@ -56,7 +56,7 @@ export default function HouseCard(props) {
56 56
       </View>
57 57
       <View className="body">
58 58
         {props.type == 'raiseMoney' && <Image src={closeImg} onClick={handleCancel} className="close-img"></Image>}
59
-        <View onClick={toHouseDetail}>
59
+        <View onClick={props.handleCard}>
60 60
           {!props.type && <View className={payStatus == 'paid' ? 'raised badge' : payStatus == 'unpaid' ? 'unraise badge' : 'badge'}>{payStatus == 'paid' ? '已缴费' : payStatus == 'unpaid' ? '未缴费' : '已退费'}</View>}
61 61
           {(props.type == 'raiseProfile' || props.type == 'houseResource') && <View style="color:#FF3C3C" className={status == '1' ? 'raised badge' : status == '2' ? 'unraise badge' : 'badge'}>{status == '1' ? '已锁给我' : '未缴费'}</View>}
62 62
           {props.type == 'raiseProfile' || props.type == 'raiseMoney' && <View style={props.type == 'raiseMoney' ? 'color:#BB9C79;font-size:30rpx' : 'color:#BB9C79;font-size:30rpx;margin-top:-20rpx'}>{apartmentName || ''}</View>}

+ 40
- 14
src/onlineSelling/pages/chooseConsultant/index.js Vedi File

@@ -3,29 +3,55 @@ import { View } from '@tarojs/components'
3 3
 import ConsultantItem from '../../components/ConsultantItem/index'
4 4
 import './index.scss'
5 5
 
6
+import { connect } from '@tarojs/redux'
7
+import * as houseActions from '@/actions/house'
8
+import Blank from '../../components/Blank'
6 9
 
10
+@connect(({ house }) => ({ house }), {...houseActions})
7 11
 export default class ConsultantList extends Component {
8 12
   config = {
9 13
     navigationBarTitleText: '选择置业顾问'
10 14
   }
15
+
16
+  componentDidMount () {
17
+    const { buildingId } = this.$router.params
18
+    const { house } = this.props
19
+
20
+    if (!house.consultantList || !house.consultantList.length) {
21
+      this.props.dispatchGetConsultants({
22
+        buildingId,
23
+        pageSize: 999,
24
+        pageNumber: 1,
25
+      })
26
+    }
27
+  }
28
+
29
+  handleItemClick = (consultant) => {
30
+    console.log('----------------->', consultant)
31
+    this.props.dispatchChooseConsultant(consultant)
32
+
33
+    Taro.navigateBack({ delta: 1 })
34
+  }
11 35
  
12 36
   render() {
13
-    const list = [
14
-      {name:'哈哈哈1',phone:'138541369741',avatar:'http://img5.imgtn.bdimg.com/it/u=3299138037,391931528&fm=26&gp=0.jpg'},
15
-      {name:'哈哈哈2',phone:'131600514722',avatar:'http://pic1.zhimg.com/50/v2-03d53b468e27ddc99f1ffcd17eec9700_hd.jpg'},
16
-      {name:'哈哈哈3',phone:'131600514722',avatar:'http://img0.imgtn.bdimg.com/it/u=3024308660,1355431406&fm=26&gp=0.jpg'},
17
-    ]
37
+    const { house } = this.props
38
+
18 39
     return (
19 40
       <View className="consultantList">
20
-          {
21
-            list.map((item,index) => (
22
-              <ConsultantItem
23
-                data={item}
24
-                key={index+'card'}
25
-                onClick={this.handleItemClick}>
26
-              </ConsultantItem>
27
-            ))
28
-          }    
41
+        {
42
+          (!house.consultantList || !house.consultantList.length) &&
43
+          (<Blank tips="暂无数据"/>)
44
+        }
45
+
46
+        {
47
+          house.consultantList.map((item,index) => (
48
+            <ConsultantItem
49
+              data={item}
50
+              key={index+'card'}
51
+              onClick={this.handleItemClick}>
52
+            </ConsultantItem>
53
+          ))
54
+        }
29 55
       </View>
30 56
     )
31 57
   }

+ 18
- 17
src/onlineSelling/pages/raiseMoney/Captcha/index.js Vedi File

@@ -10,29 +10,30 @@ export default class extends Component {
10 10
   }
11 11
 
12 12
   handleClick = (left) => {
13
+    if (!this.props.require()) return;
13 14
     if (!this.props.countdown) return;
14 15
 
15 16
     // 计时中
16 17
     if (left > 0 && left < this.props.countdown) return;
17
-
18
-    // 开始跑表
19
-    if (this.props.countdown) {
20
-      let s = 0
21
-      this.setState({ sec: s })
22
-
23
-      const tk = setInterval(() => {
24
-        if (s >= this.props.countdown) {
25
-          clearInterval(tk)
26
-          this.setState({ sec: 0 })
27
-        } else {
28
-          s += 1
18
+    
19
+    if (this.props.onClick) {
20
+      this.props.onClick().then(() => {
21
+        // 开始跑表
22
+        if (this.props.countdown) {
23
+          let s = 0
29 24
           this.setState({ sec: s })
25
+    
26
+          const tk = setInterval(() => {
27
+            if (s >= this.props.countdown) {
28
+              clearInterval(tk)
29
+              this.setState({ sec: 0 })
30
+            } else {
31
+              s += 1
32
+              this.setState({ sec: s })
33
+            }
34
+          }, 1000)
30 35
         }
31
-      }, 1000)
32
-
33
-      if (this.props.onClick) {
34
-        this.props.onClick()
35
-      }
36
+      })
36 37
     }
37 38
   }
38 39
 

+ 131
- 18
src/onlineSelling/pages/raiseMoney/index.js Vedi File

@@ -11,6 +11,8 @@ import NextStep from './NextStep'
11 11
 import { fetch, apis } from '@/utils/request'
12 12
 import Captcha from './Captcha'
13 13
 import { raiseOrder } from '@/services/project'
14
+import { getCodeMessage } from '@/services/getCode'
15
+import { checkIDCard, isEmpty } from '@/utils/tools'
14 16
 
15 17
 const getRaiseProfile = ({raiseId, salesBatchId, personId}) => {
16 18
   const queryString = [
@@ -23,7 +25,7 @@ const getRaiseProfile = ({raiseId, salesBatchId, personId}) => {
23 25
 }
24 26
 
25 27
 const saveRaiseRecord = (record, raise) => {
26
-  const data = {
28
+  const payload = {
27 29
     ...record,
28 30
     raiseId: raise.raiseId,
29 31
     orgId: raise.orgId,
@@ -31,8 +33,7 @@ const saveRaiseRecord = (record, raise) => {
31 33
     buildingId: raise.buildingId,
32 34
   }
33 35
 
34
-  return fetch({ url: `${apis.API_SAVE_RAISE_RECORD}`, method: 'POST', data })
35
-  
36
+  return fetch({ url: `${apis.API_SAVE_RAISE_RECORD}`, method: 'POST', payload })  
36 37
 }
37 38
 
38 39
 @connect(({ user, house }) => ({ ...user, house }), { ...houseActions })
@@ -47,6 +48,8 @@ export default class raiseMoney extends Component {
47 48
     record: {},
48 49
   }
49 50
 
51
+  captchaLock = false
52
+
50 53
   // 此页面不支持分享,以及二维码
51 54
   componentWillMount(){
52 55
     ready.queue(() => {
@@ -79,11 +82,21 @@ export default class raiseMoney extends Component {
79 82
   }
80 83
 
81 84
   loadHouseInfo = (houseId, salesBatchId) => {
82
-    const { personId } = this.props.userInfo.person
85
+    const { userInfo, house } = this.props
86
+    const { personId, nickname, idcard, tel, phone } = userInfo.person
83 87
 
84 88
     // 请求认筹信息
85 89
     getRaiseProfile({ salesBatchId, personId }).then(res => {
86
-      this.setState({ raiseProfile: res, record: res.record || {} })
90
+      this.setState({ raiseProfile: res, record: res.record || { personId, name: nickname, idcard, tel: tel || phone } })
91
+
92
+      // 请求置业顾问列表
93
+      if (!house.consultantList || !house.consultantList.length) {
94
+        this.props.dispatchGetConsultants({
95
+          buildingId: res.buildingId,
96
+          pageSize: 999,
97
+          pageNumber: 1,
98
+        })
99
+      }
87 100
     })
88 101
   }
89 102
 
@@ -172,12 +185,39 @@ export default class raiseMoney extends Component {
172 185
     this.setState({
173 186
       record: {
174 187
         ...(this.state.record || {}),
175
-        userId: person.personId,
188
+        personId: person.personId,
176 189
         [`${key}`]: e.detail.value,
177 190
       }
178 191
     })
179 192
   }
180 193
 
194
+  //
195
+  prepareGetCaptcha = () => {
196
+    const { record } = this.state
197
+    const disabled = !!record.raiseRecordId
198
+    if (disabled) return false;
199
+
200
+    if (this.captchaLock) {
201
+      Taro.showToast({
202
+        title: '发送中, 请稍后',
203
+        icon: 'none'
204
+      })
205
+      return false;
206
+    }
207
+
208
+    const { tel } = this.state.record || {}
209
+
210
+    if (isEmpty(tel) || tel.length < 11) {
211
+      Taro.showToast({
212
+        title: '请填写正确的手机号',
213
+        icon: 'none'
214
+      })
215
+      return false;
216
+    }
217
+
218
+    return true
219
+  }
220
+
181 221
   // 获取验证码
182 222
   handleCaptcha = e => {
183 223
     const { tel } = this.state.record || {}
@@ -187,18 +227,38 @@ export default class raiseMoney extends Component {
187 227
         title: '请填写手机号',
188 228
         icon: 'none'
189 229
       })
190
-      return;
230
+      return Promise.reject();
191 231
     }
232
+
233
+    return new Promise((resolve, reject) => {
234
+      this.captchaLock = true
235
+      getCodeMessage(tel).then(() => {
236
+        Taro.showToast({ title: '发送成功' })
237
+        this.captchaLock = false
238
+        resolve()
239
+      }).catch(() => {
240
+        this.captchaLock = false
241
+        reject()
242
+      })
243
+    })
192 244
   }
193 245
 
194 246
   tochooseConsultant() {
247
+    console.log('--------------------------------------')
195 248
     Taro.navigateTo({
196 249
       url: '/onlineSelling/pages/chooseConsultant/index'
197 250
     })
198 251
   }
252
+
253
+  getConsultant = () => {
254
+    const { house } = this.props
255
+    return house.chooseConsultant ||(house.consultantList || []).filter(x => x.mine)[0]
256
+  }
257
+
199 258
   renderProfile() {
200
-    const item = { name: '哈哈哈1', phone: '138541369741', avatar: 'http://img5.imgtn.bdimg.com/it/u=3299138037,391931528&fm=26&gp=0.jpg' }
201 259
     const { record } = this.state
260
+    const consultant = this.getConsultant()
261
+    const disabled = !!record.raiseRecordId
202 262
 
203 263
     return (
204 264
       <ScrollView scrollY className="container2">
@@ -210,40 +270,39 @@ export default class raiseMoney extends Component {
210 270
               <Text style="color:#FF3C3C;font-size:32rpx;margin-right:8rpx">*</Text>
211 271
               姓名
212 272
             </View>
213
-            <Input className='input' placeholder-style="color:#999" value={record.userName} onInput={this.handleInput.bind(this, 'userName')} type='text' placeholder='请填写真实姓名' />
273
+            <Input className='input' placeholder-style="color:#999" disabled={disabled} value={record.name} onInput={this.handleInput.bind(this, 'name')} type='text' placeholder='请填写真实姓名' />
214 274
           </View>
215 275
           <View className="profile-item">
216 276
             <View className="name">
217 277
               <Text style="color:#FF3C3C;font-size:32rpx;margin-right:8rpx">*</Text>
218 278
               身份证号
219 279
             </View>
220
-            <Input className='input' placeholder-style="color:#999" value={record.idcard} onInput={this.handleInput.bind(this, 'idcard')} type='text' placeholder='请填写身份证号' />
280
+            <Input className='input' placeholder-style="color:#999" disabled={disabled} value={record.idcard} onInput={this.handleInput.bind(this, 'idcard')} type='text' placeholder='请填写身份证号' />
221 281
           </View>
222 282
           <View className="profile-item">
223 283
             <View className="name">
224 284
               <Text style="color:#FF3C3C;font-size:32rpx;margin-right:8rpx">*</Text>
225 285
               手机号
226 286
             </View>
227
-            <Input className='input' placeholder-style="color:#999" value={record.tel} onInput={this.handleInput.bind(this, 'tel')} type='text' placeholder='请填写手机号' />
287
+            <Input className='input' placeholder-style="color:#999" disabled={disabled} value={record.tel} type='text' onInput={this.handleInput.bind(this, 'tel')} placeholder='请填写手机号' />
228 288
           </View>
229 289
           <View className="profile-item">
230 290
             <View className="name">
231 291
               <Text style="color:#FF3C3C;font-size:32rpx;margin-right:8rpx">*</Text>
232 292
               验证码
233 293
             </View>
234
-            <Input className='inputCode' placeholder-style="color:#999" onInput={this.handleCaptcha} type='text' placeholder='请填写验证码' />
235
-            <Captcha countdown={5} />
294
+            <Input className='inputCode' placeholder-style="color:#999" type='text' disabled={disabled}  onInput={this.handleInput.bind(this, 'captcha')} placeholder='请填写验证码' />
295
+            <Captcha countdown={5} require={this.prepareGetCaptcha.bind(this)} onClick={this.handleCaptcha} />
236 296
           </View>
237 297
           <View className="profile-flex" style="margin-top:20rpx">
238 298
             <View className="title">我的置业顾问信息</View>
239
-            <View className="choose" onClick={() => this.tochooseConsultant}>选择置业顾问</View>
299
+            <View className="choose" onClick={this.tochooseConsultant}>选择置业顾问</View>
240 300
           </View>
241 301
           <ConsultantItem
242
-            data={item}
302
+            data={consultant}
243 303
             style="padding:30rpx 0;width:690rpx; border-bottom: 1px solid #dcdcdc;"
244 304
             type="raiseMoney"
245
-            onClick={this.handleItemClick}>
246
-          </ConsultantItem>
305
+          />
247 306
         </View>
248 307
       </ScrollView >
249 308
     )
@@ -264,9 +323,63 @@ export default class raiseMoney extends Component {
264 323
     const { record, raiseProfile } = this.state
265 324
     const { house } = this.props
266 325
     const houseList = house.raiseCart.map(x => x.houseId)
326
+    const disabled = !!record.raiseRecordId
327
+    if (disabled) return Promise.resolve();
328
+
329
+    if (isEmpty(record.name)) {
330
+      Taro.showToast({
331
+        title: '姓名不能为空',
332
+        icon: 'none',
333
+      })
334
+
335
+      return Promise.reject()
336
+    }
337
+
338
+    // 校验身份证号
339
+    if (isEmpty(record.idcard) || !checkIDCard(record.idcard)) {
340
+      Taro.showToast({
341
+        title: '身份证号码不合法',
342
+        icon: 'none',
343
+      })
344
+
345
+      return Promise.reject()
346
+    }
347
+    
348
+    // 校验手机号
349
+    if (isEmpty(record.tel) || record.tel < 11) {
350
+      Taro.showToast({
351
+        title: '手机号格式不正确',
352
+        icon: 'none',
353
+      })
354
+    }
355
+    
356
+    // 验证码
357
+    if (isEmpty(record.captcha)) {
358
+      Taro.showToast({
359
+        title: '验证码不能为空',
360
+        icon: 'none',
361
+      })
362
+
363
+      return Promise.reject()
364
+    }
365
+
366
+    // 校验置业
367
+    const consultant = this.getConsultant()
368
+    if (!consultant || !consultant.id) {
369
+      Taro.showToast({
370
+        title: '置业顾问不能为空',
371
+        icon: 'none',
372
+      })
373
+      return Promise.reject()
374
+    }
267 375
 
268 376
     return new Promise((resolve, reject) => {
269
-      saveRaiseRecord({ ...record, houseList}, raiseProfile).then(() => {
377
+      saveRaiseRecord({
378
+        ...record,
379
+        houseList,
380
+        userId: consultant.userId,
381
+        userName: consultant.name,
382
+      }, raiseProfile).then(() => {
270 383
         resolve()
271 384
       })
272 385
     })

+ 19
- 0
src/reducers/house.js Vedi File

@@ -5,11 +5,18 @@
5 5
  import {
6 6
    ADD_CART,
7 7
    SUB_CART,
8
+   ADD_CONSULTANT,
9
+   CHOOSE_CONSULTANT,
8 10
  } from '@/constants/house'
9 11
 
10 12
 const INITIAL_STATE = {
11 13
   // 认筹临时选中房源
12 14
   raiseCart: [],
15
+
16
+  // 置业顾问列表
17
+  consultantList: [],
18
+
19
+  chooseConsultant: undefined,
13 20
 }
14 21
 
15 22
 export default function(state = INITIAL_STATE, action) {
@@ -30,6 +37,18 @@ export default function(state = INITIAL_STATE, action) {
30 37
         raiseCart: state.raiseCart.filter(x => x.houseId !== payload.houseId),
31 38
       };
32 39
 
40
+    case ADD_CONSULTANT:
41
+      return {
42
+        ...state,
43
+        consultantList: payload.records,
44
+      };
45
+
46
+    case CHOOSE_CONSULTANT:
47
+      return {
48
+        ...state,
49
+        chooseConsultant: payload,
50
+      };
51
+
33 52
     default:
34 53
       return state;
35 54
   }

+ 51
- 0
src/utils/tools.js Vedi File

@@ -641,3 +641,54 @@ export function getCanvasConfig(config, params) {
641 641
     ...itemConf,
642 642
   }
643 643
 }
644
+
645
+/**
646
+ * 身份证校验
647
+ * 
648
+ * https://blog.csdn.net/gqzydh/article/details/90295842
649
+ * 
650
+ * @param {*} idcode 
651
+ */
652
+export function checkIDCard(idcode){
653
+  // 加权因子
654
+  var weight_factor = [7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2];
655
+  // 校验码
656
+  var check_code = ['1', '0', 'X' , '9', '8', '7', '6', '5', '4', '3', '2'];
657
+
658
+  var code = idcode + "";
659
+  var last = idcode[17];//最后一位
660
+
661
+  var seventeen = code.substring(0,17);
662
+
663
+  // ISO 7064:1983.MOD 11-2
664
+  // 判断最后一位校验码是否正确
665
+  var arr = seventeen.split("");
666
+  var len = arr.length;
667
+  var num = 0;
668
+  for(var i = 0; i < len; i++){
669
+      num = num + arr[i] * weight_factor[i];
670
+  }
671
+  
672
+  // 获取余数
673
+  var resisue = num%11;
674
+  var last_no = check_code[resisue];
675
+
676
+  // 格式的正则
677
+  // 正则思路
678
+  /*
679
+  第一位不可能是0
680
+  第二位到第六位可以是0-9
681
+  第七位到第十位是年份,所以七八位为19或者20
682
+  十一位和十二位是月份,这两位是01-12之间的数值
683
+  十三位和十四位是日期,是从01-31之间的数值
684
+  十五,十六,十七都是数字0-9
685
+  十八位可能是数字0-9,也可能是X
686
+  */
687
+  var idcard_patter = /^[1-9][0-9]{5}([1][9][0-9]{2}|[2][0][0|1][0-9])([0][1-9]|[1][0|1|2])([0][1-9]|[1|2][0-9]|[3][0|1])[0-9]{3}([0-9]|[X])$/;
688
+
689
+  // 判断格式是否正确
690
+  var format = idcard_patter.test(idcode);
691
+
692
+  // 返回验证结果,校验码和格式同时正确才算是合法的身份证号码
693
+  return last === last_no && format ? true : false;
694
+}