输入框滚动加载与搜索如何进行封装?

作者站长头像
站长
· 阅读数 21

技术 vue2 项目 框架是 antdv 1.7.8我项目中有很多表单,表单中有很多Select 选择器 选择器的内容是项目中的某一个列表,有很多这种下拉,在不同的界面。我的问题是,单独的每一个下拉框都可以进行 远程搜索与滚动加载,但是接口是不同的,所以如何进行封住一下写一个,所有的下拉框都能用。代码demo如此

<template>
  <div>
    <div>
      <a-select style="width: 500px;" v-model="queryParam.industry_id" show-search :filter-option="false"
        :default-active-first-option="false" @search="handleSearch" @popupScroll="handlePopupScroll">
        <a-select-option v-for="(item, index) in industryList" :key="index" :value="item.industry_id">
            {{item.industry_name }}
        </a-select-option>
      </a-select>
    </div>
    <div>
      <a-select style="width: 500px;" show-search :filter-option="false" :default-active-first-option="false"
        @search="handleSearchEnt" v-model="queryParam.ent_id" @popupScroll="handlePopupScrollEnt">
        <a-select-option v-for="(item, index) in industryEntList" :key="index" :value="item.ent_id">
            {{ item.ent_name}}
        </a-select-option>
      </a-select>
    </div>
  </div>
</template>

<script>
import {
  orgSelectAPI,
  orgEntSelectAPI,
} from "@/api/enterprise.js";
import _ from "lodash";
export default {
  data() {
    return {
      queryParam: {},
      industryList: [], // 产业下拉数据
      industryCount: 10, // 产业默认10个
      industrySize: 0, // 总条数数据改变时候必须低于这个值
      industrySearch: "", // 远程搜索值
      industryEntList: [],
      entCount: 10,
      entSize: 0,
      industryEntSearch: "",
    };
  },
  created() {
    this.getIndustryList();
    this.getIndustryEntList();
  },
  methods: {
    // 产业下拉加载
    handlePopupScroll: _.debounce(function (e) {
      const { target } = e;
      const { scrollTop, scrollHeight, offsetHeight } = target;
      if (
        this.industryCount <= this.industrySize &&
        scrollTop + 2 + offsetHeight >= scrollHeight
      ) {
        this.industryCount = Math.min(
          this.industryCount + 10,
          this.industrySize
        );
        this.getIndustryList(this.industrySearch);
      }
    }, 500),
    // 远程搜索
    handleSearch: _.debounce(function (e) {
      this.industrySearch = e;
      this.getIndustryList(this.industrySearch);
    }, 500),
    // 获取产业数据
    async getIndustryList(e) {
      let res = await orgSelectAPI({
        page: 1,
        rows: e ? 0 : this.industryCount,
        name: e,
      });
      this.industry_id = "";
      this.industryList = [];
      this.industrySize = res.data.total;
      this.industryList = res.data.rows;
    },
    handleSearchEnt: _.debounce(function (e) {
      this.industryEntSearch = e;
      this.getIndustryEntList("", e);
    }, 500),
    handlePopupScrollEnt: _.debounce(function (e) {
      const { target } = e;
      const { scrollTop, scrollHeight, offsetHeight } = target;
      if (
        this.entCount <= this.entSize &&
        scrollTop + 2 + offsetHeight >= scrollHeight
      ) {
        this.entCount = Math.min(this.entCount + 10, this.entSize);
        this.getIndustryEntList(this.industry_id, this.industryEntSearch);
      }
    }, 500),
    // 获取企业数据
    async getIndustryEntList(e, name) {
      let res = await orgEntSelectAPI({
        page: 1,
        rows: this.entCount,
        industry_id: e,
        name,
      });
      this.industryEntList = []
      this.industryEntList = res.data.rows;
      this.entSize = res.data.total;
    },
  },
};
</script>

像这种数据我有很多,这种下拉出现一个我就得定义一个 count与 size 还有搜索与滚动的函数,写起来很麻烦,但是接口又不相同,我该如何进行封装一下,让这个东西写一次,后边的都能用

求大佬给个封装思路,接口与数据如何搞

经过大佬提示之后封装如下

<template>
  <a-select :placeholder='showPlaceHolder' v-model="selectedValue" style="width: 100%;" show-search :filter-option="false"
    :default-active-first-option="false" @search="handleSearch" @popupScroll="handlePopupScroll">
    <a-select-option v-for="(item, index) in showList" :key="index" :value="item[valueKey]">
      {{ item[labelKey] }}
    </a-select-option>
  </a-select>
