⑥ 表格类

表格类

1 构造者函数+基本功能

class ListDataCls {
  /** 公共字段... **/
  constructor(config) {
    if (Reflect.has(config, 'isChart')) {
      this.isChart = false; // 是否有 chart
      this.charts = null; // 一般是 ChartDataCls 的实例,由外面赋值
    }

    deepAssign(this, config);
  }
  // 获取接口基本信息
  async getBaseInfo(params, setting = {}) {}
  // 请求页面数据
  getListData() {}
  // 获取数据后,表格数据处理
  setBaseData(table, setting = {}) {}
  // 前端分页 展示数据初始化
  initFullData() {}
  // 翻页
  changePageNum(page) {}
  // 每页大小改变
  changePageSize(pageSize) {}
  // 表格排序
  changeTableSort(sortdata, tabledata) {}
  // 表格复选
  changeSelections(selection) {}
}

1. 引入常量

const TABLE_TIPS = {
  default: '暂无数据',
  loading: '数据查询中',
  fail: '数据加载失败'
};
/** 接口数据状态码 */
const API_CODE = {
  success: 1,
  noPermission: 403
};
/**
 * errSymbol 解析:
 *  1 axios 的 quickPost 的弹窗
 *  2 ListDataCls.dataList() 的报错
 *  3
 */
const appTrackMessage = {
  error(err, errSymbol) {
    Message.error(`${err instanceof Error ? err.message : err} (错误码${errSymbol})`);
  },
  warning(err, errSymbol) {
    Message.warning(`${err instanceof Error ? err.message : err} (警告码${errSymbol})`);
  }
};

2. 公共字段

title = '';
permitted = true; // 是否有数据权限
tips = '暂无数据';

/** 接口 */
apis = { list: '' };
query = {
  dataKey: 'list_data', // 返回的数据列表的key [list_data, data_list]
  queryId: null, // 查询id,可用于帮助信息保存等
  queryKey: ''
  queryParams: {};
};

/** 帮助信息 */
remarks = {
  enable: true,
  editable: false, // 是否可编辑,从接口返回
  /**
   * 需要另外请求帮助信息
   * 约定:为 true 时 queryRemark 只能是直接内容
   */
  dynamic: false,
  data: '' // 帮助信息的内容,直接内容或 json 内容
};

/** 表格 */
tables = {
  visible: true, // 是否显示table
  isSetColumns: false, // 是否请求完数据后调用 setColumns 方法
  isIview: false, // 是否使用iview的表格
  isFootOnTop: true, // footData 放在顶部
  isFullData: false, // 前端分页相关,一般指后端一次性返回所有数据
  meaningFieldsMap: {}, // 一行是数组型数据时,用这个来映射列(即表头)的含义。比如 `{ id: 1 }` 即 id 是第 2 列
  fieldsSort: [] // 排序
};
columns = []; // 列信息(即表头)
data = []; // 全部数据
footData = []; // 脚部数据
showData = []; // 当前显示数据
selections = []; // 表格复选栏选中的数据

/** 表格排序 */
tableSort = {
  isRemote: false, // 是否远程排序
  order: '', // 升序、降序
  which: null // 表示哪一列
};

/** 翻页 */
pages = {
  enable: true,
  pageType: null, // 暂时搁置,以前一般用于若等于 0 显示总数
  current: 1, // 目前页码
  size: 30, // 一页条目个数
  sizeOptions: [10, 20, 30, 50, 100, 200, 500, 1000],
  total: 0, // 总条目数
  isShowTotal: true // 是否显示总数
};

2 事件处理程序

1. 对象属性合并

function deepAssign(target, ...sources) {
  for (const source of sources) {
    for (const key in source) {
      if (Util.typeOf(target[key]) === 'object') {
        deepAssign(target[key], source[key]);
      } else {
        target[key] = source[key];
      }
    }
  }
  return target;
}

2. 响应数据预处理

function _successResolve() {
  return true;
}

3. 检查表格的填充条目是否数组

checkRawTableRowIsArray(data) {
  for (let i in data) {
      return Array.isArray(data[i]);
  }
  return false;
}

