h-table(表格列表组件的全封装)

文章目录

    • 概要
    • h-table的封装过程
    • 查询组件封装 h-highForm
    • 结果页
    • 右侧工具栏封装RightToolbar
    • 结果页
    • 列表组件h-table
    • 结果页
    • vue页面使用
    • js文件
    • 有需要的请私信博主,还请麻烦给个关注,博主不定期更新组件封装,或许能够有所帮助!!请关注公众号

概要

如何使用vue封装列表高级查看,表格多种配置使用

h-table的封装过程

(1)高级查询
(2)table表格
(3)tool工具设置栏

三大组件共同合成封装表格组件

查询组件封装 h-highForm

<template>
  <div class="high-form">
    <!-- 左侧筛选查询 -->
    <el-input
      placeholder="请输入内容"
      v-model="inputValue"
      @keydown.enter.native="search"
      class="input-with-select"
    >
      <el-select
        class="el-select"
        v-model="selectValue"
        slot="prepend"
        :placeholder="
          selectList && selectList.length > 0 ? selectList[0].label : '暂无数据'
        "
      >
        <el-option
          v-for="v in selectList"
          :key="v.value"
          :label="v.label"
          :value="v.value"
        >
        </el-option>
      </el-select>
      <!-- 清除按钮 -->
      <i
        slot="suffix"
        v-if="inputValue"
        @click="leftClear"
        class="el-input__icon el-icon-circle-close"
      ></i>
    </el-input>
    <!-- 外面的查询按钮 -->
    <el-button type="primary" @click="search" class="query-button-custom">
      查询
    </el-button>
    <!-- 右侧高级查询表单 -->
    <zy-dropdown
      trigger="click"
      placement="bottom"
      ref="dropDown"
      width="500"
      v-show="showSearch"
      v-if="highForm.length > 0"
      :hide-on-click="false"
      @visible-change="visibleChange"
    >
      <el-button>
        {{
          isShowDropdown
            ? "收起查询"
            : count
            ? `高级查询(${count})`
            : "高级查询"
        }}<i
          :class="
            isShowDropdown
              ? 'el-icon-arrow-up el-icon--right'
              : 'el-icon-arrow-down el-icon--right'
          "
        ></i>
      </el-button>
      <el-dropdown-menu slot="dropdown" :class="isTabItem ? 'nav-info' : ''">
        <el-button type="text" class="query-title">高级查询</el-button>
        <el-form
          :model="model"
          ref="ruleForm"
          class="form-content"
          label-width="120px"
        >
          <el-row
            v-for="(items, index) in highForm"
            :key="`highForm` + index"
            class="el-row-box"
          >
            <el-col
              :span="items.length <= 1 ? 24 : 12"
              v-for="i in items"
              :key="i.prop"
            >
              <el-form-item
                v-if="i.definite != false"
                :label="`${i.label}:`"
                :prop="i.prop"
              >
                <el-select
                  v-if="i.type === 'select'"
                  style="width: 240px"
                  v-model="model[i.prop]"
                  :placeholder="
                    i.placeholder ? i.placeholder : `请选择${i.label}`
                  "
                  :remote="i.remote ? i.remote : false"
                  :remote-method="i.remoteMethod"
                  :multiple="i.multiple ? i.multiple : false"
                  :collapse-tags="i.collapseTags ? i.collapseTags : true"
                  @blur="selectBlur($event, i)"
                  @focus="selectFocus($event, i)"
                  @change="selectChange($event, i)"
                  popper-class="popper-class"
                  :popper-append-to-body="true"
                  filterable
                  clearable
                >
                  <el-option
                    v-for="(j, k) in i.optList"
                    :key="k"
                    :label="
                      j[
                        i.options && i.options.label ? i.options.label : 'label'
                      ]
                    "
                    :value="
                      j[
                        i.options && i.options.value ? i.options.value : 'value'
                      ]
                    "
                  >
                    <div class="el-select__text" v-overflow-tooltip>
                      {{
                        j[
                          i.options && i.options.label
                            ? i.options.label
                            : "label"
                        ]
                      }}
                    </div>
                  </el-option>
                </el-select>
                <el-cascader
                  v-else-if="i.type === 'cascader'"
                  style="width: 240px"
                  v-model="model[i.prop]"
                  :props="i.options ? i.options : null"
                  :options="i.optList"
                  :collapse-tags="i.collapseTags ? i.collapseTags : true"
                  :filterable="i.filterable ? i.filterable : true"
                  :placeholder="
                    i.placeholder ? i.placeholder : `请选择${i.label}`
                  "
                  @change="cascaderChange"
                  @expand-change="expandChange"
                  clearable
                  :append-to-body="true"
                >
                </el-cascader>
                <el-date-picker
                  v-else-if="
                    i.type == 'year' ||
                    i.type == 'month' ||
                    i.type == 'date' ||
                    i.type == 'dates' ||
                    i.type == 'months' ||
                    i.type == 'years' ||
                    i.type == 'week' ||
                    i.type == 'datetime'
                  "
                  style="width: 240px"
                  v-model="model[i.prop]"
                  :type="i.type"
                  :placeholder="
                    i.placeholder ? i.placeholder : `请选择${i.label}`
                  "
                  :pickerOptions="
                    i.pickerOptions ? i.pickerOptions : pickerOptions
                  "
                  :value-format="i.format"
                  :format="i.type == 'week' ? 'yyyy 第 WW 周' : ''"
                  @change="singleChange"
                  :append-to-body="true"
                  popper-class="date-picker-pop"
                >
                </el-date-picker>
                <el-date-picker
                  v-else-if="
                    i.type == 'daterange' ||
                    i.type == 'monthrange' ||
                    i.type == 'datetimerange'
                  "
                  style="width: 240px"
                  v-model="model[i.prop]"
                  :type="i.type"
                  range-separator="至"
                  :start-placeholder="
                    i.startPlaceholder ? i.startPlaceholder : '开始时间'
                  "
                  :end-placeholder="
                    i.endPlaceholder ? i.endPlaceholder : '结束时间'
                  "
                  :pickerOptions="
                    i.pickerOptions ? i.pickerOptions : pickerOptions
                  "
                  :value-format="i.format"
                  :append-to-body="true"
                  popper-class="date-picker-pop"
                >
                </el-date-picker>
                <el-time-picker
                  v-else-if="i.type === 'timePicker'"
                  :placeholder="
                    i.placeholder ? i.placeholder : `请选择${i.label}`
                  "
                  v-model="model[i.prop]"
                  style="width: 100%"
                  popper-class="date-picker-pop"
                ></el-time-picker>
                <el-switch
                  v-else-if="i.type == 'switch'"
                  v-model="model[i.prop]"
                ></el-switch>
                <el-radio-group
                  v-else-if="i.type == 'radio'"
                  v-model="model[i.prop]"
                >
                  <el-radio
                    v-for="(j, k) in i.optList"
                    :label="j"
                    :key="k"
                  ></el-radio>
                </el-radio-group>
                <el-checkbox-group
                  v-else-if="i.type == 'checkbox'"
                  v-model="model[i.prop]"
                >
                  <el-checkbox
                    v-for="ch in i.checkboxs"
                    :label="ch.value"
                    :key="ch.value"
                    >{{ ch.label }}</el-checkbox
                  >
                </el-checkbox-group>
                <el-input
                  v-else
                  style="width: 240px"
                  :type="i.type"
                  clearable
                  v-model="model[i.prop]"
                  :maxlength="i.maxlength"
                  :placeholder="
                    i.placeholder ? i.placeholder : `请输入${i.label}`
                  "
                >
                </el-input>
              </el-form-item>
            </el-col>
          </el-row>
        </el-form>
        <slot name="operate">
          <div class="footer">
            <el-button type="primary" size="mini" @click="search">
              查询
            </el-button>
            <el-button size="mini" @click="reset">重置</el-button>
          </div>
        </slot>
      </el-dropdown-menu>
    </zy-dropdown>
  </div>
