andrew 4 年之前
父節點
當前提交
7cb56469bf
共有 61 個文件被更改,包括 3342 次插入0 次删除
  1. 39
    0
      src/components/ModalButton/ModalButton.jsx
  2. 9
    0
      src/components/ModalButton/index.jsx
  3. 34
    0
      src/components/Navigate/index.jsx
  4. 11
    0
      src/components/Navigate/style.less
  5. 95
    0
      src/components/NoticeIcon/NoticeList.jsx
  6. 105
    0
      src/components/NoticeIcon/NoticeList.less
  7. 155
    0
      src/components/NoticeIcon/index.jsx
  8. 31
    0
      src/components/NoticeIcon/index.less
  9. 16
    0
      src/components/PageLoading/index.jsx
  10. 117
    0
      src/components/PaginationWrapper/index.jsx
  11. 10
    0
      src/components/PaginationWrapper/style.less
  12. 81
    0
      src/components/SearchForm/index.jsx
  13. 65
    0
      src/components/SelectButton/AreaSelect.jsx
  14. 65
    0
      src/components/SelectButton/BuildSelect.jsx
  15. 77
    0
      src/components/SelectButton/BuildSelect2.jsx
  16. 64
    0
      src/components/SelectButton/CitySelect.jsx
  17. 65
    0
      src/components/SelectButton/CitySelect2.jsx
  18. 64
    0
      src/components/SelectButton/CitySelect3.jsx
  19. 65
    0
      src/components/SelectButton/LivePlatSelect.jsx
  20. 64
    0
      src/components/SelectButton/MiniappIconSelect.jsx
  21. 63
    0
      src/components/SelectButton/NewTypeSelect.jsx
  22. 82
    0
      src/components/SelectButton/QrcodeType.jsx
  23. 82
    0
      src/components/SelectButton/QrcodeType1.jsx
  24. 45
    0
      src/components/SelectButton/WxDictSelect.jsx
  25. 47
    0
      src/components/SelectButton/channelSelect.jsx
  26. 53
    0
      src/components/SelectLang/index.jsx
  27. 24
    0
      src/components/SelectLang/index.less
  28. 33
    0
      src/components/SettingDrawer/themeColorClient.js
  29. 109
    0
      src/components/Wangedit/Wangedit.jsx
  30. 84
    0
      src/components/XForm/FileUpload.jsx
  31. 98
    0
      src/components/XForm/ImageListUpload.jsx
  32. 77
    0
      src/components/XForm/ImageUpload.jsx
  33. 31
    0
      src/components/XForm/README.md
  34. 81
    0
      src/components/XForm/WrapperForm.jsx
  35. 151
    0
      src/components/XForm/WrapperItem.jsx
  36. 9
    0
      src/components/XForm/index.jsx
  37. 13
    0
      src/components/XForm/style.css
  38. 20
    0
      src/components/XForm/style.less
  39. 13
    0
      src/components/XForm/style.wxss
  40. 36
    0
      src/components/ZmageImg/ZmageImg.jsx
  41. 81
    0
      src/components/uploadImage/ImageUpload.jsx
  42. 8
    0
      src/components/uploadImage/style.less
  43. 1
    0
      src/e2e/__mocks__/antd-pro-merge-less.js
  44. 39
    0
      src/e2e/baseLayout.e2e.js
  45. 15
    0
      src/e2e/topMenu.e2e.js
  46. 155
    0
      src/layouts/BasicLayout.jsx
  47. 20
    0
      src/layouts/BlankLayout.jsx
  48. 17
    0
      src/layouts/SearchList/BodyHeader.jsx
  49. 34
    0
      src/layouts/SearchList/BodyPagination.jsx
  50. 28
    0
      src/layouts/SearchList/SearchBody.jsx
  51. 79
    0
      src/layouts/SearchList/SearchForm.jsx
  52. 167
    0
      src/layouts/SearchList/index.jsx
  53. 46
    0
      src/layouts/SearchList/style.less
  54. 36
    0
      src/layouts/SearchList/utils.js
  55. 47
    0
      src/layouts/SecurityLayout.jsx
  56. 42
    0
      src/layouts/UserLayout.jsx
  57. 64
    0
      src/layouts/UserLayout.less
  58. 21
    0
      src/locales/en-US.js
  59. 19
    0
      src/locales/pt-BR.js
  60. 21
    0
      src/locales/zh-CN.js
  61. 19
    0
      src/locales/zh-TW.js

+ 39
- 0
src/components/ModalButton/ModalButton.jsx 查看文件

@@ -0,0 +1,39 @@
1
+import React from 'react';
2
+import { Modal, Button, message } from 'antd';
3
+
4
+export default function ModalButton(props) {
5
+  const {
6
+    method = 'confirm',
7
+    title = '',
8
+    content = '',
9
+    modalConfigs = {},
10
+    onClick,
11
+    onCancel,
12
+    // 未选数据弹出提示
13
+    beforeCheck = '',
14
+    okText = '确认',
15
+    cancelText = '取消',
16
+    ...btnProps
17
+  } = props || {}
18
+
19
+  const handleClick = e => {
20
+    if (!!beforeCheck) { message.info(beforeCheck); return }
21
+    Modal[method]({
22
+      title,
23
+      content,
24
+      okText,
25
+      cancelText,
26
+      ...modalConfigs,
27
+      onOk() {
28
+        // eslint-disable-next-line no-unused-expressions
29
+        onClick && onClick(e);
30
+      },
31
+      onCancel() {
32
+        // eslint-disable-next-line no-unused-expressions
33
+        onCancel && onCancel();
34
+      },
35
+    })
36
+  }
37
+
38
+  return <Button {...btnProps} onClick={handleClick}>{props.children}</Button>
39
+}

+ 9
- 0
src/components/ModalButton/index.jsx 查看文件

@@ -0,0 +1,9 @@
1
+import React from 'react';
2
+import ModalButton from './ModalButton';
3
+
4
+const ConfirmButton = props => <ModalButton {...props} method="confirm" />
5
+
6
+export {
7
+  ModalButton,
8
+  ConfirmButton,
9
+}

+ 34
- 0
src/components/Navigate/index.jsx 查看文件

@@ -0,0 +1,34 @@
1
+import React from 'react';
2
+import Link from 'umi/link';
3
+
4
+import styles from './style.less';
5
+
6
+const Navigate = ({ children, to, onClick }) => {
7
+    // to 包含协议 用 a
8
+    const useA = (to || '').indexOf('://') > -1 ? 'a' : '';
9
+    // 否则用 Link
10
+    const useLink = to && to.indexOf('://') === -1 ? Link : '';
11
+    // 没有 to 则使用 span
12
+    const element = useA || useLink || 'span';
13
+
14
+    const events = onClick ? { onClick } : {};
15
+    const style = { color: '#1D74D9', cursor: 'pointer' };
16
+
17
+    let redirect = {};
18
+    if (useA) {
19
+        redirect = { href: to }
20
+    } else if (useLink) {
21
+        redirect = { to }
22
+    }
23
+
24
+    const props = {
25
+        className: 'cust-navi',
26
+        style,
27
+        ...redirect,
28
+        ...events,
29
+    }
30
+
31
+    return React.createElement(element, props, children);
32
+};
33
+
34
+export default Navigate;

+ 11
- 0
src/components/Navigate/style.less 查看文件

@@ -0,0 +1,11 @@
1
+
2
+:global {
3
+  .cust-navi {
4
+    color: #1D74D9;
5
+    cursor: pointer;
6
+  
7
+    * {
8
+      color: #1D74D9 !important;
9
+    }
10
+  }
11
+}

+ 95
- 0
src/components/NoticeIcon/NoticeList.jsx 查看文件

@@ -0,0 +1,95 @@
1
+import { Avatar, List } from 'antd';
2
+import React from 'react';
3
+import classNames from 'classnames';
4
+import styles from './NoticeList.less';
5
+
6
+const NoticeList = ({
7
+  data = [],
8
+  onClick,
9
+  onClear,
10
+  title,
11
+  onViewMore,
12
+  emptyText,
13
+  showClear = true,
14
+  clearText,
15
+  viewMoreText,
16
+  showViewMore = false,
17
+}) => {
18
+  if (data.length === 0) {
19
+    return (
20
+      <div className={styles.notFound}>
21
+        <img
22
+          src="https://gw.alipayobjects.com/zos/rmsportal/sAuJeJzSKbUmHfBQRzmZ.svg"
23
+          alt="not found"
24
+        />
25
+        <div>{emptyText}</div>
26
+      </div>
27
+    );
28
+  }
29
+
30
+  return (
31
+    <div>
32
+      <List
33
+        className={styles.list}
34
+        dataSource={data}
35
+        renderItem={(item, i) => {
36
+          const itemCls = classNames(styles.item, {
37
+            [styles.read]: item.read,
38
+          }); // eslint-disable-next-line no-nested-ternary
39
+
40
+          const leftIcon = item.avatar ? (
41
+            typeof item.avatar === 'string' ? (
42
+              <Avatar className={styles.avatar} src={item.avatar} />
43
+            ) : (
44
+              <span className={styles.iconElement}>{item.avatar}</span>
45
+            )
46
+          ) : null;
47
+          return (
48
+            <List.Item
49
+              className={itemCls}
50
+              key={item.key || i}
51
+              onClick={() => onClick && onClick(item)}
52
+            >
53
+              <List.Item.Meta
54
+                className={styles.meta}
55
+                avatar={leftIcon}
56
+                title={
57
+                  <div className={styles.title}>
58
+                    {item.title}
59
+                    <div className={styles.extra}>{item.extra}</div>
60
+                  </div>
61
+                }
62
+                description={
63
+                  <div>
64
+                    <div className={styles.description}>{item.description}</div>
65
+                    <div className={styles.datetime}>{item.datetime}</div>
66
+                  </div>
67
+                }
68
+              />
69
+            </List.Item>
70
+          );
71
+        }}
72
+      />
73
+      <div className={styles.bottomBar}>
74
+        {showClear ? (
75
+          <div onClick={onClear}>
76
+            {clearText} {title}
77
+          </div>
78
+        ) : null}
79
+        {showViewMore ? (
80
+          <div
81
+            onClick={e => {
82
+              if (onViewMore) {
83
+                onViewMore(e);
84
+              }
85
+            }}
86
+          >
87
+            {viewMoreText}
88
+          </div>
89
+        ) : null}
90
+      </div>
91
+    </div>
92
+  );
93
+};
94
+
95
+export default NoticeList;

+ 105
- 0
src/components/NoticeIcon/NoticeList.less 查看文件

@@ -0,0 +1,105 @@
1
+@import '~antd/es/style/themes/default.less';
2
+
3
+.list {
4
+  max-height: 400px;
5
+  overflow: auto;
6
+  &::-webkit-scrollbar {
7
+    display: none;
8
+  }
9
+  .item {
10
+    padding-right: 24px;
11
+    padding-left: 24px;
12
+    overflow: hidden;
13
+    cursor: pointer;
14
+    transition: all 0.3s;
15
+
16
+    .meta {
17
+      width: 100%;
18
+    }
19
+
20
+    .avatar {
21
+      margin-top: 4px;
22
+      background: #fff;
23
+    }
24
+    .iconElement {
25
+      font-size: 32px;
26
+    }
27
+
28
+    &.read {
29
+      opacity: 0.4;
30
+    }
31
+    &:last-child {
32
+      border-bottom: 0;
33
+    }
34
+    &:hover {
35
+      background: @primary-1;
36
+    }
37
+    .title {
38
+      margin-bottom: 8px;
39
+      font-weight: normal;
40
+    }
41
+    .description {
42
+      font-size: 12px;
43
+      line-height: @line-height-base;
44
+    }
45
+    .datetime {
46
+      margin-top: 4px;
47
+      font-size: 12px;
48
+      line-height: @line-height-base;
49
+    }
50
+    .extra {
51
+      float: right;
52
+      margin-top: -1.5px;
53
+      margin-right: 0;
54
+      color: @text-color-secondary;
55
+      font-weight: normal;
56
+    }
57
+  }
58
+  .loadMore {
59
+    padding: 8px 0;
60
+    color: @primary-6;
61
+    text-align: center;
62
+    cursor: pointer;
63
+    &.loadedAll {
64
+      color: rgba(0, 0, 0, 0.25);
65
+      cursor: unset;
66
+    }
67
+  }
68
+}
69
+
70
+.notFound {
71
+  padding: 73px 0 88px;
72
+  color: @text-color-secondary;
73
+  text-align: center;
74
+  img {
75
+    display: inline-block;
76
+    height: 76px;
77
+    margin-bottom: 16px;
78
+  }
79
+}
80
+
81
+.bottomBar {
82
+  height: 46px;
83
+  color: @text-color;
84
+  line-height: 46px;
85
+  text-align: center;
86
+  border-top: 1px solid @border-color-split;
87
+  border-radius: 0 0 @border-radius-base @border-radius-base;
88
+  transition: all 0.3s;
89
+  div {
90
+    display: inline-block;
91
+    width: 50%;
92
+    cursor: pointer;
93
+    transition: all 0.3s;
94
+    user-select: none;
95
+    &:hover {
96
+      color: @heading-color;
97
+    }
98
+    &:only-child {
99
+      width: 100%;
100
+    }
101
+    &:not(:only-child):last-child {
102
+      border-left: 1px solid @border-color-split;
103
+    }
104
+  }
105
+}

+ 155
- 0
src/components/NoticeIcon/index.jsx 查看文件

