Browse Source

项目编辑

魏熙美 5 years ago
parent
commit
d721117aa2

+ 5
- 5
config/config.js View File

@@ -23,11 +23,11 @@ const plugins = [
23 23
         // default true, when it is true, will use `navigator.language` overwrite default
24 24
         baseNavigator: true,
25 25
       },
26
-      // dynamicImport: {
27
-      //   loadingComponent: './components/PageLoading/index',
28
-      //   webpackChunkName: true,
29
-      //   level: 3,
30
-      // },
26
+      dynamicImport: {
27
+        loadingComponent: './components/PageLoading/index',
28
+        webpackChunkName: true,
29
+        level: 3,
30
+      },
31 31
       pwa: pwa
32 32
         ? {
33 33
           workboxPluginMode: 'InjectManifest',

BIN
src/assets/poster1.png View File


BIN
src/assets/poster2.png View File


BIN
src/assets/touxiang.jpg View File


+ 3
- 3
src/components/GlobalHeader/AvatarDropdown.jsx View File

@@ -29,7 +29,7 @@ class AvatarDropdown extends React.Component {
29 29
     const {
30 30
       currentUser = {
31 31
         avatar: '',
32
-        name: '',
32
+        userName: '',
33 33
       },
34 34
       menu,
35 35
     } = this.props;
@@ -55,11 +55,11 @@ class AvatarDropdown extends React.Component {
55 55
         </Menu.Item>
56 56
       </Menu>
57 57
     );
58
-    return currentUser && currentUser.name ? (
58
+    return currentUser && currentUser.userName ? (
59 59
       <HeaderDropdown overlay={menuHeaderDropdown}>
60 60
         <span className={`${styles.action} ${styles.account}`}>
61 61
           <Avatar size="small" className={styles.avatar} src={currentUser.avatar} alt="avatar" />
62
-          <span className={styles.name}>{currentUser.name}</span>
62
+          <span className={styles.name}>{currentUser.userName}</span>
63 63
         </span>
64 64
       </HeaderDropdown>
65 65
     ) : (

+ 7
- 1
src/components/SelectButton/BuildSelect.jsx View File

@@ -13,6 +13,7 @@ const { Option } = Select;
13 13
  */
14 14
 const BuildingSelect = (props) => {
15 15
   const [ data, setData ] = useState([])
16
+  const [ value, setValue ] = useState(props.value)
16 17
 
17 18
   useEffect(() => {
18 19
     getCityList();
@@ -28,8 +29,13 @@ const BuildingSelect = (props) => {
28 29
     })
29 30
   }
30 31
 
32
+  const handleChange = (e) => {
33
+    setValue(e)
34
+    props.onChange(e)
35
+  }
36
+
31 37
   return (
32
-      <Select value={props.value} style={{ width: '180px' }} placeholder="请选择项目" onChange={props.onChange}>
38
+      <Select value={value} style={{ width: '180px' }} placeholder="请选择项目" onChange={handleChange}>
33 39
           {data.map(building => (
34 40
             <Option key={building.buildingId}>{building.buildingName}</Option>
35 41
           ))}

+ 4
- 4
src/components/XForm/ImageUpload.jsx View File

@@ -34,10 +34,10 @@ class ImageUpload extends React.Component {
34 34
     }
35 35
   };
36 36
 
37
-  render () {
37
+  render() {
38 38
     const uploadButton = (
39 39
       <div>
40
-        <Icon style={{ fontSize: '2em', color: '#aaa' }} type={this.state.loading ? "loading" : "plus"} />        
40
+        <Icon style={{ fontSize: '2em', color: '#aaa' }} type={this.state.loading ? "loading" : "plus"} />
41 41
       </div>
42 42
     );
43 43
 
@@ -56,8 +56,8 @@ class ImageUpload extends React.Component {
56 56
         {(this.state.imageUrl || value) ? (
57 57
           <img src={this.state.imageUrl || value} alt="avatar" style={{ width: "100%" }} />
58 58
         ) : (
59
-          uploadButton
60
-        )}
59
+            uploadButton
60
+          )}
61 61
       </Upload>
62 62
     );
63 63
   }

+ 1
- 2
src/models/login.js View File

@@ -36,7 +36,6 @@ const Model = {
36 36
       }
37 37
 
38 38
       yield put(routerRedux.replace(redirect || '/'));
39
-      
40 39
     },
41 40
 
42 41
     *getCaptcha({ payload }, { call }) {
@@ -60,7 +59,7 @@ const Model = {
60 59
   },
61 60
   reducers: {
62 61
     changeLoginStatus(state, { payload }) {
63
-      setAuthority('admin');
62
+      setAuthority((payload.user.roles || []).map(x => x.roleId));
64 63
       return { ...state, status: 'ok', type: payload.type };
65 64
     },
66 65
   },

+ 3
- 2
src/models/user.js View File

@@ -5,6 +5,7 @@ const UserModel = {
5 5
   namespace: 'user',
6 6
   state: {
7 7
     currentUser: {},
8
+    menuList: [],
8 9
   },
9 10
   effects: {
10 11
     // *fetch(_, { call, put }) {
@@ -25,8 +26,8 @@ const UserModel = {
25 26
     },
26 27
   },
27 28
   reducers: {
28
-    saveCurrentUser(state, action) {
29
-      return { ...state, currentUser: action.payload || {} };
29
+    saveCurrentUser(state, { payload = {} }) {
30
+      return { ...state, currentUser: payload.taUser || {}, menuList: payload.menuList || [] };
30 31
     },
31 32
     changeNotifyCount(
32 33
       state = {

+ 87
- 62
src/pages/activity/editActivity.jsx View File

@@ -1,5 +1,5 @@
1 1
 import React, { useState, useEffect } from 'react';
2
-import { Form, Input, Button, Icon, Select, Tabs, Radio, DatePicker, message } from 'antd';
2
+import { Form, Input, Button, Icon, Select, Tabs, Radio, DatePicker, message, Upload } from 'antd';
3 3
 import { FormattedMessage } from 'umi-plugin-react/locale';
4 4
 import styles from '../style/GoodsList.less';
5 5
 import moment from 'moment';
@@ -12,8 +12,12 @@ import PosterBottom from '../../assets/bottom.png'
12 12
 import yinhao from '../../assets/yinhao.png'
13 13
 import ImageUploader from '../../components/XForm/ImageUpload';
14 14
 import logo from '../../assets/logo.png';
15
+import touxiang from '../../assets/touxiang.jpg';
16
+import poster1 from '../../assets/poster1.png';
17
+import poster2 from '../../assets/poster2.png';
15 18
 
16 19
 const { MonthPicker, RangePicker, WeekPicker } = DatePicker;
20
+const { TextArea } = Input;
17 21
 
18 22
 /**
19 23
  *
@@ -22,7 +26,8 @@ const { MonthPicker, RangePicker, WeekPicker } = DatePicker;
22 26
  * @returns
23 27
  */
24 28
 const Edit = (props) => {
25
-  const [tab, changeTab] = useState('basic')
29
+  const [tab, changeTab] = useState('poster')
30
+  // const [tab, changeTab] = useState('basic')
26 31
   const dynamicId = props.location.query.dynamicId
27 32
   const [dynamicData, setDynamicData] = useState({})
28 33
   if (dynamicId) {
@@ -165,77 +170,97 @@ const Edit = (props) => {
165 170
   }
166 171
 
167 172
   const Poster = (props) => {
173
+    const [inputValue, changeInput] = useState('')
174
+    const [textAreaValue, changeTextArea] = useState('')
175
+    const [imgValue, changeImg] = useState('')
176
+    return <div>
177
+      <div style={{ display: 'flex' }}>
178
+        <div style={{ width: '420px', height: '900px', display: 'inline-block', marginTop: '30px' }}>
179
+          <div style={{ width: '375px', height: '785px', backgroundColor: '#fff', boxShadow: '0px 0px 16px 6px rgba(0,0,0,0.15)', position: 'relative', margin: '0 auto' }}>
180
+            <img style={{ width: '100%', height: '300px' }} src={imgValue ? imgValue : poster1} alt="" />
181
+            <div style={{ display: 'flex', alignItems: 'center', marginTop: '-24px' }}>
182
+              <img style={{ width: '70px', height: '70px', border: '4px solid #fff', borderRadius: '35px', marginLeft: '16px' }} src={touxiang} alt="" />
183
+              <span style={{ color: '#222', fontWeight: '600', margin: '24px 10px 0 14px', fontSize: '17px' }}>喵喵</span>
184
+              <span style={{ color: '#999', marginTop: '25px', fontSize: '17px' }}>邀您阅读</span>
185
+              <span style={{ color: '#999', margin: '25px 0 0 60px', fontSize: '17px' }}>2019.09.21</span>
186
+            </div>
187
+            <p style={{
188
+              margin: '10px 20px', fontSize: '20px', color: '#222', fontWeight: '600',
189
+              display: '-webkit-box', lineClamp: '3', height: '60px',
190
+              WebkitLineClamp: '2',
191
+              WebkitBoxOrient: 'vertical',
192
+              overflow: 'hidden',
193
+              textOverflow: 'ellipsis'
194
+            }}>{inputValue ? inputValue : '海报标题'}</p>
168 195
 
169
-    return <div style={{ display: 'flex' }}>
170
-      <div style={{ width: '420px', height: '900px', display: 'inline-block', marginTop: '30px' }}>
171
-        <div style={{ width: '375px', height: '785px', backgroundColor: '#fff', boxShadow: '0px 0px 16px 6px rgba(0,0,0,0.15)', position: 'relative', margin: '0 auto' }}>
172
-
173
-          <img style={{ width: '100%', height: '300px' }} src="http://img0.imgtn.bdimg.com/it/u=4246326797,2657995307&fm=26&gp=0.jpg" alt="" />
196
+            <img src={yinhao} style={{ width: '30px', marginLeft: '10px' }} alt="" />
197
+            <p style={{
198
+              margin: '16px 20px 28px 20px', fontSize: '17px', color: '#999',
199
+              display: '-webkit-box', lineClamp: '3', height: '76px',
200
+              WebkitLineClamp: '3',
201
+              WebkitBoxOrient: 'vertical',
202
+              overflow: 'hidden',
203
+              textOverflow: 'ellipsis'
204
+            }}>{textAreaValue ? textAreaValue : '海报描述'}</p>
205
+            <img src={PosterBottom} style={{ width: '100%' }} alt="" />
206
+          </div>
207
+          <p style={{ textAlign: 'center', fontSize: '19px', color: '#666', marginTop: '30px' }}>海报模板</p>
208
+        </div>
174 209
 
175
-          <div style={{ display: 'flex', alignItems: 'center', marginTop: '-24px' }}>
176
-            <img style={{ width: '70px', height: '70px', border: '4px solid #fff', borderRadius: '35px', marginLeft: '16px' }} src="http://img0.imgtn.bdimg.com/it/u=3463541938,2540701974&fm=26&gp=0.jpg" alt="" />
177
-            <span style={{ color: '#222', fontWeight: '600', margin: '24px 10px 0 14px', fontSize: '17px' }}>喵喵</span>
178
-            <span style={{ color: '#999', marginTop: '25px', fontSize: '17px' }}>邀您阅读</span>
179
-            <span style={{ color: '#999', margin: '25px 0 0 60px', fontSize: '17px' }}>2019.09.21</span>
210
+        <div >
211
+          <div style={{ display: 'flex', width: '100%', margin: '60px 0' }}>
212
+            <p style={{ minWidth: '200px', color: '#222', textAlign: 'right', margin: '0 30px 0 0' }}>海报图片</p>
213
+            <ImageUploader onChange={e => changeImg(e)} />
214
+          </div>
215
+          <div style={{ display: 'flex', alignItems: 'center', width: '100%', marginBottom: '60px' }}>
216
+            <p style={{ minWidth: '200px', color: '#222', textAlign: 'right', margin: '0 30px 0 0' }}>海报标题</p>
217
+            <Input style={{ width: '20vw' }} placeholder="请输入海报标题" onChange={e => changeInput(e.target.value)} />
218
+          </div>
219
+          <div style={{ display: 'flex', margin: '10px 0 40px 0', width: '100%' }}>
220
+            <p style={{ minWidth: '200px', color: '#222', textAlign: 'right', margin: '0 30px 0 0' }}>海报描述</p>
221
+            <TextArea rows={5} onChange={e => changeTextArea(e.target.value)} />
180 222
           </div>
181
-          <p style={{
182
-            margin: '10px 20px', fontSize: '20px', color: '#222', fontWeight: '600',
183
-            display: '-webkit-box', lineClamp: '3', height: '60px',
184
-            WebkitLineClamp: '2',
185
-            WebkitBoxOrient: 'vertical',
186
-            overflow: 'hidden',
187
-            textOverflow: 'ellipsis'
188
-          }}>破晓·内容生态下半场的版权到企业高峰论坛会议破晓·内容生态下半场的版权到企业高峰论坛活动</p>
189 223
 
190
-          <img src={yinhao} style={{ width: '30px', marginLeft: '10px' }} alt="" />
191
-          <p style={{
192
-            margin: '16px 20px 28px 20px', fontSize: '17px', color: '#999',
193
-            display: '-webkit-box', lineClamp: '3', height: '76px',
194
-            WebkitLineClamp: '3',
195
-            WebkitBoxOrient: 'vertical',
196
-            overflow: 'hidden',
197
-            textOverflow: 'ellipsis'
198
-          }}>活动描述:破晓·内容生态下半场的版权到企业高峰论坛活动破晓·内容生态下半场的版权到企业高峰论坛活动破晓·内容生态下半场的版权到企业高峰论坛活动</p>
199
-          <img src={PosterBottom} style={{ width: '100%' }} alt="" />
200 224
         </div>
201
-        <p style={{ textAlign: 'center', fontSize: '19px', color: '#666', marginTop: '30px' }}>海报模板</p>
202 225
       </div>
203
-      <div></div>
226
+      <Button type="primary" onClick={() => router.go(-1)} style={{ margin: '40px 40px 40px 30vw' }}> 确定</Button>
227
+      <Button onClick={() => router.go(-1)}>取消</Button>
204 228
     </div>
229
+
205 230
   }
206 231
 
232
+
233
+
207 234
   const Share = (props) => {
208
-    const fields = [
209
-      {
210
-        label: '分享模板',
211
-        name: 'buildingId',
212
-        render: () => <div style={{ margin: ' 10px 20px' }}>
213
-          <p style={{ display: 'flex', alignItems: 'center', fontSize: '14px', color: '#999', margin: '0', lineHeight: '0' }}><img src={logo} style={{ width: '22px', marginRight: '10px' }} />知与行互动</p>
214
-          <p style={{ fontSize: '16px', color: '#222', fontWeight: '600', margin: '0' }}>置业V客厅 精准获客平台</p>
215
-          <img style={{ width: '200px', height: '140px' }} src="http://house.china.com.cn/nanjing/UserFiles/20171124/11070256.jpg" alt="" />
216
-        </div>,
217
-      },
218
-      {
219
-        label: '海报标题',
220
-        name: 'newsTypeName',
221
-        type: FieldTypes.Text,
222
-        value: '',
223
-        rules: [
224
-          { required: true, message: '请输入咨询名称' },
225
-        ]
226
-      },
227
-      {
228
-        label: '类型图',
229
-        name: 'newsTypeImg',
230
-        type: FieldTypes.ImageUploader,
231
-        value: 'newsTypeImg',
232
-      },
233
-    ]
234
-    const handleSubmit = (values) => {
235
+    const [inputValue, changeInput] = useState('')
236
+    const [imgValue, changeImg] = useState('')
237
+    // const changeInputValue = e => {
238
+    //   changeInput(e.target.value)
239
+    // }
235 240
 
236
-    }
237
-    return <div>
238
-      <XForm onSubmit={handleSubmit} onCancel={cancelPage} fields={fields}></XForm>
241
+    // const handleSubmit = (values) => {
242
+
243
+    // }
244
+
245
+    return <div style={{ padding: '20px' }}>
246
+      <div style={{ display: 'flex', margin: '10px 0 40px 0', width: '100%' }}>
247
+        <p style={{ minWidth: '200px', color: '#222', textAlign: 'right', margin: '0 30px 0 0' }}>分享模板</p>
248
+        <div>
249
+          <p style={{ display: 'flex', alignItems: 'center', fontSize: '14px', color: '#999', margin: '0', lineHeight: '0' }}><img src={logo} style={{ width: '22px', marginRight: '10px' }} />知与行互动</p>
250
+          <p style={{ fontSize: '16px', color: '#222', fontWeight: '600', margin: '0' }}>{inputValue ? inputValue : '置业V客厅 精准获客平台'}</p>
251
+          <img style={{ width: '200px', height: '140px' }} src={imgValue ? imgValue : poster2} alt="" />
252
+        </div>
253
+      </div>
254
+      <div style={{ display: 'flex', alignItems: 'center', width: '100%' }}>
255
+        <p style={{ minWidth: '200px', color: '#222', textAlign: 'right', margin: '0 30px 0 0' }}>海报标题</p>
256
+        <Input placeholder="请输入海报标题" onChange={e => changeInput(e.target.value)} />
257
+      </div>
258
+      <div style={{ display: 'flex', width: '100%', marginTop: '40px' }}>
259
+        <p style={{ minWidth: '200px', color: '#222', textAlign: 'right', margin: '0 30px 0 0' }}>分享图片</p>
260
+        <ImageUploader onChange={e => changeImg(e)} />
261
+      </div>
262
+      <Button type="primary" htmlType="submit" style={{ margin: '40px 40px 40px 220px' }}> 确定</Button>
263
+      <Button onClick={() => router.go(-1)}>取消</Button>
239 264
     </div>
240 265
   }
241 266
 

+ 35
- 8
src/pages/system/intention.jsx View File

@@ -19,8 +19,10 @@ const { MonthPicker, RangePicker, WeekPicker } = DatePicker;
19 19
  */
20 20
  const Edit = (props) => {
21 21
   const [ data, setData ] = useState([])
22
+  const [ buildingIdValue, setBuildingIdData ] = useState('')
22 23
 
23 24
   const changBuilding = (buildingId) => {
25
+    setBuildingIdData(buildingId)
24 26
     request({
25 27
       url: '/api/admin/tdBizEventIntention',
26 28
       method: 'GET',
@@ -31,6 +33,32 @@ const { MonthPicker, RangePicker, WeekPicker } = DatePicker;
31 33
     })
32 34
   }
33 35
 
36
+  const changeBox = (x) => (e) => {
37
+    setData(data.map((item) => {
38
+      return x.eventId === item.eventId ? {...item, checkbox: e.target.checked} : item
39
+    }))
40
+  }
41
+
42
+  const changeInput = (x) => (e) => {
43
+    setData(data.map((item) => {
44
+      return x.eventId === item.eventId ? {...item, intention: e.target.value} : item
45
+    }))
46
+  }
47
+
48
+  const submitValue = () => {
49
+    console.log(buildingIdValue)
50
+    if(buildingIdValue === ''){
51
+      return
52
+    }
53
+    request({
54
+      url: '/api/admin/taBuildingIntentionAddOrUpdate/' + buildingIdValue,
55
+      method: 'POST',
56
+      data
57
+    }).then((data) => {
58
+      message.info("保存成功")
59
+    })
60
+  }
61
+
34 62
   return (
35 63
     <>
36 64
     <Row>
@@ -39,26 +67,25 @@ const { MonthPicker, RangePicker, WeekPicker } = DatePicker;
39 67
       </Col>
40 68
     </Row>
41 69
     <Row>
42
-      <Col span={6} offset={6}>
70
+      <Col span={3} offset={3}>
43 71
         用户操作
44 72
       </Col>
45
-      <Col span={6} offset={6}>
73
+      <Col span={3} offset={3}>
46 74
         意向值
47 75
       </Col>
48 76
     </Row>
49 77
     {data.map((x) => {
50
-      console.log(x)
51 78
       return <Row>
52
-                <Col span={6} offset={6}>
53
-                {x.eventName}
79
+                <Col span={3} offset={3}>
80
+                  <Checkbox checked={x.checkbox} onChange={changeBox(x)}>{x.eventName}</Checkbox>
54 81
                 </Col>
55
-                <Col span={6} offset={6}>
56
-                  {x.intention}
82
+                <Col span={3} offset={3}>
83
+                  <Input value={x.intention} onChange={changeInput(x)}/>
57 84
                 </Col>
58 85
               </Row>
59 86
     })}
60 87
     <Row>
61
-      <Button type="primary" className={styles.searchBtn} >确定</Button>
88
+      <Button type="primary" className={styles.searchBtn} onClick={submitValue}>确定</Button>
62 89
     </Row>
63 90
     </>
64 91
   );

+ 4
- 2
src/pages/system/report.jsx View File

@@ -1,5 +1,5 @@
1 1
 import React, { useState, useEffect } from 'react';
2
-import { Form, Input, Button, Icon, Select, Tabs, Radio, DatePicker,message,Checkbox } from 'antd';
2
+import { Form, Input, Button, Icon, Select, Tabs, Radio, DatePicker,message,Checkbox,Row, Col  } from 'antd';
3 3
 import { FormattedMessage } from 'umi-plugin-react/locale';
4 4
 import styles from '../style/GoodsList.less';
5 5
 import moment from 'moment';
@@ -62,7 +62,9 @@ const { MonthPicker, RangePicker, WeekPicker } = DatePicker;
62 62
 
63 63
   return (
64 64
     <>
65
-      <Checkbox.Group options={data} value={checkData} onChange={onChange} />
65
+      <Row>
66
+        <Checkbox.Group options={data} value={checkData} onChange={onChange} />
67
+      </Row>
66 68
       <Button type="primary" className={styles.searchBtn} onClick={saveCheckedReport}>提交</Button>
67 69
     </>
68 70
   );

+ 31
- 0
src/utils/mixStr.js View File

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

+ 12
- 4
src/utils/request.js View File

@@ -4,6 +4,9 @@
4 4
  */
5 5
 import request from 'umi-request';
6 6
 import { notification } from 'antd';
7
+import apis from '../services/apis';
8
+import mixStr from './mixStr';
9
+
7 10
 const codeMessage = {
8 11
   200: '服务器成功返回请求的数据。',
9 12
   201: '新建或修改数据成功。',
@@ -25,18 +28,18 @@ const codeMessage = {
25 28
 const replaceURLParams = (url, params = {}) => {
26 29
   return Object.keys(params).reduce((acc, k) => { // 此方法对每个元素进行处理
27 30
     const re = new RegExp(`:${k}(?!w)`, 'i')
28
-    return acc.replace(re, args[k])
31
+    return acc.replace(re, params[k])
29 32
   }, url)
30 33
 }
31 34
 
32 35
 request.interceptors.request.use((url, options) => {
33 36
   const { urlData, headers = {}, logout = false, data, ...opts } = options
34 37
   const apiURL = urlData ? replaceURLParams(url, urlData) : url
35
-  const token = window.localStorage.getItem('x-token')
38
+  const token = mixStr(window.localStorage.getItem('test-foobar'))
36 39
   const authHeader = token ? { Authorization: `Bearer ${token}` } : {}
37 40
 
38 41
   if (logout) {
39
-    window.localStorage.removeItem('x-token')
42
+    window.localStorage.removeItem('test-foobar')
40 43
   }
41 44
 
42 45
   return (
@@ -79,7 +82,7 @@ request.interceptors.response.use(async (response, options) => {
79 82
       }
80 83
 
81 84
       if (data && data.token) {
82
-        window.localStorage.setItem('x-token', data.token)
85
+        window.localStorage.setItem('test-foobar', mixStr(data.token))
83 86
       }
84 87
 
85 88
       return data;
@@ -92,6 +95,9 @@ request.interceptors.response.use(async (response, options) => {
92 95
   }
93 96
 });
94 97
 
98
+
99
+const fetch = api => options => request(api.url, {...api, ...options || {}})
100
+
95 101
 export default config => {
96 102
   if (typeof config === 'string') {
97 103
     return request(config);
@@ -100,3 +106,5 @@ export default config => {
100 106
     return request(url, options);
101 107
   }
102 108
 };
109
+
110
+export { fetch, apis }