</template>
<script>
import ZyDropdown from "./dropdown.vue";
export default {
  name: "zy-high-form",
  components: { ZyDropdown },
  props: {
    isTabItem: {
      type: Boolean,
      default: false,
    },
    // 搜索配置
    formConfig: {
      type: Array,
      default: () => [],
    },
    // 默认查询数据,如果没有默认数据,可不传
    params: {
      type: Object,
      default: () => {},
    },
    // 是否展示高级搜索
    showSearch: {
      type: Boolean,
      default: () => true,
    },
    autoParams: Function,
  },
  data() {
    return {
      //高级查询展示/隐藏
      isShowDropdown: false,
      pickerOptions: {
        disabledDate: (time) => {
          const day = 365 * 24 * 3600 * 1000; // 31536000000
          // 返回小于当前日期并近一年内的日期
          return (
            time.getTime() > Date.now() ||
            time.getTime() < Date.now() - 8.64e7 - day
          );
        },
      },
      // 查询数据源
      model: {},
      // 下拉框的值
      selectValue: "firstKeyWord",
      // 输入框的值
      inputValue: "",
      // 已选数量
      count: 0,
    };
  },
  created() {
    // 在组件挂载后设置日期选择器的z-index样式
    const datePickerPop = document.querySelector(
      ".date-picker-pop .el-picker-panel"
    );
    if (datePickerPop) {
      datePickerPop.style.zIndex = 2000;
    }
    this.resetModel();
  },
  computed: {
    //左侧下拉框
    selectList() {
      const list = [{ label: "全部", value: "firstKeyWord" }];
      for (let i = 0; i < this.formConfig.length; i++) {
        const item = this.formConfig[i];
        if (item.nimble == true) {
          const key = item.prop;
          const capitalizedFirst = key[0].toUpperCase();
          const rest = key.slice(1);
          const text = capitalizedFirst + rest;
          list.push({
            label: item.label,
            value: `first${text}`,
          });
        }
        if (list.length > 6) break;
      }
      return list;
    },
    //处理搜索样式
    searchParams() {
      const model = JSON.parse(JSON.stringify(this.model));
      //对于非规则数据结构,支持自定义整理数据结构
      if (this.autoParams) {
        return this.autoParams(model);
      }
      for (let i = 0; i < this.formConfig.length; i++) {
        const config = this.formConfig[i];
        // 对时间范围数据结构进行处理
        if (
          config.type &&
          (config.type == "daterange" ||
            config.type == "monthrange" ||
            config.type == "datetimerange")
        ) {
          if (model[config.prop] && model[config.prop].length > 1) {
            model[config.prop + "BeginDate"] = model[config.prop][0];
            model[config.prop + "EndDate"] = model[config.prop][1];
          }
          delete model[config.prop];
        }
      }
      return model;
    },
    //高级查询数量
    highForm() {
      const list = [];
      let items = [];
      for (let i = 0; i < this.formConfig.length; i++) {
        const item = this.formConfig[i];
        if (items.length > 1) {
          list.push(items);
          items = [];
        }
        if (item.definite != false) {
          items.push(item);
        }
      }
      if (items.length != 0) {
        list.push(items);
      }
      return list;
    },
  },
  watch: {
    selectValue: {
      deep: true,
      immediate: true,
      handler(newVal, oldVal) {
        if (oldVal) {
          this.model[oldVal] = "";
        }
        if (newVal) {
          this.model[newVal] = this.inputValue;
        }
      },
    },
    inputValue(newVal) {
      this.model[this.selectValue] = newVal;
    },
    params: {
      deep: true,
      immediate: true,
      handler() {
        this.mergeModel();
      },
    },
    formConfig: {
      deep: true,
      immediate: true,
      handler(val) {
        //如果配置发生了变化,重置数据
        if (val && Object.keys(val).length != val.length) {
          this.initData();
        }
      },
    },
  },
  methods: {
    // 显示菜单项
    showDropDown() {
      this.$refs.dropDown.show();
    },
    // 下拉框选中
    selectChange(event, i) {
      if (i.change) {
        i.change(event, i);
      }
      this.$emit("change", event, i);
    },
    selectBlur(e, config) {
      if (config.blur) {
        config.blur(e, config);
      }
      this.$emit("blur", e, config);
      //下拉框需要刷新才能展示tooltip
      this.$forceUpdate();
    },
    selectFocus(e, config) {
      if (config.focus) {
        config.focus(e, config);
      }
      this.$emit("focus", e, config);
      //下拉框需要刷新才能展示tooltip
      this.$forceUpdate();
    },
    // 选中日期
    expandChange(val) {
      // if(val) {
      this.showDropDown();
      // }
    },
    // 级联选择器选中
    cascaderChange(val) {
      if (val) {
        this.showDropDown();
      }
    },
    // 日期单选
    singleChange(val) {
      if (val) {
        this.showDropDown();
      }
    },
    //点击菜单项下拉框出现/隐藏
    visibleChange(val) {
      this.showNum();
      this.isShowDropdown = val;
    },
    // 左侧清除按钮
    leftClear() {
      this.inputValue = "";
      this.$nextTick(() => {
        this.search();
      });
    },
    // 高级查询显示已选项数量
    showNum() {
      let num = 0;
      for (let i = 0; i < this.formConfig.length; i++) {
        const prop = this.formConfig[i].prop;
        const value = this.model[prop];
        var numReg = /^[0-9]*$/;
        var numRe = new RegExp(numReg);
        const isNumber = numRe.test(value);
        if (!isNumber && (!value || value == "")) continue; // 如果对应值非数字并为空,则跳过
        if (
          (value instanceof Array && value.length > 0) || // 数组切有值
          (value && typeof value == "string" && value.length > 0) || // 字符串切有值
          (!isNaN(parseFloat(value)) && isFinite(value)) // 是否位数字或者布尔类型
        ) {
          num++;
        }
      }
      this.count = num;
    },
    /**
     * 主要更新外部默认数据,当初始化/重置时外部默认数据发生表更时对数据进行合并
     */
    mergeModel() {
      this.model = {
        ...this.model,
        ...this.params,
      };
      this.showNum();
    },
    /**
     * 这里主要处理组件内搜索初始数据,初始化/重置时调用
     */
    resetModel() {
      const model = {
        firstKeyWord: "",
      };
      for (let i = 0; i < this.formConfig.length; i++) {
        const config = this.formConfig[i];
        model[config.prop] = config.multiple ? [] : "";
        if (config.nimble) {
          const key = config.prop;
          const capitalizedFirst = key[0].toUpperCase();
          const rest = key.slice(1);
          const text = "first" + capitalizedFirst + rest;
          model[text] = "";
        }
        // 当列表数据为空,而且传了api则获取API数据
        if ((!config.optList || config.optList.length == 0) && config.api) {
          config
            .api(config.params ? config.params : {})
            .then((res) => {
              config.optList = res.data;
            })
            .catch((err) => {
              console.log(err);
            });
        }
      }
      //设置模糊查询数据
      model[this.selectValue] = this.inputValue;
      this.model = {
        ...model,
        ...this.params,
      };
      this.showNum();
    },
    initData() {
      for (let i = 0; i < this.formConfig.length; i++) {
        const config = this.formConfig[i];
        // 当列表数据为空,而且传了api则获取API数据
        if ((!config.optList || config.optList.length == 0) && config.api) {
          config
            .api(config.params ? config.params : {})
            .then((res) => {
              config.optList = res.data;
            })
            .catch((err) => {
              console.log(err);
            });
        }
      }
    },
    reset() {
      this.resetModel();
      this.$emit("reset");
      //为了避免数据没及时更新,查询做延迟
      setTimeout(() => {
        this.search();
      }, 200);
    },
    search() {
      this.$emit("search", this.searchParams);
      //显示搜索数量和关闭弹窗
      this.visibleChange(false);
      this.$refs.dropDown.hide();
    },
  },
};
</script>
<style lang="scss" scoped>
.high-form {
  display: flex;
  margin-bottom: 20px;
}
.right-btn {
  float: right;
}
.high-form .query-button-custom {
  margin-right: 15px;
}
.query-title {
  height: 40px;
  background: #ffffff;
  font-size: 18px;
  font-family: PingFangSC-Medium, PingFang SC;
  font-weight: 600;
  // color: #1768b4;
  line-height: 24px;
  margin: 0px 10px 10px 10px;
  cursor: default;
}
/* 设置弹出框的宽高 */
.el-dropdown-menu {
  width: auto;
  padding: 10px 0px 20px 20px;
  position: absolute;
}
// 左侧输入框
.input-with-select {
  width: 357px;
  height: 36px;
  border-radius: 4px;
  margin-right: 15px;
  .el-select {
    width: 120px;
    height: 34px;
  }
  ::v-deep .el-input-group__prepend {
    height: 34px;
    background-color: #fff;
  }
}