@@ -0,0 +1,155 @@
1
+import { Badge, Icon, Spin, Tabs } from 'antd';
2
+import React, { Component } from 'react';
3
+import classNames from 'classnames';
4
+import NoticeList from './NoticeList';
5
+import HeaderDropdown from '../HeaderDropdown';
6
+import styles from './index.less';
7
+const { TabPane } = Tabs;
8
+export default class NoticeIcon extends Component {
9
+  static Tab = NoticeList;
10
+  static defaultProps = {
11
+    onItemClick: () => {},
12
+    onPopupVisibleChange: () => {},
13
+    onTabChange: () => {},
14
+    onClear: () => {},
15
+    onViewMore: () => {},
16
+    loading: false,
17
+    clearClose: false,
18
+    emptyImage: 'https://gw.alipayobjects.com/zos/rmsportal/wAhyIChODzsoKIOBHcBk.svg',
19
+  };
20
+  state = {
21
+    visible: false,
22
+  };
23
+  onItemClick = (item, tabProps) => {
24
+    const { onItemClick } = this.props;
25
+
26
+    if (onItemClick) {
27
+      onItemClick(item, tabProps);
28
+    }
29
+  };
30
+  onClear = (name, key) => {
31
+    const { onClear } = this.props;
32
+
33
+    if (onClear) {
34
+      onClear(name, key);
35
+    }
36
+  };
37
+  onTabChange = tabType => {
38
+    const { onTabChange } = this.props;
39
+
40
+    if (onTabChange) {
41
+      onTabChange(tabType);
42
+    }
43
+  };
44
+  onViewMore = (tabProps, event) => {
45
+    const { onViewMore } = this.props;
46
+
47
+    if (onViewMore) {
48
+      onViewMore(tabProps, event);
49
+    }
50
+  };
51
+
52
+  getNotificationBox() {
53
+    const { children, loading, clearText, viewMoreText } = this.props;
54
+
55
+    if (!children) {
56
+      return null;
57
+    }
58
+
59
+    const panes = React.Children.map(children, child => {
60
+      if (!child) {
61
+        return null;
62
+      }
63
+
64
+      const { list, title, count, tabKey, showClear, showViewMore } = child.props;
65
+      const len = list && list.length ? list.length : 0;
66
+      const msgCount = count || count === 0 ? count : len;
67
+      const tabTitle = msgCount > 0 ? `${title} (${msgCount})` : title;
68
+      return (
69
+        <TabPane tab={tabTitle} key={title}>
70
+          <NoticeList
71
+            clearText={clearText}
72
+            viewMoreText={viewMoreText}
73
+            data={list}
74
+            onClear={() => this.onClear(title, tabKey)}
75
+            onClick={item => this.onItemClick(item, child.props)}
76
+            onViewMore={event => this.onViewMore(child.props, event)}
77
+            showClear={showClear}
78
+            showViewMore={showViewMore}
79
+            title={title}
80
+            {...child.props}
81
+          />
82
+        </TabPane>
83
+      );
84
+    });
85
+    return (
86
+      <>
87
+        <Spin spinning={loading} delay={300}>
88
+          <Tabs className={styles.tabs} onChange={this.onTabChange}>
89
+            {panes}
90
+          </Tabs>
91
+        </Spin>
92
+      </>
93
+    );
94
+  }
95
+
96
+  handleVisibleChange = visible => {
97
+    const { onPopupVisibleChange } = this.props;
98
+    this.setState({
99
+      visible,
100
+    });
101
+
102
+    if (onPopupVisibleChange) {
103
+      onPopupVisibleChange(visible);
104
+    }
105
+  };
106
+
107
+  render() {
108
+    const { className, count, popupVisible, bell } = this.props;
109
+    const { visible } = this.state;
110
+    const noticeButtonClass = classNames(className, styles.noticeButton);
111
+    const notificationBox = this.getNotificationBox();
112
+    const NoticeBellIcon = bell || <Icon type="bell" className={styles.icon} />;
113
+    const trigger = (
114
+      <span
115
+        className={classNames(noticeButtonClass, {
116
+          opened: visible,
117
+        })}
118
+      >
119
+        <Badge
120
+          count={count}
121
+          style={{
122
+            boxShadow: 'none',
123
+          }}
124
+          className={styles.badge}
125
+        >
126
+          {NoticeBellIcon}
127
+        </Badge>
128
+      </span>
129
+    );
130
+
131
+    if (!notificationBox) {
132
+      return trigger;
133
+    }
134
+
135
+    const popoverProps = {};
136
+
137
+    if ('popupVisible' in this.props) {
138
+      popoverProps.visible = popupVisible;
139
+    }
140
+
141
+    return (
142
+      <HeaderDropdown
143
+        placement="bottomRight"
144
+        overlay={notificationBox}
145
+        overlayClassName={styles.popover}
146
+        trigger={['click']}
147
+        visible={visible}
148
+        onVisibleChange={this.handleVisibleChange}
149
+        {...popoverProps}
150
+      >
151
+        {trigger}
152
+      </HeaderDropdown>
153
+    );
154
+  }
155
+}

+ 31
- 0
src/components/NoticeIcon/index.less 查看文件

@@ -0,0 +1,31 @@
1
+@import '~antd/es/style/themes/default.less';
2
+
3
+.popover {
4
+  position: relative;
5
+  width: 336px;
6
+}
7
+
8
+.noticeButton {
9
+  display: inline-block;
10
+  cursor: pointer;
11
+  transition: all 0.3s;
12
+}
13
+.icon {
14
+  padding: 4px;
15
+  vertical-align: middle;
16
+}
17
+
18
+.badge {
19
+  font-size: 16px;
20
+}
21
+
22
+.tabs {
23
+  :global {
24
+    .ant-tabs-nav-scroll {
25
+      text-align: center;
26
+    }
27
+    .ant-tabs-bar {
28
+      margin-bottom: 0;
29
+    }
30
+  }
31
+}

+ 16
- 0
src/components/PageLoading/index.jsx 查看文件

@@ -0,0 +1,16 @@
1
+import React from 'react';
2
+import { Spin } from 'antd'; // loading components from code split
3
+// https://umijs.org/plugin/umi-plugin-react.html#dynamicimport
4
+
5
+const PageLoading = () => (
6
+  <div
7
+    style={{
8
+      paddingTop: 100,
9
+      textAlign: 'center',
10
+    }}
11
+  >
12
+    <Spin size="large" />
13
+  </div>
14
+);
15
+
16
+export default PageLoading;

+ 117
- 0
src/components/PaginationWrapper/index.jsx 查看文件

@@ -0,0 +1,117 @@
1
+import React, { useState } from 'react';
2
+import PropTypes from 'prop-types';
3
+import { Pagination } from 'antd';
4
+import classNames from 'classnames'
5
+import { useQuery } from '../../utils/hooks';
6
+import { ObjSpEqual } from '../../utils/utils'
7
+import Styles from './style.less'
8
+
9
+class PaginationWrapper extends React.PureComponent {
10
+
11
+  state = {
12
+    data: {
13
+      current: 1,
14
+      total: 0,
15
+      pages: 0,
16
+    },
17
+    loadding: false,
18
+  }
19
+
20
+  componentDidMount () {
21
+    this.fetchData(1)
22
+  }
23
+
24
+  componentDidUpdate(prevProps) {
25
+    if (!ObjSpEqual(prevProps.params, this.props.params)) {
26
+      this.fetchData(1)
27
+    }
28
+  } 
29
+
30
+  fetchData = (page) => {
31
+    this.setState({ loadding: true })
32
+    
33
+    const requestParams = this.props.params || {}
34
+    this.props.request({
35
+      ...requestParams,
36
+      params: {
37
+        pageNum: page,              // 有的接口是 pageNum
38
+        pageNumber: page,  // 有的接口是 pageNumber
39
+        ...(requestParams.params || {}),
40
+      }
41
+    }).then((res) => {
42
+      this.setState({ loadding: false, data: res })
43
+    })
44
+  }
45
+
46
+  handlePageChange = (page) => {
47
+    this.fetchData(page)
48
+  }
49
+
50
+  render() {
51
+    const { className, style, bodyClass, footerClass, render } = this.props
52
+    const { data } = this.state
53
+
54
+    return (
55
+      <div className={classNames(Styles['pg-wrapper'], className)} style={style}>
56
+        <div className={classNames(Styles.body, bodyClass)}>
57
+          {render(data)}
58
+        </div>
59
+        <div className={classNames(Styles.footer, footerClass)}>
60
+          <Pagination showQuickJumper defaultCurrent={1} total={data.total} onChange={this.handlePageChange} current={data.current}/>
61
+        </div>
62
+      </div>
63
+    )
64
+  }
65
+}
66
+
67
+PaginationWrapper.propTypes = {
68
+  request: PropTypes.func.isRequired,
69
+  render: PropTypes.func.isRequired,
70
+  params: PropTypes.object,
71
+  className: PropTypes.oneOfType([
72
+    PropTypes.string,
73
+    PropTypes.array,
74
+    PropTypes.object,
75
+  ]),
76
+  bodyClass: PropTypes.oneOfType([
77
+    PropTypes.string,
78
+    PropTypes.array,
79
+    PropTypes.object,
80
+  ]),
81
+  footerClass: PropTypes.oneOfType([
82
+    PropTypes.string,
83
+    PropTypes.array,
84
+    PropTypes.object,
85
+  ]),
86
+  style: PropTypes.object,
87
+}
88
+
89
+// /**
90
+//  * 分页封装
91
+//  * @param {page, params, request, render, className, style, bodyClass, footerClass} props 
92
+//  */
93
+// function PaginationWrapper(props) {
94
+//   const defaultPage = props.page || 1
95
+//   const [pageNum, setPage] = useState(defaultPage)
96
+//   const requestParams = props.params || {}
97
+//   const [data = {}, loading, err] = useQuery({ ...requestParams, params: { ...(requestParams.params || {}), pageNum, pageNumber: pageNum }}, props.request)
98
+
99
+//   // if (typeof props.setData === 'function') {
100
+//   //   props.setData(data)
101
+//   // }
102
+
103
+//   console.log('------render PaginationWrapper-------', pageNum, data, loading)
104
+
105
+//   return (
106
+//     <div className={classNames(Styles['pg-wrapper'], props.className)} style={props.style}>
107
+//       <div className={classNames(Styles.body, props.bodyClass)}>
108
+//         {props.render(data)}
109
+//       </div>
110
+//       <div className={classNames(Styles.footer, props.footerClass)}>
111
+//         <Pagination showQuickJumper defaultCurrent={defaultPage} total={data.total} onChange={pg => setPage(pg)} current={data.current}/>
112
+//       </div>
113
+//     </div>
114
+//   )
115
+// }
116
+
117
+export default PaginationWrapper

+ 10
- 0
src/components/PaginationWrapper/style.less 查看文件

@@ -0,0 +1,10 @@
1
+.pg-wrapper {
2
+  .body {}
3
+
4
+  .footer {
5
+    margin-top: 30px;
6
+    display: flex;
7
+    align-items: center;
8
+    flex-direction: row-reverse;
9
+  }
10
+}

+ 81
- 0
src/components/SearchForm/index.jsx 查看文件

@@ -0,0 +1,81 @@
1
+import React from 'react';
2
+import PropTyps from 'prop-types';
3
+import { Form, Button } from 'antd';
4
+
5
+function isFunc(fn) { return typeof fn === 'function' };
6
+
7
+class SearchForm extends React.PureComponent {
8
+  handleSubmit = e => {
9
+    e.preventDefault();
10
+    this.props.form.validateFields((err, values) => {
11
+      if (!err && isFunc(this.props.onSearch)) {
12
+        this.props.onSearch(values)
13
+        return
14
+      }
15
+
16
+      if (err) {
17
+        console.error(err)
18
+        if (isFunc(this.props.onError)) {
19
+          this.props.onError(err)
20
+        }
21
+        return
22
+      }
23
+    });
24
+  };
25
+
26
+  handleReset = e => {
27
+    this.props.form.resetFields()
28
+    if (isFunc(this.props.onReset)) {
29
+      this.props.onReset()
30
+    }
31
+  }
32
+
33
+  render () {
34
+    const { form, hideResetBtn } = this.props;
35
+
36
+    return (
37
+      <Form layout="inline" onSubmit={this.handleSubmit} colon={false}>
38
+        {
39
+          (this.props.fields || []).map((field, index) => {
40
+            const { element, render, name, label, value } = field || {}
41
+
42
+            if (!element && !render) return null;
43
+
44
+            const conifg = value  ? { initialValue: value } : {}
45
+            const Item = isFunc(render) ? render(this.props) : element
46
+
47
+            return <Form.Item label={label} key={`fm-${index}`}>{form.getFieldDecorator(name, conifg)(Item)}</Form.Item>
48
+          })
49
+        }
50
+        <Form.Item>
51
+          <Button type="primary" htmlType="submit">搜索</Button>
52
+        </Form.Item>
53
+        { hideResetBtn && (
54
+          <Form.Item>
55
+            <Button type="default" onClick="handleReset">重置</Button>
56
+          </Form.Item>
57
+        )}
58
+      </Form>
59
+    );
60
+  }
61
+}
62
+
63
+// 提供给父组件 onChange 事件
64
+function onValuesChange(props, changedValues, allValues) {
65
+  if (isFunc(props.onChange)) {
66
+    props.onChange(changedValues, allValues)
67
+  }
68
+}
69
+
70
+const EnhanceForm = Form.create({ onValuesChange })(SearchForm)
71
+
72
+EnhanceForm.propTypes = {
73
+  onSearch: PropTyps.func.isRequired,
74
+  onReset: PropTyps.func,
75
+  onError: PropTyps.func,
76
+  onChange: PropTyps.func,
77
+  fields: PropTyps.arrayOf(PropTyps.object),
78
+  hideResetBtn: PropTyps.bool,
79
+}
80
+
81
+export default EnhanceForm;

+ 65
- 0
src/components/SelectButton/AreaSelect.jsx 查看文件

@@ -0,0 +1,65 @@
1
+import React, { useState, useEffect } from 'react';
2
+import { Select } from 'antd';
3
+
4
+import request from '../../utils/request'
5
+import { APIBaseURL } from '../../utils/constant'
6
+
7
+const { Option } = Select;
8
+const prefix = APIBaseURL
9
+
10
+/**
11
+ *
12
+ *
13
+ * @param {*} props
14
+ * @returns
15
+ */
16
+const CitySelect = (props) => {
17
+  const [ data, setData ] = useState([])
18
+  const cityId = typeof props.cityId === 'function' ? props.cityId() : props.cityId
19
+
20
+  useEffect(() => {
21
+    getCityAreaList();
22
+    getValue();
23
+  },[cityId])
24
+
25
+  const getCityAreaList = (e) => {
26
+    request({
27
+        url: prefix + 'api/admin/tdCityList/tdAreaCity',
28
+        method: 'GET',
29
+        params: {leveltype: 3, cityId},
30
+        action: 'select',
31
+    }).then((data) => {
32
+        setData(data)
33
+    })
34
+  }
35
+
36
+  /**
37
+   * 因为 有个需求是,如果这个城市被删除了,那么就直接展示为空,不能展示 cityId
38
+   */
39
+  const getValue = () => {
40
+    if (props.value) {
41
+      const tempData = data.filter(f => f.id == props.value)
42
+      const va = (tempData.length > 0) ? props.value : undefined
43
+      props.onChange(va)
44
+    }
45
+  }
46
+
47
+  return (
48
+      <Select
49
+      {...props}
50
+      showSearch
51
+      value={props.value}
52
+      style={{ width: '300px' }}
53
+      placeholder="请选择区域"
54
+      onChange={props.onChange}
55
+      filterOption={(input, option) =>
56
+        option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0
57
+      }
58
+      >
59
+          {data.map(city => (
60
+            <Option key={city.id} value={city.name}>{city.name}</Option>
61
+          ))}
62
+      </Select>
63
+  )
64
+}
65
+export default CitySelect

