1、需求: 几乎每个页面的每一个表格都使用到自定义表头字段,点击表格最后一列的图标,弹出表头所有可展示的字段:

在这里插入图片描述
在这里插入图片描述

2、父组件只需传全部可展示的表头数据即可(数组格式),具体代码:
<template>
  <div class="common-box">
    <div class="content-box">
      <div class="assetManagement" v-show="!showDetail">
        <div class="table-box">
          <a-table
            :columns="state.columns"
            :row-key="(record) => record.id"
            :data-source="state.dataSource"
            :loading="loading"
            :pagination="false"
          >
            <template #setTableTitle>
              <a-tooltip>
                <template #title>设置</template>
                <span @click="handleTableHead" class="table-head-btn"><SettingOutlined /></span>
              </a-tooltip>
            </template>
            <template #action="{ text }">
              <a-tooltip>
                <template #title>操作</template>
                <a-popover title="" trigger="click" :visible="showBtn == text.region" @visibleChange="handleShowBtn(text)">
                  <template #content>
                    <div class="action-box" style="display: flex; flex-direction: column;">
                      <a @click="handleDetail(text)">查看详情</a>
                    </div>
                  </template>
                  <MenuOutlined />
                </a-popover>
              </a-tooltip>
            </template>
          </a-table>
        </div>
      </div>
      <a-modal
        class="table-head-modal"
        :visible="tableHeadPop"
        title="列表视图设置"
        :confirmLoading="tableHeadLoading"
        :width="750"
        @cancel="handleCancel"
      >
        <table-head-content
          ref="tableHeadRef"
          :columns="state.setColumns"
        ></table-head-content>
        <template #footer>
          <div class="pop-btn-box">
            <div class="btn-left">
              <a-button @click="checkAll">全选</a-button>
              <a-button  @click="checkNoAll">全不选</a-button>
            </div>
            <div class="btn-right">
              <a-button @click="handleCancel">返回</a-button>
              <a-button  type="primary" @click="handleOk" class="ok-btn">确定</a-button>
            </div>
          </div>
        </template>
      </a-modal>
    </div>
  </div>
</template>
<script setup>
import { onMounted, ref, toRaw, getCurrentInstance, reactive, nextTick, computed } from "vue";
import { request } from "@/utils/request.js";
import {
  ExclamationCircleOutlined,
  SettingOutlined,
  MenuOutlined
} from "@ant-design/icons-vue";
import { useStore } from 'vuex';
const store = useStore();
const globalProperties =
  getCurrentInstance().appContext.config.globalProperties; // 获取全局挂载
const http = globalProperties.$http;
const loading = ref(false);
const showDetail = ref(false);
const tableHeadPop = ref(false);
const tableHeadLoading = ref(false);
const showBtn = ref("");
const tableHeadRef = ref(null);
const state = reactive({
  dataSource: [],
  manufactorList: [],
  siteTypeList: [],
  regionList: [],
  columns: [
    {
      title: "区域",
      dataIndex: "region",
    },
    {
      title: "数量",
      dataIndex: "assetsNum",
      sorter: (a, b) => {
        return a.assetsNum - b.assetsNum
      },
    },
    {
      title: "已安装",
      dataIndex: "installNum",
      sorter: (a, b) => {
        return a.installNum - b.installNum
      },
    },
  ],
  columnsAll: [
    {
      title: "区域",
      dataIndex: "region",
      ellipsis: true,
    },
    {
      title: "数量",
      dataIndex: "assetsNum",
      sorter: (a, b) => {
        return a.assetsNum - b.assetsNum
      },
      ellipsis: true,
    },
    {
      title: "已安装",
      dataIndex: "installNum",
      sorter: (a, b) => {
        return a.installNum - b.installNum
      },
      ellipsis: true,
    },
    {
      title: "未安装",
      dataIndex: "notInstall",
      sorter: (a, b) => {
        return a.notInstall - b.notInstall
      },
      ellipsis: true,
    },
  ],
  setColumns: []
});
const detailRef = ref(null);
state.siteTypeList = computed( () => store.state.siteTypeList);
state.regionList = computed( () => store.state.regionList);
state.manufactorList = computed( () => store.state.manufactorList);
// 获取表格数据
const getTableData = () => {
  loading.value = true;
  if(state.columns.length && (state.columns[state.columns.length -1].key != "setItem")) {
    state.columns.push({
      key: "setItem", // 不可使用dataIndex, 会导致操作拿不到当前行的数据
      width: 60,
      slots: {
        title: "setTableTitle",
        customRender: "action",
      },
    })
  }
  request(http.GET_DEVICE_ASSET_MANAGE_LIST, "get")
    .then((res) => {
      loading.value = false;
      state.dataSource = res;
      if(state.dataSource.length > 0) {
        state.dataSource.map((item, index) => {
          item.id = index;
        })
      }
    })
    .catch(() => {
      loading.value = false;
    });
};
const handleShowBtn = (text) => {
  showBtn.value = text.region;
}
// 点击查看详情
const handleDetail = (text) => {
  showDetail.value = true;
  showBtn.value = ""; // 隐藏按钮小弹窗
  // 触发子组件请求数据的方法
  nextTick(() => {
    detailRef.value.setValue(text.region);
  })
};
// 点击动态设置表头
const handleTableHead = () => {
  tableHeadPop.value = true;
  nextTick(() => {
    tableHeadRef.value.setValues(state.columnsAll);
  });
  state.setColumns = state.columns;
  let num = state.setColumns.length - 1;
  state.setColumns = toRaw(state.setColumns).slice(0, num); // 把设置那行去掉
};
// 全选
const checkAll = () => {
  nextTick(() => {
    tableHeadRef.value.checkAllSon();
  })
};
// 全不选
const checkNoAll = () => {
  nextTick(() => {
    tableHeadRef.value.checkNoAllSon();
  })
};
const handleCancel = () => {
  tableHeadPop.value = false;
};
const handleOk = async() => {
  let arr = await tableHeadRef.value.getValues();
  if(arr.length) {
    tableHeadPop.value = false;
    state.columns = arr;
    getTableData();
  }
};
onMounted(() => {
  getTableData();
});
</script>