::v-deep .date-picker-pop {
  z-index: 2000 !important;
}
::v-deep .el-select__tags {
  flex-wrap: nowrap;
  overflow: hidden;
  margin-left: 1px;
}
.el-select__text {
  flex-wrap: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  width: 200px;
}
::v-deep .el-tag.el-tag--info {
  max-width: 150px;
}
::v-deep .el-select-dropdown {
  width: 240px;
}
.form-content {
  max-height: 500px;
  overflow-y: auto;
}
.el-row {
  max-width: 760px;
  height: 58.75px; //下拉框弹出会出现界面跳动的情况
}
.el-row-box {
  display: flex;
  flex-wrap: nowrap;
}
.footer {
  text-align: end;
  padding: 10px 20px 0 0;
  width: 100%;
}
::v-deep .el-form-item--medium .el-form-item__label {
  line-height: 18px;
  display: flex;
  justify-content: flex-end;
  align-items: center;
  height: 36px;
}
::v-deep .el-form--inline .el-form-item {
  line-height: 18px;
}
::v-deep .el-form-item {
  margin-right: 20px;
}
</style>

结果页

在这里插入图片描述

右侧工具栏封装RightToolbar

<template>
  <div class="top-right-btn" :style="style">
    <el-row>
      <el-tooltip
        class="item"
        effect="dark"
        :content="__showSearch ? '隐藏搜索' : '显示搜索'"
        placement="top"
        v-if="search"
      >
        <el-button
          size="mini"
          circle
          icon="el-icon-search"
          @click="toggleSearch()"
        />
      </el-tooltip>
      <el-tooltip
        class="item"
        effect="dark"
        content="刷新"
        placement="top"
        v-if="renew"
      >
        <el-button
          size="mini"
          circle
          icon="el-icon-refresh"
          @click="refresh()"
        />
      </el-tooltip>
      <el-tooltip
        class="item"
        effect="dark"
        content="显隐列"
        placement="top"
        v-if="columns && menu"
      >
        <el-button
          size="mini"
          circle
          icon="el-icon-menu"
          @click="showColumn()"
        />
      </el-tooltip>
    </el-row>
    <el-dialog :title="title" :visible.sync="open" append-to-body>
      <el-transfer
        :titles="['显示', '隐藏']"
        v-model="value"
        :data="columns"
        @change="dataChange"
      ></el-transfer>
    </el-dialog>
  </div>
