UNPKG

@opensig/opendesign

Version:

653 lines (652 loc) 30.9 kB
import { defineComponent, mergeModels, useSlots, computed, ref, toRefs, useModel, watch, shallowRef, provide, withDirectives, createElementBlock, openBlock, normalizeStyle, normalizeClass, unref, createCommentVNode, createVNode, createBlock, withCtx, createElementVNode, renderSlot, Fragment, renderList, resolveDynamicComponent, createSlots, mergeProps, toDisplayString, createTextVNode, reactive, markRaw, nextTick } from "vue"; import { useElementBounding, refDebounced, useCssVar } from "@vueuse/core"; import { isNumeric, isNumber, isNil, isFunction, isArray } from "../_utils/is.mjs"; import { checkElementOverflow, getCssVariable, findClosestElementWithClass, isOverflown } from "../_utils/dom.mjs"; import "../hooks/use-theme.mjs"; import "../_utils/global.mjs"; import { getRenderableComponent } from "../_utils/vue-utils.mjs"; import { vOnResize } from "../directives/on-resize.mjs"; import { debounceRAF, getValueByPath, setValueByPath } from "../_utils/helper.mjs"; import { IconInfoTip, IconLoading } from "../_utils/icons.mjs"; import { OScroller } from "../scrollbar/index.mjs"; import { OCheckbox } from "../checkbox/index.mjs"; import { OPopover } from "../popover/index.mjs"; import "../table/index.mjs"; import { useTableCommon } from "../table/useTableCommon.mjs"; import { dataTableProps, DataTableSortMethod } from "./types.mjs"; import { getIsLevelExpandable, getColumnPosition } from "./utils.mjs"; import _sfc_main$1 from "./TableColGroup.vue.mjs"; import _sfc_main$4 from "./TableRow.vue.mjs"; import _sfc_main$2 from "./TableColumnFilter.vue.mjs"; import _sfc_main$3 from "./TableColumnSorter.vue.mjs"; import { dataTableInjectKey, dataTableRowInjectKey } from "./provide.mjs"; import { useDataColumn } from "./use-data-column.mjs"; import { DEFAULT_CELL_LAST_COL_MARKER, DEFAULT_CELL_FIRST_COL_MARKER } from "../table/useTableMeta.mjs"; const _hoisted_1 = { key: 0, class: "o-data-table-header-divider-h" }; const _hoisted_2 = { key: 1, class: "o-data-table-left-shadow" }; const _hoisted_3 = ["colspan", "rowspan", "data-cell-index"]; const _hoisted_4 = { class: "o-table-cell__inner" }; const _hoisted_5 = { key: 1, class: "o-table-row-icon-placeholder" }; const _hoisted_6 = { class: "o-table-cell__inner-content" }; const _hoisted_7 = ["onMousedown"]; const _hoisted_8 = { key: 0, class: "o-table-column-resizer__indicator" }; const _hoisted_9 = { key: 0, class: "empty-placeholder" }; const _hoisted_10 = { key: 2, class: "o-table-loading-wrap" }; const _hoisted_11 = { class: "o-table-loading-label" }; const _hoisted_12 = { key: 3, class: "o-table-tip-wrap" }; const _hoisted_13 = { class: "o-table-empty-label" }; const _hoisted_14 = { key: 4, class: "o-data-table-right-shadow" }; const _sfc_main = /* @__PURE__ */ defineComponent({ __name: "ODataTable", props: /* @__PURE__ */ mergeModels(dataTableProps, { "expanded-row-keys": { default: () => reactive([]) }, "expanded-row-keysModifiers": {}, "conditions": { /** * @important 兜底如果外面没有传值的情况 */ default: () => reactive({}) }, "conditionsModifiers": {}, "sortSequence": { /** * @important 兜底如果外面没有传值的情况 */ default: () => reactive([]) }, "sortSequenceModifiers": {}, "selectedKeys": { default: () => reactive([]) }, "selectedKeysModifiers": {} }), emits: /* @__PURE__ */ mergeModels(["condition-update", "sort-update", "update:selected-keys", "selection", "selection-change", "selection-all", "load-children", "column-resize"], ["update:expanded-row-keys", "update:conditions", "update:sortSequence", "update:selectedKeys"]), setup(__props, { expose: __expose, emit: __emit }) { const props = __props; const emits = __emit; const slots = useSlots(); const tdSlotNames = computed(() => Object.keys(slots).filter((name) => name.startsWith("td_"))); const rootRef = ref(); const { width: containerBoundingWidth } = useElementBounding(rootRef); const containerWidth = refDebounced( computed(() => { var _a; void containerBoundingWidth.value; return ((_a = rootRef.value) == null ? void 0 : _a.clientWidth) ?? 0; }) ); const headerRef = ref(); const { height: headerTableHeight } = useElementBounding(headerRef); const tableTextSize = useCssVar("--table-text-size", headerRef); const tableTextHeight = useCssVar("--table-text-height", headerRef); const tableEl = ref(); const overflowState = ref(); const checkTableOverflow = debounceRAF(() => { if (!tableEl.value) { return; } overflowState.value = checkElementOverflow({ element: tableEl.value, parentElement: rootRef.value, // 由于右固定列与前一列边框重合,宽容两个边框宽度 threshold: Number.parseFloat(getCssVariable("--table-border-width", rootRef.value)) * 2 }); }); const getRowKey = (row, rowIndex) => { if (isFunction(props.rowKey)) { return props.rowKey(row) ?? rowIndex; } return getValueByPath(row, props.rowKey) ?? rowIndex; }; const { emptyLabel, loadingLabel, borderClass, handleMouseOver, clearHighlight, handleTouchStart } = useTableCommon({ ...toRefs(props), tableEl }); const { dataColumnMap, dataColumns, groupColumns, isBodyCellRemoved, isLastLeftFixedCell, isFirstRightFixedCell, hasLeftFixedColumn, hasRightFixedColumn, handleColumnResizerMousedown, resizingColumnKey } = useDataColumn({ ...toRefs(props), tableEl, containerWidth }); const setThRef = (el, column) => { if (!el) { return; } column.thRef = markRaw(el); }; const expandedRowKeys = useModel(__props, "expanded-row-keys"); const hasExpandSlot = computed(() => !!slots.expand); const isLevelExpandable = computed(() => getIsLevelExpandable({ list: props.data, hasExpandSlot, expandMethod: props.expandMethod })); const conditions = useModel(__props, "conditions"); const sortSequence = useModel(__props, "sortSequence"); const getTableFilterValue = (key) => { return getValueByPath(conditions.value, key); }; const handleTableFilterChange = (key, newVal) => { setValueByPath(conditions.value, key, newVal); emits("condition-update", { key, newVal }); }; const getTableSorterValue = (key) => { return getValueByPath(conditions.value, key); }; const sortKeys = computed(() => Array.from(new Set(dataColumns.value.filter((v) => v.sortKey).map((v) => v.sortKey)))); const handleTableSorterChange = (key, newVal) => { if (!key) { return; } let _sortSequence = [...sortSequence.value]; if (props.sortMode === "single") { sortKeys.value.forEach((_key) => { setValueByPath(conditions.value, _key, DataTableSortMethod.NA); }); _sortSequence = [key]; } setValueByPath(conditions.value, key, newVal); if (newVal === DataTableSortMethod.NA) { _sortSequence = _sortSequence.filter((k) => k !== key); } else if (!_sortSequence.includes(key)) { _sortSequence.push(key); } sortSequence.value = _sortSequence; emits("condition-update"); emits("sort-update", { key, newVal, sortSequence: [..._sortSequence] }); }; const rowKeyMap = computed(() => { var _a; const _map = /* @__PURE__ */ new Map(); const traverse = ({ row, rowIndex, ancestorRowKeys, setDescendant }) => { const rowKey = getRowKey(row, rowIndex); setDescendant == null ? void 0 : setDescendant(rowKey); const record = { row, rowIndex, disabled: isNil(props.disabledProp) ? false : !!getValueByPath(row, props.disabledProp), ancestorRowKeys, descendantRowKeys: [] }; const _setDescendant = (descendant) => { record.descendantRowKeys.push(descendant); setDescendant == null ? void 0 : setDescendant(descendant); }; if (isArray(row.children)) { row.children.forEach((v, i) => traverse({ row: v, rowIndex: i, ancestorRowKeys: [rowKey, ...ancestorRowKeys], setDescendant: _setDescendant })); } _map.set(rowKey, record); }; (_a = props.data) == null ? void 0 : _a.forEach((row, rowIndex) => traverse({ row, rowIndex, ancestorRowKeys: [] })); return _map; }); const allRowKeys = computed(() => { var _a; return ((_a = props.data) == null ? void 0 : _a.reduce((prev, row, rowIndex) => { var _a2; const rowKey = getRowKey(row, rowIndex); return [...prev, rowKey, ...((_a2 = rowKeyMap.value.get(rowKey)) == null ? void 0 : _a2.descendantRowKeys) || []]; }, [])) || []; }); const toFilteredSelectable = (arr) => { return arr.filter((v) => { var _a; return !((_a = rowKeyMap.value.get(v)) == null ? void 0 : _a.disabled); }); }; const selectableRowKeys = computed(() => toFilteredSelectable(allRowKeys.value)); const selectedKeys = useModel(__props, "selectedKeys"); const allChecked = ref([]); const indeterminate = computed(() => { return Boolean( selectedKeys.value.length && selectableRowKeys.value.some((v) => !selectedKeys.value.includes(v)) && // 兼容数据分页时,selectedKeys中包含非data的key(可能来自于其他分页) selectableRowKeys.value.some((v) => selectedKeys.value.includes(v)) ); }); const selectionChangedBySelectAll = ref(false); watch( () => [selectedKeys.value, selectableRowKeys.value], () => { if (selectionChangedBySelectAll.value) { selectionChangedBySelectAll.value = false; return; } if (selectedKeys.value.length === selectableRowKeys.value.length) { allChecked.value = [1]; } else if (!selectedKeys.value.length) { allChecked.value = []; } }, { immediate: true } ); const handleTableSelection = (key, newVal) => { emits("selection", { key, selected: !!newVal.length }); }; const handleSelectionAll = (newVal) => { selectionChangedBySelectAll.value = true; emits("selection-all", !!newVal.length); const prev = selectedKeys.value; selectedKeys.value = newVal.length ? [...selectableRowKeys.value] : []; emits("selection-change", { prev, cur: selectedKeys.value }); }; const popoverVisible = ref(false); const popoverTarget = shallowRef(); const popoverContent = ref(); const popoverKey = ref(); const handleTableMouseover = async (e) => { const cellTarget = findClosestElementWithClass(e.target, "o-table-cell-tooltip", tableEl.value); const cellInnerContent = cellTarget == null ? void 0 : cellTarget.querySelector(":scope > .o-table-cell__inner > .o-table-cell__inner-content"); if (cellTarget && cellInnerContent) { popoverKey.value = cellTarget.dataset.cellIndex; popoverTarget.value = cellInnerContent; } if (!cellTarget || cellTarget === tableEl.value || !cellInnerContent || !isOverflown(cellInnerContent)) { popoverVisible.value = false; popoverTarget.value = void 0; popoverContent.value = void 0; return; } await nextTick(); popoverContent.value = cellInnerContent.innerText; popoverVisible.value = true; }; provide(dataTableInjectKey, { ...toRefs(props), getRowKey, containerWidth, dataColumnMap, dataColumns, groupColumns, hasExpandSlot, expandedRowKeys, isBodyCellRemoved, isLastLeftFixedCell, isFirstRightFixedCell, rowKeyMap, allRowKeys, toFilteredSelectable, selectedKeys, handleTableSelection, handleLoadChildren(payload) { emits("load-children", payload); } }); provide(dataTableRowInjectKey, { isLevelExpandable }); __expose({ getRowKey, dataColumnMap, dataColumns, groupColumns, /** * @zh-CN 全选 * @en-US Select all * @since 1.2.2 */ selectAll() { selectedKeys.value = [...selectableRowKeys.value]; }, /** * @zh-CN 清空全选 * @en-US Clear all selections * @since 1.2.2 */ clearAll: () => selectedKeys.value = [], /** * @zh-CN 展开全部 * @en-US Expand all rows * @since 1.2.2 */ expandAll() { expandedRowKeys.value = [...allRowKeys.value]; }, /** * @zh-CN 收起全部 * @en-US Fold all rows * @since 1.2.2 */ foldAll: () => expandedRowKeys.value = [] }); return (_ctx, _cache) => { var _a, _b, _c, _d, _e; return withDirectives((openBlock(), createElementBlock( "div", { ref_key: "rootRef", ref: rootRef, class: normalizeClass([ "o-table", `o-table-${props.size}`, `o-table-header-${props.headerStyle}`, "o-data-table", { "o-table-stripe": props.stripe, "is-overflow-left": (_a = overflowState.value) == null ? void 0 : _a.isOverflowLeft, "is-overflow-right": (_b = overflowState.value) == null ? void 0 : _b.isOverflowRight, "is-overflow-top": (_c = overflowState.value) == null ? void 0 : _c.isOverflowTop }, ...unref(borderClass) ]), style: normalizeStyle({ "--table-header-height": unref(headerTableHeight), "--table-height": unref(isNumeric)(props.height) ? props.height + "px" : props.height, "--table-max-height": unref(isNumeric)(props.maxHeight) ? props.maxHeight + "px" : props.maxHeight }) }, [ props.showHeader && props.headerStyle === "split-line" ? (openBlock(), createElementBlock("div", _hoisted_1)) : createCommentVNode("v-if", true), !unref(hasLeftFixedColumn) && !props.loading && props.data.length ? (openBlock(), createElementBlock("div", _hoisted_2)) : createCommentVNode("v-if", true), createVNode(unref(OScroller), { class: "o-table-scroller", "wrap-class": "o-table-wrap", "bar-class": "o-table-scroll-bar", size: props.size, "disabled-x": props.loading || !props.data.length, "show-type": "always", "auto-update-on-scroll-size": "", onScroll: unref(checkTableOverflow) }, { default: withCtx(() => { var _a2; return [ createElementVNode( "table", { ref_key: "tableEl", ref: tableEl, class: "o-table-inner-table", style: normalizeStyle({ minWidth: unref(isNumeric)(props.minTableWidth) ? `${props.minTableWidth}px` : props.minTableWidth }), onMouseover: handleTableMouseover }, [ _cache[4] || (_cache[4] = createElementVNode( "caption", null, null, -1 /* CACHED */ )), createVNode(_sfc_main$1), props.showHeader ? (openBlock(), createElementBlock( "thead", { key: 0, ref_key: "headerRef", ref: headerRef, class: "o-table-header" }, [ renderSlot(_ctx.$slots, "header", { columns: unref(dataColumns), groupColumns: unref(groupColumns) }, () => [ (openBlock(true), createElementBlock( Fragment, null, renderList(unref(groupColumns), (groupColumn, groupIndex) => { var _a3; return openBlock(), createElementBlock("tr", { key: (_a3 = groupColumn[0]) == null ? void 0 : _a3.key, class: "o-table-row o-table-header-row" }, [ (openBlock(true), createElementBlock( Fragment, null, renderList(groupColumn, (column, colIndex) => { var _a4, _b2; return openBlock(), createElementBlock( Fragment, { key: column.key }, [ !column.headerHidden ? (openBlock(), createElementBlock("th", { key: 0, ref_for: true, ref: (el) => setThRef(el, column), colspan: column.colSpan || column.customColSpan, rowspan: column.rowSpan, class: normalizeClass({ "o-table-cell": true, "o-table-header-cell": true, "o-table-column-as-header": column.asHeader, "o-table-cell-tooltip": column.showHeaderOverflowToolTip !== false && column.showHeaderOverflowToolTip !== 0, "o-table-cell-wrappable": unref(isNumber)(column.showHeaderOverflowToolTip) && column.showHeaderOverflowToolTip > 1, "o-table-last-header-row-cell": !((_a4 = column.children) == null ? void 0 : _a4.length), "o-table-cell-fixed": column.fixed, "o-table-cell-fixed-left": column.fixed === "left", "o-table-cell-fixed-right": column.fixed === "right", "o-table-cell-last-left-fixed": column.isLastLeftFixedCol, "o-table-cell-first-right-fixed": column.isFirstRightFixedCol, [unref(DEFAULT_CELL_FIRST_COL_MARKER)]: column.isFirstCol, [unref(DEFAULT_CELL_LAST_COL_MARKER)]: column.isLastCol }), style: normalizeStyle({ ...unref(getColumnPosition)({ column, dataColumns: unref(dataColumns), groupColumns: unref(groupColumns), border: props.border, isHeader: true }), "--cell-max-row": unref(isNumber)(column.showHeaderOverflowToolTip) ? column.showHeaderOverflowToolTip : 1 }), "data-cell-index": `th_${groupIndex}_${colIndex}` }, [ createElementVNode("span", _hoisted_4, [ column.isFirstCol && props.selection ? (openBlock(), createBlock(unref(OCheckbox), { key: 0, modelValue: allChecked.value, "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => allChecked.value = $event), indeterminate: indeterminate.value, value: 1, class: "o-table-row-checkbox", onChange: handleSelectionAll }, null, 8, ["modelValue", "indeterminate"])) : createCommentVNode("v-if", true), column.isFirstCol && !props.selection && isLevelExpandable.value.expandable ? (openBlock(), createElementBlock("span", _hoisted_5)) : createCommentVNode("v-if", true), createElementVNode("span", _hoisted_6, [ renderSlot(_ctx.$slots, `th_${column.key}`, { column }, () => [ (openBlock(), createBlock(resolveDynamicComponent(unref(getRenderableComponent)(column.label)))) ]) ]), column.filter ? (openBlock(), createBlock(_sfc_main$2, { key: 2, disabled: props.loading, column, "model-value": getTableFilterValue(column.key), style: normalizeStyle({ "--table-text-size": unref(tableTextSize), "--table-text-height": unref(tableTextHeight) }), "onUpdate:modelValue": (newVal) => handleTableFilterChange(column.key, newVal) }, null, 8, ["disabled", "column", "model-value", "style", "onUpdate:modelValue"])) : column.sortKey ? (openBlock(), createBlock(_sfc_main$3, { key: 3, disabled: props.loading, "model-value": getTableSorterValue(column.sortKey), "onUpdate:modelValue": (newVal) => handleTableSorterChange(column.sortKey, newVal) }, null, 8, ["disabled", "model-value", "onUpdate:modelValue"])) : createCommentVNode("v-if", true), column.description ? (openBlock(), createBlock( unref(OPopover), { key: 4, position: "top", "wrap-class": "o-table-tooltip-wrapper" }, { target: withCtx(() => [ createVNode(unref(IconInfoTip), { class: "o-data-table-info__trigger" }) ]), default: withCtx(() => [ (openBlock(), createBlock(resolveDynamicComponent(unref(getRenderableComponent)(column.description)))) ]), _: 2 /* DYNAMIC */ }, 1024 /* DYNAMIC_SLOTS */ )) : createCommentVNode("v-if", true) ]), createCommentVNode(" 如果可调整宽度,且没有合并单元格,则显示 "), props.columnResizable && (unref(isNil)(column.customColSpan) || column.customColSpan <= 1) && !((_b2 = column.children) == null ? void 0 : _b2.length) ? (openBlock(), createElementBlock("div", { key: 0, class: "o-table-column-resizer", onMousedown: (event) => unref(handleColumnResizerMousedown)({ event, column, colIndex }) }, [ unref(resizingColumnKey) === column.key ? (openBlock(), createElementBlock("div", _hoisted_8)) : createCommentVNode("v-if", true) ], 40, _hoisted_7)) : createCommentVNode("v-if", true) ], 14, _hoisted_3)) : createCommentVNode("v-if", true) ], 64 /* STABLE_FRAGMENT */ ); }), 128 /* KEYED_FRAGMENT */ )) ]); }), 128 /* KEYED_FRAGMENT */ )) ]) ], 512 /* NEED_PATCH */ )) : createCommentVNode("v-if", true), createElementVNode( "tbody", { class: "o-table-body", onMousemove: _cache[1] || (_cache[1] = //@ts-ignore (...args) => unref(handleMouseOver) && unref(handleMouseOver)(...args)), onMouseleave: _cache[2] || (_cache[2] = //@ts-ignore (...args) => unref(clearHighlight) && unref(clearHighlight)(...args)), onTouchstart: _cache[3] || (_cache[3] = //@ts-ignore (...args) => unref(handleTouchStart) && unref(handleTouchStart)(...args)) }, [ (openBlock(true), createElementBlock( Fragment, null, renderList(props.data, (row, rowIndex) => { return openBlock(), createBlock(_sfc_main$4, { key: getRowKey(row, rowIndex), row, "row-index": rowIndex, level: 0 }, createSlots({ _: 2 /* DYNAMIC */ }, [ renderList(tdSlotNames.value, (name) => { return { name, fn: withCtx((slotProps) => [ renderSlot(_ctx.$slots, name, mergeProps({ ref_for: true }, slotProps)) ]) }; }), slots.expand ? { name: "expand", fn: withCtx(() => [ renderSlot(_ctx.$slots, "expand", { row, rowIndex }) ]), key: "0" } : void 0 ]), 1032, ["row", "row-index"]); }), 128 /* KEYED_FRAGMENT */ )), props.loading || !((_a2 = props.data) == null ? void 0 : _a2.length) ? (openBlock(), createElementBlock("div", _hoisted_9)) : createCommentVNode("v-if", true) ], 32 /* NEED_HYDRATION */ ) ], 36 /* STYLE, NEED_HYDRATION */ ) ]; }), _: 3 /* FORWARDED */ }, 8, ["size", "disabled-x", "onScroll"]), props.loading ? (openBlock(), createElementBlock("div", _hoisted_10, [ renderSlot(_ctx.$slots, "loading", {}, () => [ createVNode(unref(IconLoading), { class: "o-rotating" }), createElementVNode( "div", _hoisted_11, toDisplayString(unref(loadingLabel)), 1 /* TEXT */ ) ]) ])) : !((_d = props.data) == null ? void 0 : _d.length) ? (openBlock(), createElementBlock("div", _hoisted_12, [ !props.loading ? renderSlot(_ctx.$slots, "empty", { key: 0 }, () => [ createElementVNode( "div", _hoisted_13, toDisplayString(unref(emptyLabel)), 1 /* TEXT */ ) ]) : createCommentVNode("v-if", true) ])) : createCommentVNode("v-if", true), !unref(hasRightFixedColumn) && !props.loading && props.data.length ? (openBlock(), createElementBlock("div", _hoisted_14)) : createCommentVNode("v-if", true), popoverVisible.value ? (openBlock(), createBlock(unref(OPopover), { key: popoverKey.value, visivle: "", target: popoverTarget.value, position: ((_e = popoverKey.value) == null ? void 0 : _e.startsWith("td")) ? "bottom" : "top", "wrap-class": "o-table-tooltip-wrapper" }, { default: withCtx(() => [ createTextVNode( toDisplayString(popoverContent.value), 1 /* TEXT */ ) ]), _: 1 /* STABLE */ }, 8, ["target", "position"])) : createCommentVNode("v-if", true) ], 6 /* CLASS, STYLE */ )), [ [unref(vOnResize), unref(checkTableOverflow)] ]); }; } }); export { _sfc_main as default };