923 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			923 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
<script lang="ts">
 | 
						|
export default {
 | 
						|
  name: "LayTable",
 | 
						|
};
 | 
						|
</script>
 | 
						|
 | 
						|
<script setup lang="ts">
 | 
						|
import "./index.less";
 | 
						|
import {
 | 
						|
  ref,
 | 
						|
  watch,
 | 
						|
  useSlots,
 | 
						|
  withDefaults,
 | 
						|
  onMounted,
 | 
						|
  StyleValue,
 | 
						|
  WritableComputedRef,
 | 
						|
  computed,
 | 
						|
  onBeforeUnmount,
 | 
						|
} from "vue";
 | 
						|
import { Recordable } from "../../types";
 | 
						|
import LayCheckbox from "../checkbox/index.vue";
 | 
						|
import LayDropdown from "../dropdown/index.vue";
 | 
						|
import LayEmpty from "../empty/index.vue";
 | 
						|
import TableRow from "./TableRow.vue";
 | 
						|
import TablePage from "./TablePage.vue";
 | 
						|
import { nextTick } from "vue";
 | 
						|
 | 
						|
export interface TableProps {
 | 
						|
  id?: string;
 | 
						|
  skin?: string;
 | 
						|
  size?: string;
 | 
						|
  page?: Recordable;
 | 
						|
  columns: Recordable[];
 | 
						|
  dataSource: Recordable[];
 | 
						|
  defaultToolbar?: boolean | Recordable[];
 | 
						|
  selectedKey?: string;
 | 
						|
  selectedKeys?: Recordable[];
 | 
						|
  indentSize?: number;
 | 
						|
  childrenColumnName?: string;
 | 
						|
  height?: number;
 | 
						|
  maxHeight?: string;
 | 
						|
  even?: boolean;
 | 
						|
  expandIndex?: number;
 | 
						|
  rowClassName?: string | Function;
 | 
						|
  cellClassName?: string | Function;
 | 
						|
  rowStyle?: string | Function;
 | 
						|
  cellStyle?: string | Function;
 | 
						|
  spanMethod?: Function;
 | 
						|
  defaultExpandAll?: boolean;
 | 
						|
  expandKeys?: Recordable[];
 | 
						|
  loading?: boolean;
 | 
						|
  getCheckboxProps?: Function;
 | 
						|
  getRadioProps?: Function;
 | 
						|
}
 | 
						|
 | 
						|
const props = withDefaults(defineProps<TableProps>(), {
 | 
						|
  id: "id",
 | 
						|
  size: "md",
 | 
						|
  indentSize: 30,
 | 
						|
  childrenColumnName: "children",
 | 
						|
  dataSource: () => [],
 | 
						|
  selectedKeys: () => [],
 | 
						|
  selectedKey: "",
 | 
						|
  maxHeight: "auto",
 | 
						|
  even: false,
 | 
						|
  rowClassName: "",
 | 
						|
  cellClassName: "",
 | 
						|
  expandIndex: 0,
 | 
						|
  rowStyle: "",
 | 
						|
  cellStyle: "",
 | 
						|
  defaultExpandAll: false,
 | 
						|
  spanMethod: () => {},
 | 
						|
  expandKeys: () => [],
 | 
						|
  loading: false,
 | 
						|
  getCheckboxProps: () => {},
 | 
						|
  getRadioProps: () => {},
 | 
						|
});
 | 
						|
 | 
						|
const emit = defineEmits([
 | 
						|
  "change",
 | 
						|
  "update:expandKeys",
 | 
						|
  "update:selectedKeys",
 | 
						|
  "update:selectedKey",
 | 
						|
  "row-contextmenu",
 | 
						|
  "row-double",
 | 
						|
  "row",
 | 
						|
]);
 | 
						|
 | 
						|
const slot = useSlots();
 | 
						|
const slots = slot.default && slot.default();
 | 
						|
const tableRef = ref();
 | 
						|
 | 
						|
const s = "";
 | 
						|
const allChecked = ref(false);
 | 
						|
const hasChecked = ref(false);
 | 
						|
const tableDataSource = ref<any[]>([...props.dataSource]);
 | 
						|
const tableColumns = computed(() => {
 | 
						|
  return [...props.columns];
 | 
						|
});
 | 
						|
 | 
						|
const tableHeadColumns = ref<any[]>([]);
 | 
						|
const tableBodyColumns = ref<any[]>([]);
 | 
						|
 | 
						|
/**
 | 
						|
 * 获取数组深度
 | 
						|
 *
 | 
						|
 * @param arr 数组
 | 
						|
 */
 | 
						|
