基于vue2 + Ant Design 封装input(输入)下拉Table表格

15 篇文章 6 订阅
10 篇文章 0 订阅
封装 AInputTable 组件
<!--下拉Table-->
<template>
  <div class="input-select-table" ref="inputTableRef"  v-clickoutside="handleHide">
    <div class="input-select-table-input" @click="disabled?this:handleShow()">
      <a-input
        v-model="showValue"
        :disabled="disabled"
        class="select-table-input"
        :placeholder="placeholder"
        @change="inputValueChange"
      >
        <a-icon type="bars" style="color: rgba(0,0,0,.45)" slot="suffix" />
      </a-input>
    </div>
    <div v-bind:class="getTableVisible" :style="tableDivStyle()" ref="tDivTableList">
      <t-table
        ref="tTableList"
        :columns="tableColumn"
        :dataSource="dataSource"
        :tableConfig="tableConfig"
        :tableMethods="tableMethods"
      >
      </t-table>

    </div>
  </div>
</template>

<script>
import { getAction } from "@/api/manage";
import { deepClone, filterObj } from '@/utils/util';
import debounce from 'lodash/debounce'
import tTable from '@/components/content/defaultTable'

const clickoutside = {
  // 初始化指令
  bind(el, binding, vnode) {
    function documentHandler(e) {
      // 这里判断点击的元素是否是本身,是本身,则返回
      if (el.contains(e.target)) {
        return false
      }
      // 判断指令中是否绑定了函数
      if (binding.expression) {
        // 如果绑定了函数 则调用那个函数,此处binding.value就是handleHide方法
        binding.value(e)
      }
    }

    // 给当前元素绑定个私有变量,方便在unbind中可以解除事件监听
    el.__vueClickOutside__ = documentHandler;
    document.addEventListener('click', documentHandler)
  },
  update() {
  },
  unbind(el, binding) {
    // 解除事件监听
    document.removeEventListener('click', el.__vueClickOutside__);
    delete el.__vueClickOutside__
  }
};

