useColumns.ts 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. import type { BasicColumn, BasicTableProps, CellFormat, GetColumnsParams } from '../types/table';
  2. import type { PaginationProps } from '../types/pagination';
  3. import type { ComputedRef } from 'vue';
  4. import { computed, Ref, ref, reactive, toRaw, unref, watch } from 'vue';
  5. import { renderEditCell } from '../components/editable';
  6. import { usePermission } from '/@/hooks/web/usePermission';
  7. import { useI18n } from '/@/hooks/web/useI18n';
  8. import { isArray, isBoolean, isFunction, isMap, isString } from '/@/utils/is';
  9. import { cloneDeep, isEqual } from 'lodash-es';
  10. import { formatToDate } from '/@/utils/dateUtil';
  11. import { ACTION_COLUMN_FLAG, DEFAULT_ALIGN, INDEX_COLUMN_FLAG, PAGE_SIZE } from '../const';
  12. function handleItem(item: BasicColumn, ellipsis: boolean) {
  13. const { key, dataIndex, children } = item;
  14. item.align = item.align || DEFAULT_ALIGN;
  15. if (ellipsis) {
  16. if (!key) {
  17. item.key = dataIndex;
  18. }
  19. if (!isBoolean(item.ellipsis)) {
  20. Object.assign(item, {
  21. ellipsis,
  22. });
  23. }
  24. }
  25. if (children && children.length) {
  26. handleChildren(children, !!ellipsis);
  27. }
  28. }
  29. function handleChildren(children: BasicColumn[] | undefined, ellipsis: boolean) {
  30. if (!children) return;
  31. children.forEach((item) => {
  32. const { children } = item;
  33. handleItem(item, ellipsis);
  34. handleChildren(children, ellipsis);
  35. });
  36. }
  37. function handleIndexColumn(
  38. propsRef: ComputedRef<BasicTableProps>,
  39. getPaginationRef: ComputedRef<boolean | PaginationProps>,
  40. columns: BasicColumn[],
  41. ) {
  42. const { t } = useI18n();
  43. const { showIndexColumn, indexColumnProps, isTreeTable } = unref(propsRef);
  44. let pushIndexColumns = false;
  45. if (unref(isTreeTable)) {
  46. return;
  47. }
  48. columns.forEach(() => {
  49. const indIndex = columns.findIndex((column) => column.flag === INDEX_COLUMN_FLAG);
  50. if (showIndexColumn) {
  51. pushIndexColumns = indIndex === -1;
  52. } else if (!showIndexColumn && indIndex !== -1) {
  53. columns.splice(indIndex, 1);
  54. }
  55. });
  56. if (!pushIndexColumns) return;
  57. const isFixedLeft = columns.some((item) => item.fixed === 'left');
  58. columns.unshift({
  59. flag: INDEX_COLUMN_FLAG,
  60. width: 50,
  61. title: t('component.table.index'),
  62. align: 'center',
  63. customRender: ({ index }) => {
  64. const getPagination = unref(getPaginationRef);
  65. if (isBoolean(getPagination)) {
  66. return `${index + 1}`;
  67. }
  68. const { current = 1, pageSize = PAGE_SIZE } = getPagination;
  69. return ((current < 1 ? 1 : current) - 1) * pageSize + index + 1;
  70. },
  71. ...(isFixedLeft
  72. ? {
  73. fixed: 'left',
  74. }
  75. : {}),
  76. ...indexColumnProps,
  77. });
  78. }
  79. function handleActionColumn(propsRef: ComputedRef<BasicTableProps>, columns: BasicColumn[]) {
  80. const { actionColumn } = unref(propsRef);
  81. if (!actionColumn) return;
  82. const hasIndex = columns.findIndex((column) => column.flag === ACTION_COLUMN_FLAG);
  83. if (hasIndex === -1) {
  84. columns.push({
  85. ...columns[hasIndex],
  86. fixed: 'right',
  87. ...actionColumn,
  88. flag: ACTION_COLUMN_FLAG,
  89. });
  90. }
  91. }
  92. export function useColumns(
  93. propsRef: ComputedRef<BasicTableProps>,
  94. getPaginationRef: ComputedRef<boolean | PaginationProps>,
  95. ) {
  96. const columnsRef = ref(unref(propsRef).columns) as unknown as Ref<BasicColumn[]>;
  97. let cacheColumns = unref(propsRef).columns;
  98. const getColumnsRef = computed(() => {
  99. const columns = cloneDeep(unref(columnsRef));
  100. handleIndexColumn(propsRef, getPaginationRef, columns);
  101. handleActionColumn(propsRef, columns);
  102. if (!columns) {
  103. return [];
  104. }
  105. const { ellipsis } = unref(propsRef);
  106. columns.forEach((item) => {
  107. const { customRender, slots } = item;
  108. handleItem(
  109. item,
  110. Reflect.has(item, 'ellipsis') ? !!item.ellipsis : !!ellipsis && !customRender && !slots,
  111. );
  112. });
  113. return columns;
  114. });
  115. function isIfShow(column: BasicColumn): boolean {
  116. const ifShow = column.ifShow;
  117. let isIfShow = true;
  118. if (isBoolean(ifShow)) {
  119. isIfShow = ifShow;
  120. }
  121. if (isFunction(ifShow)) {
  122. isIfShow = ifShow(column);
  123. }
  124. return isIfShow;
  125. }
  126. const { hasPermission } = usePermission();
  127. const getViewColumns = computed(() => {
  128. const viewColumns = sortFixedColumn(unref(getColumnsRef));
  129. const columns = cloneDeep(viewColumns);
  130. return columns
  131. .filter((column) => {
  132. return hasPermission(column.auth) && isIfShow(column);
  133. })
  134. .map((column) => {
  135. const { slots, customRender, format, edit, editRow, flag } = column;
  136. if (!slots || !slots?.title) {
  137. // column.slots = { title: `header-${dataIndex}`, ...(slots || {}) };
  138. column.customTitle = column.title;
  139. Reflect.deleteProperty(column, 'title');
  140. }
  141. const isDefaultAction = [INDEX_COLUMN_FLAG, ACTION_COLUMN_FLAG].includes(flag!);
  142. if (!customRender && format && !edit && !isDefaultAction) {
  143. column.customRender = ({ text, record, index }) => {
  144. return formatCell(text, format, record, index);
  145. };
  146. }
  147. // edit table
  148. if ((edit || editRow) && !isDefaultAction) {
  149. column.customRender = renderEditCell(column);
  150. }
  151. return reactive(column);
  152. });
  153. });
  154. watch(
  155. () => unref(propsRef).columns,
  156. (columns) => {
  157. columnsRef.value = columns;
  158. cacheColumns = columns?.filter((item) => !item.flag) ?? [];
  159. },
  160. );
  161. function setCacheColumnsByField(dataIndex: string | undefined, value: Partial<BasicColumn>) {
  162. if (!dataIndex || !value) {
  163. return;
  164. }
  165. cacheColumns.forEach((item) => {
  166. if (item.dataIndex === dataIndex) {
  167. Object.assign(item, value);
  168. return;
  169. }
  170. });
  171. }
  172. /**
  173. * set columns
  174. * @param columnList key|column
  175. */
  176. function setColumns(columnList: Partial<BasicColumn>[] | (string | string[])[]) {
  177. const columns = cloneDeep(columnList);
  178. if (!isArray(columns)) return;
  179. if (columns.length <= 0) {
  180. columnsRef.value = [];
  181. return;
  182. }
  183. const firstColumn = columns[0];
  184. const cacheKeys = cacheColumns.map((item) => item.dataIndex);
  185. if (!isString(firstColumn) && !isArray(firstColumn)) {
  186. columnsRef.value = columns as BasicColumn[];
  187. } else {
  188. const columnKeys = (columns as (string | string[])[]).map((m) => m.toString());
  189. const newColumns: BasicColumn[] = [];
  190. cacheColumns.forEach((item) => {
  191. newColumns.push({
  192. ...item,
  193. defaultHidden: !columnKeys.includes(item.dataIndex?.toString() || (item.key as string)),
  194. });
  195. });
  196. // Sort according to another array
  197. if (!isEqual(cacheKeys, columns)) {
  198. newColumns.sort((prev, next) => {
  199. return (
  200. columnKeys.indexOf(prev.dataIndex?.toString() as string) -
  201. columnKeys.indexOf(next.dataIndex?.toString() as string)
  202. );
  203. });
  204. }
  205. columnsRef.value = newColumns;
  206. }
  207. }
  208. function getColumns(opt?: GetColumnsParams) {
  209. const { ignoreIndex, ignoreAction, sort } = opt || {};
  210. let columns = toRaw(unref(getColumnsRef));
  211. if (ignoreIndex) {
  212. columns = columns.filter((item) => item.flag !== INDEX_COLUMN_FLAG);
  213. }
  214. if (ignoreAction) {
  215. columns = columns.filter((item) => item.flag !== ACTION_COLUMN_FLAG);
  216. }
  217. if (sort) {
  218. columns = sortFixedColumn(columns);
  219. }
  220. return columns;
  221. }
  222. function getCacheColumns() {
  223. return cacheColumns;
  224. }
  225. return {
  226. getColumnsRef,
  227. getCacheColumns,
  228. getColumns,
  229. setColumns,
  230. getViewColumns,
  231. setCacheColumnsByField,
  232. };
  233. }
  234. function sortFixedColumn(columns: BasicColumn[]) {
  235. const fixedLeftColumns: BasicColumn[] = [];
  236. const fixedRightColumns: BasicColumn[] = [];
  237. const defColumns: BasicColumn[] = [];
  238. for (const column of columns) {
  239. if (column.fixed === 'left') {
  240. fixedLeftColumns.push(column);
  241. continue;
  242. }
  243. if (column.fixed === 'right') {
  244. fixedRightColumns.push(column);
  245. continue;
  246. }
  247. defColumns.push(column);
  248. }
  249. return [...fixedLeftColumns, ...defColumns, ...fixedRightColumns].filter(
  250. (item) => !item.defaultHidden,
  251. );
  252. }
  253. // format cell
  254. export function formatCell(text: string, format: CellFormat, record: Recordable, index: number) {
  255. if (!format) {
  256. return text;
  257. }
  258. // custom function
  259. if (isFunction(format)) {
  260. return format(text, record, index);
  261. }
  262. try {
  263. // date type
  264. const DATE_FORMAT_PREFIX = 'date|';
  265. if (isString(format) && format.startsWith(DATE_FORMAT_PREFIX) && text) {
  266. const dateFormat = format.replace(DATE_FORMAT_PREFIX, '');
  267. if (!dateFormat) {
  268. return text;
  269. }
  270. return formatToDate(text, dateFormat);
  271. }
  272. // Map
  273. if (isMap(format)) {
  274. return format.get(text);
  275. }
  276. } catch (error) {
  277. return text;
  278. }
  279. }