4. 使用iview的table && 单条表格数据是数组时,需变为对象

tableDataToObj(data, backData = []) {
  data.forEach(item => {
    let child = {};
    item.forEach((item, index) => {
      child['key' + index] = item;
    });
    backData.push(child);
  });
  return backData;
}

5. 字符串提取数字:(==='-' => 0,/^[0-9]%$/.test() 截取到%之前)

extractNumber(num) {
  if (num === '-') {
    return 0;
  } else if (/(\d+)(%)/.test(String(num))) {
    return parseFloat(num.substring(0, String(num).indexOf('%')));
  } else if (/^(\d+)(~)(\d+)/.test(String(num))) {
    return parseFloat(num.substring(0, String(num).indexOf('~')));
  } else if (/^(\d+)(%)(~)(\d+)/.test(String(num))) {
    return parseFloat(num.substring(0, String(num).indexOf('~')));
  } else {
    return num;
  }
}

6. 字符串排序

function sortString(a, b) {
  if (String(a) < String(b)) {
    return -1;
  } else if (String(a) > String(b)) {
    return 1;
  } else {
    return 0;
  }
}

3 处理应用程序逻辑具体实现

1. 获取接口基本信息

/**
 * 获取接口基本信息,基本是查询类页面使用
 * @param {object|undefined} params 请求参数
 * @param {object} setting 其他设置
 * @param {boolean} setting.isSetColumns 是否设置表头
 */
async getBaseInfo(params, setting = {}) {
  const { isSetColumns } = setting;
  let reqConfig = {};
  params && (reqConfig.params = params);
  const { data: result } = await $axios.get(this.apis.list, reqConfig).catch(err => {
    Message.error('getApiInfo请求出错!');
    throw err;
  });
  if (result.code === API_CODE.success) {
    const table = result.data;
    // ...一系列变量赋值
    isSetColumns && this.setColumns(table.fields, table.fields_sort, this.meaningFieldsMap);
  } else if (result.code === API_CODE.noPermission) {
    this.isPermission = false;
    throw new Error(result.msg);
  } else {
    Message.warning(result.msg);
    throw new Error(result.msg);
  }
  return result;
}

2. 请求页面数据

/**
 * - 用于把请求配置固定下来,非默认配置时自定义实例方法即可
 * - 翻页默认调用此方法
 */
getListData(url, setting) {
  return this.getListDataCore(url, setting);
}
/**
 * 请求数据,包含了通用的业务处理
 * @param {string} url 手动传入请求地址
 * @param {object} setting 其他设置
 * @param {boolean} setting.noSpin 没有全局 loading
 * @param {boolean} setting.noVerify 表示不经过验证
 * @param {boolean} setting.isSetColumns 覆盖 this.tables.isSetColumns
 * @param {function} setting.successResolve 响应数据预处理
 * @param {boolean} setting.onlyChartData 该次请求只处理 chart 数据
 * @returns {promise}
 */
