skyline-console/docs/zh/develop/3-5-BaseStore-introduction.md

16 KiB
Raw Permalink Blame History

简体中文 | English

用途

  • 数据请求的处理
  • 支持获取全部数据
  • 支持分页获取数据
  • 支持对数据的各种请求处理(PUT、POST、GET、PATCH、DELETE、HEAD 等)

BaseStore 代码文件

  • src/stores/base.js

BaseStore 属性与函数定义介绍

  • 资源数据的 Store 继承于 BaseStore 类
  • 代码位置:src/stores/xxx/xxx.js,如云主机对应的 store 在src/stores/nova/instance.js
  • 只需要复写部分函数即可完成数据的请求操作
  • 属性与函数分为以下四种,
    • 通常需要复写的属性与函数,主要包含:
      • 与生成 url 相关的属性与函数
    • 按需复写的函数与属性,主要包含:
      • 列表数据的再加工
      • 详情数据的再加工
      • 请求参数的处理
      • url 的处理
    • 无需复写的函数与属性,主要包含:
      • 清空数据
      • 封装数据时对项目信息的处理
    • 基类中的基础函数,主要包含:
      • 处理分页数据的marker
    • 更详细与全面的介绍见下

名词说明

  • 前端分页
    • 一次性从后端获取所有列表数据
    • 前端基于获取到的数据总量、页面内配置的当前页数、每页数量来展示数据(BaseList组件处理)
  • 后端分页
    • 以当前页号、每页数量向后端请求数据
  • 前端排序
    • 使用前端分页时,按设定的排序信息对所有数据排序
    • 使用后端分页时,按设定的排序信息对当前页内的数据排序
  • 后端排序
    • 以当前页号、每页数量、当前排序信息向后端请求数据
    • 不存在前端分页+后端排序这种组合方式

通常需要复写的属性与函数

  • module:

    • 必须复写该函数

    • 资源对应的模块

    • 该函数用于生成请求的 url

    • 以云主机src/stores/nova/instance.js为例

      get module() {
          return 'servers';
      }
      
  • apiVersion

    • 必须复写该函数

    • 资源对应的 api 前缀

    • 因所有的请求需要由服务端转发所以api 的前缀需要基于profile内的信息生成

    • 以云主机src/stores/nova/instance.js为例

      get apiVersion() {
        return novaBase();
      }
      
  • responseKey

    • 必须复写该函数

    • 用于生成数据返回的 key创建的 key 等

    • 以云主机src/stores/nova/instance.js为例

      get responseKey() {
        return 'server';
      }
      

      请求