+ 65
- 0
src/components/SelectButton/BuildSelect.jsx 查看文件

@@ -0,0 +1,65 @@
1
+import React, { useState, useEffect, useRef } from 'react';
2
+import { Select } from 'antd';
3
+import apis from '../../services/apis';
4
+import request from '../../utils/request'
5
+
6
+const { Option } = Select;
7
+
8
+function usePrevious(props) {
9
+  const ref = useRef();
10
+  useEffect(() => {
11
+    ref.current = props;
12
+  });
13
+  return ref.current;
14
+}
15
+
16
+/**
17
+ *
18
+ *
19
+ * @param {*} props
20
+ * @returns
21
+ */
22
+const BuildingSelect = props => {
23
+  const [data, setData] = useState([])
24
+  const [value, setValue] = useState([])
25
+  useEffect(() => {
26
+    getBuildList();
27
+  }, [props.value])
28
+
29
+
30
+  const getBuildList = e => {
31
+    request({ ...apis.building.buildingSelect, params: { pageNum: 1, pageSize: 999 } }).then(data => {
32
+        setData(data)
33
+        checkValue(data)
34
+        // 默认选中第一个
35
+    })
36
+  }
37
+
38
+
39
+  const checkValue = (data) => {
40
+    if (props.value) {
41
+      const tempData = data.filter(f => f.buildingId == props.value)
42
+      const va = (tempData.length > 0) ? props.value : '项目已下线,请重新选择项目'
43
+      props.onChange(va)
44
+
45
+    }
46
+  }
47
+
48
+  return (
49
+      <Select
50
+      showSearch
51
+      value={props.value}
52
+      style={{ width: '300px' }}
53
+      placeholder="请选择项目"
54
+      onChange={props.onChange}
55
+      filterOption={(input, option) =>
56
+        option.props.children && option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0
57
+      }>
58
+          <Option key="" value="">全部项目</Option>
59
+          {data.map(building => (
60
+            <Option key={building.buildingId} value={building.buildingId}>{building.buildingName}</Option>
61
+          ))}
62
+      </Select>
63
+  )
64
+}
65
+export default BuildingSelect

+ 77
- 0
src/components/SelectButton/BuildSelect2.jsx 查看文件

@@ -0,0 +1,77 @@
1
+import React, { useState, useEffect, useRef } from 'react';
2
+import { Select } from 'antd';
3
+import apis from '../../services/apis';
4
+import request from '../../utils/request'
5
+
6
+const { Option } = Select;
7
+
8
+function usePrevious(props) {
9
+  const ref = useRef();
10
+  useEffect(() => {
11
+    ref.current = props;
12
+  });
13
+  return ref.current;
14
+}
15
+
16
+/**
17
+ *
18
+ *
19
+ * @param {*} props
20
+ * @returns
21
+ */
22
+const BuildingSelect2 = props => {
23
+  const [data, setData] = useState([])
24
+  const [value, setValue] = useState([])
25
+  console.log('props', props.cityId);
26
+  useEffect(() => {
27
+    getBuildList();
28
+  }, [props.cityId])
29
+
30
+
31
+  const getBuildList = e => {
32
+    request({ ...apis.building.buildingSelect, params: { cityId: props.cityId, pageNum: 1, pageSize: 999 } }).then(data => {
33
+      setData(data)
34
+      checkValue(data)
35
+      // 默认选中第一个
36
+    })
37
+  }
38
+
39
+
40
+  const checkValue = (data) => {
41
+    if (props.value) {
42
+      const tempData = data.filter(f => f.buildingId == props.value)
43
+      const va = (tempData.length > 0) ? props.value : '项目已下线,请重新选择项目'
44
+      props.onChange(va)
45
+
46
+    }
47
+  }
48
+
49
+  const onChange = (buildingId) => {
50
+    const building = data.filter(x => buildingId === x.buildingId)[0]
51
+
52
+    props.onChange(buildingId, building)
53
+    // if (props.value) {
54
+    //   const tempData = data.filter(f => f.buildingId == props.value)
55
+    //   const va = (tempData.length > 0) ? props.value : '项目已下线,请重新选择项目'
56
+    //   props.onChange(va)
57
+
58
+    // }={props.onChange}building.buildingId
59
+  }
60
+
61
+  return (
62
+    <Select
63
+      showSearch
64
+      value={props.value}
65
+      style={{ width: '300px' }}
66
+      placeholder="请选择项目"
67
+      onChange={ onChange}
68
+      filterOption={(input, option) =>
69
+        option.props.children && option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0
70
+      }>
71
+      {data.map(building => (
72
+        <Option key={building.buildingId} value={building.buildingId}>{building.buildingName}</Option>
73
+      ))}
74
+    </Select>
75
+  )
76
+}
77
+export default BuildingSelect2

+ 64
- 0
src/components/SelectButton/CitySelect.jsx 查看文件

@@ -0,0 +1,64 @@
1
+import React, { useState, useEffect } from 'react';
2
+import { Select } from 'antd';
3
+
4
+import request from '../../utils/request'
5
+import { APIBaseURL } from '../../utils/constant'
6
+
7
+const { Option } = Select;
8
+const prefix = APIBaseURL
9
+
10
+/**
11
+ *
12
+ *
13
+ * @param {*} props
14
+ * @returns
15
+ */
16
+const CitySelect = (props) => {
17
+  const [ data, setData ] = useState([])
18
+
19
+  useEffect(() => {
20
+    getCityList();
21
+    getValue();
22
+  },[props.value])
23
+
24
+  const getCityList = (e) => {
25
+    request({
26
+        url: prefix + 'api/admin/tdCityList/tdCity',
27
+        method: 'GET',
28
+        params: {leveltype: 2, pageNum: 1,pageSize: 999},
29
+        action: 'select',
30
+    }).then((data) => {
31
+        setData(data)
32
+    })
33
+  }
34
+
35
+  /**
36
+   * 因为 有个需求是,如果这个城市被删除了,那么就直接展示为空,不能展示 cityId
37
+   */
38
+  const getValue = () => {
39
+    if (props.value) {
40
+      const tempData = data.filter(f => f.id == props.value)
41
+      const va = (tempData.length > 0) ? props.value : undefined
42
+      props.onChange(va)
43
+    }
44
+  }
45
+
46
+  return (
47
+      <Select
48
+      {...props}
49
+      showSearch
50
+      value={props.value}
51
+      style={{ width: '300px' }}
52
+      placeholder="请选择城市"
53
+      onChange={props.onChange}
54
+      filterOption={(input, option) =>
55
+        option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0
56
+      }
57
+      >
58
+          {data.map(city => (
59
+            <Option key={city.id} value={city.id}>{city.name}</Option>
60
+          ))}
61
+      </Select>
62
+  )
63
+}
64
+export default CitySelect

+ 65
- 0
src/components/SelectButton/CitySelect2.jsx 查看文件

@@ -0,0 +1,65 @@
1
+import React, { useState, useEffect } from 'react';
2
+import { Select } from 'antd';
3
+
4
+import request from '../../utils/request'
5
+import { APIBaseURL } from '../../utils/constant'
6
+
7
+const { Option } = Select;
8
+const prefix = APIBaseURL
9
+
10
+/**
11
+ *
12
+ *
13
+ * @param {*} props
14
+ * @returns
15
+ */
16
+const CitySelect = (props) => {
17
+  const [ data, setData ] = useState([])
18
+
19
+  useEffect(() => {
20
+    getCityList();
21
+  },[props.value])
22
+
23
+  const getCityList = (e) => {
24
+    request({
25
+        url: prefix + 'api/admin/tdCityList/tdCity',
26
+        method: 'GET',
27
+        params: {leveltype: 2, pageNum: 1,pageSize: 999},
28
+        action: 'select',
29
+    }).then((data) => {
30
+        setData(data)
31
+        getValue(data);
32
+        console.log('---123---')
33
+    })
34
+  }
35
+
36
+  /**
37
+   * 因为 有个需求是,如果这个城市被删除了,那么就直接展示为空,不能展示 cityId
38
+   */
39
+  const getValue = (data) => {
40
+    console.log(props.value)
41
+    if (props.value) {
42
+      const tempData = data.filter(f => f.id == props.value)
43
+      const va = (tempData.length > 0) ? props.value : undefined
44
+      props.onChange(va)
45
+    }
46
+  }
47
+
48
+  return (
49
+      <Select
50
+      showSearch
51
+      value={props.value}
52
+      style={{ width: '180px' }}
53
+      placeholder="请选择城市"
54
+      onChange={props.onChange}
55
+      filterOption={(input, option) =>
56
+        option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0
57
+      }
58
+      >
59
+          {data.map(city => (
60
+            <Option key={city.id} value={city.id}>{city.name}</Option>
61
+          ))}
62
+      </Select>
63
+  )
64
+}
65
+export default CitySelect

+ 64
- 0
src/components/SelectButton/CitySelect3.jsx 查看文件

@@ -0,0 +1,64 @@
1
+import React, { useState, useEffect } from 'react';
2
+import { Select } from 'antd';
3
+
4
+import request from '../../utils/request'
5
+import { APIBaseURL } from '../../utils/constant'
6
+
7
+const { Option } = Select;
8
+const prefix = APIBaseURL
9
+
10
+/**
11
+ *
12
+ *
13
+ * @param {*} props
14
+ * @returns
15
+ */
16
+const CitySelect3 = (props) => {
17
+  const [ data, setData ] = useState([])
18
+  console.log('props', props.buildingId)
19
+  useEffect(() => {
20
+    getCityList();
21
+    getValue();
22
+  },[props.buildingId])
23
+
24
+  const getCityList = (e) => {
25
+    request({
26
+        url: prefix + 'api/admin/tdCityList/tdCity',
27
+        method: 'GET',
28
+        params: {leveltype: 2, pageNum: 1,pageSize: 999},
29
+        action: 'select',
30
+    }).then((data) => {
31
+        setData(data)
32
+    })
33
+  }
34
+
35
+  /**
36
+   * 因为 有个需求是,如果这个城市被删除了,那么就直接展示为空,不能展示 cityId
37
+   */
38
+  const getValue = () => {
39
+    if (props.value) {
40
+      const tempData = data.filter(f => f.id == props.value)
41
+      const va = (tempData.length > 0) ? props.value : undefined
42
+      props.onChange(va)
43
+    }
44
+  }
45
+
46
+  return (
47
+      <Select
48
+      {...props}
49
+      showSearch
50
+      value={props.value}
51
+      style={{ width: '300px' }}
52
+      placeholder="请选择城市"
53
+      onChange={props.onChange}
54
+      filterOption={(input, option) =>
55
+        option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0
56
+      }
57
+      >
58
+          {data.map(city => (
59
+            <Option key={city.id} value={city.id}>{city.name}</Option>
60
+          ))}
61
+      </Select>
62
+  )
63
+}
64
+export default CitySelect3

+ 65
- 0
src/components/SelectButton/LivePlatSelect.jsx 查看文件

@@ -0,0 +1,65 @@
1
+import React, { useState, useEffect, useRef } from 'react';
2
+import { Select } from 'antd';
3
+import apis from '../../services/apis';
4
+import request from '../../utils/request'
5
+
6
+const { Option } = Select;
7
+
8
+function usePrevious(props) {
9
+  const ref = useRef();
10
+  useEffect(() => {
11
+    ref.current = props;
12
+  });
13
+  return ref.current;
14
+}
15
+
16
+/**
17
+ *
18
+ *
19
+ * @param {*} props
20
+ * @returns
21
+ */
22
+const LivePlatSelect = props => {
23
+  const [data, setData] = useState([])
24
+  const [value, setValue] = useState([])
25
+
26
+  useEffect(() => {
27
+    getLivePlatList();
28
+  }, [props.value])
29
+
30
+
31
+  const getLivePlatList = e => {
32
+    request({ ...apis.taliveActivity.livePlatList, params: { pageNum: 1, pageSize: 999 } }).then(data => {
33
+        setData(data)
34
+        checkValue(data)
35
+        // 默认选中第一个
36
+    })
37
+  }
38
+
39
+
40
+  const checkValue = (data) => {
41
+    if (props.value) {
42
+      const tempData = data.filter(f => f.id == props.value)
43
+      const va = (tempData.length > 0) ? props.value : '平台已下线,请重新选择'
44
+      props.onChange(va)
45
+
46
+    }
47
+  }
48
+
49
+  return (
50
+      <Select
51
+      showSearch
52
+      value={`${props.value || ''}`}
53
+      style={{ width: '300px' }}
54
+      placeholder="请选择平台"
55
+      onChange={props.onChange}
56
+      filterOption={(input, option) =>
57
+        option.props.children && option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0
58
+      }>
59
+          {data.map(live => (
60
+            <Option key={live.id} value={`${live.id}`}>{live.livePlatName}</Option>
61
+          ))}
62
+      </Select>
63
+  )
64
+}
65
+export default LivePlatSelect

+ 64
- 0
src/components/SelectButton/MiniappIconSelect.jsx 查看文件

@@ -0,0 +1,64 @@
1
+import React, { useState, useEffect, useRef } from 'react';
2
+import { Select } from 'antd';
3
+import apis from '../../services/apis';
4
+import request from '../../utils/request'
5
+
6
+const { Option } = Select;
7
+
8
+function usePrevious(props) {
9
+  const ref = useRef();
10
+  useEffect(() => {
11
+    ref.current = props;
12
+  });
13
+  return ref.current;
14
+}
15
+
16
+/**
17
+ *
18
+ *
19
+ * @param {*} props
20
+ * @returns
21
+ */
22
+const MiniappIconSelect = props => {
23
+  const [data, setData] = useState([])
24
+  const [value, setValue] = useState([])
25
+  useEffect(() => {
26
+    getMiniappIconSelect();
27
+  }, [props.value])
28
+
29
+
30
+  const getMiniappIconSelect = e => {
31
+    request({ ...apis.icon.minippIconList, params: { pageNum: 1, pageSize: 999 } }).then(data => {
32
+        setData(data)
33
+        checkValue(data)
34
+        // 默认选中第一个
35
+    })
36
+  }
37
+
38
+
39
+  const checkValue = (data) => {
40
+    if (props.value) {
41
+      const tempData = data.filter(f => f.iconCode == props.value)
42
+      const va = (tempData.length > 0) ? props.value : ''
43
+      props.onChange(va)
44
+
45
+    }
46
+  }
47
+
48
+  return (
49
+      <Select
50
+      showSearch
51
+      value={props.value}
52
+      style={{ width: '300px' }}
53
+      placeholder="请选择功能"
54
+      onChange={props.onChange}
55
+      filterOption={(input, option) =>
56
+        option.props.children && option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0
57
+      }>
58
+          {data.map(icon => (
59
+            <Option key={icon.iconCode} value={icon.iconCode}>{icon.iconName}</Option>
60
+          ))}
61
+      </Select>
62
+  )
63
+}
64
+export default MiniappIconSelect

