mirror of
https://github.com/vbenjs/gf-vben-admin.git
synced 2025-01-23 11:50:20 +08:00
perf(table): the table fills the height according to the screen close #310
This commit is contained in:
parent
745fcfc014
commit
551fe50a44
@ -9,6 +9,8 @@
|
||||
|
||||
- 表格关闭分页时不再携带分页参数
|
||||
- 登录页监听回车事件进行登录
|
||||
- 当表格设置自适应大小时,根据屏幕来铺满了高度.
|
||||
- Tree 滚动条优化
|
||||
|
||||
### 🐛 Bug Fixes
|
||||
|
||||
|
@ -109,7 +109,7 @@
|
||||
"vite-plugin-mock": "^2.2.0",
|
||||
"vite-plugin-purge-icons": "^0.7.0",
|
||||
"vite-plugin-pwa": "^0.5.6",
|
||||
"vite-plugin-style-import": "^0.7.6",
|
||||
"vite-plugin-style-import": "^0.8.1",
|
||||
"vite-plugin-svg-icons": "^0.2.1",
|
||||
"vite-plugin-theme": "^0.4.8",
|
||||
"vite-plugin-windicss": "0.6.10",
|
||||
|
@ -1,6 +1,11 @@
|
||||
<template>
|
||||
<div :class="getClass">
|
||||
<PageHeader :ghost="ghost" v-bind="$attrs" ref="headerRef">
|
||||
<PageHeader
|
||||
:ghost="ghost"
|
||||
v-bind="$attrs"
|
||||
ref="headerRef"
|
||||
v-if="content || $slots.headerContent"
|
||||
>
|
||||
<template #default>
|
||||
<template v-if="content">
|
||||
{{ content }}
|
||||
@ -11,7 +16,11 @@
|
||||
<slot :name="item" v-bind="data"></slot>
|
||||
</template>
|
||||
</PageHeader>
|
||||
<div :class="[`${prefixCls}-content`, $attrs.contentClass]" :style="getContentStyle">
|
||||
<div
|
||||
class="m-4 overflow-hidden"
|
||||
:class="[`${prefixCls}-content`, contentClass]"
|
||||
:style="getContentStyle"
|
||||
>
|
||||
<slot></slot>
|
||||
</div>
|
||||
<PageFooter v-if="getShowFooter" ref="footerRef">
|
||||
@ -48,6 +57,8 @@
|
||||
},
|
||||
contentBackground: propTypes.bool,
|
||||
contentFullHeight: propTypes.bool,
|
||||
contentClass: propTypes.string,
|
||||
fixedHeight: propTypes.bool,
|
||||
},
|
||||
setup(props, { slots }) {
|
||||
const headerRef = ref<ComponentRef>(null);
|
||||
@ -73,15 +84,17 @@
|
||||
|
||||
const getContentStyle = computed(
|
||||
(): CSSProperties => {
|
||||
const { contentBackground, contentFullHeight, contentStyle } = props;
|
||||
const { contentBackground, contentFullHeight, contentStyle, fixedHeight } = props;
|
||||
const bg = contentBackground ? { backgroundColor: '#fff' } : {};
|
||||
if (!contentFullHeight) {
|
||||
return { ...bg, ...contentStyle };
|
||||
}
|
||||
const height = `${unref(pageHeight)}px`;
|
||||
return {
|
||||
...bg,
|
||||
...contentStyle,
|
||||
minHeight: `${unref(pageHeight)}px`,
|
||||
minHeight: height,
|
||||
...(fixedHeight ? { height } : {}),
|
||||
paddingBottom: `${unref(footerHeight)}px`,
|
||||
};
|
||||
}
|
||||
@ -137,18 +150,11 @@
|
||||
position: relative;
|
||||
|
||||
.ant-page-header {
|
||||
// padding: 12px 16px;
|
||||
|
||||
&:empty {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&-content {
|
||||
// padding: 12px;
|
||||
margin: 16px;
|
||||
}
|
||||
|
||||
&--dense {
|
||||
.@{prefix-cls}-content {
|
||||
margin: 0;
|
||||
|
@ -3,6 +3,7 @@
|
||||
ref="wrapRef"
|
||||
:class="[
|
||||
prefixCls,
|
||||
$attrs.class,
|
||||
{
|
||||
[`${prefixCls}-form-container`]: getBindValues.useSearchForm,
|
||||
[`${prefixCls}--inset`]: getBindValues.inset,
|
||||
@ -211,6 +212,8 @@
|
||||
propsData = omit(propsData, 'scroll');
|
||||
}
|
||||
|
||||
propsData = omit(propsData, 'class');
|
||||
|
||||
return propsData;
|
||||
});
|
||||
|
||||
|
@ -55,6 +55,7 @@ export function useTableScroll(
|
||||
// No need to repeat queries
|
||||
let paginationEl: HTMLElement | null;
|
||||
let footerEl: HTMLElement | null;
|
||||
let bodyEl: HTMLElement | null;
|
||||
|
||||
async function calcTableHeight() {
|
||||
const { resizeHeightOffset, pagination, maxHeight } = unref(propsRef);
|
||||
@ -68,6 +69,7 @@ export function useTableScroll(
|
||||
if (!tableEl) return;
|
||||
|
||||
const headEl = tableEl.querySelector('.ant-table-thead ');
|
||||
|
||||
if (!headEl) return;
|
||||
|
||||
// Table height from bottom
|
||||
@ -117,6 +119,11 @@ export function useTableScroll(
|
||||
|
||||
height = (height > maxHeight! ? (maxHeight as number) : height) ?? height;
|
||||
setHeight(height);
|
||||
|
||||
if (!bodyEl) {
|
||||
bodyEl = tableEl.querySelector('.ant-table-body');
|
||||
}
|
||||
bodyEl!.style.height = `${height}px`;
|
||||
}
|
||||
|
||||
useWindowSizeFn(calcTableHeight, 200);
|
||||
|
@ -4,12 +4,11 @@
|
||||
|
||||
.@{prefix-cls} {
|
||||
&-form-container {
|
||||
width: 100%;
|
||||
padding: 16px;
|
||||
|
||||
.ant-form {
|
||||
padding: 16px 16px 6px 12px;
|
||||
margin-bottom: 18px;
|
||||
padding: 12px 10px 6px 10px;
|
||||
margin-bottom: 16px;
|
||||
background: #fff;
|
||||
border-radius: 4px;
|
||||
}
|
||||
@ -74,40 +73,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
// .ant-table-bordered .ant-table-header > table,
|
||||
// .ant-table-bordered .ant-table-body > table,
|
||||
// .ant-table-bordered .ant-table-fixed-left table,
|
||||
// .ant-table-bordered .ant-table-fixed-right table {
|
||||
// border: 1px solid @border-color !important;
|
||||
// }
|
||||
|
||||
// .ant-table-thead {
|
||||
// tr {
|
||||
// border: none;
|
||||
// }
|
||||
|
||||
// th {
|
||||
// border: none;
|
||||
// }
|
||||
// }
|
||||
|
||||
// .ant-table-bordered .ant-table-tbody > tr > td {
|
||||
// border-bottom: 1px solid @border-color !important;
|
||||
|
||||
// &:last-child {
|
||||
// border-right: none !important;
|
||||
// }
|
||||
// }
|
||||
|
||||
// .ant-table.ant-table-bordered .ant-table-footer,
|
||||
// .ant-table.ant-table-bordered .ant-table-title {
|
||||
// border: 1px solid @border-color !important;
|
||||
// }
|
||||
|
||||
// .ant-table-bordered.ant-table-empty .ant-table-placeholder {
|
||||
// border: 1px solid @border-color !important;
|
||||
// }
|
||||
|
||||
.ant-table-tbody > tr > td,
|
||||
.ant-table-tbody > tr > th,
|
||||
.ant-table-thead > tr > td,
|
||||
@ -115,62 +80,10 @@
|
||||
white-space: pre;
|
||||
}
|
||||
|
||||
// .ant-table-row-cell-last {
|
||||
// border-right: none !important;
|
||||
// }
|
||||
|
||||
// .ant-table-bordered .ant-table-thead > tr > th,
|
||||
// .ant-table-bordered .ant-table-tbody > tr > td {
|
||||
// border-right: 1px solid @border-color !important;
|
||||
// }
|
||||
|
||||
.ant-pagination {
|
||||
margin: 10px 0 0 0;
|
||||
}
|
||||
|
||||
// .ant-table-body {
|
||||
// overflow-x: auto !important;
|
||||
// overflow-y: scroll !important;
|
||||
// }
|
||||
|
||||
// .ant-table-header {
|
||||
// margin-bottom: 0 !important;
|
||||
// overflow-x: hidden !important;
|
||||
// overflow-y: scroll !important;
|
||||
// }
|
||||
|
||||
// .ant-table-fixed-right {
|
||||
// right: -1px;
|
||||
|
||||
// .ant-table-header {
|
||||
// border-left: 1px solid @border-color !important;
|
||||
|
||||
// .ant-table-fixed {
|
||||
// border-bottom: none;
|
||||
|
||||
// .ant-table-thead th {
|
||||
// background: rgb(241, 243, 244);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// .ant-table-fixed-left {
|
||||
// .ant-table-header {
|
||||
// overflow-y: hidden !important;
|
||||
// }
|
||||
|
||||
// .ant-table-fixed {
|
||||
// border-bottom: none;
|
||||
// }
|
||||
// }
|
||||
|
||||
// .ant-table-bordered .ant-table-thead > tr:not(:last-child) > th,
|
||||
// .ant-table-tbody > tr > td {
|
||||
// word-break: break-word;
|
||||
// // border-color: @border-color !important;
|
||||
// }
|
||||
|
||||
.ant-table-footer {
|
||||
padding: 0;
|
||||
|
||||
|
@ -1,10 +1,21 @@
|
||||
<script lang="tsx">
|
||||
import type { ReplaceFields, Keys, CheckKeys, TreeActionType, TreeItem } from './types';
|
||||
|
||||
import { defineComponent, reactive, computed, unref, ref, watchEffect, toRaw, watch } from 'vue';
|
||||
import { Tree } from 'ant-design-vue';
|
||||
import {
|
||||
defineComponent,
|
||||
reactive,
|
||||
computed,
|
||||
unref,
|
||||
ref,
|
||||
watchEffect,
|
||||
toRaw,
|
||||
watch,
|
||||
CSSProperties,
|
||||
} from 'vue';
|
||||
import { Tree, Empty } from 'ant-design-vue';
|
||||
import { TreeIcon } from './TreeIcon';
|
||||
import TreeHeader from './TreeHeader.vue';
|
||||
import { ScrollContainer } from '/@/components/Container';
|
||||
// import { DownOutlined } from '@ant-design/icons-vue';
|
||||
|
||||
import { omit, get } from 'lodash-es';
|
||||
@ -95,6 +106,11 @@
|
||||
emit('update:value', rawVal);
|
||||
},
|
||||
onRightClick: handleRightClick,
|
||||
// onSelect: (k, e) => {
|
||||
// setTimeout(() => {
|
||||
// emit('select', k, e);
|
||||
// }, 16);
|
||||
// },
|
||||
};
|
||||
propsData = omit(propsData, 'treeData', 'class');
|
||||
return propsData;
|
||||
@ -104,6 +120,10 @@
|
||||
searchState.startSearch ? searchState.searchData : unref(treeDataRef)
|
||||
);
|
||||
|
||||
const getNotFound = computed((): boolean => {
|
||||
return searchState.startSearch && searchState.searchData?.length === 0;
|
||||
});
|
||||
|
||||
const {
|
||||
deleteNodeByKey,
|
||||
insertNodeByKey,
|
||||
@ -178,10 +198,10 @@
|
||||
return;
|
||||
}
|
||||
searchState.startSearch = true;
|
||||
const { title: titleField } = unref(getReplaceFields);
|
||||
|
||||
searchState.searchData = filter(unref(treeDataRef), (node) => {
|
||||
const { title } = node;
|
||||
return title?.includes(searchValue) ?? false;
|
||||
return node[titleField]?.includes(searchValue) ?? false;
|
||||
});
|
||||
}
|
||||
|
||||
@ -284,7 +304,7 @@
|
||||
title: () => (
|
||||
<span
|
||||
class={`${prefixCls}-title pl-2`}
|
||||
onClick={handleClickNode.bind(null, item.key, children)}
|
||||
onClick={handleClickNode.bind(null, item[keyField], item[childrenField])}
|
||||
>
|
||||
{icon && <TreeIcon icon={icon} />}
|
||||
<span
|
||||
@ -304,9 +324,11 @@
|
||||
}
|
||||
return () => {
|
||||
const { title, helpMessage, toolbar, search } = props;
|
||||
const showTitle = title || toolbar || search;
|
||||
const scrollStyle: CSSProperties = { height: 'calc(100% - 38px)' };
|
||||
return (
|
||||
<div class={[prefixCls, 'h-full bg-white', attrs.class]}>
|
||||
{(title || toolbar || search) && (
|
||||
{showTitle && (
|
||||
<TreeHeader
|
||||
checkAll={checkAll}
|
||||
expandAll={expandAll}
|
||||
@ -318,6 +340,7 @@
|
||||
onSearch={handleSearch}
|
||||
/>
|
||||
)}
|
||||
<ScrollContainer style={scrollStyle} v-show={!unref(getNotFound)}>
|
||||
<Tree {...unref(getBindValues)} showIcon={false}>
|
||||
{{
|
||||
// switcherIcon: () => <DownOutlined />,
|
||||
@ -325,6 +348,9 @@
|
||||
...extendSlots(slots),
|
||||
}}
|
||||
</Tree>
|
||||
</ScrollContainer>
|
||||
|
||||
<Empty v-show={unref(getNotFound)} class="!mt-4" />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
43
src/views/demo/system/account/DeptTree.vue
Normal file
43
src/views/demo/system/account/DeptTree.vue
Normal file
@ -0,0 +1,43 @@
|
||||
<template>
|
||||
<div class="bg-white m-4 mr-0 overflow-hidden">
|
||||
<BasicTree
|
||||
title="部门列表"
|
||||
toolbar
|
||||
search
|
||||
:clickRowToExpand="false"
|
||||
:treeData="treeData"
|
||||
:replaceFields="{ key: 'id', title: 'deptName' }"
|
||||
@select="handleSelect"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, onMounted, ref } from 'vue';
|
||||
|
||||
import { BasicTree, TreeItem } from '/@/components/Tree';
|
||||
import { getDeptList } from '/@/api/demo/system';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'DeptTree',
|
||||
components: { BasicTree },
|
||||
|
||||
emits: ['select'],
|
||||
setup(_, { emit }) {
|
||||
const treeData = ref<TreeItem[]>([]);
|
||||
|
||||
async function fetch() {
|
||||
treeData.value = ((await getDeptList()) as unknown) as TreeItem[];
|
||||
}
|
||||
|
||||
function handleSelect(keys: string, e) {
|
||||
emit('select', keys[0]);
|
||||
console.log(keys, e);
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
fetch();
|
||||
});
|
||||
return { treeData, handleSelect };
|
||||
},
|
||||
});
|
||||
</script>
|
@ -1,6 +1,7 @@
|
||||
<template>
|
||||
<div :class="[prefixCls]">
|
||||
<BasicTable @register="registerTable">
|
||||
<PageWrapper dense contentFullHeight fixedHeight contentClass="flex">
|
||||
<DeptTree class="w-1/4 xl:w-1/5" @select="handleSelect" />
|
||||
<BasicTable @register="registerTable" class="w-3/4 xl:w-4/5">
|
||||
<template #toolbar>
|
||||
<a-button type="primary" @click="handleCreate"> 新增账号 </a-button>
|
||||
</template>
|
||||
@ -24,14 +25,15 @@
|
||||
</template>
|
||||
</BasicTable>
|
||||
<AccountModal @register="registerModal" @success="handleSuccess" />
|
||||
</div>
|
||||
</PageWrapper>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
|
||||
import { useDesign } from '/@/hooks/web/useDesign';
|
||||
import { BasicTable, useTable, TableAction } from '/@/components/Table';
|
||||
import { getAccountList } from '/@/api/demo/system';
|
||||
import { PageWrapper } from '/@/components/Page';
|
||||
import DeptTree from './DeptTree.vue';
|
||||
|
||||
import { useModal } from '/@/components/Modal';
|
||||
import AccountModal from './AccountModal.vue';
|
||||
@ -40,10 +42,8 @@
|
||||
|
||||
export default defineComponent({
|
||||
name: 'AccountManagement',
|
||||
components: { BasicTable, AccountModal, TableAction },
|
||||
components: { BasicTable, PageWrapper, DeptTree, AccountModal, TableAction },
|
||||
setup() {
|
||||
const { prefixCls } = useDesign('account-management');
|
||||
|
||||
const [registerModal, { openModal }] = useModal();
|
||||
const [registerTable, { reload }] = useTable({
|
||||
title: '账号列表',
|
||||
@ -86,22 +86,19 @@
|
||||
reload();
|
||||
}
|
||||
|
||||
function handleSelect(deptId: string = '') {
|
||||
reload({ searchInfo: { deptId } });
|
||||
}
|
||||
|
||||
return {
|
||||
prefixCls,
|
||||
registerTable,
|
||||
registerModal,
|
||||
handleCreate,
|
||||
handleEdit,
|
||||
handleDelete,
|
||||
handleSuccess,
|
||||
handleSelect,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
@prefix-cls: ~'@{namespace}-account-management';
|
||||
|
||||
.@{prefix-cls} {
|
||||
display: flex;
|
||||
}
|
||||
</style>
|
||||
|
18
yarn.lock
18
yarn.lock
@ -3662,10 +3662,10 @@ es-module-lexer@^0.3.26:
|
||||
resolved "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.3.26.tgz#7b507044e97d5b03b01d4392c74ffeb9c177a83b"
|
||||
integrity sha512-Va0Q/xqtrss45hWzP8CZJwzGSZJjDM5/MJRE3IXXnUCcVLElR9BRaE9F62BopysASyc4nM3uwhSW7FFB9nlWAA==
|
||||
|
||||
es-module-lexer@^0.4.0:
|
||||
version "0.4.0"
|
||||
resolved "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.4.0.tgz#21f4181cc8b7eee06855f1c59e6087c7bc4f77b0"
|
||||
integrity sha512-iuEGihqqhKWFgh72Q/Jtch7V2t/ft8w8IPP2aEN8ArYKO+IWyo6hsi96hCdgyeEDQIV3InhYQ9BlwUFPGXrbEQ==
|
||||
es-module-lexer@^0.4.1:
|
||||
version "0.4.1"
|
||||
resolved "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.4.1.tgz#dda8c6a14d8f340a24e34331e0fab0cb50438e0e"
|
||||
integrity sha512-ooYciCUtfw6/d2w56UVeqHPcoCFAiJdz5XOkYpv/Txl1HMUozpXjz/2RIQgqwKdXNDPSF1W7mJCFse3G+HDyAA==
|
||||
|
||||
es-to-primitive@^1.2.1:
|
||||
version "1.2.1"
|
||||
@ -9120,15 +9120,15 @@ vite-plugin-pwa@^0.5.6:
|
||||
pretty-bytes "^5.6.0"
|
||||
workbox-build "^6.1.1"
|
||||
|
||||
vite-plugin-style-import@^0.7.6:
|
||||
version "0.7.6"
|
||||
resolved "https://registry.npmjs.org/vite-plugin-style-import/-/vite-plugin-style-import-0.7.6.tgz#909a5402f3a915fb2512e2a039e9cdb360fd2882"
|
||||
integrity sha512-EDjscCzMsmi6mJ0UbMLMkCGLo7LCdFsRJZdjO7sfUIB+2wsC1FjDJcIEGWg0Lzl+4gghv9rk+AP+WCibI83WNw==
|
||||
vite-plugin-style-import@^0.8.1:
|
||||
version "0.8.1"
|
||||
resolved "https://registry.npmjs.org/vite-plugin-style-import/-/vite-plugin-style-import-0.8.1.tgz#e098c633cba3abef9b5a156aaf47f001567ebbb9"
|
||||
integrity sha512-qZg73SA2+tbuEk7b0VjubjceUKVzHB6NwDYd3R9Hd6At4+sJ/85UIlTkzxSWHNgkTQh4sIOMQi1olXjkSF7tjg==
|
||||
dependencies:
|
||||
"@rollup/pluginutils" "^4.1.0"
|
||||
change-case "^4.1.2"
|
||||
debug "^4.3.2"
|
||||
es-module-lexer "^0.4.0"
|
||||
es-module-lexer "^0.4.1"
|
||||
magic-string "^0.25.7"
|
||||
|
||||
vite-plugin-svg-icons@^0.2.1:
|
||||
|
Loading…
Reference in New Issue
Block a user