Quellcode durchsuchen

统计概览> + 账号资源

sophieChenyx vor 6 Jahren
Ursprung
Commit
84b205c805

+ 1 - 2
.gitignore

@@ -13,11 +13,10 @@ _roadhog-api-doc
 .DS_Store
 npm-debug.log*
 yarn-error.log
-
+jsconfig.json
 /coverage
 .idea
 yarn.lock
 package-lock.json
 *bak
-jsconfig.json
 .prettierrc

+ 1 - 1
package.json

@@ -4,7 +4,7 @@
   "description": "Enterprise applications - Content Manage System",
   "private": true,
   "scripts": {
-    "precommit": "npm run lint-staged",
+    "_precommit": "npm run lint-staged",
     "start": "cross-env ESLINT=none roadhog dev",
     "start:no-proxy": "cross-env NO_PROXY=true ESLINT=none roadhog dev",
     "build": "cross-env NODE_ENV=production ESLINT=none roadhog build",

+ 4 - 0
src/common/menu.js

@@ -15,6 +15,10 @@ const menuData = () => {
       name: '销售详情',
       path: 'sold',
       icon: <AXIcon type="action" />,
+    }, {
+      name: '账号资源',
+      path: 'accounts',
+      icon: 'database',
     }],
     authority: ['admin', 'platform'],
   }, {

+ 21 - 0
src/common/router.js

@@ -387,6 +387,27 @@ export const getRouterData = (app) => {
     '/dashboard/sold': {
       component: dynamicWrapper(app, ['trade'], () => import('../routes/Dashboard/SnapshotList')),
     },
+
+    // 统计概览 贝尔安亲 需求增加
+    '/dashboard/accounts': {
+      component: dynamicWrapper(app, ['trade'], () => import('../routes/Dashboard/Accounts')),
+    },
+    // 总统计表
+    '/dashboard/accounts/totalList': {
+      component: dynamicWrapper(app, ['terminal', 'campus', 'merchant'], () => import('../routes/Dashboard/Accounts/AccountsTotalList')),
+    },
+    // 校区列表
+    '/dashboard/accounts/campus': {
+      component: dynamicWrapper(app, ['accounts'], () => import('../routes/Dashboard/Accounts/AccountsCampus')),
+    },
+    // 终端用户
+    '/dashboard/accounts/terminals': {
+      component: dynamicWrapper(app, ['accounts'], () => import('../routes/Dashboard/Accounts/AccountsTerminals')),
+    },
+    // 即将逾期
+    '/dashboard/accounts/overdue': {
+      component: dynamicWrapper(app, [], () => import('../routes/Dashboard/Accounts/AccountsOverdue')),
+    },
     // 异常相关路由注册
     '/exception/403': {
       component: dynamicWrapper(app, [], () => import('../routes/Exception/403')),

+ 10 - 1
src/components/AXList/StandardTableList.js

@@ -68,7 +68,7 @@ export default class StandardTableList extends PureComponent {
   getListHeader = () => {
     const {
       showStatusSelect,
-      header: { basicSearch, onAdvanceFilterClick, onCreateClick },
+      header: { basicSearch, onAdvanceFilterClick, onCreateClick, onDownload },
       footer: { pagination },
     } = this.props;
     const { keys } = basicSearch;
@@ -131,6 +131,15 @@ export default class StandardTableList extends PureComponent {
                 >新建
                 </Button>
               )}
+              {onDownload !== undefined && (
+                <Button
+                  icon="download"
+                  type="primary"
+                  style={{ marginLeft: 5 }}
+                  onClick={onDownload}
+                >下载
+                </Button>
+              )}
             </div>
           </div>
         </div>

+ 66 - 0
src/models/accounts.js

@@ -0,0 +1,66 @@
+import {
+  queryCampusList,
+  downloadCampusExcel,
+  queryTerminalsList,
+  downloadTerminalsExcel,
+} from '../services/accounts';
+
+export default {
+  namespace: 'accounts',
+
+  state: {
+    list: [],
+    pageNo: 1,
+    pageSize: 15,
+    totalSize: 0,
+    currentItem: {},
+  },
+
+  effects: {
+    *fetchCampusList({ payload }, { call, put }) {
+      const response = yield call(queryCampusList, payload);
+      if (response.success) {
+        yield put({
+          type: 'querySuccess',
+          payload: {
+            list: response.data.list || [],
+            pageSize: response.data.pageSize,
+            totalSize: response.data.totalSize,
+            pageNo: response.data.pageNo,
+          },
+        });
+      }
+    },
+    *fetchCampusExcel({ payload }, { call }) {
+      yield call(downloadCampusExcel, payload);
+    },
+    *fetchTerminalsList({ payload }, { call, put }) {
+      console.log('fetchTerminalsList');
+      const response = yield call(queryTerminalsList, payload);
+      if (response.success) {
+        yield put({
+          type: 'querySuccess',
+          payload: {
+            list: response.data.list || [],
+            pageSize: response.data.pageSize,
+            totalSize: response.data.totalSize,
+            pageNo: response.data.pageNo,
+          },
+        });
+      }
+    },
+    *fetchTerminalsExcel({ payload }, { call }) {
+      yield call(downloadTerminalsExcel, payload);
+    },
+  },
+
+  reducers: {
+    querySuccess(state, action) {
+      return {
+        ...state,
+        ...action.payload,
+      };
+    },
+  },
+
+};

+ 148 - 0
src/routes/Dashboard/Accounts/AccountsCampus.js

@@ -0,0 +1,148 @@
+import React, { Component } from 'react';
+import moment from 'moment';
+import { connect } from 'dva';
+import { Card,message } from 'antd';
+import { StandardTableList } from '../../../components/AXList';
+import { addRowKey } from '../../../utils/utils';
+const Message = message;
+
+@connect(({ loading, accounts }) => ({
+  accounts,
+  loading: loading.models.terminal,
+}))
+
+export default class CampusAccountsPage extends Component {
+  constructor(props) {
+    super(props);
+    const { state } = props.location;
+    this.state = {
+      UIParams: (state || {}).UIParams, // 组件的状态参数
+      Queryers: (state || {}).Queryers, // 查询的条件参数
+    };
+  }
+
+  componentWillMount() {
+    this.props.dispatch({
+      type: 'accounts/fetchCampusList',
+      payload: { ...this.state.Queryers }
+    })
+  }
+  handleDownloadOperation = () => {
+    console.log('>>>>>press AccountsCampus download')
+    this.props.dispatch({
+      type: 'accounts/fetchCampusExcel'
+    })
+  };
+  handleFilterOperation = (params, states) => {
+    this.setState({
+      UIParams: states,
+      Queryers: params,
+    });
+    this.props.dispatch({
+      type: 'accounts/fetchCampusList',
+      payload: {
+        ...params,
+      },
+    });
+  };
+
+  handleBatchOperation = () => {
+    Message.info('暂不支持批量操作!');
+  };
+  render() {
+    const { loading,accounts } = this.props;
+    const { list, totalSize, pageSize, pageNo } = accounts;
+    const batchActions = [{
+      key: 'delete',
+      name: '批量禁用',
+    }, {
+      key: 'recovery',
+      name: '批量解禁',
+    }, {
+      key: 'unbound',
+      name: '批量解绑',
+    }];
+
+    const basicSearch = {
+      keys: [{
+        name: '校区编号',
+        field: 'code',
+      }, {
+        name: '校区名称',
+        field: 'name',
+      }],
+    };
+
+
+    const pagination = {
+      pageNo,
+      pageSize,
+      totalSize,
+    };
+
+    const columns = [{
+      title: '校区编号',
+      key: 1,
+      dataIndex: 'code',
+      width: '10%',
+    }, {
+      title: '校区名称',
+      key: 2,
+      dataIndex: 'name',
+      width: '23%',
+    }, {
+      title: '校区类型',
+      key: 3,
+      dataIndex: 'merchantName',
+      width: '10%',
+    }, {
+      title: '所属省(直辖市)',
+      key: 4,
+      dataIndex: 'provinceName',
+      width: '12%',
+    }, {
+      title: '所属市(区)/县',
+      key: 5,
+      dataIndex: 'cityName',
+      width: '12%',
+    }, {
+      title: '联系人',
+      key: 6,
+      dataIndex: 'contactName',
+      width: '7%',
+    }, {
+      title: '联系电话',
+      key: 7,
+      dataIndex: 'mobile',
+      width: '10%',
+    }, {
+      title: '更新时间',
+      key: 8,
+      dataIndex: 'gmtModified',
+      render: text => moment(text).format('YYYY-MM-DD HH:mm:ss'),
+      width: '15%',
+    }];
+
+    return (
+      <Card>
+        <StandardTableList
+          columns={columns}
+          loading={loading}
+          dataSource={addRowKey(list)}
+          header={{
+            basicSearch,
+            onFilterClick: this.handleFilterOperation,
+            onDownload: this.handleDownloadOperation,
+          }}
+          footer={{
+            pagination,
+            batchActions,
+            onBatchClick: this.handleBatchOperation,
+          }}
+          keepUIState={{ ...this.state.UIParams }}
+          showStatusSelect={false}
+        />
+      </Card>
+    );
+  }
+}

+ 21 - 0
src/routes/Dashboard/Accounts/AccountsOverdue.js

@@ -0,0 +1,21 @@
+import React, { Component } from 'react';
+import moment from 'moment';
+import { Card, Modal, Button, message } from 'antd';
+const Message = message;
+
+export default class OverdueAccountsPage extends Component {
+  constructor(props) {
+    super(props);
+  }
+  componentDidMount() {
+    
+  }
+
+  render() {
+    return (
+      <Card>
+        OverdueAccountsPage
+      </Card>
+    );
+  }
+}

+ 159 - 0
src/routes/Dashboard/Accounts/AccountsTerminals.js

@@ -0,0 +1,159 @@
+import React, { Component } from 'react';
+import moment from 'moment';
+import { connect } from 'dva';
+import { Card, message, Badge } from 'antd';
+import { StandardTableList } from '../../../components/AXList';
+import { addRowKey } from '../../../utils/utils';
+import styles from './AccountsTerminals.less';
+const Message = message;
+const timestamp2Str = ts => moment(ts).format('YYYY-MM-DD HH:mm:ss');
+
+@connect(({ loading, accounts }) => ({
+  accounts,
+  loading: loading.models.terminal,
+}))
+
+export default class TerminalsAccountsPage extends Component {
+  constructor(props) {
+    super(props);
+    const { state } = props.location;
+    this.state = {
+      UIParams: (state || {}).UIParams, // 组件的状态参数
+      Queryers: (state || {}).Queryers, // 查询的条件参数
+    };
+  }
+
+  componentWillMount() {
+    console.log('TerminalsAccountsPage');
+
+    this.props.dispatch({
+      type: 'accounts/fetchTerminalsList',
+      payload: { ...this.state.Queryers }
+    })
+  }
+  handleDownloadOperation = () => {
+    this.props.dispatch({
+      type: 'accounts/fetchTerminalsExcel'
+    })
+  };
+  handleFilterOperation = (params, states) => {
+    this.setState({
+      UIParams: states,
+      Queryers: params,
+    });
+    this.props.dispatch({
+      type: 'accounts/fetchTerminalsList',
+      payload: {
+        ...params,
+      },
+    });
+  };
+
+  handleBatchOperation = () => {
+    Message.info('暂不支持批量操作!');
+  };
+  render() {
+    const { loading,accounts } = this.props;
+    const { list, totalSize, pageSize, pageNo } = accounts;
+    const batchActions = [{
+      key: 'delete',
+      name: '批量禁用',
+    }, {
+      key: 'recovery',
+      name: '批量解禁',
+    }, {
+      key: 'unbound',
+      name: '批量解绑',
+    }];
+
+    const basicSearch = {
+      keys: [{
+        name: '终端编号',
+        field: 'code',
+      }],
+    };
+
+
+    const pagination = {
+      pageNo,
+      pageSize,
+      totalSize,
+    };
+
+    const columns = [{
+      title: '终端编号',
+      key: 1,
+      dataIndex: 'ucode',
+      width: '15%',
+    }, {
+      title: '产品编号',
+      key: 2,
+      dataIndex: 'pcode',
+      width: '10%',
+    }, {
+      title: '产品名称',
+      key: 3,
+      dataIndex: 'pname',
+      width: '15%',
+    }, {
+      title: '权限有效期',
+      key: 4,
+      render: (_, record) => {
+        const { startTime, endTime } = record;
+        return (
+          <div className={styles.authDesc}>
+            <p><span>起始时间:&nbsp;&nbsp;</span>{`${timestamp2Str(startTime)}`}</p>
+            <p><span>到期时间:&nbsp;&nbsp;</span>{`${timestamp2Str(endTime)}`}</p>
+          </div>
+        );
+      },
+      dataIndex: 'cityName',
+      width: '30%',
+      align: 'center',
+    }, {
+      title: '权限有效时长',
+      key: 5,
+      dataIndex: 'endTime',
+      render: (text) => {
+        const day = moment(text).diff(moment(), 'days');
+        if (day < 0) {
+          return <Badge status="error" text="已到期" />;
+        }
+        return <span><span style={{ color: '#52c41a', fontWeight: 'bold' }}>{day}</span>天到期</span>;
+      },
+      width: '10%',
+    }, {
+      title: '联系电话',
+      key: 6,
+      dataIndex: 'campusContactWay',
+      width: '12%',
+    }, {
+      title: '联系人',
+      key: 7,
+      dataIndex: 'campusContactName',
+      width: '8%',
+    }, ];
+
+    return (
+      <Card>
+        <StandardTableList
+          columns={columns}
+          loading={loading}
+          dataSource={addRowKey(list)}
+          header={{
+            basicSearch,
+            onFilterClick: this.handleFilterOperation,
+            onDownload: this.handleDownloadOperation,
+          }}
+          footer={{
+            pagination,
+            batchActions,
+            onBatchClick: this.handleBatchOperation,
+          }}
+          keepUIState={{ ...this.state.UIParams }}
+          showStatusSelect={false}
+        />
+      </Card>
+    );
+  }
+}

+ 9 - 0
src/routes/Dashboard/Accounts/AccountsTerminals.less

@@ -0,0 +1,9 @@
+@import "../../../../node_modules/antd/lib/style/themes/default.less";
+.authDesc {
+  p {
+    margin-bottom: unset !important;
+  }
+  & > p > span {
+    font-weight: bold;
+  }
+}

+ 182 - 0
src/routes/Dashboard/Accounts/AccountsTotalList.js

@@ -0,0 +1,182 @@
+import React, { Component } from 'react';
+import moment from 'moment';
+import { Card, Modal, Button, message } from 'antd';
+const Message = message;
+
+export default class TotalListAccountsPage extends Component {
+  constructor(props) {
+    super(props);
+  }
+  componentDidMount() {
+    
+  }
+
+  render() {
+    return (
+      <Card>
+        TotalListAccountsPage
+      </Card>
+    );
+  }
+}
+
+
+// import React, { Component } from 'react';
+// import moment from 'moment';
+// import { connect } from 'dva';
+// import { Card, message, Badge } from 'antd';
+// import { StandardTableList } from '../../../components/AXList';
+// import { addRowKey } from '../../../utils/utils';
+// import styles from './AccountsTerminals.less';
+// const Message = message;
+// const timestamp2Str = ts => moment(ts).format('YYYY-MM-DD HH:mm:ss');
+
+// @connect(({ loading, accounts }) => ({
+//   accounts,
+//   loading: loading.models.terminal,
+// }))
+
+// export default class TerminalsAccountsPage extends Component {
+//   constructor(props) {
+//     super(props);
+//     const { state } = props.location;
+//     this.state = {
+//       UIParams: (state || {}).UIParams, // 组件的状态参数
+//       Queryers: (state || {}).Queryers, // 查询的条件参数
+//     };
+//   }
+
+//   componentWillMount() {
+//     console.log('TerminalsAccountsPage');
+
+//     this.props.dispatch({
+//       type: 'accounts/fetchTerminalsList',
+//       payload: { ...this.state.Queryers }
+//     })
+//   }
+//   handleDownloadOperation = () => {
+//     this.props.dispatch({
+//       type: 'accounts/fetchTerminalsExcel'
+//     })
+//   };
+//   handleFilterOperation = (params, states) => {
+//     this.setState({
+//       UIParams: states,
+//       Queryers: params,
+//     });
+//     this.props.dispatch({
+//       type: 'accounts/fetchTerminalsList',
+//       payload: {
+//         ...params,
+//       },
+//     });
+//   };
+
+//   handleBatchOperation = () => {
+//     Message.info('暂不支持批量操作!');
+//   };
+//   render() {
+//     const { loading,accounts } = this.props;
+//     const { list, totalSize, pageSize, pageNo } = accounts;
+//     const batchActions = [{
+//       key: 'delete',
+//       name: '批量禁用',
+//     }, {
+//       key: 'recovery',
+//       name: '批量解禁',
+//     }, {
+//       key: 'unbound',
+//       name: '批量解绑',
+//     }];
+
+//     const basicSearch = {
+//       keys: [{
+//         name: '终端编号',
+//         field: 'code',
+//       }],
+//     };
+
+
+//     const pagination = {
+//       pageNo,
+//       pageSize,
+//       totalSize,
+//     };
+
+//     const columns = [{
+//       title: '终端编号',
+//       key: 1,
+//       dataIndex: 'ucode',
+//       width: '15%',
+//     }, {
+//       title: '产品编号',
+//       key: 2,
+//       dataIndex: 'pcode',
+//       width: '10%',
+//     }, {
+//       title: '产品名称',
+//       key: 3,
+//       dataIndex: 'pname',
+//       width: '15%',
+//     }, {
+//       title: '权限有效期',
+//       key: 4,
+//       render: (_, record) => {
+//         const { startTime, endTime } = record;
+//         return (
+//           <div className={styles.authDesc}>
+//             <p><span>起始时间:&nbsp;&nbsp;</span>{`${timestamp2Str(startTime)}`}</p>
+//             <p><span>到期时间:&nbsp;&nbsp;</span>{`${timestamp2Str(endTime)}`}</p>
+//           </div>
+//         );
+//       },
+//       dataIndex: 'cityName',
+//       width: '30%',
+//       align: 'center',
+//     }, {
+//       title: '权限有效时长',
+//       key: 5,
+//       dataIndex: 'endTime',
+//       render: (text) => {
+//         const day = moment(text).diff(moment(), 'days');
+//         if (day < 0) {
+//           return <Badge status="error" text="已到期" />;
+//         }
+//         return <span><span style={{ color: '#52c41a', fontWeight: 'bold' }}>{day}</span>天到期</span>;
+//       },
+//       width: '10%',
+//     }, {
+//       title: '联系电话',
+//       key: 6,
+//       dataIndex: 'campusContactWay',
+//       width: '12%',
+//     }, {
+//       title: '联系人',
+//       key: 7,
+//       dataIndex: 'campusContactName',
+//       width: '8%',
+//     }, ];
+
+//     return (
+//       <Card>
+//         <StandardTableList
+//           columns={columns}
+//           loading={loading}
+//           dataSource={addRowKey(list)}
+//           header={{
+//             basicSearch,
+//             onFilterClick: this.handleFilterOperation,
+//             onDownload: this.handleDownloadOperation,
+//           }}
+//           footer={{
+//             pagination,
+//             batchActions,
+//             onBatchClick: this.handleBatchOperation,
+//           }}
+//           keepUIState={{ ...this.state.UIParams }}
+//           showStatusSelect={false}
+//         />
+//       </Card>
+//     );
+//   }
+// }

+ 73 - 0
src/routes/Dashboard/Accounts/index.js

@@ -0,0 +1,73 @@
+import React, { Component } from 'react';
+import { Redirect, Route, Switch, routerRedux } from 'dva/router';
+import { connect } from 'dva';
+import PageHeaderLayout from '../../../layouts/PageHeaderLayout';
+import { getRoutes } from '../../../utils/utils';
+
+// tab首页
+@connect()
+
+export default class AccountsPage extends Component {
+  handleTabChange = (key) => {
+    const { dispatch, match } = this.props;
+    switch (key) {
+      // 校区列表
+      case 'campus':
+        dispatch(routerRedux.push(`${match.url}/campus`));
+        break;
+      // 产品权限
+      case 'terminals':
+        dispatch(routerRedux.push(`${match.url}/terminals`));
+        break;
+      // 即将逾期
+      case 'overdue':
+        dispatch(routerRedux.push(`${match.url}/overdue`));
+        break;
+      // 总统计表
+      case 'totalList':
+        dispatch(routerRedux.push(`${match.url}/totalList`));
+        break;
+      default:
+        break;
+    }
+  };
+
+  render() {
+    const tabList = [
+      {
+        key: 'campus',
+        tab: '校区列表',
+      },
+      {
+        key: 'terminals',
+        tab: '产品权限',
+      },
+      {
+        key: 'overdue',
+        tab: '即将逾期',
+      },
+      {
+        key: 'totalList',
+        tab: '总统计表',
+      },
+    ];
+
+    const { match, routerData, location } = this.props;
+    const routes = getRoutes(match.path, routerData);
+
+    return (
+      <PageHeaderLayout
+        tabList={tabList}
+        tabActiveKey={location.pathname.replace(`${match.path}/`, '')}
+        onTabChange={this.handleTabChange}
+      >
+        <Switch>
+          {routes.map(item => (
+            <Route key={item.key} path={item.path} component={item.component} exact={item.exact} />
+          ))}
+          <Redirect exact from="/dashboard/accounts" to="/dashboard/accounts/campus" />
+        </Switch>
+      </PageHeaderLayout>
+    );
+  }
+}

+ 1 - 0
src/routes/Dashboard/SnapshotList.js

@@ -12,6 +12,7 @@ const Message = message;
   trade,
   loading: loading.models.trade,
 }))
+
 export default class SnapshotListPage extends Component {
   componentDidMount() {
     this.props.dispatch({

+ 0 - 1
src/routes/Frontend/Tag/index.js

@@ -9,7 +9,6 @@ export default class Tag extends Component {
   render() {
     const { match, routerData } = this.props;
     const routes = getRoutes(match.path, routerData);
-
     return (
       <PageHeaderLayout>
         <Switch>

+ 25 - 0
src/services/accounts.js

@@ -0,0 +1,25 @@
+import { stringify } from 'qs';
+import request from '../utils/request';
+import { api, Hotax } from '../utils/config';
+
+
+export async function queryCampusList(params) {
+  const newParams = {
+    pageSize: Hotax.PAGE_SIZE,
+    ...params,
+  };
+  return request(`${api.accountsCampusList}?${stringify(newParams)}`);
+}
+export async function downloadCampusExcel() {
+  return request(`${api.accountsCampusDownload}`);
+}
+export async function queryTerminalsList(params) {
+  const newParams = {
+    pageSize: Hotax.PAGE_SIZE,
+    ...params,
+  };
+  return request(`${api.accountsTerminalsList}?${stringify(newParams)}`);
+}
+export async function downloadTerminalsExcel() {
+  return request(`${api.accountsTerminalsDownload}`);
+}

+ 4 - 0
src/utils/config.js

@@ -144,6 +144,10 @@ const apiObj = {
   userTagCopy: '/userTag/copy',
   userRecommend: '/user/userRecommend/uid',
   userDevice: '/userDevice/list',
+  accountsCampusList: '/stmt/campus/list',
+  accountsCampusDownload: '/stmt/campus/export',
+  accountsTerminalsList: '/stmt/terminal/user/page',
+  accountsTerminalsDownload: '/stmt/terminal/user/export',
 };
 
 /**