</template>
<script>
export default {
  name: "RightToolbar",
  data() {
    return {
      // 显隐数据
      value: [],
      // 弹出层标题
      title: "显示/隐藏",
      // 是否显示弹出层
      open: false,
      __showSearch: true,
    };
  },
  props: {
    showSearch: {
      type: Boolean,
      default: true,
    },
    columns: {
      type: Array,
    },
    search: {
      type: Boolean,
      default: true,
    },
    gutter: {
      type: Number,
      default: 10,
    },
    //是否展示刷新按钮
    renew: {
      type: Boolean,
      default: true,
    },
    menu: {
      type: Boolean,
      default: true,
    },
  },
  watch: {
    search: {
      immediate: true,
      handler(val) {
        if (val == false) {
          this.$nextTick(() => {
            this.__showSearch = true;
            this.toggleSearch();
          });
        }
      },
    },
    showSearch: {
      immediate: true,
      handler(val) {
        if (val != this.__showSearch) {
          this.__showSearch = val;
        }
      },
    },
  },
  computed: {
    style() {
      const ret = {};
      if (this.gutter) {
        ret.marginRight = `${this.gutter / 2}px`;
      }
      return ret;
    },
  },
  created() {
    // 显隐列初始默认隐藏列
    for (let item in this.columns) {
      if (this.columns[item].visible === false) {
        this.value.push(parseInt(item));
      }
    }
  },
  methods: {
    // 搜索
    toggleSearch() {
      this.__showSearch = !this.__showSearch;
      this.$emit("update:showSearch", this.__showSearch);
    },
    // 刷新
    refresh() {
      this.$emit("queryTable");
    },
    // 右侧列表元素变化
    dataChange(data) {
      for (let item in this.columns) {
        const key = this.columns[item].key;
        this.columns[item].visible = !data.includes(key);
      }
    },
    // 打开显隐列dialog
    showColumn() {
      this.open = true;
    },
  },
};
</script>
<style lang="scss" scoped>
::v-deep .el-transfer__button {
  border-radius: 50%;
  padding: 12px;
  display: block;
  margin-left: 0px;
}
::v-deep .el-transfer__button:first-child {
  margin-bottom: 10px;
}
</style>

结果页

在这里插入图片描述

列表组件h-table

<template>
  <div id="BasicTable">
    <zy-high-form
      v-if="showSearch"
      :form-config="bindTableOptions.formConfig"
      :auto-params="bindTableOptions.autoParams"
      :show-search="showSearch"
      :params="params"
      @search="__refreshList"
      @reset="__reset"
      @blur="highFormBlur"
      @focus="highFormFocus"
      @change="highFormChange"
      ref="searchForm"
    >
    </zy-high-form>
    <el-row v-if="$slots.header">
      <slot name="header" />
    </el-row>
    <el-row>
      <slot name="operator" />
      <right-toolbar
        v-if="
          bindTableOptions.showRightToolbar != null
            ? bindTableOptions.showRightToolbar
            : true
        "
        :showSearch.sync="showSearch"
        :search="bindTableOptions.search"
        :renew="bindTableOptions.refresh"
        :menu="bindTableOptions.menu"
        @queryTable="__getList"
        :columns="columns"
      ></right-toolbar>
    </el-row>
    <el-table
      ref="table"
      v-bind="bindTableOptions"
      :key="tableKey"
      :row-key="rowKey"
      :data="list"
      v-loading="loading"
      style="width: 100%"
      v-on="$listeners"
      :header-cell-style="{ fontWeight: 'bold', color: '#333' }"
      :default-sort="bindTableOptions.sortable ? bindTableOptions.sortable : {}"
      @sort-change="__onSortChange"
      @row-click="__rowClick"
      @selection-change="__handleSelectionChange"
    >
      <template
        v-for="(item, index) in bindTableOptions &&
        bindTableOptions.table.columns"
      >
        <!-- 需要自定义表格列样式 -->
        <el-table-column
          v-if="item.custom && item.visible && !item.columns"
          v-bind="item"
          :min-width="item.minWidth || 150"
          :key="item.prop"
        >
          <template slot-scope="scope">
            <slot :name="item.prop" :row="scope.row" :column="item"> </slot>
          </template>
        </el-table-column>
        <el-table-column
          v-bind="item"
          :min-width="item.minWidth || 150"
          v-if="!item.custom && item.visible && !item.columns"
          :key="index"
          show-overflow-tooltip
        >
        </el-table-column>
        <el-table-column
          v-if="item.visible && item.columns"
          v-bind="item"
          :min-width="item.minWidth || 180"
          :key="index"
        >
          <template slot-scope="scope">
            <zy-tool
              :config="item"
              :scope="scope"
              @click="__handleClickAction"
              :key="toolKey"
            >
            </zy-tool>
          </template>
        </el-table-column>
      </template>
    </el-table>
    <div class="page_box" v-if="showPagination">
      <pagination
        :total="pagination.total"
        :page.sync="pagination.pageNum"
        :limit.sync="pagination.pageSize"
        :page-sizes="pageSizes"
        @pagination="__getList"
      />
    </div>
  </div>