+ 63
- 0
src/components/SelectButton/NewTypeSelect.jsx 查看文件

@@ -0,0 +1,63 @@
1
+import React, { useState, useEffect, useRef } from 'react';
2
+import { Select } from 'antd';
3
+
4
+import request from '../../utils/request'
5
+import { APIBaseURL } from '../../utils/constant'
6
+
7
+const prefix = `${APIBaseURL}api/admin`
8
+
9
+const { Option } = Select;
10
+
11
+function usePrevious(props) {
12
+  const ref = useRef();
13
+  useEffect(() => {
14
+    ref.current = props;
15
+  });
16
+  return ref.current;
17
+}
18
+
19
+/**
20
+ *
21
+ *
22
+ * @param {*} props
23
+ * @returns
24
+ */
25
+const NewsTypeSelect = (props) => {
26
+  const [ data, setData ] = useState([])
27
+  const [ value, setValue ] = useState('')
28
+  const preProps = usePrevious(props)
29
+  
30
+  if ((!preProps || !preProps.value) && props.value && !value) {
31
+    setValue(props.value)
32
+  }
33
+
34
+  useEffect(() => {
35
+    getNewsTypeList();
36
+  },[props.buildingId])
37
+
38
+  const getNewsTypeList = (e) => {
39
+    request({
40
+        url: `${prefix}/taNewsType`,
41
+        method: 'GET',
42
+        params: {pageNum: 1,pageSize: 999,buildingId: props.buildingId},
43
+        action: 'admin.taNewsType.get',
44
+    }).then((data) => {
45
+        setData(data.records)
46
+    })
47
+  }
48
+
49
+  const handleChange = (e) => {
50
+    setValue(e)
51
+    props.onChange(e)
52
+  }
53
+
54
+  return (
55
+      <Select value={props.value} style={{ width: '180px' }} placeholder="请选择资讯类型" onChange={handleChange}>
56
+          {data.map(type => (
57
+            <Option key={type.newsTypeId} value={type.newsTypeId}>{type.newsTypeName}</Option>
58
+          ))}
59
+      </Select>
60
+  )
61
+}
62
+export default NewsTypeSelect
63
+

+ 82
- 0
src/components/SelectButton/QrcodeType.jsx 查看文件

@@ -0,0 +1,82 @@
1
+import React, { useMemo } from 'react';
2
+import { Select } from 'antd';
3
+
4
+const { Option } = Select;
5
+
6
+
7
+const qrcodeType = props => {
8
+    //   const [data, setData] = useState([])
9
+    //   useEffect(() => {
10
+
11
+    //   }, [props.value])
12
+    // const visible2 = props.visible || true
13
+    const all = props.all || false
14
+
15
+    const visible = useMemo(() => (props.visible), [props.visible]);
16
+    
17
+    const data = [
18
+        {
19
+            name: '项目',
20
+            value: 'project',
21
+            visible,
22
+        },
23
+        {
24
+            name: '报名活动',
25
+            value: 'activity',
26
+            visible,
27
+        },
28
+        {
29
+            name: '助力活动',
30
+            value: 'help',
31
+            visible,
32
+        },
33
+        {
34
+            name: '拼团活动',
35
+            value: 'group',
36
+            visible,
37
+        },
38
+        {
39
+            name: 'H5活动',
40
+            value: 'h5',
41
+            visible: true,
42
+        },
43
+        {
44
+            name: '直播活动',
45
+            value: 'live',
46
+            visible: true,
47
+        },
48
+        {
49
+            name: '资讯',
50
+            value: 'news',
51
+            visible,
52
+        },
53
+        {
54
+            name: '在线选房',
55
+            value: 'salesBatch',
56
+            visible,
57
+        },
58
+    ]
59
+
60
+
61
+    return (
62
+        // <>234</>
63
+        <Select
64
+            showSearch
65
+            value={props.value}
66
+            style={{ width: '250px' }}
67
+            placeholder="请选择二维码内容类型"
68
+            onChange={props.onChange}
69
+            // filterOption={(input, option) =>
70
+            //     // eslint-disable-next-line max-len
71
+            //     option.props.children && option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0
72
+            // }
73
+        >
74
+            {all && <Option key="" value="">全部类型</Option>}
75
+            {data.map(Item => (
76
+
77
+                Item.visible ? <Option key={Item.vlaue} value={Item.value}> {Item.name} </Option> : ''
78
+            ))}
79
+        </Select>
80
+    )
81
+}
82
+export default qrcodeType

+ 82
- 0
src/components/SelectButton/QrcodeType1.jsx 查看文件

@@ -0,0 +1,82 @@
1
+import React, { useMemo } from 'react';
2
+import { Select } from 'antd';
3
+
4
+const { Option } = Select;
5
+
6
+
7
+const qrcodeType = props => {
8
+    //   const [data, setData] = useState([])
9
+    //   useEffect(() => {
10
+
11
+    //   }, [props.value])
12
+    // const visible2 = props.visible || true
13
+    const all = props.all || false
14
+
15
+    const visible = useMemo(() => (props.visible), [props.visible]);
16
+    
17
+    const data = [
18
+        {
19
+            name: '项目',
20
+            value: 'building',
21
+            visible,
22
+        },
23
+        {
24
+            name: '报名活动',
25
+            value: 'activity',
26
+            visible,
27
+        },
28
+        {
29
+            name: '助力活动',
30
+            value: 'help',
31
+            visible,
32
+        },
33
+        {
34
+            name: '拼团活动',
35
+            value: 'group',
36
+            visible,
37
+        },
38
+        {
39
+            name: 'H5活动',
40
+            value: 'h5',
41
+            visible: true,
42
+        },
43
+        {
44
+            name: '直播活动',
45
+            value: 'live',
46
+            visible: true,
47
+        },
48
+        {
49
+            name: '资讯',
50
+            value: 'news',
51
+            visible,
52
+        },
53
+        {
54
+            name: '在线选房',
55
+            value: 'house',
56
+            visible,
57
+        },
58
+    ]
59
+
60
+
61
+    return (
62
+        // <>234</>
63
+        <Select
64
+            showSearch
65
+            value={props.value}
66
+            style={{ width: '250px' }}
67
+            placeholder="请选择二维码内容类型"
68
+            onChange={props.onChange}
69
+            // filterOption={(input, option) =>
70
+            //     // eslint-disable-next-line max-len
71
+            //     option.props.children && option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0
72
+            // }
73
+        >
74
+            {all && <Option key="" value="">全部类型</Option>}
75
+            {data.map(Item => (
76
+
77
+                Item.visible ? <Option key={Item.vlaue} value={Item.value}> {Item.name} </Option> : ''
78
+            ))}
79
+        </Select>
80
+    )
81
+}
82
+export default qrcodeType

+ 45
- 0
src/components/SelectButton/WxDictSelect.jsx 查看文件

@@ -0,0 +1,45 @@
1
+import React, { useState, useEffect, useRef } from 'react';
2
+import { Select } from 'antd';
3
+import apis from '../../services/apis';
4
+import request from '../../utils/request'
5
+
6
+const { Option } = Select;
7
+
8
+/**
9
+ *
10
+ *
11
+ * @param {*} props
12
+ * @returns
13
+ */
14
+const WxDictSelect = props => {
15
+  const [data, setData] = useState([])
16
+  const [value, setValue] = useState([])
17
+  useEffect(() => {
18
+    getWxDictList();
19
+  }, [props.value])
20
+
21
+
22
+  const getWxDictList = e => {
23
+    request({ ...apis.wxDict.list, params: { pageNumber: 1, pageSize: 999 } }).then(data => {
24
+        setData(data.records)
25
+        // 默认选中第一个
26
+    })
27
+  }
28
+
29
+  return (
30
+      <Select
31
+      showSearch
32
+      value={props.value}
33
+      style={{ width: '250px' }}
34
+      placeholder="请选择用户来源"
35
+      onChange={props.onChange}
36
+      filterOption={(input, option) =>
37
+        option.props.children && option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0
38
+      }>
39
+          {data.map(wxDict => (
40
+            <Option key={wxDict.sceneType} value={wxDict.sceneType}>{wxDict.sceneAlias}</Option>
41
+          ))}
42
+      </Select>
43
+  )
44
+}
45
+export default WxDictSelect

+ 47
- 0
src/components/SelectButton/channelSelect.jsx 查看文件

@@ -0,0 +1,47 @@
1
+import React, { useState, useEffect, useMemo } from 'react';
2
+import { Select } from 'antd';
3
+import apis from '../../services/apis';
4
+import request from '../../utils/request'
5
+
6
+const { Option } = Select;
7
+
8
+/**
9
+ *
10
+ *
11
+ * @param {*} props
12
+ * @returns
13
+ */
14
+const ChannelSelect = props => {
15
+  const [data, setData] = useState([])
16
+  useEffect(() => {
17
+    getChannelSelect();
18
+  }, [props.value])
19
+
20
+  const all = props.all || false
21
+
22
+
23
+  const getChannelSelect = e => {
24
+    request({ ...apis.channelList.getList, params: { pageNumber: 1, pageSize: 999 } }).then(data => {
25
+      setData(data.channelNmae)
26
+      // 默认选中第一个
27
+    })
28
+  }
29
+
30
+  return (
31
+    <Select
32
+      showSearch
33
+      value={props.value}
34
+      style={{ width: '250px' }}
35
+      placeholder="请选择渠道"
36
+      onChange={props.onChange}
37
+      filterOption={(input, option) =>
38
+        option.props.children && option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0
39
+      }>
40
+      {all && <Option key="" value="">全部渠道</Option>}
41
+      {data.map(Item => (
42
+        <Option key={Item.channelId} value={Item.channelId}> {Item.channelName} </Option>
43
+      ))}
44
+    </Select>
45
+  )
46
+}
47
+export default ChannelSelect

+ 53
- 0
src/components/SelectLang/index.jsx 查看文件

@@ -0,0 +1,53 @@
1
+import { Icon, Menu } from 'antd';
2
+import { formatMessage, getLocale, setLocale } from 'umi-plugin-react/locale';
3
+import React from 'react';
4
+import classNames from 'classnames';
5
+import HeaderDropdown from '../HeaderDropdown';
6
+import styles from './index.less';
7
+
8
+const SelectLang = props => {
9
+  const { className } = props;
10
+  const selectedLang = getLocale();
11
+
12
+  const changeLang = ({ key }) => setLocale(key, false);
13
+
14
+  const locales = ['zh-CN', 'zh-TW', 'en-US', 'pt-BR'];
15
+  const languageLabels = {
16
+    'zh-CN': '简体中文',
17
+    'zh-TW': '繁体中文',
18
+    'en-US': 'English',
19
+    'pt-BR': 'Português',
20
+  };
21
+  const languageIcons = {
22
+    'zh-CN': '🇨🇳',
23
+    'zh-TW': '🇭🇰',
24
+    'en-US': '🇺🇸',
25
+    'pt-BR': '🇧🇷',
26
+  };
27
+  const langMenu = (
28
+    <Menu className={styles.menu} selectedKeys={[selectedLang]} onClick={changeLang}>
29
+      {locales.map(locale => (
30
+        <Menu.Item key={locale}>
31
+          <span role="img" aria-label={languageLabels[locale]}>
32
+            {languageIcons[locale]}
33
+          </span>{' '}
34
+          {languageLabels[locale]}
35
+        </Menu.Item>
36
+      ))}
37
+    </Menu>
38
+  );
39
+  return (
40
+    <HeaderDropdown overlay={langMenu} placement="bottomRight">
41
+      <span className={classNames(styles.dropDown, className)}>
42
+        <Icon
43
+          type="global"
44
+          title={formatMessage({
45
+            id: 'navBar.lang',
46
+          })}
47
+        />
48
+      </span>
49
+    </HeaderDropdown>
50
+  );
51
+};
52
+
53
+export default SelectLang;

+ 24
- 0
src/components/SelectLang/index.less 查看文件

@@ -0,0 +1,24 @@
1
+@import '~antd/es/style/themes/default.less';
2
+
3
+.menu {
4
+  :global(.anticon) {
5
+    margin-right: 8px;
6
+  }
7
+  :global(.ant-dropdown-menu-item) {
8
+    min-width: 160px;
9
+  }
10
+}
11
+
12
+.dropDown {
13
+  line-height: @layout-header-height;
14
+  vertical-align: top;
15
+  cursor: pointer;
16
+  > i {
17
+    font-size: 16px !important;
18
+    transform: none !important;
19
+    svg {
20
+      position: relative;
21
+      top: -1px;
22
+    }
23
+  }
24
+}

+ 33
- 0
src/components/SettingDrawer/themeColorClient.js 查看文件

@@ -0,0 +1,33 @@
1
+// eslint-disable-next-line eslint-comments/disable-enable-pair
2
+
3
+/* eslint-disable import/no-extraneous-dependencies */
4
+import client from 'webpack-theme-color-replacer/client';
5
+import generate from '@ant-design/colors/lib/generate';
6
+export default {
7
+  getAntdSerials(color) {
8
+    const lightCount = 9;
9
+    const divide = 10; // 淡化(即less的tint)
10
+
11
+    let lightens = new Array(lightCount).fill(0);
12
+    lightens = lightens.map((_, i) => client.varyColor.lighten(color, i / divide));
13
+    const colorPalettes = generate(color);
14
+    return lightens.concat(colorPalettes);
15
+  },
16
+
17
+  changeColor(color) {
18
+    if (!color) {
19
+      return Promise.resolve();
20
+    }
21
+
22
+    const options = {
23
+      // new colors array, one-to-one corresponde with `matchColors`
24
+      newColors: this.getAntdSerials(color),
25
+
26
+      changeUrl(cssUrl) {
27
+        // while router is not `hash` mode, it needs absolute path
28
+        return `/${cssUrl}`;
29
+      },
30
+    };
31
+    return client.changer.changeColor(options, Promise);
32
+  },
33
+};

+ 109
- 0
src/components/Wangedit/Wangedit.jsx 查看文件

