useListPage.ts 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  1. import { reactive, ref, Ref, unref } from 'vue';
  2. import { merge } from 'lodash-es';
  3. import { DynamicProps } from '/#/utils';
  4. import { BasicTableProps, TableActionType, useTable } from '/@/components/Table';
  5. import { ColEx } from '/@/components/Form/src/types';
  6. import { FormActionType } from '/@/components/Form';
  7. import { useMessage } from '/@/hooks/web/useMessage';
  8. import { useMethods } from '/@/hooks/system/useMethods';
  9. import { useDesign } from '/@/hooks/web/useDesign';
  10. import { filterObj } from '/@/utils/common/compUtils';
  11. import { isFunction } from '@/utils/is';
  12. const { handleExportXls, handleImportXls } = useMethods();
  13. // 定义 useListPage 方法所需参数
  14. interface ListPageOptions {
  15. // 样式作用域范围
  16. designScope?: string;
  17. // 【必填】表格参数配置
  18. tableProps: TableProps;
  19. // 是否分页
  20. pagination?: boolean;
  21. // 导出配置
  22. exportConfig?: {
  23. url: string | (() => string);
  24. // 导出文件名
  25. name?: string | (() => string);
  26. //导出参数
  27. params?: object | (() => object);
  28. };
  29. // 导入配置
  30. importConfig?: {
  31. //update-begin-author:taoyan date:20220507 for: erp代码生成 子表 导入地址是动态的
  32. url: string | (() => string);
  33. //update-end-author:taoyan date:20220507 for: erp代码生成 子表 导入地址是动态的
  34. // 导出成功后的回调
  35. success?: (fileInfo?: any) => void;
  36. };
  37. }
  38. interface IDoRequestOptions {
  39. // 是否显示确认对话框,默认 true
  40. confirm?: boolean;
  41. // 是否自动刷新表格,默认 true
  42. reload?: boolean;
  43. // 是否自动清空选择,默认 true
  44. clearSelection?: boolean;
  45. }
  46. /**
  47. * listPage页面公共方法
  48. *
  49. * @param options
  50. */
  51. export function useListPage(options: ListPageOptions) {
  52. const $message = useMessage();
  53. let $design = {} as ReturnType<typeof useDesign>;
  54. if (options.designScope) {
  55. $design = useDesign(options.designScope);
  56. }
  57. const tableContext = useListTable(options.tableProps);
  58. const [, { getForm, reload, setLoading }, { selectedRowKeys }] = tableContext;
  59. // 导出 excel
  60. async function onExportXls() {
  61. //update-begin---author:wangshuai ---date:20220411 for:导出新增自定义参数------------
  62. let { url, name, params } = options?.exportConfig ?? {};
  63. let realUrl = typeof url === 'function' ? url() : url;
  64. if (realUrl) {
  65. let title = typeof name === 'function' ? name() : name;
  66. //update-begin-author:taoyan date:20220507 for: erp代码生成 子表 导出报错,原因未知-
  67. let paramsForm: any = {};
  68. try {
  69. //update-begin-author:liusq---date:2025-03-20--for: [QQYUN-11627]代码生成原生表单,数据导出,前端报错,并且范围参数没有转换 #7962
  70. //当useSearchFor不等于false的时候,才去触发validate
  71. if (options?.tableProps?.useSearchForm !== false) {
  72. paramsForm = await getForm().validate();
  73. console.log('paramsForm', paramsForm);
  74. }
  75. //update-end-author:liusq---date:2025-03-20--for:[QQYUN-11627]代码生成原生表单,数据导出,前端报错,并且范围参数没有转换 #7962
  76. } catch (e) {
  77. console.warn(e);
  78. }
  79. //update-end-author:taoyan date:20220507 for: erp代码生成 子表 导出报错,原因未知-
  80. //update-begin-author:liusq date:20230410 for:[/issues/409]导出功能没有按排序结果导出,设置导出默认排序,创建时间倒序
  81. if (!paramsForm?.column) {
  82. Object.assign(paramsForm, { column: 'createTime', order: 'desc' });
  83. }
  84. //update-begin-author:liusq date:20230410 for: [/issues/409]导出功能没有按排序结果导出,设置导出默认排序,创建时间倒序
  85. //如果参数不为空,则整合到一起
  86. //update-begin-author:taoyan date:20220507 for: erp代码生成 子表 导出动态设置mainId
  87. if (params) {
  88. //update-begin-author:liusq---date:2025-03-20--for: [QQYUN-11627]代码生成原生表单,数据导出,前端报错,并且范围参数没有转换 #7962
  89. const realParams = isFunction(params) ? await params() : { ...(params || {}) };
  90. //update-end-author:liusq---date:2025-03-20--for:[QQYUN-11627]代码生成原生表单,数据导出,前端报错,并且范围参数没有转换 #7962
  91. Object.keys(realParams).map((k) => {
  92. let temp = (realParams as object)[k];
  93. if (temp) {
  94. paramsForm[k] = unref(temp);
  95. }
  96. });
  97. }
  98. //update-end-author:taoyan date:20220507 for: erp代码生成 子表 导出动态设置mainId
  99. if (selectedRowKeys.value && selectedRowKeys.value.length > 0) {
  100. paramsForm['selections'] = selectedRowKeys.value.join(',');
  101. }
  102. return handleExportXls(title as string, realUrl, filterObj(paramsForm));
  103. //update-end---author:wangshuai ---date:20220411 for:导出新增自定义参数--------------
  104. } else {
  105. $message.createMessage.warn('没有传递 exportConfig.url 参数');
  106. return Promise.reject();
  107. }
  108. }
  109. // 导入 excel
  110. function onImportXls(file) {
  111. let { url, success } = options?.importConfig ?? {};
  112. //update-begin-author:taoyan date:20220507 for: erp代码生成 子表 导入地址是动态的
  113. let realUrl = typeof url === 'function' ? url() : url;
  114. if (realUrl) {
  115. return handleImportXls(file, realUrl, success || reload);
  116. //update-end-author:taoyan date:20220507 for: erp代码生成 子表 导入地址是动态的
  117. } else {
  118. $message.createMessage.warn('没有传递 importConfig.url 参数');
  119. return Promise.reject();
  120. }
  121. }
  122. /**
  123. * 通用请求处理方法,可自动刷新表格,自动清空选择
  124. * @param api 请求api
  125. * @param options 是否显示确认框
  126. */
  127. function doRequest(api: () => Promise<any>, options?: IDoRequestOptions) {
  128. return new Promise((resolve, reject) => {
  129. const execute = async () => {
  130. try {
  131. setLoading(true);
  132. const res = await api();
  133. if (options?.reload ?? true) {
  134. reload();
  135. }
  136. if (options?.clearSelection ?? true) {
  137. selectedRowKeys.value = [];
  138. }
  139. resolve(res);
  140. } catch (e) {
  141. reject(e);
  142. } finally {
  143. setLoading(false);
  144. }
  145. };
  146. if (options?.confirm ?? true) {
  147. $message.createConfirm({
  148. iconType: 'warning',
  149. title: '删除',
  150. content: '确定要删除吗?',
  151. onOk: () => execute(),
  152. onCancel: () => reject(),
  153. });
  154. } else {
  155. execute();
  156. }
  157. });
  158. }
  159. /** 执行单个删除操作 */
  160. function doDeleteRecord(api: () => Promise<any>) {
  161. return doRequest(api, { confirm: false, clearSelection: false });
  162. }
  163. return {
  164. ...$design,
  165. ...$message,
  166. onExportXls,
  167. onImportXls,
  168. doRequest,
  169. doDeleteRecord,
  170. tableContext,
  171. };
  172. }
  173. // 定义表格所需参数
  174. type TableProps = Partial<DynamicProps<BasicTableProps>>;
  175. type UseTableMethod = TableActionType & {
  176. getForm: () => FormActionType;
  177. };
  178. /**
  179. * useListTable 列表页面标准表格参数
  180. *
  181. * @param tableProps 表格参数
  182. */
  183. export function useListTable(tableProps: TableProps): [
  184. (instance: TableActionType, formInstance: UseTableMethod) => void,
  185. TableActionType & {
  186. getForm: () => FormActionType;
  187. },
  188. {
  189. rowSelection: any;
  190. selectedRows: Ref<Recordable[]>;
  191. selectedRowKeys: Ref<any[]>;
  192. },
  193. ] {
  194. // 自适应列配置
  195. const adaptiveColProps: Partial<ColEx> = {
  196. xs: 24, // <576px
  197. sm: 12, // ≥576px
  198. md: 12, // ≥768px
  199. lg: 8, // ≥992px
  200. xl: 8, // ≥1200px
  201. xxl: 6, // ≥1600px
  202. };
  203. const defaultTableProps: TableProps = {
  204. rowKey: 'id',
  205. // 使用查询条件区域
  206. useSearchForm: true,
  207. // 查询条件区域配置
  208. formConfig: {
  209. // 紧凑模式
  210. compact: true,
  211. // label默认宽度
  212. // labelWidth: 120,
  213. // 按下回车后自动提交
  214. autoSubmitOnEnter: true,
  215. // 默认 row 配置
  216. rowProps: { gutter: 8 },
  217. // 默认 col 配置
  218. baseColProps: {
  219. ...adaptiveColProps,
  220. },
  221. labelCol: {
  222. xs: 24,
  223. sm: 8,
  224. md: 6,
  225. lg: 8,
  226. xl: 6,
  227. xxl: 6,
  228. },
  229. wrapperCol: {},
  230. // 是否显示 展开/收起 按钮
  231. showAdvancedButton: true,
  232. // 超过指定列数默认折叠
  233. autoAdvancedCol: 3,
  234. // 操作按钮配置
  235. actionColOptions: {
  236. ...adaptiveColProps,
  237. style: { textAlign: 'left' },
  238. },
  239. },
  240. // 斑马纹
  241. striped: false,
  242. // 是否可以自适应高度
  243. canResize: true,
  244. // 表格最小高度
  245. // update-begin--author:liaozhiyang---date:20240603---for【TV360X-861】列表查询区域不可往上滚动
  246. minHeight: 300,
  247. // update-end--author:liaozhiyang---date:20240603---for【TV360X-861】列表查询区域不可往上滚动
  248. // 点击行选中
  249. clickToRowSelect: false,
  250. // 是否显示边框
  251. bordered: true,
  252. // 是否显示序号列
  253. showIndexColumn: false,
  254. // 显示表格设置
  255. showTableSetting: true,
  256. // 表格全屏设置
  257. tableSetting: {
  258. fullScreen: false,
  259. },
  260. // 是否显示操作列
  261. showActionColumn: true,
  262. // 操作列
  263. actionColumn: {
  264. width: 120,
  265. title: '操作',
  266. //是否锁定操作列取值 right ,left,false
  267. fixed: false,
  268. dataIndex: 'action',
  269. slots: { customRender: 'action' },
  270. },
  271. };
  272. // 合并用户个性化配置
  273. if (tableProps) {
  274. //update-begin---author:wangshuai---date:2024-04-28---for:【issues/6180】前端代码配置表变查询条件显示列不生效---
  275. if (tableProps.formConfig) {
  276. setTableProps(tableProps.formConfig);
  277. }
  278. //update-end---author:wangshuai---date:2024-04-28---for:【issues/6180】前端代码配置表变查询条件显示列不生效---
  279. // merge 方法可深度合并对象
  280. merge(defaultTableProps, tableProps);
  281. }
  282. // 发送请求之前调用的方法
  283. function beforeFetch(params) {
  284. // 默认以 createTime 降序排序
  285. return Object.assign({ column: 'createTime', order: 'desc' }, params);
  286. }
  287. // 合并方法
  288. Object.assign(defaultTableProps, { beforeFetch });
  289. if (typeof tableProps.beforeFetch === 'function') {
  290. defaultTableProps.beforeFetch = function (params) {
  291. params = beforeFetch(params);
  292. // @ts-ignore
  293. tableProps.beforeFetch(params);
  294. return params;
  295. };
  296. }
  297. // 当前选择的行
  298. const selectedRowKeys = ref<any[]>([]);
  299. // 选择的行记录
  300. const selectedRows = ref<Recordable[]>([]);
  301. // 表格选择列配置
  302. const rowSelection: any = tableProps?.rowSelection ?? {};
  303. const defaultRowSelection = reactive({
  304. ...rowSelection,
  305. type: rowSelection.type ?? 'checkbox',
  306. // 选择列宽度,默认 50
  307. columnWidth: rowSelection.columnWidth ?? 50,
  308. selectedRows: selectedRows,
  309. selectedRowKeys: selectedRowKeys,
  310. onChange(...args) {
  311. selectedRowKeys.value = args[0];
  312. selectedRows.value = args[1];
  313. if (typeof rowSelection.onChange === 'function') {
  314. rowSelection.onChange(...args);
  315. }
  316. },
  317. });
  318. delete defaultTableProps.rowSelection;
  319. /**
  320. * 设置表格参数
  321. *
  322. * @param formConfig
  323. */
  324. function setTableProps(formConfig: any) {
  325. const replaceAttributeArray: string[] = ['baseColProps', 'labelCol'];
  326. for (let item of replaceAttributeArray) {
  327. if (formConfig && formConfig[item]) {
  328. if (defaultTableProps.formConfig) {
  329. let defaultFormConfig: any = defaultTableProps.formConfig;
  330. defaultFormConfig[item] = formConfig[item];
  331. }
  332. formConfig[item] = {};
  333. }
  334. }
  335. }
  336. return [
  337. ...useTable(defaultTableProps),
  338. {
  339. selectedRows,
  340. selectedRowKeys,
  341. rowSelection: defaultRowSelection,
  342. },
  343. ];
  344. }