export default {
  name: 'AInputTable',
  props: {
    placeholder: {
      type: String,
      required: false,
      default: ''
    },
    disabled: {
      type: Boolean,
      required: false,
      default: false,
    },
    url: {
      type: String,
      required: false,
      default: ''
    },
    columns: {
      type: Array,
      required: false,
      default: () => [],
    },
    value: {
      type: String,
      required: false,
      default: '',
    },
    saveLabel: {
      type: String,
      required: false,
      default: 'id',
    },
    showLabel: {
      type: String,
      required: false,
      default: 'name',
    },
    sorter: {
      type: Object,
      required: false,
      default: ()=>{},
    },
    queryParams: {
      type: Object,
      required: false,
      default: ()=>{},
    },
    superQueryParams: {
      type: String,
      required: false,
      default: '',
    },
    pageSize: {
      type: Number,
      required: false,
      default: 10,
    },
    rowKey: {
      type: String,
      required: false,
      default: "id",
    },
    scroll: {
      type: Object,
      required: false,
      default: function () {
        return { x:true, y: 300 }
      },
    },
  },
  directives: {clickoutside},
  components: {
    tTable
  },
  data() {
    return {
      tableVisible: false,
      //----- new -----
      dataSource: [],
      /* 分页参数 */
      ipagination: {
        current: 1,
        pageSize: this.pageSize,
        pageSizeOptions: ['5','10','15','20','30'],
        showTotal: (total, range) => {
          return range[0] + "-" + range[1] + " 共" + total + "条"
        },
        showSizeChanger: true,
        total: 0
      },
      isorter:  this.sorter,/* 排序参数 */
      showValue:'',//展示值
      saveValue:'',//保存值
      record:{},
      /* table加载状态 */
      loading: false,
      // 存储各个div的style
      style: {
        tableDiv:{
          position: "fixed",
          // position: "absolute",
          zIndex: "999",
          backgroundColor: "#ffffff",
          border:"#dedede 1px solid",
          padding: "5"
        },
      },
      tableMethods: {
        change: this.handleTableChange,
      },
    }
  },
  created() {
  },
  computed: {
    tableColumn() {
      let jsonColumns = deepClone(this.columns);
      return jsonColumns.filter(item => item.dataIndex != 'action')
    },
    tableConfig() {
      return {
        pagination: this.ipagination,
        loading: this.loading,
        rowKey: "id",
        size:"small",
        scroll: this.scroll,
        bordered: true,
        customRow: this.onCustomRow
      }
    },
    getTableVisible() {
      if (this.tableVisible) {
        return 'showTable';
      } else {
        return 'hideTable';
      }
    },
  },
  watch: {
    value: {
      immediate: true,
      handler(val) {
        if (val) {
          this.saveValue = val;
          this.queryShowValueBySaveValue();
        } else {
          this.saveValue = '';
          this.showValue = '';
          this.record = {};
          this.$emit('getRecord',{});
          this.loadData(1);
        }
      }
    },
  },
  methods: {
    loadData(arg) {
      if(!this.url){
        this.$message.error("AInputTable组件未指定请求url,请设置url属性!")
        return
      }
      //加载数据 若传入参数1则加载第一页的内容
      if (arg === 1) {
        this.ipagination.current = 1;
      }
      let params = this.getQueryParams();//查询条件
      getAction(this.url, params).then((res) => {
        if (res.success) {
          this.dataSource = res.result.records;
          this.ipagination.total = res.result.total;
        }
        if(res.code===510){
          this.$message.warning(res.message)
        }
      })
    },
    getQueryParams() {
      let superQueryParamsArr = []
      if(this.superQueryParams) {
        superQueryParamsArr = JSON.parse(this.superQueryParams)
      }
      if(this.showValue) {
        //对输入文字进行模糊查询
        let queryParamsModel = {type: 'string', rule: 'like', field: this.showLabel, val: this.showValue}
        superQueryParamsArr.push(queryParamsModel)
      }
      let sqp = {}
      if(superQueryParamsArr) {
        sqp['superQueryParams'] = encodeURI(JSON.stringify(superQueryParamsArr))
      }

      let param = Object.assign(sqp, this.queryParam, this.isorter ,this.filters);
      param.field = this.getQueryField();
      param.pageNo = this.ipagination.current;
      param.pageSize = this.ipagination.pageSize;
      return filterObj(param);
    },
    getQueryField() {
      //TODO 字段权限控制
      let str = "id,";
      this.tableColumn.forEach(function (value) {
        str += "," + value.dataIndex;
      });
      return str;
    },
    handleHide() { // 点击除了页面其他地方关闭车型选择
      this.tableVisible = false;
    },
    handleShow() {
      this.$emit('click', this.saveValue);
      this.loadData(1);
      this.tableVisible = true;
    },
    onCustomRow(record) {
      return {
        props: {},
        on: {
          click: () => {
            this.record = record;
            this.saveValue = this.record[this.saveLabel];
            this.showValue = this.record[this.showLabel];
            this.$emit('select', this.saveValue);
            this.$emit('input', this.saveValue);
            this.$emit('change', this.saveValue);
            this.$emit('getRecord',this.record);
            this.handleHide();
          },
        },
      };
    },
    inputValueChange(e) {
      this.showValue = e.target.value;
      this.$emit('inputChange',e.target.value);
      if(this.showValue === null || this.showValue === undefined || this.showValue === ''){
        this.clear();
      }
      debounce(()=>{this.loadData(1)}, 1000);
    },
    getShowValue(){
      return this.showValue;
    },
    getSaveValue(){
      return this.saveValue;
    },
    clear() {
      this.saveValue = '';
      this.showValue = '';
      this.record = {};
      this.$emit('select', '');
      this.$emit('input', '');
      this.$emit('change', '');
      this.$emit('getRecord',{});
      this.handleHide();
    },
    handleTableChange(pagination, filters, sorter) {
      //分页、排序、筛选变化时触发
      //TODO 筛选
      if (Object.keys(sorter).length > 0) {
        this.isorter.column = sorter.field;
        this.isorter.order = "ascend" == sorter.order ? "asc" : "desc"
      }
      this.ipagination = pagination;
      this.loadData();
    },
    queryShowValueBySaveValue(page = 1) {
      let params = this.getQueryParams(); // 查询条件
      params.pageNo = page; // 设置当前页码
      params.pageSize = this.ipagination.pageSize; // 设置每页记录数

      getAction(this.url, params).then((res) => {
        if (res.success) {
          this.dataSource = res.result.records;
          this.ipagination.total = res.result.total;

          // 在当前页查找目标值
          let found = false;
          for (let i = 0; i < this.dataSource.length; i++) {
            const item = this.dataSource[i];
            if (item && item[this.saveLabel] == this.saveValue) {
              this.showValue = item[this.showLabel];
              this.record = item;
              this.ipagination.current = page; // 更新当前页码
              found = true;
              break;
            }
          }

          if (!found && page * params.pageSize < res.result.total) {
            // 如果当前页未找到目标值且还有下一页,继续查找下一页
            this.queryShowValueBySaveValue(page + 1);
          }
        } else {
          this.$message.warning(res.message);
        }
      });
    },
    //表格div样式
    tableDivStyle() {
      let style = Object.assign({}, this.style.tableDiv)
      let width1 = this.getDivTopLength();
      let width2 = this.realTrWidth();
      if(width1>width2){
        style['width'] = width2+"px";
      }else {
        style['width'] = width1+"px";
      }

      this.scroll.x = style.width

      //设置表格div的位置样式
      this.setTableDivPosition()
      return style
    },
    //设置表格div的位置样式
    setTableDivPosition() {
      this.$nextTick(() => {
        if (this.$refs.inputTableRef && this.$refs.tDivTableList) {
          let tDivTableList = this.$refs.tDivTableList
          // 输入框在页面上的位置
          const inputBox = this.$refs.inputTableRef.getBoundingClientRect();

          // 视口高度
          const viewportHeight =
            window.innerHeight || document.documentElement.clientHeight;
          // 视口高度 - 输入框元素底距离视顶的高度 = 输入框距离视口下方的高度
          let inputBoxBottomDistance = viewportHeight - inputBox.bottom;
          // 表格的高度
          let selectTableHeight = tDivTableList.offsetHeight + 8;

          // 如果下方的距离不够表格展示
          if (inputBoxBottomDistance < selectTableHeight) {
            tDivTableList.style.top = inputBox.top - selectTableHeight + "px";
          } else {
            tDivTableList.style.top = inputBox.top + inputBox.height + "px";
          }
        }
      })
    },
    //table 列总宽度
    realTrWidth() {
      let calcWidth = 0
      this.tableColumn.forEach((column, i) => {
        let { type, width } = column
        // 隐藏字段不参与计算
        if (typeof width === 'number') {
          calcWidth += width
        } else if (typeof width === 'string') {
          width = width.replace("px","");
          calcWidth += Number(width)
        } else {
          calcWidth += Number('120')
        }
      })
      return calcWidth
    },
    //获取指定第v据屏幕右侧宽度
    getDivTopLength() {
      return 600;
    },
  },
  //2.2新增 在组件内定义 指定父组件调用时候的传值属性和事件类型
  model: {
    prop: 'value',
    event: 'input'
  }
}
</script>
<style scoped lang="less">
.input-select-table {
  width: 91%;
  .showTable {
    display: block;
  }
  .hideTable {
    display: none;
  }
  .select-table-input {
    margin-right: 20px;
  }

  /deep/ .ant-spin-container > .ant-table > .ant-table-content > .ant-table-scroll > .ant-table-header {
    overflow: hidden !important;
    overflow-y: scroll !important;
    margin-bottom: 0 !important;
  }
}
</style>
参数配置
参数类型必填说明
placeholderString输入框未填值展示提示信息
disabledBooleantrue/false 默认false
valueString绑定v-model或是v-decorator后不需要设置
columnsArray表格列的配置描述
urlStringtable 数据源请求地址 查询需返回带分页的数据
sorterObject排序字段及排序方式 { column: this.sorter, order: ‘desc’ }
saveLabelString选中实际保存字段
showLabelString选中输入框展示字段
queryParamsString查询条件
pageSizeString指定每页显示条数
rowKeyString指定rowKey 默认id 没有id时指定
scrollString设置显示Table高度限制,超出指定高度 滚动展示
superQueryParamsString高级查询条件
使用示例
<template>
  <div style="padding: 0 15px">
    <j-vxe-table
      keep-source
      ref="vTable"
      :loading="loading"
      :columns="columns"
      :dataSource="dataSource"
      :maxHeight="400"
      :disabled="formDisabled"
      :toolbar="true"
      :bordered="true"
      class="model_table"
      :alwaysEdit="true"
      @valueChange="handleValueChange"
    >
      <template v-slot:action="props">
        <a-popconfirm :title="$t('system.confirmDelete') + '?'" @confirm="handleDelete(props)">
          <a>{{$t('system.delete')}}</a>
        </a-popconfirm>
      </template>
      <template v-slot:productCode="props">
        <span v-if="formDisabled" >{{props.value}}</span>
        <a-input-table
          v-else
          v-model="props.value"
          :columns="partColumns"
          :url="partUrl.list"
          :show-label="'partCode'"
          :save-label="'partCode'"
          :super-query-params="superQueryParams"
          @getRecord="getProductCodeRecord($event, props)"
        >
        </a-input-table>

      </template>
    </j-vxe-table>
  </div>
