张延森 4 年 前
コミット
0c0f3e3f1c

+ 1
- 1
package.json ファイルの表示

@@ -72,7 +72,7 @@
72 72
     "umi-plugin-pro-block": "^1.3.6",
73 73
     "umi-plugin-react": "^1.15.8",
74 74
     "umi-request": "^1.0.8",
75
-    "wangeditor": "^3.1.1"
75
+    "wangeditor": "^4.6.2"
76 76
   },
77 77
   "devDependencies": {
78 78
     "@ant-design/colors": "^3.1.0",

+ 44
- 0
src/components/Wangedit/Preview.jsx ファイルの表示

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

+ 27
- 0
src/components/Wangedit/PreviewMenu.js ファイルの表示

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

+ 25
- 12
src/components/Wangedit/Wangedit.jsx ファイルの表示

@@ -1,5 +1,7 @@
1 1
 import React from 'react';
2 2
 import E from 'wangeditor';
3
+import PreviewMenu from './PreviewMenu'
4
+import Preview from './Preview'
3 5
 import { fetch, apis } from '../../utils/request';
4 6
 
5 7
 /**
@@ -10,6 +12,7 @@ class Wangedit extends React.Component {
10 12
   constructor(props, context) {
11 13
     super(props, context);
12 14
     this.state = {
15
+      preview: false,
13 16
       html: undefined,
14 17
       contenteditable: props.contenteditable == false ? false : true
15 18
     }
@@ -18,8 +21,10 @@ class Wangedit extends React.Component {
18 21
 
19 22
   render() {
20 23
     return (
21
-      <div ref="editorElem" style={{ textAlign: 'left' }}>
22
-      </div>
24
+      <>
25
+        <div ref="editorElem" style={{ textAlign: 'left' }} />
26
+        <Preview width={426} style={{width: '426px', height: '863px', margin: 0, padding: 0}} visible={this.state.preview} html={this.state.html} onCancel={() => this.setState({preview: false})} />
27
+      </>
23 28
     );
24 29
   }
25 30
 
@@ -27,24 +32,31 @@ class Wangedit extends React.Component {
27 32
     const elem = this.refs.editorElem
28 33
     this.editor = new E(elem)
29 34
     // 使用 onchange 函数监听内容的变化
30
-    this.editor.customConfig.onchange = html => {
35
+    this.editor.config.onchange = html => {
31 36
       this.setState({ html })
32 37
 
33 38
       if (typeof this.props.onChange === 'function') {
34 39
         this.props.onChange(html)
35 40
       }
36 41
     }
37
-    this.editor.customConfig.zIndex = 100
38
-    this.editor.customConfig.uploadImgMaxLength = 1
39
-    this.editor.customConfig.customUploadImg = function (files, insert) {
42
+    this.editor.config.zIndex = 100
43
+    this.editor.config.uploadImgMaxLength = 1
44
+    this.editor.config.customUploadImg = function (files, insert) {
40 45
       if (!files.length) return
41 46
       
42 47
       const data = new FormData()
43 48
       data.append('file', files[0])
44 49
 
45 50
       fetch(apis.image.upload)({data}).then(insert)
51
+    }    
52
+
53
+    // 扩展按钮
54
+    this.editor.menus.extend('previewMenu', PreviewMenu)
55
+    PreviewMenu.preview = (html) => {
56
+      this.setState({preview: true})
46 57
     }
47
-    this.editor.customConfig.menus = [
58
+
59
+    this.editor.config.menus = [
48 60
       'head',  // 标题
49 61
       'bold',  // 粗体
50 62
       'fontSize',  // 字号
@@ -59,12 +71,13 @@ class Wangedit extends React.Component {
59 71
       'quote',  // 引用
60 72
       'image',  // 插入图片
61 73
       'undo',  // 撤销
62
-      'redo'  // 重复
74
+      'redo',  // 重复
75
+      'previewMenu'
63 76
     ]
64 77
     
65 78
     // 过滤 word 字符
66
-    this.editor.customConfig.pasteFilterStyle = false
67
-    this.editor.customConfig.pasteTextHandle = function(content) {
79
+    this.editor.config.pasteFilterStyle = false
80
+    this.editor.config.pasteTextHandle = function(content) {
68 81
       const regs = [
69 82
         /<!--\[if [\s\S]*?endif\]-->/ig,
70 83
         /<[a-zA-Z0-9]+\:[^>]+>[^>]*<\/[a-zA-Z0-9]+\:[^>]+>/ig,
@@ -80,7 +93,7 @@ class Wangedit extends React.Component {
80 93
 
81 94
     this.editor.create()
82 95
     this.editor.$textElem.attr('contenteditable',this.state.contenteditable);
83
-    this.editor.customConfig.uploadImgShowBase64 = true
96
+    this.editor.config.uploadImgShowBase64 = true
84 97
     this.editor.txt.html(this.props.value)
85 98
   }
86 99
 
@@ -101,7 +114,7 @@ class Wangedit extends React.Component {
101 114
    * @memberof Wangedit
102 115
    */
103 116
   shouldComponentUpdate(nextProps) {
104
-    return nextProps.value !== this.editor.txt.html()
117
+    return nextProps.value !== this.editor.txt.html() || nextProps.preview !== this.state.preview
105 118
   }
106 119
 }
