Newer
Older
dxCard-admin / src / hooks / system / useListPage.ts
YFJ on 23 Sep 12 KB 项目推送
import { reactive, ref, Ref, unref } from 'vue';
import { merge } from 'lodash-es';
import { DynamicProps } from '/#/utils';
import { BasicTableProps, TableActionType, useTable } from '/@/components/Table';
import { ColEx } from '/@/components/Form/src/types';
import { FormActionType } from '/@/components/Form';
import { useMessage } from '/@/hooks/web/useMessage';
import { useMethods } from '/@/hooks/system/useMethods';
import { useDesign } from '/@/hooks/web/useDesign';
import { filterObj } from '/@/utils/common/compUtils';
import { isFunction } from '@/utils/is';
const { handleExportXls, handleImportXls } = useMethods();

// 定义 useListPage 方法所需参数
interface ListPageOptions {
  // 样式作用域范围
  designScope?: string;
  // 【必填】表格参数配置
  tableProps: TableProps & {
    // 添加 defSort 类型定义
    defSort?: DefSort;
  };
  // 是否分页
  pagination?: boolean;
  // 导出配置
  exportConfig?: {
    url: string | (() => string);
    // 导出文件名
    name?: string | (() => string);
    //导出参数
    params?: object | (() => object);
  };
  // 导入配置
  importConfig?: {
    //update-begin-author:taoyan date:20220507 for: erp代码生成 子表 导入地址是动态的
    url: string | (() => string);
    //update-end-author:taoyan date:20220507 for: erp代码生成 子表 导入地址是动态的
    // 导出成功后的回调
    success?: (fileInfo?: any) => void;
  };
}

interface IDoRequestOptions {
  // 是否显示确认对话框,默认 true
  confirm?: boolean;
  // 是否自动刷新表格,默认 true
  reload?: boolean;
  // 是否自动清空选择,默认 true
  clearSelection?: boolean;
}

interface DefSort {
  column: string;
  order: 'asc' | 'desc';
}

/**
 * listPage页面公共方法
 *
 * @param options
 */