@@ -0,0 +1,109 @@
1
+import React from 'react';
2
+import E from 'wangeditor';
3
+import { fetch, apis } from '../../utils/request';
4
+
5
+/**
6
+ * @param {*} props
7
+ * @returns
8
+ */
9
+class Wangedit extends React.Component {
10
+  constructor(props, context) {
11
+    super(props, context);
12
+    this.state = {
13
+      html: undefined,
14
+      contenteditable: props.contenteditable == false ? false : true
15
+    }
16
+    this.editor = undefined;
17
+  }
18
+
19
+  render() {
20
+    return (
21
+      <div ref="editorElem" style={{ textAlign: 'left' }}>
22
+      </div>
23
+    );
24
+  }
25
+
26
+  componentDidMount() {
27
+    const elem = this.refs.editorElem
28
+    this.editor = new E(elem)
29
+    // 使用 onchange 函数监听内容的变化
30
+    this.editor.customConfig.onchange = html => {
31
+      this.setState({ html })
32
+
33
+      if (typeof this.props.onChange === 'function') {
34
+        this.props.onChange(html)
35
+      }
36
+    }
37
+    this.editor.customConfig.zIndex = 100
38
+    this.editor.customConfig.uploadImgMaxLength = 1
39
+    this.editor.customConfig.customUploadImg = function (files, insert) {
40
+      if (!files.length) return
41
+      
42
+      const data = new FormData()
43
+      data.append('file', files[0])
44
+
45
+      fetch(apis.image.upload)({data}).then(insert)
46
+    }
47
+    this.editor.customConfig.menus = [
48
+      'head',  // 标题
49
+      'bold',  // 粗体
50
+      'fontSize',  // 字号
51
+      'fontName',  // 字体
52
+      'italic',  // 斜体
53
+      'underline',  // 下划线
54
+      'strikeThrough',  // 删除线
55
+      'foreColor',  // 文字颜色
56
+      'backColor',  // 背景颜色
57
+      'list',  // 列表
58
+      'justify',  // 对齐方式
59
+      'quote',  // 引用
60
+      'image',  // 插入图片
61
+      'undo',  // 撤销
62
+      'redo'  // 重复
63
+    ]
64
+    
65
+    // 过滤 word 字符
66
+    this.editor.customConfig.pasteFilterStyle = false
67
+    this.editor.customConfig.pasteTextHandle = function(content) {
68
+      const regs = [
69
+        /<!--\[if [\s\S]*?endif\]-->/ig,
70
+        /<[a-zA-Z0-9]+\:[^>]+>[^>]*<\/[a-zA-Z0-9]+\:[^>]+>/ig,
71
+        /<[a-zA-Z0-9]+\:[^>]+\/>/ig,
72
+        /<style>[\s\S]*?<\/style>/ig,
73
+        new RegExp('\u2029', 'ig'),     // 替换word分隔符 序号 8233
74
+      ]
75
+
76
+      return regs.reduce((acc, reg) => {
77
+        return acc.replace(reg, '')
78
+      }, content)
79
+    }
80
+
81
+    this.editor.create()
82
+    this.editor.$textElem.attr('contenteditable',this.state.contenteditable);
83
+    this.editor.customConfig.uploadImgShowBase64 = true
84
+    this.editor.txt.html(this.props.value)
85
+  }
86
+
87
+  componentDidUpdate(props, state) {
88
+    if (this.props.value && !state.html) {
89
+      if (this.editor) {
90
+        this.editor.txt.html(this.props.value)
91
+      }
92
+    }
93
+  }
94
+
95
+  /**
96
+   *增加这个 shouldComponentUpdate 生命函数
97
+    处理自动聚焦到富文本上
98
+   *
99
+   * @param {*} nextProps
100
+   * @returns
101
+   * @memberof Wangedit
102
+   */
103
+  shouldComponentUpdate(nextProps) {
104
+    return nextProps.value !== this.editor.txt.html()
105
+  }
106
+}
107
+
108
+export default Wangedit
109
+

+ 84
- 0
src/components/XForm/FileUpload.jsx 查看文件

@@ -0,0 +1,84 @@
1
+import React, { useState, useEffect } from 'react'
2
+import { Upload, Button, Icon } from 'antd';
3
+import { uploaderProps } from '../../utils/upload';
4
+
5
+/**
6
+ * value 数据的接收格式 [{ url: xxxxx.mp4 }]
7
+ * size  参数限制可以上传多少个文件
8
+ * @param { value, size } props
9
+ */
10
+function fileUpload(props) {
11
+
12
+  const { value } = props
13
+  // console.log('fileUploadProps: ', props)
14
+  // eslint-disable-next-line react-hooks/rules-of-hooks
15
+  const [defaultFileList, setDefaultFileList] = useState([])
16
+
17
+  // eslint-disable-next-line react-hooks/rules-of-hooks
18
+  useEffect(() => {
19
+    setDefaultValue()
20
+  }, [props.value]);
21
+
22
+
23
+  function getFileList() {
24
+    console.log('fileUpload: ', value)
25
+    // value 数据的接收格式 [{ url: xxxxx.mp4 }, { url: xxxxx.jpg }]
26
+    return (value || []).filter(f => f !== undefined).map((img, inx) => ({ uid: inx, url: img, name: img.substring(img.lastIndexOf('/') + 1, img.length), status: 'done' }))
27
+  }
28
+
29
+  function setDefaultValue() {
30
+    if (!value) {
31
+      return;
32
+    }
33
+
34
+    setDefaultFileList(getFileList())
35
+  }
36
+
37
+  function onFileChange({ file, fileList }) {
38
+    console.log(file, fileList)
39
+    setDefaultFileList(fileList)
40
+    if (file.status === 'uploading') {
41
+      return
42
+    }
43
+
44
+    if (file.status === 'done' || file.status === 'removed') {
45
+
46
+      // 因为这个控件本身返回的数据格式 [{ ..., response: '服务器返回的数据' }]
47
+      // 但是 我这里自己用的时候 传入的数据是 [ 'xxxx.mp4', 'xxxx.jpg' ], 然后通过 getFileList() 转换成了 [{ url: 'xxx.mp4' }] 这样的格式
48
+
49
+      // 原因是因为 控件返回的数据 和 fileList 展示已经上传的数据 的格式字段不一样
50
+
51
+      const resFileList = fileList.filter(f => f.response !== undefined).map(i => i.response)
52
+      const tempFileList = fileList.filter(f => f.url !== undefined).map(i => i.url)
53
+      const resultList = tempFileList.concat(resFileList || [])
54
+      props.onChange(resultList)
55
+    }
56
+  }
57
+
58
+  return (
59
+    <>
60
+      <Upload
61
+        { ...uploaderProps }
62
+        {...props}
63
+        onChange={onFileChange}
64
+        fileList={defaultFileList}
65
+      >
66
+        {
67
+          props.size ?
68
+          (props.size > defaultFileList.length
69
+          && (
70
+            <Button>
71
+                <Icon type="upload" /> {props.label}
72
+              </Button>
73
+          )) : (
74
+            <Button>
75
+              <Icon type="upload" /> {props.label}
76
+            </Button>
77
+          )
78
+        }
79
+      </Upload>
80
+    </>
81
+  )
82
+}
83
+
84
+export default fileUpload

+ 98
- 0
src/components/XForm/ImageListUpload.jsx 查看文件

@@ -0,0 +1,98 @@
1
+import React from 'react';
2
+import { Upload, Icon, Modal } from 'antd';
3
+import './style.less';
4
+import { uploaderProps } from '../../utils/upload';
5
+
6
+/**
7
+ * unlimited  属性表示上传图片无限制
8
+ * 例子: <ImageListUpload unlimited/>
9
+ */
10
+class ImageListUpload extends React.Component {
11
+  state = {
12
+    previewVisible: false,
13
+    previewImage: '',
14
+    loadding: false,
15
+  };
16
+
17
+  getFileList = () => {
18
+    return (this.props.value || []).map((img, inx) => ({ uid: inx, url: img, status: 'done' }))
19
+  }
20
+
21
+  handleCancel = () => this.setState({ previewVisible: false });
22
+
23
+  handlePreview = async file => {
24
+    this.setState({
25
+      previewImage: file.url ,
26
+      previewVisible: true,
27
+    });
28
+  };
29
+
30
+  handleChange = (e) => {
31
+    if (e.file.status === "uploading") {
32
+      this.setState({ loading: true });
33
+      return;
34
+    }
35
+
36
+    const fileList = (this.props.value || []).filter(x => x != e.file.url);
37
+    this.props.onChange(fileList);
38
+
39
+    // console.log('删除图片触发了', e.file)
40
+    // if (e.file.state === 'removed') {
41
+    //   const fileList = (this.props.value || []).filter(x => x != e.file.url);
42
+    //   this.props.onChange(fileList);
43
+    // }
44
+
45
+    // if (e.file.status === "done") {
46
+    //   this.setState({
47
+    //     loading: false,
48
+    //   })
49
+
50
+    //   if (e.file.response && e.file.response.url) {
51
+    //     if (typeof this.props.onChange === 'function') {
52
+    //       const fileList = this.getFileList()
53
+    //       this.props.onChange([...fileList || [], e.file.response.url]);
54
+    //     }
55
+    //   }
56
+    // }
57
+  };
58
+
59
+  handleUploadSucess = (url) => {
60
+    this.setState({ loading: false });
61
+    if (typeof this.props.onChange === 'function') {
62
+      const fileList = this.props.value || [];
63
+      this.props.onChange([...fileList, url]);
64
+    }
65
+  }
66
+
67
+  render() {
68
+    const { previewVisible, previewImage } = this.state;
69
+    const fileList = this.getFileList();
70
+
71
+    const uploadButton = (
72
+      <div>
73
+        <Icon style={{ fontSize: '2em', color: '#aaa' }} type={this.state.loading ? "loading" : "plus"}  />
74
+      </div>
75
+    );
76
+    return (
77
+      <div className="clearfix">
78
+        <Upload
79
+          listType="picture-card"
80
+          multiple={true}
81
+          fileList={fileList}
82
+          onPreview={this.handlePreview}
83
+          onChange={this.handleChange}
84
+          { ...uploaderProps }
85
+          onSuccess={this.handleUploadSucess}
86
+        >
87
+          {/* unlimited 表示上传无限制数量 */}
88
+          {(this.props.unlimited && uploadButton) || ((fileList || images).length >= 8 ? null : uploadButton)}
89
+        </Upload>
90
+        <Modal visible={previewVisible} footer={null} onCancel={this.handleCancel}>
91
+          <img alt="example" style={{ width: '100%' }} src={previewImage} />
92
+        </Modal>
93
+      </div>
94
+    );
95
+  }
96
+}
97
+
98
+export default ImageListUpload;

+ 77
- 0
src/components/XForm/ImageUpload.jsx 查看文件

@@ -0,0 +1,77 @@
1
+import React from 'react';
2
+import { Upload, Icon, message } from 'antd';
3
+import './style.less';
4
+import { uploaderProps } from '../../utils/upload';
5
+
6
+
7
+
8
+class ImageUpload extends React.Component {
9
+  state = {
10
+    loading: false,
11
+    imageUrl: undefined,
12
+  };
13
+
14
+  handleChange = info => {
15
+    if (info.file.status === "uploading") {
16
+      this.setState({ loading: true });
17
+      return;
18
+    }
19
+    
20
+    if (info.file.status === 'removed') {
21
+      this.props.onChange();
22
+    }
23
+
24
+    // if (info.file.status === "done") {
25
+    //   this.setState({
26
+    //     loading: false,
27
+    //   })
28
+
29
+    //   if (info.file.response && info.file.response.url) {
30
+    //     this.setState({
31
+    //       imageUrl: info.file.response.thumbUrl,
32
+    //     });
33
+
34
+    //     if (typeof this.props.onChange === 'function') {
35
+    //       this.props.onChange(info.file.response.url);
36
+    //     }
37
+    //   }
38
+    // }
39
+  };
40
+
41
+  handleUploadSucess = (url) => {
42
+    this.setState({ loading: false });
43
+    if (typeof this.props.onChange === 'function') {
44
+      this.props.onChange(url);
45
+    }
46
+  }
47
+
48
+  render() {
49
+    const uploadButton = (
50
+      <div>
51
+        <Icon style={{ fontSize: '2em', color: '#aaa' }} type={this.state.loading ? "loading" : "plus"} />
52
+      </div>
53
+    );
54
+
55
+    const value = this.props.value;
56
+    return (
57
+      <Upload
58
+        listType="picture-card"
59
+        className="avatar-uploader"
60
+        showUploadList={false}
61
+        beforeUpload={this.props.beforeUpload}
62
+        onChange={this.handleChange}
63
+        {...uploaderProps}
64
+        disabled={this.props.disabled}
65
+        onSuccess={this.handleUploadSucess}
66
+      >
67
+        {(this.state.imageUrl || value) ? (
68
+          <img src={this.state.imageUrl || value} alt="avatar" style={{ width: "100%" }} />
69
+        ) : (
70
+            uploadButton
71
+          )}
72
+      </Upload>
73
+    );
74
+  }
75
+}
76
+
77
+export default ImageUpload;

+ 31
- 0
src/components/XForm/README.md 查看文件

@@ -0,0 +1,31 @@
1
+# XForm
2
+Antd Form 扩展
3
+
4
+## 使用说明
5
+
6
+```jsx
7
+import XForm, { FieldTypes } from '/path/to/XForm';
8
+
9
+const tplJSON = [
10
+  {},
11
+  {}
12
+]
13
+
14
+```
15
+
16
+通过 `json` 对象来实现自动构造 form 表单
17
+
18
+计划支持的属性
19
+
20
+[] label      显示的文字
21
+
22
+[] name       form name, 用于表单提交
23
+
24
+[] type       组件类型, 参见 FieldTypes
25
+
26
+[] rules      校验规则
27
+
28
+[] element    函数, 用于生成组件. (props) => <></> . 其中 props 包含 ...
29
+
30
+[] value      绑定的数据
31
+

+ 81
- 0
src/components/XForm/WrapperForm.jsx 查看文件