</template>

<script>
import ZyTool from "./tool.vue";
export default {
  name: "zy-table",
  components: { ZyTool },
  props: {
    //搜索条件默认数据,如果没有默认数据可以不填
    params: {
      type: Object,
      default: () => {},
    },
    //表格高度
    height: String,
    //是否展示底部分页
    showPagination: {
      type: Boolean,
      default: true,
    },
    pageSizes: {
      type: Array,
      default: () => {
        return [10, 20, 30, 50, 100];
      },
    },
    pageSize: {
      type: Number,
      default: 10,
    },
    rowKey: {
      type: String,
      default: "id",
    },
  },
  data() {
    return {
      columns: [],
      tableKey: new Date().getMilliseconds(),
      loading: false,
      showSearch: true,
      // 分页详情
      pagination: {
        pageSize: this.pageSize,
        pageNum: 1,
        total: 0,
      },
      sort: {
        prop: "", //排序字段名
        order: "", // 排序方式
      },
      // 列表数据
      list: [],
      //行选中数据
      multipleSelection: [],
      //存在列表数据变更,操作栏数据没变更的情况,因此这里设置key
      toolKey: new Date().getTime(),
    };
  },
  computed: {
    bindTableOptions() {
      return {
        height: this.height ?? `calc(100vh - ${this.showSearch ? 298 : 240}px)`, // 根据是否隐藏搜索栏调整高度
        ...this.$attrs,
      };
    },
    sortParams() {
      //如果排序字段不存在,则由服务端自主排讯
      if (!this.sort.order) return {};
      return {
        prop: this.sort.prop,
        order: this.sort.order == "ascending" ? "asc" : "desc",
      };
    },
  },
  watch: {
    columns: {
      deep: true,
      handler() {
        this.__resetColumns();
      },
    },
    showSearch: {
      deep: true,
      handler(val) {
        this.$emit("showChange", this.showSearch);
      },
    },
    "bindTableOptions.sortable": {
      deep: true,
      immediate: true,
      handler(val) {
        if (val) {
          this.sort = JSON.parse(JSON.stringify(val));
        }
      },
    },
    "bindTableOptions.multipleSelection": {
      deep: true,
      immediate: true,
      handler(val) {
        if (val) {
          const valStr = JSON.stringify(val);
          const selection = JSON.stringify(this.multipleSelection);

          if (valStr != selection) {
            this.multipleSelection = JSON.parse(valStr);
          }
        }
      },
    },
  },
  created() {
    this.columns = this.bindTableOptions.table.columns.map((v, i) => {
      return { key: i, label: v.label, visible: v.visible ?? true };
    });
    // 初始化搜索
    // 初始化搜索
    if (this.bindTableOptions.autoSearch != false) {
      this.$nextTick(() => {
        this.__getList();
      });
    }
    //如果配置不展示右上角的操作栏搜索功能,顶部的搜索同时不展示
    if (this.bindTableOptions.search == false) {
      this.showSearch = false;
    }
  },
  methods: {
    /**
     * 重置列隐藏设置
     */
    __resetColumns() {
      // 遍历设置列数据
      this.bindTableOptions.table.columns.forEach((v) => {
        // 遍历隐藏/展示列设置数据
        this.columns.forEach((v2) => {
          // 对普通列同步隐藏/展示状态
          if (v.label == v2.label) {
            v.visible = v2.visible;
          }
        });
      });
      this.$nextTick(() => {
        this.tableKey = new Date().getMilliseconds();
      });
    },
    __onSortChange({ prop, order }) {
      this.sort.prop = prop;
      this.sort.order = order;
      this.__getList();
    },
    //重置页码为1
    __refreshList() {
      this.pagination.pageNum = 1;
      this.__getList();
    },
    // 刷新列表
    __getList() {
      if (!this.bindTableOptions.api) return;
      const params = {
        ...this.getModel(),
        ...this.pagination,
        ...this.sortParams,
      };
      //删除总页数
      delete params.total;
      this.loading = true;
      this.bindTableOptions
        .api(params)
        .then((res) => {
          this.loading = false;
          // this.list = res.rows ? res.rows : this.handleTree(res.data);
          const rows = res.rows ? res.rows : res.data;
          this.list = rows;
          this.$emit("update-list", rows);
          this.$emit("update", res);
          this.toolKey = new Date().getTime();
          this.pagination.total = res.total;
          const selections = JSON.parse(JSON.stringify(this.multipleSelection));
          this.clearSelection();
          selections.forEach((row) => {
            this.toggleRowSelection(row, true);
          });
        })
        .catch((err) => {
          this.loading = false;
        });
    },
    // 点击操作栏
    __handleClickAction(type, scope, btn) {
      //  对需要进行二次确认的进行二次确认。
      if (btn.confirm) {
        this.$confirm(btn.msg ? btn.msg : `您确定${btn.text}吗?`, "提示", {
          confirmButtonText: "确定",
          cancelButtonText: "取消",
          type: "warning",
        })
          .then(() => {
            this.$emit("handleClickAction", type, scope, btn);
          })
          .catch(() => {});
        return;
      }
      this.$emit("handleClickAction", type, scope, btn);
    },
    /**
     * 选中行回调
     * @param {选中行源数据} row
     * @param {选择列配置} column
     * @param {选中事件} event
     */
    __rowClick(row, column, event) {
      //回调点击行事件
      this.$emit("row-click", row, column, event);
      // 点击表格行切换selection状态
      if (this.bindTableOptions.rowClickSelection) {
        var result = this.multipleSelection.some(
          (item) =>
            item[this.bindTableOptions.rowKey ?? "id"] ===
            row[this.bindTableOptions.rowKey ?? "id"]
        );
        this.toggleRowSelection(row, !result);
      }
    },
    /**
     *
     * @param {发生变化的表格} selection
     */
    __handleSelectionChange(selection) {
      this.multipleSelection = JSON.parse(JSON.stringify(selection));
      this.$emit("selection-change", this.multipleSelection);
    },
    /**
     * 数据重置,当没有默认数据时可以不处理,如默认时间,需要在reset回调中重置默认时间
     */
    __reset() {
      this.$emit("reset");
    },
    /**
     * 刷新数据
     * @param {是否重置页码} reset
     */
    refresh(reset) {
      if (reset) {
        this.pagination.pageNum = 1;
      }
      this.__getList();
    },
    // 获取表格实例
    getTableRef() {
      if (this.$refs.table) {
        return this.$refs.table;
      }
    },
    /**
     * 获取搜索条件
     */
    getModel() {
      if (this.$refs.searchForm) {
        return this.$refs.searchForm.searchParams;
      }
      return { ...(this.params ?? {}) };
    },
    /**
     * 获取表格查询结果
     */
    getData() {
      return this.list;
    },
    /**
     * 对表格行进行选择状态设置
     * @param {操作行数据} row
     * @param {选中状态} selection
     */
    toggleRowSelection(row, selection = true) {
      this.$refs.table.toggleRowSelection(row, selection);
    },
    clearSelection() {
      this.$refs.table.clearSelection();
    },
    highFormBlur(event, config) {
      this.$emit("blur", event, config);
    },
    highFormFocus(event, config) {
      this.$emit("focus", event, config);
    },
    highFormChange(event, config) {
      this.$emit("change", event, config);
    },
  },
};
</script>