按需复写的属性与函数

  • listDidFetch

    • 列表数据二次加工使用的函数

    • 可请求其他 API 后,整合数据

    • 可过滤数据

    • 请求某个指定云硬盘的快照列表时,可以基于filters中的参数再次过滤数据

      • 以云硬盘快照src/stores/cinder/snapshot.js为例

        async listDidFetch(items, allProjects, filters) {
          if (items.length === 0) {
            return items;
          }
          const { id } = filters;
          const data = id ? items.filter((it) => it.volume_id === id) : items;
          return data;
        }
        
    • 如果需要显示加密信息,需要发起额外请求后,整合数据

      • 以云硬盘类型src/stores/cinder/volume-type.js为例

        async listDidFetch(items, allProjects, filters) {
          const { showEncryption } = filters;
          if (items.length === 0) {
            return items;
          }
          if (!showEncryption) {
            return items;
          }
          const promiseList = items.map((i) =>
            request.get(`${this.getDetailUrl({ id: i.id })}/encryption`)
          );
          const encryptionList = await Promise.all(promiseList);
          const result = items.map((i) => {
            const { id } = i;
            const encryption = encryptionList.find((e) => e.volume_type_id === id);
            return {
              ...i,
              encryption,
            };
          });
          return result;
        }
        
  • detailDidFetch

    • 详情数据二次加工使用的函数

    • 可请求其他 API 后,整合数据

    • 以云硬盘快照src/stores/cinder/snapshot.js为例

      async detailDidFetch(item) {
        const { volume_id } = item;
        const volumeUrl = `${cinderBase()}/${
          globals.user.project.id
        }/volumes/${volume_id}`;
        const { volume } = await request.get(volumeUrl);
        item.volume = volume;
        return item;
      }
      
  • listResponseKey

    • 列表数据的返回 Key

    • 默认是${this.responseKey}s

    • 以云硬盘快照src/stores/cinder/snapshot.js为例

      get responseKey() {
        return 'snapshot';
      }
      
      get listResponseKey() {
        return 'volume_snapshots';
      }
      
  • getListUrl

    • 请求数据使用的 url

    • 前端分页请求列表数据(即一次性获取所有数据)时,优先使用this.getListDetailUrl()

    • 后端分页请求列表数据时,按优先级this.getListPageUrl() > this.getListDetailUrl() > this.getListUrl()

    • 默认值为

      getListUrl = () => `${this.apiVersion}/${this.module}`;
      
    • 以 Heat 的堆栈的日志src/stores/heat/event.js为例

      getListUrl = ({ id, name }) =>
        `${this.apiVersion}/${this.module}/${name}/${id}/events`;
      
  • getListDetailUrl

    • 请求数据使用的 url

    • 前端分页请求列表数据(即一次性获取所有数据)时,优先使用this.getListDetailUrl()

    • 后端分页请求列表数据时,按优先级this.getListPageUrl() > this.getListDetailUrl() > this.getListUrl()

    • 默认值为

      getListDetailUrl = () => '';
      
    • 以云硬盘src/stores/cinder/volume.js为例

      getListDetailUrl = () => `${skylineBase()}/extension/volumes`;
      
  • getListPageUrl

    • 后端分页数据使用的 url

    • 后端分页请求列表数据时,按优先级this.getListPageUrl() > this.getListDetailUrl() > this.getListUrl()

    • 默认值为

      getListPageUrl = () => '';
      
    • 以云硬盘src/stores/cinder/volume.js为例

      getListPageUrl = () => `${skylineBase()}/extension/volumes`;
      
  • getDetailUrl

    • 详情数据对应的 url

    • 使用 rest 风格的 API所以该 url 也是 put, delete, patch 对应的 url

    • 默认值为

      getDetailUrl = ({ id }) => `${this.getListUrl()}/${id}`;
      
    • 以堆栈src/stores/heat/stack.js为例

      getDetailUrl = ({ id, name }) => `${this.getListUrl()}/${name}/${id}`;
      
  • needGetProject

    • 对服务端返回的数据是否需要二次加工其中的项目信息

    • 一般 Openstack API 返回的数据只有project_id信息,按页面展示的需求,在管理平台需要展示项目名称

    • 默认值是true

    • 以元数据src/stores/glance/metadata.js为例

      get needGetProject() {
        return false;
      }
      
  • mapper

    • 对服务端返回的列表、详情数据做二次加工

    • 一般是为了更便捷的在资源列表、资源详情中展示数据使用

    • 默认值为

      get mapper() {
        return (data) => data;
      }
      
    • 以云硬盘src/stores/cinder/volume.js为例

      get mapper() {
        return (volume) => ({
          ...volume,
          disk_tag: isOsDisk(volume) ? 'os_disk' : 'data_disk',
          description: volume.description || (volume.origin_data || {}).description,
        });
      }
      
  • mapperBeforeFetchProject

    • 在处理项目信息前,对服务端返回的列表、详情数据做二次加工

    • 一般是为了处理返回数据中的项目信息使用

    • 默认值为

      get mapperBeforeFetchProject() {
        return (data) => data;
      }
      
    • 以镜像src/stores/glance/image.js为例

      get mapperBeforeFetchProject() {
        return (data, filters, isDetail) => {
          if (isDetail) {
            return {
              ...data,
              project_id: data.owner,
            };
          }
          return {
            ...data,
            project_id: data.owner,
            project_name: data.owner_project_name || data.project_name,
          };
        };
      }
      
  • paramsFunc

    • 前端分页请求(即fetchList)时,对请求参数的更新

    • 默认是对从资源列表代码(pages/xxxx/xxx/index.jsx)使用fetchList时,参数的过滤

    • 默认值

      get paramsFunc() {
        if (this.filterByApi) {
          return (params) => params;
        }
        return (params) => {
          const reservedKeys = [
            'all_data',
            'all_projects',
            'device_id',
            'network_id',
            'floating_network_id',
            'start_at_gt',
            'start_at_lt',
            'binary',
            'fixed_ip_address',
            'device_owner',
            'project_id',
            'type',
            'sort',
            'security_group_id',
            'id',
            'security_group_id',
            'owner_id',
            'status',
            'fingerprint',
            'resource_types',
            'floating_ip_address',
            'uuid',
            'loadbalancer_id',
            'ikepolicy_id',
            'ipsecpolicy_id',
            'endpoint_id',
            'peer_ep_group_id',
            'local_ep_group_id',
            'vpnservice_id',
          ];
          const newParams = {};
          Object.keys(params).forEach((key) => {
            if (reservedKeys.indexOf(key) >= 0) {
              newParams[key] = params[key];
            }
          });
          return newParams;
        };
      }
      
    • 以云硬盘src/stores/cinder/volume.js为例

      get paramsFunc() {
        return (params) => {
          const { serverId, ...rest } = params;
          return rest;
        };
      }
      
  • paramsFuncPage

    • 后端分页请求(即fetchListByPage)时,对请求参数的更新

    • 默认是对从资源列表代码(pages/xxxx/xxx/index.jsx)使用fetchListByPage时,参数的过滤

    • 默认值

      get paramsFuncPage() {
        return (params) => {
          const { current, ...rest } = params;
          return rest;
        };
      }
      
    • 以云硬盘类型src/stores/cinder/volume-type.js为例

      get paramsFuncPage() {
        return (params) => {
          const { current, showEncryption, ...rest } = params;
          return rest;
        };
      }
      
  • fetchListByLimit

    • 前端分页请求所有数据时,是否要基于limit发起多个请求,最终实现所有数据的获取

    • Openstack API 默认返回的是 1000 个数据,对于某些资源数据很多的情况,需要使用该配置以便获取到所有数据

    • 默认值是false

    • 以镜像src/stores/glance/image.js为例

      get fetchListByLimit() {
        return true;
      }
      
  • markerKey

    • 后端分页请求数据时marker 的来源

    • 因为对 Openstack 的请求是由后端转发的,所以并没有直接使用列表数据返回的 Openstack 拼接好的下一页数据应该使用的 Url而是根据返回的数据解析出marker

    • 默认值是id

    • 通常不需要复写

    • 以密钥src/stores/nova/keypair.js为例

      get markerKey() {
        return 'keypair.name';
      }
      
  • requestListByMarker

    • 后端分页时,使用marker请求分页下的数据

    • 通常不需要复写

    • 默认值是

      async requestListByMarker(url, params, limit, marker) {
        const newParams = {
          ...params,
          limit,
        };
        if (marker) {
          newParams.marker = marker;
        }
        return request.get(url, newParams);
      }
      
    • 以云主机组src/stores/nova/server-group.js为例

      async requestListByMarker(url, params, limit, marker) {
        const newParams = {
          ...params,
          limit,
        };
        if (marker) {
          newParams.offset = marker;
        }
        return request.get(url, newParams);
      }
      
  • requestListAllByLimit

    • this.fetchListByLimit=true时,前端分页使用该方法获取所有数据
    • 通常不需要复写
  • updateUrl

    • 更新列表数据请求的 url
    • 不常用
  • updateParamsSortPage

    • 使用后端排序时,对排序参数的处理

    • 使用后端排序时,会在资源列表代码pages/xxx/XXX/index.jsx中自动生成相应的请求参数store 对这些参数往往需要再次整理,否则会不符合 API 的参数要求

    • 以云硬盘src/stores/cinder/volume.js为例

      updateParamsSortPage = (params, sortKey, sortOrder) => {
        if (sortKey && sortOrder) {
          params.sort_keys = sortKey;
          params.sort_dirs = sortOrder === 'descend' ? 'desc' : 'asc';
        }
      };
      
  • listFilterByProject

    • 列表数据是否需要基于项目信息过滤

    • admin权限下的部分 Openstack 资源(如neutron),会默认返回所有项目的数据,所以在控制台展示资源时,会根据该配置过滤数据

    • 默认值是false

    • 以 VPNsrc/stores/neutron/vpn-service.js为例

      get listFilterByProject() {
        return true;
      }
      
  • fetchList

    • pages下的列表页通常使用this.store.fetchList来获取前端分页数据
    • 不建议复写该函数,如果需要再加工数据,建议使用listDidFetch
    • 该函数会更新this.list属性中的相关数据,pages下的资源列表组件也是基于this.list进行数据展示
  • fetchListByPage

    • pages下的列表页通常使用this.store.fetchList来获取后端分页数据
    • 不建议复写该函数,如果需要再加工数据,建议使用listDidFetch
    • 该函数会更新this.list属性中的相关数据,pages下的资源列表组件也是基于this.list进行数据展示
  • getCountForPage

    • 获取列表数据的总量
    • 通常在后端分页时可复写
  • getDetailParams

    • 更新详情数据请求时的参数

    • 默认值为

      getDetailParams = () => undefined;
      
  • fetchDetail

    • pages下的详情页通常使用this.store.fetchDetail来获取详情数据
    • 通常不需要复写
    • 数据再加工通常是重写mapperdetailDidFetch
  • create

    • 创建资源
    • 使用POSTapi
    • 通常不需要复写
    • 使用this.submitting保证在发送请求时页面处于loading状态
  • edit

    • 更新资源
    • 使用PUTapi
    • 通常不需要复写
    • 使用this.submitting保证在发送请求时页面处于loading状态
  • patch

    • 更新资源
    • 使用PATCHapi
    • 通常不需要复写
    • 使用this.submitting保证在发送请求时页面处于loading状态
  • delete

    • 删除资源
    • 使用DELETEapi
    • 通常不需要复写
    • 使用this.submitting保证在发送请求时页面处于loading状态

不需要复写的属性与函数

  • submitting
    • 用于数据创建、数据更新时
    • 依据请求的响应变更this.isSubmitting,对应的 Form列表页等会展示 Loading 状态
  • currentProject
    • 当前用户登录的项目 ID
  • itemInCurrentProject
    • 数据是否属于当前用户登录的项目
  • listDidFetchProject
    • 对列表数据添加项目信息
  • requestListAll
    • 前端分页获取所有数据
  • requestListByPage
    • 后端分页所有当前页的数据
  • pureFetchList
    • 列表数据的请求函数
    • 返回原始数据,不会对 API 的返回数据做加工
  • parseMarker
    • 使用后端分页时,从返回数据中解析出marker,用于请求上一页、下一页数据时使用
  • updateMarker
    • 更新listmarkers
    • list.markers是个数组,每个元素对应于下标+1页的marker
  • getMarker
    • 获取指定页对应的marker
  • getListDataFromResult
    • 从 API 的返回值中取出列表数据
    • 利用this.listResponseKey获取
  • setSelectRowKeys
    • pages下的资源列表组件列表中数据项的选中记录
  • clearData
    • 清空list数据

基类中的基础函数

  • 建议查看代码理解,src/stores/base.js