张延森 4 vuotta sitten
vanhempi
commit
100ba79545

+ 43
- 0
src/components/ActionList/index.jsx Näytä tiedosto

@@ -0,0 +1,43 @@
1
+import React from 'react';
2
+import { Menu, Dropdown, Icon } from 'antd';
3
+
4
+import './style.less';
5
+
6
+export default function withActions(actionsRender, options = {}) {
7
+  return function ActionList(text, record) {
8
+    const actions = actionsRender(text, record);
9
+    const [Act1, Act2, ...leftActs] = (actions || []).filter(Boolean);
10
+    const showMore = leftActs && leftActs.length > 0;
11
+
12
+    const act2Style = options.noMargin ? { marginLeft: 0 } : {}
13
+
14
+    const menus = (
15
+      <Menu className="actlist-menu">
16
+        {[Act2].concat(leftActs).filter(Boolean).map((Act, index) => (<Menu.Item key={`dm-${index}`}>{Act}</Menu.Item>))}
17
+      </Menu>
18
+    );
19
+
20
+    return (
21
+      <div className="action-list">
22
+        {
23
+          Act1 && <span>{Act1}</span>
24
+        }
25
+        {
26
+          !showMore && Act2 && <span style={act2Style}>{Act2}</span>
27
+        }
28
+        {
29
+          showMore ?
30
+          (
31
+            <span>
32
+              <Dropdown overlay={menus} trigger={['click']}>
33
+                <a onClick={e => e.preventDefault()}>
34
+                  <span>更多</span> <Icon type="down" />
35
+                </a>
36
+              </Dropdown>
37
+            </span>
38
+          ) : null
39
+        }
40
+      </div>
41
+    );
42
+  }
43
+}

+ 29
- 0
src/components/ActionList/style.less Näytä tiedosto

@@ -0,0 +1,29 @@
1
+
2
+:global{
3
+  .action-list {  
4
+    span {
5
+      display: inline-block;
6
+      & + span {
7
+        margin-left: 16px;
8
+      }
9
+  
10
+      a {
11
+        color: #666;
12
+  
13
+        span {
14
+          color: #FF4A4A;
15
+        }
16
+      }
17
+    }
18
+  }
19
+  
20
+  .actlist-menu {
21
+    .ant-dropdown-menu-item {
22
+      color: #666 !important;
23
+
24
+      &:hover {
25
+        background-color: rgba(0,0,0, .1);
26
+      }
27
+    }
28
+  }
29
+}

+ 23
- 0
src/components/AuthButton/index.jsx Näytä tiedosto

@@ -0,0 +1,23 @@
1
+import React from 'react';
2
+
3
+let allBtns = [];
4
+let current = [];
5
+
6
+const AuthButton = ({ children, name, noRight }) => {
7
+  const btn = allBtns.filter(x => x.code === name)[0]
8
+  if (!btn) {
9
+    return <>{children}</>
10
+  }
11
+
12
+  const hasRight = current.filter(x => x.code === name)[0]  
13
+  return hasRight ? <>{children}</> : <>{noRight}</>
14
+}
15
+
16
+const setAllBtnAuth = x => allBtns = x;
17
+const setUserBtnAuth = x => current = x;
18
+
19
+export default AuthButton;
20
+export {
21
+  setAllBtnAuth,
22
+  setUserBtnAuth,
23
+};

+ 30
- 0
src/components/OperButton/index.jsx Näytä tiedosto

@@ -0,0 +1,30 @@
1
+import { Button, Modal } from 'antd'
2
+import { useState } from 'react'
3
+
4
+const OperButton = (props) => <Button type="link" size="small" {...props} >{props.children}</Button>
5
+
6
+const ConfirmButton = (props) => {
7
+  const { onClick, color = 'red', title, content, children, ...leftProps } = props
8
+
9
+  const handleClick = () => {
10
+    Modal.confirm({
11
+      title,
12
+      content,
13
+      onOk: onClick,
14
+    })
15
+  }
16
+  
17
+  return (
18
+    <Button
19
+      type="link"
20
+      size="small"
21
+      style={{ color }}
22
+      {...leftProps}
23
+      onClick={handleClick}
24
+    >{children}</Button>
25
+  )
26
+}
27
+
28
+OperButton.Confirm = ConfirmButton
29
+
30
+export default OperButton

