Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

使用ant design开发完整的后台系统 #64

Open
reng99 opened this issue Jun 8, 2020 · 0 comments
Open

使用ant design开发完整的后台系统 #64

reng99 opened this issue Jun 8, 2020 · 0 comments
Labels
blog a single blog

Comments

@reng99
Copy link
Owner

reng99 commented Jun 8, 2020

这里要说的是ant designvue版和react版本的使用。这里不考虑到两种框架vuereact的底层。

vue ant design 项目

这是一个测试平台的项目。

vue ant design

因为使用的是整套的框架,那么我们按照vue ant design流程走一波。

推荐使用yarn进行安装~

# 安装脚手架
yarn global add @vue/cli

# 初始化项目
vue create antd-demo

# 引入vue ant design
yarn add ant-design-vue

之后我们就直接在main.js文件中全局引入

// main.js
import Vue from 'vue'
import Antd from 'ant-design-vue'
import 'ant-design-vue/dist/antd.less'

Vue.use(Antd)

使用axios操作接口。我们可以根据实际情况进行处理:

// request.js
import Vue from 'vue'
import notification from 'ant-design-vue/es/notification'
import JSONbig from 'json-bigint' // 处理大数据, 建议大数字后端变成字符串传回

// 创建 auth axios 实例
const auth = axios.create({
  headers: {
    'Content-Type': 'application/json;charset=UTF-8',
    'X-Requested-With': 'XMLHttpRequest'
  },
  baseURL: 'api', // api base_url
  timeout: 10 * 60 * 1000, // 请求超时时间 10 分钟
  transformResponse: [function (data) {
    return JSONbig.parse(data)
  }],
})

// 请求拦截器
auth.interceptors.request.use(config => {
  const token = Vue.ls.get(ACCESS_TOKEN)
  if (token) {
    config.headers[ 'Authorization' ] = 'JWT '+ token // 让每个请求携带自定义 token 请根据实际情况自行修改
  }
  return config
}, err)

// 响应拦截器
auth.interceptors.response.use(
  response => {
    if (response.code === 10140) {
      loginTimeOut()
    } else {
      return response.data
    }
  }, 
  error => { // 错误处理
    // 超时
    if(error.response && error.response.status === 500){
      notice({
        title: `服务端异常 ${error.response.statusText}`,
      }, 'notice', 'error', 5)
      return
    }
    // ...
    notice({
        title: `${error.response && error.response.data && error.response.data.msg}`,
    }, 'notice', 'error', 5)
  }
)

那么我们来考虑下对接口的管理:

# api目录
- api
  - basic_config
    - device.js
    - ...
  - index.js

上面是api的基本管理的目录,这里呈现device.jsindex.js入口文件。

// device.js
import { auth } from '@/utils/request'

export function getDeviceList(params) {
  return auth({
    url: '/path/to/url',
    method: 'get',
    params
  })
}

export default {
  getDeviceList
}

接下来我们在原型上挂载接口:

// index.js
import bc_device from './basis_config/device'
// ...

const api = {
  bc_device
}

export default api

export const ApiPlugin = {}

ApiPlugin.install = function (Vue, options) {
  Vue.prototype.api = api // 挂载api在原型上
}

之后,调整下要代理地址:

// vue.config.js

const HOST = '0.0.0.0';
const PORT = '9002';
const DEV_URL = 'http://10.***.**.***' // 测试环境
module.exports = {
  devServer: {
    host: HOST,
    port: PORT,
    https: false,
    hotOnly: false,
    proxy: { // 配置跨域
      '/api': {
        //要访问的跨域的api的域名
        target: `${DEV_URL}`,
        ws: true,
        changOrigin: true,
        // pathRewrite: {
        //     '^/api': ''
        // }
      }    
    }
  }
  // ...
};

完成上面的配置和修改后,那么我们可以搬砖了...

以刚才配置的bc_device接口为例:

# 部分页面目录
- views
  - basis_config
    - comp # 这里是私有组件的存放
    - device_manage.vue