@@ -0,0 +1,81 @@
1
+import React from 'react';
2
+import PropTypes from 'prop-types';
3
+import { Form, Button } from 'antd';
4
+import WrapperItem from './WrapperItem';
5
+
6
+const formItemLayout = {
7
+  labelCol: {
8
+    xs: { span: 24 },
9
+    sm: { span: 8 },
10
+  },
11
+  wrapperCol: {
12
+    xs: { span: 24 },
13
+    sm: { span: 16 },
14
+  },
15
+};
16
+
17
+class WrapperForm extends React.Component {
18
+  constructor(props) {
19
+    super(props);
20
+
21
+    this.state = {}
22
+  }
23
+
24
+  handleSubmit = e => {
25
+    e.preventDefault();
26
+    this.props.form.validateFieldsAndScroll((err, values) => {
27
+      if (err) {
28
+        if (typeof this.props.onError === 'function') {
29
+          typeof this.props.onError(err)
30
+        } else {
31
+          window.console.error(err)
32
+        }
33
+      } else {
34
+        if (typeof this.props.onSubmit === 'function') {
35
+          typeof this.props.onSubmit(values)
36
+        }
37
+      }
38
+    });
39
+  }
40
+
41
+  handleCancel = e => {
42
+    e.preventDefault();
43
+
44
+    if (typeof this.props.onCancel === 'function') {
45
+      typeof this.props.onCancel()
46
+    }
47
+  }
48
+
49
+  renderDefaultAction = (submitBtn, cancelBtn) => {
50
+    let FieldSubmit = null
51
+    let FieldCancel = null
52
+    if (submitBtn !== false) {
53
+      FieldSubmit = <Button htmlType="submit" type="primary">提交</Button>
54
+    }
55
+    if (cancelBtn !== false) {
56
+      FieldCancel = <Button htmlType="button" onClick={this.handleCancel} style={{ marginLeft: '48px' }}>取消</Button>
57
+    }
58
+
59
+    return FieldSubmit || FieldCancel ? <WrapperItem action render={<>{FieldSubmit}{FieldCancel}</>} /> : null
60
+  }
61
+
62
+  render () {
63
+    const {fields, form, children, submitBtn, cancelBtn, ...formProps} = this.props;
64
+    console.log('fields:', fields)
65
+    const FeildItems = (fields || []).filter(x => x).map((props, inx) => (<WrapperItem key={inx} {...props} form={form} />))
66
+    
67
+    return (
68
+      <Form {...formItemLayout} {...formProps} onSubmit={this.handleSubmit}>
69
+        {FeildItems}
70
+        {this.renderDefaultAction(submitBtn, cancelBtn)}
71
+        {children}
72
+      </Form>
73
+    );
74
+  }
75
+}
76
+
77
+WrapperForm.propTypes = {
78
+  fields: PropTypes.array.isRequired
79
+}
80
+
81
+export default WrapperForm;

+ 151
- 0
src/components/XForm/WrapperItem.jsx 查看文件

@@ -0,0 +1,151 @@
1
+import React from 'react';
2
+import {
3
+  Form,
4
+  Input,
5
+  InputNumber,
6
+  Checkbox,
7
+  Radio,
8
+  DatePicker,
9
+  TimePicker,
10
+  Select,
11
+  Switch,
12
+} from 'antd';
13
+import ImageUploader from './ImageUpload';
14
+
15
+const {Item} = Form
16
+const { Option } = Select;
17
+const { MonthPicker, RangePicker } = DatePicker
18
+
19
+const FieldTypes = {
20
+  Text: 'Text',
21
+  Password: 'Password',
22
+  Number: 'Number',
23
+  Switch: 'Switch',
24
+  TimePicker: 'TimePicker',
25
+  DatePicker: 'DatePicker',
26
+  RangePicker: 'RangePicker',
27
+  MonthPicker: 'MonthPicker',
28
+  Select: 'Select',
29
+  ImageUploader: 'ImageUploader',
30
+}
31
+
32
+const tailFormItemLayout = {
33
+  wrapperCol: {
34
+    xs: {
35
+      span: 24,
36
+      offset: 0,
37
+    },
38
+    sm: {
39
+      span: 16,
40
+      offset: 8,
41
+    },
42
+  },
43
+};
44
+
45
+const WrapperItem = props => {
46
+  const {
47
+    form,
48
+    label,
49
+    name,
50
+    type,
51
+    placeholder,
52
+    render,
53
+    rules,
54
+    value,
55
+    dict,
56
+    action,
57
+    props: fieldProps,
58
+    hidden = false,
59
+    help,
60
+    ...restProps
61
+  } = props;
62
+
63
+  const { getFieldDecorator } = form || {};
64
+
65
+  let config = {
66
+    rules,
67
+    initialValue: value,
68
+  }
69
+
70
+  if (type === FieldTypes.Switch) {
71
+    config = {
72
+      ...config,
73
+      valuePropName: 'checked',
74
+    };
75
+  }
76
+
77
+  if (type === FieldTypes.ImageUploader) {
78
+    config = {
79
+      ...config,
80
+      getValueFromEvent: x => x,
81
+    };
82
+  }
83
+
84
+  // 没有类型与组件, 生成隐藏字段
85
+  if (!type && !render) {
86
+    getFieldDecorator(name, config);
87
+    return null;
88
+  }
89
+
90
+  const SelectOpts = (dict || []).map((item, index) => (<Option value={item.value}>{item.label}</Option>))
91
+
92
+  let Field = <></>;
93
+  if (render) {
94
+    Field = typeof render === 'function' ? render(props) : render
95
+  } else {
96
+    switch (type) {
97
+      case FieldTypes.Text:
98
+        Field = <Input placeholder={placeholder} style={{ width: '100%' }} {...fieldProps}/>;
99
+        break;
100
+      case FieldTypes.Password:
101
+        Field = <Input.Password placeholder={placeholder} style={{ width: '100%' }} {...fieldProps}/>;
102
+        break;
103
+      case FieldTypes.Number:
104
+        Field = <InputNumber placeholder={placeholder} style={{ width: '100%' }} {...fieldProps}/>;
105
+        break;
106
+      case FieldTypes.Switch:
107
+        Field = <Switch {...fieldProps} />;
108
+        break;
109
+      case FieldTypes.TimePicker:
110
+        Field = <TimePicker {...fieldProps} style={{ width: '100%' }}/>;
111
+        break;
112
+      case FieldTypes.DatePicker:
113
+        Field = <DatePicker {...fieldProps} style={{ width: '100%' }}/>;
114
+        break;
115
+      case FieldTypes.RangePicker:
116
+        Field = <RangePicker {...fieldProps} style={{ width: '100%' }}/>;
117
+        break;
118
+      case FieldTypes.MonthPicker:
119
+        Field = <MonthPicker {...fieldProps} style={{ width: '100%' }}/>;
120
+        break;
121
+      case FieldTypes.Select:
122
+        Field = <Select placeholder={placeholder} style={{ width: '100%' }} {...fieldProps}>{SelectOpts}</Select>
123
+        break;
124
+      case FieldTypes.ImageUploader:
125
+        const beforeUpload = { beforeUpload: props.beforeUpload }
126
+        Field = <ImageUploader {...fieldProps} { ...beforeUpload}/>
127
+        break;
128
+      default:
129
+        throw new Error(`暂时不支持的组件类型: ${type}`)
130
+    }
131
+  }
132
+
133
+  const visible = typeof hidden === 'function' ? !hidden() : !hidden;
134
+
135
+  if (!label && !name && !action && !help) return visible ? Field : null;
136
+
137
+  const itemProps = action ? { ...restProps, ...tailFormItemLayout } : restProps
138
+  const labelNode = typeof label === 'function' ? label() : label;
139
+  const helpNode = typeof help === 'function' ? help() : help;
140
+
141
+  const newItemProps = { ...itemProps, help: helpNode }
142
+
143
+  return visible && (
144
+    <Item label={labelNode} { ...newItemProps }>
145
+      {action ? Field : getFieldDecorator(name, config)(Field)}
146
+    </Item>
147
+  )
148
+};
149
+
150
+export default WrapperItem;
151
+export { FieldTypes };

+ 9
- 0
src/components/XForm/index.jsx 查看文件

@@ -0,0 +1,9 @@
1
+import React from 'react';
2
+import { Form } from 'antd';
3
+import WrapperForm from './WrapperForm';
4
+import { FieldTypes } from './WrapperItem';
5
+
6
+const createForm = option => Form.create(option)(WrapperForm);
7
+
8
+export default Form.create()(WrapperForm);
9
+export { FieldTypes, createForm };

+ 13
- 0
src/components/XForm/style.css 查看文件

@@ -0,0 +1,13 @@
1
+:global .avatar-uploader > .ant-upload {
2
+  width: 128px;
3
+  height: 128px;
4
+}
5
+/* you can make up upload button and sample style by using stylesheets */
6
+.ant-upload-select-picture-card i {
7
+  font-size: 32px;
8
+  color: #999;
9
+}
10
+.ant-upload-select-picture-card .ant-upload-text {
11
+  margin-top: 8px;
12
+  color: #666;
13
+}

+ 20
- 0
src/components/XForm/style.less 查看文件

@@ -0,0 +1,20 @@
1
+:global {
2
+  .avatar-uploader {
3
+    & > .ant-upload {
4
+      width: 128px;
5
+      height: 128px;
6
+    }
7
+  }
8
+}
9
+
10
+
11
+/* you can make up upload button and sample style by using stylesheets */
12
+.ant-upload-select-picture-card i {
13
+  font-size: 32px;
14
+  color: #999;
15
+}
16
+
17
+.ant-upload-select-picture-card .ant-upload-text {
18
+  margin-top: 8px;
19
+  color: #666;
20
+}

+ 13
- 0
src/components/XForm/style.wxss 查看文件

@@ -0,0 +1,13 @@
1
+:global .avatar-uploader > .ant-upload {
2
+  width: 128px;
3
+  height: 128px;
4
+}
5
+/* you can make up upload button and sample style by using stylesheets */
6
+.ant-upload-select-picture-card i {
7
+  font-size: 32px;
8
+  color: #999;
9
+}
10
+.ant-upload-select-picture-card .ant-upload-text {
11
+  margin-top: 8px;
12
+  color: #666;
13
+}

+ 36
- 0
src/components/ZmageImg/ZmageImg.jsx 查看文件

@@ -0,0 +1,36 @@
1
+import React from 'react'
2
+import Zmage from 'react-zmage'
3
+
4
+function body(props) {
5
+  return (
6
+    <>
7
+      <Zmage
8
+        style={props.style}
9
+        src={props.src}
10
+        // alt="0"
11
+        backdrop="linear-gradient(90deg, rgba(86,81,81,0.7) 0%, rgba(86,81,81, 0.7) 100%)"
12
+        radius={5}
13
+        edge={20}
14
+        animate={{
15
+          flip: 'fade',
16
+        }}
17
+        controller={{
18
+          // 关闭按钮
19
+          close: false,
20
+          // 缩放按钮
21
+          zoom: false,
22
+          // 下载按钮
23
+          download: false,
24
+          // 旋转按钮
25
+          rotate: false,
26
+          // 翻页按钮
27
+          flip: true,
28
+          // 多页指示
29
+          pagination: true,
30
+        }}
31
+      />
32
+    </>
33
+  )
34
+}
35
+
36
+export default body

+ 81
- 0
src/components/uploadImage/ImageUpload.jsx 查看文件

@@ -0,0 +1,81 @@
1
+import React from 'react';
2
+import { Upload, Icon, message, Modal } from 'antd';
3
+import './style.less';
4
+import { uploaderProps } from '../../utils/upload';
5
+
6
+
7
+
8
+class ImageUpload extends React.Component {
9
+
10
+  state = {
11
+    loading: false,
12
+    previewVisible: false,
13
+    previewImage: '',
14
+    fileList: this.getPropsFileList(),
15
+  };
16
+
17
+  componentDidUpdate(prevProps) {
18
+    // 直接 setState 会死循环
19
+    if (prevProps.value !== this.props.value) {
20
+      this.setState({ fileList: this.getPropsFileList() })
21
+    }
22
+  }
23
+
24
+  getPropsFileList() {
25
+    return !this.props.value ? [] : [{
26
+      uid: '-1',
27
+      name: 'image.png',
28
+      status: 'done',
29
+      url: this.props.value,
30
+    }]
31
+  }
32
+
33
+
34
+  handleChange = ({ file, fileList }) => {
35
+    const { status, response } = file
36
+    this.setState({ fileList, loading: status === "uploading" })
37
+
38
+    if (typeof this.props.onChange === 'function' && status != "uploading") {
39
+      const image = status === 'done' ? response : undefined
40
+      this.props.onChange(image);
41
+    }
42
+  };
43
+
44
+  handleCancel = () => this.setState({ previewVisible: false });
45
+
46
+  handlePreview = async file => {
47
+    console.log(file)
48
+    this.setState({
49
+      previewImage: file.url,
50
+      previewVisible: true,
51
+    });
52
+  };
53
+
54
+  render() {
55
+    const uploadButton = (
56
+      <div>
57
+        <Icon style={{ fontSize: '2em', color: '#aaa' }} type={this.state.loading ? "loading" : "plus"} />
58
+      </div>
59
+    );
60
+
61
+    return (
62
+      <>
63
+      <Upload
64
+        {...uploaderProps}
65
+        listType="picture-card"
66
+        className="avatar-uploader"
67
+        onChange={this.handleChange}
68
+        fileList={this.state.fileList}
69
+        onPreview={this.handlePreview}
70
+      >
71
+        {this.state.fileList.length >= 1 ? null : uploadButton}  
72
+      </Upload>
73
+      <Modal visible={this.state.previewVisible} footer={null} onCancel={this.handleCancel}>
74
+      <img alt="example" style={{ width: '100%' }} src={this.state.previewImage} />
75
+      </Modal>
76
+      </>
77
+    );
78
+  }
79
+}
80
+
81
+export default ImageUpload;

+ 8
- 0
src/components/uploadImage/style.less 查看文件

@@ -0,0 +1,8 @@
1
+:global {
2
+  .avatar-uploader {
3
+    & > .ant-upload {
4
+      width: 128px;
5
+      height: 128px;
6
+    }
7
+  }
8
+}

+ 1
- 0
src/e2e/__mocks__/antd-pro-merge-less.js 查看文件

@@ -0,0 +1 @@
1
+export default undefined;

+ 39
- 0
src/e2e/baseLayout.e2e.js 查看文件

@@ -0,0 +1,39 @@
1
+const RouterConfig = require('../../config/config').default.routes;
2
+const { uniq } = require('lodash');
3
+
4
+const BASE_URL = `http://localhost:${process.env.PORT || 8000}`;
5
+
6
+function formatter(routes, parentPath = '') {
7
+  const fixedParentPath = parentPath.replace(/\/{1,}/g, '/');
8
+  let result = [];
9
+  routes.forEach(item => {
10
+    if (item.path) {
11
+      result.push(`${fixedParentPath}/${item.path}`.replace(/\/{1,}/g, '/'));
12
+    }
13
+    if (item.routes) {
14
+      result = result.concat(
15
+        formatter(item.routes, item.path ? `${fixedParentPath}/${item.path}` : parentPath),
16
+      );
17
+    }
18
+  });
19
+  return uniq(result.filter(item => !!item));
20
+}
21
+
22
+describe('Ant Design Pro E2E test', () => {
23
+  const testPage = path => async () => {
24
+    await page.goto(`${BASE_URL}${path}`);
25
+    await page.waitForSelector('footer', {
26
+      timeout: 2000,
27
+    });
28
+    const haveFooter = await page.evaluate(
29
+      () => document.getElementsByTagName('footer').length > 0,
30
+    );
31
+    expect(haveFooter).toBeTruthy();
32
+  };
33
+
34
+  const routers = formatter(RouterConfig);
35
+  console.log('routers', routers);
36
+  routers.forEach(route => {
37
+    it(`test pages ${route}`, testPage(route));
38
+  });
39
+});