+ 34
- 0
src/components/Search/Field.jsx Näytä tiedosto

@@ -0,0 +1,34 @@
1
+import React from 'react'
2
+import { Input, Select, DatePicker } from 'antd'
3
+
4
+const Option = Select.Option
5
+const { RangePicker } = DatePicker
6
+
7
+export default (props) => {
8
+  const { style, type, value, options = [], placeholder, onChange } = props
9
+
10
+  const handleChange = (e) => {
11
+    const isInput = !type || type === 'text' || type === 'input'
12
+    onChange(isInput ? e.target.value : e)
13
+  }
14
+
15
+  switch (type) {
16
+    case 'datepicker':
17
+    case 'rangepicker':
18
+      return (
19
+        <RangePicker value={value} placeholder={placeholder} onChange={handleChange} />
20
+      )
21
+    case 'select':
22
+      return (
23
+        <Select style={style || {width: 120}} placeholder={placeholder} value={value} onChange={handleChange}>
24
+          {
25
+            options.map((item) => <Option key={item.value} value={item.value}>{item.label}</Option>)
26
+          }
27
+        </Select>
28
+      )
29
+    case 'text':
30
+    case 'input':
31
+    default:
32
+      return <Input style={style} value={value} placeholder={placeholder} onChange={handleChange} />
33
+  }
34
+}

+ 53
- 0
src/components/Search/SearchForm.jsx Näytä tiedosto

@@ -0,0 +1,53 @@
1
+import React, { useRef, useState } from 'react'
2
+import { Form, Input, Button, Radio } from 'antd'
3
+import SearchItem from './SearchItem'
4
+import { defaultLayout, getFormDataFrom } from './utils'
5
+
6
+export default (props) => {
7
+  const { fields = [], extraParams = {}, onSearch, onReset } = props
8
+  const initValue = useRef(getFormDataFrom(fields))
9
+  const [formData, setFormData] = useState(initValue.current)
10
+
11
+  const handleChange = (data) => {
12
+    setFormData({
13
+      ...formData,
14
+      ...data,
15
+    })
16
+  }
17
+
18
+  const handleSubmit = () => {
19
+    onSearch({
20
+      ...formData,
21
+      ...extraParams,
22
+    })
23
+  }
24
+
25
+  const handleReset = () => {
26
+    setFormData(initValue.current)
27
+    onReset({
28
+      ...initValue.current,
29
+      ...extraParams,
30
+    })
31
+  }
32
+
33
+  return (
34
+    <Form layout="inline">
35
+      {
36
+        fields.map((field) => (
37
+          <SearchItem
38
+            key={field.name}
39
+            config={field}
40
+            label={field.label}
41
+            name={field.name}
42
+            value={formData[field.name]}
43
+            onChange={handleChange}
44
+          />
45
+        ))
46
+      }
47
+      <Form.Item>
48
+        <Button type="primary" onClick={handleSubmit}>搜索</Button>
49
+        <Button onClick={handleReset} style={{marginLeft: '1em'}}>重置</Button>
50
+      </Form.Item>
51
+    </Form>
52
+  )
53
+}

+ 26
- 0
src/components/Search/SearchItem.jsx Näytä tiedosto

@@ -0,0 +1,26 @@
1
+import React, { useCallback } from 'react'
2
+import { Form } from 'antd'
3
+import Field from './Field'
4
+
5
+export default (props) => {
6
+  const { label, name, value, config } = props
7
+
8
+  const fieldProps = {
9
+    value,
10
+    ...config,
11
+  }
12
+
13
+  const handleChange = useCallback((data) => {
14
+    props.onChange({[name]: data})
15
+  }, [name, props.onChange])
16
+
17
+  return (
18
+    <Form.Item label={label}>
19
+      <Field
20
+        {...fieldProps}
21
+        value={value}
22
+        onChange={handleChange}
23
+      />
24
+    </Form.Item>
25
+  )
26
+}

+ 19
- 0
src/components/Search/index.jsx Näytä tiedosto