getListDataCore(url = this.apis.list, setting = {}) {
  const { noVerify, noSpin } = setting;

  // 请求配置
  const requestConfig = { noSpin };
  // 请求参数
  let params = {
    page_num: this.pages.current,
    page_size: this.pages.size
  };
  if (this.tableSort.order) {
    params.sort_type = this.tableSort.order;
    params.sort_index = this.tableSort.which;
  }
  if (this.isChart) {
    params.is_chart = 1;
  }
  Object.assign(params, this.query.queryParams)
  // 请求前校验
  if (!noVerify && !this.verify(params)) {
    return Promise.reject(new Error('验证不通过'));
  }
  this.selections = []; // 清空选中
  this.tips = TABLE_TIPS.loading; // 提示语
  // 是图表
  if (this.isChart) {
    this.charts.setStatusCode(1);
  }
  return await getDataListFunc(url, params, requestConfig, setting)
}
async getDataListFunc(url, params, requestConfig, setting = {}) {
  try {
    let { data: result } = await $axios.post(url, params, requestConfig);
    let isSecondaryError = result.code != API_CODE.success;
    
    if (typeof result !== 'object') {
      throw new Error('返回的数据格式错误!');
    }
    if (result.code === API_CODE.success) {
      this.setListData(result, isSecondaryError, setting)
      return result;
    } else {
      Message.warning(result.msg);
      throw new Error(result.msg);
    }
  } catch(err) {
    if (!$axios.isCancel(err)) {
      appTrackMessage.error(err, 2);
    }
    throw err;
  }
}
setListData(result, isSecondaryError, setting = {}) {
  // 次级错误,成功请求接口了,但业务层面返回失败(code != successCode)
  try {
    let { successResolve = _successResolve } = setting
    result.queryParams = params; // 把请求参数放进结果
    this.tips = TABLE_TIPS.default;
    this.charts.setStatusCode(2);
    if (!successResolve(result)) {
      return result;
    }
    let table = result.data; // table 相关数据
  
    switch (true) {
      case setting.onlyChartData:
        table = this.charts.resolveChart(result);
        break;
  
      // chart处理
      case this.isChart:
        table = this.charts.resolveChart(result);
        this.setBaseData(table, setting);
        this.initFullData();
        break;
  
      // 前端分页处理
      case this.tables.isFullData || table.is_full_data:
        this.setBaseData(table, setting);
        this.initFullData();
        break;
  
      // 斋表格
      default:
        this.setBaseData(table, setting);
        this.setListShowData();
    }
    if (table.total_page > 1 && table[this.query.dataKey].length <= 0) {
      Message.warning(result.msg);
    }
  
    return result;
  } catch(err) {
    if (isSecondaryError) {
      this.tips = err.message;
    } else {
      this.tips = TABLE_TIPS.fail;
    }
    this.charts.setStatusCode(-1);
    this.clearTable();
    console.error(err);
  }
}
  • 清空表格数据
clearTable() {
  this.data = [];
  this.footData = [];
  this.showData = [];
  this.overviews.fields = [];
  this.overviews.data = [];
  this.pages.total = 0;
}

3. 获取数据后,表格数据处理

setBaseData(table, setting = {}) {
  const { isSetColumns = this.tables.isSetColumns } = setting;
  const tableData = table[this.query.dataKey];

  // ...一系列变量赋值
  if (table.key_fields) {
    this.meaningFieldsMap = table.key_fields;
  }
  // 设置表头
  if (isSetColumns) {
    this.setColumns(table.fields, table.fields_sort, this.meaningFieldsMap);
  }
  // 使用 iview table && 单条表格数据是数组时,需变为对象
  if (this.tables.isIview && this.checkRawTableRowIsArray(tableData)) {
    this.data = ListDataCls.tableDataToObj(tableData);
    this.footData = ListDataCls.tableDataToObj(table.foot_data);
  } else {
    this.data = tableData;
    this.footData = table.foot_data;
  }
}

4. 前端分页 展示数据初始化

initFullData() {
  if (this.pages.enable) {
    this.updateFullShowData(1);
  } else {
    this.setListShowData();
  }
}
  • 前端分页 根据当前页码更新显示数据
updateFullShowData(pageNum = this.pages.current) {
  const start = (pageNum - 1) * this.pages.size;
  const end = pageNum * this.pages.size;

  this.setListShowData(this.data.slice(start, end));
}
  • 控制fdata是否在头部
setListShowData(data = this.data, footData = this.footData) {
  if (footData) {
    if (this.tables.isFootOnTop) {
      this.showData = footData.concat(data);
    } else {
      this.showData = data.concat(footData);
    }
  } else {
    this.showData = data;
  }
}

5. 分页操作

  • 翻页
changePageNum(page) {
  this.pages.current = page;
  if (this.isChart || this.tables.isFullData) {
    this.updateFullShowData();
  } else {
    this.getListData(undefined, { isSetColumns: false });
  }
}
  • 每页大小改变
changePageSize(pageSize) {
  this.pages.size = pageSize;
  if (this.pages.current === 1) this.changePageNum(1);
}

6. 表格排序