</template>
<script>
import _ from "lodash";
export default {
  props: {
    showPlaceHolder: '',
    apiFunction: {
      type: Function,
      required: true,
    },
    queryParams: {
      type: Object,
      default: () => ({}),
    },
    valueKey: {
      type: String,
      default: 'id',
    },
    labelKey: {
      type: String,
      default: 'name',
    },
    initialCount: {
      type: Number,
      default: 10,
    },
  },
  data() {
    return {
      selectedValue: undefined,
      showList: [],
      pageCount: this.initialCount, // 每页多少条
      page: 1, // 当前第几页
      allPage: 0, // 总共有多少页
      pageTotal: 0, // 总共有多少条
      itemSearch: "", // 搜索框搜索输入值
      countNum: 0, // 计数器
    };
  },
  watch: {
    selectedValue(newV) {
      this.$emit('getValue', newV)
      this.itemSearch = undefined
      this.getList();
    },
    queryParams: {
      deep: true,
      handler(newV, oldV) {
        // 表单重置
        if (oldV[this.valueKey] && !newV[this.valueKey]) {
          this.selectedValue = null
        }
      }
    }
  },
  created() {
    if (this.queryParams && this.queryParams[this.valueKey]) {
      // 有值就表示回显数据,回显只查当前条
      this.selectedValue = this.queryParams[this.valueKey]
      this.byIdGetList(this.queryParams)
    } else {
      this.getList();
    }
  },
  /**
   * 引用 
   * import popupScrollSelect from "@/views/common/components/popupScrollSelect.vue";
   * <popupScrollSelect :showPlaceHolder="$t('pleaseSelect') + $t('facility_cn')" :query-params="{ facility_id: formRight.facility_id }" :api-function="emissionConfigFacilityAPI" value-key="facility_id" label-key="facility_cn" @getValue="e => formRight.facility_id = e"> </popupScrollSelect>
   * 现在需要修改成分页请求数据,第一次请求 第1页,10条 第二次请求 2页,10条,将 第二页与第一页的数据拼接起来 
   * 设计思路,每页十条得到总页数,向上取整,页码每次加一,直到加到最大值,每次得到数据之后,push到原始展示数组中。
   * 
   */
  methods: {
    async getList(searchValue = "", page) {
      const response = await this.apiFunction({
        page: page ? page : 1,
        rows: searchValue ? 0 : this.pageCount,
        name: searchValue,
      });
      if (searchValue) {
        this.showList = response.data.rows;
      } else {
        // 无法判断上次数据是搜索之后的 showList 还是下拉之后的数据,需要去重才能赋值
        const mergedArray = [...this.showList.concat(response.data.rows)];
        this.showList = _.uniqWith(mergedArray, _.isEqual)
      }
      // 总页数,总条数只需要计算一次
      if (this.countNum == 0) {
        this.pageTotal = response.data.total;
        this.allPage = Math.ceil(this.pageTotal / this.pageCount) // 最多有多少页
        this.countNum++
      }
    },
    handleSearch: _.debounce(function (value) {
      this.itemSearch = value;
      this.getList(this.itemSearch);
    }, 500),
    handlePopupScroll: _.debounce(function (e) {
      const { target } = e;
      const { scrollTop, scrollHeight, offsetHeight } = target;
      if (
        this.page <= this.allPage &&
        scrollTop + 2 + offsetHeight >= scrollHeight
      ) {
        // this.pageCount = Math.min(this.pageCount + 10, this.pageTotal);
        this.page = Math.min(this.page + 1, this.allPage);
        this.getList(this.itemSearch, this.page);
      }
    }, 500),
    async byIdGetList(queryParams) {
      const response = await this.apiFunction(queryParams);
      if (!this.showList.some(item => JSON.stringify(item) == JSON.stringify(response.data.rows[0]))) {
        this.showList = [...this.showList, ...response.data.rows]
      }
    },
  },
};
</script>

回复
1个回答
avatar
test
2024-06-26

封装:

<template>
  <a-select :placeholder="placeholder" v-model="selectedValue" style="width: 100%;" show-search :filter-option="false"
    :default-active-first-option="false" @search="handleSearch" @popupScroll="handlePopupScroll">
    <a-select-option v-for="(item, index) in list" :key="index" :value="item[valueKey]">
      {{ item[labelKey] }}
    </a-select-option>
  </a-select>
</template>

<script>
import _ from "lodash";

export default {
  props: {
    placeholder: String,
    apiFunction: {
      type: Function,
      required: true,
    },
    queryParams: Object,
    valueKey: {
      type: String,
      default: 'id',
    },
    labelKey: {
      type: String,
      default: 'name',
    },
    initialCount: {
      type: Number,
      default: 10,
    },
  },
  data() {
    return {
      selectedValue: null,
      list: [],
      count: this.initialCount,
      page: 1,
      total: 0,
    };
  },
  watch: {
    selectedValue(value) {
      this.$emit('input', value);
    },
    queryParams: {
      deep: true,
      handler() {
        this.getList();
      },
    },
  },
  created() {
    this.getList();
  },
  methods: {
    async getList(searchValue = "", page = 1) {
      try {
        const response = await this.apiFunction({
          page,
          rows: searchValue ? 0 : this.count,
          name: searchValue,
        });
        this.list = searchValue ? response.data.rows : [...this.list, ...response.data.rows];
        this.total = response.data.total;
      } catch (error) {
        console.error(error);
      }
    },
    handleSearch: _.debounce(function (value) {
      this.getList(value);
    }, 500),
    handlePopupScroll: _.debounce(function (e) {
      const { target } = e;
      const { scrollTop, scrollHeight, offsetHeight } = target;
      if (this.page * this.count < this.total && scrollTop + offsetHeight >= scrollHeight) {
        this.page += 1;
        this.getList("", this.page);
      }
    }, 500),
  },
};
</script>
回复
likes
适合作为回答的
  • 经过验证的有效解决办法
  • 自己的经验指引,对解决问题有帮助
  • 遵循 Markdown 语法排版,代码语义正确
不该作为回答的
  • 询问内容细节或回复楼层
  • 与题目无关的内容
  • “赞”“顶”“同问”“看手册”“解决了没”等毫无意义的内容