feat: 新组件ReVxeTableBar
搭配vxe-table
使用 (#1087)
Before Width: | Height: | Size: 439 B After Width: | Height: | Size: 439 B |
Before Width: | Height: | Size: 392 B After Width: | Height: | Size: 392 B |
Before Width: | Height: | Size: 161 B After Width: | Height: | Size: 161 B |
Before Width: | Height: | Size: 235 B After Width: | Height: | Size: 235 B |
Before Width: | Height: | Size: 840 B After Width: | Height: | Size: 840 B |
@ -18,11 +18,11 @@ import {
|
|||||||
getKeyList
|
getKeyList
|
||||||
} from "@pureadmin/utils";
|
} from "@pureadmin/utils";
|
||||||
|
|
||||||
import DragIcon from "./svg/drag.svg?component";
|
import DragIcon from "@/assets/table-bar/drag.svg?component";
|
||||||
import ExpandIcon from "./svg/expand.svg?component";
|
import ExpandIcon from "@/assets/table-bar/expand.svg?component";
|
||||||
import RefreshIcon from "./svg/refresh.svg?component";
|
import RefreshIcon from "@/assets/table-bar/refresh.svg?component";
|
||||||
import SettingIcon from "./svg/settings.svg?component";
|
import SettingIcon from "@/assets/table-bar/settings.svg?component";
|
||||||
import CollapseIcon from "./svg/collapse.svg?component";
|
import CollapseIcon from "@/assets/table-bar/collapse.svg?component";
|
||||||
|
|
||||||
const props = {
|
const props = {
|
||||||
/** 头部最左边的标题 */
|
/** 头部最左边的标题 */
|
||||||
|
5
src/components/ReVxeTableBar/index.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import vxeTableBar from "./src/bar";
|
||||||
|
import { withInstall } from "@pureadmin/utils";
|
||||||
|
|
||||||
|
/** 配合 `vxe-table` 实现快速便捷的表格操作 */
|
||||||
|
export const VxeTableBar = withInstall(vxeTableBar);
|
362
src/components/ReVxeTableBar/src/bar.tsx
Normal file
@ -0,0 +1,362 @@
|
|||||||
|
import Sortable from "sortablejs";
|
||||||
|
import { transformI18n } from "@/plugins/i18n";
|
||||||
|
import { useEpThemeStoreHook } from "@/store/modules/epTheme";
|
||||||
|
import { delay, cloneDeep, getKeyList } from "@pureadmin/utils";
|
||||||
|
import {
|
||||||
|
type PropType,
|
||||||
|
ref,
|
||||||
|
unref,
|
||||||
|
computed,
|
||||||
|
nextTick,
|
||||||
|
defineComponent,
|
||||||
|
getCurrentInstance
|
||||||
|
} from "vue";
|
||||||
|
|
||||||
|
import DragIcon from "@/assets/table-bar/drag.svg?component";
|
||||||
|
import ExpandIcon from "@/assets/table-bar/expand.svg?component";
|
||||||
|
import RefreshIcon from "@/assets/table-bar/refresh.svg?component";
|
||||||
|
import SettingIcon from "@/assets/table-bar/settings.svg?component";
|
||||||
|
import CollapseIcon from "@/assets/table-bar/collapse.svg?component";
|
||||||
|
|
||||||
|
const props = {
|
||||||
|
/** 头部最左边的标题 */
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
default: "列表"
|
||||||
|
},
|
||||||
|
vxeTableRef: {
|
||||||
|
type: Object as PropType<any>
|
||||||
|
},
|
||||||
|
/** 需要展示的列 */
|
||||||
|
columns: {
|
||||||
|
type: Array as PropType<any>,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
/** 是否为树列表 */
|
||||||
|
tree: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
isExpandAll: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
tableKey: {
|
||||||
|
type: [String, Number] as PropType<string | number>,
|
||||||
|
default: "0"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: "VxeTableBar",
|
||||||
|
props,
|
||||||
|
emits: ["refresh"],
|
||||||
|
setup(props, { emit, slots, attrs }) {
|
||||||
|
const size = ref("small");
|
||||||
|
const loading = ref(false);
|
||||||
|
const checkAll = ref(true);
|
||||||
|
const isIndeterminate = ref(false);
|
||||||
|
const instance = getCurrentInstance()!;
|
||||||
|
const isExpandAll = ref(props.isExpandAll);
|
||||||
|
let checkColumnList = getKeyList(cloneDeep(props?.columns), "title");
|
||||||
|
const checkedColumns = ref(getKeyList(cloneDeep(props?.columns), "title"));
|
||||||
|
const dynamicColumns = ref(cloneDeep(props?.columns));
|
||||||
|
|
||||||
|
const getDropdownItemStyle = computed(() => {
|
||||||
|
return s => {
|
||||||
|
return {
|
||||||
|
background:
|
||||||
|
s === size.value ? useEpThemeStoreHook().epThemeColor : "",
|
||||||
|
color: s === size.value ? "#fff" : "var(--el-text-color-primary)"
|
||||||
|
};
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const iconClass = computed(() => {
|
||||||
|
return [
|
||||||
|
"text-black",
|
||||||
|
"dark:text-white",
|
||||||
|
"duration-100",
|
||||||
|
"hover:!text-primary",
|
||||||
|
"cursor-pointer",
|
||||||
|
"outline-none"
|
||||||
|
];
|
||||||
|
});
|
||||||
|
|
||||||
|
const topClass = computed(() => {
|
||||||
|
return [
|
||||||
|
"flex",
|
||||||
|
"justify-between",
|
||||||
|
"pt-[3px]",
|
||||||
|
"px-[11px]",
|
||||||
|
"border-b-[1px]",
|
||||||
|
"border-solid",
|
||||||
|
"border-[#dcdfe6]",
|
||||||
|
"dark:border-[#303030]"
|
||||||
|
];
|
||||||
|
});
|
||||||
|
|
||||||
|
function onReFresh() {
|
||||||
|
loading.value = true;
|
||||||
|
emit("refresh");
|
||||||
|
delay(500).then(() => (loading.value = false));
|
||||||
|
}
|
||||||
|
|
||||||
|
function onExpand() {
|
||||||
|
isExpandAll.value = !isExpandAll.value;
|
||||||
|
isExpandAll.value
|
||||||
|
? props.vxeTableRef.setAllTreeExpand(true)
|
||||||
|
: props.vxeTableRef.clearTreeExpand();
|
||||||
|
props.vxeTableRef.refreshColumn();
|
||||||
|
}
|
||||||
|
|
||||||
|
function reloadColumn() {
|
||||||
|
const curCheckedColumns = cloneDeep(dynamicColumns.value).filter(item =>
|
||||||
|
checkedColumns.value.includes(item.title)
|
||||||
|
);
|
||||||
|
props.vxeTableRef.reloadColumn(curCheckedColumns);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleCheckAllChange(val: boolean) {
|
||||||
|
checkedColumns.value = val ? checkColumnList : [];
|
||||||
|
isIndeterminate.value = false;
|
||||||
|
reloadColumn();
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleCheckedColumnsChange(value: string[]) {
|
||||||
|
checkedColumns.value = value;
|
||||||
|
const checkedCount = value.length;
|
||||||
|
checkAll.value = checkedCount === checkColumnList.length;
|
||||||
|
isIndeterminate.value =
|
||||||
|
checkedCount > 0 && checkedCount < checkColumnList.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function onReset() {
|
||||||
|
checkAll.value = true;
|
||||||
|
isIndeterminate.value = false;
|
||||||
|
dynamicColumns.value = cloneDeep(props?.columns);
|
||||||
|
checkColumnList = [];
|
||||||
|
checkColumnList = await getKeyList(cloneDeep(props?.columns), "title");
|
||||||
|
checkedColumns.value = getKeyList(cloneDeep(props?.columns), "title");
|
||||||
|
props.vxeTableRef.refreshColumn();
|
||||||
|
}
|
||||||
|
|
||||||
|
function changeSize(curSize: string) {
|
||||||
|
size.value = curSize;
|
||||||
|
props.vxeTableRef.refreshColumn();
|
||||||
|
}
|
||||||
|
|
||||||
|
const dropdown = {
|
||||||
|
dropdown: () => (
|
||||||
|
<el-dropdown-menu class="translation">
|
||||||
|
<el-dropdown-item
|
||||||
|
style={getDropdownItemStyle.value("medium")}
|
||||||
|
onClick={() => changeSize("medium")}
|
||||||
|
>
|
||||||
|
宽松
|
||||||
|
</el-dropdown-item>
|
||||||
|
<el-dropdown-item
|
||||||
|
style={getDropdownItemStyle.value("small")}
|
||||||
|
onClick={() => changeSize("small")}
|
||||||
|
>
|
||||||
|
默认
|
||||||
|
</el-dropdown-item>
|
||||||
|
<el-dropdown-item
|
||||||
|
style={getDropdownItemStyle.value("mini")}
|
||||||
|
onClick={() => changeSize("mini")}
|
||||||
|
>
|
||||||
|
紧凑
|
||||||
|
</el-dropdown-item>
|
||||||
|
</el-dropdown-menu>
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 列展示拖拽排序 */
|
||||||
|
const rowDrop = (event: { preventDefault: () => void }) => {
|
||||||
|
event.preventDefault();
|
||||||
|
nextTick(() => {
|
||||||
|
const wrapper: HTMLElement = (
|
||||||
|
instance?.proxy?.$refs[`VxeGroupRef${unref(props.tableKey)}`] as any
|
||||||
|
).$el.firstElementChild;
|
||||||
|
Sortable.create(wrapper, {
|
||||||
|
animation: 300,
|
||||||
|
handle: ".drag-btn",
|
||||||
|
onEnd: ({ newIndex, oldIndex, item }) => {
|
||||||
|
const targetThElem = item;
|
||||||
|
const wrapperElem = targetThElem.parentNode as HTMLElement;
|
||||||
|
const oldColumn = dynamicColumns.value[oldIndex];
|
||||||
|
const newColumn = dynamicColumns.value[newIndex];
|
||||||
|
if (oldColumn?.fixed || newColumn?.fixed) {
|
||||||
|
// 当前列存在fixed属性 则不可拖拽
|
||||||
|
const oldThElem = wrapperElem.children[oldIndex] as HTMLElement;
|
||||||
|
if (newIndex > oldIndex) {
|
||||||
|
wrapperElem.insertBefore(targetThElem, oldThElem);
|
||||||
|
} else {
|
||||||
|
wrapperElem.insertBefore(
|
||||||
|
targetThElem,
|
||||||
|
oldThElem ? oldThElem.nextElementSibling : oldThElem
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const currentRow = dynamicColumns.value.splice(oldIndex, 1)[0];
|
||||||
|
dynamicColumns.value.splice(newIndex, 0, currentRow);
|
||||||
|
reloadColumn();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const isFixedColumn = (title: string) => {
|
||||||
|
return dynamicColumns.value.filter(
|
||||||
|
item => transformI18n(item.title) === transformI18n(title)
|
||||||
|
)[0].fixed
|
||||||
|
? true
|
||||||
|
: false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const rendTippyProps = (content: string) => {
|
||||||
|
// https://vue-tippy.netlify.app/props
|
||||||
|
return {
|
||||||
|
content,
|
||||||
|
offset: [0, 18],
|
||||||
|
duration: [300, 0],
|
||||||
|
followCursor: true,
|
||||||
|
hideOnClick: "toggle"
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const reference = {
|
||||||
|
reference: () => (
|
||||||
|
<SettingIcon
|
||||||
|
class={["w-[16px]", iconClass.value]}
|
||||||
|
v-tippy={rendTippyProps("列设置")}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
return () => (
|
||||||
|
<>
|
||||||
|
<div {...attrs} class="w-[99/100] mt-2 px-2 pb-2 bg-bg_color">
|
||||||
|
<div class="flex justify-between w-full h-[60px] p-4">
|
||||||
|
{slots?.title ? (
|
||||||
|
slots.title()
|
||||||
|
) : (
|
||||||
|
<p class="font-bold truncate">{props.title}</p>
|
||||||
|
)}
|
||||||
|
<div class="flex items-center justify-around">
|
||||||
|
{slots?.buttons ? (
|
||||||
|
<div class="flex mr-4">{slots.buttons()}</div>
|
||||||
|
) : null}
|
||||||
|
{props.tree ? (
|
||||||
|
<>
|
||||||
|
<ExpandIcon
|
||||||
|
class={["w-[16px]", iconClass.value]}
|
||||||
|
style={{
|
||||||
|
transform: isExpandAll.value ? "none" : "rotate(-90deg)"
|
||||||
|
}}
|
||||||
|
v-tippy={rendTippyProps(
|
||||||
|
isExpandAll.value ? "折叠" : "展开"
|
||||||
|
)}
|
||||||
|
onClick={() => onExpand()}
|
||||||
|
/>
|
||||||
|
<el-divider direction="vertical" />
|
||||||
|
</>
|
||||||
|
) : null}
|
||||||
|
<RefreshIcon
|
||||||
|
class={[
|
||||||
|
"w-[16px]",
|
||||||
|
iconClass.value,
|
||||||
|
loading.value ? "animate-spin" : ""
|
||||||
|
]}
|
||||||
|
v-tippy={rendTippyProps("刷新")}
|
||||||
|
onClick={() => onReFresh()}
|
||||||
|
/>
|
||||||
|
<el-divider direction="vertical" />
|
||||||
|
<el-dropdown
|
||||||
|
v-slots={dropdown}
|
||||||
|
trigger="click"
|
||||||
|
v-tippy={rendTippyProps("密度")}
|
||||||
|
>
|
||||||
|
<CollapseIcon class={["w-[16px]", iconClass.value]} />
|
||||||
|
</el-dropdown>
|
||||||
|
<el-divider direction="vertical" />
|
||||||
|
|
||||||
|
<el-popover
|
||||||
|
v-slots={reference}
|
||||||
|
placement="bottom-start"
|
||||||
|
popper-style={{ padding: 0 }}
|
||||||
|
width="200"
|
||||||
|
trigger="click"
|
||||||
|
>
|
||||||
|
<div class={[topClass.value]}>
|
||||||
|
<el-checkbox
|
||||||
|
class="!-mr-1"
|
||||||
|
label="列展示"
|
||||||
|
v-model={checkAll.value}
|
||||||
|
indeterminate={isIndeterminate.value}
|
||||||
|
onChange={value => handleCheckAllChange(value)}
|
||||||
|
/>
|
||||||
|
<el-button type="primary" link onClick={() => onReset()}>
|
||||||
|
重置
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="pt-[6px] pl-[11px]">
|
||||||
|
<el-scrollbar max-height="36vh">
|
||||||
|
<el-checkbox-group
|
||||||
|
ref={`VxeGroupRef${unref(props.tableKey)}`}
|
||||||
|
modelValue={checkedColumns.value}
|
||||||
|
onChange={value => handleCheckedColumnsChange(value)}
|
||||||
|
>
|
||||||
|
<el-space
|
||||||
|
direction="vertical"
|
||||||
|
alignment="flex-start"
|
||||||
|
size={0}
|
||||||
|
>
|
||||||
|
{checkColumnList.map((item, index) => {
|
||||||
|
return (
|
||||||
|
<div class="flex items-center">
|
||||||
|
<DragIcon
|
||||||
|
class={[
|
||||||
|
"drag-btn w-[16px] mr-2",
|
||||||
|
isFixedColumn(item)
|
||||||
|
? "!cursor-no-drop"
|
||||||
|
: "!cursor-grab"
|
||||||
|
]}
|
||||||
|
onMouseenter={(event: {
|
||||||
|
preventDefault: () => void;
|
||||||
|
}) => rowDrop(event)}
|
||||||
|
/>
|
||||||
|
<el-checkbox
|
||||||
|
key={index}
|
||||||
|
label={item}
|
||||||
|
value={item}
|
||||||
|
onChange={reloadColumn}
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
title={transformI18n(item)}
|
||||||
|
class="inline-block w-[120px] truncate hover:text-text_color_primary"
|
||||||
|
>
|
||||||
|
{transformI18n(item)}
|
||||||
|
</span>
|
||||||
|
</el-checkbox>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</el-space>
|
||||||
|
</el-checkbox-group>
|
||||||
|
</el-scrollbar>
|
||||||
|
</div>
|
||||||
|
</el-popover>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{slots.default({
|
||||||
|
size: size.value,
|
||||||
|
dynamicColumns: dynamicColumns.value
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
@ -20,11 +20,12 @@ import {
|
|||||||
// 可选组件
|
// 可选组件
|
||||||
Icon,
|
Icon,
|
||||||
Column,
|
Column,
|
||||||
|
Grid,
|
||||||
|
Pager,
|
||||||
|
Select,
|
||||||
// Colgroup,
|
// Colgroup,
|
||||||
// Grid,
|
|
||||||
// Tooltip,
|
// Tooltip,
|
||||||
// Toolbar,
|
// Toolbar,
|
||||||
// Pager,
|
|
||||||
// Form,
|
// Form,
|
||||||
// FormItem,
|
// FormItem,
|
||||||
// FormGather,
|
// FormGather,
|
||||||
@ -35,7 +36,6 @@ import {
|
|||||||
// RadioButton,
|
// RadioButton,
|
||||||
// Switch,
|
// Switch,
|
||||||
// Input,
|
// Input,
|
||||||
// Select,
|
|
||||||
// Optgroup,
|
// Optgroup,
|
||||||
// Option,
|
// Option,
|
||||||
// Textarea,
|
// Textarea,
|
||||||
@ -76,11 +76,12 @@ export function useVxeTable(app: App) {
|
|||||||
// 可选组件
|
// 可选组件
|
||||||
.use(Icon)
|
.use(Icon)
|
||||||
.use(Column)
|
.use(Column)
|
||||||
|
.use(Grid)
|
||||||
|
.use(Pager)
|
||||||
|
.use(Select)
|
||||||
// .use(Colgroup)
|
// .use(Colgroup)
|
||||||
// .use(Grid)
|
|
||||||
// .use(Tooltip)
|
// .use(Tooltip)
|
||||||
// .use(Toolbar)
|
// .use(Toolbar)
|
||||||
// .use(Pager)
|
|
||||||
// .use(Form)
|
// .use(Form)
|
||||||
// .use(FormItem)
|
// .use(FormItem)
|
||||||
// .use(FormGather)
|
// .use(FormGather)
|
||||||
@ -91,7 +92,6 @@ export function useVxeTable(app: App) {
|
|||||||
// .use(RadioButton)
|
// .use(RadioButton)
|
||||||
// .use(Switch)
|
// .use(Switch)
|
||||||
// .use(Input)
|
// .use(Input)
|
||||||
// .use(Select)
|
|
||||||
// .use(Optgroup)
|
// .use(Optgroup)
|
||||||
// .use(Option)
|
// .use(Option)
|
||||||
// .use(Textarea)
|
// .use(Textarea)
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import List from "./list.vue";
|
import List from "./list.vue";
|
||||||
import TreeList from "./treeList.vue";
|
import TreeList from "./treeList.vue";
|
||||||
|
import PageList from "./pageList.vue";
|
||||||
|
|
||||||
const rendContent = (val: string) =>
|
const rendContent = (val: string) =>
|
||||||
`代码位置:src/views/table/virtual/${val}.vue`;
|
`代码位置:src/views/table/virtual/${val}.vue`;
|
||||||
@ -8,13 +9,19 @@ export const list = [
|
|||||||
{
|
{
|
||||||
key: "list",
|
key: "list",
|
||||||
content: rendContent("list"),
|
content: rendContent("list"),
|
||||||
title: "虚拟列表",
|
title: "虚拟表格",
|
||||||
component: List
|
component: List
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "treeList",
|
key: "treeList",
|
||||||
content: rendContent("treeList"),
|
content: rendContent("treeList"),
|
||||||
title: "虚拟树",
|
title: "虚拟树形表格",
|
||||||
component: TreeList
|
component: TreeList
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "pageList",
|
||||||
|
content: rendContent("pageList"),
|
||||||
|
title: "分页表格",
|
||||||
|
component: PageList
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
@ -1,9 +1,20 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref } from "vue";
|
import { ref } from "vue";
|
||||||
|
import { VxeTableBar } from "@/components/ReVxeTableBar";
|
||||||
|
|
||||||
|
const vxeTableRef = ref();
|
||||||
|
const loading = ref(true);
|
||||||
const tableData = ref([]);
|
const tableData = ref([]);
|
||||||
|
|
||||||
setTimeout(() => {
|
const columns = [
|
||||||
|
{ type: "seq", field: "seq", title: "序号", width: 100 },
|
||||||
|
{ field: "name", title: "名称", sortable: true },
|
||||||
|
{ field: "role", title: "角色" },
|
||||||
|
{ field: "sex", title: "性别" }
|
||||||
|
];
|
||||||
|
|
||||||
|
async function onSearch() {
|
||||||
|
loading.value = true;
|
||||||
// 模拟数据
|
// 模拟数据
|
||||||
const mockList = [];
|
const mockList = [];
|
||||||
for (let index = 0; index < 500; index++) {
|
for (let index = 0; index < 500; index++) {
|
||||||
@ -15,21 +26,33 @@ setTimeout(() => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
tableData.value = mockList;
|
tableData.value = mockList;
|
||||||
}, 100);
|
setTimeout(() => {
|
||||||
|
loading.value = false;
|
||||||
|
}, 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
onSearch();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<vxe-table
|
<VxeTableBar
|
||||||
border
|
:vxeTableRef="vxeTableRef"
|
||||||
show-overflow
|
:columns="columns"
|
||||||
height="500"
|
title="虚拟表格"
|
||||||
:row-config="{ isHover: true }"
|
@refresh="onSearch"
|
||||||
:data="tableData"
|
|
||||||
:scroll-y="{ enabled: true }"
|
|
||||||
>
|
>
|
||||||
<vxe-column type="seq" title="序号" width="100" />
|
<template v-slot="{ size, dynamicColumns }">
|
||||||
<vxe-column field="name" title="名称" sortable />
|
<vxe-grid
|
||||||
<vxe-column field="role" title="角色" />
|
ref="vxeTableRef"
|
||||||
<vxe-column field="sex" title="性别" />
|
v-loading="loading"
|
||||||
</vxe-table>
|
show-overflow
|
||||||
|
height="500"
|
||||||
|
:size="size"
|
||||||
|
:row-config="{ isHover: true }"
|
||||||
|
:scroll-y="{ enabled: true }"
|
||||||
|
:columns="dynamicColumns"
|
||||||
|
:data="tableData"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</VxeTableBar>
|
||||||
</template>
|
</template>
|
||||||
|
86
src/views/table/virtual/pageList.vue
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref, reactive, computed } from "vue";
|
||||||
|
import { VxeTableBar } from "@/components/ReVxeTableBar";
|
||||||
|
|
||||||
|
const vxeTableRef = ref();
|
||||||
|
const loading = ref(true);
|
||||||
|
const tableData = ref([]);
|
||||||
|
|
||||||
|
const getTableHeight = computed(() => {
|
||||||
|
return (size: string) => {
|
||||||
|
switch (size) {
|
||||||
|
case "medium":
|
||||||
|
return 531;
|
||||||
|
case "small":
|
||||||
|
return 482;
|
||||||
|
case "mini":
|
||||||
|
return 433;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const pagerConfig = reactive({
|
||||||
|
total: 0,
|
||||||
|
currentPage: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
pageSizes: [5, 10, 15, 20]
|
||||||
|
});
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{ type: "seq", field: "seq", title: "序号", width: 100 },
|
||||||
|
{ field: "name", title: "名称", sortable: true },
|
||||||
|
{ field: "role", title: "角色" },
|
||||||
|
{ field: "sex", title: "性别" }
|
||||||
|
];
|
||||||
|
|
||||||
|
async function onSearch() {
|
||||||
|
loading.value = true;
|
||||||
|
// 模拟数据
|
||||||
|
const mockList = [];
|
||||||
|
for (let index = 0; index < 10; index++) {
|
||||||
|
mockList.push({
|
||||||
|
id: index,
|
||||||
|
name: "Test" + index,
|
||||||
|
role: "Developer",
|
||||||
|
sex: "男"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
pagerConfig.total = 20;
|
||||||
|
tableData.value = mockList;
|
||||||
|
setTimeout(() => {
|
||||||
|
loading.value = false;
|
||||||
|
}, 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
const handlePageChange = ({ currentPage, pageSize }) => {
|
||||||
|
pagerConfig.currentPage = currentPage;
|
||||||
|
pagerConfig.pageSize = pageSize;
|
||||||
|
onSearch();
|
||||||
|
};
|
||||||
|
|
||||||
|
onSearch();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<VxeTableBar
|
||||||
|
:vxeTableRef="vxeTableRef"
|
||||||
|
:columns="columns"
|
||||||
|
title="分页表格"
|
||||||
|
@refresh="onSearch"
|
||||||
|
>
|
||||||
|
<template v-slot="{ size, dynamicColumns }">
|
||||||
|
<vxe-grid
|
||||||
|
ref="vxeTableRef"
|
||||||
|
v-loading="loading"
|
||||||
|
show-overflow
|
||||||
|
:height="getTableHeight(size)"
|
||||||
|
:size="size"
|
||||||
|
:column-config="{ resizable: true }"
|
||||||
|
:columns="dynamicColumns"
|
||||||
|
:pagerConfig="pagerConfig"
|
||||||
|
:data="tableData"
|
||||||
|
@page-change="handlePageChange"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</VxeTableBar>
|
||||||
|
</template>
|
@ -1,56 +1,51 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref } from "vue";
|
import { ref } from "vue";
|
||||||
import treeList from "./tree.json";
|
import treeList from "./tree.json";
|
||||||
|
import { VxeTableBar } from "@/components/ReVxeTableBar";
|
||||||
|
|
||||||
const tableRef = ref();
|
const vxeTableRef = ref();
|
||||||
|
|
||||||
const loading = ref(false);
|
const loading = ref(true);
|
||||||
const tableData = ref([]);
|
const tableData = ref([]);
|
||||||
|
|
||||||
const loadList = () => {
|
const columns = [
|
||||||
|
{ type: "seq", field: "seq", title: "序号", width: 200, treeNode: true },
|
||||||
|
{ field: "id", title: "Id" },
|
||||||
|
{ field: "name", title: "地点" }
|
||||||
|
];
|
||||||
|
|
||||||
|
async function onSearch() {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
|
tableData.value = treeList;
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
tableData.value = treeList;
|
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
}, 200);
|
}, 500);
|
||||||
};
|
}
|
||||||
|
|
||||||
const expandAllEvent = () => {
|
onSearch();
|
||||||
const $table = tableRef.value;
|
|
||||||
if ($table) {
|
|
||||||
$table.setAllTreeExpand(true);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const claseExpandEvent = () => {
|
|
||||||
const $table = tableRef.value;
|
|
||||||
if ($table) {
|
|
||||||
$table.clearTreeExpand();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
loadList();
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<VxeTableBar
|
||||||
<div class="mb-4">
|
tree
|
||||||
<el-button @click="expandAllEvent">展开所有</el-button>
|
title="虚拟树形表格"
|
||||||
<el-button @click="claseExpandEvent">收起所有</el-button>
|
:isExpandAll="false"
|
||||||
</div>
|
:vxeTableRef="vxeTableRef"
|
||||||
|
:columns="columns"
|
||||||
<vxe-table
|
@refresh="onSearch"
|
||||||
ref="tableRef"
|
>
|
||||||
show-overflow
|
<template v-slot="{ size, dynamicColumns }">
|
||||||
height="500"
|
<vxe-grid
|
||||||
:loading="loading"
|
ref="vxeTableRef"
|
||||||
:tree-config="{ transform: true }"
|
v-loading="loading"
|
||||||
:scroll-y="{ enabled: true, gt: 20 }"
|
show-overflow
|
||||||
:data="tableData"
|
height="500"
|
||||||
>
|
:size="size"
|
||||||
<vxe-column type="seq" title="序号" width="200" tree-node />
|
:tree-config="{ transform: true }"
|
||||||
<vxe-column field="id" title="Id" />
|
:scroll-y="{ enabled: true, gt: 20 }"
|
||||||
<vxe-column field="name" title="地点" />
|
:columns="dynamicColumns"
|
||||||
</vxe-table>
|
:data="tableData"
|
||||||
</div>
|
/>
|
||||||
|
</template>
|
||||||
|
</VxeTableBar>
|
||||||
</template>
|
</template>
|
||||||
|