vue3:Table组件动态的字段(列)权限、显示隐藏和左侧固定

发布于:2025-06-04 ⋅ 阅读:(25) ⋅ 点赞:(0)

 效果展示

根据后端接口返回,当前登录用户详情中的页面中el-table组件的显示隐藏等功能。根据菜单id查询该菜单下能后显示的列。

后端返回的数据类型:

接收到后端返回的数据后处理数据结构. Table组件文件

<!-- 自己封装的Table组件文件 -->
onMounted(()=>{
//判断页面传递过来的数据对象:PropTableS中keySrequest是否为true;为true则请求后端的table列数据,否则使用自己定义的。(因为还没有定义所有页面的 列字段权限。所有得区分开来,不然其他页面在后端中不存在数据,table显示都会是空的)
    if(props.PropTableS.keySrequest){
    tablekey()
 }
})

onActivated(() => {
//如果不是第一次进入这个页面,可以直接拿之前第一次进入保存的数据.
  if(props.PropTableS.keySrequest &&  proxy.$router.currentRoute.value.meta.key){
    TabKys = proxy.$router.currentRoute.value.meta.key
  }
})



//请求后端数据,处理数据格式
function tablekey(){
    proxy.$axios.get('/system/user/list_fields', {
      permission_id:proxy.$router.currentRoute.value.meta.id
    }).then((res) => {
      let keycopy = JSON.parse(JSON.stringify(TabKys))
      let list = {}
      res.data.map(item=>{
        list[item.code] = {
          title :item.name, //列名
          left_fixed:item.left_fixed == 1 ? true : false, //是否固定
          fixed:item.left_fixed == 1 ? 'left' : false, //默认固定左侧
          id:item.id, //字段id
          sort:item.sort, //排序
          status:item.status == 1 ? true : false, //显示/隐藏
          isshow:item.isshow, //也是显示/隐藏,这个可以在自定义的数据中传递过来,因为之前的页面都是用的isshow,后端用的是status,前面页面太多不想改了。
          width:keycopy[item.code].width, //自定义传递过来的列宽度
          type:keycopy[item.code].type, //自定义传递过来的列类型,如('text','selelct','input'等等)
          permission_id:proxy.$router.currentRoute.value.meta.id//提交给后端的菜单id。
        }
      })
      //TabKys :渲染el-table组件的列对象数据  list:处理后端数据.
      TabKys = list
      //此处理是将进入页面请求后的数据保存,可以避免切换页面,丢失当前页面的table列。出现问题的场景(进入页面A,table组件列正常显示,切换到页面B后,在回到页面A。页面A没有列的名称了。因为路由设置页页面记住缓存。)
      proxy.$router.options.routes[1].children.forEach(route=>{
        route.meta.id == proxy.$router.currentRoute.value.meta.id ?  route.meta.key == undefined ?  route.meta.key = TabKys : '' : ''
      })
      tablekeyindex.value++
    });
}

页面中定义的数据:

页面中定义的table数据

let PropTableS = reactive({ 
  tableStyle: { //table的css
    width: "99%",
    margin: "auto",
  },
  keySrequest:true, //是否需要后端数据渲染
  //自己定义的列的数据 
  keyS: proxy.$PublicAPI.SetTableCotentWidth({ 
    //这不是一个完整列对象数据,因为其他值都在后端返回了。这里的意义就是可以自定义列的类型。后端中没有定义当前列类型字段,也可以不需要后端保存,可以自定义,
    danjuleixing: {
      type:'select' //类型:下拉
      widht:'120px' //定义的宽度 
    },
    status: {
      type:'select'
    },
    
  }),
});

Table组件中右侧定义设置的组件(小齿轮)