<!-- device_manage.vue -->
<template>
  <div class="device_manage">
    <a-table 
      :rowKey="row => row.id"
      :columns="columns"
      :dataSource="data"
      :pagination="false"
      :loading="loading"
      />
  </div>
</template>
<script>
export default {
  name: 'device_manage',
  data () {
    const columns = [{
      dataIndex: 'operation',
      title: '操作'
    },
    // ...
    ]
    return {
      columns,
      data: [],
      loading: false,
    }
  },
  mounted() {
    this.getList()
  },
  methods: {
    // 获取列表数据
    getLists() {
      let vm = this
      vm.loading = true
      const params = {}
      vm.api.bc_device.getDeviceList(params).then(res => {
        if(res.code === '00000') {
          vm.data = res.data || []
        } else {
          vm.$message.warning(res.msg || '获取设备列表失败!')
        }
	  }).finally(() => {
        vm.loading = false
      })
    }
  }
}
</script>

可以愉快地在搭建好的框架里面添加东西了。

react ant design 项目

使用react ant design开发的项目是一个信息配置后台系统。

react ant design

这里直接使用Ant Design Pro开发的。

这里的安装方法根据官网执行:

# 新建一个空的文件夹作为项目目录,并在目录下执行:
yarn create umi

# 选择`ant-design-pro`
Select the boilerplate type (Use arrow keys)
❯ ant-design-pro  - Create project with an layout-only ant-design-pro boilerplate, use together with umi block.
  app             - Create project with a simple boilerplate, support typescript.
  block           - Create a umi block.
  library         - Create a library with umi.
  plugin          - Create a umi plugin.

我这里结合了typescript来开发,推荐使用。

我们使用集成的umi-request

// request.ts
/**
 * request 网络请求工具
 * 更详细的 api 文档: https://github.com/umijs/umi-request
 */
import { getStore, removeStore } from '@/utils/storage';

import { extend } from 'umi-request';

import { notification } from 'antd';

import { stringify } from 'querystring';

import { router } from 'umi';

/**
 * 异常处理程序
 */
const errorHandler = (error: {
  response: Response;
  data: { code: number; message: string };
}): Response => {
  const { response, data } = error;
  if (response && response.status) {
    // const errorText = codeMessage[response.status] || response.statusText;
    const errorText = data ? data.message : '错误了!';

    const { status } = response;

    notification.error({
      message: `请求错误 - ${status}`,
      description: errorText,
    });

    /**
     * 10000 IP change
     * 10001 token timeout
     */
    if (response.status === 401 && (data.code === 10000 || data.code === 10001)) {
      router.replace({
        pathname: '/user/login',
        search: stringify({
          redirect: window.location.href,
        }),
      });
      removeStore('token');
    }
  } else if (!response) {
    notification.error({
      description: '您的网络发生异常,无法连接服务器',
      message: '网络异常',
    });
  }
  return response;
};

/**
 * 配置request请求时的默认参数
 */
const request = extend({
  errorHandler, // 默认错误处理
  credentials: 'include', // 默认请求是否带上cookie
});

// 请求拦截器
request.interceptors.request.use((url, options) => {
  const token = getStore('token') || ''; // 401的时候要清空下token
  if (token) {
    options.headers = {
      // 处理header中的token
      'Content-Type': 'application/json',
      Accept: 'application/json',
      Authorization: `** ${token}`,
    };
  }
  return {
    url,
    options: { ...options },
  };
});

request.interceptors.response.use(async (response: any) => response);

export default request;

处理接口请求:

// 接口的部分目录
- services
  - port.ts
  - ...
import request from '@/utils/request';

// 获取配置端口列表
export async function getNginxPort(params: object) {
  return request('/path/to/url', {
    method: 'get',
    params,
  });
}

在执行api请求操作之前,我们配置下代理~

// config/proxy.ts
/**
 * 在生产环境 代理是无法生效的,所以这里没有生产环境的配置
 * The agent cannot take effect in the production environment
 * so there is no configuration of the production environment
 * For details, please see
 * https://pro.ant.design/docs/deploy
 */
