Your Name 3 years ago
parent
commit
695bda2a79

+ 14
- 0
src/components/Cell/index.jsx View File

@@ -0,0 +1,14 @@
1
+import React from 'react'
2
+import { View } from '@tarojs/components'
3
+import './style.scss'
4
+
5
+export default (props) => {
6
+  const { header, footer, children } = props
7
+  return (
8
+    <View className='yz-cell'>
9
+      <View className='yz-cell-header'>{header}</View>
10
+      <View className='yz-cell-body'>{children}</View>
11
+      <View className='yz-cell-footer'>{footer}</View>
12
+    </View>
13
+  )
14
+}

+ 29
- 0
src/components/Cell/style.scss View File

@@ -0,0 +1,29 @@
1
+
2
+.yz-cell {
3
+  width: 100%;
4
+  position: relative;
5
+  display: flex;
6
+  border-bottom: 1px solid #eee;
7
+  padding: 0.5em 1em;
8
+  box-sizing: border-box;
9
+
10
+  &-header, &-footer {
11
+    flex: none;
12
+  }
13
+
14
+  &-header {
15
+    box-sizing: border-box;
16
+    padding-right: .5em;
17
+  }
18
+
19
+  &-footer {
20
+    box-sizing: border-box;
21
+    padding-left: .5em;
22
+  }
23
+
24
+  &-body {
25
+    flex: auto;
26
+    text-align: right;
27
+    box-sizing: border-box;
28
+  }
29
+}

+ 36
- 0
src/components/Tabs/index.jsx View File

@@ -0,0 +1,36 @@
1
+import React, { useState } from 'react'
2
+import { View, ScrollView } from '@tarojs/components'
3
+import './style.scss'
4
+
5
+export default (props) => {
6
+  const { current, tabs, onChange } = props
7
+
8
+  const handleTabClick = (index) => () => {
9
+    if (onChange) {
10
+      onChange(index + 1)
11
+    }
12
+  }
13
+
14
+  return (
15
+    <View className='yz-tabs'>
16
+      <ScrollView scrollX scrollIntoView={`item-${current}`}>
17
+        <View className='yz-tabs-nav'>
18
+        {
19
+          (tabs||[]).map((item, index) => {
20
+            const activeClass = current === (index + 1) ? 'active' : ''
21
+
22
+            return (
23
+              <View
24
+                key={item}
25
+                id={`item-${index}`}
26
+                className={`yz-tabs-tab ${activeClass}`}
27
+                onClick={handleTabClick(index)}
28
+              >{item}</View>
29
+            )
30
+          })
31
+        }
32
+        </View>
33
+      </ScrollView>
34
+    </View>
35
+  )
36
+}

+ 32
- 0
src/components/Tabs/style.scss View File

@@ -0,0 +1,32 @@
1
+.yz-tabs {
2
+  width: 100%;
3
+  background: #fff;
4
+  position: relative;
5
+  border-top: 1px solid #eee;
6
+  border-bottom: 1px solid #eee;
7
+
8
+  .yz-tabs-nav {
9
+    width: 100%;
10
+    display: flex;
11
+  }
12
+
13
+  .yz-tabs-tab {
14
+    text-align: center;
15
+    flex: auto;
16
+    padding: 0.5em 0;
17
+    outline: none;
18
+    box-sizing: border-box;
19
+    min-width: 20%;
20
+    border-bottom-width: 4px;
21
+    border-bottom-style: solid;
22
+    border-bottom-color: transparent;
23
+
24
+    & + .yz-tabs-tab {
25
+      border-left: 1px solid #eee;
26
+    }
27
+
28
+    &.active {
29
+      border-bottom-color: #193C83;
30
+    }
31
+  }
32
+}

+ 8
- 10
src/pages/mine/customerAnalyse/index.jsx View File

@@ -5,11 +5,13 @@ import { ScrollView } from '@tarojs/components'
5 5
 import { useSelector } from 'react-redux'
6 6
 import { fetch } from '@/utils/request'
7 7
 import { API_SEX_INFO, API_ECHERTS_DAILY } from '@/constants/api'
8
-import echarts from '@/native/ec-canvas/echarts'
8
+import LineChart from './components/LineChart'
9 9
 // import dayjs from 'dayjs'