<style lang="less" scoped>
.assetManagement {
  height: 100%;
  padding: 0.1rem;
}
</style>
3、主要代码在这块:
 nextTick(() => {
    tableHeadRef.value.setValues(state.columnsAll);
  });
4、开始封装, 因为设计到选中样式,所以自己添加了checked属性,在子组件中定义一个变量,接收父组件通过参数传过来的数组
<template>
  <div class="table-head-content">
    <a-checkbox-group
      v-model:value="state.checkedList"
      name="checkboxgroup"
      @change="onChange"
    >
      <ul class="check-ul">
        <li v-for="(item, index) in state.columnsAll" :key="index" class="check-li" :class="{'isChecked': item.checked}">
          <a-checkbox :value="item.title" @change="getCheckOne">
            <span class="commonCode checkCode">{{ item.title }}</span>
          </a-checkbox>
        </li>
      </ul>
    </a-checkbox-group>
  </div>
</template>
<script setup>
import { reactive, toRaw, getCurrentInstance } from "vue";
import { getJsonArrEqual } from "@/utils/common";
const globalProperties = getCurrentInstance().appContext.config.globalProperties; // 获取全局挂载
const toast = globalProperties.$toast;
const props = defineProps({
  columns: Array,
});
const state = reactive({
  checkedList: [],
  columnsAll: []
});
// 初始化
const setValues = (columnsAll) => {
  state.checkedList = [];
  state.columnsAll = columnsAll
  let newArr = getJsonArrEqual(toRaw(props.columns), state.columnsAll); // 获取相同项
  state.columnsAll.forEach(i => { // 初始化checked属性
    i.checked = false;
  })
  state.columnsAll.forEach(item => {
    newArr.map(i => {
      if(item.title == i.title) {
        item.checked = true; // 初始化选中的checked值
        state.checkedList.push(i.title);
      }
    })
  });
};
// 选中
const onChange = (check) => {
  state.checkedList = check;
};
// 单个选中
const getCheckOne = (e) => {
  state.columnsAll.forEach((item, index) => {
    if(item.title == e.target.value) {
      state.columnsAll[index].checked = e.target.checked; // 根据选中与否赋值
    }
  })
}; 
// 获取选中
const getValues = () => {
  return new Promise((resolve, reject) => {
    if(state.checkedList.length >= 3) { // 必须选择三项或者三项以上
      let arr = [];
      state.checkedList.forEach(item => {
        state.columnsAll.map(i => {
          if(i.title == item) {
            arr.push(i);
          }
        })
      })
      resolve(arr);
    } else {
      toast('必须选中三项或者三项以上喔!')
      reject([]);
    }
  })
};
// 全选
const checkAllSon = () => {
  if(state.columnsAll.length) {
    state.checkedList = []; // 防止叠加
    state.columnsAll.map(i => {
      state.checkedList.push(i.title);
      return i.checked = true;
    })
  }
};
// 全不选
const checkNoAllSon = () => {
  state.checkedList = [];
  state.columnsAll.map(i => {
    return i.checked = false;
  })
};
defineExpose({
  setValues,
  getValues,
  checkAllSon,
  checkNoAllSon
})
</script>
<style lang="less" scoped>
.table-head-content {
  .check-ul {
    display: flex;
    flex-wrap: wrap;
    .check-li {
      padding: 2px 8px;
      border-radius: 4px;
      margin-right: .1rem;
      margin-bottom: .1rem;
      white-space: nowrap;
      ::v-deep(.ant-checkbox-wrapper) {
        width: 200px;
      }
      &:hover {
        background: @table-head-set-bg;
      }
    }
    .isChecked {
      background: @table-head-set-bg;
    }
  }
};
</style>

5、此组件用到两个Json数组取相同项的方法,详见我另一篇文章:https://blog.csdn.net/agua001/article/details/123321784?spm=1001.2014.3001.5502
6、把公用组件放到src/components/common

在这里插入图片描述

7、在main.js中注册成全局组件
import tableHeadContent from './components/common/tableHeadContent.vue'

const app = createApp(App)
app.component('table-head-content', tableHeadContent);
app.use(antd).use(router).use(store).mount('#app')
8、在组件中不用引入,直接使用,官方文档:https://v3.cn.vuejs.org/guide/component-registration.html#%E5%85%A8%E5%B1%80%E6%B3%A8%E5%86%8C
<table-head-content ref="tableHeadRef" />

面朝大海,春暖花开。

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