@@ -0,0 +1,19 @@
1
+import SearchForm from './SearchForm'
2
+
3
+export { SearchForm }
4
+
5
+export default (props) => {
6
+  const style = {
7
+    boxSizing: 'border-box',
8
+    background: '#fff',
9
+    boxShadow: '0 1px 4px rgba(0, 21, 41, 0.08)',
10
+    padding: '1em',
11
+    marginBottom: '1.6em',
12
+  }
13
+
14
+  return (
15
+    <div style={style}>
16
+      <SearchForm {...props} />
17
+    </div>
18
+  )
19
+}

+ 10
- 0
src/components/Search/utils.js Näytä tiedosto

@@ -0,0 +1,10 @@
1
+
2
+export const getFormDataFrom = (fields = []) => {
3
+  return fields.reduce((data, field) => {
4
+    const { name, value } = field
5
+    return {
6
+      ...data,
7
+      [name]: value
8
+    }
9
+  }, [])
10
+}

+ 40
- 0
src/components/TableList/Pagination.jsx Näytä tiedosto

@@ -0,0 +1,40 @@
1
+import React from 'react'
2
+import { Pagination } from 'antd'
3
+import classNames from 'classnames'
4
+import Styles from './style.less'
5
+
6
+export default (props) => {
7
+  const { current, pageSize, total, onChange } = props
8
+
9
+  const style = {
10
+    marginTop: '1em',
11
+    background: 'transparent',
12
+  }
13
+
14
+  const handleChange = (current) => {
15
+    onChange(current)
16
+  }
17
+
18
+  const handleShowSizeChange = (current, pageSize) => {
19
+    onChange(current, pageSize)
20
+  }
21
+
22
+  if (!total || total < 1) {
23
+    return null
24
+  }
25
+
26
+  return (
27
+    <div className={classNames(Styles['flex-right'])} style={style}>
28
+      <div className={Styles['flex-right-item']}>
29
+        <Pagination
30
+          showSizeChanger
31
+          current={current}
32
+          pageSize={pageSize}
33
+          total={total}
34
+          onChange={handleChange}
35
+          onShowSizeChange={handleShowSizeChange}
36
+        />
37
+      </div>
38
+    </div>
39
+  )
40
+}

+ 31
- 0
src/components/TableList/TableList.jsx Näytä tiedosto

@@ -0,0 +1,31 @@
1
+import React from 'react'
2
+import { Table } from 'antd'
3
+import Styles from './style.less'
4
+
5
+const Actions = (props) => {
6
+  const style = {
7
+    marginBottom: '1em'
8
+  }
9
+
10
+  return (
11
+    <div className={Styles['flex-right']} style={style}>
12
+      <div className={Styles['flex-right-item']}>{props.children}</div>
13
+    </div>
14
+  )
15
+}
16
+
17
+export default (props) => {
18
+  const { actions, ...leftProps } = props
19
+
20
+  const tableProps = {
21
+    ...leftProps,
22
+    pagination: false,
23
+  }
24
+  
25
+  return (
26
+    <div className={Styles['box-wrapper']}>
27
+      {actions && <Actions>{actions}</Actions>}
28
+      <Table {...tableProps} />
29
+    </div>
30
+  )
31
+}

+ 52
- 0
src/components/TableList/index.jsx Näytä tiedosto

@@ -0,0 +1,52 @@
1
+import { useCallback, useState, useEffect } from 'react';
2
+import { stringify } from 'querystring';
3
+import { notification } from 'antd';
4
+import request from '@/utils/request'
5
+import TableList from './TableList';
6
+import Pagination from './Pagination';
7
+
8
+export default (props) => {
9
+  const [loading, setLoading] = useState(false)
10
+  const [pageConfig, setPageConfig] = useState({ current: 1, pageSize: 10, total: 0 })
11
+  const [list, setList] = useState()
12
+  const { api, params = {}, ...tableProps } = props
13
+  const paramsStr = stringify({
14
+    ...params,
15
+    pageNum: pageConfig.current,
16
+    pageSize: pageConfig.pageSize,
17
+  })
18
+
19
+  const updatePageConfig = useCallback((config) => {
20
+    setPageConfig({
21
+      ...pageConfig,
22
+      ...config
23
+    })
24
+  }, [pageConfig])
25
+
26
+  const handlePageChange = (current, pageSize) => {
27
+    const config = pageSize ? { current, pageSize } : { current }
28
+    updatePageConfig(config)
29
+  }
30
+
31
+  useEffect(() => {
32
+    const url = `${api.url}?${paramsStr}`
33
+    setLoading(true)
34
+    request({...api, url}).then((res) => {
35
+      const {current, total, records = []} = res
36
+      setLoading(false)
37
+      setList(records)
38
+      updatePageConfig({current, total})
39
+    }).catch((err) => {
40
+      console.error(err)
41
+      setLoading(false)
42
+      notification.warn({ message: err.message })
43
+    })
44
+  }, [paramsStr, api])
45
+
46
+  return (
47
+    <>
48
+      <TableList loading={loading} dataSource={list} {...tableProps} />
49
+      <Pagination {...pageConfig} onChange={handlePageChange} />
50
+    </>
51
+  )
52
+};