<style lang="scss" scoped>
.page_box {
  margin-top: 20px;
}
.dropdown-menu-btn {
  margin-top: 2px !important;
}
::v-deep .el-table__empty-block {
  width: 100%;
  min-width: 100%;
  max-width: 100%;
  // padding-right: 100%;
}
::v-deep .el-table__empty-text {
  line-height: normal;
}
.el-button + .el-button {
  margin-left: 10px;
}
.el-table {
  margin-top: 10px;
}
</style>

结果页

在这里插入图片描述

vue页面使用

<template>
  <div class="app-container">
    <zy-table
      ref="ZYTable"
      v-bind="tableOptions"
      @handleClickAction="handleClickAction"
    >
      <template #operator>
        <el-button
          type="primary"
          plain
          size="small"
          icon="el-icon-plus"
          @click="handleAdd"
          v-hasPermi="['marketing:agent:add']"
          >新增经纪人</el-button
        >
      </template>
    </zy-table>
    <!-- 编辑经纪人 -->
    <edit-dialog
      ref="dialog"
      @refresh="refresh"
    ></edit-dialog>
  </div>
</template>

<script>
import columns, { formConfig } from "./table-config";
import { getAgentList,deleteAgent} from "@/api/marketing/agent";
import EditDialog from "./edit.vue";
export default {
  name: "Agent",
  components: {
    EditDialog
  },
  data() {
    return {
      tableOptions: {
        table: columns,
        api: getAgentList,
        formConfig: formConfig,
      },
    };
  },
  activated() {
    this.refresh()
  },
  methods: {
    handleClickAction(type, scope, btn) {
      switch (type) {
        case "edit":
          this.handleEdit(scope.row);
          break;
        case "delete":
          this.handleDelete(scope.row);
          break;
      }
    },
    // 新增
    handleAdd() {
      this.$refs.dialog.open(false);
    },
    // 编辑
    handleEdit(row) {
      this.$refs.dialog.open(true,row);
    },
    // 删除
    handleDelete(row) {
      deleteAgent({ activityReBrokerId: row.id }).then(res => {
        this.refresh();
        this.$message.success("删除成功!");
      })
    },
    refresh() {
      this.$refs.ZYTable.refresh();
    },
  },
};
</script>

<style lang="scss" scoped>
::v-deep .fileUrl {
  .cell {
    padding: 2px 0;
    display: flex;
    align-items: center;
    justify-content: center;
    .el-image__error {
      padding: 0 10px;
      font-size: 12px;
    }
  }
}
</style>

js文件

import { getConfigList } from "@/api/marketing/configuration";
import { emptyTextFormatter } from "@/utils/tableFormatter";

// 操作列
export const actionColumns = [
  {
    text: "编辑",
    event: "edit",
    type: "text",
    has: "marketing:agent:edit",
  },
  {
    text: "删除",
    event: "delete",
    type: "text",
    has: "marketing:agent:delete",
    confirm: true,
    msg: "您确定删除该经纪人信息吗?",
  },
];