const getLevel = (arr: any[]) => {
 | 
						|
  let maxLevel = 0;
 | 
						|
  (function callBack(arr, level) {
 | 
						|
    ++level;
 | 
						|
    maxLevel = Math.max(level, maxLevel);
 | 
						|
    for (let i = 0; i < arr.length; i++) {
 | 
						|
      let item = arr[i];
 | 
						|
      if (item.children && item.children.length > 0) {
 | 
						|
        callBack(item.children, level);
 | 
						|
      } else {
 | 
						|
        delete item.children;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  })(arr, 0);
 | 
						|
  return maxLevel;
 | 
						|
};
 | 
						|
 | 
						|
/**
 | 
						|
 * 获取叶节点的数量
 | 
						|
 *
 | 
						|
 * @param json 当前节点
 | 
						|
 */
 | 
						|
function getLeafCountTree(json: any) {
 | 
						|
  if (!json.children || json.children.length == 0) {
 | 
						|
    json.colspan = 1;
 | 
						|
    return 1;
 | 
						|
  } else {
 | 
						|
    var leafCount = 0;
 | 
						|
    for (var i = 0; i < json.children.length; i++) {
 | 
						|
      leafCount = leafCount + getLeafCountTree(json.children[i]);
 | 
						|
    }
 | 
						|
    json.colspan = leafCount;
 | 
						|
    return leafCount;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * 计算内容列
 | 
						|
 *
 | 
						|
 * @param columns 原始列
 | 
						|
 */
 | 
						|
const findFindNode = (columns: any[]) => {
 | 
						|
  columns.forEach((column) => {
 | 
						|
    if (column.children) {
 | 
						|
      findFindNode(column.children);
 | 
						|
    } else {
 | 
						|
      tableBodyColumns.value.push(column);
 | 
						|
    }
 | 
						|
  });
 | 
						|
};
 | 
						|
 | 
						|
findFindNode(tableColumns.value);
 | 
						|
 | 
						|
/**
 | 
						|
 * 计算显示列
 | 
						|
 *
 | 
						|
 * @param columns 原始列
 | 
						|
 */
 | 
						|
const tableColumnKeys = ref<any[]>([]);
 | 
						|
 | 
						|
const findFindNodes = (columns: any[]) => {
 | 
						|
  columns.forEach((column) => {
 | 
						|
    if (column.children) {
 | 
						|
      tableColumnKeys.value.push(column.key);
 | 
						|
      findFindNodes(column.children);
 | 
						|
    } else {
 | 
						|
      if (!column.hide) {
 | 
						|
        tableColumnKeys.value.push(column.key);
 | 
						|
      }
 | 
						|
    }
 | 
						|
  });
 | 
						|
};
 | 
						|
 | 
						|
findFindNodes(tableColumns.value);
 | 
						|
 | 
						|
/**
 | 
						|
 * 计算数组差异
 | 
						|
 *
 | 
						|
 * @param arr1 数组
 | 
						|
 * @param arr2 数组
 | 
						|
 */
 | 
						|
function diff(arr1: any[], arr2: any[]) {
 | 
						|
  var newArr = [];
 | 
						|
  arr1 = Array.from(new Set(arr1)); // 去重
 | 
						|
  arr2 = Array.from(new Set(arr2)); // 去重
 | 
						|
  newArr = arr1.concat(arr2);
 | 
						|
  return newArr.filter((x) => !(arr1.includes(x) && arr2.includes(x)));
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * 计算标题列
 | 
						|
 *
 | 
						|
 * @param level 层级, 用于决定会被 push 到的目标数组
 | 
						|
 */
 | 
						|
const findFinalNode = (level: number, columns: any[]) => {
 | 
						|
  columns.forEach((column) => {
 | 
						|
    if (column.children) {
 | 
						|
      const colSpan = getLeafCountTree(column);
 | 
						|
      column.colspan = colSpan;
 | 
						|
      if (!tableHeadColumns.value[level]) {
 | 
						|
        tableHeadColumns.value[level] = [];
 | 
						|
      }
 | 
						|
      // 如果列固定,并且 width 不存在, 设置默认值
 | 
						|
      if (column.fixed && !column.width) {
 | 
						|
        column.type ? (column.width = "50px") : (column.width = "100px");
 | 
						|
      }
 | 
						|
      tableHeadColumns.value[level].push(column);
 | 
						|
      findFinalNode(level + 1, column.children);
 | 
						|
    } else {
 | 
						|
      const rowSpan = getLevel(columns);
 | 
						|
      column.rowspan = rowSpan;
 | 
						|
      if (!tableHeadColumns.value[level]) {
 | 
						|
        tableHeadColumns.value[level] = [];
 | 
						|
      }
 | 
						|
      // 如果列固定,并且 width 不存在, 设置默认值
 | 
						|
      if (column.fixed && !column.width) {
 | 
						|
        column.type ? (column.width = "50px") : (column.width = "100px");
 | 
						|
      }
 | 
						|
      tableHeadColumns.value[level].push(column);
 | 
						|
    }
 | 
						|
  });
 | 
						|
};
 | 
						|
 | 
						|
findFinalNode(0, tableColumns.value);
 | 
						|
 | 
						|
const tableSelectedKeys = ref<Recordable[]>([...props.selectedKeys]);
 | 
						|
const tableExpandKeys = ref<Recordable[]>([...props.expandKeys]);
 | 
						|
 | 
						|
watch(
 | 
						|
  () => props.selectedKeys,
 | 
						|
  () => {
 | 
						|
    tableSelectedKeys.value = props.selectedKeys;
 | 
						|
  },
 | 
						|
  { deep: true }
 | 
						|
);
 | 
						|
 | 
						|
watch(
 | 
						|
  () => props.expandKeys,
 | 
						|
  () => {
 | 
						|
    tableExpandKeys.value = props.expandKeys;
 | 
						|
  },
 | 
						|
  { deep: true }
 | 
						|
);
 | 
						|
 | 
						|
const tableSelectedKey: WritableComputedRef<string> = computed({
 | 
						|
  get() {
 | 
						|
    return props.selectedKey;
 | 
						|
  },
 | 
						|
  set(val) {
 | 
						|
    emit("update:selectedKey", val);
 | 
						|
  },
 | 
						|
});
 | 
						|
 | 
						|
watch(
 | 
						|
  () => props.dataSource,
 | 
						|
  () => {
 | 
						|
    tableDataSource.value = [...props.dataSource];
 | 
						|
    tableSelectedKeys.value = [];
 | 
						|
    tableSelectedKey.value = s;
 | 
						|
  },
 | 
						|
  { deep: true }
 | 
						|
);
 | 
						|
 | 
						|
const changeAll = (isChecked: boolean) => {
 | 
						|
  if (isChecked) {
 | 
						|
    const datasources = props.dataSource.filter((item: any, index: number) => {
 | 
						|
      return !props.getCheckboxProps(item, index)?.disabled;
 | 
						|
    });
 | 
						|
    const ids = datasources.map((item) => {
 | 
						|
      return item[props.id];
 | 
						|
    });
 | 
						|
    tableSelectedKeys.value = [...ids];
 | 
						|
  } else {
 | 
						|
    tableSelectedKeys.value = [];
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
watch(
 | 
						|
  tableSelectedKeys,
 | 
						|
  () => {
 | 
						|
    if (tableSelectedKeys.value.length === props.dataSource.length) {
 | 
						|
      allChecked.value = true;
 | 
						|
    } else {
 | 
						|
      allChecked.value = false;
 | 
						|
    }
 | 
						|
    if (tableSelectedKeys.value.length > 0) {
 | 
						|
      hasChecked.value = true;
 | 
						|
    } else {
 | 
						|
      hasChecked.value = false;
 | 
						|
    }
 | 
						|
    emit("update:selectedKeys", tableSelectedKeys.value);
 | 
						|
  },
 | 
						|
  { deep: true, immediate: true }
 | 
						|
);
 | 
						|
 | 
						|
watch(
 | 
						|
  tableExpandKeys,
 | 
						|
  () => {
 | 
						|
    emit("update:expandKeys", tableExpandKeys.value);
 | 
						|
  },
 | 
						|
  { deep: true, immediate: true }
 | 
						|
);
 | 
						|
 | 
						|
const change = function (page: any) {
 | 
						|
  emit("change", page);
 | 
						|
};
 | 
						|
 | 
						|
const rowClick = function (data: any, evt: MouseEvent) {
 | 
						|
  emit("row", data, evt);
 | 
						|
};
 | 
						|
 | 
						|
const rowDoubleClick = function (data: any, evt: MouseEvent) {
 | 
						|
  emit("row-double", data, evt);
 | 
						|
};
 | 
						|
 | 
						|
const rowContextmenu = (data: any, evt: MouseEvent) => {
 | 
						|
  emit("row-contextmenu", data, evt);
 | 
						|
};
 | 
						|
 | 
						|
// 页面打印
 | 
						|
const print = () => {
 | 
						|
  let subOutputRankPrint = tableRef.value as HTMLElement;
 | 
						|
  let newContent = subOutputRankPrint.innerHTML;
 | 
						|
  let oldContent = document.body.innerHTML;
 | 
						|
  document.body.innerHTML = newContent;
 | 
						|
  window.print();
 | 
						|
  window.location.reload();
 | 
						|
  document.body.innerHTML = oldContent;
 | 
						|
};
 | 
						|
 | 
						|
// 报表导出
 | 
						|
const exportData = () => {
 | 
						|
  var tableStr = ``;
 | 
						|
  for (let tableHeadColumn of tableHeadColumns.value) {
 | 
						|
    tableStr += "<tr>";
 | 
						|
    for (let column of tableHeadColumn) {
 | 
						|
      tableStr += `<td colspan=${column.colspan} rowspan=${column.rowspan}>${column.title}</td>`;
 | 
						|
    }
 | 
						|
    tableStr += "</tr>";
 | 
						|
  }
 | 
						|
  tableDataSource.value.forEach((item, rowIndex) => {
 | 
						|
    tableStr += "<tr>";
 | 
						|
    tableBodyColumns.value.forEach((tableColumn, columnIndex) => {
 | 
						|
      Object.keys(item).forEach((name) => {
 | 
						|
        if (tableColumn.key === name) {
 | 
						|
          const rowColSpan = props.spanMethod(
 | 
						|
            item,
 | 
						|
            tableColumn,
 | 
						|
            rowIndex,
 | 
						|
            columnIndex
 | 
						|
          );
 | 
						|
          const rowspan = rowColSpan ? rowColSpan[0] : 1;
 | 
						|
          const colspan = rowColSpan ? rowColSpan[1] : 1;
 | 
						|
          if (rowspan != 0 && colspan != 0) {
 | 
						|
            tableStr += `<td colspan=${colspan} rowspan=${rowspan}>${item[name]}</td>`;
 | 
						|
          }
 | 
						|
        }
 | 
						|
      });
 | 
						|
    });
 | 
						|
    tableStr += "</tr>";
 | 
						|
  });
 | 
						|
  var worksheet = "Sheet1";
 | 
						|
  var uri = "data:application/vnd.ms-excel;base64,";
 | 
						|
  var exportTemplate = `<html xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-microsoft-com:office:excel" 
 | 
						|
        xmlns="http://www.w3.org/TR/REC-html40">
 | 
						|
        <head><!--[if gte mso 9]><xml><x:ExcelWorkbook><x:ExcelWorksheets><x:ExcelWorksheet>
 | 
						|
            <x:Name>${worksheet}</x:Name>
 | 
						|
                <x:WorksheetOptions><x:DisplayGridlines/></x:WorksheetOptions></x:ExcelWorksheet>
 | 
						|
            </x:ExcelWorksheets></x:ExcelWorkbook></xml><![endif]-->
 | 
						|
        </head>
 | 
						|
        <body>
 | 
						|
            <table syle="table-layout: fixed;word-wrap: break-word; word-break: break-all;">${tableStr}</table>
 | 
						|
        </body>
 | 
						|
    </html>`;
 | 
						|
  window.location.href = uri + base64(exportTemplate);
 | 
						|
  return;
 | 
						|
};
 | 
						|
 | 
						|
// BASE64编码
 | 
						|
function base64(s: string) {
 | 
						|
  return window.btoa(unescape(encodeURIComponent(s)));
 | 
						|
}
 | 
						|
 | 
						|
// 列排序
 | 
						|
const sortTable = (e: any, key: string, sort: string) => {
 | 
						|
  let currentSort = e.target.parentNode.getAttribute("lay-sort");
 | 
						|
  if (sort === "desc") {
 | 
						|
    if (currentSort === sort) {
 | 
						|
      e.target.parentNode.setAttribute("lay-sort", "");
 | 
						|
      tableDataSource.value = [...props.dataSource];
 | 
						|
    } else {
 | 
						|
      e.target.parentNode.setAttribute("lay-sort", "desc");
 | 
						|
      tableDataSource.value.sort((x, y) => {
 | 
						|
        if (x[key] < y[key]) return 1;
 | 
						|
        else if (x[key] > y[key]) return -1;
 | 
						|
        else return 0;
 | 
						|
      });
 | 
						|
    }
 | 
						|
  } else {
 | 
						|
    if (currentSort === sort) {
 | 
						|
      e.target.parentNode.setAttribute("lay-sort", "");
 | 
						|
      tableDataSource.value = [...props.dataSource];
 | 
						|
    } else {
 | 
						|
      e.target.parentNode.setAttribute("lay-sort", "asc");
 | 
						|
      tableDataSource.value.sort((x, y) => {
 | 
						|
        if (x[key] < y[key]) return -1;
 | 
						|
        else if (x[key] > y[key]) return 1;
 | 
						|
        else return 0;
 | 
						|
      });
 | 
						|
    }
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
let tableBody = ref<HTMLElement | null>(null);
 | 
						|
let tableHeader = ref<HTMLElement | null>(null);
 | 
						|
let tableHeaderTable = ref<HTMLElement | null>(null);
 | 
						|
const tableBodyEmptyWidth = ref();
 | 
						|
let scrollWidthCell = ref(0);
 | 
						|
 | 
						|
const getScrollWidth = () => {
 | 
						|
  const clientWidth: number = tableBody.value?.clientWidth || 0;
 | 
						|
  const offsetWidth: number = tableBody.value?.offsetWidth || 0;
 | 
						|
  if (clientWidth < offsetWidth) {
 | 
						|
    scrollWidthCell.value = offsetWidth - clientWidth;
 | 
						|
  } else {
 | 
						|
    scrollWidthCell.value = 0;
 | 
						|
  }
 | 
						|
  tableBodyEmptyWidth.value = tableHeaderTable.value?.offsetWidth + "px";
 | 
						|
};
 | 
						|
 | 
						|
const hasl = ref(false);
 | 
						|
const hasr = ref(false);
 | 
						|
 | 
						|
const classes = computed(() => {
 | 
						|
  return [
 | 
						|
    hasl.value ? "layui-table-has-fixed-left" : "",
 | 
						|
    hasr.value ? "layui-table-has-fixed-right" : "",
 | 
						|
  ];
 | 
						|
});
 | 
						|
 | 
						|
watch(
 | 
						|
  () => [props.height, props.maxHeight, props.dataSource],
 | 
						|
  () => {
 | 
						|
    nextTick(() => {
 | 
						|
      getScrollWidth();
 | 
						|
    });
 | 
						|
  }
 | 
						|
);
 | 
						|
 | 
						|
onMounted(() => {
 | 
						|
  getScrollWidth();
 | 
						|
  getFixedColumn();
 | 
						|
 | 
						|
  tableBody.value?.addEventListener("scroll", () => {
 | 
						|
    getFixedColumn();
 | 
						|
  });
 | 
						|
 | 
						|
  tableBody.value?.addEventListener("transitionend", () => {
 | 
						|
    getScrollWidth();
 | 
						|
  });
 | 
						|
 | 
						|
  window.onresize = () => {
 | 
						|
    getScrollWidth();
 | 
						|
    getFixedColumn();
 | 
						|
  };
 | 
						|
});
 | 
						|
 | 
						|
const getFixedColumn = () => {
 | 
						|
  tableHeader.value!.scrollLeft = tableBody.value?.scrollLeft || 0;
 | 
						|
  // @ts-ignore
 | 
						|
  if (tableBody.value?.scrollWidth > tableBody.value?.clientWidth) {
 | 
						|
    if (tableBody.value?.scrollLeft == 0) {
 | 
						|
      hasl.value = false;
 | 
						|
      hasr.value = true;
 | 
						|
    } else {
 | 
						|
      // @ts-ignore
 | 
						|
      const t = tableBody.value?.scrollLeft + tableBody.value?.offsetWidth + 2;
 | 
						|
      const s = tableBody.value?.scrollWidth;
 | 
						|
      // @ts-ignore
 | 
						|
      if (t > s) {
 | 
						|
        hasl.value = true;
 | 
						|
        hasr.value = false;
 | 
						|
      } else {
 | 
						|
        hasl.value = true;
 | 
						|
        hasr.value = true;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  } else {
 | 
						|
    hasl.value = false;
 | 
						|
    hasr.value = false;
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
const slotsData = ref<string[]>([]);
 | 
						|
 | 
						|
props.columns.map((value: any) => {
 | 
						|
  if (value.customSlot) {
 | 
						|
    slotsData.value.push(value.customSlot);
 | 
						|
  }
 | 
						|
});
 | 
						|
 | 
						|
const currentIndentSize = ref(0);
 | 
						|
const childrenExpandSpace = computed(() => {
 | 
						|
  return (
 | 
						|
    props.dataSource.find((value: any) => {
 | 
						|
      if (value[props.childrenColumnName]) {
 | 
						|
        return true;
 | 
						|
      }
 | 
						|
    }) != undefined
 | 
						|
  );
 | 
						|
});
 | 
						|
 | 
						|
/**
 | 
						|
 * @remark 排除 hide 列
 | 
						|
 */
 | 
						|
const renderFixedStyle = (column: any, columnIndex: number) => {
 | 
						|
  if (column.fixed) {
 | 
						|
    if (column.fixed == "left") {
 | 
						|
      var left = 0;
 | 
						|
      for (var i = 0; i < columnIndex; i++) {
 | 
						|
        if (
 | 
						|
          props.columns[i].fixed &&
 | 
						|
          props.columns[i].fixed == "left" &&
 | 
						|
          tableColumnKeys.value.includes(props.columns[i].key)
 | 
						|
        ) {
 | 
						|
          left = left + Number(props.columns[i]?.width?.replace("px", ""));
 | 
						|
        }
 | 
						|
      }
 | 
						|
      return { left: `${left}px` } as StyleValue;
 | 
						|
    } else {
 | 
						|
      var right = 0;
 | 
						|
      for (var i = columnIndex + 1; i < props.columns.length; i++) {
 | 
						|
        if (
 | 
						|
          props.columns[i].fixed &&
 | 
						|
          props.columns[i].fixed == "right" &&
 | 
						|
          tableColumnKeys.value.includes(props.columns[i].key)
 | 
						|
        ) {
 | 
						|
          right = right + Number(props.columns[i]?.width?.replace("px", ""));
 | 
						|
        }
 | 
						|
      }
 | 
						|
      return { right: `${right}px` } as StyleValue;
 | 
						|
    }
 | 
						|
  } else {
 | 
						|
    var isLast = true;
 | 
						|
    for (var i = columnIndex + 1; i < props.columns.length; i++) {
 | 
						|
      if (
 | 
						|
        props.columns[i].fixed == undefined &&
 | 
						|
        tableColumnKeys.value.includes(props.columns[i].key)
 | 
						|
      ) {
 | 
						|
        isLast = false;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    return isLast ? ({ "border-right": "none" } as StyleValue) : {};
 | 
						|
  }
 | 
						|
  return {} as StyleValue;
 | 
						|
};
 | 
						|
 | 
						|
/**
 | 
						|
 * @remark 排除 hide 列
 | 
						|
 */
 | 
						|
const renderFixedClassName = (column: any, columnIndex: number) => {
 | 
						|
  if (column.fixed) {
 | 
						|
    if (column.fixed == "left") {
 | 
						|
      var left = true;
 | 
						|
      for (var i = columnIndex + 1; i < props.columns.length; i++) {
 | 
						|
        if (
 | 
						|
          props.columns[i].fixed &&
 | 
						|
          props.columns[i].fixed == "left" &&
 | 
						|
          tableColumnKeys.value.includes(props.columns[i].key)
 | 
						|
        ) {
 | 
						|
          left = false;
 | 
						|
        }
 | 
						|
      }
 | 
						|
      return left ? `layui-table-fixed-left-last` : "";
 | 
						|
    } else {
 | 
						|
      var right = true;
 | 
						|
      for (var i = 0; i < columnIndex; i++) {
 | 
						|
        if (
 | 
						|
          props.columns[i].fixed &&
 | 
						|
          props.columns[i].fixed == "right" &&
 | 
						|
          tableColumnKeys.value.includes(props.columns[i].key)
 | 
						|
        ) {
 | 
						|
          right = false;
 | 
						|
        }
 | 
						|
      }
 | 
						|
      return right ? `layui-table-fixed-right-first` : "";
 | 
						|
    }
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
const hasTotalRow = computed(() => {
 | 
						|
  let b = false;
 | 
						|
  props.columns.forEach((item) => {
 | 
						|
    if (item.totalRow) {
 | 
						|
      b = true;
 | 
						|
    }
 | 
						|
  });
 | 
						|
  return b;
 | 
						|
});
 | 
						|
 | 
						|
const renderTotalRowCell = (column: any) => {
 | 
						|
  if (column.totalRow) {
 | 
						|
    if (column.totalRow != true) {
 | 
						|
      return column.totalRow;
 | 
						|
    } else {
 | 
						|
      if (column.totalRowMethod) {
 | 
						|
        return column.totalRowMethod(column, tableDataSource.value);
 | 
						|
      } else {
 | 
						|
        return totalRowMethod(column, tableDataSource.value);
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
const totalRowMethod = (column: any, dataSource: any[]) => {
 | 
						|
  let total = 0;
 | 
						|
  dataSource.forEach((item) => {
 | 
						|
    total = total + Number(item[column.key]);
 | 
						|
  });
 | 
						|
  return total;
 | 
						|
};
 | 
						|
 | 
						|
onBeforeUnmount(() => {
 | 
						|
  window.onresize = null;
 | 
						|
});
 | 
						|
</script>
 | 
						|
 | 
						|
<template>
 | 
						|
  <div ref="tableRef">
 | 
						|
    <table class="layui-hide" lay-filter="test"></table>
 | 
						|
    <div class="layui-form layui-border-box layui-table-view" :class="classes">
 | 
						|
      <div v-if="defaultToolbar || slot.toolbar" class="layui-table-tool">
 | 
						|
        <div class="layui-table-tool-temp">
 | 
						|
          <slot name="toolbar"></slot>
 | 
						|
        </div>
 | 
						|
        <div v-if="defaultToolbar" class="layui-table-tool-self">
 | 
						|
          <lay-dropdown updateAtScroll>
 | 
						|
            <div class="layui-inline" title="筛选" lay-event>
 | 
						|
              <i class="layui-icon layui-icon-slider"></i>
 | 
						|
            </div>
 | 
						|
            <template #content>
 | 
						|
              <div class="layui-table-tool-checkbox">
 | 
						|
                <lay-checkbox
 | 
						|
                  v-for="column in tableHeadColumns[0]"
 | 
						|
                  v-model="tableColumnKeys"
 | 
						|
                  skin="primary"
 | 
						|
                  :disabled="column.children"
 | 
						|
                  :key="column.key"
 | 
						|
                  :value="column.key"
 | 
						|
                  >{{ column.title }}</lay-checkbox
 | 
						|
                >
 | 
						|
              </div>
 | 
						|
            </template>
 | 
						|
          </lay-dropdown>
 | 
						|
          <div
 | 
						|
            class="layui-inline"
 | 
						|
            title="导出"
 | 
						|
            lay-event
 | 
						|
            @click="exportData()"
 | 
						|
          >
 | 
						|
            <i class="layui-icon layui-icon-export"></i>
 | 
						|
          </div>
 | 
						|
          <div class="layui-inline" title="打印" lay-event @click="print()">
 | 
						|
            <i class="layui-icon layui-icon-print"></i>
 | 
						|
          </div>
 | 
						|
        </div>
 | 
						|
      </div>
 | 
						|
 | 
						|
      <div class="layui-table-box-header" v-if="slot.header">
 | 
						|
        <slot name="header"></slot>
 | 
						|
      </div>
 | 
						|
 | 
						|
      <div class="layui-table-box">
 | 
						|
        <!-- 表头 -->
 | 
						|
        <div
 | 
						|
          class="layui-table-header"
 | 
						|
          :style="[{ 'padding-right': `${scrollWidthCell}px` }]"
 | 
						|
        >
 | 
						|
          <div class="layui-table-header-wrapper" ref="tableHeader">
 | 
						|
            <table
 | 
						|
              class="layui-table"
 | 
						|
              :lay-size="size"
 | 
						|
              :lay-skin="skin"
 | 
						|
              ref="tableHeaderTable"
 | 
						|
            >
 | 
						|
              <colgroup>
 | 
						|
                <template v-for="column in tableBodyColumns" :key="column">
 | 
						|
                  <template v-if="tableColumnKeys.includes(column.key)">
 | 
						|
                    <col
 | 
						|
                      :width="column.width"
 | 
						|
                      :style="{
 | 
						|
                        minWidth: column.minWidth ? column.minWidth : '50px',
 | 
						|
                      }"
 | 
						|
                    />
 | 
						|
                  </template>
 | 
						|
                </template>
 | 
						|
              </colgroup>
 | 
						|
              <thead>
 | 
						|
                <template
 | 
						|
                  v-for="(
 | 
						|
                    tableHeadColumn, tableHeadColumnIndex
 | 
						|
                  ) in tableHeadColumns"
 | 
						|
                  :key="tableHeadColumnIndex"
 | 
						|
                >
 | 
						|
                  <tr>
 | 
						|
                    <template
 | 
						|
                      v-for="(column, columnIndex) in tableHeadColumn"
 | 
						|
                      :key="column"
 | 
						|
                    >
 | 
						|
                      <th
 | 
						|
                        v-if="tableColumnKeys.includes(column.key)"
 | 
						|
                        :colspan="column.colspan"
 | 
						|
                        :rowspan="column.rowspan"
 | 
						|
                        class="layui-table-cell"
 | 
						|
                        :class="[
 | 
						|
                          renderFixedClassName(column, columnIndex),
 | 
						|
                          column.fixed
 | 
						|
                            ? `layui-table-fixed-${column.fixed}`
 | 
						|
                            : '',
 | 
						|
                          column.type == 'checkbox'
 | 
						|
                            ? 'layui-table-cell-checkbox'
 | 
						|
                            : '',
 | 
						|
                          column.type == 'radio'
 | 
						|
                            ? 'layui-table-cell-radio'
 | 
						|
                            : '',
 | 
						|
                          column.type == 'number'
 | 
						|
                            ? 'layui-table-cell-number'
 | 
						|
                            : '',
 | 
						|
                        ]"
 | 
						|
                        :style="[
 | 
						|
                          {
 | 
						|
                            textAlign: column.align,
 | 
						|
                          },
 | 
						|
                          renderFixedStyle(column, columnIndex),
 | 
						|
                        ]"
 | 
						|
                      >
 | 
						|
                        <template v-if="column.type == 'checkbox'">
 | 
						|
                          <lay-checkbox
 | 
						|
                            v-model="hasChecked"
 | 
						|
                            :is-indeterminate="!allChecked"
 | 
						|
                            skin="primary"
 | 
						|
                            value="all"
 | 
						|
                            @change="changeAll"
 | 
						|
                          />
 | 
						|
                        </template>
 | 
						|
                        <template v-else>
 | 
						|
                          <span>
 | 
						|
                            <template v-if="column.titleSlot">
 | 
						|
                              <slot :name="column.titleSlot"></slot>
 | 
						|
                            </template>
 | 
						|
                            <template v-else>
 | 
						|
                              {{ column.title }}
 | 
						|
                            </template>
 | 
						|
                          </span>
 | 
						|
                          <!-- 插槽 -->
 | 
						|
                          <span
 | 
						|
                            v-if="column.sort"
 | 
						|
                            class="layui-table-sort layui-inline"
 | 
						|
                            lay-sort
 | 
						|
                          >
 | 
						|
                            <i
 | 
						|
                              @click.stop="sortTable($event, column.key, 'asc')"
 | 
						|
                              class="layui-edge layui-table-sort-asc"
 | 
						|
                              title="升序"
 | 
						|
                            ></i>
 | 
						|
                            <i
 | 
						|
                              @click.stop="
 | 
						|
                                sortTable($event, column.key, 'desc')
 | 
						|
                              "
 | 
						|
                              class="layui-edge layui-table-sort-desc"
 | 
						|
                              title="降序"
 | 
						|
                            ></i>
 | 
						|
                          </span>
 | 
						|
                        </template>
 | 
						|
                      </th>
 | 
						|
                    </template>
 | 
						|
                  </tr>
 | 
						|
                </template>
 | 
						|
              </thead>
 | 
						|
            </table>
 | 
						|
          </div>
 | 
						|
        </div>
 | 
						|
        <!-- 表身 -->
 | 
						|
        <div
 | 
						|
          class="layui-table-body layui-table-main"
 | 
						|
          :style="{ height: height, maxHeight: maxHeight }"
 | 
						|
          ref="tableBody"
 | 
						|
        >
 | 
						|
          <table
 | 
						|
            class="layui-table"
 | 
						|
            v-if="tableDataSource.length > 0 && loading == false"
 | 
						|
            :class="{ 'layui-table-even': props.even }"
 | 
						|
            :lay-size="size"
 | 
						|
            :lay-skin="skin"
 | 
						|
          >
 | 
						|
            <colgroup>
 | 
						|
              <template
 | 
						|
                v-for="(column, columnIndex) in tableBodyColumns"
 | 
						|
                :key="columnIndex"
 | 
						|
              >
 | 
						|
                <template v-if="tableColumnKeys.includes(column.key)">
 | 
						|
                  <col
 | 
						|
                    :width="column.width"
 | 
						|
                    :style="{
 | 
						|
                      minWidth: column.minWidth ? column.minWidth : '50px',
 | 
						|
                    }"
 | 
						|
                  />
 | 
						|
                </template>
 | 
						|
              </template>
 | 
						|
            </colgroup>
 | 
						|
            <tbody>
 | 
						|
              <!-- 渲染 -->
 | 
						|
              <template
 | 
						|
                v-for="(children, index) in tableDataSource"
 | 
						|
                :key="index"
 | 
						|
              >
 | 
						|
                <table-row
 | 
						|
                  :id="id"
 | 
						|
                  :index="index"
 | 
						|
                  :data="children"
 | 
						|
                  :columns="tableBodyColumns"
 | 
						|
                  :indent-size="indentSize"
 | 
						|
                  :currentIndentSize="currentIndentSize"
 | 
						|
                  :tableColumnKeys="tableColumnKeys"
 | 
						|
                  :expandSpace="childrenExpandSpace"
 | 
						|
                  :expandIndex="expandIndex"
 | 
						|
                  :cellStyle="cellStyle"
 | 
						|
                  :cellClassName="cellClassName"
 | 
						|
                  :rowStyle="rowStyle"
 | 
						|
                  :rowClassName="rowClassName"
 | 
						|
                  :spanMethod="spanMethod"
 | 
						|
                  :defaultExpandAll="defaultExpandAll"
 | 
						|
                  :getCheckboxProps="getCheckboxProps"
 | 
						|
                  :getRadioProps="getRadioProps"
 | 
						|
                  v-model:expandKeys="tableExpandKeys"
 | 
						|
                  v-model:selectedKeys="tableSelectedKeys"
 | 
						|
                  v-model:selectedKey="tableSelectedKey"
 | 
						|
                  @row="rowClick"
 | 
						|
                  @row-double="rowDoubleClick"
 | 
						|
                  @row-contextmenu="rowContextmenu"
 | 
						|
                >
 | 
						|
                  <template v-for="name in slotsData" #[name]="{ data }">
 | 
						|
                    <slot :name="name" :data="data"></slot>
 | 
						|
                  </template>
 | 
						|
                  <template v-if="slot.expand" #expand="{ data }">
 | 
						|
                    <slot name="expand" :data="data"></slot>
 | 
						|
                  </template>
 | 
						|
                </table-row>
 | 
						|
              </template>
 | 
						|
              <tr v-if="hasTotalRow" class="layui-table-total">
 | 
						|
                <template
 | 
						|
                  v-for="(column, columnIndex) in columns"
 | 
						|
                  :key="columnIndex"
 | 
						|
                >
 | 
						|
                  <template v-if="tableColumnKeys.includes(column.key)">
 | 
						|
                    <td
 | 
						|
                      :style="[
 | 
						|
                        {
 | 
						|
                          textAlign: column.align,
 | 
						|
                          whiteSpace: column.ellipsisTooltip
 | 
						|
                            ? 'nowrap'
 | 
						|
                            : 'normal',
 | 
						|
                        },
 | 
						|
                        renderFixedStyle(column, columnIndex),
 | 
						|
                      ]"
 | 
						|
                      :class="[
 | 
						|
                        'layui-table-cell',
 | 
						|
                        renderFixedClassName(column, columnIndex),
 | 
						|
                        column.fixed ? `layui-table-fixed-${column.fixed}` : '',
 | 
						|
                      ]"
 | 
						|
                      v-html="renderTotalRowCell(column)"
 | 
						|
                    ></td>
 | 
						|
                  </template>
 | 
						|
                </template>
 | 
						|
              </tr>
 | 
						|
            </tbody>
 | 
						|
          </table>
 | 
						|
          <template v-if="tableDataSource.length == 0 && loading == false">
 | 
						|
            <lay-empty></lay-empty>
 | 
						|
            <div :style="{ width: tableBodyEmptyWidth }"></div>
 | 
						|
          </template>
 | 
						|
          <template v-if="loading == true">
 | 
						|
            <div class="layui-table-loading">
 | 
						|
              <i
 | 
						|
                class="layui-icon-loading layui-icon layui-anim layui-anim-rotate layui-anim-loop"
 | 
						|
              ></i>
 | 
						|
            </div>
 | 
						|
          </template>
 | 
						|
        </div>
 | 
						|
        <div class="layui-table-footer" v-if="slot.footer">
 | 
						|
          <slot name="footer"></slot>
 | 
						|
        </div>
 | 
						|
      </div>
 | 
						|
      <div v-if="page && page.total > 0" class="layui-table-page">
 | 
						|
        <table-page
 | 
						|
          :total="page.total"
 | 
						|
          :pages="page.pages"
 | 
						|
          :theme="page.theme"
 | 
						|
          :limits="page.limits"
 | 
						|
          :showSkip="page.showSkip"
 | 
						|
          :show-page="page.showPage"
 | 
						|
          :showRefresh="page.showRefresh"
 | 
						|
          :showLimit="page.showLimit"
 | 
						|
          :showCount="page.showCount"
 | 
						|
          :count="page.count"
 | 
						|
          v-model:current="page.current"
 | 
						|
          v-model:limit="page.limit"
 | 
						|
          @change="change"
 | 
						|
        >
 | 
						|
        </table-page>
 | 
						|
      </div>
 | 
						|
    </div>
 | 
						|
  </div>
 | 
						|
</template>
 |