Sfoglia il codice sorgente

下拉树所属机构组件开发 70%

林倩 3 anni fa
parent
commit
9a60ce8a52

+ 1 - 0
build/webpack.base.conf.js

@@ -28,6 +28,7 @@ module.exports = {
             "@": resolve("src"),
             "@@": resolve("src/components/base"),
             "@srcPath": resolve('node_modules/ui-jz-v4-common/src'),
+            "@uiSrcPath": resolve('node_modules/ui-component-v4/src'),
         }
     },
     module: {

+ 187 - 0
src/components/select-org-tree/index.vue

@@ -0,0 +1,187 @@
+<!--
+机构下拉树组件
+@Author: linqian
+@Date: 2021-08-02 13:49
+-->
+<template>
+    <!-- :loading="loading" -->
+
+  <el-select
+    ref="reference"
+    class="dg-select"
+    v-model="selectedNodeLabel"
+    v-bind="$attrs"
+    v-on="$listeners"
+    @filter-org="handleFilterOrg"
+    :placeholder="placeholder"
+    :filterable="true"
+    :options="treeData"
+    clearable
+    style="width: 300px"
+  >
+    <template slot="prefix">
+      <slot name="prefix"></slot>
+    </template>
+    <!-- <template slot="empty" v-if="treeData.length == 0 && !loading">
+      <slot name="empty"> 空数据 </slot>
+    </template> -->
+    <dg-scrollbar
+      ref="scrollbar"
+      wrap-class="el-select-dropdown__wrap"
+      view-class="el-select-dropdown__list dg-select-dropdown__list"
+      @scroll-bottom="handleScrollToBottom"
+    >
+      <dg-tree
+        ref="tree"
+        :key="key"
+        radioType="all"
+        :props="props"
+        v-bind="treeProps"
+        :lazy="lazy"
+        :data="treeData"
+        :render-after-expand="false"
+        v-model="treeValue"
+        @node-click="handleNodeClick"
+        @check-change="handleCheckChange"
+      ></dg-tree>
+    </dg-scrollbar>
+  </el-select>
+</template>
+
+<script>
+import ElSelect from './select';
+import * as commonApi from '@/api/common';
+export default {
+  name: 'DgSelect',
+
+  components: {
+    ElSelect
+  },
+
+  props: {
+    nodeKey: {
+      type: String,
+      default: 'code'
+    },
+    apiName: {
+      type: String,
+      default: 'getTree'
+    },
+    placeholder: {
+      type: String,
+      default: '请选择所属单位'
+    }
+  },
+  data() {
+    return {
+      props: {
+        value: this.nodeKey,
+        label: 'name',
+        children: 'children',
+        isLeaf(data) {
+          return data.isParent !== true;
+        }
+      },
+      treeProps: {
+        nodeKey: this.nodeKey,
+        load: this.loadNode
+      },
+      searchForm: {
+        mtType: 'app',
+        name: '',
+        pageNum: 0,
+        pageSize: 50
+      },
+      treeValue: '',
+      data: [],
+      lazy: true,
+      treeData: [],
+      key: 0,
+      selectedNodeLabel: '',
+      loading: false
+    };
+  },
+
+  computed: {
+    hasSlots() {
+      return this.$slots && this.$slots.default && this.$slots.default.length > 0;
+    }
+  },
+  watch: {
+    treeValue(val) {
+      const { data } = this.$refs.tree.getNode(val);
+      this.$refs.reference.blur();
+      this.selectedNodeLabel = data.name;
+      this.$refs.reference.oldValue = data.name;
+    }
+  },
+  methods: {
+    handleNodeClick(data, node, tree) {
+      // debugger
+    },
+    loadNode(node, resolve) {
+      // debugger
+      this.getTree(node.level === 0 ? void 0 : node.data.id, resolve, node.level);
+    },
+    // 组织机构
+    getTree(id, resolve) {
+      const { params, apiName } = this;
+      commonApi[apiName]({ id, ...params })
+        .then((res) => {
+          // 就为了存根节点, 用于刷新
+          return resolve(res || []);
+        })
+        .catch(() => resolve([]));
+    },
+    handleChange(val) {
+      this.$emit('submitTreeValue', val);
+    },
+    handleCheckChange(data, checked, indeterminate) {
+      // debugger;
+    },
+    searchOrgTree() {},
+    handleFilterOrg(val) {
+      this.searchForm.name = $.trim(val);
+      this.searchForm.pageNum = 0;
+      if (this.searchForm.name) {
+        if (this.lazy === true) {
+          this.key++;
+          this.lazy = false;
+        }
+        this.getSearchTree();
+      } else {
+        if (this.lazy === false) {
+          this.key++;
+          this.lazy = true;
+        }
+      }
+    },
+    handleScrollToBottom() {
+      if (!this.lazy && !this.isLast) {
+        this.searchForm.pageNum++;
+        this.getSearchTree();
+      }
+    },
+    getSearchTree() {
+      this.loading = true;
+      commonApi.treeMatch(this.searchForm).then((res) => {
+        let { content, totalElements } = res.data;
+        const _content = content.map((item) => {
+          return {
+            ...item,
+            isParent: true,
+            name: item.fullName
+          };
+        });
+        if (this.searchForm.pageNum == 0) {
+          this.treeData = [];
+        }
+        this.treeData = [...this.treeData, ..._content];
+        this.isLast = this.treeData.length == totalElements;
+        this.loading = false;
+      });
+    },
+    handleFilter(val, data) {}
+  }
+};
+</script>

+ 391 - 0
src/components/select-org-tree/select.vue

@@ -0,0 +1,391 @@
+<template>
+  <div
+    class="el-select"
+    :class="[selectSize ? 'el-select--' + selectSize : '']"
+    @click.stop="toggleMenu"
+    v-clickoutside="handleClose"
+  >
+    <!-- update author: tangdm descript: add overflow: 'hidden' -->
+    <div
+      class="el-select__tags"
+      v-if="multiple"
+      ref="tags"
+      :style="{ 'max-width': inputWidth - 32 + 'px', width: '100%', overflow: 'hidden' }"
+    >
+      <span v-if="collapseTags && selected.length">
+        <el-tag
+          :closable="!selectDisabled"
+          :size="collapseTagSize"
+          :hit="selected[0].hitState"
+          @close="deleteTag($event, selected[0])"
+          disable-transitions
+        >
+          <span class="el-select__tags-text" :title="selected[0].currentLabel">{{ selected[0].currentLabel }}</span>
+        </el-tag>
+        <el-tag v-if="selected.length > 1" :closable="false" :size="collapseTagSize" disable-transitions>
+          <span class="el-select__tags-text">+ {{ selected.length - 1 }}</span>
+        </el-tag>
+      </span>
+      <!-- update author: tangdm descript: add dragTags -->
+      <transition-group @after-leave="resetInputHeight" v-if="!collapseTags && !dragTags">
+        <el-tag
+          v-for="item in selected"
+          :key="getValueKey(item)"
+          :closable="!selectDisabled"
+          :size="collapseTagSize"
+          :hit="item.hitState"
+          @close="deleteTag($event, item)"
+          disable-transitions
+        >
+          <span class="el-select__tags-text" :title="item.currentLabel">{{ item.currentLabel }}</span>
+        </el-tag>
+      </transition-group>
+      <!-- add author: tangdm descript: drag-tags content -->
+      <transition @after-leave="resetInputHeight" v-if="!collapseTags && dragTags">
+        <div class="dg-drag-tags__content" ref="tagContent" :style="[{ width: tagItemLength + 'px' }]">
+          <el-tag
+            v-for="item in selected"
+            :key="getValueKey(item)"
+            :closable="!selectDisabled"
+            :size="collapseTagSize"
+            :hit="item.hitState"
+            ref="tagItem"
+            @close="deleteTag($event, item)"
+            disable-transitions
+          >
+            <span class="el-select__tags-text" :title="item.currentLabel">{{ item.currentLabel }}</span>
+          </el-tag>
+        </div>
+      </transition>
+      <input
+        type="text"
+        class="el-select__input"
+        :class="[selectSize ? `is-${selectSize}` : '']"
+        :disabled="selectDisabled"
+        :autocomplete="autoComplete || autocomplete"
+        @focus="handleFocus"
+        @blur="softFocus = false"
+        @click.stop
+        @keyup="managePlaceholder"
+        @keydown="resetInputState"
+        @keydown.down.prevent="navigateOptions('next')"
+        @keydown.up.prevent="navigateOptions('prev')"
+        @keydown.enter.prevent="selectOption"
+        @keydown.esc.stop.prevent="visible = false"
+        @keydown.delete="deletePrevTag"
+        @compositionstart="handleComposition"
+        @compositionupdate="handleComposition"
+        @compositionend="handleComposition"
+        v-model="query"
+        @input="debouncedQueryChange"
+        v-if="filterable"
+        :style="{
+          'flex-grow': '1',
+          width: inputLength / (inputWidth - 32) + '%',
+          'max-width': inputWidth - 42 + 'px'
+        }"
+        ref="input"
+      />
+    </div>
+    <el-input
+      ref="reference"
+      v-model="selectedLabel"
+      type="text"
+      :placeholder="currentPlaceholder"
+      :name="name"
+      :id="id"
+      :autocomplete="autoComplete || autocomplete"
+      :size="selectSize"
+      :disabled="selectDisabled"
+      :readonly="readonly"
+      :validate-event="false"
+      :class="{ 'is-focus': visible }"
+      @focus="handleFocus"
+      @blur="handleBlur"
+      @keyup.native="debouncedOnInputChange"
+      @keydown.native.down.stop.prevent="navigateOptions('next')"
+      @keydown.native.up.stop.prevent="navigateOptions('prev')"
+      @keydown.native.enter.prevent="selectOption"
+      @keydown.native.esc.stop.prevent="visible = false"
+      @keydown.native.tab="visible = false"
+      @paste.native="debouncedOnInputChange"
+      @mouseenter.native="inputHovering = true"
+      @mouseleave.native="inputHovering = false"
+    >
+      <template slot="prefix" v-if="$slots.prefix">
+        <slot name="prefix"></slot>
+      </template>
+      <template slot="suffix">
+        <i v-show="!showClose" :class="['el-select__caret', 'el-input__icon', 'el-icon-' + iconClass]"></i>
+        <i v-if="showClose" class="el-select__caret el-input__icon el-icon-circle-close" @click="handleClearClick"></i>
+      </template>
+    </el-input>
+    <transition name="el-zoom-in-top" @before-enter="handleMenuEnter" @after-leave="doDestroy">
+      <el-select-menu ref="popper" :append-to-body="popperAppendToBody" v-show="visible && emptyText !== false">
+          <!-- <template v-if="emptyText && (!allowCreate || loading || (allowCreate && options.length === 0 ))">
+          <slot name="empty" v-if="$slots.empty"></slot>
+          <p class="el-select-dropdown__empty" v-else>
+            {{ emptyText }}
+          </p>
+        </template> -->
+        <slot></slot>
+      </el-select-menu>
+    </transition>
+  </div>
+</template>
+
+<script type="text/babel">
+import { Select as ElSelect } from 'element-ui';
+import debounce from 'throttle-debounce/debounce';
+import { addResizeListener } from '@uiSrcPath/utils/resize-event.js';
+import { complex } from '@uiSrcPath/dg-utils/shear.js';
+// import { valueEquals } from '@uiSrcPath/utils/util.js';
+import _ from 'lodash';
+
+ElSelect.components.ElSelectMenu.watch['$parent.inputWidth'] = function () {
+  this.minWidth = Math.max(this.$parent.$el.getBoundingClientRect().width, this.$parent.$el.clientWidth) + 'px';
+};
+
+const Select = complex(ElSelect, ['created', 'mounted']);
+
+export default {
+  mixins: [Select],
+
+  props: {
+    // add author: tangdm
+    dragTags: Boolean,
+    defaultProp: Object
+  },
+
+  data() {
+    return {
+      dragStatus: false,
+      tagItemLength: 0,
+      debounceTime: 300,
+      oldValue: ''
+    };
+  },
+
+  computed: {
+    scrollbarCls() {
+      const { allowCreate, query, filteredOptionsCount, scrollbarClass } = this;
+      return [!allowCreate && query && filteredOptionsCount === 0 ? 'is-empty' : '', scrollbarClass || ''].join(' ');
+    },
+    // 覆盖element ui - select中方法
+    debounce() {
+      return this.debounceTime;
+    }
+  },
+  watch: {
+    selectDisabled() {
+      // create author:lutz describe:!multiple不渲染
+      if (!this.multiple) {
+        return;
+      }
+      this.$nextTick(() => {
+        this.resetInputHeight();
+      });
+    },
+
+    // add author: tangdm
+    selected() {
+      this.$nextTick(() => {
+        // 检测判断标签值是否存在
+        if (this.$refs.tagContent) {
+          this.$refs.tagContent.style.transform = 'translateX(0px)';
+        }
+
+        // 计算标签数量值的长度
+        let count = 0;
+        let tagItem = this.$refs.tagItem;
+        if (tagItem) {
+          tagItem.forEach((item) => {
+            count += item.$el.offsetWidth + 8;
+          });
+        }
+        this.tagItemLength = count;
+      });
+    }
+  },
+
+  methods: {
+    valueEquals(a, b) {
+      // see: https://stackoverflow.com/questions/3115982/how-to-check-if-two-arrays-are-equal-with-javascript
+      if (a === b) return true;
+      if (!(a instanceof Array)) return false;
+      if (!(b instanceof Array)) return false;
+      if (a.length !== b.length) return false;
+      for (let i = 0; i !== a.length; ++i) {
+        if (a[i] !== b[i]) return false;
+      }
+      return true;
+    },
+    resetInputHeight() {
+      if (this.collapseTags && !this.filterable) return;
+      this.$nextTick(() => {
+        if (!this.$refs.reference) return;
+        let inputChildNodes = this.$refs.reference.$el.childNodes;
+        let input = [].filter.call(inputChildNodes, (item) => item.tagName === 'INPUT')[0];
+        const tags = this.$refs.tags;
+        const sizeInMap = this.initialInputHeight || 40;
+
+        // update author: tangdm descript: 不同分辨率兼容高度问题
+        // [code] input.style.height = this.selected.length === 0
+        //        ? sizeInMap + 'px'
+        //        : Math.max(
+        //        tags ? (tags.clientHeight + (tags.clientHeight > sizeInMap ? 6 : 0)) : 0,
+        //        sizeInMap
+        // ) + 'px';
+
+        input.style.height =
+          this.selected.length === 0 ? sizeInMap + 'px' : Math.max(tags ? tags.clientHeight : 0, sizeInMap) + 'px';
+        if (this.visible && this.emptyText !== false) {
+          this.broadcast('ElSelectDropdown', 'updatePopper');
+        }
+      });
+    },
+
+    toggleMenu() {
+      // update author: tangdm descript: 拖拽状态判断
+      if (this.dragStatus) {
+        this.dragStatus = false;
+        return false;
+      }
+
+      if (!this.selectDisabled) {
+        if (this.menuVisibleOnFocus) {
+          this.menuVisibleOnFocus = false;
+        } else {
+          this.visible = !this.visible;
+        }
+        if (this.visible) {
+          (this.$refs.input || this.$refs.reference).focus();
+        }
+      }
+    },
+
+    // @update @author:lutz @descript:添加 change 事件参数
+    emitChange(val) {
+      const { getLabel } = this;
+      if (!this.valueEquals(this.value, val)) {
+        this.$emit('change', val, getLabel(val));
+      }
+    },
+
+    // @add @author:lutz @descript:获取过滤值
+    getLabel(val) {
+      const { data } = this.$attrs;
+      const { value } = this.defaultProp;
+      return _.isArray(val)
+        ? _.intersectionBy(
+            data,
+            val.map((item) => ({ [value]: item })),
+            value
+          )
+        : _.find(data, (item) => val === item[value]);
+    },
+
+    resetInputWidth() {
+      // update author: tangdm [code] this.inputWidth = this.$refs.reference.$el.getBoundingClientRect().width;
+      // descript: element count item's width for error in layers.
+      this.inputWidth = Math.max(
+        this.$refs.reference.$el.getBoundingClientRect().width,
+        this.$refs.reference.$el.clientWidth
+      );
+    },
+
+    //  下拉触发
+    handleScrollToBottom() {
+      this.$emit('scroll-bottom');
+    },
+    handleQueryChange(val) {
+      if (this.oldValue === val || this.isOnComposition) return;
+      this.oldValue = val;
+      this.$nextTick(() => {
+        if (this.visible) this.broadcast('ElSelectDropdown', 'updatePopper');
+      });
+      this.hoverIndex = -1;
+      if (this.multiple && this.filterable) {
+        this.$nextTick(() => {
+          const length = this.$refs.input.value.length * 15 + 20;
+          this.inputLength = this.collapseTags ? Math.min(50, length) : length;
+          this.managePlaceholder();
+          this.resetInputHeight();
+        });
+      }
+      if (this.remote && typeof this.remoteMethod === 'function') {
+        this.hoverIndex = -1;
+        this.remoteMethod(val);
+      } else if (typeof this.filterMethod === 'function') {
+        this.filterMethod(val);
+        this.broadcast('ElOptionGroup', 'queryChange');
+      } else {
+        this.filteredOptionsCount = this.optionsCount;
+        this.broadcast('ElOption', 'queryChange', val);
+        this.broadcast('ElOptionGroup', 'queryChange');
+      }
+      if (this.defaultFirstOption && (this.filterable || this.remote) && this.filteredOptionsCount) {
+        this.checkDefaultFirstOption();
+      }
+      this.$emit('filter-org', val);
+    }
+  },
+
+  created() {
+    this.cachedPlaceHolder = this.currentPlaceholder = this.placeholder;
+    if (this.multiple && !Array.isArray(this.value)) {
+      this.$emit('input', []);
+    }
+    if (!this.multiple && Array.isArray(this.value)) {
+      this.$emit('input', '');
+    }
+
+    this.debouncedOnInputChange = debounce(this.debounce, () => {
+      this.onInputChange();
+    });
+
+    this.debouncedQueryChange = debounce(this.debounce, (e) => {
+      this.handleQueryChange(e.target.value);
+    });
+
+    this.$on('handleOptionClick', this.handleOptionSelect);
+    this.$on('setSelected', this.setSelected);
+  },
+
+  mounted() {
+    if (this.multiple && Array.isArray(this.value) && this.value.length > 0) {
+      this.currentPlaceholder = '';
+    }
+    addResizeListener(this.$el, this.handleResize);
+
+    const reference = this.$refs.reference;
+    if (reference && reference.$el) {
+      const sizeMap = {
+        medium: 36,
+        small: 32,
+        mini: 28
+      };
+      const input = reference.$el.querySelector('input');
+
+      // add author: tangdm descript: 增加分辨率识别
+      const clientBodyWidth = document.body.clientWidth;
+      const autoFixedHeight = clientBodyWidth < 1366 ? 24 : clientBodyWidth < 1681 ? 28 : 32;
+
+      // update author: tangdm [code] this.initialInputHeight = input.getBoundingClientRect().height || sizeMap[this.selectSize];
+      this.initialInputHeight = sizeMap[this.selectSize] || autoFixedHeight;
+    }
+    if (this.remote && this.multiple) {
+      this.resetInputHeight();
+    }
+    this.$nextTick(() => {
+      if (reference && reference.$el) {
+        // update author: tangdm [code] this.inputWidth = reference.$el.getBoundingClientRect().width;
+        this.inputWidth = Math.max(reference.$el.getBoundingClientRect().width, reference.$el.clientWidth);
+      }
+    });
+    this.setSelected();
+  }
+};
+</script>
+
+