<template>
  <div class="Drawer">
    <el-drawer
      z-index="12"
      :modal="false"
      :before-close="beforeClose"
      append-to="el-main"
      class="drawerBox"
      v-model="Props.DrawerObject.visible"
      :close-on-click-modal="false"
      :destroy-on-close="true"
      :show-close="false"
    >
      <template #header="{ close, titleId, titleClass }">
        <div class="deawerTitle">
          <span>表单固定设置</span>
          <span>
            <Icons @click="close" :theme="'outline'" :size="20" :is="'close'"
          /></span>
        </div>
      </template>
      <div class="deawerScanner">
        <el-input
          v-model="input2"
          @input="handelInput"
          class="deawerScanner_input"
          size="small"
          placeholder="搜索关键词"
          :prefix-icon="Search"
        />
      </div>
      <div>
        <el-tabs
          v-model="TabactiveName"
          class="demo-tabs"
          @tab-click="handleTableActiveClick"
        >
          <el-tab-pane label="固定列" name="0"></el-tab-pane>
          <el-tab-pane label="隐藏/显示" name="1"></el-tab-pane>
        </el-tabs>
      </div>
      <div class="deawerTop" :style="{ height: TopHeght + 'px' }">
        <div class="deawerTop_All">
          <el-checkbox
            @click="handelCheack"
            v-model="checked4"
            label="全部固定"
          />
        </div>
        <div>
          <FromData
            ref="From"
            :key="formkey"
            @Drawer_switchChange="Drawer_switchChange"
            class="FromDataDom"
            :fromlist="fromlist"
            style=""
            :style="{ 'margin-top': '0px', height: domHeight + 'px' }"
          ></FromData>
        </div>
        <div class="botton_but">
          <Button
            plain
            :title="'取消'"
            @click="ButtonClick('')"
            :pattern="'centre'"
          />
          <Button
            :plain="fromlist.scannerIsPlain"
            :type="'primary'"
            :title="'保存'"
            @click="ButtonClick('save')"
            :pattern="'centre'"
          />
        </div>
      </div>
    </el-drawer>
  </div>
</template>

<script lang="ts" setup>
import {
  ref,
  reactive,
  onMounted,
  defineProps,
  watch,
  onActivated,
  getCurrentInstance,
  onBeforeUnmount,
  onUnmounted,
} from "vue";
import { Search } from "@element-plus/icons-vue";
import { FromData, Drawer, Icons, Button } from "@/components";
let Props = defineProps(["DrawerObject", "primarytablelist"]);
import { ElMessage, ElMessageBox } from "element-plus";
const primarytablelist = Props.primarytablelist;
const { proxy } = getCurrentInstance();
const From = ref(null);
let fromlist = reactive({});
let input2 = ref("");
let checked4 = ref(false);
let isEmit = ref(true);
let formkey = ref(0);
let TabactiveName = ref("0");
let domHeight = ref(0);
let TopHeght = ref(0);
onMounted(() => {
  GelTableTitleUpdate(Props.DrawerObject.TabKys);
  window.addEventListener("resize", resizeFun);
  setTimeout(() => {
    resizeFun();
  }, 1);

});

onUnmounted(() => {
  proxy.$bus.emit("addEvent", false);
});

function resizeFun() {
  domHeight.value =
    document.querySelector(".drawerBox").offsetHeight - 41 - 39 - 54 - 84 - 50;
  TopHeght.value =
    document.querySelector(".drawerBox").offsetHeight - 41 - 39 - 64;
}

function GelTableTitleUpdate(obj: object) {
  let index = 0;
  let objlength = 0;
  for (const key in obj) {
    if (key != "operate" && key != "selection" && key != "index") {
      objlength++;
      obj[key].type = "switch";
      obj[key].icon = "drag";
      if (obj[key].fixed) {
        index++;
        obj[key].value = true;
      } else if (obj[key].value == "" || obj[key].value == false) {
        delete obj[key].value;
      }
    } else if (key == "operate" || key == "selection" || key == "index") {
      delete obj[key];
    }
  }

  fromlist = {
    index: "2",
    labelwidth: 130,
    formInline: false,
    isInput: true,
    position: "left",
    component: "rigthoptins",
    listData: obj,
    newListData: JSON.parse(JSON.stringify(obj)),
  };
  handleTableActiveClick({props:{name:'0'}})

  proxy.$bus.emit("addEvent", true);
  index == objlength ? (checked4.value = true) : "";
}


function ButtonClick(type: string) {
  let keys = Object.keys(fromlist.listData)
  let list = {
      permission_id:fromlist.listData[keys[0]].permission_id,
      details:[]
  }

  if (type === "save") {
    for (const key in fromlist.listData) {
      let aa ={
        'field_id':fromlist.listData[key].id,
        'status':fromlist.listData[key].status,
        'left_fixed':fromlist.listData[key].left_fixed
      }
      list.details.push(aa)
    }
  proxy.$axios.post('/system/user/list_set_fields', list).then((res) => {
    if(res.code == 200){
      ElMessage.success(res.message)
      proxy.$bus.emit('UpdataTablerowKeys')
    }else if(res.code == 500){
      ElMessage.error(res.message)
    }
  })
  }
}