export function useListPage(options: ListPageOptions) {
  const $message = useMessage();
  let $design = {} as ReturnType<typeof useDesign>;
  if (options.designScope) {
    $design = useDesign(options.designScope);
  }

  const tableContext = useListTable(options.tableProps);

  const [, { getForm, reload, setLoading }, { selectedRowKeys }] = tableContext;

  // 导出 excel
  async function onExportXls() {
    //update-begin---author:wangshuai ---date:20220411  for:导出新增自定义参数------------
    let { url, name, params } = options?.exportConfig ?? {};
    let realUrl = typeof url === 'function' ? url() : url;
    if (realUrl) {
      let title = typeof name === 'function' ? name() : name;
      //update-begin-author:taoyan date:20220507 for: erp代码生成 子表 导出报错,原因未知-
      let paramsForm:any = {};
      try {
        //update-begin-author:liusq---date:2025-03-20--for: [QQYUN-11627]代码生成原生表单,数据导出,前端报错,并且范围参数没有转换 #7962
        //当useSearchFor不等于false的时候,才去触发validate
        if (options?.tableProps?.useSearchForm !== false) {
          paramsForm = await getForm().validate();
          console.log('paramsForm', paramsForm);
        }
        //update-end-author:liusq---date:2025-03-20--for:[QQYUN-11627]代码生成原生表单,数据导出,前端报错,并且范围参数没有转换 #7962
      } catch (e) {
        console.warn(e);
      }
      //update-end-author:taoyan date:20220507 for: erp代码生成 子表 导出报错,原因未知-

      //update-begin-author:liusq date:20230410 for:[/issues/409]导出功能没有按排序结果导出,设置导出默认排序,创建时间倒序
      // 获取表格的默认排序
      const { defSort } = options?.tableProps ?? {};
      if (defSort && !paramsForm?.column) {
        // 使用类型断言确保 defSort 类型正确
        Object.assign(paramsForm, {
          column: (defSort as DefSort).column,
          order: (defSort as DefSort).order,
        });
      } else if (!paramsForm?.column) {
        // 如果没有默认排序,则使用创建时间倒序
        Object.assign(paramsForm, { column: 'createTime', order: 'desc' });
      }
      //update-begin-author:liusq date:20230410 for: [/issues/409]导出功能没有按排序结果导出,设置导出默认排序,创建时间倒序

      //如果参数不为空,则整合到一起
      //update-begin-author:taoyan date:20220507 for: erp代码生成 子表 导出动态设置mainId
      if (params) {
        //update-begin-author:liusq---date:2025-03-20--for: [QQYUN-11627]代码生成原生表单,数据导出,前端报错,并且范围参数没有转换 #7962
        const realParams = isFunction(params) ? await params() : { ...(params || {}) };
        //update-end-author:liusq---date:2025-03-20--for:[QQYUN-11627]代码生成原生表单,数据导出,前端报错,并且范围参数没有转换 #7962
        Object.keys(realParams).map((k) => {
          let temp = (realParams as object)[k];
          if (temp) {
            paramsForm[k] = unref(temp);
          }
        });
      }
      //update-end-author:taoyan date:20220507 for: erp代码生成 子表 导出动态设置mainId
      if (selectedRowKeys.value && selectedRowKeys.value.length > 0) {
        paramsForm['selections'] = selectedRowKeys.value.join(',');
      }
      console.log()
      return handleExportXls(title as string, realUrl, filterObj(paramsForm));
      //update-end---author:wangshuai ---date:20220411  for:导出新增自定义参数--------------
    } else {
      $message.createMessage.warn('没有传递 exportConfig.url 参数');
      return Promise.reject();
    }
  }

  // 导入 excel
  function onImportXls(file) {
    let { url, success } = options?.importConfig ?? {};
    //update-begin-author:taoyan date:20220507 for: erp代码生成 子表 导入地址是动态的
    let realUrl = typeof url === 'function' ? url() : url;
    if (realUrl) {
      return handleImportXls(file, realUrl, success || reload);
      //update-end-author:taoyan date:20220507 for: erp代码生成 子表 导入地址是动态的
    } else {
      $message.createMessage.warn('没有传递 importConfig.url 参数');
      return Promise.reject();
    }
  }

  /**
   * 通用请求处理方法,可自动刷新表格,自动清空选择
   * @param api 请求api
   * @param options 是否显示确认框
   */
  function doRequest(api: () => Promise<any>, options?: IDoRequestOptions) {
    return new Promise((resolve, reject) => {
      const execute = async () => {
        try {
          setLoading(true);
          const res = await api();
          if (options?.reload ?? true) {
            reload();
          }
          if (options?.clearSelection ?? true) {
            selectedRowKeys.value = [];
          }
          resolve(res);
        } catch (e) {
          reject(e);
        } finally {
          setLoading(false);
        }
      };
      if (options?.confirm ?? true) {
        $message.createConfirm({
          iconType: 'warning',
          title: '删除',
          content: '确定要删除吗?',
          onOk: () => execute(),
          onCancel: () => reject(),
        });
      } else {
        execute();
      }
    });
  }

  /** 执行单个删除操作 */
  function doDeleteRecord(api: () => Promise<any>) {
    return doRequest(api, { confirm: false, clearSelection: false });
  }

  return {
    ...$design,
    ...$message,
    onExportXls,
    onImportXls,
    doRequest,
    doDeleteRecord,
    tableContext,
  };
}

// 定义表格所需参数
type TableProps = Partial<DynamicProps<BasicTableProps>>;
type UseTableMethod = TableActionType & {
  getForm: () => FormActionType;
};

/**
 * useListTable 列表页面标准表格参数
 *
 * @param tableProps 表格参数
 */