+ 4 - 1
src/pages/list-manage/white-list-manage/component/batch-add-user.vue

@@ -28,7 +28,8 @@
         </el-form-item>
       </el-form>
       <!-- 表格 -->
-      <new-table ref="table" :selection="true" :tableUrl="tableUrl" :tableHeader="tableHeader" :condition="form"> </new-table>
+      <new-table ref="table" :selection="true" :tableUrl="tableUrl" :tableHeader="tableHeader" :condition="form">
+      </new-table>
     </dg-row>
     <div v-footer>
       <dg-button @click="handleCancel">取消</dg-button>
@@ -42,11 +43,13 @@ import { notInWhiteUserTableUrl } from '@/api/list-manage';
 import newTable from '@/pages/common/new-table';
 import batchSetlevelUser from './batch-setlevel-user.vue';
 import selectTree from '@/pages/common/select-tree';
+import selectOrgTree from '@/components/select-org-tree';
 
 export default {
   components: {
     newTable,
     selectTree
+    
   },
   data() {
     return {

+ 7 - 2
src/pages/list-manage/white-list-manage/component/user-list.vue

@@ -20,7 +20,9 @@
       <el-form-item label="所属单位">
         <select-tree ref="selectTree" api-name="getOrgTree" nodeKey="id" @submitTreeValue="getTreeValue"></select-tree>
       </el-form-item>
-
+        <el-form-item label="所属单位">
+          <select-org-tree api-name="getOrgTree" nodeKey="id"></select-org-tree>
+        </el-form-item>
       <el-form-item>
         <dg-button type="primary" @click="handleSearch" icon="el-icon-search">查询</dg-button>
         <dg-button type="primary" @click="handleReset" icon="el-icon-refresh-right">重置</dg-button>
@@ -61,10 +63,13 @@ import newTable from '@/pages/common/new-table';
 import batchAddUser from './batch-add-user.vue';
 import batchSetlevelUser from './batch-setlevel-user.vue';
 import batchDeleteUser from './batch-delete-user.vue';
+import selectOrgTree from '@/components/select-org-tree';
+
 export default {
   components: {
     selectTree,
-    newTable
+    newTable,
+    selectOrgTree
   },
   data() {
     return {