function beforeClose() {
  Props.DrawerObject.visible = false;
  proxy.$bus.emit("addEvent", false);
}

proxy.$bus.on("UpdataRouterPath", (val: boolean) => {
  Props.DrawerObject.visible = val;
});

function Drawer_switchChange(
  key:string,
  TableList:any
) {
  fromlist.listData[key][TabactiveName.value == '0' ? 'left_fixed'  : 'status'] = TableList[key]
  Object.keys(TableList).length  == Object.keys(fromlist.listData).length  && Object.values(TableList).every(Boolean) ? checked4.value = true : checked4.value = false
}

function handelCheack() {
  let bol = !checked4.value
  From.value.getUpdataRigthOptions(bol);
  for (const key in fromlist.listData) {
    fromlist.listData[key][TabactiveName.value == '0' ? 'left_fixed'  : 'status'] = From.value.TableList[key]
  }
  
}

let inputval = ref("");
function handelInput(value: string) {
  inputval.value = value;
  setTimeout(() => {
    aa(value);
  }, 1000);
}

function aa(val: string) {
  if (inputval.value == val) {
    let list = JSON.parse(JSON.stringify(fromlist.newListData));
    for (const key in list) {
      if (val == "") {
        fromlist.listData = JSON.parse(JSON.stringify(fromlist.newListData));
      }
      if (list[key].title.indexOf(val) == -1) {
        delete list[key];
      }
    }
    fromlist.listData = list;
    formkey.value++;
  }
}

function handleTableActiveClick(value: any) {
  TabactiveName.value = value.props.name;
  for (const key in fromlist.listData) {
    fromlist.listData[key].value = fromlist.listData[key][TabactiveName.value == '0' ? 'left_fixed'  : 'status'] 

  }
  checked4.value = Object.keys(fromlist.listData).every(item => fromlist.listData[item].value == true)
  formkey.value++;
}
</script>

<style lang="less" scoped>
::v-deep el-drawer {
  background: saddlebrown !important;
}
::v-deep .el-icon {
  height: none;
}

::v-deep .drawerBox {
  .el-drawer__header {
    padding: 0px !important;
    margin-bottom: 0px !important;
  }
  .el-drawer__body {
    padding: 0px !important;
  }
  .el-input__wrapper {
    box-shadow: none !important;
  }
  .deawerScanner {
    border: none;
    width: 100%;
    padding: 0 9px;
    border-bottom: 1px solid #e8eaf2;
    height: 40px;
    .input__wrapper {
      box-shadow: none !important;
      border: none !important;
    }
    .deawerScanner_input {
      height: 39px !important;
      border: none !important;
    }
  }
  .elForm {
    max-height: none !important;
  }
  .FromDataDom {
    overflow: hidden;
    overflow-y: auto;
    scrollbar-width: thin; /* 设置滚动条为细条 */
    // scrollbar-color: #888 #f1f1f1; /* 设置滚动条和轨道的颜色 */
  }
  .deawerTop {
    width: 100%;
    height: 87%;
    padding: 0px 0px 0 12px;

    .deawerTop_All {
      height: 40px;
      display: flex;
      align-items: center;
    }
  }
  width: 244px !important;
  .deawerTitle {
    padding: 0px 15px !important;
    color: #5b6070 !important;
    font-size: 14px !important;
    width: 214px;
    height: 40px;
    display: flex;
    justify-content: space-between;
    align-items: center;
    border-bottom: 1px solid #e8eaf2;
  }

  .botton_but {
    width: 83%;
    height: 84px;
    display: flex;
    justify-items: center;
    align-items: center;
    margin-left: 8%;
    .ButBox {
      display: contents;
      background: salmon;
    }
  }
  .bottonMax_but {
    position: fixed;
    bottom: 0;
  }
}

::v-deep.elForm {
  padding: 0px !important;
}
::v-deep .el-drawer {
  height: 89%;
  top: 11% !important;
  bottom: 0 !important;
}

// ::v-deep .drawerBox {
//   position: absolute !important;
//   top: 100 !important;
//   right: 0 !important;
//   bottom: 0 !important;
//   z-index: 12;
// }

