Your Name 4 年前
父节点
当前提交
ff02f36378

+ 5
- 0
config/dev.js 查看文件

@@ -3,6 +3,11 @@ module.exports = {
3 3
     NODE_ENV: '"development"'
4 4
   },
5 5
   defineConstants: {
6
+    HOST: '"https://zcloud.njyunzhi.com"', //正式
7
+    WSS_HOST: '"wss://zcloud.njyunzhi.com"',
8
+    OSS_PATH: '"https://zhiyun-image.oss-accelerate.aliyuncs.com/"',
9
+    OSS_FAST_PATH: '"https://zhiyun-image.oss-accelerate.aliyuncs.com/"',
10
+    Version: '"V3.5.29-20201112"'
6 11
   },
7 12
   mini: {},
8 13
   h5: {}

+ 10
- 0
config/index.js 查看文件

@@ -1,3 +1,5 @@
1
+const path = require('path')
2
+
1 3
 const config = {
2 4
   projectName: 'miniapp',
3 5
   date: '2021-5-12',
@@ -12,6 +14,14 @@ const config = {
12 14
   plugins: [],
13 15
   defineConstants: {
14 16
   },
17
+  alias: {
18
+    '@/actions': path.resolve(__dirname, '..', 'src/actions'),
19
+    '@/components': path.resolve(__dirname, '..', 'src/components'),
20
+    '@/layout': path.resolve(__dirname, '..', 'src/layout'),
21
+    '@/constants': path.resolve(__dirname, '..', 'src/constants'),
22
+    '@/reducers': path.resolve(__dirname, '..', 'src/reducers'),
23
+    '@/utils': path.resolve(__dirname, '..', 'src/utils'),
24
+  },
15 25
   copy: {
16 26
     patterns: [
17 27
     ],

+ 5
- 0
config/prod.js 查看文件

@@ -3,6 +3,11 @@ module.exports = {
3 3
     NODE_ENV: '"production"'
4 4
   },
5 5
   defineConstants: {
6
+    HOST: '"https://zcloud.njyunzhi.com"', //正式
7
+    WSS_HOST: '"wss://zcloud.njyunzhi.com"',
8
+    OSS_PATH: '"https://zhiyun-image.oss-accelerate.aliyuncs.com/"',
9
+    OSS_FAST_PATH: '"https://zhiyun-image.oss-accelerate.aliyuncs.com/"',
10
+    Version: '"V3.5.29-20201112"'
6 11
   },
7 12
   mini: {},
8 13
   h5: {

+ 86
- 0
src/actions/user.js 查看文件

@@ -0,0 +1,86 @@
1
+import {
2
+  USER_INFO,
3
+  // USER_QUERY_INFO,
4
+  USER_SIGNIN,
5
+  USER_GOODS,
6
+  UPDATE_USER_INFO
7
+} from '@/constants/user'
8
+
9
+import {
10
+  API_USER_INFO,
11
+  // API_QUERY_USER_INFO,
12
+  API_USER_SIGNIN,
13
+  API_QUERY_CUSTOMER_INFO,
14
+  API_QUERY_MYREPORT,
15
+  API_QUERY_USERINFO_BYID,
16
+  API_QUERY_DOCUMENTVERIFY
17
+} from '@/constants/api'
18
+
19
+import { createAction,createActionNormal } from '@/utils/redux'
20
+
21
+/**
22
+ * 获取用户信息
23
+ * @param {*} payload
24
+ */
25
+export const dispatchUpdateUserInfo = payload => createAction({
26
+  url: API_USER_INFO,
27
+  type: USER_INFO,
28
+  payload,
29
+  method: 'POST'
30
+})
31
+
32
+// export const dispatchQueryUserInfo = payload => createAction({
33
+//   url: API_QUERY_USER_INFO,
34
+//   type: USER_QUERY_INFO,
35
+//   payload
36
+// })
37
+
38
+export const dispatchUpdateUserInfoNew = id => createAction({
39
+  url: `${API_QUERY_USERINFO_BYID}/${id}`,
40
+  type: UPDATE_USER_INFO
41
+})
42
+
43
+
44
+
45
+/**
46
+ * 个人签到
47
+ * @param {*} payload
48
+ */
49
+export const dispatchUserSignin = payload => createAction({
50
+  url: API_USER_SIGNIN,
51
+  type: USER_SIGNIN,
52
+  payload,
53
+  method: 'POST'
54
+})
55
+
56
+
57
+/**
58
+ * 更新我的商品兑换记录
59
+ * @param {*} payload
60
+ */
61
+export const dispatchUserGoods = payload => {
62
+  return dispatch => dispatch({ type: USER_GOODS, payload })
63
+}
64
+
65
+/**
66
+ * 我的客户列表
67
+ * @param {*} payload
68
+ */
69
+export const dispatchCustomerInfo = payload => createAction({
70
+  url: API_QUERY_CUSTOMER_INFO,
71
+  type: '',
72
+  payload,
73
+  method: 'GET'
74
+})
75
+
76
+/**
77
+ * 我报备的客户列表
78
+ * @param {*} payload
79
+ */
80
+export const dispatchMyReport = payload =>createAction({
81
+  url: API_QUERY_MYREPORT,
82
+  type: '',
83
+  payload,
84
+  method: 'GET'
85
+})
86
+

+ 1
- 0
src/constants/activity.js 查看文件

@@ -0,0 +1 @@
1
+export const ACTIVITY_SIGNIN = 'ACTIVITY_SIGNIN'

+ 237
- 0
src/constants/api.js 查看文件

@@ -0,0 +1,237 @@
1
+/**
2
+ * NOTE HOST、HOST_M 是在 config 中通过 defineConstants 配置的
3
+ * 只所以不在代码中直接引用,是因为 eslint 会报 no-undef 的错误,因此用如下方式处理
4
+ */
5
+/* eslint-disable */
6
+export const host = HOST
7
+export const wss_host = WSS_HOST
8
+
9
+/* eslint-enable */
10
+
11
+export const pathname = '/api/wx'
12
+
13
+const resolvePath = api => `${host + pathname}/${api}`
14
+
15
+// common
16
+export const API_PRELOAD = resolvePath('preload')
17
+export const API_QRCODE = resolvePath('qrcode')
18
+export const API_BANNER_LIST = resolvePath('extendContent')
19
+export const API_QUERY_CODE_SCENE = resolvePath('qrcode/scene')
20
+
21
+// user
22
+export const API_USER_LOGIN = resolvePath('login')
23
+export const API_USER_INFO = resolvePath('signup')
24
+export const API_USER_SIGNIN = resolvePath('user/signin')
25
+export const API_USER_PHONE = resolvePath('userPhone')
26
+export const API_QUERY_USER_INFO = resolvePath('user/info')
27
+export const API_QUERY_USERINFO_BYID = resolvePath('onePerson')
28
+export const API_QUERY_CUSTOMER_INFO = resolvePath('customer/myCustomer')
29
+export const API_QUERY_MYREPORT = resolvePath('customer/myReport')
30
+export const API_QUERY_DOCUMENTVERIFY = resolvePath('documentVerify')
31
+
32
+export const API_QUERY_DOCUMENTVERIFY_list = resolvePath('documentVerify/list')
33
+export const API_QUERY_TADRAINGE = resolvePath('taDrainage')
34
+
35
+
36
+// chat
37
+export const API_CHAT_HISTORY = resolvePath('chat/history')
38
+export const API_CHAT_SEND = wss_host + '/wx/chat'
39
+export const API_CHAT_FRIENDS = resolvePath('chat/with')
40
+export const API_UPLOAD_IMAGE = resolvePath('image')
41
+export const API_CHAT_READED = resolvePath('chat/message')
42
+
43
+// card
44
+export const API_CARDS_LIST = resolvePath('cards')
45
+export const API_CARDS_APPLY = resolvePath('cards/apply')
46
+export const API_FORM_ID = resolvePath('formid')
47
+export const API_UPDATE_PHOTO = resolvePath('userInfo/updatePhoto')
48
+
49
+// project
50
+export const API_INDEX_CUSTOMER = resolvePath('indexCustomer')
51
+export const API_ITEMS_LIST = resolvePath('building/list')
52
+export const API_ITEMS_DETAIL = resolvePath('buildingSelectId')
53
+export const API_ITEMS_TYPE = resolvePath('tdBuildingType')
54
+export const API_ACTIVITY_GROUP = resolvePath('helpActivityAndGroup')
55
+export const API_LOCATION_CITYP = resolvePath('location/city')
56
+export const API_TACUSTOMIMG = resolvePath('taCustomImg')
57
+export const API_INDEX_PROJECTS = resolvePath('building/main')
58
+export const API_INDEX_ICONS = resolvePath('taMiniappOrgIcon')
59
+
60
+
61
+
62
+export const API_HOUSE_LIST = resolvePath('listHousingResources')
63
+export const API_PRESELECTION_RECORD = resolvePath('listPreselectionRecord')
64
+export const API_RAISE_CHECK = resolvePath('check/raise')
65
+export const API_RAISE_PROFILE = resolvePath('taRaise')
66
+export const API_SAVE_RAISE_RECORD = resolvePath('taRaiseRecord')
67
+export const API_RATSE_RECORD = resolvePath('taRaiseRecord/list')
68
+export const API_RATSE_DETAIL = resolvePath('taRaiseRecord')
69
+export const API_RATSE_ORDER = resolvePath('unifiedOrder')
70
+export const API_CANCEL_ORDER = resolvePath('cancel')
71
+export const API_HOUSE_DETAIL = resolvePath('getHousingDetailById')
72
+export const API_ADD_PRESELECTION = resolvePath('taPreselectionRecord')
73
+export const API_CANCEL_PRESELECTION = resolvePath('taPreselectionRecordCancel')
74
+export const API_APARTMENT_TYPE = resolvePath('listBuildApartmentBySalesBatchId')
75
+export const API_SALESBATCH_LIST = resolvePath('taSalesBatch/choice')
76
+export const API_NOTICE_INFO = resolvePath('getNoticeInfo')
77
+export const API_SALES_BATCH = resolvePath('taSalesBatch')
78
+export const API_ADDLIVE_NUM = resolvePath('addVisitNum')
79
+export const API_LIVE_DEATIL = resolvePath('taLiveActivity')
80
+// /wx/location/city?location=lon,lat
81
+
82
+//sign
83
+export const API_CONTRACT_USER = resolvePath('contract/user/add')
84
+export const API_CONTRACT_GET = resolvePath('contract/user/get')
85
+export const API_CONTRACT_AUTO = resolvePath('contract/auto')
86
+export const API_CONTRACT_MANUAL = resolvePath('contract/manual')
87
+export const API_CONTRACT_CHECK = resolvePath('contract/check')
88
+
89
+// shop
90
+export const API_GOODS_LIST = resolvePath('goods')
91
+export const API_GOODS_BUILDING = resolvePath('goodsToBuilding')
92
+export const API_GOODS_EXCHANGE = resolvePath('goods/exchange')
93
+export const API_GOODS_EXCHANGE_RECORDS = resolvePath('taPointsExchange')
94
+export const API_POINTS_RECORDS = resolvePath('taPointsRecords')
95
+export const API_POINTS_RULE = resolvePath('tdPointsRules')
96
+export const API_POINTS_ADRESS = resolvePath('person/city')
97
+export const API_GOODS_BELONGS = resolvePath('goodsToBuilding')
98
+
99
+// client
100
+export const API_CLIENT_LIST = resolvePath('customer/recommend/mine')
101
+export const API_RECOMENT_CLIENT = resolvePath('customer/new')
102
+export const API_CLIENT_PROGRESS = resolvePath('customer')
103
+export const API_TYPE_DATA = resolvePath('awesome/dict/recommendcustomer')
104
+
105
+// report
106
+export const API_REPORT_LIST = resolvePath('myReport')
107
+export const API_REPORT_CLIENT = resolvePath('customer/report')
108
+export const API_RECOMMEND_GET = resolvePath('customer/recommend/get')
109
+export const API_RECOMMEND_EDIT = resolvePath('customer/recommend/edit')
110
+
111
+// common
112
+export const API_ITEMS_UV = resolvePath('project/hot')
113
+export const API_CARDS_UV = resolvePath('consultant/hot')
114
+export const API_ACTIVITY_UV = resolvePath('activity/pvNum')
115
+export const API_NEWS_UV = resolvePath('taNews/pvNum')
116
+
117
+export const API_CARDS_LIKE = resolvePath('consultant/like')
118
+export const API_ITEMS_LIKE = resolvePath('project/like')
119
+export const API_ACTIVITY_LIKE = resolvePath('activity/like')
120
+export const API_NEWS_LIKE = resolvePath('news/like')
121
+
122
+export const API_CARDS_SHARE = resolvePath('consultant/share')
123
+export const API_ITEMS_SHARE = resolvePath('project/share')
124
+export const API_ACTIVITY_SHARE = resolvePath('activity/share')
125
+export const API_HELP_SHARE = resolvePath('help/share')
126
+export const API_GROUP_SHARE = resolvePath('group/share')
127
+export const API_HFIVE_SHARE = resolvePath('h5/share')
128
+export const API_NEWS_SHARE = resolvePath('news/share')
129
+export const API_HOUSE_SHARE = resolvePath('houseApp/share')
130
+export const API_HOUSE_POSTER = resolvePath('housePost/share')
131
+export const API_LIVE_SHARE = resolvePath('liveApp/share')
132
+export const API_LIVE_POSTER = resolvePath('livePost/share')
133
+
134
+export const API_INDEX_SHARE = resolvePath('main/share')
135
+
136
+export const API_ACTIVITY_FAVOR = resolvePath('activity/save')
137
+export const API_NEWS_FAVOR = resolvePath('news/save')
138
+export const API_CARD_FAVOR = resolvePath('card/save')
139
+export const API_PROJECT_FAVOR = resolvePath('project/save')
140
+
141
+// 埋点接口
142
+export const API_BURIED_POINT = resolvePath('taPersonVisitRecord')
143
+
144
+
145
+// activity
146
+export const API_ACTIVITY_LIST = resolvePath('buildingDynamiceList')
147
+//拼团
148
+export const API_GROUP_LIST = resolvePath('taShareRecord')
149
+//助力
150
+export const API_HELP_LIST = resolvePath('helpInitiateRecord')
151
+
152
+// save activity
153
+export const API_ACTIVITY_SAVE = resolvePath('save/buildingDynamiceList')
154
+
155
+export const API_ACTIVITY_DETAIL = resolvePath('buildingDynamiceInfo')
156
+// signup
157
+export const API_ACTIVITY_SIGNUP = resolvePath('activityDynamicEnlistAdd')
158
+// signin
159
+export const API_ACTIVITY_SIGNIN = resolvePath('taCheckin/activity')
160
+
161
+// news
162
+export const API_NEWS_LIST = resolvePath('taNews')
163
+
164
+
165
+// feedback
166
+export const API_FEEDBACK_SUBMIT = resolvePath('taCustomerMessage')
167
+
168
+
169
+// getCode
170
+export const API_GET_CODE = resolvePath('captcha')
171
+
172
+// checkCode
173
+export const API_CHECK_CODE = resolvePath('check/captcha')
174
+
175
+
176
+// /wx/registerConsultantOneClick
177
+// registerConsultant
178
+export const API_PUT_REGISTER = resolvePath('registerConsultant')
179
+
180
+// registerConsultantOneClick
181
+export const API_PUT_REGISTERCONSULTANT = resolvePath('registerConsultantOneClick')
182
+
183
+// city
184
+export const API_CITY_LIST = resolvePath('tdCity')
185
+
186
+
187
+// policy
188
+export const API_POLICY_LIST = resolvePath('taPolicy')
189
+
190
+//agent
191
+
192
+export const API_AGENT_LIST = resolvePath('agent/recommend/mine')
193
+
194
+export const API_REGISTER_AGENT = resolvePath('registerAgent')
195
+
196
+// agent change
197
+export const API_AGENT_CHANNEL = resolvePath('customer/recommend/becomeChannel')
198
+
199
+export const API_AGENT_CURRENT = resolvePath('user/current')
200
+
201
+export const API_EDIT_AGENT = resolvePath('editPerson')
202
+
203
+// 活动列表-新
204
+export const API_ACTNEW_LIST = resolvePath('activity/list')
205
+
206
+// 助力
207
+export const API_HELP_DETAIL = resolvePath('helpActivity')
208
+export const API_HELP_CREATE = resolvePath('helpInitiateRecord')
209
+export const API_HELP_FRIEND = resolvePath('helpRecord')
210
+
211
+//签到
212
+export const API_CHECKIN_DETAILS = resolvePath('buildingDynamiceEnlistInfo')
213
+export const API_CHECKIN_ACTIVE = resolvePath('buildingDynamicCheckin')
214
+
215
+// 拼团
216
+export const API_GROUP_DETAIL = resolvePath('taShareActivity')
217
+export const API_GROUP_CREATE = resolvePath('taShareRecord')
218
+export const API_GROUP_JOIN = resolvePath('taShareChildRecord')
219
+
220
+//person
221
+export const API_MY_CUSTOMER = resolvePath('inventory/customer/statistics')
222
+export const API_CUSTOMER_LIST = resolvePath('inventory/customer/statistics')
223
+export const API_CUSTOMER_DETAIL = resolvePath('inventory/customer')
224
+export const API_VISIT_RECORD = resolvePath('taPersonVisitRecord')
225
+export const API_ACTIVITY_LIST_CUSTOMER = resolvePath('customer/activity/list')
226
+export const API_FOLLOW_LIST = resolvePath('taCustomerFollowUpRecord')
227
+export const API_SEX_INFO = resolvePath('genderStatistic')
228
+export const API_ECHERTS_DAILY = resolvePath('customerStatisticDaily')
229
+export const API_ECHERTS_MONTH = resolvePath('customerStatisticMonthly')
230
+export const API_BUILDING_TYPE = resolvePath('getByBuildingIdSelectBuildingProjectType')
231
+export const API_CONSULTANT_VISIT_RECORD = resolvePath('activityVisitRecord')
232
+export const API_MORE_ACTIVITY = resolvePath('visitRecord/activity')
233
+export const API_SHARE_LIST = resolvePath('shareRecords')
234
+export const API_SHARE_INFOLIST = resolvePath('sharePersonInfoList')
235
+export const API_BIND_CUSTOMER = resolvePath('judgeBindCustomer')
236
+// 消息模板
237
+export const API_TEMPLATE_TYPE = resolvePath('template/of/')

+ 5
- 0
src/constants/card.js 查看文件

@@ -0,0 +1,5 @@
1
+export const CARD_INFO = 'CARD_INFO'
2
+export const CARD_LIST = 'CARD_LIST'
3
+export const CARD_LIKE = 'CARD_LIKE'
4
+export const CARD_UNLIKE = 'CARD_UNLIKE'
5
+

+ 2
- 0
src/constants/checkin.js 查看文件

@@ -0,0 +1,2 @@
1
+export const CHECKIN_DETAILS = 'CHECKIN_DETAILS'
2
+export const CHECKIN_ACTIVE = 'CHECKIN_ACTIVE'

+ 2
- 0
src/constants/city.js 查看文件

@@ -0,0 +1,2 @@
1
+export const CITY_LIST = 'CITY_LIST'
2
+export const CITY_SELECTED = 'CITY_SELECTED'

+ 20
- 0
src/constants/common.js 查看文件

@@ -0,0 +1,20 @@
1
+// 通知消息
2
+export const TPL_NOTICE = "notice";
3
+
4
+// 助力消息
5
+export const TPL_HELP = "help-result";
6
+
7
+// 拼团消息
8
+export const TPL_GROUP = "group-result";
9
+
10
+// 预选消息
11
+export const TPL_HOUSE = "house";
12
+
13
+// 签署合同消息
14
+export const TPL_SIGN = "contract-signing";
15
+
16
+// 更新预拉取数据
17
+export const UPDATE_PRELOAD = "UPDATE_PRELOAD";
18
+
19
+// 同步系统信息
20
+export const SYNC_SYSTEMINFO = "SYNC_SYSTEMINFO";

+ 21
- 0
src/constants/house.js 查看文件

@@ -0,0 +1,21 @@
1
+
2
+// 加入选择-暂存
3
+export const ADD_CART = 'ADD_CART'
4
+
5
+// 取消选择-暂存
6
+export const SUB_CART = 'SUB_CART'
7
+
8
+// 所有进入-暂存
9
+export const FLUSH_TO_CART = 'FLUSH_TO_CART'
10
+
11
+// 置业顾问列表
12
+export const ADD_CONSULTANT = 'ADD_CONSULTANT'
13
+
14
+// 临时选择置业
15
+export const CHOOSE_CONSULTANT = 'CHOOSE_CONSULTANT'
16
+
17
+// 获取预选记录
18
+export const MY_PRESELECT_RECORD = 'MY_PRESELECT_RECORD'
19
+
20
+//
21
+export const SET_ONLINE_PROTOCOL = 'SET_ONLINE_PROTOCOL'

+ 1
- 0
src/constants/login.js 查看文件

@@ -0,0 +1 @@
1
+export const USER_LOGIN = 'USER_LOGIN'

+ 4
- 0
src/constants/news.js 查看文件

@@ -0,0 +1,4 @@
1
+export const NEWS_LIST = 'NEWS_LIST'
2
+export const NEWS_DETAIL = 'NEWS_DETAIL'
3
+
4
+

+ 5
- 0
src/constants/project.js 查看文件

@@ -0,0 +1,5 @@
1
+export const PROJECT_LIST = 'PROJECT_LIST'
2
+export const PROJECT_DETAIL = 'PROJECT_DETAIL'
3
+export const PROJECT_SCREEN = 'PROJECT_SCREEN'
4
+export const SET_SCREEN_SHOWED = 'SET_SCREEN_SHOWED'
5
+export const LOCATION_CITY = 'LOCATION_CITY'

+ 7
- 0
src/constants/shop.js 查看文件

@@ -0,0 +1,7 @@
1
+export const GOODS_LIST = 'GOODS_LIST'
2
+export const GOODS_DETAIL = 'GOODS_DETAIL'
3
+export const GOODS_EXCHANGE = 'GOODS_EXCHANGE'
4
+export const GOODS_EXCHANGE_RECORDS = 'GOODS_EXCHANGE_RECORDS'
5
+export const POINTS_RECORDS = 'POINTS_RECORDS'
6
+export const POINTS_RULE = 'POINTS_RULE'
7
+export const POINTS_ADRESS = 'POINTS_ADRESS'

+ 18
- 0
src/constants/user.js 查看文件

@@ -0,0 +1,18 @@
1
+export const USER_INFO = 'USER_INFO'
2
+// export const USER_QUERY_INFO = 'USER_QUERY_INFO'
3
+export const USER_SIGNIN = 'USER_SIGNIN'
4
+export const USER_GOODS = 'USER_GOODS'
5
+export const ASSIGN_UNREADNUM = 'ASSIGN_UNREADNUM'
6
+export const INCREASE_UNREADNUM = 'INCREASE_UNREADNUM'
7
+export const DECREASE_UNREADNUM = 'DECREASE_UNREADNUM'
8
+export const API_QUERY_CUSTOMER_INFO = 'API_QUERY_CUSTOMER_INFO'
9
+export const API_QUERY_MYREPORT = 'API_QUERY_MYREPORT'
10
+export const UPDATE_USER_INFO = 'UPDATE_USER_INFO'
11
+
12
+export const ROLE_CODE = {
13
+  CONSULTANT: 'Realty Consultant', // 置业顾问
14
+  ESTATE_AGENT: 'estate agent', // 独立经纪人
15
+  CHANNEL_AGENT: 'channel agent', // 渠道经纪人
16
+  DRIFT: 'drift', // 游客
17
+  CUSTOMER: 'customer' // 客户
18
+}

+ 21
- 0
src/layout/Basic.jsx 查看文件

@@ -0,0 +1,21 @@
1
+import { Component } from 'react'
2
+import { connect } from 'react-redux'
3
+import { View } from '@tarojs/components'
4
+
5
+import './style.scss'
6
+
7
+@connect(({ user }) => ({...user}))
8
+class BasicLayout extends Component {
9
+  render () {
10
+    const person = this.props.person
11
+    const loading = !person || !person.personId
12
+
13
+    return (
14
+      <View className='demo'>
15
+      </View>
16
+    )
17
+  }
18
+}
19
+
20
+export default BasicLayout
21
+

+ 40
- 0
src/layout/style.scss 查看文件

@@ -0,0 +1,40 @@
1
+.demo {
2
+  width: 100px;
3
+  height: 100px;
4
+  border-radius: 100%;
5
+  animation: demo1 1.6s linear infinite;
6
+  border: 4px solid #f3f3f3;
7
+  margin: 100px auto;
8
+  display: inline-block;
9
+}
10
+
11
+@keyframes demo1 {
12
+  0% {
13
+    border-left-color: red;
14
+    transform:rotate(0deg);
15
+  }
16
+
17
+  25% {
18
+    border-left-color: red;
19
+    border-top-color: red;
20
+    transform:rotate(180deg);
21
+  }
22
+
23
+  50% {
24
+    border-left-color: red;
25
+    border-top-color: red;
26
+    border-bottom-color: red;
27
+    transform:rotate(180deg);
28
+  }
29
+
30
+  75% {
31
+    border-left-color: red;
32
+    border-top-color: red;
33
+    transform:rotate(180deg);
34
+  }
35
+
36
+  100% {
37
+    border-left-color: red;
38
+    transform:rotate(360deg);
39
+  }
40
+}

+ 2
- 36
src/pages/index/index.jsx 查看文件

@@ -1,45 +1,11 @@
1 1
 import { Component } from 'react'
2
-import { connect } from 'react-redux'
3
-import { View, Button, Text } from '@tarojs/components'
4 2
 
5
-import { add, minus, asyncAdd } from '../../actions/counter'
3
+import BasicLayout from '@/layout/Basic'
6 4
 
7
-import './index.scss'
8
-
9
-
10
-@connect(({ counter }) => ({
11
-  counter
12
-}), (dispatch) => ({
13
-  add () {
14
-    dispatch(add())
15
-  },
16
-  dec () {
17
-    dispatch(minus())
18
-  },
19
-  asyncAdd () {
20
-    dispatch(asyncAdd())
21
-  }
22
-}))
23 5
 class Index extends Component {
24
-  componentWillReceiveProps (nextProps) {
25
-    console.log(this.props, nextProps)
26
-  }
27
-
28
-  componentWillUnmount () { }
29
-
30
-  componentDidShow () { }
31
-
32
-  componentDidHide () { }
33
-
34 6
   render () {
35 7
     return (
36
-      <View className='index'>
37
-        <Button className='add_btn' onClick={this.props.add}>+</Button>
38
-        <Button className='dec_btn' onClick={this.props.dec}>-</Button>
39
-        <Button className='dec_btn' onClick={this.props.asyncAdd}>async</Button>
40
-        <View><Text>{this.props.counter.num}</Text></View>
41
-        <View><Text>Hello, World</Text></View>
42
-      </View>
8
+      <BasicLayout />
43 9
     )
44 10
   }
45 11
 }

+ 3
- 1
src/reducers/index.js 查看文件

@@ -1,6 +1,8 @@
1 1
 import { combineReducers } from 'redux'
2 2
 import counter from './counter'
3
+import user from './user'
3 4
 
4 5
 export default combineReducers({
5
-  counter
6
+  counter,
7
+  user,
6 8
 })

+ 127
- 0
src/reducers/user.js 查看文件

@@ -0,0 +1,127 @@
1
+import { mergeNotNull } from '@/utils/tools'
2
+
3
+import {
4
+  USER_INFO,
5
+  // USER_QUERY_INFO,
6
+  USER_SIGNIN,
7
+  ASSIGN_UNREADNUM,
8
+  INCREASE_UNREADNUM,
9
+  DECREASE_UNREADNUM,
10
+  UPDATE_USER_INFO,
11
+} from '@/constants/user'
12
+
13
+const INITIAL_STATE = {
14
+  userInfo: {
15
+    person: {},
16
+    extraInfo: {},
17
+    miniApp: {},
18
+  },
19
+  unReadNum: 0
20
+}
21
+
22
+export default function user(state = INITIAL_STATE, action) {
23
+  switch (action.type) {
24
+    case USER_INFO: {
25
+      if (!(state.userInfo || {}).person) {
26
+        return {
27
+          ...state,
28
+          userInfo: {
29
+            ...action.payload
30
+          }
31
+        }
32
+      } else {
33
+        const { person, extraInfo, miniApp } = action.payload || {}
34
+        const newPerson = mergeNotNull(state.userInfo.person, person)
35
+        const newExtraInfo = mergeNotNull(state.userInfo.extraInfo, extraInfo)
36
+        const newMiniApp = mergeNotNull(state.userInfo.miniApp, miniApp)
37
+        return {
38
+          ...state,
39
+          userInfo: {
40
+            ...state.userInfo,
41
+            ...action.payload,
42
+            person: newPerson,
43
+            extraInfo: newExtraInfo,
44
+            miniApp: newMiniApp,
45
+          }
46
+        }
47
+      }
48
+    }
49
+    // case USER_QUERY_INFO: {
50
+    //   const { person } = state.userInfo
51
+    //   return {
52
+    //     ...state,
53
+    //     userInfo: {
54
+    //       person,
55
+    //       extraInfo: {
56
+    //         ...action.payload
57
+    //       }
58
+
59
+    //     }
60
+    //   }
61
+    // }
62
+    case UPDATE_USER_INFO: {
63
+      // debugger
64
+      const { person, extraInfo, miniApp = {} } = state.userInfo
65
+      const newPerson = mergeNotNull(person, action.payload)
66
+
67
+      return {
68
+        ...state,
69
+        userInfo: {
70
+          extraInfo,
71
+          person: newPerson,
72
+          miniApp,
73
+        }
74
+      }
75
+    }
76
+    case USER_SIGNIN: {
77
+      let { person, extraInfo, miniApp = {} } = state.userInfo
78
+      return {
79
+        ...state,
80
+        userInfo: {
81
+          person: {
82
+            ...person,
83
+            points: (person.points + 1)
84
+          },
85
+          extraInfo: {
86
+            ...extraInfo,
87
+            havaSigned: 1
88
+          },
89
+          miniApp,
90
+        }
91
+      }
92
+    }
93
+    case ASSIGN_UNREADNUM: {
94
+      let { unReadNum } = action.payload
95
+      return {
96
+        ...state,
97
+        unReadNum
98
+      }
99
+    }
100
+    case INCREASE_UNREADNUM: {
101
+      let { unReadNum } = state
102
+      return {
103
+        ...state,
104
+        unReadNum: unReadNum + 1
105
+      }
106
+    }
107
+    case DECREASE_UNREADNUM: {
108
+      let { unReadNum } = state
109
+
110
+      if (action.payload === undefined) {
111
+        unReadNum = 0
112
+      } else {
113
+        unReadNum = unReadNum - action.payload
114
+        if (unReadNum < 0) {
115
+          unReadNum = 0
116
+        }
117
+      }
118
+
119
+      return {
120
+        ...state,
121
+        unReadNum,
122
+      }
123
+    }
124
+    default:
125
+      return state
126
+  }
127
+}

+ 30
- 0
src/utils/redux.js 查看文件

@@ -0,0 +1,30 @@
1
+/**
2
+ * 适当封装 Redux,简化调用
3
+ */
4
+/* eslint-disable import/prefer-default-export */
5
+import { fetch } from './request'
6
+
7
+export function createAction(options) {
8
+  const { url, payload, method, fetchOptions, cb, type } = options
9
+  return (dispatch) => {
10
+    try {
11
+      return fetch({ url, payload, method, ...fetchOptions }).then((res) => {
12
+        dispatch({ type, payload: cb ? cb(res) : res })
13
+        return res
14
+      })
15
+    } catch (e) {
16
+      throw e
17
+    }
18
+  }
19
+}
20
+
21
+// 跟后端不交互
22
+export function createActionNormal(options) {
23
+  const { payload, type } = options
24
+  return (dispatch) => {
25
+    return new Promise(resolve => {
26
+      dispatch({ type, payload })
27
+      resolve(payload)
28
+    })
29
+  }
30
+}

+ 173
- 0
src/utils/request.js 查看文件

@@ -0,0 +1,173 @@
1
+import Taro from '@tarojs/taro'
2
+import * as apis from '@/constants/api'
3
+// import login from '@/utils/login'
4
+import rtLog from './rtLog'
5
+
6
+const CODE_SUCCESS = 1000
7
+const CODE_UNLOGIN = 1001
8
+const CODE_AUTH_EXPIRED = 1002
9
+const accountInfo = wx.getAccountInfoSync();
10
+const appId = accountInfo.miniProgram.appId
11
+
12
+export { apis };
13
+
14
+export const getStorage = function (key) {
15
+  return Taro.getStorage({ key }).then(res => res.data).catch(() => '')
16
+}
17
+
18
+export const setStorage = function (key, data) {
19
+  return Taro.setStorage({ key, data })
20
+}
21
+
22
+/**
23
+ * 简易封装网络请求
24
+ * // NOTE 需要注意 RN 不支持 *StorageSync,此处用 async/await 解决
25
+ * @param {*} options
26
+ */
27
+export const fetch = async (options) => {
28
+  const opts = optionBuilder(options)
29
+
30
+  return Taro.request(opts[0])
31
+    .then((res) => reqSuccess(res, opts))
32
+    .catch((err) => reqFail(err, opts))
33
+}
34
+
35
+export const uploadFiles = async (files) => {
36
+  const uploads = []
37
+  const token = await getStorage('token')
38
+  const authHeader = token ? { 'authorization': `Bearer ${token}` } : {}
39
+  for (var i = 0; i < files.length; i++) {
40
+    uploads[i] = new Promise((resolve, reject) => {
41
+      wx.uploadFile({
42
+        url: apis.API_UPLOAD_IMAGE,
43
+        filePath: files[i],
44
+        header: {
45
+          ...authHeader,
46
+          'appId': appId,
47
+          'x-action': 'miniapp'
48
+        },
49
+        name: 'file',
50
+        success: function (res) {
51
+          // debugger
52
+          const _data = JSON.parse(res.data)
53
+          if (_data.code !== CODE_SUCCESS) {
54
+            reject(new Error(_data.message))
55
+          }
56
+
57
+          resolve(_data.data)
58
+        },
59
+        fail(err) {
60
+          reject(err)
61
+        }
62
+      })
63
+    })
64
+  }
65
+
66
+  return Promise.all(uploads)
67
+}
68
+
69
+export function optionBuilder(options) {
70
+  const { url, payload = {}, method = 'GET', showToast = true, autoLogin = true, header } = options
71
+  const showMessage = Object.prototype.hasOwnProperty.call(payload, 'showToast') ? payload.showToast : showToast
72
+  const consultant = Taro.getStorageSync('consultantId') || ''
73
+  const recommender = Taro.getStorageSync('recommender') || ''
74
+
75
+  return [
76
+    {
77
+      url,
78
+      method,
79
+      data: payload,
80
+      header: {
81
+        'content-type': 'application/json',
82
+        'authorization': `Bearer ${Taro.getStorageSync('token')}`,
83
+        'appId': appId,
84
+        'x-action': 'miniapp',
85
+        'x-version': Version,
86
+        'x-consultant': consultant,
87
+        'x-recommender': recommender,
88
+        ...header || {},
89
+      }
90
+    },
91
+    {
92
+      showMessage,
93
+      autoLogin
94
+    }
95
+  ]
96
+}
97
+
98
+export function reqSuccess(res, opts) {
99
+  const [, settings] = opts || []
100
+  const { showMessage, autoLogin = true } = settings || {}
101
+
102
+  if (res.statusCode === 500) {
103
+    Taro.showToast({
104
+      title: "请检查网络",
105
+      icon: 'none'
106
+    })
107
+    console.error(res)
108
+    throw new Error('请检查网络')
109
+  }
110
+
111
+  const { data, code, message } = typeof res.data === 'string' ? JSON.parse(res.data) : res.data
112
+  if (code === CODE_SUCCESS) {
113
+    return data
114
+  } else {
115
+    const isExpired = (code === CODE_AUTH_EXPIRED || code === CODE_UNLOGIN)
116
+    const defaultMsg = isExpired ? '登录失效' : message
117
+    if (isExpired && autoLogin) {
118
+      // token失效,重新登录后,跳转到首页
119
+      login({}, () => {
120
+        Taro.switchTab({
121
+          url: '/pages/project/index'
122
+        })
123
+      })
124
+    } else {
125
+      if (showMessage) {
126
+        Taro.showToast({
127
+          title: defaultMsg,
128
+          icon: 'none'
129
+        })
130
+      }
131
+    }
132
+    if (showMessage) {
133
+
134
+      Taro.showToast({
135
+        title: message,
136
+        icon: 'none'
137
+      })
138
+    }
139
+
140
+    throw res.data
141
+  }
142
+}
143
+
144
+export function reqFail(err, opts) {
145
+  const [reqParams, settings] = opts || []
146
+  const { showMessage } = settings || {}
147
+
148
+  rtLog.error('[request]', "==================================")
149
+  rtLog.error('[request]', err)
150
+  rtLog.error('[request]', reqParams.url)
151
+  rtLog.error('[request]', wx.getLaunchOptionsSync())
152
+  rtLog.error('[request]', "==================================")
153
+
154
+  let errMessage = `请求失败: ${err.errMsg}`
155
+  if (err.errMsg) {
156
+    errMessage = '请检查网络'
157
+  }
158
+
159
+  if (err.message) {
160
+    console.error('请求失败', err.message)
161
+    errMessage = err.message.indexOf('java') > -1 ? '服务内部错误' : err.message
162
+  }
163
+
164
+  if (showMessage) {
165
+    Taro.showToast({
166
+      title: errMessage,
167
+      icon: 'none',
168
+      duration: 3000
169
+    })
170
+  }
171
+
172
+  throw new Error(errMessage)
173
+}

+ 30
- 0
src/utils/rtLog.js 查看文件

@@ -0,0 +1,30 @@
1
+
2
+let logger = wx.getRealtimeLogManager ? wx.getRealtimeLogManager() : null;
3
+
4
+function info(...args) {
5
+  // 本地打印
6
+  console.info(...args)
7
+
8
+  // 远程提交
9
+  if (logger) {
10
+    logger.info(...args)
11
+  }
12
+}
13
+
14
+function error(...args) {
15
+  console.error(...args)
16
+
17
+  if (logger) {
18
+    logger.error(...args)
19
+  }
20
+}
21
+
22
+function warn(...args) {
23
+  console.warn(...args)
24
+
25
+  if (logger) {
26
+    logger.warn(...args)
27
+  }
28
+}
29
+
30
+export default { info, error, warn }

+ 317
- 0
src/utils/tools.js 查看文件

@@ -0,0 +1,317 @@
1
+import Taro from "@tarojs/taro";
2
+import store from "../store";
3
+import rtLog from "./rtLog";
4
+
5
+/**
6
+ * 是否为空
7
+ * @param {*} o
8
+ */
9
+export function isEmpty(o) {
10
+  if (typeof o === "string") {
11
+    if (!o || o === "null" || o === "undefined") {
12
+      return true;
13
+    }
14
+  }
15
+
16
+  if (typeof o === "object") {
17
+    return !Object.keys(o).length;
18
+  }
19
+
20
+  if (Array.isArray(o)) {
21
+    return !o.length;
22
+  }
23
+
24
+  if (o === null || o === undefined) {
25
+    return true;
26
+  }
27
+
28
+  return false;
29
+}
30
+
31
+export function ifNull(o, def) {
32
+  return isEmpty(o) ? def : o;
33
+}
34
+
35
+/**
36
+ * 是否函数
37
+ * @param {*} f
38
+ */
39
+export function isFunction(f) {
40
+  return typeof f === "function";
41
+}
42
+
43
+/**
44
+ * 是否分享场景(含扫码)
45
+ * @param {*} scene
46
+ */
47
+export function sceneInShare(scene) {
48
+  return (
49
+    [1007, 1008, 1011, 1012, 1013, 1031, 1032, 1036, 1047, 1048, 1049].indexOf(
50
+      scene
51
+    ) > -1
52
+  );
53
+}
54
+
55
+/**
56
+ * 造空数组
57
+ * @param {int}} n
58
+ */
59
+export function times(n) {
60
+  return n > 0 ? "*".repeat(n - 1).split("*") : [];
61
+}
62
+
63
+/**
64
+ * 屏蔽手机中间 4 位
65
+ * @param {*} phone
66
+ */
67
+export function maskPhone(phone) {
68
+  return phone.replace(/^(\d{3})(\d{4})/, "$1****");
69
+}
70
+
71
+/**
72
+ * 将 b 中不为 null 或者 undefined 的值合并到 a 中
73
+ * @param {*} a
74
+ * @param {*} b
75
+ */
76
+export function mergeNotNull(a, b) {
77
+  const bKeys = Object.keys(b || {});
78
+  if (!bKeys.length) {
79
+    return a;
80
+  }
81
+
82
+  let res = { ...(a || {}) };
83
+
84
+  bKeys.forEach(k => {
85
+    const v = b[k];
86
+    if (v === null || v === undefined) {
87
+      return;
88
+    }
89
+
90
+    res[k] = v;
91
+  });
92
+
93
+  return res;
94
+}
95
+
96
+const ossPath = OSS_PATH;
97
+const ossFastPath = OSS_FAST_PATH;
98
+
99
+/**
100
+ *
101
+ * @param {*} img
102
+ * @param {*} quality  仅支持 70,50,30,5
103
+ */
104
+export function getThumbnail(img, quality) {
105
+  if (!img) return img;
106
+
107
+  if (img.indexOf(ossPath) === 0 || img.indexOf(ossFastPath) === 0) {
108
+    return `${img.replace(
109
+      ossPath,
110
+      ossFastPath
111
+    )}?x-oss-process=style/compress${quality || 30}`;
112
+  }
113
+
114
+  return img;
115
+}
116
+
117
+export function resizeImage(img, size) {
118
+  if (!img) return img;
119
+
120
+  if (img.indexOf(ossPath) === 0 || img.indexOf(ossFastPath) === 0) {
121
+    return `${img.replace(
122
+      ossPath,
123
+      ossFastPath
124
+    )}?x-oss-process=style/resize${size || 750}`;
125
+  }
126
+
127
+  return img;
128
+}
129
+
130
+/**
131
+ * 压缩图片 80%, 最大宽度 750
132
+ * @param {*} img
133
+ */
134
+export function transferImage(img) {
135
+  if (!img) return img;
136
+
137
+  if (img.indexOf(ossPath) === 0 || img.indexOf(ossFastPath) === 0) {
138
+    if (store.getState().system.systemInfo.platform !== "ios") {
139
+      // ios 暂时不支持 webp
140
+      return `${img.replace(
141
+        ossPath,
142
+        ossFastPath
143
+      )}?x-oss-process=style/transwebp`;
144
+    }
145
+
146
+    return `${img.replace(
147
+      ossPath,
148
+      ossFastPath
149
+    )}?x-oss-process=image/resize,m_lfit,w_750/quality,Q_80`;
150
+  }
151
+
152
+  return img;
153
+}
154
+
155
+/**
156
+ * 简易版解析 url
157
+ * @param {*} url
158
+ */
159
+export function parseURL(url) {
160
+  if (!url) return undefined;
161
+
162
+  let strTmp;
163
+  let query;
164
+  let hash;
165
+
166
+  const gotHash = url.split("#");
167
+  strTmp = gotHash[0];
168
+  if (gotHash.length > 1) {
169
+    hash = gotHash[1];
170
+  }
171
+
172
+  const gotQuery = strTmp.split("?");
173
+  strTmp = gotQuery[0];
174
+  if (gotQuery.length > 1) {
175
+    query = gotQuery[1];
176
+  }
177
+
178
+  // 小程序只支持 https 开头
179
+  const [_, origin, path] = /(https?:\/\/[^/]+)(.*)/.exec(strTmp);
180
+
181
+  return {
182
+    origin,
183
+    path,
184
+    query,
185
+    hash
186
+  };
187
+}
188
+
189
+/**
190
+ * 解析 queryString   a=b&c=d  ==> { a:b, c:d }
191
+ * @param {*} queryString
192
+ */
193
+export function parseQueryString(queryString) {
194
+  if (!queryString || "?" === queryString) return undefined;
195
+
196
+  const query =
197
+    queryString.indexOf("?") === 0
198
+      ? queryString.replace(/^\?/, "")
199
+      : queryString;
200
+
201
+  return query
202
+    .split("&")
203
+    .filter(x => x)
204
+    .reduce((acc, it) => {
205
+      const [k, v] = it.split("=");
206
+      const val = Object.prototype.hasOwnProperty.call(acc, k)
207
+        ? [...[].concat(acc[k]), v]
208
+        : v;
209
+
210
+      return {
211
+        ...acc,
212
+        [`${k}`]: ifNull(val, undefined)
213
+      };
214
+    }, {});
215
+}
216
+
217
+export function toQueryString(o) {
218
+  const obj = o || {};
219
+  return Object.keys(obj)
220
+    .map(key => `${key}=${obj[key]}`)
221
+    .join("&");
222
+}
223
+
224
+/**
225
+ *
226
+ * @param {*} url
227
+ * @param {*} params
228
+ */
229
+export function mergeQueryParams(from, to) {
230
+  const originParams = parseQueryString(from) || {};
231
+  const newParams = {
232
+    ...originParams,
233
+    ...(parseQueryString(to) || {})
234
+  };
235
+
236
+  return Object.keys(newParams)
237
+    .map(key => `${key}=${newParams[key]}`)
238
+    .join("&");
239
+}
240
+
241
+/**
242
+ * 格式化剩余时间为 xx天xx小时xx分xx秒
243
+ * @param {int} leftTime 时间毫秒数
244
+ */
245
+export function formateLeftTime(leftTime, unit) {
246
+  const nd = 1000 * 24 * 60 * 60;
247
+  const nh = 1000 * 60 * 60;
248
+  const nm = 1000 * 60;
249
+  const ns = 1000;
250
+
251
+  const day = Math.floor(leftTime / nd);
252
+  const hour = Math.floor((leftTime % nd) / nh);
253
+  const min = Math.floor(((leftTime % nd) % nh) / nm);
254
+  const sec = Math.floor((((leftTime % nd) % nh) % nm) / ns);
255
+
256
+  switch (unit) {
257
+    case "min":
258
+      return `${day}天${hour}小时${min}分`;
259
+    default:
260
+      return `${day}天${hour}小时${min}分${sec}秒`;
261
+  }
262
+}
263
+
264
+export function getDownloadURL(url, type) {
265
+  if (!url) return url;
266
+
267
+  switch (type) {
268
+    case "avatar":
269
+      return url
270
+        .replace("https://wx.qlogo.cn/", `${HOST}/qlogo/`)
271
+        .replace("https://thirdwx.qlogo.cn/", `${HOST}/thirdwx/`);
272
+    case "alioss":
273
+      return url
274
+        .replace(ossPath, `${HOST}/alioss/`)
275
+        .replace(ossFastPath, `${HOST}/alioss/`);
276
+    default:
277
+      return url;
278
+  }
279
+}
280
+
281
+/**
282
+ * @description 获取当前页url
283
+ */
284
+export const getCurrentPageUrl = () => {
285
+  let pages = Taro.getCurrentPages();
286
+  let currentPage = pages[pages.length - 1];
287
+  let url = currentPage.route;
288
+  return url;
289
+};
290
+
291
+export const pageToLogin = () => {
292
+  let path = getCurrentPageUrl();
293
+  if (!path.includes("login")) {
294
+    Taro.navigateTo({
295
+      url: "/pages/login/login"
296
+    });
297
+  }
298
+};
299
+
300
+export const isObject = function(value) {
301
+  var type = typeof value;
302
+  return value != null && type === "object";
303
+};
304
+
305
+//使用递归的方式实现数组、对象的深拷贝
306
+export const deepClone = function(obj) {
307
+  if (!isObject(obj)) {
308
+    throw new Error("obj 不是一个对象!");
309
+  }
310
+  let isArray = Array.isArray(obj);
311
+  let cloneObj = isArray ? [] : {};
312
+  for (let key in obj) {
313
+    cloneObj[key] = isObject(obj[key]) ? deepClone(obj[key]) : obj[key];
314
+  }
315
+
316
+  return cloneObj;
317
+};