Your Name 4 years ago
parent
commit
04dbf7cb96
44 changed files with 667 additions and 378 deletions
  1. 2
    2
      package.json
  2. 1
    1
      src/components/Authorized/CheckPermissions.jsx
  3. 1
    1
      src/components/Authorized/renderAuthorize.js
  4. 5
    5
      src/components/Container/index.jsx
  5. 9
    1
      src/components/GlobalHeader/AvatarDropdown.jsx
  6. 3
    0
      src/components/GlobalHeader/NoticeIconView.jsx
  7. 2
    1
      src/components/NoticeIcon/NoticeList.jsx
  8. 1
    0
      src/components/NoticeIcon/index.jsx
  9. 154
    0
      src/components/TestQuestions/index.jsx
  10. 32
    29
      src/components/UploadImage/index.jsx
  11. 44
    0
      src/components/UploadVideo/index.jsx
  12. 10
    0
      src/components/UploadVideo/style.less
  13. 13
    13
      src/components/WangEditor/Preview.jsx
  14. 12
    12
      src/components/WangEditor/PreviewMenu.js
  15. 77
    82
      src/components/WangEditor/WangEditor.jsx
  16. 3
    0
      src/components/WangEditor/index.js
  17. 1
    1
      src/global.jsx
  18. 38
    38
      src/layouts/BasicLayout.jsx
  19. 1
    1
      src/layouts/UserLayout.jsx
  20. 1
    0
      src/locales/en-US.js
  21. 1
    0
      src/locales/id-ID.js
  22. 1
    0
      src/locales/ja-JP.js
  23. 1
    0
      src/locales/pt-BR.js
  24. 1
    0
      src/locales/zh-CN.js
  25. 1
    0
      src/locales/zh-TW.js
  26. 1
    0
      src/models/global.js
  27. 10
    10
      src/models/login.js
  28. 2
    2
      src/models/user.js
  29. 2
    1
      src/pages/Admin.jsx
  30. 10
    0
      src/pages/Post/Edit/components/Answer.jsx
  31. 68
    56
      src/pages/Post/Edit/components/Form.jsx
  32. 34
    30
      src/pages/Post/Edit/index.jsx
  33. 3
    1
      src/pages/Post/List/index.jsx
  34. 3
    3
      src/pages/TableList/components/UpdateForm.jsx
  35. 3
    6
      src/pages/TableList/index.jsx
  36. 1
    0
      src/pages/TableList/service.js
  37. 7
    9
      src/pages/User/login/index.jsx
  38. 2
    4
      src/pages/Welcome.jsx
  39. 4
    4
      src/pages/model.js
  40. 3
    3
      src/utils/authority.js
  41. 43
    39
      src/utils/request.js
  42. 47
    15
      src/utils/uploadFile.js
  43. 8
    8
      src/utils/utils.js
  44. 1
    0
      src/utils/utils.test.js

+ 2
- 2
package.json View File

18
     "lint:js": "eslint --cache --ext .js,.jsx,.ts,.tsx --format=pretty ./src",
18
     "lint:js": "eslint --cache --ext .js,.jsx,.ts,.tsx --format=pretty ./src",
19
     "lint:prettier": "prettier --check \"src/**/*\" --end-of-line auto",
19
     "lint:prettier": "prettier --check \"src/**/*\" --end-of-line auto",
20
     "lint:style": "stylelint --fix \"src/**/*.less\" --syntax less",
20
     "lint:style": "stylelint --fix \"src/**/*.less\" --syntax less",
21
-    "precommit": "lint-staged",
22
     "prettier": "prettier -c --write \"src/**/*\"",
21
     "prettier": "prettier -c --write \"src/**/*\"",
23
     "start": "cross-env UMI_ENV=dev umi dev",
22
     "start": "cross-env UMI_ENV=dev umi dev",
24
     "start:dev": "cross-env REACT_APP_ENV=dev MOCK=none UMI_ENV=dev umi dev",
23
     "start:dev": "cross-env REACT_APP_ENV=dev MOCK=none UMI_ENV=dev umi dev",
58
     "md5": "^2.3.0",
57
     "md5": "^2.3.0",
59
     "moment": "^2.25.3",
58
     "moment": "^2.25.3",
60
     "omit.js": "^2.0.2",
59
     "omit.js": "^2.0.2",
61
-    "react": "^16.14.0",
60
+    "react": "17.0.0",
62
     "react-dev-inspector": "^1.1.1",
61
     "react-dev-inspector": "^1.1.1",
63
     "react-dom": "^17.0.0",
62
     "react-dom": "^17.0.0",
64
     "react-helmet-async": "^1.0.4",
63
     "react-helmet-async": "^1.0.4",
64
+    "react-player": "^2.9.0",
65
     "umi": "^3.4.1",
65
     "umi": "^3.4.1",
66
     "umi-request": "^1.0.8",
66
     "umi-request": "^1.0.8",
67
     "wangeditor": "^4.6.15"
67
     "wangeditor": "^4.6.15"

+ 1
- 1
src/components/Authorized/CheckPermissions.jsx View File

66
 export { checkPermissions };
66
 export { checkPermissions };
67
 
67
 
68
 function check(authority, target, Exception) {
68
 function check(authority, target, Exception) {
69
-  const permit = typeof CURRENT === 'function' ? CURRENT() : CURRENT
69
+  const permit = typeof CURRENT === 'function' ? CURRENT() : CURRENT;
70
   return checkPermissions(authority, permit, target, Exception);
70
   return checkPermissions(authority, permit, target, Exception);
71
 }
71
 }
72
 
72
 

+ 1
- 1
src/components/Authorized/renderAuthorize.js View File

9
  * @param {string|()=>String} currentAuthority
9
  * @param {string|()=>String} currentAuthority
10
  */
10
  */
11
 const renderAuthorize = (Authorized) => (currentAuthority) => {
11
 const renderAuthorize = (Authorized) => (currentAuthority) => {
12
-  CURRENT = currentAuthority
12
+  CURRENT = currentAuthority;
13
   return Authorized;
13
   return Authorized;
14
 };
14
 };
15
 
15
 

+ 5
- 5
src/components/Container/index.jsx View File

1
-import React from 'react'
2
-import { Spin } from 'antd'
1
+import React from 'react';
2
+import { Spin } from 'antd';
3
 
3
 
4
 export default (props) => {
4
 export default (props) => {
5
-  const loading = typeof props.loading === 'boolean' ? props.loading : false
5
+  const loading = typeof props.loading === 'boolean' ? props.loading : false;
6
 
6
 
7
   return (
7
   return (
8
     <Spin spinning={loading} tip={props.loadingTip}>
8
     <Spin spinning={loading} tip={props.loadingTip}>
9
       <div style={{ background: '#fff', padding: '2em' }}>{props.children}</div>
9
       <div style={{ background: '#fff', padding: '2em' }}>{props.children}</div>
10
     </Spin>
10
     </Spin>
11
-  )
12
-}
11
+  );
12
+};

+ 9
- 1
src/components/GlobalHeader/AvatarDropdown.jsx View File

42
     return currentUser && currentUser.userName ? (
42
     return currentUser && currentUser.userName ? (
43
       <HeaderDropdown overlay={menuHeaderDropdown}>
43
       <HeaderDropdown overlay={menuHeaderDropdown}>
44
         <span className={`${styles.action} ${styles.account}`}>
44
         <span className={`${styles.action} ${styles.account}`}>
45
-          <Avatar size="small" className={styles.avatar} src={currentUser.avatar || 'https://gw.alipayobjects.com/zos/antfincdn/XAosXuNZyF/BiazfanxmamNRoxxVxka.png'} alt="avatar" />
45
+          <Avatar
46
+            size="small"
47
+            className={styles.avatar}
48
+            src={
49
+              currentUser.avatar ||
50
+              'https://gw.alipayobjects.com/zos/antfincdn/XAosXuNZyF/BiazfanxmamNRoxxVxka.png'
51
+            }
52
+            alt="avatar"
53
+          />
46
           <span className={`${styles.name} anticon`}>{currentUser.userName}</span>
54
           <span className={`${styles.name} anticon`}>{currentUser.userName}</span>
47
         </span>
55
         </span>
48
       </HeaderDropdown>
56
       </HeaderDropdown>

+ 3
- 0
src/components/GlobalHeader/NoticeIconView.jsx View File

28
       });
28
       });
29
     }
29
     }
30
   };
30
   };
31
+
31
   handleNoticeClear = (title, key) => {
32
   handleNoticeClear = (title, key) => {
32
     const { dispatch } = this.props;
33
     const { dispatch } = this.props;
33
     message.success(`${'清空了'} ${title}`);
34
     message.success(`${'清空了'} ${title}`);
39
       });
40
       });
40
     }
41
     }
41
   };
42
   };
43
+
42
   getNoticeData = () => {
44
   getNoticeData = () => {
43
     const { notices = [] } = this.props;
45
     const { notices = [] } = this.props;
44
 
46
 
80
     });
82
     });
81
     return groupBy(newNotices, 'type');
83
     return groupBy(newNotices, 'type');
82
   };
84
   };