::v-deep .drawerDiv {
  inset: 100% !important;
  position: absolute !important;
  top: 0 !important;
  right: 0 !important;
  bottom: 0 !important;
}
::v-deep .el-switch.is-checked .el-switch__core {
  background: var(--Click_Nav_FontColor) !important;
}
::v-deep .el-button--primary {
  background: var(--Click_Nav_FontColor) !important;
}

::v-deep .el-form-item__label {
  display: block;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

::v-deep .el-tabs__nav-wrap::after {
  height: 1px;
}

::v-deep .el-tabs__nav {
  width: 100%;
  display: flex;
  justify-content: center;
}

::v-deep .switchClass {
  display: flex;
  i {
    display: block;
    height: 32px;
    display: flex;
    justify-content: center;
  }
}
::v-deep .el-tabs__item:hover,
::v-deep .el-checkbox__input.is-checked + .el-checkbox__label {
  color: var(--Click_Nav_FontColor) !important;
}
::v-deep .el-tabs__item.is-active {
  color: var(--Click_Nav_FontColor) !important;
}
::cv-deep .el-tabs__active-bar {
  background-color: var(--Click_Nav_FontColor) !important;
}
</style>

table组件

<template>
  <div class="TableBox" id="TableBox">
    <div class="DateilsTitle" v-if="props.PropTableS.title">
      {{ props.PropTableS.title }}
    </div>
    <div v-if="props.PropTableS.headerRemarks" class="headerRemarks">
      {{ props.PropTableS.headerRemarks }}
    </div>
    <el-table
      v-if="TabKys"
      class="singleTableRef"
      ref="singleTableRef"
      :data="getTables()"
      :key="tablekeyindex"
      
    >
      <template v-for="(child, key, index) in TabKys">
            <!-- 存在显示隐藏标识的字段 控制显示列  evalRowShow-->
              <el-table-column
              v-if="props.PropTableS.keySrequest ? evalRowShow(child.status||child.isshow) : true"></el-table-column>
      </template>
      <!-- 小齿轮在右侧显示 -->
      <template v-if="PropTableS.tables" class="aaaaaa">
        <el-table-column fixed="right" width="20px">
          <template #header>
            <Icons
              @click="handelRightIcon(TabKys)"
              :theme="'outline'"
              :size="36"
              :is="'config'"
            />
          </template>
        </el-table-column>
      </template>
    </el-table>
    <!-- 小齿轮组件 -->
    <Drawer
      v-if="DrawerObject.visible"
      :DrawerObject="DrawerObject"
      :primarytablelist="TabKys"
    ></Drawer>

   
  </div>
</template>

<script setup lang="ts">

onMounted(() => {
   //判断是否包含keySrequest ,否则去自定义传递过来的table数据对象
  if(props.PropTableS.keySrequest){
    tablekey()
  }
});

onActivated(() => {
    //看看列信息是否在路由中有存起来,有值就是代表不是第一次进入该页面
  if(props.PropTableS.keySrequest &&  proxy.$router.currentRoute.value.meta.key){
    TabKys = proxy.$router.currentRoute.value.meta.key
  }
});

//点击右侧设置按钮
function handelRightIcon(params: object) {
  DrawerObject.visible = true;
  DrawerObject.TabKys = JSON.parse(JSON.stringify(TabKys));
}
function evalRowShow(params:any){
  return eval(params);
}

  proxy.$bus.on('UpdataTablerowKeys',()=>{
    tablekey()
  })
function tablekey(){
    proxy.$axios.get('/system/user/list_fields', {
      permission_id:proxy.$router.currentRoute.value.meta.id
    }).then((res) => {
      let keycopy = JSON.parse(JSON.stringify(TabKys))
      let list = {}
      res.data.map(item=>{
        list[item.code] = {
          title :item.name,
          left_fixed:item.left_fixed == 1 ? true : false,
          fixed:item.left_fixed == 1 ? 'left' : false,
          id:item.id,
          sort:item.sort,
          status:item.status == 1 ? true : false,
          isshow:item.isshow,
          width:keycopy[item.code].width,
          type:keycopy[item.code].type,
          permission_id:proxy.$router.currentRoute.value.meta.id
        }
      })
      TabKys = list
       //进入页面将列值数据保存
      proxy.$router.options.routes[1].children.forEach(route=>{
        route.meta.id == proxy.$router.currentRoute.value.meta.id ?  route.meta.key == undefined ?  route.meta.key = TabKys : '' : ''
      })
      tablekeyindex.value++
    });
}


网站公告

今日签到

点亮在社区的每一天
去签到