|
@@ -0,0 +1,552 @@
|
|
|
+<template>
|
|
|
+ <div class="header-actions" v-if="getActionList.length != 0">
|
|
|
+ <div class="overflow-box">
|
|
|
+ <el-button
|
|
|
+ v-for="(item, index) in getActionList"
|
|
|
+ :key="index"
|
|
|
+ :type="item.type || 'primary'"
|
|
|
+ :plain="item.plain || false"
|
|
|
+ v-bind="getHeaderActions(item)"
|
|
|
+ @click="item.action"
|
|
|
+ :disabled="item.disabled || false"
|
|
|
+ >
|
|
|
+ {{ item.text }}
|
|
|
+ </el-button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="table-list-container by-table">
|
|
|
+ <!-- v-if="!hideHeader" -->
|
|
|
+ <header v-if="false" class="header">
|
|
|
+ <h2>{{ title }}</h2>
|
|
|
+ </header>
|
|
|
+ <div class="by-search" v-if="!hideSearch">
|
|
|
+ <div style="display: flex">
|
|
|
+ <div
|
|
|
+ class="by-dropdown"
|
|
|
+ v-for="(i,index) in selectConfigCopy"
|
|
|
+ :key="i.prop"
|
|
|
+ style="margin-right: 10px"
|
|
|
+ >
|
|
|
+ <div class="by-dropdown-title">
|
|
|
+ {{ i.label || i.labelCopy
|
|
|
+ }}<i
|
|
|
+ style="margin-left: 5px"
|
|
|
+ class="iconfont icon-iconm_xialan1"
|
|
|
+ ></i>
|
|
|
+ </div>
|
|
|
+ <ul class="by-dropdown-lists">
|
|
|
+ <li
|
|
|
+ @click="searchItemSelct('all', i,index)"
|
|
|
+ v-if="i.isShowAll === false ? i.isShowAll : true"
|
|
|
+ >
|
|
|
+ 全部
|
|
|
+ </li>
|
|
|
+ <li
|
|
|
+ v-for="j in i.data"
|
|
|
+ :key="j.value"
|
|
|
+ @click="searchItemSelct(j, i)"
|
|
|
+ >
|
|
|
+ {{ j.label }}
|
|
|
+ </li>
|
|
|
+ </ul>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div style="display: flex">
|
|
|
+ <el-input
|
|
|
+ placeholder="请输入关键字"
|
|
|
+ suffix-icon="search"
|
|
|
+ size="mini"
|
|
|
+ v-model="keywrod"
|
|
|
+ @keyup.enter="searchFn"
|
|
|
+ >
|
|
|
+ </el-input>
|
|
|
+ <el-button
|
|
|
+ type="primary"
|
|
|
+ style="margin-left: 10px"
|
|
|
+ size="default"
|
|
|
+ @click="searchFn"
|
|
|
+ >搜索</el-button
|
|
|
+ >
|
|
|
+
|
|
|
+ <div class="more-icon"><i class="el-icon-wind-power"></i></div>
|
|
|
+ <div class="more-icon">
|
|
|
+ <i class="el-icon-notebook-2"></i>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <component :is="containerTag">
|
|
|
+ <div class="filter-form-container">
|
|
|
+ <slot />
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <el-table
|
|
|
+ ref="hocElTable"
|
|
|
+ v-loading="loading"
|
|
|
+ :data="source"
|
|
|
+ v-if="!hideTable"
|
|
|
+ style="width: 100%"
|
|
|
+ v-bind="$attrs"
|
|
|
+ v-on="tableEvents"
|
|
|
+ row-key="id"
|
|
|
+ :tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
|
|
|
+ >
|
|
|
+ <el-table-column
|
|
|
+ v-for="(item, index) in config"
|
|
|
+ :key="index"
|
|
|
+ v-bind="getAttrsValue(item)"
|
|
|
+ :type="item.type || ''"
|
|
|
+ :selectable="
|
|
|
+ (rowData, rowIndex) => isSelectable(rowData, rowIndex, item)
|
|
|
+ "
|
|
|
+ >
|
|
|
+ <template #default="scope" v-if="!item.type">
|
|
|
+ <slot
|
|
|
+ :name="item.attrs.slot"
|
|
|
+ :item="scope.row"
|
|
|
+ v-if="item.attrs.slot"
|
|
|
+ >
|
|
|
+ 插槽占位符
|
|
|
+ </slot>
|
|
|
+ <div v-else-if="isFunction(getValue(scope, item))">
|
|
|
+ <component
|
|
|
+ :is="renderTypeList[getMatchRenderFunction(item)].target"
|
|
|
+ :cell-list="getValue(scope, item)()"
|
|
|
+ :row="scope.row"
|
|
|
+ :parent="getParent"
|
|
|
+ @click="
|
|
|
+ ($event) => {
|
|
|
+ handleNativeClick(getAttrsValue(item), $event, item);
|
|
|
+ }
|
|
|
+ "
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ <div v-else>
|
|
|
+ {{ getValue(scope, item) }}
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+
|
|
|
+ <el-row
|
|
|
+ v-if="!hidePagination"
|
|
|
+ class="table-pagination"
|
|
|
+ justify="end"
|
|
|
+ type="flex"
|
|
|
+ >
|
|
|
+ <el-pagination
|
|
|
+ background
|
|
|
+ layout="total, sizes, prev, pager, next, jumper"
|
|
|
+ :current-page="getPagination.pageNum"
|
|
|
+ :page-size="getPagination.pageSize"
|
|
|
+ :total="getPagination.total"
|
|
|
+ @size-change="handleSizeChange"
|
|
|
+ @current-change="handlePageChange"
|
|
|
+ />
|
|
|
+ </el-row>
|
|
|
+ </component>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+ <script>
|
|
|
+import { isFunction as isFn, isBoolean } from "./type";
|
|
|
+import ElementsMapping from "./ElementsMapping";
|
|
|
+import ComponentsMapping from "./ComponentsMapping";
|
|
|
+import { computed, defineComponent, getCurrentInstance, ref } from "vue";
|
|
|
+import expand from "./expand";
|
|
|
+export default defineComponent({
|
|
|
+ name: "Table",
|
|
|
+ components: {
|
|
|
+ ElementsMapping,
|
|
|
+ ComponentsMapping,
|
|
|
+ },
|
|
|
+ props: {
|
|
|
+ hideSearch: {
|
|
|
+ type: Boolean,
|
|
|
+ default: false,
|
|
|
+ },
|
|
|
+ hideTable: {
|
|
|
+ type: Boolean,
|
|
|
+ default: false,
|
|
|
+ },
|
|
|
+ //顶部搜索下拉配置
|
|
|
+ selectConfig: {
|
|
|
+ type: Array,
|
|
|
+ default() {
|
|
|
+ return [];
|
|
|
+ },
|
|
|
+ },
|
|
|
+ // 获取表格元数据时携带的参数
|
|
|
+ filterParams: {
|
|
|
+ type: Object,
|
|
|
+ default() {
|
|
|
+ return {};
|
|
|
+ },
|
|
|
+ },
|
|
|
+ // 表格加载 loading
|
|
|
+ loading: {
|
|
|
+ type: Boolean,
|
|
|
+ default: false,
|
|
|
+ },
|
|
|
+ // 表格名称
|
|
|
+ title: {
|
|
|
+ type: String,
|
|
|
+ default: "",
|
|
|
+ },
|
|
|
+ // 表格元数据
|
|
|
+ source: {
|
|
|
+ type: Array,
|
|
|
+ required: true,
|
|
|
+ default() {
|
|
|
+ return [];
|
|
|
+ },
|
|
|
+ },
|
|
|
+ searchConfig: {
|
|
|
+ type: Object,
|
|
|
+ default() {
|
|
|
+ return {
|
|
|
+ keyword: "",
|
|
|
+ };
|
|
|
+ },
|
|
|
+ },
|
|
|
+ // 指定外层容器的渲染组件
|
|
|
+ containerTag: {
|
|
|
+ type: String,
|
|
|
+ default: "div",
|
|
|
+ },
|
|
|
+ // 是否隐藏表头
|
|
|
+ hideHeader: {
|
|
|
+ type: Boolean,
|
|
|
+ default: false,
|
|
|
+ },
|
|
|
+ // 是否隐藏分页
|
|
|
+ hidePagination: {
|
|
|
+ type: Boolean,
|
|
|
+ default: false,
|
|
|
+ },
|
|
|
+ // 分页配置
|
|
|
+ pagination: {
|
|
|
+ type: Object,
|
|
|
+ default() {
|
|
|
+ return {};
|
|
|
+ },
|
|
|
+ },
|
|
|
+ // 表格配置文件
|
|
|
+ config: {
|
|
|
+ type: Array,
|
|
|
+ default() {
|
|
|
+ return [];
|
|
|
+ },
|
|
|
+ },
|
|
|
+ // 表头右上方的按钮组
|
|
|
+ actionList: {
|
|
|
+ type: Array,
|
|
|
+ default() {
|
|
|
+ return [{ text: "", action: () => {} }];
|
|
|
+ },
|
|
|
+ },
|
|
|
+ // element table 原生事件
|
|
|
+ tableEvents: {
|
|
|
+ type: Object,
|
|
|
+ default() {
|
|
|
+ return {};
|
|
|
+ },
|
|
|
+ },
|
|
|
+ searchKey: {
|
|
|
+ type: String,
|
|
|
+ default: "keyword",
|
|
|
+ },
|
|
|
+ // 是否显示过滤的全部选项
|
|
|
+ // isShowAll: {
|
|
|
+ // type: Boolean,
|
|
|
+ // default: true,
|
|
|
+ // },
|
|
|
+ },
|
|
|
+ setup(props) {
|
|
|
+ const { proxy } = getCurrentInstance();
|
|
|
+ const keywrod = ref("");
|
|
|
+ const selectConfigCopy = computed(() => {
|
|
|
+ return props.selectConfig.map((item) => {
|
|
|
+ if(!item.labelCopy) item.labelCopy = {...item}.label;
|
|
|
+ return item;
|
|
|
+ });
|
|
|
+ });
|
|
|
+ console.log(selectConfigCopy);
|
|
|
+ const getAttrsValue = (item) => {
|
|
|
+ const { attrs } = item;
|
|
|
+ const result = {
|
|
|
+ ...attrs,
|
|
|
+ };
|
|
|
+ delete result.prop;
|
|
|
+ return result;
|
|
|
+ };
|
|
|
+ const renderTypeList = ref({
|
|
|
+ render: {},
|
|
|
+ renderHTML: {
|
|
|
+ target: "elements-mapping",
|
|
|
+ },
|
|
|
+ renderComponent: {
|
|
|
+ target: "components-mapping",
|
|
|
+ },
|
|
|
+ });
|
|
|
+ const getParent = computed(() => {
|
|
|
+ return proxy.$parent;
|
|
|
+ });
|
|
|
+ const getPagination = computed(() => {
|
|
|
+ const params = {
|
|
|
+ pageNum: 1,
|
|
|
+ pageSize: 10,
|
|
|
+ total: 0,
|
|
|
+ };
|
|
|
+ return Object.assign({}, params, props.pagination);
|
|
|
+ });
|
|
|
+ const getActionList = computed(() => {
|
|
|
+ return props.actionList
|
|
|
+ .slice()
|
|
|
+ .reverse()
|
|
|
+ .filter((it) => it.text);
|
|
|
+ });
|
|
|
+ const getValue = (scope, configItem) => {
|
|
|
+ const prop = configItem.attrs.prop;
|
|
|
+ const renderName = getMatchRenderFunction(configItem);
|
|
|
+ const renderObj = renderTypeList.value[renderName];
|
|
|
+ if (renderObj && isFunction(configItem[renderName])) {
|
|
|
+ return renderObj.target
|
|
|
+ ? getRenderValue(scope, configItem, {
|
|
|
+ name: renderName,
|
|
|
+ type: "bind",
|
|
|
+ })
|
|
|
+ : getRenderValue(scope, configItem);
|
|
|
+ }
|
|
|
+ return scope.row[prop];
|
|
|
+ };
|
|
|
+ const getRenderValue = (
|
|
|
+ scope,
|
|
|
+ item,
|
|
|
+ fn = { name: "render", type: "call" }
|
|
|
+ ) => {
|
|
|
+ const prop = item.attrs.prop;
|
|
|
+ const propValue = prop && scope.row[prop];
|
|
|
+ scope.row.$index = scope.$index;
|
|
|
+ const args = propValue !== undefined ? propValue : scope.row;
|
|
|
+
|
|
|
+ return item[fn.name][fn.type](getParent.value, args);
|
|
|
+ };
|
|
|
+ // 匹配 render 开头的函数
|
|
|
+ const getMatchRenderFunction = (obj) => {
|
|
|
+ return Object.keys(obj).find((key) => {
|
|
|
+ const matchRender = key.match(/^render.*/);
|
|
|
+ return matchRender && matchRender[0];
|
|
|
+ });
|
|
|
+ };
|
|
|
+ const isFunction = (fn) => {
|
|
|
+ return isFn(fn);
|
|
|
+ };
|
|
|
+ const searchFn = (val) => {
|
|
|
+ console.log(props);
|
|
|
+ proxy.$emit(
|
|
|
+ "getList",
|
|
|
+ Object.assign(props.filterParams, { [props.searchKey]: keywrod.value })
|
|
|
+ );
|
|
|
+ };
|
|
|
+ const handlePageChange = (val) => {
|
|
|
+ proxy.$emit(
|
|
|
+ "getList",
|
|
|
+ Object.assign(props.filterParams, { pageNum: val })
|
|
|
+ );
|
|
|
+ };
|
|
|
+ const handleSizeChange = (val) => {
|
|
|
+ proxy.$emit(
|
|
|
+ "getList",
|
|
|
+ Object.assign(props.filterParams, { pageSize: val })
|
|
|
+ );
|
|
|
+ };
|
|
|
+ const getHeaderActions = (item) => {
|
|
|
+ return {
|
|
|
+ ...item.attrs,
|
|
|
+ };
|
|
|
+ };
|
|
|
+ const stopBubbles = (e) => {
|
|
|
+ const event = e || window.event;
|
|
|
+ if (event && event.stopPropagation) {
|
|
|
+ event.stopPropagation();
|
|
|
+ } else {
|
|
|
+ event.cancelBubble = true;
|
|
|
+ }
|
|
|
+ };
|
|
|
+ const handleNativeClick = ({ isBubble }, e, item) => {
|
|
|
+ // 考虑到单元格内渲染了组件,并且组件自身可能含有点击事件,故添加了阻止冒泡机制
|
|
|
+ // 若指定 isBubble 为 false,则当前单元格恢复冒泡机制
|
|
|
+ if (isBoolean(isBubble) && !isBubble) return;
|
|
|
+ stopBubbles(e);
|
|
|
+ };
|
|
|
+ //下拉搜索相关
|
|
|
+
|
|
|
+ const searchItemSelct = (item, i,index) => {
|
|
|
+ if (item == "all") {
|
|
|
+ console.log(props.selectConfig)
|
|
|
+ i.label = {...props.selectConfig[index]}.labelCopy
|
|
|
+ proxy.$emit(
|
|
|
+ "getList",
|
|
|
+ Object.assign(props.filterParams, { [i.prop]: "" })
|
|
|
+ );
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ i.label = item.label;
|
|
|
+ console.log(item, i);
|
|
|
+ proxy.$emit(
|
|
|
+ "getList",
|
|
|
+ Object.assign(props.filterParams, { [i.prop]: item.value })
|
|
|
+ );
|
|
|
+ };
|
|
|
+
|
|
|
+ const isSelectable = (row, index, item) => {
|
|
|
+ if (item.type === "selection") {
|
|
|
+ if (item.attrs && item.attrs.checkAtt) {
|
|
|
+ if (row[item.attrs.checkAtt]) {
|
|
|
+ return row[item.attrs.checkAtt];
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ return {
|
|
|
+ getParent,
|
|
|
+ getPagination,
|
|
|
+ renderTypeList,
|
|
|
+ getActionList,
|
|
|
+ getAttrsValue,
|
|
|
+ getValue,
|
|
|
+ getRenderValue,
|
|
|
+ getMatchRenderFunction,
|
|
|
+ isFunction,
|
|
|
+ handlePageChange,
|
|
|
+ handleSizeChange,
|
|
|
+ getHeaderActions,
|
|
|
+ stopBubbles,
|
|
|
+ handleNativeClick,
|
|
|
+ keywrod,
|
|
|
+ searchFn,
|
|
|
+ searchItemSelct,
|
|
|
+ selectConfigCopy,
|
|
|
+ isSelectable,
|
|
|
+ };
|
|
|
+ },
|
|
|
+});
|
|
|
+</script>
|
|
|
+ <style>
|
|
|
+.table-list-container th {
|
|
|
+ color: #333 !important;
|
|
|
+}
|
|
|
+.by-table td .el-button + .el-button {
|
|
|
+ margin-left: 0 !important;
|
|
|
+}
|
|
|
+.by-table td .el-button {
|
|
|
+ background: none !important;
|
|
|
+ margin: 0 !important;
|
|
|
+ padding: 8px 6px !important;
|
|
|
+}
|
|
|
+</style>
|
|
|
+ <style lang="scss" scoped>
|
|
|
+.by-search {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ margin-bottom: 10px;
|
|
|
+}
|
|
|
+.by-dropdown {
|
|
|
+ position: relative;
|
|
|
+ text-align: left;
|
|
|
+ height: 32px;
|
|
|
+ z-index: 100;
|
|
|
+ padding: 0 10px;
|
|
|
+ transition: all 0.5s ease;
|
|
|
+ cursor: pointer;
|
|
|
+ line-height: 32px;
|
|
|
+ .by-dropdown-title {
|
|
|
+ font-size: 14px;
|
|
|
+ background-color: #fff;
|
|
|
+ }
|
|
|
+ ul {
|
|
|
+ position: absolute;
|
|
|
+ left: 0;
|
|
|
+
|
|
|
+ top: 32px;
|
|
|
+ padding: 0;
|
|
|
+ margin: 0;
|
|
|
+ z-index: 100;
|
|
|
+ display: none;
|
|
|
+ white-space: nowrap;
|
|
|
+ li {
|
|
|
+ list-style: none;
|
|
|
+ font-size: 12px;
|
|
|
+ height: 30px;
|
|
|
+ padding: 0 10px;
|
|
|
+ }
|
|
|
+ li:hover {
|
|
|
+ background-color: #eff6ff;
|
|
|
+ color: #0084ff;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.by-dropdown::before {
|
|
|
+ display: block;
|
|
|
+ width: 1px;
|
|
|
+ content: " ";
|
|
|
+ position: absolute;
|
|
|
+ height: 14px;
|
|
|
+ top: 8px;
|
|
|
+ background-color: #ddd;
|
|
|
+ right: 0;
|
|
|
+ z-index: 101;
|
|
|
+}
|
|
|
+
|
|
|
+.by-dropdown:hover {
|
|
|
+ background: #ffffff;
|
|
|
+
|
|
|
+ border-radius: 2px 2px 2px 2px;
|
|
|
+ opacity: 1;
|
|
|
+ ul {
|
|
|
+ background: #ffffff;
|
|
|
+ box-shadow: 0px 2px 16px 1px rgba(0, 0, 0, 0.06);
|
|
|
+ border-radius: 2px 2px 2px 2px;
|
|
|
+ opacity: 1;
|
|
|
+ display: block;
|
|
|
+ text-align: left;
|
|
|
+ }
|
|
|
+}
|
|
|
+.header-actions {
|
|
|
+ flex: 1;
|
|
|
+ overflow-x: auto;
|
|
|
+ padding: 20px;
|
|
|
+ background: #fff;
|
|
|
+ margin-bottom: 20px;
|
|
|
+ .overflow-box {
|
|
|
+ :deep() .el-button:nth-child(1) {
|
|
|
+ margin-left: 10px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+.table-list-container {
|
|
|
+ background: #fff;
|
|
|
+ padding: 13px 20px 20px;
|
|
|
+ .table-pagination {
|
|
|
+ padding-top: 20px;
|
|
|
+ }
|
|
|
+ .header {
|
|
|
+ display: flex;
|
|
|
+ padding-bottom: 20px;
|
|
|
+ }
|
|
|
+ .el-table {
|
|
|
+ :deep() th {
|
|
|
+ font-size: 14px;
|
|
|
+ }
|
|
|
+ :deep() td {
|
|
|
+ font-size: 14px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+</style>
|