107 120
 

+ 9
- 0
src/global.less ファイルの表示

@@ -196,6 +196,15 @@ ol {
196 196
     border-top:none;
197 197
   }
198 198
 }
199
+
200
+.editor-preivew-modal-content {
201
+  .ant-modal-content {
202
+    background: transparent !important;
203
+    box-shadow: none;
204
+  }
205
+}
206
+
207
+
199 208
 :global {
200 209
   .ant-layout {
201 210
     min-height: 100vh;

+ 9
- 1
src/pages/activity/SignList.jsx ファイルの表示

@@ -68,6 +68,8 @@ const columns = [
68 68
 
69 69
 const header = props => {
70 70
   const [data, setData] = useState({ list: {} })
71
+  const [actInfo, setActInfo] = useState()
72
+
71 73
   //   const [page, changePage] = useState({})
72 74
   // 存入导入数据的值
73 75
   const { getFieldDecorator, getFieldsValue } = props.form
@@ -75,6 +77,11 @@ const header = props => {
75 77
   useEffect(() => {
76 78
     // eslint-disable-next-line no-use-before-define
77 79
     getSignList({ pageNum: 1, pageSize: 10, dynamicId: props.location.query.dynamicId });
80
+    //
81
+    
82
+    request({ ...apis.activity.details, params: { dynamicId: props.location.query.dynamicId } }).then(res => {
83
+      setActInfo(res)
84
+    })
78 85
   }, [])
79 86
 
80 87
   // 查询列表
@@ -117,11 +124,12 @@ const header = props => {
117 124
         if (!data) {
118 125
           return
119 126
         }
127
+
120 128
         const url = window.URL.createObjectURL(new Blob([data]))
121 129
         const link = document.createElement('a')
122 130
         link.style.display = 'none'
123 131
         link.href = url
124
-        link.setAttribute('download', '报名列表.xlsx')
132
+        link.setAttribute('download', `${actInfo.title || {}}-报名列表.xlsx`)
125 133
         document.body.append(link)
126 134
         link.click()
127 135
       }).catch(() => {

+ 14
- 1
src/pages/integralMall/exchangeRecords.jsx ファイルの表示

@@ -8,6 +8,7 @@ import BuildSelect from '../../components/SelectButton/BuildSelect'
8 8
 import apis from '../../services/apis';
9 9
 import request from '../../utils/request'
10 10
 import AuthButton from '@/components/AuthButton';
11
+import { exportExcel } from '@/utils/utils'
11 12
 
12 13
 /**
13 14
   @param {*} props
@@ -21,6 +22,7 @@ function record(props) {
21 22
 
22 23
   // 获取初始化数据
23 24
   const [data, setData] = useState({})
25
+  const [queryParam, setQueryParam] = useState()
24 26
 
25 27
   useEffect(() => {
26 28
     getList({ pageNum: 1, pageSize: 10 });
@@ -65,12 +67,20 @@ function record(props) {
65 67
           submitValue.endVerifyDate = null
66 68
         }
67 69
 
70
+        setQueryParam(submitValue)
71
+
68 72
         console.log(submitValue)
69 73
         getList({ pageNum: 1, pageSize: 10, ...submitValue })
70 74
       }
71 75
     });
72 76
   }
73 77
 
78
+  const handleExport = () => {
79
+    request({ ...apis.integralMall.taPointsExchangeExport, params: queryParam, }).then((data) => {
80
+      exportExcel(data, '积分兑换记录')
81
+    })
82
+  }
83
+
74 84
   const changePageNum = (pageNumber) => {
75 85
     props.form.validateFields((err, values) => {
76 86
       if (!err) {
@@ -92,7 +102,7 @@ function record(props) {
92 102
       dataIndex: 'personType',
93 103
       key: 'personType',
94 104
       align: 'center',
95
-      render: (personType) => <><span>{personType === 'Realty Consultant' ? '置业顾问' : personType === 'Sales Executive' ? '销售主管' : personType === 'estate agent' ? '经纪人' : personType === 'customer' ? '客户' : ''}</span></>
105
+      render: (personType) => <><span>{personType === 'prop' ? '物业相关' : personType === 'life-consultant' ? '生活管家' : ''}</span></>
96 106
     },
97 107
     {
98 108
       title: '手机号',
@@ -210,6 +220,9 @@ function record(props) {
210 220
         </div>
211 221
 
212 222
       </Form>
223
+      <div style={{marginTop: 24}}>
224
+        <Button onClick={handleExport}>导出</Button>
225
+      </div>
213 226
       <Table rowKey="exchangeRecords" style={{ marginTop: '40px' }} dataSource={data.records} columns={columns} pagination={false} />
214 227
       <div style={{ display: 'flex', justifyContent: 'flex-end', marginTop: '30px' }}>
215 228
         <Pagination showQuickJumper defaultCurrent={1} total={data.total} onChange={changePageNum} current={data.current} />

+ 1
- 0
src/pages/property/bill/info/index.jsx ファイルの表示

@@ -13,6 +13,7 @@ const BillStatusDict = [
13 13
   { value: '0', label: '未缴费' },
14 14
   { value: '1', label: '已线上缴费' },
15 15
   { value: '2', label: '已线下缴费' },
16
+  { value: '3', label: '支付中' },
16 17
 ]
17 18
 
18 19
 const PayTypeDict = [

+ 5
- 0
src/services/apis.js ファイルの表示

@@ -727,6 +727,11 @@ export default {
727 727
       method: 'GET',
728 728
       action: 'admin.taPointsExchange.get',
729 729
     },
730
+    taPointsExchangeExport: {
731
+      url: `${prefix}/taPointsExchange/export`,
732
+      method: 'GET',
733
+      action: 'admin.taPointsExchange.get',
734
+    },
730 735
     getTaGoods: {
731 736
       url: `${prefix}/taGoods`,
732 737
       method: 'GET',

+ 11
- 0
src/utils/utils.js ファイルの表示

@@ -25,3 +25,14 @@ export const getPageQuery = () => parse(window.location.href.split('?')[1]);
25 25
 export function ObjSpEqual(a = {}, b = {}) {
26 26
   return JSON.stringify(a) === JSON.stringify(b)
27 27
 }
28
+
29
+
30
+export function exportExcel(data, title) {  
31
+  const url = window.URL.createObjectURL(new Blob([data]))
32
+  const link = document.createElement('a')
33
+  link.style.display = 'none'
34
+  link.href = url
35
+  link.setAttribute('download', `${title}.xlsx`)
36
+  document.body.append(link)
37
+  link.click()
38
+}