+ 15
- 0
src/components/TableList/style.less Näytä tiedosto

@@ -0,0 +1,15 @@
1
+
2
+.box-wrapper {
3
+  box-sizing: border-box;
4
+  background: #fff;
5
+  box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
6
+}
7
+
8
+.flex-right {
9
+  display: flex;
10
+  flex-direction: row-reverse;
11
+
12
+  &-item {
13
+    flex: none;
14
+  }
15
+}

+ 3
- 3
src/models/user.js Näytä tiedosto

@@ -1,6 +1,6 @@
1 1
 import { fetch, apis } from '@/utils/request';
2 2
 import { setAuthority } from '@/utils/authority'
3
-// import { setAllBtnAuth, setUserBtnAuth } from '@/components/AuthButton';
3
+import { setAllBtnAuth, setUserBtnAuth } from '@/components/AuthButton';
4 4
 
5 5
 const getCurrentUser = fetch(apis.user.current)
6 6
 
@@ -26,8 +26,8 @@ const UserModel = {
26 26
       const { taUser = {} , menuList = [], buttonList = [] } = payload || {}
27 27
       const currentUser = { ...taUser, roles: (taUser.roles || []).map(x => x.roleId) }
28 28
 
29
-      // setAllBtnAuth(buttonList)
30
-      // setUserBtnAuth(currentUser.buttons)
29
+      setAllBtnAuth(buttonList)
30
+      setUserBtnAuth(currentUser.buttons)
31 31
 
32 32
       setAuthority(currentUser.roles)
33 33
 

+ 48
- 0
src/pages/building/List/components/Search.jsx Näytä tiedosto

@@ -0,0 +1,48 @@
1
+import React from 'react'
2
+import Search from '@/components/Search'
3
+
4
+export default (props) => {
5
+  const searchFields = [
6
+    {
7
+      name: 'name',
8
+      label: '楼盘名称',
9
+      placeholder: '请输入楼盘名称',
10
+    },
11
+    {
12
+      name: 'buildingStatus',
13
+      label: '楼盘状态',
14
+      placeholder: '请选择楼盘状态',
15
+      type: 'select',
16
+      options: [
17
+        {label: '已发布', value: 1},
18
+        {label: '未发布', value: 2}
19
+      ]
20
+    },
21
+    {
22
+      name: 'buildingStatus',
23
+      label: '销售状态',
24
+      placeholder: '请选择销售状态',
25
+      type: 'select',
26
+      options: [
27
+        {label: '待售', value: '待售'},
28
+        {label: '在售', value: '在售'},
29
+        {label: '售罄', value: '售罄'},
30
+        {label: '在租', value: '在租'},
31
+      ]
32
+    },
33
+    {
34
+      name: 'isMain',
35
+      label: '首页推荐',
36
+      placeholder: '请选择首页推荐',
37
+      type: 'select',
38
+      options: [
39
+        {label: '推荐', value: 1},
40
+        {label: '未推荐', value: 2},
41
+      ]
42
+    },
43
+  ]
44
+
45
+  return (
46
+    <Search fields={searchFields} onSearch={props.onSearch} onReset={props.onSearch} />
47
+  )
48
+}

+ 100
- 0
src/pages/building/List/components/Table.jsx Näytä tiedosto

@@ -0,0 +1,100 @@
1
+import React, { useState } from 'react'
2
+import { router } from 'umi'
3
+import { Button, Badge } from 'antd'
4
+import moment from 'moment'
5
+import TableList from '@/components/TableList'
6
+import OperButton from '@/components/OperButton'
7
+import AuthButton from '@/components/AuthButton'
8
+import withActions from '@/components/ActionList'
9
+
10
+export default (props) => {
11
+  const [page, setPage] = useState({current: 1, pageSize: 10})
12
+  const { api, params, onDelete, onPublish } = props
13
+
14
+  const columns = [
15
+    {
16
+      title: '编号',
17
+      key: '#',
18
+      align: 'center',
19
+      render: (t, r, index) => (page.current - 1) * page.pageSize + index + 1,
20
+    },
21
+    {
22
+      title: '封面',
23
+      dataIndex: 'buildingListImg',
24
+      key: 'buildingListImg',
25
+      align: 'center',
26
+      render: (_, row) => {
27
+        if (row.buildingListImg && row.buildingListImg[0]) {
28
+          return <img src={row.buildingListImg[0].url} width={128} height={72} style={{borderRadius: '4px'}} alt="" />
29
+        }
30
+        return null
31
+      }
32
+    },
33
+    {
34
+      title: '楼盘名称',
35
+      dataIndex: 'buildingName',
36
+      key: 'buildingName',
37
+      align: 'center',
38
+      render: (t, row) => (
39
+        <AuthButton name="admin.building.update" noRight={t}>
40
+          <Button
41
+            type="link"
42
+            onClick={() => router.push({
43
+              pathname: '/building/list/add',
44
+              query: {
45
+                id: row.buildingId,
46
+              },
47
+            })}
48
+          >{t}</Button>
49
+        </AuthButton>
50
+      )
51
+    },
52
+    {
53
+      title: '销售状态',
54
+      dataIndex: 'marketStatus',
55
+      key: 'marketStatus',
56
+      align: 'center',
57
+    },
58
+    {
59
+      title: '录入时间',
60
+      dataIndex: 'createDate',
61
+      key: 'createDate',
62
+      align: 'center',
63
+      render: (t) => moment(t).format('YYYY-MM-DD HH:mm')
64
+    },
65
+    {
66
+      title: '状态',
67
+      dataIndex: 'status',
68
+      key: 'status',
69
+      align: 'center',
70
+      render: (t) => t === 1 ? <Badge status="success" text="已发布" /> : <Badge status="processing" text="未发布" />
71
+    },
72
+    {
73
+      title: '操作',
74
+      key: 'options',
75
+      align: 'center',
76
+      render: withActions((_, row) => [
77
+        <AuthButton name="admin.building.public" noRight={null}>
78
+          <OperButton onClick={() => onPublish(row)}>{row.status === 1 ? '取消发布' : '发布'}</OperButton>
79
+        </AuthButton>,
80
+        <AuthButton name="admin.building.delete" noRight={null}>
81
+          <OperButton.Confirm
82
+            title="确认删除?"
83
+            content="删除之后不可恢复"
84
+            onClick={() => onDelete(row)}
85
+          >删除</OperButton.Confirm>
86
+        </AuthButton>
87
+      ], {noMargin: true})
88
+    },
89
+  ]
90
+
91
+  return (
92
+    <TableList
93
+      api={api}
94
+      params={params}
95
+      rowKey="buildingId"
96
+      columns={columns}
97
+      onPageChange={(pg) => setPage(pg)}
98
+    />
99
+  )
100
+}

+ 23
- 2
src/pages/building/List/index.jsx Näytä tiedosto

@@ -1,7 +1,28 @@
1
-import React from 'react'
1
+import React, { useState } from 'react'
2
+import apis from '@/services/apis'
3
+import Search from './components/Search'
4
+import Table from './components/Table'
2 5
 
3 6
 export default (props) => {
7
+  const [searchData, setSearchData] = useState({})
8
+
9
+  const handleDelete = (row) => {
10
+    //
11
+  }
12
+
13
+  const handlePublish = (row) => {
14
+
15
+  }
16
+
4 17
   return (
5
-    <div></div>
18
+    <div>
19
+      <Search onSearch={(data) => setSearchData(data)} />
20
+      <Table
21
+        api={apis.building.getList}
22
+        params={searchData}
23
+        onDelete={handleDelete}
24
+        onPublish={handlePublish}
25
+      />
26
+    </div>
6 27
   )
7 28
 }