wms-antdvue/.svn/pristine/b9/b9ad95908a848b5d802cab654d2ff721fdcb257d.svn-base
2024-11-07 16:33:03 +08:00

439 lines
11 KiB
Plaintext

<template>
<div
ref="basicTableRef"
:class="{
'table-full-screen': isFullscreen,
}"
>
<div class="table-toolbar">
<!--顶部左侧区域-->
<div class="flex items-center table-toolbar-left">
<template v-if="leftTitle">
<div class="table-toolbar-left-title">
{{ leftTitle }}
<a-tooltip v-if="leftTitleTooltip">
<template #title>{{ leftTitleTooltip }}</template>
<span class="ml-1 text-gray-400 cursor-pointer">
<QuestionCircleOutlined style="font-size: 18px" />
</span>
</a-tooltip>
</div>
</template>
<slot name="tableTitle"></slot>
</div>
<div class="flex items-center table-toolbar-right">
<!--顶部右侧区域-->
<slot name="toolbar"></slot>
<template v-if="isTableSetting">
<!--表格斑马纹-->
<a-tooltip v-if="isShowTableStriped">
<template #title>表格斑马纹</template>
<div class="table-toolbar-right-icon">
<a-switch
v-model:checked="tableStriped"
checked-children="开"
un-checked-children="关"
/>
</div>
</a-tooltip>
<a-divider type="vertical" />
<!--刷新-->
<a-tooltip v-if="isShowTableRedo">
<template #title>刷新</template>
<div
class="table-toolbar-right-icon"
:class="{ 'ml-0': isShowTableStriped }"
@click="reload"
>
<ReloadOutlined />
</div>
</a-tooltip>
<!--密度-->
<a-tooltip v-if="isShowTableSize">
<template #title>密度</template>
<div class="table-toolbar-right-icon">
<a-dropdown>
<ColumnHeightOutlined />
<template #overlay>
<a-menu v-model:value="tableSize" @click="densitySelect">
<a-menu-item v-for="item in densityOptions" :key="item.key">
<span>{{ item.label }}</span>
</a-menu-item>
</a-menu>
</template>
</a-dropdown>
</div>
</a-tooltip>
<!--表格设置单独抽离成组件-->
<ColumnSetting
v-if="isShowTableSetting"
@columns-change="columnsChange"
:tableSetting="props.tableSetting"
/>
<!--全屏-->
<a-tooltip v-if="isShowTableFullscreen">
<template #title>{{ isFullscreen ? '还原' : '全屏' }}</template>
<div class="table-toolbar-right-icon" @click="toggleTableFullScreen">
<FullscreenExitOutlined v-if="isFullscreen" />
<FullscreenOutlined v-else />
</div>
</a-tooltip>
</template>
</div>
</div>
<div class="s-table" v-if="isShowTable">
<a-table
ref="tableElRef"
v-bind="getBindValues"
:pagination="pagination"
@change="handleTableChange"
:style="{ minHeight: deviceHeight+45+'px' }"
class="basicTable"
>
<template v-for="item in Object.keys($slots)" :key="item" #[item]="data">
<slot v-bind="data" :name="item"></slot>
</template>
</a-table>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, unref, toRaw, computed, onMounted, nextTick } from 'vue';
import { createTableContext } from './hooks/useTableContext';
import ColumnSetting from './components/settings/ColumnSetting.vue';
import { useLoading } from './hooks/useLoading';
import { useColumns } from './hooks/useColumns';
import { useDataSource } from './hooks/useDataSource';
import { usePagination } from './hooks/usePagination';
import { basicProps } from './props';
import { BasicTableProps } from './types/table';
import { getViewportOffset } from '@/utils/domUtils';
import { useWindowSizeFn } from '@/hooks/event/useWindowSizeFn';
import { isBoolean } from '@/utils/is';
import { useFullscreen } from '@vueuse/core';
import {
ReloadOutlined,
ColumnHeightOutlined,
QuestionCircleOutlined,
FullscreenExitOutlined,
FullscreenOutlined,
} from '@ant-design/icons-vue';
const props = defineProps({
...basicProps,
});
const emit = defineEmits([
'fetch-success',
'fetch-error',
'update:checked-row-keys',
'edit-end',
'edit-cancel',
'edit-row-end',
'edit-change',
'columns-change',
]);
const densityOptions = [
{
label: '紧凑',
key: 'small',
},
{
label: '中间',
key: 'middle',
},
{
label: '默认',
key: 'default',
},
];
const isShowTable = ref(true);
const tableStriped = ref(false);
const deviceHeight = ref(150);
const tableElRef = ref(null);
const wrapRef = ref(null);
const basicTableRef = ref<HTMLElement | null>(null);
let paginationEl: HTMLElement | null;
const tableData = ref<Recordable[]>([]);
const innerPropsRef = ref<Partial<BasicTableProps>>();
const { isFullscreen, toggle } = useFullscreen(basicTableRef as any);
const getProps = computed(() => {
return { ...props, ...unref(innerPropsRef) } as BasicTableProps;
});
const { getLoading, setLoading } = useLoading(getProps);
const { getPaginationInfo, setPagination } = usePagination(getProps);
const {
getDataSourceRef,
getRowKey,
reload,
restReload,
setTableData,
getDataSource,
updateTableData,
tableChange,
} = useDataSource(
getProps,
{
getPaginationInfo,
setPagination,
tableData,
setLoading,
},
emit,
);
const { getPageColumns, setColumns, getColumns, getCacheColumns, setCacheColumnsField } =
useColumns(getProps);
const tableSize = ref(unref(getProps as any).size || 'medium');
//columns 列变动
function columnsChange(columns) {
emit('columns-change', columns);
}
//分页、排序、筛选变化时触发
function handleTableChange(res) {
tableChange(res);
}
//密度切换
function densitySelect(e) {
tableSize.value = e.key;
}
//获取表格大小
const getTableSize = computed(() => tableSize.value);
//表格设置工具
const isTableSetting = computed(() => getProps.value.showTableSetting);
//是否显示刷新按钮
const isShowTableRedo = computed(() => getProps.value.tableSetting?.redo ?? true);
//斑马纹
const isShowTableStriped = computed(() => getProps.value.tableSetting?.striped ?? true);
//是否显示尺寸调整按钮
const isShowTableSize = computed(() => getProps.value.tableSetting?.size ?? true);
//是否显示字段调整按钮
const isShowTableSetting = computed(() => getProps.value.tableSetting?.setting ?? true);
//是否显示表格全屏按钮
const isShowTableFullscreen = computed(() => getProps.value.tableSetting?.fullscreen ?? true);
//计算高度
const getDeviceHeight = computed(() => {
const tableData = unref(getDataSourceRef);
const maxHeight = tableData.length ? `${unref(deviceHeight)}px` : 'auto';
return maxHeight;
});
const getTableScroll = computed(() => {
const { scroll, canResize } = getProps.value;
return {
x: '100%',
y: canResize ? getDeviceHeight.value : null,
...(scroll || {}),
};
});
//组装表格信息
const getBindValues = computed(() => {
const tableData = unref(getDataSourceRef);
return {
...unref(getProps),
loading: unref(getLoading),
columns: toRaw(unref(getPageColumns)),
rowKey: unref(getRowKey),
dataSource: tableData,
size: unref(getTableSize),
scroll: unref(getTableScroll),
rowClassName:
tableStriped.value && !unref(getProps).rowClassName
? (_record, index) => (index % 2 === 1 ? 'table-striped' : null)
: unref(getProps).rowClassName,
};
});
//返回表格 ref 实例
function getTableRef() {
return tableElRef.value;
}
//表格全屏
function toggleTableFullScreen() {
toggle();
}
//重新计算表格高度
function redoHeight() {
computeTableHeight();
}
//获取分页信息
const pagination = computed(() => toRaw(unref(getPaginationInfo)));
function setProps(props: Partial<BasicTableProps>) {
innerPropsRef.value = { ...unref(innerPropsRef), ...props };
}
const tableAction = {
reload,
restReload,
redoHeight,
setColumns,
setLoading,
setProps,
getTableRef,
getColumns,
getPageColumns,
getCacheColumns,
setCacheColumnsField,
emit,
};
createTableContext({ ...tableAction, wrapRef, getBindValues, isShowTable });
const getCanResize = computed(() => {
const { canResize } = unref(getProps);
return canResize;
});
async function computeTableHeight() {
const table = unref(tableElRef);
if (!table) return;
if (!unref(getCanResize)) return;
// @ts-ignore
const tableEl: any = table?.$el;
await nextTick();
const headEl = tableEl.querySelector('.ant-table-thead');
const { bottomIncludeBody } = getViewportOffset(headEl);
const headerH = 64;
let paginationH = 2;
let marginH = 27;
console.log(pagination)
if (!isBoolean(pagination)) {
paginationEl = tableEl.querySelector('.ant-pagination') as HTMLElement;
if (paginationEl) {
const offsetHeight = paginationEl.offsetHeight;
paginationH += offsetHeight || 0;
} else {
paginationH += 28;
}
}
let height =
bottomIncludeBody - (headerH + paginationH + marginH + (props.resizeHeightOffset || 0));
const maxHeight = props.maxHeight;
height = maxHeight && maxHeight < height ? maxHeight : height;
deviceHeight.value = height;
}
useWindowSizeFn(computeTableHeight, 280);
onMounted(() => {
nextTick(() => {
computeTableHeight();
});
});
//导出方法到外部使用
defineExpose({
reload,
restReload,
getTableRef,
getDataSource,
setTableData,
updateTableData,
redoHeight,
});
</script>
<style lang="less" scoped>
.table-toolbar {
display: flex;
justify-content: space-between;
padding: 0 0 16px 0;
&-left {
display: flex;
align-items: center;
justify-content: flex-start;
flex: 1;
&-title {
display: flex;
align-items: center;
justify-content: flex-start;
font-size: 16px;
font-weight: 600;
}
}
&-right {
display: flex;
justify-content: flex-end;
flex: 1;
&-icon {
margin-left: 12px;
font-size: 16px;
cursor: pointer;
//color: var(--text-color);
//:hover {
// color: v-bind(getAppTheme);
//}
}
.ml-0 {
margin-left: 0;
}
}
}
.table-toolbar-inner-popover-title {
padding: 2px 0;
}
.s-table :deep(.ant-table-pagination.ant-pagination) {
float: none;
text-align: center;
margin-bottom: 0;
}
.table-full-screen {
background: #fff;
padding: 20px;
}
.dark {
.table-full-screen {
background: #151515;
}
}
.basicTable :deep(.table-striped) td {
background-color: #fafafa;
}
</style>