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>
 | 
			
		||||
 | 
			
		||||