tableSortChange(sortdata, tabledata) {
  // TableInfinite和Table的sortdata有差异(index <==> key)
  let sortIndex = Reflect.has(sortdata, 'index') ? sortdata.index : sortdata.key; 
  if (!tabledata || tabledata.length === 0) return;
  if (
    this.tableSort.isRemote ||
    (!this.isChart && !this.tables.isFullData && this.tables.fieldsSort.length > 0)
  ) {
    // 请求排序
    this.sortTableRemotely(sortdata.order, sortIndex);
  } else {
    // 手动排序
    this.sortTableHandle(tabledata, sortdata.order, soetIndex)      
  }
}
  • 请求排序
sortTableRemotely(order, sortIndex) {
  this.pages.current = 1;
  this.tableSort.order = order;
  this.tableSort.which = sortIndex;
  this.getListData(undefined, { isSetColumns: false });
}
  • 手动排序
sortTableHandle(tabledata, order, sortIndex) {
  let orderType = order == 'asc' ? 1 : order == 'desc' ? -1 : 0,
    isnum = 0;

  if (!isNaN(tabledata[Math.floor(tabledata.length / 2)][sortIndex])) {
    // 是数字
    isnum++;
  } else {
    var reg = /(\d+)(%)/,
        reg2 = /^(\d+)(~)(\d+)/,
        reg3 = /^(\d+)(%)(~)(\d+)/;
    for (var i = 0; i < tabledata.length; i++) {
      if (
        tabledata[i][sortIndex] === '-' ||
        reg.test(tabledata[i][sortIndex]) ||
        reg2.test(tabledata[i][sortIndex]) ||
        reg3.test(tabledata[i][sortIndex])
      ) {
        // 包含数字
        isnum++;
        break;
      }
    }
  }
  tableSortManually(orderType, tabledata, sortIndex, isnum);
  if (this.isChart || this.tables.isFullData) {
    this.pages.current = 1;
    this.initFullData();
  } else {
    this.setListShowData();
  }
}
  • 按类型排序
/**
 * 按类型排序
 * @param {Number} type 排序类型asc[-1],desc[1],normal[0]
 * @param {Array} data 数据 [[], []] or [{}, {}]
 * @param {String | Number} sortIndex 表格排序列对应的key
 * @param {Number} isnum 是否是数字排序(大于0数字,小于全部字符串,不设置时间排序)
 */
tableSortManually(type, data, sortIndex, isnum) {
  switch (type) {
    case -1:
      isnum > 0
        ? data.sort((a, b) => extractNumber(a[sortIndex]) - extractNumber(b[sortIndex]))
        : data.sort((a, b) => sortString(a[sortIndex], b[sortIndex]));
      break;
    case 1:
      isnum > 0
        ? data.sort((a, b) => extractNumber(b[sortIndex]) - extractNumber(a[sortIndex]))
        : data.sort((a, b) => sortString(b[sortIndex], a[sortIndex]));
      break;
    default:
      return false;
  }
}

7. 表格复选

changeSelections(selections) {
  this.selections = selections;
}

8. 清空表格数据

clearTable() {
  this.data = [];
  this.footData = [];
  this.showData = [];
  this.overviews.fields = [];
  this.overviews.data = [];
  this.pages.total = 0;
}

9. 重置表格排序

resetTableSort() {
  this.pages.current = 1;
  this.tableSort.order = '';
  this.tableSort.which = null;
}

10. 应由外面赋值的方法

  • 设置表头
setColumns() {
  console.warn('缺少自定义的 setColumns 方法');
}
  • 设置请求参数
// params 请求参数
setQueryParam(params) {
  return params;
}
  • 自定义验证方法
verify() {
  return true;
}
  • 设置下载参数
/**
 * 设置下载参数
 * @param {object} params 接口参数
 * @param {object} setting 其他配置
 * @param {boolean} setting.noVerify 表示不经过验证
 */
setDownloadInfo(params, setting) {
  const { noVerify } = setting;

  // 若需要验证
  if (!noVerify && !this.verify(params)) {
    params.IS_STOP_DOWNLOAD = true;
    return;
  }
  this.setQueryParam(params);
}
posted on 2023-02-09 14:26  pleaseAnswer  阅读(12)  评论(0编辑  收藏  举报