|
@@ -1,7 +1,30 @@
|
|
|
<script lang="ts" setup generic="T">
|
|
<script lang="ts" setup generic="T">
|
|
|
import type { TableInstance, TableProps } from 'element-plus'
|
|
import type { TableInstance, TableProps } from 'element-plus'
|
|
|
-import { FilterPayload, SortField, SortOrder, TableContextKey } from './token'
|
|
|
|
|
-import { DefaultRow } from 'element-plus/es/components/table/src/table/defaults'
|
|
|
|
|
|
|
+import {
|
|
|
|
|
+ Comment,
|
|
|
|
|
+ Fragment,
|
|
|
|
|
+ Text,
|
|
|
|
|
+ cloneVNode,
|
|
|
|
|
+ computed,
|
|
|
|
|
+ nextTick,
|
|
|
|
|
+ provide,
|
|
|
|
|
+ ref,
|
|
|
|
|
+ useAttrs,
|
|
|
|
|
+ useSlots
|
|
|
|
|
+} from 'vue'
|
|
|
|
|
+import { TableContextKey } from './token'
|
|
|
|
|
+import type { ColumnFixed, ColumnSettingItem } from './token'
|
|
|
|
|
+import type { DefaultRow } from 'element-plus/es/components/table/src/table/defaults'
|
|
|
|
|
+import type { VNode } from 'vue'
|
|
|
|
|
+
|
|
|
|
|
+interface ColumnMeta {
|
|
|
|
|
+ key: string
|
|
|
|
|
+ label: string
|
|
|
|
|
+ action: boolean
|
|
|
|
|
+ configurable: boolean
|
|
|
|
|
+ fixed: ColumnFixed
|
|
|
|
|
+ children: ColumnMeta[]
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
interface Props
|
|
interface Props
|
|
|
extends /* @vue-ignore */ Partial<
|
|
extends /* @vue-ignore */ Partial<
|
|
@@ -9,21 +32,18 @@ interface Props
|
|
|
> {
|
|
> {
|
|
|
data: T[]
|
|
data: T[]
|
|
|
loading: boolean
|
|
loading: boolean
|
|
|
- handleQuery?: (payload?: FilterPayload) => void
|
|
|
|
|
- sortingFields?: SortField[]
|
|
|
|
|
- sortFn?: (prop: string, order: SortOrder | null) => void
|
|
|
|
|
customClass?: boolean
|
|
customClass?: boolean
|
|
|
showBorder?: boolean
|
|
showBorder?: boolean
|
|
|
|
|
+ columnMaxWidth?: number
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
const props = defineProps<Props>()
|
|
const props = defineProps<Props>()
|
|
|
-
|
|
|
|
|
-const emits = defineEmits<{
|
|
|
|
|
- 'update:sortingFields': [fields: SortField[]]
|
|
|
|
|
-}>()
|
|
|
|
|
-
|
|
|
|
|
const attrs = useAttrs()
|
|
const attrs = useAttrs()
|
|
|
|
|
+const slots = useSlots()
|
|
|
const tableRef = ref<TableInstance>()
|
|
const tableRef = ref<TableInstance>()
|
|
|
|
|
+const columnSettings = ref<ColumnSettingItem[]>([])
|
|
|
|
|
+const columnDefaultSettings = ref<ColumnSettingItem[]>([])
|
|
|
|
|
+const slotColumnSignature = ref('')
|
|
|
|
|
|
|
|
const defaultOptions: Partial<Props> = {
|
|
const defaultOptions: Partial<Props> = {
|
|
|
size: 'default',
|
|
size: 'default',
|
|
@@ -40,7 +60,13 @@ const defaultOptions: Partial<Props> = {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
const bindProps = computed(() => {
|
|
const bindProps = computed(() => {
|
|
|
- const { data, sortingFields, ...otherProps } = props
|
|
|
|
|
|
|
+ const {
|
|
|
|
|
+ data,
|
|
|
|
|
+ customClass: _customClass,
|
|
|
|
|
+ showBorder: _showBorder,
|
|
|
|
|
+ columnMaxWidth,
|
|
|
|
|
+ ...otherProps
|
|
|
|
|
+ } = props
|
|
|
|
|
|
|
|
return {
|
|
return {
|
|
|
...defaultOptions,
|
|
...defaultOptions,
|
|
@@ -50,44 +76,289 @@ const bindProps = computed(() => {
|
|
|
}
|
|
}
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
-const handleDefaultSort = (prop: string, order: SortOrder | null) => {
|
|
|
|
|
- const newFields = [...(props.sortingFields || [])]
|
|
|
|
|
|
|
+const safeData = computed(() => props.data || [])
|
|
|
|
|
+const safeLoading = computed(() => props.loading)
|
|
|
|
|
+const safeColumnMaxWidth = computed(() => {
|
|
|
|
|
+ const maxWidth = Number(props.columnMaxWidth)
|
|
|
|
|
+ return Number.isFinite(maxWidth) && maxWidth > 0 ? maxWidth : 360
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
|
|
+const forwardedSlots = computed(() => {
|
|
|
|
|
+ const { default: _default, ...restSlots } = slots
|
|
|
|
|
+ return restSlots
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
|
|
+const getPropValue = (props: Record<string, any> | null | undefined, ...keys: string[]) => {
|
|
|
|
|
+ if (!props) return undefined
|
|
|
|
|
+ for (const key of keys) {
|
|
|
|
|
+ if (props[key] !== undefined) return props[key]
|
|
|
|
|
+ }
|
|
|
|
|
+ return undefined
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
- const idx = newFields.findIndex((f) => f.field === prop)
|
|
|
|
|
|
|
+const toBoolean = (value: unknown) => value === true || value === ''
|
|
|
|
|
|
|
|
- if (order === null) {
|
|
|
|
|
- if (idx > -1) {
|
|
|
|
|
- newFields.splice(idx, 1)
|
|
|
|
|
|
|
+const normalizeFixed = (fixed: unknown): ColumnFixed => {
|
|
|
|
|
+ if (fixed === true || fixed === 'left') return 'left'
|
|
|
|
|
+ if (fixed === 'right') return 'right'
|
|
|
|
|
+ return false
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const isColumnNode = (node: VNode) => {
|
|
|
|
|
+ if (!node.type && typeof node.type !== 'object') return false
|
|
|
|
|
+ const nodeType = node.type as { __name?: string }
|
|
|
|
|
+ return nodeType.__name === 'ZmTableColumn'
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const canResolveColumnChildren = (node: VNode) => {
|
|
|
|
|
+ const nodeProps = node.props
|
|
|
|
|
+ return toBoolean(getPropValue(nodeProps, 'isParent'))
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const getColumnKey = (node: VNode, index: number, parentKey?: string) => {
|
|
|
|
|
+ const nodeProps = node.props
|
|
|
|
|
+ const key = getPropValue(nodeProps, 'columnKey', 'column-key')
|
|
|
|
|
+ const prop = getPropValue(nodeProps, 'prop')
|
|
|
|
|
+ const type = getPropValue(nodeProps, 'type')
|
|
|
|
|
+ const label = getPropValue(nodeProps, 'label')
|
|
|
|
|
+ const fallbackKey = parentKey ? `${parentKey}.column-${index}` : `column-${index}`
|
|
|
|
|
+ return String(key ?? prop ?? type ?? label ?? fallbackKey)
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const getColumnLabel = (node: VNode, index: number, parentKey?: string) => {
|
|
|
|
|
+ const label = getPropValue(node.props, 'label')
|
|
|
|
|
+ return String(label ?? getColumnKey(node, index, parentKey))
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const getColumnChildren = (node: VNode) => {
|
|
|
|
|
+ if (!canResolveColumnChildren(node)) return []
|
|
|
|
|
+ const children = node.children
|
|
|
|
|
+ if (children && typeof children === 'object' && 'default' in children) {
|
|
|
|
|
+ const defaultSlot = (children as { default?: unknown }).default
|
|
|
|
|
+ if (typeof defaultSlot !== 'function') return []
|
|
|
|
|
+ try {
|
|
|
|
|
+ const slotNodes = defaultSlot()
|
|
|
|
|
+ const normalizedNodes = Array.isArray(slotNodes) ? slotNodes : slotNodes ? [slotNodes] : []
|
|
|
|
|
+ return flattenSlotNodes(normalizedNodes as VNode[]).filter(isColumnNode)
|
|
|
|
|
+ } catch {
|
|
|
|
|
+ return []
|
|
|
}
|
|
}
|
|
|
- } else {
|
|
|
|
|
- if (idx > -1) {
|
|
|
|
|
- newFields[idx] = { ...newFields[idx], order }
|
|
|
|
|
- } else {
|
|
|
|
|
- newFields.push({ field: prop, order })
|
|
|
|
|
|
|
+ }
|
|
|
|
|
+ return []
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const getColumnMeta = (node: VNode, index: number, parentKey?: string): ColumnMeta => {
|
|
|
|
|
+ const key = getColumnKey(node, index, parentKey)
|
|
|
|
|
+ const action = toBoolean(getPropValue(node.props, 'action'))
|
|
|
|
|
+ const hideInColumnSettings = toBoolean(
|
|
|
|
|
+ getPropValue(node.props, 'hideInColumnSettings', 'hide-in-column-settings')
|
|
|
|
|
+ )
|
|
|
|
|
+ return {
|
|
|
|
|
+ key,
|
|
|
|
|
+ label: getColumnLabel(node, index, parentKey),
|
|
|
|
|
+ action,
|
|
|
|
|
+ configurable: !action && !hideInColumnSettings,
|
|
|
|
|
+ fixed: normalizeFixed(getPropValue(node.props, 'fixed')),
|
|
|
|
|
+ children: getColumnChildren(node)
|
|
|
|
|
+ .map((child, childIndex) => getColumnMeta(child, childIndex, key))
|
|
|
|
|
+ .filter((item) => item.configurable)
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const flattenSlotNodes = (nodes: Array<VNode | null | undefined>): VNode[] => {
|
|
|
|
|
+ return nodes.flatMap((node) => {
|
|
|
|
|
+ if (!node) return []
|
|
|
|
|
+ if (node.type === Fragment && Array.isArray(node.children)) {
|
|
|
|
|
+ return flattenSlotNodes(node.children as VNode[])
|
|
|
}
|
|
}
|
|
|
|
|
+ if (node.type === Comment || node.type === Text) return []
|
|
|
|
|
+ return [node]
|
|
|
|
|
+ })
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const syncColumnSettings = (nodes: VNode[]) => {
|
|
|
|
|
+ const metas = nodes
|
|
|
|
|
+ .map((node, index) => getColumnMeta(node, index))
|
|
|
|
|
+ .filter((item) => item.configurable)
|
|
|
|
|
+ const signature = JSON.stringify(metas)
|
|
|
|
|
+ if (signature === slotColumnSignature.value) return
|
|
|
|
|
+ slotColumnSignature.value = signature
|
|
|
|
|
+ nextTick(() => {
|
|
|
|
|
+ const defaults = metas.map(createDefaultColumnSetting)
|
|
|
|
|
+ columnDefaultSettings.value = defaults
|
|
|
|
|
+ columnSettings.value = mergeColumnSettings(defaults, columnSettings.value)
|
|
|
|
|
+ })
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const createDefaultColumnSetting = (meta: ColumnMeta): ColumnSettingItem => ({
|
|
|
|
|
+ key: meta.key,
|
|
|
|
|
+ label: meta.label,
|
|
|
|
|
+ visible: true,
|
|
|
|
|
+ fixed: meta.fixed,
|
|
|
|
|
+ children: meta.children.length ? meta.children.map(createDefaultColumnSetting) : undefined
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
|
|
+const mergeColumnSettings = (
|
|
|
|
|
+ defaults: ColumnSettingItem[],
|
|
|
|
|
+ current: ColumnSettingItem[]
|
|
|
|
|
+): ColumnSettingItem[] => {
|
|
|
|
|
+ const defaultMap = new Map(defaults.map((item) => [item.key, item]))
|
|
|
|
|
+ const currentMap = new Map(current.map((item) => [item.key, item]))
|
|
|
|
|
+ const existingItems = current
|
|
|
|
|
+ .filter((item) => defaultMap.has(item.key))
|
|
|
|
|
+ .map((item) => {
|
|
|
|
|
+ const defaultItem = defaultMap.get(item.key)!
|
|
|
|
|
+ return {
|
|
|
|
|
+ ...defaultItem,
|
|
|
|
|
+ visible: item.visible,
|
|
|
|
|
+ fixed: item.fixed,
|
|
|
|
|
+ children: defaultItem.children
|
|
|
|
|
+ ? mergeColumnSettings(defaultItem.children, item.children || [])
|
|
|
|
|
+ : undefined
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ const addedItems = defaults.filter((item) => !currentMap.has(item.key))
|
|
|
|
|
+ return [...existingItems, ...addedItems]
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const mapColumnSettings = (
|
|
|
|
|
+ items: ColumnSettingItem[],
|
|
|
|
|
+ mapper: (item: ColumnSettingItem) => ColumnSettingItem
|
|
|
|
|
+): ColumnSettingItem[] => {
|
|
|
|
|
+ return items.map((item) => {
|
|
|
|
|
+ const mappedItem = mapper(item)
|
|
|
|
|
+ return {
|
|
|
|
|
+ ...mappedItem,
|
|
|
|
|
+ children: mappedItem.children ? mapColumnSettings(mappedItem.children, mapper) : undefined
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const updateColumnVisible = (key: string, visible: boolean) => {
|
|
|
|
|
+ columnSettings.value = mapColumnSettings(columnSettings.value, (item) =>
|
|
|
|
|
+ item.key === key ? { ...item, visible } : item
|
|
|
|
|
+ )
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const updateColumnFixed = (key: string, fixed: ColumnFixed) => {
|
|
|
|
|
+ columnSettings.value = mapColumnSettings(columnSettings.value, (item) =>
|
|
|
|
|
+ item.key === key ? { ...item, fixed } : item
|
|
|
|
|
+ )
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const sortSettingsByKeys = (items: ColumnSettingItem[], keys: string[]) => {
|
|
|
|
|
+ const orderMap = new Map(keys.map((key, index) => [key, index]))
|
|
|
|
|
+ return [...items].sort((a, b) => {
|
|
|
|
|
+ const orderA = orderMap.get(a.key) ?? Number.MAX_SAFE_INTEGER
|
|
|
|
|
+ const orderB = orderMap.get(b.key) ?? Number.MAX_SAFE_INTEGER
|
|
|
|
|
+ return orderA - orderB
|
|
|
|
|
+ })
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const updateColumnOrder = (keys: string[], parentKey?: string) => {
|
|
|
|
|
+ if (!parentKey) {
|
|
|
|
|
+ columnSettings.value = sortSettingsByKeys(columnSettings.value, keys)
|
|
|
|
|
+ return
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- emits('update:sortingFields', newFields)
|
|
|
|
|
- props.handleQuery?.()
|
|
|
|
|
|
|
+ columnSettings.value = mapColumnSettings(columnSettings.value, (item) => {
|
|
|
|
|
+ if (item.key !== parentKey) return item
|
|
|
|
|
+
|
|
|
|
|
+ return {
|
|
|
|
|
+ ...item,
|
|
|
|
|
+ children: item.children ? sortSettingsByKeys(item.children, keys) : item.children
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-const safeSortingFields = computed(() => props.sortingFields || [])
|
|
|
|
|
-const safeData = computed(() => props.data || [])
|
|
|
|
|
-const safeLoading = computed(() => props.loading)
|
|
|
|
|
|
|
+const cloneColumnSettings = (items: ColumnSettingItem[]): ColumnSettingItem[] => {
|
|
|
|
|
+ return items.map((item) => ({
|
|
|
|
|
+ ...item,
|
|
|
|
|
+ children: item.children ? cloneColumnSettings(item.children) : undefined
|
|
|
|
|
+ }))
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
-provide(TableContextKey, {
|
|
|
|
|
- onQuery: (payload) => props.handleQuery?.(payload),
|
|
|
|
|
- onSort: (prop, order) => {
|
|
|
|
|
- if (props.sortFn) {
|
|
|
|
|
- props.sortFn(prop, order)
|
|
|
|
|
- } else {
|
|
|
|
|
- handleDefaultSort(prop, order)
|
|
|
|
|
|
|
+const resetColumnSettings = () => {
|
|
|
|
|
+ columnSettings.value = cloneColumnSettings(columnDefaultSettings.value)
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const applyColumnSetting = (node: VNode, setting: ColumnSettingItem) => {
|
|
|
|
|
+ const clonedNode = cloneVNode(
|
|
|
|
|
+ node,
|
|
|
|
|
+ {
|
|
|
|
|
+ columnKey: setting.key,
|
|
|
|
|
+ fixed: setting.fixed || undefined,
|
|
|
|
|
+ key: `${setting.key}-${setting.visible}-${setting.fixed || 'none'}`
|
|
|
|
|
+ },
|
|
|
|
|
+ true
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ const childNodes = getColumnChildren(node)
|
|
|
|
|
+ if (!childNodes.length || !setting.children?.length) return clonedNode
|
|
|
|
|
+
|
|
|
|
|
+ const originalChildren =
|
|
|
|
|
+ clonedNode.children && typeof clonedNode.children === 'object' ? clonedNode.children : {}
|
|
|
|
|
+
|
|
|
|
|
+ return {
|
|
|
|
|
+ ...clonedNode,
|
|
|
|
|
+ children: {
|
|
|
|
|
+ ...(originalChildren as Record<string, unknown>),
|
|
|
|
|
+ default: () => renderColumnNodes(childNodes, setting.children || [], setting.key)
|
|
|
}
|
|
}
|
|
|
- },
|
|
|
|
|
- // 关键:传递响应式的 data 和 sortingFields
|
|
|
|
|
|
|
+ } as VNode
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const isColumnVisible = (setting: ColumnSettingItem) => {
|
|
|
|
|
+ if (setting.visible === false) return false
|
|
|
|
|
+ if (!setting.children?.length) return true
|
|
|
|
|
+ return setting.children.some(isColumnVisible)
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const renderColumnNodes = (nodes: VNode[], settings: ColumnSettingItem[], parentKey?: string) => {
|
|
|
|
|
+ const settingMap = new Map(settings.map((item) => [item.key, item]))
|
|
|
|
|
+ const orderMap = new Map(settings.map((item, index) => [item.key, index]))
|
|
|
|
|
+ const sortedConfigurableNodes = nodes
|
|
|
|
|
+ .map((node, index) => ({ node, meta: getColumnMeta(node, index, parentKey) }))
|
|
|
|
|
+ .filter(({ meta }) => {
|
|
|
|
|
+ const setting = settingMap.get(meta.key)
|
|
|
|
|
+ return meta.configurable && setting && isColumnVisible(setting)
|
|
|
|
|
+ })
|
|
|
|
|
+ .sort((a, b) => {
|
|
|
|
|
+ const orderA = orderMap.get(a.meta.key) ?? Number.MAX_SAFE_INTEGER
|
|
|
|
|
+ const orderB = orderMap.get(b.meta.key) ?? Number.MAX_SAFE_INTEGER
|
|
|
|
|
+ return orderA - orderB
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ return nodes.flatMap((node, index) => {
|
|
|
|
|
+ const meta = getColumnMeta(node, index, parentKey)
|
|
|
|
|
+
|
|
|
|
|
+ if (!meta.configurable) return [node]
|
|
|
|
|
+
|
|
|
|
|
+ const nextColumn = sortedConfigurableNodes.shift()
|
|
|
|
|
+ if (!nextColumn) return []
|
|
|
|
|
+
|
|
|
|
|
+ const setting = settingMap.get(nextColumn.meta.key)
|
|
|
|
|
+ return setting ? [applyColumnSetting(nextColumn.node, setting)] : [nextColumn.node]
|
|
|
|
|
+ })
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const renderDefaultSlot = () => {
|
|
|
|
|
+ const nodes = flattenSlotNodes(slots.default?.() || [])
|
|
|
|
|
+ syncColumnSettings(nodes)
|
|
|
|
|
+ return renderColumnNodes(nodes, columnSettings.value)
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const TableDefaultSlot = () => renderDefaultSlot()
|
|
|
|
|
+
|
|
|
|
|
+provide(TableContextKey, {
|
|
|
data: safeData,
|
|
data: safeData,
|
|
|
- sortingFields: safeSortingFields,
|
|
|
|
|
- loading: safeLoading
|
|
|
|
|
|
|
+ loading: safeLoading,
|
|
|
|
|
+ columnMaxWidth: safeColumnMaxWidth,
|
|
|
|
|
+ columnSettings,
|
|
|
|
|
+ updateColumnVisible,
|
|
|
|
|
+ updateColumnFixed,
|
|
|
|
|
+ updateColumnOrder,
|
|
|
|
|
+ resetColumnSettings
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
defineExpose({
|
|
defineExpose({
|
|
@@ -103,34 +374,92 @@ defineExpose({
|
|
|
v-bind="bindProps"
|
|
v-bind="bindProps"
|
|
|
:data="data"
|
|
:data="data"
|
|
|
>
|
|
>
|
|
|
- <template v-for="(_, name) in $slots" #[name]="slotData">
|
|
|
|
|
|
|
+ <template v-for="(_, name) in forwardedSlots" #[name]="slotData">
|
|
|
<slot :name="name" v-bind="slotData || {}"></slot>
|
|
<slot :name="name" v-bind="slotData || {}"></slot>
|
|
|
</template>
|
|
</template>
|
|
|
|
|
+ <TableDefaultSlot />
|
|
|
</el-table>
|
|
</el-table>
|
|
|
</template>
|
|
</template>
|
|
|
|
|
|
|
|
<style lang="scss">
|
|
<style lang="scss">
|
|
|
.zm-table {
|
|
.zm-table {
|
|
|
|
|
+ --zm-table-width: 100%;
|
|
|
|
|
+ --zm-table-overflow: hidden;
|
|
|
--zm-table-radius: 10px;
|
|
--zm-table-radius: 10px;
|
|
|
|
|
+ --zm-table-bg: var(--el-bg-color);
|
|
|
|
|
+ --zm-table-border-width: 1px;
|
|
|
|
|
+ --zm-table-border-style: solid;
|
|
|
--zm-table-border-color: #e7edf4;
|
|
--zm-table-border-color: #e7edf4;
|
|
|
|
|
+ --zm-table-box-shadow: none;
|
|
|
|
|
+ --zm-table-font-family: inherit;
|
|
|
|
|
+ --zm-table-font-size: 12px;
|
|
|
--zm-table-header-border-color: #e3eaf2;
|
|
--zm-table-header-border-color: #e3eaf2;
|
|
|
--zm-table-row-border-color: #edf2f7;
|
|
--zm-table-row-border-color: #edf2f7;
|
|
|
--zm-table-header-bg: #f7f9fc;
|
|
--zm-table-header-bg: #f7f9fc;
|
|
|
--zm-table-header-text-color: #6b7f99;
|
|
--zm-table-header-text-color: #6b7f99;
|
|
|
|
|
+ --zm-table-header-cell-height: 36px;
|
|
|
|
|
+ --zm-table-header-group-cell-height: 42px;
|
|
|
|
|
+ --zm-table-header-font-size: 12px;
|
|
|
|
|
+ --zm-table-header-font-weight: 600;
|
|
|
|
|
+ --zm-table-header-wrapper-width: 100%;
|
|
|
|
|
+ --zm-table-header-wrapper-height: 100%;
|
|
|
|
|
+ --zm-table-header-wrapper-min-width: 0;
|
|
|
|
|
+ --zm-table-header-wrapper-gap: 6px;
|
|
|
|
|
+ --zm-table-header-wrapper-font-size: var(--zm-table-header-font-size);
|
|
|
|
|
+ --zm-table-header-wrapper-font-weight: var(--zm-table-header-font-weight);
|
|
|
|
|
+ --zm-table-header-wrapper-line-height: 16px;
|
|
|
|
|
+ --zm-table-header-wrapper-text-color: var(--zm-table-header-text-color);
|
|
|
|
|
+ --zm-table-header-title-min-width: 0;
|
|
|
|
|
+ --zm-table-header-action-area-height: 100%;
|
|
|
|
|
+ --zm-table-header-action-area-margin-left: 4px;
|
|
|
|
|
+ --zm-table-header-action-area-gap: 3px;
|
|
|
|
|
+ --zm-table-header-icon-btn-size: 18px;
|
|
|
|
|
+ --zm-table-header-icon-btn-padding: 0;
|
|
|
|
|
+ --zm-table-header-icon-btn-color: #8aa0b8;
|
|
|
|
|
+ --zm-table-header-icon-btn-bg: transparent;
|
|
|
|
|
+ --zm-table-header-icon-btn-border: 0;
|
|
|
|
|
+ --zm-table-header-icon-btn-radius: 4px;
|
|
|
|
|
+ --zm-table-header-icon-btn-transition-duration: 0.16s;
|
|
|
|
|
+ --zm-table-header-icon-btn-transition-timing: ease;
|
|
|
|
|
+ --zm-table-header-icon-btn-hover-color: var(--el-color-primary);
|
|
|
|
|
+ --zm-table-header-icon-btn-hover-bg: var(--el-color-primary-light-9);
|
|
|
|
|
+ --zm-table-header-icon-btn-active-color: var(--el-color-primary);
|
|
|
|
|
+ --zm-table-header-icon-btn-active-bg: var(--el-color-primary-light-9);
|
|
|
|
|
+ --zm-table-header-icon-size: 16px;
|
|
|
--zm-table-body-text-color: #40546d;
|
|
--zm-table-body-text-color: #40546d;
|
|
|
--zm-table-strong-text-color: #24364d;
|
|
--zm-table-strong-text-color: #24364d;
|
|
|
|
|
+ --zm-table-row-font-weight: 500;
|
|
|
--zm-table-stripe-bg: #fcfdff;
|
|
--zm-table-stripe-bg: #fcfdff;
|
|
|
--zm-table-hover-bg: #f5f9ff;
|
|
--zm-table-hover-bg: #f5f9ff;
|
|
|
--zm-table-current-bg: #eef6ff;
|
|
--zm-table-current-bg: #eef6ff;
|
|
|
-
|
|
|
|
|
- width: 100%;
|
|
|
|
|
- overflow: hidden;
|
|
|
|
|
- font-size: 12px;
|
|
|
|
|
|
|
+ --zm-table-cell-height: 38px;
|
|
|
|
|
+ --zm-table-cell-padding-x: 13px;
|
|
|
|
|
+ --zm-table-cell-first-padding-left: 16px;
|
|
|
|
|
+ --zm-table-cell-last-padding-right: 16px;
|
|
|
|
|
+ --zm-table-cell-line-height: 18px;
|
|
|
|
|
+ --zm-table-cell-transition-duration: 0.16s;
|
|
|
|
|
+ --zm-table-cell-transition-timing: ease;
|
|
|
|
|
+ --zm-table-empty-min-height: 148px;
|
|
|
|
|
+ --zm-table-empty-bg: var(--el-bg-color);
|
|
|
|
|
+ --zm-table-empty-text-font-size: 12px;
|
|
|
|
|
+ --zm-table-empty-text-color: var(--el-text-color-secondary);
|
|
|
|
|
+ --zm-table-fixed-left-shadow: 6px 0 12px -10px rgb(15 23 42 / 22%);
|
|
|
|
|
+ --zm-table-fixed-right-shadow: -6px 0 12px -10px rgb(15 23 42 / 22%);
|
|
|
|
|
+ --zm-table-scrollbar-size: 7px;
|
|
|
|
|
+ --zm-table-scrollbar-thumb-bg: #b8c5d6;
|
|
|
|
|
+ --zm-table-scrollbar-thumb-radius: 999px;
|
|
|
|
|
+ --zm-table-scrollbar-thumb-opacity: 0.55;
|
|
|
|
|
+ --zm-table-scrollbar-thumb-hover-opacity: 0.85;
|
|
|
|
|
+
|
|
|
|
|
+ width: var(--zm-table-width);
|
|
|
|
|
+ overflow: var(--zm-table-overflow);
|
|
|
|
|
+ font-family: var(--zm-table-font-family);
|
|
|
|
|
+ font-size: var(--zm-table-font-size);
|
|
|
color: var(--zm-table-body-text-color);
|
|
color: var(--zm-table-body-text-color);
|
|
|
- background: var(--el-bg-color);
|
|
|
|
|
- border: 1px solid var(--zm-table-border-color);
|
|
|
|
|
|
|
+ background: var(--zm-table-bg);
|
|
|
|
|
+ border: var(--zm-table-border-width) var(--zm-table-border-style) var(--zm-table-border-color);
|
|
|
border-radius: var(--zm-table-radius);
|
|
border-radius: var(--zm-table-radius);
|
|
|
- box-shadow: none;
|
|
|
|
|
|
|
+ box-shadow: var(--zm-table-box-shadow);
|
|
|
|
|
|
|
|
&::before,
|
|
&::before,
|
|
|
&::after {
|
|
&::after {
|
|
@@ -160,15 +489,18 @@ defineExpose({
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
.el-table__cell {
|
|
.el-table__cell {
|
|
|
- height: 38px;
|
|
|
|
|
|
|
+ height: var(--zm-table-cell-height);
|
|
|
padding: 0;
|
|
padding: 0;
|
|
|
color: var(--zm-table-body-text-color);
|
|
color: var(--zm-table-body-text-color);
|
|
|
- background: var(--el-bg-color);
|
|
|
|
|
- border-right: 1px solid var(--zm-table-row-border-color) !important;
|
|
|
|
|
- border-bottom: 1px solid var(--zm-table-row-border-color) !important;
|
|
|
|
|
|
|
+ background: var(--zm-table-bg);
|
|
|
|
|
+ border-right: var(--zm-table-border-width) var(--zm-table-border-style)
|
|
|
|
|
+ var(--zm-table-row-border-color) !important;
|
|
|
|
|
+ border-bottom: var(--zm-table-border-width) var(--zm-table-border-style)
|
|
|
|
|
+ var(--zm-table-row-border-color) !important;
|
|
|
transition:
|
|
transition:
|
|
|
- background-color 0.16s ease,
|
|
|
|
|
- color 0.16s ease;
|
|
|
|
|
|
|
+ background-color var(--zm-table-cell-transition-duration)
|
|
|
|
|
+ var(--zm-table-cell-transition-timing),
|
|
|
|
|
+ color var(--zm-table-cell-transition-duration) var(--zm-table-cell-transition-timing);
|
|
|
|
|
|
|
|
&:last-child {
|
|
&:last-child {
|
|
|
border-right: none !important;
|
|
border-right: none !important;
|
|
@@ -176,22 +508,24 @@ defineExpose({
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
.cell {
|
|
.cell {
|
|
|
- padding-right: 13px;
|
|
|
|
|
- padding-left: 13px;
|
|
|
|
|
- line-height: 18px;
|
|
|
|
|
|
|
+ padding-right: var(--zm-table-cell-padding-x);
|
|
|
|
|
+ padding-left: var(--zm-table-cell-padding-x);
|
|
|
|
|
+ line-height: var(--zm-table-cell-line-height);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
.el-table__header {
|
|
.el-table__header {
|
|
|
color: var(--zm-table-header-text-color);
|
|
color: var(--zm-table-header-text-color);
|
|
|
|
|
|
|
|
.el-table__cell {
|
|
.el-table__cell {
|
|
|
- height: 36px;
|
|
|
|
|
- font-size: 12px;
|
|
|
|
|
- font-weight: 600;
|
|
|
|
|
|
|
+ height: var(--zm-table-header-cell-height);
|
|
|
|
|
+ font-size: var(--zm-table-header-font-size);
|
|
|
|
|
+ font-weight: var(--zm-table-header-font-weight);
|
|
|
color: var(--zm-table-header-text-color);
|
|
color: var(--zm-table-header-text-color);
|
|
|
background: var(--zm-table-header-bg) !important;
|
|
background: var(--zm-table-header-bg) !important;
|
|
|
- border-right: 1px solid var(--zm-table-header-border-color) !important;
|
|
|
|
|
- border-bottom: 1px solid var(--zm-table-header-border-color) !important;
|
|
|
|
|
|
|
+ border-right: var(--zm-table-border-width) var(--zm-table-border-style)
|
|
|
|
|
+ var(--zm-table-header-border-color) !important;
|
|
|
|
|
+ border-bottom: var(--zm-table-border-width) var(--zm-table-border-style)
|
|
|
|
|
+ var(--zm-table-header-border-color) !important;
|
|
|
|
|
|
|
|
.cell {
|
|
.cell {
|
|
|
display: flex;
|
|
display: flex;
|
|
@@ -223,7 +557,7 @@ defineExpose({
|
|
|
|
|
|
|
|
tr:not(:last-child) {
|
|
tr:not(:last-child) {
|
|
|
.el-table__cell {
|
|
.el-table__cell {
|
|
|
- height: 42px;
|
|
|
|
|
|
|
+ height: var(--zm-table-header-group-cell-height);
|
|
|
border-bottom-color: var(--zm-table-header-border-color) !important;
|
|
border-bottom-color: var(--zm-table-header-border-color) !important;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
@@ -253,31 +587,31 @@ defineExpose({
|
|
|
|
|
|
|
|
.el-table__row {
|
|
.el-table__row {
|
|
|
.el-table__cell {
|
|
.el-table__cell {
|
|
|
- font-weight: 500;
|
|
|
|
|
|
|
+ font-weight: var(--zm-table-row-font-weight);
|
|
|
color: var(--zm-table-strong-text-color);
|
|
color: var(--zm-table-strong-text-color);
|
|
|
|
|
|
|
|
&:first-child {
|
|
&:first-child {
|
|
|
.cell {
|
|
.cell {
|
|
|
- padding-left: 16px;
|
|
|
|
|
|
|
+ padding-left: var(--zm-table-cell-first-padding-left);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
&:last-child {
|
|
&:last-child {
|
|
|
.cell {
|
|
.cell {
|
|
|
- padding-right: 16px;
|
|
|
|
|
|
|
+ padding-right: var(--zm-table-cell-last-padding-right);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
.el-table__empty-block {
|
|
.el-table__empty-block {
|
|
|
- min-height: 148px;
|
|
|
|
|
- background: var(--el-bg-color);
|
|
|
|
|
|
|
+ min-height: var(--zm-table-empty-min-height);
|
|
|
|
|
+ background: var(--zm-table-empty-bg);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
.el-table__empty-text {
|
|
.el-table__empty-text {
|
|
|
- font-size: 12px;
|
|
|
|
|
- color: var(--el-text-color-secondary);
|
|
|
|
|
|
|
+ font-size: var(--zm-table-empty-text-font-size);
|
|
|
|
|
+ color: var(--zm-table-empty-text-color);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
.el-table__cell.el-table-fixed-column--left,
|
|
.el-table__cell.el-table-fixed-column--left,
|
|
@@ -286,35 +620,36 @@ defineExpose({
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
.el-table__cell.el-table-fixed-column--left.is-last-column {
|
|
.el-table__cell.el-table-fixed-column--left.is-last-column {
|
|
|
- box-shadow: 6px 0 12px -10px rgb(15 23 42 / 22%);
|
|
|
|
|
|
|
+ box-shadow: var(--zm-table-fixed-left-shadow);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
.el-table__cell.el-table-fixed-column--right.is-first-column {
|
|
.el-table__cell.el-table-fixed-column--right.is-first-column {
|
|
|
- box-shadow: -6px 0 12px -10px rgb(15 23 42 / 22%);
|
|
|
|
|
|
|
+ box-shadow: var(--zm-table-fixed-right-shadow);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
.el-table__fixed-right-patch {
|
|
.el-table__fixed-right-patch {
|
|
|
background: var(--zm-table-header-bg);
|
|
background: var(--zm-table-header-bg);
|
|
|
- border-bottom: 1px solid var(--zm-table-header-border-color);
|
|
|
|
|
|
|
+ border-bottom: var(--zm-table-border-width) var(--zm-table-border-style)
|
|
|
|
|
+ var(--zm-table-header-border-color);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
.el-scrollbar__bar {
|
|
.el-scrollbar__bar {
|
|
|
&.is-horizontal {
|
|
&.is-horizontal {
|
|
|
- height: 7px;
|
|
|
|
|
|
|
+ height: var(--zm-table-scrollbar-size);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
&.is-vertical {
|
|
&.is-vertical {
|
|
|
- width: 7px;
|
|
|
|
|
|
|
+ width: var(--zm-table-scrollbar-size);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
.el-scrollbar__thumb {
|
|
.el-scrollbar__thumb {
|
|
|
- background: #b8c5d6;
|
|
|
|
|
- border-radius: 999px;
|
|
|
|
|
- opacity: 0.55;
|
|
|
|
|
|
|
+ background: var(--zm-table-scrollbar-thumb-bg);
|
|
|
|
|
+ border-radius: var(--zm-table-scrollbar-thumb-radius);
|
|
|
|
|
+ opacity: var(--zm-table-scrollbar-thumb-opacity);
|
|
|
|
|
|
|
|
&:hover {
|
|
&:hover {
|
|
|
- opacity: 0.85;
|
|
|
|
|
|
|
+ opacity: var(--zm-table-scrollbar-thumb-hover-opacity);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|