// 表格列
const tableColumns = [
  {
    type: "index",
    label: "序号",
    width: "60",
    align: "center",
  },
  {
    prop: "name",
    label: "姓名",
    minWidth: "120",
    "show-overflow-tooltip": true,
    formatter: emptyTextFormatter,
  },
  {
    prop: "phone",
    label: "联系电话",
    minWidth: "120",
    "show-overflow-tooltip": true,
    formatter: emptyTextFormatter,
  },
  {
    prop: "marketActivityName",
    label: "所属项目",
    minWidth: "200",
    "show-overflow-tooltip": true,
    formatter: emptyTextFormatter,
  },
  {
    prop: "channelsTypeStr",
    label: "渠道身份",
    minWidth: "120",
    "show-overflow-tooltip": true,
    formatter: emptyTextFormatter,
  },
  {
    prop: "registerSourceStr",
    label: "注册来源",
    minWidth: "120",
    "show-overflow-tooltip": true,
    formatter: emptyTextFormatter,
  },
  {
    prop: "registerDate",
    label: "注册时间",
    minWidth: "180",
    align: "center",
    "show-overflow-tooltip": true,
    formatter: emptyTextFormatter,
  },
  {
    prop: "consultant",
    label: "置业顾问",
    minWidth: "120",
    "show-overflow-tooltip": true,
    formatter: emptyTextFormatter,
  },
  {
    prop: "createUserName",
    label: "创建人",
    minWidth: "120",
    "show-overflow-tooltip": true,
    formatter: emptyTextFormatter,
  },
  {
    prop: "createDate",
    label: "创建时间",
    align: "center",
    width: "180px",
    "show-overflow-tooltip": true,
    formatter: emptyTextFormatter,
  },
  {
    prop: "updateUserName",
    label: "更新人",
    "show-overflow-tooltip": true,
    formatter: emptyTextFormatter,
  },
  {
    prop: "updateDate",
    label: "更新时间",
    align: "center",
    width: "180px",
    "show-overflow-tooltip": true,
    formatter: emptyTextFormatter,
  },
  {
    prop: "",
    label: "操作",
    columns: actionColumns,
    fixed: "right",
    align: "center",
  },
];

export const formConfig = [
  {
    type: "input",
    prop: "name",
    label: "姓名",
    placeholder: "请输入姓名",
    nimble: true,
  },
  {
    type: "input",
    prop: "consultant",
    label: "置业顾问",
    placeholder: "请输入置业顾问",
    nimble: true,
  },
  {
    type: "input",
    prop: "phone",
    label: "联系电话",
    placeholder: "请输入联系电话",
    nimble: true,
  },
  {
    type: "daterange",
    prop: "register",
    label: "注册时间",
    format: "yyyy-MM-dd",
    pickerOptions: {},
  },
  {
    type: "select",
    prop: "marketActivityIds",
    label: "所属项目",
    placeholder: "请选择所属项目",
    multiple: true,
    api: getConfigList,
    options: {
      label: "marketActivityName",
      value: "id",
    },
  },
  {
    type: "select",
    prop: "channelsTypes",
    label: "渠道身份",
    placeholder: "请选择渠道身份",
    multiple: true,
    // api: getRoomStatus,
    // options: {
    //   label: "label",
    //   value: "id",
    // },
    optList: [
      { value: "0", label: "自由经纪人" },
      { value: "1", label: "渠道商" },
      { value: "2", label: "内部员工" },
      { value: "3", label: "招商人员" },
      { value: "4", label: "运营人员" },
    ],
  },
  {
    type: "select",
    prop: "registerSource",
    label: "注册来源",
    placeholder: "请选择注册来源",
    // api: getUseType,
    // options: {
    //   label: "label",
    //   value: "id",
    // },
    optList: [
      { value: "0", label: "小程序" },
      { value: "1", label: "注册后台" },
    ],
  },
  {
    type: "input",
    prop: "createUserName",
    label: "创建人",
    placeholder: "请输入创建人",
  },
  {
    type: "daterange",
    prop: "create",
    label: "创建时间",
    format: "yyyy-MM-dd",
    pickerOptions: {},
  },
  {
    type: "input",
    prop: "updateUserName",
    label: "更新人",
    placeholder: "请输入更新人",
  },
  {
    type: "daterange",
    prop: "update",
    label: "更新时间",
    format: "yyyy-MM-dd",
    pickerOptions: {},
  },
];

export default {
  columns: tableColumns,
};

有需要的请私信博主,还请麻烦给个关注,博主不定期更新组件封装,或许能够有所帮助!!请关注公众号

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/404551.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

如何用GPT进行成像光谱遥感数据处理?

第一&#xff1a;遥感科学 从摄影侦察到卫星图像 遥感的基本原理 遥感的典型应用 第二&#xff1a;ChatGPT ChatGPT可以做什么&#xff1f; ChatGPT演示使用 ChatGPT的未来 第三&#xff1a;prompt 提示词 Prompt技巧&#xff08;大几岁&#xff09; 最好的原则和策…

互动游戏团队如何将性能体验优化做到TOP级别

一、背景 随着互动游戏业务 DAU 量级增加&#xff0c;性能和体验重要性也越发重要&#xff0c;好的性能和体验不仅可以增加用户使用体感&#xff0c;也可以增加用户对于互动游戏的使用粘性。 对现状分析&#xff0c;主要存在首屏渲染速度慢、打开页面存在白屏、页面加载过多资…

app测试必掌握的核心测试:UI、功能测试!

一、UI测试 UI即User Interface (用户界面)的简称。UI 设计则是指对软件的人机交互、操作逻辑、界面美观的整体设计。好的UI设计不仅是让软件变得有个性有品味,还要让软件的操作变得舒适、简单、自由、充分体现软件的定位和特点。手机APP从启动界面开始, 到运行过程,直至退出,…

聊聊mysql的七种日志

进入正题前,可以先简单介绍一下,MySQL的逻辑架构, MySQL的逻辑架构大致可以分为三层: 第一层:处理客户端连接、授权认证,安全校验等。第二层:服务器 server 层,负责对SQL解释、分析、优化、执行操作引擎等。第三层:存储引擎,负责MySQL中数据的存储和提取。我们要知道…

云图极速版限时免费活动

产品介绍 云图极速版是针对拥有攻击面管理需求的用户打造的 SaaS 应用&#xff0c;致力于协助用户发现并管理互联网资产攻击面。 实战数据 (2023.11.6 - 2024.2.23) 云图极速版上线 3 个月以来&#xff0c;接入用户 3,563 家&#xff0c;扫描主体 19,961 个&#xff0c;累计发…

OpenCV笔记4:级联分类器实现嘴部检测