85
+
83
   getUnreadData = (noticeData) => {
86
   getUnreadData = (noticeData) => {
84
     const unreadMsg = {};
87
     const unreadMsg = {};
85
     Object.keys(noticeData).forEach((key) => {
88
     Object.keys(noticeData).forEach((key) => {

+ 2
- 1
src/components/NoticeIcon/NoticeList.jsx View File

35
         renderItem={(item, i) => {
35
         renderItem={(item, i) => {
36
           const itemCls = classNames(styles.item, {
36
           const itemCls = classNames(styles.item, {
37
             [styles.read]: item.read,
37
             [styles.read]: item.read,
38
-          }); // eslint-disable-next-line no-nested-ternary
38
+          });
39
 
39
 
40
+          // eslint-disable-next-line no-nested-ternary
40
           const leftIcon = item.avatar ? (
41
           const leftIcon = item.avatar ? (
41
             typeof item.avatar === 'string' ? (
42
             typeof item.avatar === 'string' ? (
42
               <Avatar className={styles.avatar} src={item.avatar} />
43
               <Avatar className={styles.avatar} src={item.avatar} />

+ 1
- 0
src/components/NoticeIcon/index.jsx View File

6
 import NoticeList from './NoticeList';
6
 import NoticeList from './NoticeList';
7
 import HeaderDropdown from '../HeaderDropdown';
7
 import HeaderDropdown from '../HeaderDropdown';
8
 import styles from './index.less';
8
 import styles from './index.less';
9
+
9
 const { TabPane } = Tabs;
10
 const { TabPane } = Tabs;
10
 
11
 
11
 const NoticeIcon = (props) => {
12
 const NoticeIcon = (props) => {

+ 154
- 0
src/components/TestQuestions/index.jsx View File

1
+import React, { useState } from 'react';
2
+import { Button, Row, Col, Checkbox, Radio, Input, Space } from 'antd';
3
+import WangEditor from '@/components/WangEditor';
4
+
5
+export default (props) => {
6
+  const [formData, setFormData] = useState({
7
+    question: undefined,
8
+    answerType: 'radio',
9
+    correctAnswers: undefined,
10
+    optionA: undefined,
11
+    optionB: undefined,
12
+    optionC: undefined,
13
+    optionD: undefined,
14
+  });
15
+
16
+  // const [answerTypeValue, setAnswerTypeValue] = useState([])
17
+
18
+  const handleFormChange = (field) => (e) => {
19
+    const value = e && e.target ? e.target.value : e;
20
+    console.log('------111--->', field, e, value);
21
+    setFormData({
22
+      ...formData,
23
+      [field]: value,
24
+    });
25
+  };
26
+
27
+  const handleCorrectAnswers = () => {};
28
+
29
+  const handleSubmit = () => {};
30
+
31
+  const handleCancel = () => {
32
+    if (props.onCancel) {
33
+      props.onCancel();
34
+    }
35
+  };
36
+
37
+  const answerTypeDict = [
38
+    { value: 'switch', label: '判断' },
39
+    { value: 'radio', label: '单选' },
40
+    { value: 'checkbox', label: '多选' },
41
+  ];
42
+
43
+  return (
44
+    <div>
45
+      <Space size="large" direction="vertical" style={{ width: '100%' }}>
46
+        <section>
47
+          <h3>试题:</h3>
48
+          <WangEditor value={formData.question} onChange={handleFormChange('question')} />
49
+        </section>
50
+
51
+        <section>
52
+          <h3>选项:</h3>
53
+          <Space size="large" direction="vertical" style={{ width: '100%' }}>
54
+            <Row gutter="24">
55
+              <Col span={12}>
56
+                <Input addonBefore="A" onChange={handleFormChange('optionA')} />
57
+              </Col>
58
+              <Col span={12}>
59
+                <Input addonBefore="B" onChange={handleFormChange('optionB')} />
60
+              </Col>
61
+            </Row>
62
+            <Row gutter="24">
63
+              <Col span={12}>
64
+                <Input addonBefore="C" onChange={handleFormChange('optionC')} />
65
+              </Col>
66
+              <Col span={12}>
67
+                <Input addonBefore="D" onChange={handleFormChange('optionD')} />
68
+              </Col>
69
+            </Row>
70
+          </Space>
71
+        </section>
72
+
73
+        <section>
74
+          <Row gutter="24">
75
+            <Col span={12}>
76
+              <h3>题型:</h3>
77
+              <Radio.Group
78
+                options={answerTypeDict}
79
+                value={formData.answerType}
80
+                onChange={handleFormChange('answerType')}
81
+              />
82
+            </Col>
83
+            <Col span={12}>
84
+              <h3>正确答案:</h3>
85
+              {formData.answerType === 'switch' && (
86
+                <Radio.Group onChange={handleCorrectAnswers} style={{ width: '100%' }}>
87
+                  <Row gutter="12">
88
+                    <Col span={6}>
89
+                      <Radio value="A">是</Radio>
90
+                    </Col>
91
+                    <Col span={6}>
92
+                      <Radio value="B">否</Radio>
93
+                    </Col>
94
+                  </Row>
95
+                </Radio.Group>
96
+              )}
97
+              {formData.answerType === 'radio' && (
98
+                <Radio.Group onChange={handleCorrectAnswers} style={{ width: '100%' }}>
99
+                  <Row gutter="12">
100
+                    <Col span={6}>
101
+                      <Radio value="A">A</Radio>
102
+                    </Col>
103
+                    <Col span={6}>
104
+                      <Radio value="B">B</Radio>
105
+                    </Col>
106
+                    <Col span={6}>
107
+                      <Radio value="C">C</Radio>
108
+                    </Col>
109
+                    <Col span={6}>
110
+                      <Radio value="D">D</Radio>
111
+                    </Col>
112
+                  </Row>
113
+                </Radio.Group>
114
+              )}
115
+              {formData.answerType === 'checkbox' && (
116
+                <Checkbox.Group onChange={handleCorrectAnswers} style={{ width: '100%' }}>
117
+                  <Row gutter="12">
118
+                    <Col span={6}>
119
+                      <Checkbox value="A">A</Checkbox>
120
+                    </Col>
121
+                    <Col span={6}>
122
+                      <Checkbox value="B">B</Checkbox>
123
+                    </Col>
124
+                    <Col span={6}>
125
+                      <Checkbox value="C">C</Checkbox>
126
+                    </Col>
127
+                    <Col span={6}>
128
+                      <Checkbox value="D">D</Checkbox>
129
+                    </Col>
130
+                  </Row>
131
+                </Checkbox.Group>
132
+              )}
133
+            </Col>
134
+          </Row>
135
+        </section>
136
+
137
+        <section>
138
+          <Row gutter="24">
139
+            <Col span={4}>
140
+              <Button type="primary" onClick={handleSubmit}>
141
+                确定
142
+              </Button>
143
+            </Col>
144
+            <Col span={4}>
145
+              <Button type="default" onClick={handleCancel}>
146
+                取消
147
+              </Button>
148
+            </Col>
149
+          </Row>
150
+        </section>
151
+      </Space>
152
+    </div>
153
+  );
154
+};

+ 32
- 29
src/components/UploadImage/index.jsx View File

1
-import React, { useState, useCallback } from 'react'
2
-import { Upload, notification } from 'antd'
3
-import { LoadingOutlined, PlusOutlined } from '@ant-design/icons'
4
-import uploadFile from '@/utils/uploadFile'
1
+import React, { useState, useCallback } from 'react';
2
+import { Upload, notification } from 'antd';
3
+import { LoadingOutlined, PlusOutlined } from '@ant-design/icons';
4
+import uploadFile from '@/utils/uploadFile';
5
 
5
 
6
 export default (props) => {
6
 export default (props) => {
7
-  const [loading, setLoading] = useState(false)
7
+  const [loading, setLoading] = useState(false);
8
 
8
 
9
-  const handleChange = useCallback(({ file }) => {
10
-    switch (file.status) {
11
-      case 'done':
12
-        setLoading(false)
13
-        props.onChange(file.response)
14
-        break
15
-      case 'error':
16
-        setLoading(false)
17
-        notification.error({ message: file.error })
18
-      break
19
-      default:
20
-        setLoading(true)
21
-    }
22
-  }, [props])
9
+  const handleChange = useCallback(
10
+    ({ file }) => {
11
+      switch (file.status) {
12
+        case 'done':
13
+          setLoading(false);
14
+          props.onChange(file.response);
15
+          break;
16
+        case 'error':
17
+          setLoading(false);
18
+          notification.error({ message: file.error });
19
+          break;
20
+        default:
21
+          setLoading(true);
22
+      }
23
+    },
24
+    [props],
25
+  );
23
 
26
 
24
   return (
27
   return (
25
     <Upload
28
     <Upload
30
       customRequest={uploadFile}
33
       customRequest={uploadFile}
31
       onChange={handleChange}
34
       onChange={handleChange}
32
     >
35
     >
33
-      {
34
-        props.value ?
35
-          <img src={props.value} width="100%" alt="" /> :
36
-          <div>
37
-            {loading ? <LoadingOutlined /> : <PlusOutlined />}
38
-            <div style={{ marginTop: 8 }}>上传</div>
39
-          </div>
40
-      }
36
+      {props.value ? (
37
+        <img src={props.value} width="100%" alt="" />
38
+      ) : (
39
+        <div>
40
+          {loading ? <LoadingOutlined /> : <PlusOutlined />}
41
+          <div style={{ marginTop: 8 }}>上传</div>
42
+        </div>
43
+      )}
41
     </Upload>
44
     </Upload>
42
-  )
43
-}
45
+  );
46
+};

+ 44
- 0
src/components/UploadVideo/index.jsx View File

1
+import React, { useState, useCallback } from 'react';
2
+import { Button, Spin, Upload, notification } from 'antd';
3
+import { UploadOutlined } from '@ant-design/icons';
4
+import ReactPlayer from 'react-player/lazy';
5
+import uploadFile from '@/utils/uploadFile';
6
+
7
+import Styles from './style.less';
8
+
9
+export default (props) => {
10
+  const [loading, setLoading] = useState(false);
11
+
12
+  const handleChange = useCallback(
13
+    ({ file }) => {
14
+      switch (file.status) {
15
+        case 'done':
16
+          setLoading(false);
17
+          props.onChange(file.response);
18
+          break;
19
+        case 'error':
20
+          setLoading(false);
21
+          notification.error({ message: file.error });
22
+          break;
23
+        default:
24
+          setLoading(true);
25
+      }
26
+    },
27
+    [props],
28
+  );
29
+
30
+  return (
31
+    <div className={Styles['player-wrapper']}>
32
+      <div className={Styles['upload-btn']}>
33
+        <Upload maxCount={1} customRequest={uploadFile} onChange={handleChange}>
34
+          <Button icon={<UploadOutlined />}>上传视频</Button>
35
+        </Upload>
36
+      </div>
37
+      <Spin spinning={loading}>
38
+        <div className={Styles['react-player']}>
39
+          <ReactPlayer controls muted url={props.value} width="100%" height="100%" />
40
+        </div>
41
+      </Spin>
42
+    </div>
43
+  );
44
+};

+ 10
- 0
src/components/UploadVideo/style.less View File

1
+.player-wrapper {
2
+  width: 600px;
3
+}
4
+
5
+.react-player {
6
+  width: 600px;
7
+  height: 375px;
8
+  margin: 1em 0;
9
+  background: rgba(0, 0, 0, 0.2);
10
+}

+ 13
- 13
src/components/WangEditor/Preview.jsx View File

1
-import React, { useEffect, useRef } from 'react'
2
-import { Modal } from 'antd'
1
+import React, { useEffect, useRef } from 'react';
2
+import { Modal } from 'antd';
3
 
3
 
4
-export default props => {
5
-  const ref = useRef()
4
+export default (props) => {
5
+  const ref = useRef();
6
 
6
 
7
   useEffect(() => {
7
   useEffect(() => {
8
-    let t = null
8
+    let t = null;
9
     if (props.visible) {
9
     if (props.visible) {
10
       // 防止 postMessage 的时候 iframe 内容还没有加载完成
10
       // 防止 postMessage 的时候 iframe 内容还没有加载完成
11
       t = setInterval(() => {
11
       t = setInterval(() => {
12
         // window.preViewFrame.window.postMessage(props.html, '*')
12
         // window.preViewFrame.window.postMessage(props.html, '*')
13
         if (ref.current) {
13
         if (ref.current) {
14
-          ref.current.contentWindow.postMessage(props.html, '*')
14
+          ref.current.contentWindow.postMessage(props.html, '*');
15
         }
15
         }
16
-      }, 800)
16
+      }, 800);
17
     }
17
     }
18
 
18
 
19
-    return () => t && clearInterval(t)
20
-  }, [props.visible, props.html])
19
+    return () => t && clearInterval(t);
20
+  }, [props.visible, props.html]);
21
 
21
 
22
   return (
22
   return (
23
     <Modal
23
     <Modal
29
       closable={false}
29
       closable={false}
30
       title={null}
30
       title={null}
31
       onCancel={props.onCancel}
31
       onCancel={props.onCancel}
32
-      >
32
+    >
33
       <iframe
33
       <iframe
34
         ref={ref}
34
         ref={ref}
35
-        style={{width: '100%', height: '100%'}}
35
+        style={{ width: '100%', height: '100%' }}
36
         src={`${window.location.origin}/preview-html/index.html`}
36
         src={`${window.location.origin}/preview-html/index.html`}
37
         frameBorder={0}
37
         frameBorder={0}
38
         name="preViewFrame"
38
         name="preViewFrame"
39
       ></iframe>
39
       ></iframe>
40
     </Modal>
40
     </Modal>
41
-  )
42
-}
41
+  );
42
+};

+ 12
- 12
src/components/WangEditor/PreviewMenu.js View File

1
-import E from 'wangeditor'
1
+import E from 'wangeditor';
2
 
2
 
3
-const { BtnMenu } = E
3
+const { BtnMenu } = E;
4
 
4
 
5
 class PreViewMenu extends BtnMenu {
5
 class PreViewMenu extends BtnMenu {
6
   constructor(editor) {
6
   constructor(editor) {
7
     // data-title属性表示当鼠标悬停在该按钮上时提示该按钮的功能简述
7
     // data-title属性表示当鼠标悬停在该按钮上时提示该按钮的功能简述
8
-      const $elem = E.$(
9
-          `<div class="w-e-menu" data-title="预览">
8
+    const $elem = E.$(
9
+      `<div class="w-e-menu" data-title="预览">
10
             <i>预览</i>
10
             <i>预览</i>
11
-          </div>`
12
-      )
13
-      super($elem, editor)
14
-      this.editor = editor
15
-      // <i class="w-e-icon-fullscreen"></i>
11
+          </div>`,
12
+    );
13
+    super($elem, editor);
14
+    this.editor = editor;
15
+    // <i class="w-e-icon-fullscreen"></i>
16
   }
16
   }
17
 
17
 
18
   static preview() {}
18
   static preview() {}
19
 
19
 
20
   clickHandler() {
20
   clickHandler() {
21
-    PreViewMenu.preview(this.editor.txt.html())
21
+    PreViewMenu.preview(this.editor.txt.html());
22
   }
22
   }
23
 
23
 
24
-  tryChangeActive(){}
24
+  tryChangeActive() {}
25
 }
25
 }
26
 
26
 
27
-export default PreViewMenu
27
+export default PreViewMenu;

+ 77
- 82
src/components/WangEditor/WangEditor.jsx View File

1
 import React, { useState, useRef, useEffect, useCallback } from 'react';
1
 import React, { useState, useRef, useEffect, useCallback } from 'react';
2
 import E from 'wangeditor';
2
 import E from 'wangeditor';
3
-import PreviewMenu from './PreviewMenu'
4
-import Preview from './Preview'
5
-import { fetch, apis } from '../../utils/request';
6
-
7
-export default props => {
8
-  const ref = useRef()
9
-  const editorRef = useRef()
10
-  const [preview, setPreview] = useState(false)
11
-  const [content, setContent] = useState()
12
-
13
-  // wangeditor 有bug, 初始先触发 onchange
14
-  const firstChanged = useRef(false)
3
+// import PreviewMenu from './PreviewMenu'
4
+import Preview from './Preview';
5
+import { uploadImage } from '@/utils/uploadFile';
6
+
7
+export default (props) => {
8
+  const ref = useRef();
9
+  const editorRef = useRef();
10
+  const [preview, setPreview] = useState(false);
11
+  // const [content, setContent] = useState()
12
+
13
+  const inited = useRef(false);
14
+  const handleChange = useCallback(
15
+    (html) => {
16
+      if ((inited.current || html) && typeof props.onChange === 'function') {
17
+        inited.current = true;
18
+        props.onChange(html);
19
+      }
20
+    },
21
+    [props],
22
+  );
15
 
23
 
16
   const initEditor = useCallback(() => {
24
   const initEditor = useCallback(() => {
17
-    const editor = new E(ref.current)
18
-    editorRef.current = editor
19
-    
25
+    const editor = new E(ref.current);
26
+    editorRef.current = editor;
27
+
20
     // 取消自动 focus
28
     // 取消自动 focus
21
-    editor.config.focus = false
29
+    editor.config.focus = false;
22
 
30
 
23
     // 触发 change
31
     // 触发 change
24
-    editor.config.onchange = html => {
25
-      // 规避 bug
26
-      if (!firstChanged.current) {
27
-        firstChanged.current = true
28
-        return
29
-      }
32
+    editor.config.onchange = handleChange;
30
 
33
 
31
-      setContent(html)
32
-
33
-      if (typeof props.onChange === 'function') {
34
-        props.onChange(html)
35
-      }
36
-    }
37
-
38
-    editor.config.zIndex = 100
34
+    editor.config.zIndex = 100;
39
 
35
 
40
     // 自定义图片上传
36
     // 自定义图片上传
41
-    editor.config.uploadImgMaxLength = 1
42
-    editor.config.customUploadImg = function (files, insert) {
43
-      if (!files.length) return
44
-      
45
-      const data = new FormData()
46
-      data.append('file', files[0])
47
-      fetch(apis.image.upload)({data}).then(insert)
48
-    }
37
+    editor.config.uploadImgMaxLength = 1;
38
+    editor.config.customUploadImg = (files, insert) => {
39
+      if (!files.length) return;
40
+
41
+      uploadImage(files[0]).then(insert);
42
+    };
49
 
43
 
50
     // 扩展预览按钮
44
     // 扩展预览按钮
51
-    editor.menus.extend('previewMenu', PreviewMenu)
52
-    PreviewMenu.preview = (html) => setPreview(true)
45
+    // editor.menus.extend('previewMenu', PreviewMenu)
46
+    // PreviewMenu.preview = (html) => setPreview(true)
53
 
47
 
54
     // 配置菜单
48
     // 配置菜单
55
     editor.config.menus = [
49
     editor.config.menus = [
56
-      'head',  // 标题
57
-      'bold',  // 粗体
58
-      'fontSize',  // 字号
59
-      'fontName',  // 字体
60
-      'italic',  // 斜体
61
-      'underline',  // 下划线
62
-      'strikeThrough',  // 删除线
63
-      'foreColor',  // 文字颜色
64
-      'backColor',  // 背景颜色
65
-      'list',  // 列表
66
-      'justify',  // 对齐方式
67
-      'quote',  // 引用
68
-      'image',  // 插入图片
69
-      'undo',  // 撤销
70
-      'redo',  // 重复
71
-      'previewMenu'
72
-    ]
50
+      'head', // 标题
51
+      'bold', // 粗体
52
+      'fontSize', // 字号
53
+      'fontName', // 字体
54
+      'italic', // 斜体
55
+      'underline', // 下划线
56
+      'strikeThrough', // 删除线
57
+      'foreColor', // 文字颜色
58
+      'backColor', // 背景颜色
59
+      'list', // 列表
60
+      'justify', // 对齐方式
61
+      'quote', // 引用
62
+      'image', // 插入图片
63
+      'undo', // 撤销
64
+      'redo', // 重复
65
+      // 'previewMenu'
66
+    ];
73
 
67
 
74
     // 过滤 word 字符
68
     // 过滤 word 字符
75
-    editor.config.pasteFilterStyle = false
76
-    editor.config.pasteTextHandle = ctt => {
69
+    editor.config.pasteFilterStyle = false;
70
+    editor.config.pasteTextHandle = (ctt) => {
77
       const regs = [
71
       const regs = [
78
-        /<!--\[if [\s\S]*?endif\]-->/ig,
79
-        /<[a-zA-Z0-9]+\:[^>]+>[^>]*<\/[a-zA-Z0-9]+\:[^>]+>/ig,
80
-        /<[a-zA-Z0-9]+\:[^>]+\/>/ig,
81
-        /<style>[\s\S]*?<\/style>/ig,
82
-        new RegExp('\u2029', 'ig'),     // 替换word分隔符 序号 8233
83
-      ]
72
+        /<!--\[if [\s\S]*?endif\]-->/gi,
73
+        /<[a-zA-Z0-9]+:[^>]+>[^>]*<\/[a-zA-Z0-9]+:[^>]+>/gi,
74
+        /<[a-zA-Z0-9]+:[^>]+\/>/gi,
75
+        /<style>[\s\S]*?<\/style>/gi,
76
+        new RegExp('\u2029', 'ig'), // 替换word分隔符 序号 8233
77
+      ];
84
 
78
 
85
       return regs.reduce((acc, reg) => {
79
       return regs.reduce((acc, reg) => {
86
-        return acc.replace(reg, '')
87
-      }, ctt)
88
-    }
80
+        return acc.replace(reg, '');
81
+      }, ctt);
82
+    };
89
 
83
 
90
-    editor.create()
91
-    editor.$textElem.attr('contenteditable', props.contenteditable !== false)
84
+    editor.create();
85
+    editor.$textElem.attr('contenteditable', props.contenteditable !== false);
92
 
86
 
93
-    return () => editor.destroy()
94
-  }, [props.contenteditable])
87
+    return () => editor.destroy();
88
+  }, [props, handleChange]);
95
 
89
 
96
   useEffect(() => {
90
   useEffect(() => {
97
-    initEditor()
98
-  }, [])
99
-  
91
+    initEditor();
92
+  }, [initEditor]);
93
+
94
+  //
100
   useEffect(() => {
95
   useEffect(() => {
101
-    if (props.value !== content && editorRef.current) {
102
-      setContent(props.value)
103
-      editorRef.current.txt.html(props.value)
96
+    if (props.value && !inited.current && editorRef.current) {
97
+      inited.current = true;
98
+      editorRef.current.txt.html(props.value);
104
     }
99
     }
105
-  }, [props.value, content])
100
+  }, [props.value]);
106
 
101
 
107
   return (
102
   return (
108
     <>
103
     <>
109
       <div ref={ref} style={{ textAlign: 'left' }} />
104
       <div ref={ref} style={{ textAlign: 'left' }} />
110
       <Preview
105
       <Preview
111
         width={426}
106
         width={426}
112
-        style={{width: '426px', height: '863px', margin: 0, padding: 0}}
107
+        style={{ width: '426px', height: '863px', margin: 0, padding: 0 }}
113
         visible={preview}
108
         visible={preview}
114
         html={props.value}
109
         html={props.value}
115
         onCancel={() => setPreview(false)}
110
         onCancel={() => setPreview(false)}
116
       />
111
       />
117
     </>
112
     </>
118
-  )
119
-}
113
+  );
114
+};

+ 3
- 0
src/components/WangEditor/index.js View File

1
+import WangEditor from './WangEditor';
2
+
3
+export default WangEditor;

+ 1
- 1
src/global.jsx View File

12
       // useIntl().formatMessage({
12
       // useIntl().formatMessage({
13
       //   id: 'app.pwa.offline',
13
       //   id: 'app.pwa.offline',
14
       // }),
14
       // }),
15
-      "当前处于离线状态"
15
+      '当前处于离线状态',
16
     );
16
     );
17
   }); // Pop up a prompt on the page asking the user if they want to use the latest version
17
   }); // Pop up a prompt on the page asking the user if they want to use the latest version
18
 
18
 

+ 38
- 38
src/layouts/BasicLayout.jsx View File

4
  * @see You can view component api by: https://github.com/ant-design/ant-design-pro-layout
4
  * @see You can view component api by: https://github.com/ant-design/ant-design-pro-layout
5
  */
5
  */
6
 import ProLayout, { DefaultFooter } from '@ant-design/pro-layout';
6
 import ProLayout, { DefaultFooter } from '@ant-design/pro-layout';
7
-import React, { useEffect, useMemo } from 'react';
7
+import React, { useMemo } from 'react';
8
 // import { Link, useIntl, connect, history } from 'umi';
8
 // import { Link, useIntl, connect, history } from 'umi';
9
 import { Link, connect, history } from 'umi';
9
 import { Link, connect, history } from 'umi';
10
-import { GithubOutlined } from '@ant-design/icons';
10
+// import { GithubOutlined } from '@ant-design/icons';
11
 import { Result, Button } from 'antd';
11
 import { Result, Button } from 'antd';
12
 import Authorized from '@/utils/Authorized';
12
 import Authorized from '@/utils/Authorized';
13
-import { getCurrentRoute } from '@/utils/utils'
13
+import { getCurrentRoute } from '@/utils/utils';
14
 import RightContent from '@/components/GlobalHeader/RightContent';
14
 import RightContent from '@/components/GlobalHeader/RightContent';
15
 // import { getMatchMenu } from '@umijs/route-utils';
15
 // import { getMatchMenu } from '@umijs/route-utils';
16
 import logo from '../assets/logo.svg';
16
 import logo from '../assets/logo.svg';
31
 /** Use Authorized check all menu item */
31
 /** Use Authorized check all menu item */
32
 const menuDataRender = (menuRoles) => (menuList) =>
32
 const menuDataRender = (menuRoles) => (menuList) =>
33
   menuList.map((item) => {
33
   menuList.map((item) => {
34
-    const needAuth = !!item.menuCode
34
+    const needAuth = !!item.menuCode;
35
 
35
 
36
-    const authority = (menuRoles.filter(x => x.menuCode === item.menuCode)[0] || {}).roleString || ''
36
+    const authority =
37
+      (menuRoles.filter((x) => x.menuCode === item.menuCode)[0] || {}).roleString || '';
37
 
38
 
38
     const localItem = {
39
     const localItem = {
39
       ...item,
40
       ...item,
46
 const defaultFooterDom = (
47
 const defaultFooterDom = (
47
   <DefaultFooter
48
   <DefaultFooter
48
     copyright={`${new Date().getFullYear()} 云致科技`}
49
     copyright={`${new Date().getFullYear()} 云致科技`}
49
-    links={[
50
-      // {
51
-      //   key: 'Ant Design Pro',
52
-      //   title: 'Ant Design Pro',
53
-      //   href: 'https://pro.ant.design',
54
-      //   blankTarget: true,
55
-      // },
56
-      // {
57
-      //   key: 'github',
58
-      //   title: <GithubOutlined />,
59
-      //   href: 'https://github.com/ant-design/ant-design-pro',
60
-      //   blankTarget: true,
61
-      // },
62
-      // {
63
-      //   key: 'Ant Design',
64
-      //   title: 'Ant Design',
65
-      //   href: 'https://ant.design',
66
-      //   blankTarget: true,
67
-      // },
68
-    ]}
50
+    links={
51
+      [
52
+        // {
53
+        //   key: 'Ant Design Pro',
54
+        //   title: 'Ant Design Pro',
55
+        //   href: 'https://pro.ant.design',
56
+        //   blankTarget: true,
57
+        // },
58
+        // {
59
+        //   key: 'github',
60
+        //   title: <GithubOutlined />,
61
+        //   href: 'https://github.com/ant-design/ant-design-pro',
62
+        //   blankTarget: true,
63
+        // },
64
+        // {
65
+        //   key: 'Ant Design',
66
+        //   title: 'Ant Design',
67
+        //   href: 'https://ant.design',
68
+        //   blankTarget: true,
69
+        // },
70
+      ]
71
+    }
69
   />
72
   />
70
 );
73
 );
71
 
74
 
90
     }
93
     }
91
   }; // get children authority
94
   }; // get children authority
92
 
95
 
93
-  const authorized = useMemo(
94
-    () => {
95
-      const routeItem = getCurrentRoute(location.pathname || '/')
96
-      if (!routeItem || !routeItem.menuCode) {
97
-        return { authority: undefined }
98
-      }
96
+  const authorized = useMemo(() => {
97
+    const routeItem = getCurrentRoute(location.pathname || '/');
98
+    if (!routeItem || !routeItem.menuCode) {
99
+      return { authority: undefined };
100
+    }
99
 
101
 
100
-      const authority = (menus.filter(x => x.menuCode === routeItem.menuCode)[0] || {}).roleString
101
-      return {
102
-        authority: authority ? authority.split(',') : ['any-string-for-no-right']
103
-      }
104
-    },
105
-    [location.pathname, menus],
106
-  );
102
+    const authority = (menus.filter((x) => x.menuCode === routeItem.menuCode)[0] || {}).roleString;
103
+    return {
104
+      authority: authority ? authority.split(',') : ['any-string-for-no-right'],
105
+    };
106
+  }, [location.pathname, menus]);
107
 
107
 
108
   // const { formatMessage } = useIntl();
108
   // const { formatMessage } = useIntl();
109
 
109
 

+ 1
- 1
src/layouts/UserLayout.jsx View File

23
   const { breadcrumb } = getMenuData(routes);
23
   const { breadcrumb } = getMenuData(routes);
24
   const title = getPageTitle({
24
   const title = getPageTitle({
25
     pathname: location.pathname,
25
     pathname: location.pathname,
26
-    formatMessage: x => x,
26
+    formatMessage: (x) => x,
27
     breadcrumb,
27
     breadcrumb,
28
     ...props,
28
     ...props,
29
   });
29
   });

+ 1
- 0
src/locales/en-US.js View File

5
 import settingDrawer from './en-US/settingDrawer';
5
 import settingDrawer from './en-US/settingDrawer';
6
 import settings from './en-US/settings';
6
 import settings from './en-US/settings';
7
 import pages from './en-US/pages';
7
 import pages from './en-US/pages';
8
+
8
 export default {
9
 export default {
9
   'navBar.lang': 'Languages',
10
   'navBar.lang': 'Languages',
10
   'layout.user.link.help': 'Help',
11
   'layout.user.link.help': 'Help',

+ 1
- 0
src/locales/id-ID.js View File

5
 import settingDrawer from './id-ID/settingDrawer';
5
 import settingDrawer from './id-ID/settingDrawer';
6
 import settings from './id-ID/settings';
6
 import settings from './id-ID/settings';
7
 import pages from './id-ID/pages';
7
 import pages from './id-ID/pages';
8
+
8
 export default {
9
 export default {
9
   'navbar.lang': 'Bahasa',
10
   'navbar.lang': 'Bahasa',
10
   'layout.user.link.help': 'Bantuan',
11
   'layout.user.link.help': 'Bantuan',

+ 1
- 0
src/locales/ja-JP.js View File

5
 import pwa from './ja-JP/pwa';
5
 import pwa from './ja-JP/pwa';
6
 import component from './ja-JP/component';
6
 import component from './ja-JP/component';
7
 import pages from './ja-JP/pages';
7
 import pages from './ja-JP/pages';
8
+
8
 export default {
9
 export default {
9
   'navBar.lang': '言語',
10
   'navBar.lang': '言語',
10
   'layout.user.link.help': 'ヘルプ',
11
   'layout.user.link.help': 'ヘルプ',

+ 1
- 0
src/locales/pt-BR.js View File

4
 import pwa from './pt-BR/pwa';
4
 import pwa from './pt-BR/pwa';
5
 import settingDrawer from './pt-BR/settingDrawer';
5
 import settingDrawer from './pt-BR/settingDrawer';
6
 import settings from './pt-BR/settings';
6
 import settings from './pt-BR/settings';
7
+
7
 export default {
8
 export default {
8
   'navBar.lang': 'Idiomas',
9
   'navBar.lang': 'Idiomas',
9
   'layout.user.link.help': 'ajuda',
10
   'layout.user.link.help': 'ajuda',

+ 1
- 0
src/locales/zh-CN.js View File

5
 import settingDrawer from './zh-CN/settingDrawer';
5
 import settingDrawer from './zh-CN/settingDrawer';
6
 import settings from './zh-CN/settings';
6
 import settings from './zh-CN/settings';
7
 import pages from './zh-CN/pages';
7
 import pages from './zh-CN/pages';
8
+
8
 export default {
9
 export default {
9
   'navBar.lang': '语言',
10
   'navBar.lang': '语言',
10
   'layout.user.link.help': '帮助',
11
   'layout.user.link.help': '帮助',

+ 1
- 0
src/locales/zh-TW.js View File

4
 import pwa from './zh-TW/pwa';
4
 import pwa from './zh-TW/pwa';
5
 import settingDrawer from './zh-TW/settingDrawer';
5
 import settingDrawer from './zh-TW/settingDrawer';
6
 import settings from './zh-TW/settings';
6
 import settings from './zh-TW/settings';
7
+
7
 export default {
8
 export default {
8
   'navBar.lang': '語言',
9
   'navBar.lang': '語言',
9
   'layout.user.link.help': '幫助',
10
   'layout.user.link.help': '幫助',

+ 1
- 0
src/models/global.js View File

1
 import { queryNotices } from '@/services/user';
1
 import { queryNotices } from '@/services/user';
2
+
2
 const GlobalModel = {
3
 const GlobalModel = {
3
   namespace: 'global',
4
   namespace: 'global',
4
   state: {
5
   state: {

+ 10
- 10
src/models/login.js View File

12
   },
12
   },
13
   effects: {
13
   effects: {
14
     *login({ payload }, { call, put }) {
14
     *login({ payload }, { call, put }) {
15
-      let response
16
-      let status
15
+      let response;
16
+      let status;
17
       try {
17
       try {
18
         response = yield call(userLogin, payload);
18
         response = yield call(userLogin, payload);
19
-        status = 'success'
19
+        status = 'success';
20
       } catch (e) {
20
       } catch (e) {
21
         // message.error(e.message)
21
         // message.error(e.message)
22
-        status = 'error'
22
+        status = 'error';
23
       }
23
       }
24
 
24
 
25
       yield put({
25
       yield put({
28
       });
28
       });
29
 
29
 
30
       if (status === 'success') {
30
       if (status === 'success') {
31
-        localStorage.setItem('token', response.token)
31
+        localStorage.setItem('token', response.token);
32
 
32
 
33
         const urlParams = new URL(window.location.href);
33
         const urlParams = new URL(window.location.href);
34
         const params = getPageQuery();
34
         const params = getPageQuery();
35
         message.success('🎉 🎉 🎉  登录成功!');
35
         message.success('🎉 🎉 🎉  登录成功!');
36
         let { redirect } = params;
36
         let { redirect } = params;
37
-  
37
+
38
         if (redirect) {
38
         if (redirect) {
39
           const redirectUrlParams = new URL(redirect);
39
           const redirectUrlParams = new URL(redirect);
40
-  
40
+
41
           if (redirectUrlParams.origin === urlParams.origin) {
41
           if (redirectUrlParams.origin === urlParams.origin) {
42
             redirect = redirect.substr(urlParams.origin.length);
42
             redirect = redirect.substr(urlParams.origin.length);
43
-  
43
+
44
             if (window.routerBase !== '/') {
44
             if (window.routerBase !== '/') {
45
               redirect = redirect.replace(window.routerBase, '/');
45
               redirect = redirect.replace(window.routerBase, '/');
46
             }
46
             }
47
-  
47
+
48
             if (redirect.match(/^\/.*#/)) {
48
             if (redirect.match(/^\/.*#/)) {
49
               redirect = redirect.substr(redirect.indexOf('#') + 1);
49
               redirect = redirect.substr(redirect.indexOf('#') + 1);
50
             }
50
             }
53
             return;
53
             return;
54
           }
54
           }
55
         }
55
         }
56
-  
56
+
57
         history.replace(redirect || '/');
57
         history.replace(redirect || '/');
58
       }
58
       }
59
     },
59
     },

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

30
   },
30
   },
31
   reducers: {
31
   reducers: {
32
     saveCurrentUser(state, action) {
32
     saveCurrentUser(state, action) {
33
-      const { user, menus, roles } = action.payload || {}
33
+      const { user, menus, roles } = action.payload || {};
34
 
34
 
35
-      setAuthority(roles.map(x => `${x.roleId}`))
35
+      setAuthority(roles.map((x) => `${x.roleId}`));
36
 
36
 
37
       return { ...state, currentUser: user, menus };
37
       return { ...state, currentUser: user, menus };
38
     },
38
     },

+ 2
- 1
src/pages/Admin.jsx View File

7
 export default () => {
7
 export default () => {
8
   // const intl = useIntl();
8
   // const intl = useIntl();
9
   return (
9
   return (
10
-    <PageHeaderWrapper content="这个页面只有 admin 权限才能查看"
10
+    <PageHeaderWrapper
11
+      content="这个页面只有 admin 权限才能查看"
11
       // content={intl.formatMessage({
12
       // content={intl.formatMessage({
12
       //   id: 'pages.admin.subPage.title',
13
       //   id: 'pages.admin.subPage.title',
13
       //   defaultMessage: ' 这个页面只有 admin 权限才能查看',
14
       //   defaultMessage: ' 这个页面只有 admin 权限才能查看',

+ 10
- 0
src/pages/Post/Edit/components/Answer.jsx View File

1
+import React from 'react';
2
+import TestQuestions from '@/components/TestQuestions';
3
+
4
+export default () => {
5
+  return (
6
+    <div>
7
+      <TestQuestions />
8
+    </div>
9
+  );
10
+};

+ 68
- 56
src/pages/Post/Edit/components/Form.jsx View File

1
-import React, { useEffect, useMemo } from 'react'
2
-import ProForm,
3
-  {
4
-    ProFormText,
5
-    ProFormSelect,
6
-    ProFormUploadButton,
7
-    ProFormTextArea,
8
-    ProFormSwitch,
9
-    ProFormDigit,
10
-    ProFormRadio,
11
-  } from '@ant-design/pro-form'
12
-import UploadImage from '@/components/UploadImage'
13
-import request from '@/utils/request'
14
-import { notification, Form } from 'antd'
15
-  
1
+import React, { useEffect, useMemo, useState } from 'react';
2
+import ProForm, {
3
+  ProFormText,
4
+  ProFormSelect,
5
+  ProFormTextArea,
6
+  ProFormSwitch,
7
+  ProFormDigit,
8
+  ProFormRadio,
9
+} from '@ant-design/pro-form';
10
+import UploadImage from '@/components/UploadImage';
11
+import UploadVideo from '@/components/UploadVideo';
12
+import WangEditor from '@/components/WangEditor';
13
+import request from '@/utils/request';
14
+import { notification, Form } from 'antd';
15
+
16
 export default (props) => {
16
 export default (props) => {
17
-  const [form] = Form.useForm()
18
-  
17
+  const [form] = Form.useForm();
18
+  const [isVideo, setIsVideo] = useState(false);
19
+
19
   const typeDict = useMemo(() => {
20
   const typeDict = useMemo(() => {
20
     return (props.typeList || []).reduce((acc, item) => {
21
     return (props.typeList || []).reduce((acc, item) => {
21
       return {
22
       return {
22
         ...acc,
23
         ...acc,
23
-        [item.typeId]: item.name
24
-      }
25
-    }, {})
26
-  }, [props.typeList])
24
+        [item.typeId]: item.name,
25
+      };
26
+    }, {});
27
+  }, [props.typeList]);
27
 
28
 
28
   const handleSubmit = (values) => {
29
   const handleSubmit = (values) => {
29
     if (!values.postId) {
30
     if (!values.postId) {
30
-      return request('/post', { method: 'post', data: values }).then((res) => {
31
-        props.onChange(res)
32
-      }).catch((e) => {
33
-        notification.error({ message: e.message })
34
-        return Promise.reject(e.message)
35
-      })
31
+      return request('/post', { method: 'post', data: values })
32
+        .then((res) => {
33
+          props.onChange(res);
34
+        })
35
+        .catch((e) => {
36
+          notification.error({ message: e.message });
37
+          return Promise.reject(e.message);
38
+        });
36
     }
39
     }
37
-  }
40
+
41
+    // eslint-disable-next-line
42
+    return;
43
+  };
44
+
45
+  const handleValueChange = (changed) => {
46
+    const key = Object.keys(changed)[0];
47
+
48
+    if (key === 'isVideo') {
49
+      setIsVideo(changed[key]);
50
+    }
51
+  };
38
 
52
 
39
   useEffect(() => {
53
   useEffect(() => {
40
     if (props.post && props.post.postId) {
54
     if (props.post && props.post.postId) {
41
-      form.setFieldsValue(props.post)
55
+      form.setFieldsValue(props.post);
56
+      setIsVideo(props.post.isVideo);
42
     }
57
     }
43
-  }, [props.post, form])
58
+  }, [props.post, form]);
44
 
59
 
45
   return (
60
   return (
46
-    <ProForm form={form} onFinish={handleSubmit}>
61
+    <ProForm form={form} onFinish={handleSubmit} onValuesChange={handleValueChange}>
47
       <ProFormText
62
       <ProFormText
48
         label="标题"
63
         label="标题"
49
         placeholder="请输入标题"
64
         placeholder="请输入标题"
70
         <UploadImage />
85
         <UploadImage />
71
       </Form.Item>
86
       </Form.Item>
72
 
87
 
73
-      <ProFormTextArea
74
-        label="简介"
75
-        placeholder="请输入简介"
76
-        name="summary"
77
-      />
88
+      <ProFormTextArea label="简介" placeholder="请输入简介" name="summary" />
78
 
89
 
79
-      <ProFormSwitch
80
-        label="视频"
81
-        name="isVideo"
82
-        checkedChildren="视频"
83
-        unCheckedChildren="图文"
84
-      />
90
+      <ProFormSwitch label="视频" name="isVideo" checkedChildren="视频" unCheckedChildren="图文" />
91
+
92
+      {isVideo ? (
93
+        <>
94
+          <Form.Item name="videoPoster" label="视频封面" placeholder="请设置封面">
95
+            <UploadImage />
96
+          </Form.Item>
97
+          <Form.Item name="videoUrl" label="视频地址" placeholder="请上传视频">
98
+            <UploadVideo />
99
+          </Form.Item>
100
+        </>
101
+      ) : (
102
+        <Form.Item name="videoUrl" label="图文内容" placeholder="请填写图文内容">
103
+          <WangEditor />
104
+        </Form.Item>
105
+      )}
85
 
106
 
86
       <ProFormDigit
107
       <ProFormDigit
87
         label="答题数"
108
         label="答题数"
105
         precision={0}
126
         precision={0}
106
       />
127
       />
107
 
128
 
108
-      <ProFormSwitch
109
-        label="热门"
110
-        name="isTopic"
111
-        checkedChildren="是"
112
-        unCheckedChildren="否"
113
-      />
129
+      <ProFormSwitch label="热门" name="isTopic" checkedChildren="是" unCheckedChildren="否" />
114
 
130
 
115
       <ProFormRadio.Group
131
       <ProFormRadio.Group
116
         label="状态"
132
         label="状态"
126
           },
142
           },
127
         ]}
143
         ]}
128
       />
144
       />
129
-      
130
-      <ProFormText
131
-        label="创建日期"
132
-        name="createDate"
133
-        readonly
134
-      />
145
+
146
+      <ProFormText label="创建日期" name="createDate" readonly />
135
     </ProForm>
147
     </ProForm>
136
-  )
137
-}
148
+  );
149
+};

+ 34
- 30
src/pages/Post/Edit/index.jsx View File

1
-import React, { useEffect, useMemo, useState } from 'react'
2
-import { connect } from 'umi'
3
-import { notification, Tabs } from 'antd'
4
-import { PageContainer } from '@ant-design/pro-layout'
5
-import Container from '@/components/Container'
6
-import request from '@/utils/request'
7
-import PostForm from './components/Form'
1
+import React, { useEffect, useState } from 'react';
2
+import { connect } from 'umi';
3
+import { notification, Tabs } from 'antd';
4
+import { PageContainer } from '@ant-design/pro-layout';
5
+import Container from '@/components/Container';
6
+import request from '@/utils/request';
7
+import PostForm from './components/Form';
8
+import Answer from './components/Answer';
9
+
10
+const { TabPane } = Tabs;
8
 
11
 
9
-const { TabPane } = Tabs
10
-  
11
 const PostEdit = (props) => {
12
 const PostEdit = (props) => {
12
-  const { id } = props.location.query
13
-  const [loading, setLoading] = useState(false)
14
-  const [post, setPost] = useState({})
13
+  const { id } = props.location.query;
14
+  const [loading, setLoading] = useState(false);
15
+  const [post, setPost] = useState({});
15
 
16
 
16
   const handleFormChange = (newPost) => {
17
   const handleFormChange = (newPost) => {
17
-    setPost(newPost)
18
-  }
18
+    setPost(newPost);
19
+  };
19
 
20
 
20
   useEffect(() => {
21
   useEffect(() => {
21
-    setLoading(true)
22
-    request(`/post/${id}`).then((res) => {
23
-      setPost(res)
24
-      setLoading(false)
25
-    }).catch((e) => {
26
-      setLoading(false)
27
-      notification.error({ message: e.message })
28
-    })
29
-  }, [id])
22
+    setLoading(true);
23
+    request(`/post/${id}`)
24
+      .then((res) => {
25
+        setPost(res);
26
+        setLoading(false);
27
+      })
28
+      .catch((e) => {
29
+        setLoading(false);
30
+        notification.error({ message: e.message });
31
+      });
32
+  }, [id]);
30
 
33
 
31
   useEffect(() => {
34
   useEffect(() => {
32
     if (!props.typeList || !props.typeList.length) {
35
     if (!props.typeList || !props.typeList.length) {
33
       props.dispatch({
36
       props.dispatch({
34
         type: 'post/getTypeList',
37
         type: 'post/getTypeList',
35
-        payload: { pageSize: 999 }
36
-      })
38
+        payload: { pageSize: 999 },
39
+      });
37
     }
40
     }
38
-  }, [props])
41
+  }, [props]);
39
 
42
 
40
   return (
43
   return (
41
     <PageContainer>
44
     <PageContainer>
48
           </TabPane>
51
           </TabPane>
49
           <TabPane tab="题库设置" key="2">
52
           <TabPane tab="题库设置" key="2">
50
             <Container>
53
             <Container>
54
+              <Answer />
51
             </Container>
55
             </Container>
52
           </TabPane>
56
           </TabPane>
53
         </Tabs>
57
         </Tabs>
54
       </Container>
58
       </Container>
55
     </PageContainer>
59
     </PageContainer>
56
-  )
57
-}
60
+  );
61
+};
58
 
62
 
59
 export default connect((s) => ({
63
 export default connect((s) => ({
60
-  typeList: s.post.typeList
61
-}))(PostEdit)
64
+  typeList: s.post.typeList,
65
+}))(PostEdit);

+ 3
- 1
src/pages/Post/List/index.jsx View File

100
       key: 'option',
100
       key: 'option',
101
       valueType: 'option',
101
       valueType: 'option',
102
       render: (_, item) => [
102
       render: (_, item) => [
103
-        <a key="opt1" onClick={() => history.push(`/post/edit?id=${item.postId}`)}>编辑</a>,
103
+        <a key="opt1" onClick={() => history.push(`/post/edit?id=${item.postId}`)}>
104
+          编辑
105
+        </a>,
104
       ],
106
       ],
105
     },
107
     },
106
   ];
108
   ];

+ 3
- 3
src/pages/TableList/components/UpdateForm.jsx View File

64
           rules={[
64
           rules={[
65
             {
65
             {
66
               required: true,
66
               required: true,
67
-              message: "请输入规则名称!",
67
+              message: '请输入规则名称!',
68
               // message: (
68
               // message: (
69
               //   <FormattedMessage
69
               //   <FormattedMessage
70
               //     id="pages.searchTable.updateForm.ruleName.nameRules"
70
               //     id="pages.searchTable.updateForm.ruleName.nameRules"
90
           rules={[
90
           rules={[
91
             {
91
             {
92
               required: true,
92
               required: true,
93
-              message: "请输入至少五个字符的规则描述!",
93
+              message: '请输入至少五个字符的规则描述!',
94
               // message: (
94
               // message: (
95
               //   <FormattedMessage
95
               //   <FormattedMessage
96
               //     id="pages.searchTable.updateForm.ruleDesc.descRules"
96
               //     id="pages.searchTable.updateForm.ruleDesc.descRules"
180
           rules={[
180
           rules={[
181
             {
181
             {
182
               required: true,
182
               required: true,
183
-              message: "请选择开始时间!",
183
+              message: '请选择开始时间!',
184
               // message: (
184
               // message: (
185
               //   <FormattedMessage
185
               //   <FormattedMessage
186
               //     id="pages.searchTable.updateForm.schedulingPeriod.timeRules"
186
               //     id="pages.searchTable.updateForm.schedulingPeriod.timeRules"

+ 3
- 6
src/pages/TableList/index.jsx View File

260
           extra={
260
           extra={
261
             <div>
261
             <div>
262
               {/* <FormattedMessage id="pages.searchTable.chosen" defaultMessage="已选择" /> */}
262
               {/* <FormattedMessage id="pages.searchTable.chosen" defaultMessage="已选择" /> */}
263
-              已选择 {' '}
263
+              已选择{' '}
264
               <a
264
               <a
265
                 style={{
265
                 style={{
266
                   fontWeight: 600,
266
                   fontWeight: 600,
268
               >
268
               >
269
                 {selectedRowsState.length}
269
                 {selectedRowsState.length}
270
               </a>{' '}
270
               </a>{' '}
271
-              项
272
-              {/* <FormattedMessage id="pages.searchTable.item" defaultMessage="项" /> */}
271
+              项{/* <FormattedMessage id="pages.searchTable.item" defaultMessage="项" /> */}
273
               &nbsp;&nbsp;
272
               &nbsp;&nbsp;
274
               <span>
273
               <span>
275
                 {/* <FormattedMessage
274
                 {/* <FormattedMessage
276
                   id="pages.searchTable.totalServiceCalls"
275
                   id="pages.searchTable.totalServiceCalls"
277
                   defaultMessage="服务调用次数总计"
276
                   defaultMessage="服务调用次数总计"
278
                 /> */}
277
                 /> */}
279
-                服务调用次数总计
280
-                {' '}
281
-                {selectedRowsState.reduce((pre, item) => pre + item.callNo, 0)}{' '}
278
+                服务调用次数总计 {selectedRowsState.reduce((pre, item) => pre + item.callNo, 0)}{' '}
282
                 {/* <FormattedMessage id="pages.searchTable.tenThousand" defaultMessage="万" /> */}
279
                 {/* <FormattedMessage id="pages.searchTable.tenThousand" defaultMessage="万" /> */}
283
280
284
               </span>
281
               </span>

+ 1
- 0
src/pages/TableList/service.js View File

1
 import request from '@/utils/request';
1
 import request from '@/utils/request';
2
+
2
 export async function queryRule(params) {
3
 export async function queryRule(params) {
3
   return request('/api/rule', {
4
   return request('/api/rule', {
4
     params,
5
     params,

+ 7
- 9
src/pages/User/login/index.jsx View File

1
-import {
2
-  LockOutlined,
3
-  UserOutlined,
4
-} from '@ant-design/icons';
1
+import { LockOutlined, UserOutlined } from '@ant-design/icons';
5
 import { Alert } from 'antd';
2
 import { Alert } from 'antd';
6
-import React, { useState } from 'react';
3
+import React from 'react';
7
 import ProForm, { ProFormText } from '@ant-design/pro-form';
4
 import ProForm, { ProFormText } from '@ant-design/pro-form';
8
 // import { useIntl, connect, FormattedMessage } from 'umi';
5
 // import { useIntl, connect, FormattedMessage } from 'umi';
9
 import { connect } from 'umi';
6
 import { connect } from 'umi';
10
-import md5 from 'md5'
7
+import md5 from 'md5';
11
 
8
 
12
 import styles from './index.less';
9
 import styles from './index.less';
13
 
10
 
57
         }}
54
         }}
58
       >
55
       >
59
         {status === 'error' && !submitting && (
56
         {status === 'error' && !submitting && (
60
-          <LoginMessage content="账户或密码错误"
57
+          <LoginMessage
58
+            content="账户或密码错误"
61
             // content={intl.formatMessage({
59
             // content={intl.formatMessage({
62
             //   id: 'pages.login.accountLogin.errorMessage',
60
             //   id: 'pages.login.accountLogin.errorMessage',
63
             //   defaultMessage: '账户或密码错误(admin/ant.design)',
61
             //   defaultMessage: '账户或密码错误(admin/ant.design)',
78
           rules={[
76
           rules={[
79
             {
77
             {
80
               required: true,
78
               required: true,
81
-              message: "请输入用户名!",
79
+              message: '请输入用户名!',
82
               // message: (
80
               // message: (
83
               //   <FormattedMessage
81
               //   <FormattedMessage
84
               //     id="pages.login.username.required"
82
               //     id="pages.login.username.required"
108
               //     defaultMessage="请输入密码!"
106
               //     defaultMessage="请输入密码!"
109
               //   />
107
               //   />
110
               // ),
108
               // ),
111
-              message: "请输入密码!"
109
+              message: '请输入密码!',
112
             },
110
             },
113
           ]}
111
           ]}
114
         />
112
         />

+ 2
- 4
src/pages/Welcome.jsx View File

33
         />
33
         />
34
         <Typography.Text strong>
34
         <Typography.Text strong>
35
           {/* <FormattedMessage id="pages.welcome.advancedComponent" defaultMessage="高级表格" /> */}
35
           {/* <FormattedMessage id="pages.welcome.advancedComponent" defaultMessage="高级表格" /> */}
36
-          高级表格
37
-          {' '}
36
+          高级表格{' '}
38
           <a
37
           <a
39
             href="https://procomponents.ant.design/components/table"
38
             href="https://procomponents.ant.design/components/table"
40
             rel="noopener noreferrer"
39
             rel="noopener noreferrer"
52
           }}
51
           }}
53
         >
52
         >
54
           {/* <FormattedMessage id="pages.welcome.advancedLayout" defaultMessage="高级布局" /> */}
53
           {/* <FormattedMessage id="pages.welcome.advancedLayout" defaultMessage="高级布局" /> */}
55
-          高级布局
56
-          {' '}
54
+          高级布局{' '}
57
           <a
55
           <a
58
             href="https://procomponents.ant.design/components/layout"
56
             href="https://procomponents.ant.design/components/layout"
59
             rel="noopener noreferrer"
57
             rel="noopener noreferrer"

+ 4
- 4
src/pages/model.js View File

4
 const Model = {
4
 const Model = {
5
   namespace: 'post',
5
   namespace: 'post',
6
   state: {
6
   state: {
7
-    typeList: []
7
+    typeList: [],
8
   },
8
   },
9
   effects: {
9
   effects: {
10
     *getTypeList({ payload }, { call, put }) {
10
     *getTypeList({ payload }, { call, put }) {
11
-      let response
11
+      let response;
12
 
12
 
13
       try {
13
       try {
14
         response = yield call((params) => request('/post-type', { params }), payload);
14
         response = yield call((params) => request('/post-type', { params }), payload);
15
       } catch (e) {
15
       } catch (e) {
16
-        message.error(e.message)
17
-        return
16
+        message.error(e.message);
17
+        return;
18
       }
18
       }
19
 
19
 
20
       yield put({
20
       yield put({

+ 3
- 3
src/utils/authority.js View File

1
 import { reloadAuthorized } from './Authorized';
1
 import { reloadAuthorized } from './Authorized';
2
 
2
 
3
-const authority = { value: undefined }
3
+const authority = { value: undefined };
4
 
4
 
5
 export function getAuthority() {
5
 export function getAuthority() {
6
   // 这个 if 不要删除
6
   // 这个 if 不要删除
7
   // 虽然 authority 是全局的, 但是这个地方第一次仍然访问不到
7
   // 虽然 authority 是全局的, 但是这个地方第一次仍然访问不到
8
   // 原因未知
8
   // 原因未知
9
   if (!authority) {
9
   if (!authority) {
10
-    return undefined
10
+    return undefined;
11
   }
11
   }
12
 
12
 
13
   return authority.value;
13
   return authority.value;
14
 }
14
 }
15
 
15
 
16
 export function setAuthority(auth) {
16
 export function setAuthority(auth) {
17
-  authority.value = auth
17
+  authority.value = auth;
18
   reloadAuthorized();
18
   reloadAuthorized();
19
 }
19
 }

+ 43
- 39
src/utils/request.js View File

31
       description: errorText,
31
       description: errorText,
32
     });
32
     });
33
   } else if (error.code && error.message) {
33
   } else if (error.code && error.message) {
34
-    throw new Error(error.message)
34
+    throw new Error(error.message);
35
   } else {
35
   } else {
36
     notification.error({
36
     notification.error({
37
       description: '您的网络发生异常,无法连接服务器',
37
       description: '您的网络发生异常,无法连接服务器',
44
 /** 配置request请求时的默认参数 */
44
 /** 配置request请求时的默认参数 */
45
 
45
 
46
 const request = extend({
46
 const request = extend({
47
-  prefix: process.env.NODE_ENV === 'production' ? '/api/admin' : '/api/admin', 
47
+  prefix: process.env.NODE_ENV === 'production' ? '/api/admin' : '/api/admin',
48
   errorHandler,
48
   errorHandler,
49
   // 默认错误处理
49
   // 默认错误处理
50
   credentials: 'include', // 默认请求是否带上cookie
50
   credentials: 'include', // 默认请求是否带上cookie
51
 });
51
 });
52
 
52
 
53
 request.interceptors.request.use((url, options) => {
53
 request.interceptors.request.use((url, options) => {
54
-  const headers = options.headers || {}
55
-  const token = localStorage.getItem('token') ? { 'X-Authorization-JWT': localStorage.getItem('token') } : {}
54
+  const headers = options.headers || {};
55
+  const token = localStorage.getItem('token')
56
+    ? { 'X-Authorization-JWT': localStorage.getItem('token') }
57
+    : {};
56
 
58
 
57
   return {
59
   return {
58
     url,
60
     url,
62
         ...headers,
64
         ...headers,
63
         ...token,
65
         ...token,
64
       },
66
       },
65
-    }
66
-  }
67
-})
67
+    },
68
+  };
69
+});
68
 
70
 
69
 request.interceptors.response.use(async (response) => {
71
 request.interceptors.response.use(async (response) => {
70
-  const token = response.headers.get('x-authorization-jwt')
72
+  const token = response.headers.get('x-authorization-jwt');
71
   if (token) {
73
   if (token) {
72
-    localStorage.setItem('token', token)
74
+    localStorage.setItem('token', token);
73
   }
75
   }
74
 
76
 
75
-  const contextType = response.headers.get('content-type')
77
+  const contextType = response.headers.get('content-type');
76
   if (contextType.indexOf('json') > -1) {
78
   if (contextType.indexOf('json') > -1) {
77
-    const result = await response.clone().json()
79
+    const result = await response.clone().json();
78
     if (result.code === 1000) {
80
     if (result.code === 1000) {
79
-      return result.data
81
+      return result.data;
80
     }
82
     }
81
-    return Promise.reject(result)
83
+    return Promise.reject(result);
82
   }
84
   }
83
 
85
 
84
-  return response
85
-})
86
+  return response;
87
+});
86
 
88
 
87
 export default request;
89
 export default request;
88
 
90
 
89
 // 专门为 ProTable 定制的 request
91
 // 专门为 ProTable 定制的 request
90
 // https://procomponents.ant.design/components/table#request
92
 // https://procomponents.ant.design/components/table#request
91
 export const queryTable = (url) => {
93
 export const queryTable = (url) => {
92
-  return (params, sort, filter) => {
93
-    return new Promise((resolve, reject) => {
94
-      const { current, pageSize, ...leftParams } = params
94
+  return (params) => {
95
+    return new Promise((resolve) => {
96
+      const { current, pageSize, ...leftParams } = params;
95
       const options = {
97
       const options = {
96
         params: {
98
         params: {
97
           pageNum: current,
99
           pageNum: current,
98
           pageSize,
100
           pageSize,
99
           ...leftParams,
101
           ...leftParams,
100
-        }
101
-      }
102
+        },
103
+      };
102
 
104
 
103
-      request(url, options).then(res => {
104
-        // 成功返回 success: true
105
-        resolve({
106
-          success: true,
107
-          total: res.total,
108
-          data: res.records
105
+      request(url, options)
106
+        .then((res) => {
107
+          // 成功返回 success: true
108
+          resolve({
109
+            success: true,
110
+            total: res.total,
111
+            data: res.records,
112
+          });
109
         })
113
         })
110
-      }).catch((e) => {
111
-        notification.error({
112
-          message: e.message,
114
+        .catch((e) => {
115
+          notification.error({
116
+            message: e.message,
117
+          });
118
+          // 失败返回 success: false
119
+          resolve({
120
+            success: false,
121
+            total: 0,
122
+            data: [],
123
+          });
113
         });
124
         });
114
-        // 失败返回 success: false
115
-        resolve({
116
-          success: false,
117
-          total: 0,
118
-          data: []
119
-        })
120
-      })
121
-    })
122
-  }
123
-}
125
+    });
126
+  };
127
+};

+ 47
- 15
src/utils/uploadFile.js View File

3
 
3
 
4
 // 通过 OSS STS 模式上传
4
 // 通过 OSS STS 模式上传
5
 // oss client 是单例的
5
 // oss client 是单例的
6
-let ossClient
7
-let stsInfo
6
+let ossClient;
7
+let stsInfo;
8
 
8
 
9
 function getOssClient() {
9
 function getOssClient() {
10
   // 请求 sts 临时授权
10
   // 请求 sts 临时授权
11
   return request('/oss-sts')
11
   return request('/oss-sts')
12
     .then((sts) => {
12
     .then((sts) => {
13
-      stsInfo = sts
13
+      stsInfo = sts;
14
       ossClient = new OSS({
14
       ossClient = new OSS({
15
         accessKeyId: sts.accessKeyId,
15
         accessKeyId: sts.accessKeyId,
16
         accessKeySecret: sts.accessKeySecret,
16
         accessKeySecret: sts.accessKeySecret,
23
               accessKeyId: res.accessKeyId,
23
               accessKeyId: res.accessKeyId,
24
               accessKeySecret: res.accessKeySecret,
24
               accessKeySecret: res.accessKeySecret,
25
               stsToken: res.stsToken,
25
               stsToken: res.stsToken,
26
-            }
27
-          })
28
-        }
26
+            };
27
+          });
28
+        },
29
       });
29
       });
30
     })
30
     })
31
     .catch((e) => {
31
     .catch((e) => {
35
 
35
 
36
 // https://github.com/react-component/upload#customrequest
36
 // https://github.com/react-component/upload#customrequest
37
 export default ({ file, onSuccess, onError }) => {
37
 export default ({ file, onSuccess, onError }) => {
38
-
39
   const upload = () => {
38
   const upload = () => {
40
     const data = new FormData();
39
     const data = new FormData();
41
     data.append('file', file);
40
     data.append('file', file);
50
       .catch((e2) => {
49
       .catch((e2) => {
51
         onError(e2);
50
         onError(e2);
52
       });
51
       });
53
-  }
52
+  };
54
 
53
 
55
   if (!ossClient) {
54
   if (!ossClient) {
56
-    getOssClient().then(() => {
57
-      upload()
58
-    })
59
-    .catch((e) => {
60
-      onError(e);
61
-    });
55
+    getOssClient()
56
+      .then(() => {
57
+        upload();
58
+      })
59
+      .catch((e) => {
60
+        onError(e);
61
+      });
62
   } else {
62
   } else {
63
-    upload()
63
+    upload();
64
   }
64
   }
65
 
65
 
66
   return {
66
   return {
67
     abort: () => {},
67
     abort: () => {},
68
   };
68
   };
69
 };
69
 };
70
+
71
+export function uploadImage(file) {
72
+  return new Promise((resolve, reject) => {
73
+    const upload = () => {
74
+      const data = new FormData();
75
+      data.append('file', file);
76
+      const now = new Date();
77
+      const fileName = `${stsInfo.path}/${now.valueOf()}-${file.name}`;
78
+
79
+      ossClient
80
+        .put(fileName, file)
81
+        .then((res) => {
82
+          resolve(res.url);
83
+        })
84
+        .catch((e2) => {
85
+          reject(e2);
86
+        });
87
+    };
88
+
89
+    if (!ossClient) {
90
+      getOssClient()
91
+        .then(() => {
92
+          upload();
93
+        })
94
+        .catch((e) => {
95
+          reject(e);
96
+        });
97
+    } else {
98
+      upload();
99
+    }
100
+  });
101
+}

+ 8
- 8
src/utils/utils.js View File

1
 import { parse } from 'querystring';
1
 import { parse } from 'querystring';
2
-import routes from '../../config/routes'
2
+import routes from '../../config/routes';
3
 
3
 
4
 /* eslint no-useless-escape:0 import/prefer-default-export:0 */
4
 /* eslint no-useless-escape:0 import/prefer-default-export:0 */
5
 
5
 
26
 export const getPageQuery = () => parse(window.location.href.split('?')[1]);
26
 export const getPageQuery = () => parse(window.location.href.split('?')[1]);
27
 
27
 
28
 export function getCurrentRoute(pathname) {
28
 export function getCurrentRoute(pathname) {
29
-  const routeArr = []
29
+  const routeArr = [];
30
 
30
 
31
   const flatten = (list) => {
31
   const flatten = (list) => {
32
     list.forEach((route) => {
32
     list.forEach((route) => {
33
-      routeArr.push(route)
33
+      routeArr.push(route);
34
 
34
 
35
       if (route.routes) {
35
       if (route.routes) {
36
-        flatten(route.routes)
36
+        flatten(route.routes);
37
       }
37
       }
38
-    })
39
-  }
38
+    });
39
+  };
40
 
40
 
41
   // 取有效的数据
41
   // 取有效的数据
42
-  flatten(routes[0].routes)
42
+  flatten(routes[0].routes);
43
 
43
 
44
-  return routeArr.filter(x => x.path === pathname)[0]
44
+  return routeArr.filter((x) => x.path === pathname)[0];
45
 }
45
 }

+ 1
- 0
src/utils/utils.test.js View File

1
 import { isUrl } from './utils';
1
 import { isUrl } from './utils';
2
+
2
 describe('isUrl tests', () => {
3
 describe('isUrl tests', () => {
3
   it('should return false for invalid and corner case inputs', () => {
4
   it('should return false for invalid and corner case inputs', () => {
4
     expect(isUrl([])).toBeFalsy();
5
     expect(isUrl([])).toBeFalsy();