export default {
  dev: {
    '/api/': {
      target: 'http://***.**.***.**',
      changeOrigin: true,
      pathRewrite: { '^': '' },
    },
  },
};

那么我们来实现下分配端口的列表数据请求:

// pages/port/index.tsx
import React, { Component } from 'react';
import { PageHeaderWrapper } from '@ant-design/pro-layout';
import { Table, message, Tag, Row, Input, Button } from 'antd';
import {
  SearchOutlined,
  DeleteOutlined,
  PlusOutlined,
  InfoCircleOutlined,
} from '@ant-design/icons';
import { getNginxPort, postNginxPortItem, deleteNginxPortItem } from '@/services/port';
import moment from 'moment';

const { confirm } = Modal;

interface NginxPortProps {}

interface NginxPortState {
  data: object[];
  loading: boolean;
  current: number;
  pageSize: number;
  total: number;
  showSizeChanger: boolean;
  showQuickJumper: boolean;
}

class NginxPort extends Component<NginxPortProps, NginxPortState> {
  constructor(props: any) {
    super(props);
    this.state = {
      data: [],
      loading: false,
      current: 1,
      pageSize: 10,
      total: 0,
      showSizeChanger: true, // 展示页条数与否
      showQuickJumper: true, // 展示跳转与否
      search: ''
    };
  }

  componentDidMount() {
    this.getList();
  }

  getList = () => {
    const rn = this;
    const { pageSize, current } = rn.state;
    rn.setState({
      loading: true,
    });
    getNginxPort({
      page_size: pageSize,
      page: current
    })
      .then((res: any) => {
        if (res.code === 200) {
          rn.setState({
            data: res.data.lists || [],
            total: res.data.total || 0,
          });
        } else {
          message.warn(res.msg || '获取端口列表失败!');
        }
      })
      .finally(() => {
        rn.setState({
          loading: false,
        });
      });
  };

  // 改变表格
  onchange = (pagination: any) => {
    this.setState(
      {
        current: pagination.current,
        pageSize: pagination.pageSize,
      },
      this.getList,
    );
  };

  render() {
    const columns = [
      {
        title: '端口号',
        dataIndex: 'port',
        key: 'port',
        width: 120,
      },
      {
        title: '服务代号',
        dataIndex: 'sercode',
        key: 'sercode',
      },
      {
        title: 'GIT地址',
        dataIndex: 'git',
        key: 'git',
      },
      {
        title: '类型',
        dataIndex: 'type',
        key: 'type',
        render: (type: any) => <Tag color="blue">{type}</Tag>,
        width: 120,
      },
      {
        title: '创建时间',
        dataIndex: 'created_on',
        key: 'created_on',
        render: (text: number) => <span>{moment(text * 1000).format('YYYY-MM-DD')}</span>,
        width: 120,
      },
    ];
    const {
      data,
      loading,
      total,
      current,
      pageSize,
      showSizeChanger,
      showQuickJumper,
      showModal,
    } = this.state;
    return (
      <PageHeaderWrapper title={false}>
        <Table
          scroll={{ x: 1200 }}
          dataSource={data}
          columns={columns}
          loading={loading}
          onChange={this.onchange}
          rowKey={(record: any) => record.id}
          pagination={{
            total,
            current,
            pageSize,
            showTotal: (_total) => `共 ${_total} 条数据`,
            showSizeChanger,
            showQuickJumper,
          }}
        />
      </PageHeaderWrapper>
    );
  }
}

export default NginxPort;

好了,可以安静地打代码了。

后话

嗯~

根据实际情况增加功能,比如图表展示你要选啥实现:echart, bizchart...等等

在使用vuereact版本的ant design开发后台系统,自己还是比较偏向使用react ant design来开发,当然,这是根据团队和业务实际情况来选择。对了,typescript真的挺好用的,都2020年了,可以考虑在项目中引入使用。

@reng99 reng99 added the blog a single blog label Jun 8, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
blog a single blog
Projects
None yet
Development

No branches or pull requests

1 participant