OpenCV 嘴部检测 """ 嘴部区域检测 1. 静态图像检测嘴部区域创建分类器加载特征文件检测图像绘制嘴部区域显示 2. 切换为摄像头 """ import cv2 import numpy as npclass FaceDetect:def __init__(self):# 级联分类器# 创建级联分类器&#xf…

云原生之容器管理工具Portainer

1. 简介 前面文章我们讲Docker、Docker Compose和Docker Swarm都是在Linux系统上手工命令行去操作&#xff0c;在第一次安装的时候可以命令行&#xff0c;以后运维和CICD流程操作中&#xff0c;如果还要命令行去各个节点操作&#xff0c;操作就麻烦了&#xff0c;工作效…

Seata 入门知识

目录 概述 工作流程 工作模式 AT模式 TCC模式 概述 Seata 是一款开源的分布式事务解决方案&#xff0c;致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式&#xff0c;为用户打造一站式的分布式解决方案。 AT模式是阿里首推…

Linux系统运维:离线安装sar-性能监视和分析工具

目 录 一、前言 二、系统环境 三、安装sar &#xff08;一&#xff09;准备工作 1、下载 sar 工具的安装包&#xff1a; 2、将安装包传输到 CentOS 服务器 &#xff08;二&#xff09;安装工作 1、解压 2、配置安装 3、编译 4、安装 &#xff08;三&#xff0…

C# Onnx 使用onnxruntime部署实时视频帧插值

目录 介绍 效果 模型信息 项目 代码 下载 C# Onnx 使用onnxruntime部署实时视频帧插值 介绍 github地址&#xff1a;https://github.com/google-research/frame-interpolation FILM: Frame Interpolation for Large Motion, In ECCV 2022. The official Tensorflow 2…

【Flink集群RPC通讯机制(四)】集群组件(tm、jm与rm)之间的RPC通信

文章目录 1. 集群内部通讯方法概述2. TaskManager向ResourceManager注册RPC服务3. JobMaster向ResourceManager申请Slot计算资源 现在我们已经知道Flink中RPC通信框架的底层设计与实现&#xff0c;接下来通过具体的实例了解集群运行时中组件如何基于RPC通信框架构建相互之间的调…

大数据 - Spark系列《十一》- Spark累加器详解

Spark系列文章&#xff1a; 大数据 - Spark系列《一》- 从Hadoop到Spark&#xff1a;大数据计算引擎的演进-CSDN博客 大数据 - Spark系列《二》- 关于Spark在Idea中的一些常用配置-CSDN博客 大数据 - Spark系列《三》- 加载各种数据源创建RDD-CSDN博客 大数据 - Spark系列《…

2024/02/23

使用消息队列完成两个进程间相互通信 A.c #include<myhead.h> struct msgbuf {long mtype;char mtext[1024]; }; //定义表示正文内容大小的宏 #define MSGSIZE sizeof(struct msgbuf)-sizeof(long)int main(int argc, const char *argv[]) {//创建一个key值key_t key;ke…

知乎66条高赞回答,句句醍醐灌顶!

-01- 穷人是小心翼翼地大方&#xff0c; 有钱人是大大方方地小气。 ——论如何判断一个人是真有钱还是装有钱 -02- 枕头要常晒&#xff0c; 因为里面装满了心酸的泪和发霉的梦。 ——一切终将随风而逝 -03- 人活得累&#xff0c;一是太认真&#xff0c;二是太想要。 …

第3部分 原理篇2去中心化数字身份标识符(DID)(3)

3.2.2.4. DID文档 (DID Document) 本聪老师&#xff1a;DID标识符和DID URL还都只是ID&#xff0c;必须为它附加一个基本属性才可以证明是该主体独有的。这个就是我们下面介绍的DID文档。 本聪老师&#xff1a;每个DID标识符都唯一对应一个DID文档&#xff0c;也可以说&#x…

计算机功能简介:EC, NVMe, SCSI/ISCSI与块存储接口 RBD,NUMA

一 EC是指Embedded Controller 主要应用于移动计算机系统和嵌入式计算机系统中&#xff0c;为此类计算机提供系统管理功能。EC的主要功能是控制计算机主板上电时序、管理电池充电和放电&#xff0c;提供键盘矩阵接口、智能风扇接口、串口、GPIO、PS/2等常规IO功能&#xff0c;…

docker自定义网络实现容器之间的通信

Background docker原理 docker是一个Client-Server结构的系统&#xff0c;Docker的守护进程运行在主机上。通过Socket从客户端访问。docker核心三大组件&#xff1a;image–镜像、container-容器、 repository-仓库。docker使用的cpu、内存以及系统内核等资源都是直接使用宿主…

A Novel Two-Layer DAG-based Reactive Protocol for IoT Data Reliability in Metaverse

在IOT 场景中&#xff0c;需要保证数据的完整性和可靠性。通常区块链可以用来做这件事&#xff0c;但是IoT 设备的计算能力和贷款都是有限的。 对于PBFT 要求的通信量太大。 本文提出的 two layer directed acycle graph (2LDAG) 是一种被动共识协议&#xff0c;除非有节点主动…

快速构建 Debezium MySQL Example 数据库

博主历时三年精心创作的《大数据平台架构与原型实现&#xff1a;数据中台建设实战》一书现已由知名IT图书品牌电子工业出版社博文视点出版发行&#xff0c;点击《重磅推荐&#xff1a;建大数据平台太难了&#xff01;给我发个工程原型吧&#xff01;》了解图书详情&#xff0c;…

EXCEL 在列不同单元格之间插入N个空行

1、第一步数据&#xff0c;要求在每个数字之间之间插入3个空格 2、拿数据个数*&#xff08;要插入空格数1&#xff09; 19*4 3、填充 4、复制数据到D列 5、下拉数据&#xff0c;选择复制填充这样1-19就会重复4次 6、全选数据D列排序&#xff0c;这样即完成了插入空格 以…