10 10
 import './index.scss'
11 11
 
12
-export default withLayout(() => {
12
+export default withLayout((props) => {
13
+  const { person } = props
14
+  const PersonId = person.personId
13 15
 
14 16
   const [MenuList] = useState([
15 17
     { name: '新增客户', id: 1 },
@@ -26,14 +28,9 @@ export default withLayout(() => {
26 28
 
27 29
   const [ChartList, setChartList] = useState([])
28 30
   const [SexList, setSexList] = useState([])
29
-  const user = useSelector(state => state.user)
30
-  const [PersonId, setPersonId] = useState(null)
31 31
 
32
-  useEffect(() => {
33
-    if (PersonId !== user.userInfo.person.personId) {
34
-      setPersonId(user.userInfo.person.personId)
35
-    }
36
-  }, [user])
32
+  const [dimensions, setDimensions] = useState(['day', 'customerNum'])
33
+  const [source, setSource] = useState([])
37 34
 
38 35
   useEffect(() => {
39 36
     setChartList([])
@@ -66,6 +63,7 @@ export default withLayout(() => {
66 63
     fetch({ url: `${API_ECHERTS_DAILY}/${CurrentMenuId === 1 ? 'new' : CurrentMenuId === 2 ? 'follow' : 'visite'}`, method: 'get' }).then((res) => {
67 64
       const Arr = (res || []).reverse()
68 65
       setChartList(Arr.map(x => ({ name: x.day, value: x.customerNum })))
66
+      setSource(Arr)
69 67
     })
70 68
   }
71 69
 
@@ -185,7 +183,7 @@ export default withLayout(() => {
185 183
                   }
186 184
                 </view>
187 185
                 <view className='LineChart'>
188
-                  <ec-canvas id='CanvasChart' canvas-id='CanvasChart' ec={{ lazyLoad: true }}></ec-canvas>
186
+                  <LineChart dimensions={dimensions} source={source} />
189 187
                 </view>
190 188
               </view>
191 189
 

+ 134
- 0
src/pages/mine/mortgageCalc/calc.js View File

@@ -0,0 +1,134 @@
1
+
2
+/**
3
+ * 等额本金 摘要
4
+ * @param {*} money 
5
+ * @param {*} year 
6
+ * @param {*} rate 
7
+ */
8
+export function calcBJSummary (moneyW, year, rateB) {
9
+  let money = moneyW * 10000;
10
+  const month = year * 12;
11
+  const rate= rateB / 100;
12
+  let benjin= money / month; //每个月还款本金 
13
+  
14
+  let lxTotal=0;
15
+  let mostlx = 0; // 最高利息
16
+  let emTotal = 0; // 最高月付
17
+  for(let i = 0; i < month; i += 1) {
18
+    const lixi = (money * rate / 12); //每月还款利息 
19
+    money -= benjin; //剩余本金
20
+    lxTotal += lixi; //利息总额
21
+
22
+    if(i === 0) {
23
+      mostlx = lixi;
24
+      emTotal = benjin + mostlx;
25
+    }
26
+  }
27
+
28
+  return {
29
+    totalRate: parseFloat(lxTotal/10000).toFixed(2),
30
+    totalMoney: parseFloat((moneyW*10000+lxTotal)/10000).toFixed(2),
31
+    rateOfFirstMonth: parseFloat(mostlx/10000).toFixed(2),
32
+    moneyOfFirstMonth: parseFloat(emTotal/10000).toFixed(2),
33
+  }
34
+}
35
+
36
+/**
37
+ * 等额本金 列表
38
+ * @param {*} moneyW 
39
+ * @param {*} year 
40
+ * @param {*} rateB 
41
+ * @returns 
42
+ */
43
+export function calcBJList (moneyW, year, rateB) {
44
+  let money = moneyW * 10000;
45
+  const month = year * 12;
46
+  const rate = rateB / 100;
47
+  const benjin = money / month; //每个月还款本金 
48
+
49
+  const result = [];
50
+  for(let i = 0; i < month; i += 1) {
51
+    const lx = (money * rate / 12); // 每月还款利息
52
+    money -= benjin; //剩余本金
53
+
54
+    result.push({
55
+      benjin: parseFloat(benjin).toFixed(2),
56
+      benxi: parseFloat(benjin +lx).toFixed(2),
57
+      rate: parseFloat(lx).toFixed(2),
58
+      left: parseFloat(money).toFixed(2),
59
+    })
60
+  }
61
+
62
+  if (result.length > 0) {
63
+    // 修改最后一条记录的剩余金额为0
64
+    result[result.length - 1].left = 0
65
+  }
66
+
67
+  return result;
68
+}
69
+
70
+/**
71
+ * 等额本息 摘要
72
+ * @param {*} moneyW 
73
+ * @param {*} year 
74
+ * @param {*} rateB 
75
+ */
76
+export function calcBXSummary (moneyW, year, rateB) {
77
+  let money = moneyW*10000;
78
+  const month = year * 12;
79
+  const rate = rateB/100;
80
+  const emTotal = (money * rate / 12 * Math.pow(1 + rate / 12, month) / (Math.pow(1 + rate/ 12, month) - 1)); //每月还款金额
81
+
82
+  let lxTotal = 0; //总利息
83
+  let mostlx = 0;
84
+  for (var i = 0; i < month; i+=1) {
85
+      const lx=(money * rate / 12);  //每月还款利息 
86
+      if(i===0) mostlx=lx; //最高利息
87
+      const em = emTotal-lx; //每月还款本金 
88
+      money -= em;
89
+      lxTotal+=lx;
90
+  }
91
+
92
+  return {
93
+    totalRate: parseFloat(lxTotal/10000).toFixed(2),
94
+    totalMoney: parseFloat((moneyW*10000+lxTotal)/10000).toFixed(2),
95
+    rateOfFirstMonth: parseFloat(mostlx/10000).toFixed(2),
96
+    moneyOfFirstMonth: parseFloat(emTotal/10000).toFixed(2),
97
+  }
98
+}
99
+
100
+/**
101
+ * 等额本息 列表
102
+ * @param {*} moneyW 
103
+ * @param {*} year 
104
+ * @param {*} rateB 
105
+ * @returns 
106
+ */
107
+export function calcBXList (moneyW, year, rateB) {
108
+  let money = moneyW*10000;
109
+  const month = year * 12;
110
+  const rate = rateB/100;
111
+  const emTotal = (money * rate / 12 * Math.pow(1 + rate / 12, month) / (Math.pow(1 + rate/ 12, month) - 1)); //每月还款金额
112
+
113
+  const result = [];
114
+  for(let i = 0; i < month; i += 1) {
115
+    const lx=(money * rate / 12);  //每月还款利息 
116
+    const em = emTotal-lx; //每月还款本金 
117
+    money = money - em;
118
+
119
+
120
+    result.push({
121
+      benjin: parseFloat(em).toFixed(2),
122
+      benxi: parseFloat(em +lx).toFixed(2),
123
+      rate: parseFloat(lx).toFixed(2),
124
+      left: parseFloat(money).toFixed(2),
125
+    })
126
+  }
127
+
128
+  if (result.length > 0) {
129
+    // 修改最后一条记录的剩余金额为0
130
+    result[result.length - 1].left = 0
131
+  }
132
+
133
+  return result;
134
+}

+ 34
- 0
src/pages/mine/mortgageCalc/components/GJJForm.jsx View File

@@ -0,0 +1,34 @@
1
+import React, { useEffect, useState } from 'react'
2
+import Cell from '@/components/Cell'
3
+
4
+export default (props) => {
5
+  const { onChange } = props
6
+
7
+  const [money, setMoney] = useState()
8
+  const [year, setYear] = useState(20)
9
+  const [rate, setRate] = useState(3.25)
10
+
11
+  useEffect(() => {
12
+    if (onChange) {
13
+      onChange({
14
+        money: money||0 - 0,
15
+        year: year||0 - 0,
16
+        rate: rate||0 - 0,
17
+      })
18
+    }
19
+  }, [money, year, rate, onChange])
20
+
21
+  return (
22
+    <view>
23
+      <Cell header='贷款总额 : ' footer='万元'>
24
+        <input type='digit' placeholder='请填写金额' value={money} onInput={e => setMoney(e.detail.value)} />
25
+      </Cell>
26
+      <Cell header='贷款年限 : ' footer='年'>
27
+        <slider min={10} max={30} showValue activeColor='#193C83' value={year} onChange={e => setYear(e.detail.value)} />
28
+      </Cell>
29
+      <Cell header='贷款利率 : ' footer='%'>
30
+        <input type='digit' placeholder='请输入利率' value={rate} onInput={e => setRate(e.detail.value)} />
31
+      </Cell>
32
+    </view>
33
+  )
34
+}

+ 46
- 0
src/pages/mine/mortgageCalc/components/SYForm.jsx View File

@@ -0,0 +1,46 @@
1
+import React, { useEffect, useState } from 'react'
2
+import Cell from '@/components/Cell'
3
+
4
+export default (props) => {
5
+  const { onChange } = props
6
+
7
+  const [money, setMoney] = useState()
8
+  const [year, setYear] = useState(20)
9
+  const [LPR, setLPR] = useState(4.65)
10
+  const [add, setAdd] = useState()
11
+  const [rate, setRate] = useState(4.65)
12
+
13
+  useEffect(() => {
14
+    setRate((LPR||0) + (add||0)/100)
15
+  }, [LPR, add])
16
+
17
+  useEffect(() => {
18
+    if (onChange) {
19
+      onChange({
20
+        money: money||0 - 0,
21
+        year: year||0 - 0,
22
+        rate: rate||0 - 0,
23
+      })
24
+    }
25
+  }, [money, year, rate, onChange])
26
+
27
+  return (
28
+    <view>
29
+      <Cell header='贷款总额 : ' footer='万元'>
30
+        <input type='digit' placeholder='请填写金额' value={money} onInput={e => setMoney(e.detail.value)} />
31
+      </Cell>
32
+      <Cell header='贷款年限 : ' footer='年'>
33
+        <slider min={10} max={30} showValue activeColor='#193C83' value={year} onChange={e => setYear(e.detail.value)} />
34
+      </Cell>
35
+      <Cell header='LPR : ' footer='%'>
36
+        <input type='digit' placeholder='请输入LPR' value={LPR} onInput={e => setLPR(e.detail.value)} />
37
+      </Cell>
38
+      <Cell header='基点 : ' footer='BP‱'>
39
+        <input type='digit' placeholder='请输入基点' value={add} onInput={e => setAdd(e.detail.value)} />
40
+      </Cell>
41
+      <Cell header='贷款利率 : ' footer='%'>
42
+        {`${LPR}% + ${add||''}BP‱ = ${rate}`}
43
+      </Cell>
44
+    </view>
45
+  )
46
+}

+ 92
- 55
src/pages/mine/mortgageCalc/index.jsx View File

@@ -1,72 +1,109 @@
1 1
 import { useState, useEffect } from 'react'
2
+import Taro from '@tarojs/taro'
2 3
 import withLayout from '@/layout'
3
-import { ScrollView, Input } from '@tarojs/components'
4
-import '@/assets/css/iconfont.css'
4
+import { RadioGroup, Label, Radio } from '@tarojs/components'
5
+import Tabs from '@/components/Tabs'
6
+import Cell from '@/components/Cell'
7
+import GJJForm from './components/GJJForm'
8
+import SYForm from './components/SYForm'
9
+import * as sdk from './calc'
5 10
 import './index.scss'
6 11
 
7
-export default withLayout(() => {
12
+const tabs = ['商业贷款', '公积金贷款', '组合贷款']
8 13
 
9
-  return (
10
-    <view className='Page mortgageCalc'>
14
+const showError = title => Taro.showToast({ title, icon: 'none'});
11 15
 
12
-      <ScrollView scroll-y>
13
-        <view className='PageContent'>
16
+export default withLayout(() => {
17
+  const [loading, setLoading] = useState(false)
18
+  const [current, setCurrent] = useState(1)
14 19
 
15
-          <text>房产总价/万元</text>
16
-          <view className='FormLine flex-h'>
17
-            <view className='flex-item'>
18
-              <Input placeholder='请输入房产总额'></Input>
19
-            </view>
20
-          </view>
20
+  const [SYFormData, setSYFormData] = useState()
21
+  const [GJJFormData, setGJJFormData] = useState()
21 22
 
22
-          <text>首付比例</text>
23
-          <view className='FormLine flex-h'>
24
-            <view className='flex-item'>
25
-              <text>请选择</text>
26
-            </view>
27
-            <text className='iconfont icon-jiantoudown'></text>
28
-          </view>
23
+  const handleCalc = () => {
24
+    // 校验商业贷款
25
+    if (current !== 2) {
26
+      if (SYFormData.year < 0 || SYFormData.year > 30) {
27
+        showError('商业贷款年限设置不正确');
28
+        return
29
+      }
30
+      // eslint-disable-next-line no-restricted-globals
31
+      if (isNaN(SYFormData.money) || isNaN(SYFormData.rate)) {
32
+        showError('商业贷款金额或利率设置不正确');
33
+        return
34
+      }
35
+    }
36
+    // 校验公积金贷款
37
+    if (current !== 1) {
38
+      if (GJJFormData.year < 0 || GJJFormData.year > 30) {
39
+        showError('公积金贷款年限设置不正确');
40
+        return
41
+      }
42
+      // eslint-disable-next-line no-restricted-globals
43
+      if (isNaN(GJJFormData.money) || isNaN(GJJFormData.rate)) {
44
+        showError('公积金贷款金额或利率设置不正确');
45
+        return
46
+      }
47
+    }
29 48
 
30
-          <text>首付金额/万元</text>
31
-          <view className='FormLine flex-h'>
32
-            <view className='flex-item'>
33
-              <Input placeholder='请输入首付金额/万元'></Input>
34
-            </view>
35
-          </view>
49
+    setLoading(true)
50
+    const syResult ={}
51
+    const gjjResult = {}
52
+    if (current !== 2) {
53
+      syResult.bj = {
54
+        summary: sdk.calcBJSummary(SYFormData.money, SYFormData.year, SYFormData.rate),
55
+        list: sdk.calcBJList(SYFormData.money, SYFormData.year, SYFormData.rate),
56
+      }
57
+      syResult.bx = {
58
+        summary: sdk.calcBXSummary(SYFormData.money, SYFormData.year, SYFormData.rate),
59
+        list: sdk.calcBXList(SYFormData.money, SYFormData.year, SYFormData.rate),
60
+      }
61
+    }
36 62
 
37
-          <text>商业贷款/万元</text>
38
-          <view className='FormLine flex-h'>
39
-            <view className='flex-item'>
40
-              <Input placeholder='请输入商业贷款/万元'></Input>
41
-            </view>
42
-          </view>
63
+    if (current !== 1) {
64
+      gjjResult.bj = {
65
+        summary: sdk.calcBJSummary(GJJFormData.money, GJJFormData.year, GJJFormData.rate),
66
+        list: sdk.calcBJList(GJJFormData.money, GJJFormData.year, GJJFormData.rate),
67
+      }
68
+      gjjResult.bx = {
69
+        summary: sdk.calcBXSummary(GJJFormData.money, GJJFormData.year, GJJFormData.rate),
70
+        list: sdk.calcBXList(GJJFormData.money, GJJFormData.year, GJJFormData.rate),
71
+      }
72
+    }
73
+    setLoading(false)
43 74
 
44
-          <text>贷款年限</text>
45
-          <view className='FormLine flex-h'>
46
-            <view className='flex-item'>
47
-              <slider min="10" max="30" show-value activeColor='#193C83' />
48
-            </view>
49
-          </view>
50
-
51
-          <text>商业贷款利率</text>
52
-          <view className='FormLine flex-h'>
53
-            <view className='flex-item'>
54
-              <text>请选择</text>
55
-            </view>
56
-            <text className='iconfont icon-jiantoudown'></text>
57
-          </view>
75
+    console.log('------商业------>', syResult)
76
+    console.log('------公积金------>', gjjResult)
77
+  }
58 78
 
59
-          <view className='Agree'>
60
-            <text className='iconfont icon-gou'></text>
61
-            <text>使用公积金贷款</text>
79
+  return (
80
+    <view className='Page mortgageCalc'>
81
+      <Tabs tabs={tabs} current={current} onChange={setCurrent} />
82
+      {/* <Cell header='还款方式 : '>
83
+        <RadioGroup>
84
+          <Label><Radio value='debx' /> 等额本息</Label>
85
+          <Label style={{marginLeft: '1em'}}><Radio value='debj' /> 等额本金</Label>
86
+        </RadioGroup>
87
+      </Cell> */}
88
+      {
89
+        current !== 2 && (
90
+          <view className='container'>
91
+            {current === 3 && <view className='title'>商业贷款</view>}
92
+            <SYForm onChange={setSYFormData} />
62 93
           </view>
63
-
64
-          <view className='Btn'>
65
-            <text>计算</text>
94
+        )
95
+      }
96
+      {
97
+        current !== 1 && (
98
+          <view className='container'>
99
+            {current === 3 && <view className='title'>公积金贷款</view>}
100
+            <GJJForm onChange={setGJJFormData} />
66 101
           </view>
67
-
68
-        </view>
69
-      </ScrollView>
102
+        )
103
+      }
104
+      <view className='Btn'>
105
+        <button loading={loading} onClick={handleCalc}>开始计算</button>
106
+      </view>
70 107
 
71 108
     </view>
72 109
   )

+ 30
- 81
src/pages/mine/mortgageCalc/index.scss View File

@@ -1,87 +1,36 @@
1 1
 .Page.mortgageCalc {
2
-  background: #fff;
2
+  background: #fafafa;
3 3
   height: 100vh;
4 4
   width: 100%;
5
-  > scroll-view {
6
-    width: 100%;
7
-    height: 100%;
8
-    .PageContent {
9
-      position: relative;
10
-      overflow: hidden;
11
-      min-height: 100vh;
12
-      > text {
13
-        font-size: 34px;
14
-        font-weight: bold;
15
-        color: #333;
16
-        display: block;
17
-        text-indent: 40px;
18
-        margin-top: 30px;
19
-      }
20
-      > .flex-h {
21
-        padding: 20px 40px;
22
-        border-bottom: 2px solid rgba(0, 0, 0, 0.12);
23
-        margin-top: 10px;
24
-        align-items: center;
25
-        >view {
26
-          > input {
27
-            display: block;
28
-            width: 100%;
29
-            background: none;
30
-            font-size: 26px;
31
-            line-height: 40px;
32
-            height: 40px;
33
-          }
34
-          >text {
35
-            display: block;
36
-            width: 100%;
37
-            background: none;
38
-            font-size: 26px;
39
-            line-height: 40px;
40
-            height: 40px;
41
-          }
42
-        }
43
-        >text {
44
-          font-size: 30px;
45
-        }
46
-      }
47
-      >.Agree {
48
-        font-size: 0;
49
-        white-space: nowrap;
50
-        padding: 0 30px;
51
-        >text {
52
-          display: inline-block;
53
-          vertical-align: middle;
54
-          font-size: 30px;
55
-          font-weight: bold;
56
-          color: #333;
57
-          line-height: 1;
58
-          margin-top: 40px;
59
-          &.iconfont {
60
-            font-weight: normal;
61
-            margin-right: 10px;
62
-            color: #ccc;
63
-            &.active {
64
-              color: #193C83;
65
-            }
66
-          }
67
-        }
68
-      }
69
-      .Btn {
70
-        padding: 40px;
71
-        position: relative;
72
-        overflow: hidden;
73
-        margin-top: 80px;
74
-        >text {
75
-          display: block;
76
-          text-align: center;
77
-          font-size: 32px;
78
-          line-height: 92px;
79
-          background: #193C83;
80
-          color: #fff;
81
-          font-weight: bold;
82
-          border-radius: 92px;
83
-        }
84
-      }
5
+
6
+  .container {
7
+    margin-top: 1em;
8
+    background: #fff;
9
+  }
10
+
11
+  .title {
12
+    padding-left: 1em;
13
+    padding-bottom: .5em;
14
+    background: #fafafa;
15
+  }
16
+
17
+  slider {
18
+    margin-top: 0;
19
+    margin-bottom: 0;
20
+  }
21
+
22
+  .Btn {
23
+    padding: 40px;
24
+    position: relative;
25
+    overflow: hidden;
26
+    > button {
27
+      display: block;
28
+      text-align: center;
29
+      font-size: 32px;
30
+      line-height: 92px;
31
+      background: #193c83;
32
+      color: #fff;
33
+      border-radius: 92px;
85 34
     }
86 35
   }
87 36
 }