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
|
||||
} from "@pureadmin/utils";
|
||||
|
||||
import DragIcon from "./svg/drag.svg?component";
|
||||
import ExpandIcon from "./svg/expand.svg?component";
|
||||
import RefreshIcon from "./svg/refresh.svg?component";
|
||||
import SettingIcon from "./svg/settings.svg?component";
|
||||
import CollapseIcon from "./svg/collapse.svg?component";
|
||||
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 = {
|
||||
/** 头部最左边的标题 */
|
||||
|
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,
|
||||
Column,
|
||||
Grid,
|
||||
Pager,
|
||||
Select,
|
||||
// Colgroup,
|
||||
// Grid,
|
||||
// Tooltip,
|
||||
// Toolbar,
|
||||
// Pager,
|
||||
// Form,
|
||||
// FormItem,
|
||||
// FormGather,
|
||||
@ -35,7 +36,6 @@ import {
|
||||
// RadioButton,
|
||||
// Switch,
|
||||
// Input,
|
||||
// Select,
|
||||
// Optgroup,
|
||||
// Option,
|
||||
// Textarea,
|
||||
@ -76,11 +76,12 @@ export function useVxeTable(app: App) {
|
||||
// 可选组件
|
||||
.use(Icon)
|
||||
.use(Column)
|
||||
.use(Grid)
|
||||
.use(Pager)
|
||||
.use(Select)
|
||||
// .use(Colgroup)
|
||||
// .use(Grid)
|
||||
// .use(Tooltip)
|
||||
// .use(Toolbar)
|
||||
// .use(Pager)
|
||||
// .use(Form)
|
||||
// .use(FormItem)
|
||||
// .use(FormGather)
|
||||
@ -91,7 +92,6 @@ export function useVxeTable(app: App) {
|
||||
// .use(RadioButton)
|
||||
// .use(Switch)
|
||||
// .use(Input)
|
||||
// .use(Select)
|
||||
// .use(Optgroup)
|
||||
// .use(Option)
|
||||
// .use(Textarea)
|
||||
|
@ -1,5 +1,6 @@
|
||||
import List from "./list.vue";
|
||||
import TreeList from "./treeList.vue";
|
||||
import PageList from "./pageList.vue";
|
||||
|
||||
const rendContent = (val: string) =>
|
||||
`代码位置:src/views/table/virtual/${val}.vue`;
|
||||
@ -8,13 +9,19 @@ export const list = [
|
||||
{
|
||||
key: "list",
|
||||
content: rendContent("list"),
|
||||
title: "虚拟列表",
|
||||
title: "虚拟表格",
|
||||
component: List
|
||||
},
|
||||
{
|
||||
key: "treeList",
|
||||
content: rendContent("treeList"),
|
||||
title: "虚拟树",
|
||||
title: "虚拟树形表格",
|
||||
component: TreeList
|
||||
},
|
||||
{
|
||||
key: "pageList",
|
||||
content: rendContent("pageList"),
|
||||
title: "分页表格",
|
||||
component: PageList
|
||||
}
|
||||
];
|
||||
|
@ -1,9 +1,20 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref } from "vue";
|
||||
import { VxeTableBar } from "@/components/ReVxeTableBar";
|
||||
|
||||
const vxeTableRef = ref();
|
||||
const loading = ref(true);
|
||||
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 = [];
|
||||
for (let index = 0; index < 500; index++) {
|
||||
@ -15,21 +26,33 @@ setTimeout(() => {
|
||||
});
|
||||
}
|
||||
tableData.value = mockList;
|
||||
}, 100);
|
||||
setTimeout(() => {
|
||||
loading.value = false;
|
||||
}, 500);
|
||||
}
|
||||
|
||||
onSearch();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<vxe-table
|
||||
border
|
||||
<VxeTableBar
|
||||
:vxeTableRef="vxeTableRef"
|
||||
:columns="columns"
|
||||
title="虚拟表格"
|
||||
@refresh="onSearch"
|
||||
>
|
||||
<template v-slot="{ size, dynamicColumns }">
|
||||
<vxe-grid
|
||||
ref="vxeTableRef"
|
||||
v-loading="loading"
|
||||
show-overflow
|
||||
height="500"
|
||||
:size="size"
|
||||
:row-config="{ isHover: true }"
|
||||
:data="tableData"
|
||||
:scroll-y="{ enabled: true }"
|
||||
>
|
||||
<vxe-column type="seq" title="序号" width="100" />
|
||||
<vxe-column field="name" title="名称" sortable />
|
||||
<vxe-column field="role" title="角色" />
|
||||
<vxe-column field="sex" title="性别" />
|
||||
</vxe-table>
|
||||
:columns="dynamicColumns"
|
||||
:data="tableData"
|
||||
/>
|
||||
</template>
|
||||
</VxeTableBar>
|
||||
</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>
|
||||
import { ref } from "vue";
|
||||
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 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;
|
||||
setTimeout(() => {
|
||||
tableData.value = treeList;
|
||||
setTimeout(() => {
|
||||
loading.value = false;
|
||||
}, 200);
|
||||
};
|
||||
|
||||
const expandAllEvent = () => {
|
||||
const $table = tableRef.value;
|
||||
if ($table) {
|
||||
$table.setAllTreeExpand(true);
|
||||
}, 500);
|
||||
}
|
||||
};
|
||||
|
||||
const claseExpandEvent = () => {
|
||||
const $table = tableRef.value;
|
||||
if ($table) {
|
||||
$table.clearTreeExpand();
|
||||
}
|
||||
};
|
||||
|
||||
loadList();
|
||||
onSearch();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div class="mb-4">
|
||||
<el-button @click="expandAllEvent">展开所有</el-button>
|
||||
<el-button @click="claseExpandEvent">收起所有</el-button>
|
||||
</div>
|
||||
|
||||
<vxe-table
|
||||
ref="tableRef"
|
||||
<VxeTableBar
|
||||
tree
|
||||
title="虚拟树形表格"
|
||||
:isExpandAll="false"
|
||||
:vxeTableRef="vxeTableRef"
|
||||
:columns="columns"
|
||||
@refresh="onSearch"
|
||||
>
|
||||
<template v-slot="{ size, dynamicColumns }">
|
||||
<vxe-grid
|
||||
ref="vxeTableRef"
|
||||
v-loading="loading"
|
||||
show-overflow
|
||||
height="500"
|
||||
:loading="loading"
|
||||
:size="size"
|
||||
:tree-config="{ transform: true }"
|
||||
:scroll-y="{ enabled: true, gt: 20 }"
|
||||
:columns="dynamicColumns"
|
||||
:data="tableData"
|
||||
>
|
||||
<vxe-column type="seq" title="序号" width="200" tree-node />
|
||||
<vxe-column field="id" title="Id" />
|
||||
<vxe-column field="name" title="地点" />
|
||||
</vxe-table>
|
||||
</div>
|
||||
/>
|
||||
</template>
|
||||
</VxeTableBar>
|
||||
</template>
|
||||
|