export function useListTable(tableProps: TableProps): [
  (instance: TableActionType, formInstance: UseTableMethod) => void,
  TableActionType & {
    getForm: () => FormActionType;
  },
  {
    rowSelection: any;
    selectedRows: Ref<Recordable[]>;
    selectedRowKeys: Ref<any[]>;
  }
] {
  // 自适应列配置
  const adaptiveColProps: Partial<ColEx> = {
    xs: 24, // <576px
    sm: 12, // ≥576px
    md: 12, // ≥768px
    lg: 8, // ≥992px
    xl: 8, // ≥1200px
    xxl: 6, // ≥1600px
  };
  const defaultTableProps: TableProps = {
    rowKey: 'id',
    // 使用查询条件区域
    useSearchForm: true,
    // 查询条件区域配置
    formConfig: {
      // 紧凑模式
      compact: true,
      // label默认宽度
      // labelWidth: 120,
      // 按下回车后自动提交
      autoSubmitOnEnter: true,
      // 默认 row 配置
      rowProps: { gutter: 8 },
      // 默认 col 配置
      baseColProps: {
        ...adaptiveColProps,
      },
      labelCol: {
        xs: 24,
        sm: 8,
        md: 6,
        lg: 8,
        xl: 6,
        xxl: 6,
      },
      wrapperCol: {},
      // 是否显示 展开/收起 按钮
      showAdvancedButton: true,
      // 超过指定列数默认折叠
      autoAdvancedCol: 3,
      // 操作按钮配置
      actionColOptions: {
        ...adaptiveColProps,
        style: { textAlign: 'left' },
      },
    },
    // 斑马纹
    striped: false,
    // 是否可以自适应高度
    canResize: true,
    // 表格最小高度
    // update-begin--author:liaozhiyang---date:20240603---for【TV360X-861】列表查询区域不可往上滚动
    minHeight: 300,
    // update-end--author:liaozhiyang---date:20240603---for【TV360X-861】列表查询区域不可往上滚动
    // 点击行选中
    clickToRowSelect: false,
    // 是否显示边框
    bordered: true,
    // 是否显示序号列
    showIndexColumn: false,
    // 显示表格设置
    showTableSetting: true,
    // 表格全屏设置
    tableSetting: {
      fullScreen: false,
    },
    // 是否显示操作列
    showActionColumn: true,
    // 操作列
    actionColumn: {
      width: 120,
      title: '操作',
      //是否锁定操作列取值 right ,left,false
      fixed: false,
      dataIndex: 'action',
      slots: { customRender: 'action' },
    },
  };
  // 合并用户个性化配置
  if (tableProps) {
    //update-begin---author:wangshuai---date:2024-04-28---for:【issues/6180】前端代码配置表变查询条件显示列不生效---
    if(tableProps.formConfig){
      setTableProps(tableProps.formConfig);
    }
    //update-end---author:wangshuai---date:2024-04-28---for:【issues/6180】前端代码配置表变查询条件显示列不生效---
    // merge 方法可深度合并对象
    merge(defaultTableProps, tableProps);
  }

  // 发送请求之前调用的方法
  function beforeFetch(params) {
    // 默认以 createTime 降序排序
    return Object.assign({ column: 'createTime', order: 'desc' }, params);
  }

  // 合并方法
  Object.assign(defaultTableProps, { beforeFetch });
  if (typeof tableProps.beforeFetch === 'function') {
    defaultTableProps.beforeFetch = function (params) {
      params = beforeFetch(params);
      // @ts-ignore
      tableProps.beforeFetch(params);
      return params;
    };
  }

  // 当前选择的行
  const selectedRowKeys = ref<any[]>([]);
  // 选择的行记录
  const selectedRows = ref<Recordable[]>([]);

  // 表格选择列配置
  const rowSelection: any = tableProps?.rowSelection ?? {};
  const defaultRowSelection = reactive({
    ...rowSelection,
    type: rowSelection.type ?? 'checkbox',
    // 选择列宽度,默认 50
    columnWidth: rowSelection.columnWidth ?? 50,
    selectedRows: selectedRows,
    selectedRowKeys: selectedRowKeys,
    onChange(...args) {
      selectedRowKeys.value = args[0];
      selectedRows.value = args[1];
      if (typeof rowSelection.onChange === 'function') {
        rowSelection.onChange(...args);
      }
    },
  });
  delete defaultTableProps.rowSelection;

  /**
   * 设置表格参数
   *
   * @param formConfig
   */
  function setTableProps(formConfig: any) {
    const replaceAttributeArray: string[] = ['baseColProps','labelCol'];
    for (let item of replaceAttributeArray) {
      if(formConfig && formConfig[item]){
        if(defaultTableProps.formConfig){
          let defaultFormConfig:any = defaultTableProps.formConfig;
          defaultFormConfig[item] = formConfig[item];
        }
        formConfig[item] = {};
      }
    }
  }

  return [
    ...useTable(defaultTableProps),
    {
      selectedRows,
      selectedRowKeys,
      rowSelection: defaultRowSelection,
    },
  ];
}