</template>

<script>
import '@/assets/less/TableExpand.less'
import {deleteAction, getAction, httpAction} from '@/api/manage'
import { columns, url, storageQuantityCol } from './config'
import { deepClone } from '@/utils/util'
import AInputTable from '@/components/common/AInputTable'
import { selectPartColumns as partColumns, url as partUrl } from '@/views/psi/psiBasics/psiPart/config/index'
import {ajaxGetDictItems, getDictItemsFromCache} from "@/api/api";

export default {
  name: 'PsiPurchaseDetailList',
  props: {
    //表单禁用
    disabled: {
      type: Boolean,
      default: false,
      required: false
    },
    parentModel: {
      type: Object,
      default: () => {},
      required: false
    }
  },
  components: {
    AInputTable
  },
  data() {
    return {
      description: '表体页面',
      dictOptions: {},
      superFieldList: [],
      disableMixinCreated: true,
      dataSource: [],
      loading: false,
      // 表头
      columns,
      url,
      storageQuantityCol,
      partColumns,
      partUrl,
      currentRow: {},
      superQueryParams: JSON.stringify([{field: 'partStatus', rule: 'in', type: 'string', val: '1'}]),
    }
  },
  watch: {

  },
  computed: {
    formDisabled() {
      return this.disabled
    },
  },
  methods: {
    //获取列表
    getDataList(purchaseId) {
      if(!purchaseId) {
        return
      }

      this.loading = true
      getAction(this.url.list, {purchaseId: purchaseId, pageSize: 50}).then(res => {
        if (res.success) {
          this.dataSource = res.result.records || res.result
        }
      }).finally(() => {
        this.loading = false
      })
    },
    //产品编号选择
    getProductCodeRecord(record, props) {
      this.currentRow = props;
      //物料确认选择
      this.selectConfirmOk(record)
    },
    //物料确认选择
    selectConfirmOk(record) {
      if(this.currentRow.row) {
        this.currentRow.row.productCode = record.partCode
        this.currentRow.row.productName = record.partName
        this.currentRow.row.productId = record.id
        this.currentRow.row.spec = record.partSpec
        this.currentRow.row.uom = record.partUnit
        this.currentRow.row.proDesc = record.partDesc

        this.currentRow.target.setValues([this.currentRow.row])
      }

    },
    //子表单提交验证
    submitValidate() {
      let _this = this
      return new Promise(function(resolve, reject){
        _this.$refs.vTable.validateTable().then(errMap => {
          if (errMap) {
            resolve(false)
          } else {
            const values = _this.$refs.vTable.getTableData()
            if(!(values && values.length > 0)) {
              _this.$message.warning("至少有一条明细!")
              resolve(false)
            } else {
              resolve(true)
            }
          }
        })
      });
    },
    getSubData() {
      return this.$refs.vTable.getTableData()
    },
    //删除方法
    handleDelete(props) {
      if (!this.url.delete) {
        this.$message.error('请设置url.delete属性!')
        return
      }
      props.target.removeRows(props.row)
      let that = this
      deleteAction(that.url.delete, { id: props.rowId }).then(res => {
        if (res.success) {
          //重新计算分页问题
          that.$message.success(res.message)
        } else {
          that.$message.warning(res.message)
        }
      })
    },
    //监听输入值改变
    handleValueChange(e) {
      
    },
  }
}
</script>

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

参考博客链接:https://blog.csdn.net/weixin_44912745/article/details/125407433

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值