+ 15
- 0
src/e2e/topMenu.e2e.js 查看文件

@@ -0,0 +1,15 @@
1
+const BASE_URL = `http://localhost:${process.env.PORT || 8000}`;
2
+
3
+describe('Homepage', () => {
4
+  it('topmenu should have footer', async () => {
5
+    const params = '/form/basic-form?navTheme=light&layout=topmenu';
6
+    await page.goto(`${BASE_URL}${params}`);
7
+    await page.waitForSelector('footer', {
8
+      timeout: 2000,
9
+    });
10
+    const haveFooter = await page.evaluate(
11
+      () => document.getElementsByTagName('footer').length > 0,
12
+    );
13
+    expect(haveFooter).toBeTruthy();
14
+  });
15
+});

+ 155
- 0
src/layouts/BasicLayout.jsx 查看文件

@@ -0,0 +1,155 @@
1
+/**
2
+ * Ant Design Pro v4 use `@ant-design/pro-layout` to handle Layout.
3
+ * You can view component api by:
4
+ * https://github.com/ant-design/ant-design-pro-layout
5
+ */
6
+import ProLayout from '@ant-design/pro-layout';
7
+import React, { useEffect } from 'react';
8
+import Link from 'umi/link';
9
+import Redirect from 'umi/redirect';
10
+import { connect } from 'dva';
11
+import { formatMessage } from 'umi-plugin-react/locale';
12
+// import Authorized from '@/utils/Authorized';
13
+import RightContent from '@/components/GlobalHeader/RightContent';
14
+import RenderAuthorize from '@/components/Authorized';
15
+import { isAntDesignPro } from '@/utils/utils';
16
+import logo from '../assets/logo.png';
17
+
18
+const footerRender = () => {
19
+  return (<></>
20
+    // <div
21
+    //   style={{
22
+    //     padding: ' 44px 0',
23
+    //     textAlign: 'center',
24
+    //     fontSize: '30px',
25
+    //     fontFamily: 'monospace',
26
+    //     fontWeight: '200',
27
+    //     // height:'200px',
28
+    //     color: 'rgba(102, 102, 102, 1)',
29
+    //     // position: 'fixed',
30
+    //     // bottom: '0',
31
+    //     // right: '0',
32
+    //     // marginTop: '20px',
33
+    //     // width:'100%'
34
+    //   }}
35
+    // >
36
+    //   Copy Right @ 南京云致
37
+    // </div>
38
+  )
39
+}
40
+const menuHeaderRender = (user) => (logo, title) => {
41
+  if (user && user.orgId) {
42
+    return (
43
+      <Link to="/">
44
+        <img src={user.orgLogo} alt="logo"/>
45
+        <h1>{user.miniAppName}</h1>
46
+      </Link>
47
+    );
48
+  } else {
49
+    return (
50
+      <Link to="/">
51
+        {logo}
52
+        {title}
53
+      </Link>
54
+    );
55
+  }
56
+}
57
+
58
+const BasicLayout = props => {
59
+  const { dispatch, children, settings } = props;
60
+
61
+  useEffect(() => {
62
+    if (dispatch && !props.user.currentUser.userId) {
63
+      dispatch({
64
+        type: 'user/fetchCurrent',
65
+      });
66
+      dispatch({
67
+        type: 'settings/getSetting',
68
+      });
69
+    }
70
+  }, []);
71
+
72
+  useEffect(() => {
73
+    const link = document.querySelector("link[rel*='icon']")
74
+    if (link) {
75
+      link.setAttribute('rel', 'shortcut icon')
76
+      link.setAttribute('href', props.user.currentUser.orgLogo)
77
+    }
78
+  }, [])
79
+
80
+  const Authorized = RenderAuthorize(props.user.currentUser.roles)
81
+
82
+  const handleMenuCollapse = payload => {
83
+    if (dispatch) {
84
+      dispatch({
85
+        type: 'global/changeLayoutCollapsed',
86
+        payload,
87
+      });
88
+    }
89
+  };
90
+
91
+  const findAuthority = path => ((props.user.menuList || []).filter(x => x.code === path)[0] || {}).roles
92
+
93
+  /**
94
+   * use Authorized check all menu item
95
+   */
96
+  const menuDataRender = menuList =>
97
+    menuList.map(item => {
98
+      const localItem = { ...item, children: item.children ? menuDataRender(item.children) : [] };
99
+      const authority = findAuthority(item.path);
100
+      // return Authorized.check(item.authority, localItem, null);
101
+      return Authorized.check(authority, localItem, null);
102
+    });
103
+
104
+  const checkRights = children => {
105
+    const authority = findAuthority(props.location.pathname);
106
+    return Authorized.check(authority, children, <Redirect to="/403" />);
107
+  }
108
+
109
+  return (
110
+    <ProLayout
111
+      logo={logo}
112
+      onCollapse={handleMenuCollapse}
113
+      menuItemRender={(menuItemProps, defaultDom) => {
114
+        if (menuItemProps.isUrl) {
115
+          return defaultDom;
116
+        }
117
+        return <Link to={menuItemProps.path}>{defaultDom}</Link>;
118
+      }}
119
+      breadcrumbRender={(routers = []) => [
120
+        {
121
+          path: '/',
122
+          breadcrumbName: formatMessage({
123
+            id: 'menu.home',
124
+            defaultMessage: 'Home',
125
+          }),
126
+        },
127
+        ...routers,
128
+      ]}
129
+      itemRender={(route, params, routes, paths) => {
130
+        const first = routes.indexOf(route) === 0;
131
+        return first ? (
132
+          <Link to={paths.join('/')}>{route.breadcrumbName}</Link>
133
+        ) : (
134
+            <span>{route.breadcrumbName}</span>
135
+          );
136
+      }}
137
+      footerRender={footerRender}
138
+      menuDataRender={menuDataRender}
139
+      menuHeaderRender={menuHeaderRender(props.user.currentUser)}
140
+      // formatMessage={formatMessage}
141
+      rightContentRender={rightProps => <RightContent {...rightProps} />}
142
+      {...props}
143
+      {...settings}
144
+    // pageTitleRender={()=><></>}
145
+    >
146
+      {checkRights(children)}
147
+    </ProLayout>
148
+  );
149
+};
150
+
151
+export default connect(({ global, settings, user }) => ({
152
+  collapsed: global.collapsed,
153
+  settings,
154
+  user
155
+}))(BasicLayout);

+ 20
- 0
src/layouts/BlankLayout.jsx 查看文件

@@ -0,0 +1,20 @@
1
+import React from 'react';
2
+import { PageHeaderWrapper } from '@ant-design/pro-layout';
3
+
4
+const Layout = ({ children }) => (
5
+  <PageHeaderWrapper style={{height:'auto',background:'rgba(240,240,240,1)',paddingTop:'15px'}}>
6
+    <div
7
+      style={{
8
+        backgroundColor: '#fff',
9
+        padding: '32PX 28px',
10
+        boxShadow: '0px 0px 16px 2px rgba(0,0,0,0.12)',
11
+        borderRadius: '12px',
12
+        minHeight:'60vh'
13
+      }}
14
+    >
15
+      {children}
16
+    </div>
17
+  </PageHeaderWrapper>
18
+);
19
+
20
+export default Layout;

+ 17
- 0
src/layouts/SearchList/BodyHeader.jsx 查看文件

@@ -0,0 +1,17 @@
1
+import React, { PureComponent } from 'react'
2
+import Style from './style.less';
3
+
4
+export default class BodyHeader extends PureComponent {
5
+  render() {
6
+    return (
7
+      <div className={Style['body-header']}>
8
+        <div className={Style['body-header-title']}>
9
+          {this.props.title}
10
+        </div>
11
+        <div className={Style['body-header-action']}>
12
+          {this.props.actions}
13
+        </div>
14
+      </div>
15
+    )
16
+  }
17
+}

+ 34
- 0
src/layouts/SearchList/BodyPagination.jsx 查看文件

@@ -0,0 +1,34 @@
1
+import React, { PureComponent } from 'react'
2
+import PropTypes from 'prop-types'
3
+import { Pagination } from 'antd'
4
+
5
+import Style from './style.less'
6
+
7
+export default class BodyPagination extends PureComponent {
8
+  static propTypes = {
9
+    storage: PropTypes.object,
10
+    onChange: PropTypes.func,
11
+    configs: PropTypes.object,
12
+  }
13
+
14
+  // storage = this.props.storage
15
+
16
+  handleChange = (page, pageSize) => {
17
+    if (this.props.onChange) {
18
+      this.props.onChange(page, pageSize)
19
+    }
20
+  }
21
+
22
+  render() {
23
+    return (
24
+      <div className={Style['body-paged']}>
25
+        <Pagination
26
+          showSizeChanger
27
+          onChange={this.handleChange}
28
+          onShowSizeChange={this.handleChange} 
29
+          {...(this.props.configs || {})}
30
+        />
31
+      </div>
32
+    )
33
+  }
34
+}

+ 28
- 0
src/layouts/SearchList/SearchBody.jsx 查看文件

@@ -0,0 +1,28 @@
1
+import React, { PureComponent } from 'react'
2
+import PropTypes from 'prop-types'
3
+
4
+import { Table } from 'antd'
5
+import { isFunction } from './utils'
6
+
7
+export default class SearchBody extends PureComponent {
8
+  static propTypes = {
9
+    storage: PropTypes.object,
10
+    dataSource: PropTypes.array,
11
+    configs: PropTypes.object,
12
+  }
13
+
14
+  // storage = this.props.storage
15
+
16
+  render() {
17
+    const { dataSource, configs } = this.props
18
+    const { render, ...leftProps } = configs || {}
19
+
20
+    if (isFunction(render)) {
21
+      return render(dataSource)
22
+    }
23
+
24
+    return (
25
+      <Table pagination={false} dataSource={dataSource} {...leftProps} />
26
+    )
27
+  }
28
+}

+ 79
- 0
src/layouts/SearchList/SearchForm.jsx 查看文件

@@ -0,0 +1,79 @@
1
+import React, { PureComponent } from 'react'
2
+import PropTypes from 'prop-types'
3
+import createForm from '@zjxpcyc/xrc-form2'
4
+import { Button } from 'antd'
5
+
6
+const XForm = createForm()
7
+
8
+function clearProperties (o) {
9
+  return Object.keys(o).reduce((acc, k) => {
10
+    return {
11
+      ...acc,
12
+      [`${k}`]: undefined,
13
+    }
14
+  }, {})
15
+}
16
+
17
+export default class SearchForm extends PureComponent {
18
+  static propTypes = {
19
+    storage: PropTypes.object,
20
+    onSubmit: PropTypes.func,
21
+    onCancel: PropTypes.func,
22
+    configs: PropTypes.object,
23
+  }
24
+
25
+  wrapperForm = null
26
+  storage = this.props.storage || {}
27
+
28
+  componentDidMount() {
29
+    const conditions = this.storage.get('conditions')
30
+
31
+    if (this.wrapperForm && conditions) {
32
+      this.wrapperForm.props.form.setFieldsValue(conditions)
33
+    }
34
+  }
35
+
36
+  handleCancel = () => {
37
+    const conditions = this.storage.get('conditions') || {}
38
+    this.storage.set('conditions', {})
39
+
40
+    if (this.wrapperForm) {
41
+      this.wrapperForm.props.form.setFieldsValue(clearProperties(conditions))
42
+    }
43
+
44
+    if (this.props.onCancel) {
45
+      this.props.onCancel()
46
+    }
47
+  }
48
+
49
+  wrapperFields = (fields) => {
50
+    return [
51
+      ...fields,
52
+      {
53
+        action: true,
54
+        render: (props) => (<Button htmlType="submit" type="primary">搜索</Button>)
55
+      },
56
+      {
57
+        action: true,
58
+        render: (props) => (<Button htmlType="button" onClick={this.handleCancel} style={{ marginLeft: '16px' }}>重置</Button>)
59
+      },
60
+    ]
61
+  }
62
+
63
+  render() {
64
+    const { fields, ...leftProps } = this.props.configs || {}
65
+
66
+    return (
67
+      <XForm
68
+        submitBtn={false}
69
+        cancelBtn={false}
70
+        layout="inline"
71
+        fields={this.wrapperFields(fields)}
72
+        onSubmit={this.props.onSubmit}
73
+        wrappedComponentRef={f => this.wrapperForm = f}
74
+        {...leftProps}
75
+      />
76
+    )
77
+  }
78
+}
79
+

+ 167
- 0
src/layouts/SearchList/index.jsx 查看文件

@@ -0,0 +1,167 @@
1
+import React from 'react'
2
+import PropTypes from 'prop-types'
3
+import classNames from 'classnames'
4
+import { Spin, notification } from 'antd'
5
+
6
+import SearchForm from './SearchForm'
7
+import SearchBody from './SearchBody'
8
+import BodyHeader from './BodyHeader'
9
+import BodyPagination from './BodyPagination'
10
+import { getStorageBuilder } from './utils'
11
+import Style from './style.less'
12
+
13
+class SearchList extends React.PureComponent {
14
+  static propTypes = {
15
+    storage: PropTypes.object,
16
+    title: PropTypes.any,
17
+    actions: PropTypes.any,
18
+    search: PropTypes.object,
19
+    body: PropTypes.object,
20
+    pagination: PropTypes.object,
21
+    service: PropTypes.func,
22
+    ejectRetrieve: PropTypes.func,
23
+  }
24
+
25
+  state = {
26
+    defaultStorage: this.props.storage || getStorageBuilder('PAGE-SearchList'),
27
+    dataSource: [],
28
+    conditions: this.getStorage().get('conditions') || {},
29
+    loadding: false,
30
+    pageConfig: {
31
+      ...(this.getStorage().get('pagination') || { current: 1, pageSize: 10 }),
32
+      total: 0,
33
+    },
34
+  }
35
+
36
+  componentDidMount () {
37
+    if (this.props.ejectRetrieve) {
38
+      this.props.ejectRetrieve(this.queryData)
39
+    }
40
+  }
41
+  
42
+  getStorage() {
43
+    return this.props.storage || this.state.defaultStorage
44
+  }
45
+
46
+  handlePageChange = (current, pageSize) => {
47
+    this.getStorage().set('pagination', { current, pageSize: 10 })
48
+
49
+    this.setState(
50
+      (state) => ({
51
+        ...state,
52
+        pageConfig: {
53
+          ...state.pageConfig,
54
+          current,
55
+          pageSize,
56
+        }
57
+      }),
58
+      this.queryData,
59
+    )
60
+  }
61
+
62
+  repondToSearch = (conditions) => {
63
+    // 查询提交会重置页码
64
+    const { pageConfig } = this.state
65
+    const newPageConfig = { ...pageConfig, current: 1, total: 0 }
66
+
67
+    this.getStorage().set('conditions', conditions)
68
+    this.getStorage().set('pagination', newPageConfig)
69
+
70
+    this.setState(
71
+      (state) => ({
72
+        ...state,
73
+        conditions,
74
+        pageConfig: newPageConfig,
75
+      }),
76
+      this.queryData,
77
+    )
78
+  }
79
+
80
+  handleSearchSubmit = (conditions) => {
81
+    console.log('------conditions-------->?', conditions)
82
+    this.repondToSearch(conditions)
83
+  }
84
+
85
+  handleSearchCancel = () => {
86
+    this.repondToSearch({})
87
+  }
88
+
89
+  queryData = () => {
90
+    if (this.props.service) {
91
+      const pageConfigProps = this.props.pagination || {}
92
+      const keyOfPageNumber = pageConfigProps.keyOfPageNumber || 'pageNumber'
93
+      const keyOfPageSize= pageConfigProps.keyOfPageSize || 'pageSize'
94
+      const keyOfTotalSize= pageConfigProps.keyOfTotalSize || 'total'
95
+
96
+      const { conditions = {}, pageConfig } = this.state
97
+
98
+      this.setState({ loadding: true })
99
+
100
+      this.props.service({
101
+        ...conditions,
102
+        ...{
103
+          [`${keyOfPageNumber}`]: pageConfig.current,
104
+          [`${keyOfPageSize}`]: pageConfig.pageSize,
105
+        },
106
+      }).then((res) => {
107
+        const [dataSource, pageParams = {}] = res || []
108
+
109
+        this.setState((state) => ({
110
+          ...state,
111
+          loadding: false,
112
+          dataSource,
113
+          pageConfig: {
114
+            ...state.pageConfig,
115
+            total: pageParams[keyOfTotalSize],
116
+          },
117
+        }))
118
+      }).catch((e) => {
119
+        this.setState({ loadding: false })
120
+        console.error(e)
121
+      })
122
+    }
123
+  }
124
+
125
+  render() {
126
+    const {
127
+      keyOfPageNumber,
128
+      keyOfPageSize,
129
+      keyOfTotalSize,
130
+      ...leftPageParams
131
+    } = this.props.pagination || {}
132
+
133
+    const pageConfig = {
134
+      ...leftPageParams,
135
+      ...this.state.pageConfig,
136
+    }
137
+
138
+    const storage = this.getStorage()
139
+
140
+    return (
141
+      <div className={Style['search-list']}>
142
+        <div className={Style['head']}>
143
+          <SearchForm storage={storage} onSubmit={this.handleSearchSubmit} onCancel={this.handleSearchCancel} configs={this.props.search} />
144
+        </div>
145
+        <div className={Style['body']}>
146
+          <BodyHeader storage={storage} title={this.props.title} actions={this.props.actions} />
147
+          <Spin spinning={this.state.loadding}>
148
+            <SearchBody storage={storage} dataSource={this.state.dataSource} configs={this.props.body} />
149
+          </Spin>
150
+          <BodyPagination storage={storage} onChange={this.handlePageChange} configs={pageConfig} />
151
+        </div>
152
+      </div>
153
+    )
154
+  }
155
+}
156
+
157
+function withSearchList (configs = {}) {
158
+  const cacheKey = `PAGE-${configs.name || 'SearchList'}`
159
+  const storage = getStorageBuilder(cacheKey)
160
+
161
+  return React.forwardRef((props, ref) => {
162
+    return <SearchList storage={storage} {...props} ref={ref} />
163
+  })
164
+}
165
+
166
+export default SearchList;
167
+export { withSearchList }

+ 46
- 0
src/layouts/SearchList/style.less 查看文件

@@ -0,0 +1,46 @@
1
+
2
+.search-list {
3
+  .head {
4
+    margin-bottom: 24px;
5
+    padding: 16px;
6
+    overflow: hidden;
7
+    background: #fff;
8
+  }
9
+
10
+  .body {
11
+    &::after {
12
+      content: '';
13
+      clear: both;
14
+      display: block;
15
+    }
16
+
17
+    .body-header {
18
+      display: flex;
19
+      align-items: center;
20
+      justify-content: space-between;
21
+      height: 64px;
22
+      padding: 0 24px;
23
+      line-height: 64px;
24
+
25
+      .body-header-title {
26
+        flex: 1 1;
27
+        color: #000;
28
+        font-size: 16px;
29
+        line-height: 24px;
30
+      }
31
+
32
+      .body-header-action {
33
+        display: flex;
34
+        align-items: center;
35
+        justify-content: flex-end;
36
+      }
37
+
38
+    }
39
+
40
+    .body-paged {
41
+      padding: 0 24px;
42
+      float: right;
43
+      margin: 16px 0;
44
+    }
45
+  }
46
+}

+ 36
- 0
src/layouts/SearchList/utils.js 查看文件

@@ -0,0 +1,36 @@
1
+
2
+export function isFunction(fn) {
3
+  return typeof fn === 'function'
4
+}
5
+
6
+export function setStorage(k, v) {
7
+  if (typeof v !== 'object') return
8
+
9
+  window.localStorage.setItem(k, window.JSON.stringify(v))
10
+}
11
+
12
+export function getStorage(k) {
13
+  const str = window.localStorage.getItem(k);
14
+  if (!str) return;
15
+
16
+  return window.JSON.parse(str);
17
+}
18
+
19
+export function getStorageBuilder(key, initData) {
20
+  if (initData) {
21
+    setStorage(key, initData)  
22
+  }
23
+
24
+  return {
25
+    set: (k, v) => {
26
+      const data = getStorage(key) || {}
27
+
28
+      setStorage(key, { ...data, [`${k}`]: v })
29
+    },
30
+
31
+    get: (k) => {
32
+      const data = getStorage(key) || {}
33
+      return data[k]
34
+    }
35
+  }
36
+}

+ 47
- 0
src/layouts/SecurityLayout.jsx 查看文件

@@ -0,0 +1,47 @@
1
+import React from 'react';
2
+import { connect } from 'dva';
3
+import { Redirect } from 'umi';
4
+import PageLoading from '@/components/PageLoading';
5
+import Swiper from '../pages/swiper/index';
6
+
7
+class SecurityLayout extends React.Component {
8
+  state = {
9
+    isReady: false,
10
+  };
11
+
12
+  componentDidMount() {
13
+    this.setState({
14
+      isReady: true,
15
+    });
16
+    const { dispatch } = this.props;
17
+
18
+    if (dispatch) {
19
+      dispatch({
20
+        type: 'user/fetchCurrent',
21
+      });
22
+    }
23
+  }
24
+
25
+  render() {
26
+    const { isReady } = this.state;
27
+    const { children, loading, currentUser } = this.props;
28
+
29
+    if ((!currentUser.userId && loading) || !isReady) {
30
+      return <PageLoading />;
31
+    }
32
+
33
+    if (!currentUser.userId) {
34
+      return <Redirect to="/user/login"></Redirect>;
35
+    }
36
+    console.log(window.location, "window.locationwindow.locationwindow.location")
37
+    return <>
38
+      {children}
39
+      {window.location.hash == "#/index" && <Swiper />}
40
+    </>;
41
+  }
42
+}
43
+
44
+export default connect(({ user, loading }) => ({
45
+  currentUser: user.currentUser,
46
+  loading: loading.models.user,
47
+}))(SecurityLayout);

+ 42
- 0
src/layouts/UserLayout.jsx 查看文件

@@ -0,0 +1,42 @@
1
+import { DefaultFooter, getMenuData, getPageTitle } from '@ant-design/pro-layout';
2
+import DocumentTitle from 'react-document-title';
3
+import Link from 'umi/link';
4
+import React from 'react';
5
+import { connect } from 'dva';
6
+import { formatMessage } from 'umi-plugin-react/locale';
7
+import logo from '../assets/logo.svg';
8
+import styles from './UserLayout.less';
9
+
10
+const UserLayout = props => {
11
+  const {
12
+    route = {
13
+      routes: [],
14
+    },
15
+  } = props;
16
+  const { routes = [] } = route;
17
+  const {
18
+    children,
19
+    location = {
20
+      pathname: '',
21
+    },
22
+  } = props;
23
+  const { breadcrumb } = getMenuData(routes);
24
+  return (
25
+    <DocumentTitle
26
+      title={getPageTitle({
27
+        pathname: location.pathname,
28
+        breadcrumb,
29
+        formatMessage,
30
+        ...props,
31
+      })}
32
+    >
33
+      <div className={styles.container}>
34
+        <div className={styles.content}>
35
+          {children}
36
+        </div>
37
+      </div>
38
+    </DocumentTitle>
39
+  );
40
+};
41
+
42
+export default connect(({ settings }) => ({ ...settings }))(UserLayout);

+ 64
- 0
src/layouts/UserLayout.less 查看文件

@@ -0,0 +1,64 @@
1
+@import '~antd/es/style/themes/default.less';
2
+
3
+.container {
4
+  display: flex;
5
+  flex-direction: column;
6
+  height: 100vh;
7
+  overflow: auto;
8
+  background: @layout-body-background;
9
+}
10
+
11
+.lang {
12
+  width: 100%;
13
+  height: 40px;
14
+  line-height: 44px;
15
+  text-align: right;
16
+  :global(.ant-dropdown-trigger) {
17
+    margin-right: 24px;
18
+  }
19
+}
20
+
21
+.content {
22
+  flex: 1;
23
+}
24
+
25
+@media (min-width: @screen-md-min) {
26
+  .container {
27
+    background-color: #fff;
28
+  }
29
+
30
+}
31
+
32
+.top {
33
+  text-align: center;
34
+}
35
+
36
+.header {
37
+  height: 44px;
38
+  line-height: 44px;
39
+  a {
40
+    text-decoration: none;
41
+  }
42
+}
43
+
44
+.logo {
45
+  height: 44px;
46
+  margin-right: 16px;
47
+  vertical-align: top;
48
+}
49
+
50
+.title {
51
+  position: relative;
52
+  top: 2px;
53
+  color: @heading-color;
54
+  font-weight: 600;
55
+  font-size: 33px;
56
+  font-family: Avenir, 'Helvetica Neue', Arial, Helvetica, sans-serif;
57
+}
58
+
59
+.desc {
60
+  margin-top: 12px;
61
+  margin-bottom: 40px;
62
+  color: @text-color-secondary;
63
+  font-size: @font-size-base;
64
+}

+ 21
- 0
src/locales/en-US.js 查看文件

@@ -0,0 +1,21 @@
1
+import component from './en-US/component';
2
+import globalHeader from './en-US/globalHeader';
3
+import menu from './en-US/menu';
4
+import pwa from './en-US/pwa';
5
+import settingDrawer from './en-US/settingDrawer';
6
+import settings from './en-US/settings';
7
+export default {
8
+  'navBar.lang': 'Languages',
9
+  'layout.user.link.help': 'Help',
10
+  'layout.user.link.privacy': 'Privacy',
11
+  'layout.user.link.terms': 'Terms',
12
+  'app.preview.down.block': 'Download this page to your local project',
13
+  'app.welcome.link.fetch-blocks': 'Get all block',
14
+  'app.welcome.link.block-list': 'Quickly build standard, pages based on `block` development',
15
+  ...globalHeader,
16
+  ...menu,
17
+  ...settingDrawer,
18
+  ...settings,
19
+  ...pwa,
20
+  ...component,
21
+};

+ 19
- 0
src/locales/pt-BR.js 查看文件

@@ -0,0 +1,19 @@
1
+import component from './pt-BR/component';
2
+import globalHeader from './pt-BR/globalHeader';
3
+import menu from './pt-BR/menu';
4
+import pwa from './pt-BR/pwa';
5
+import settingDrawer from './pt-BR/settingDrawer';
6
+import settings from './pt-BR/settings';
7
+export default {
8
+  'navBar.lang': 'Idiomas',
9
+  'layout.user.link.help': 'ajuda',
10
+  'layout.user.link.privacy': 'política de privacidade',
11
+  'layout.user.link.terms': 'termos de serviços',
12
+  'app.preview.down.block': 'Download this page to your local project',
13
+  ...globalHeader,
14
+  ...menu,
15
+  ...settingDrawer,
16
+  ...settings,
17
+  ...pwa,
18
+  ...component,
19
+};

+ 21
- 0
src/locales/zh-CN.js 查看文件

@@ -0,0 +1,21 @@
1
+import component from './zh-CN/component';
2
+import globalHeader from './zh-CN/globalHeader';
3
+import menu from './zh-CN/menu';
4
+import pwa from './zh-CN/pwa';
5
+import settingDrawer from './zh-CN/settingDrawer';
6
+import settings from './zh-CN/settings';
7
+export default {
8
+  'navBar.lang': '语言',
9
+  'layout.user.link.help': '帮助',
10
+  'layout.user.link.privacy': '隐私',
11
+  'layout.user.link.terms': '条款',
12
+  'app.preview.down.block': '下载此页面到本地项目',
13
+  'app.welcome.link.fetch-blocks': '获取全部区块',
14
+  'app.welcome.link.block-list': '基于 block 开发,快速构建标准页面',
15
+  ...globalHeader,
16
+  ...menu,
17
+  ...settingDrawer,
18
+  ...settings,
19
+  ...pwa,
20
+  ...component,
21
+};

+ 19
- 0
src/locales/zh-TW.js 查看文件

@@ -0,0 +1,19 @@
1
+import component from './zh-TW/component';
2
+import globalHeader from './zh-TW/globalHeader';
3
+import menu from './zh-TW/menu';
4
+import pwa from './zh-TW/pwa';
5
+import settingDrawer from './zh-TW/settingDrawer';
6
+import settings from './zh-TW/settings';
7
+export default {
8
+  'navBar.lang': '語言',
9
+  'layout.user.link.help': '幫助',
10
+  'layout.user.link.privacy': '隱私',
11
+  'layout.user.link.terms': '條款',
12
+  'app.preview.down.block': '下載此頁面到本地項目',
13
+  ...globalHeader,
14
+  ...menu,
15
+  ...settingDrawer,
16
+  ...settings,
17
+  ...pwa,
18
+  ...component,
19
+};