Explorar o código

feat: 优化人员选择弹框,支持已选人员回显与多选合并

xieyong hai 6 días
pai
achega
640f1dcfbb

+ 5 - 5
src/components/user-select/user-select-search.vue

@@ -8,7 +8,7 @@
   >
     <el-row :gutter="24">
       <el-col v-bind="styleResponsive ? { md: 6 } : { span: 6 }">
-      <el-form-item>
+        <el-form-item>
           <el-input
             clearable
             size="small"
@@ -16,11 +16,9 @@
             placeholder="请输入工号"
           />
         </el-form-item>
-        </el-col>
+      </el-col>
 
       <el-col v-bind="styleResponsive ? { md: 6 } : { span: 6 }">
-
-
         <el-form-item>
           <el-input
             clearable
@@ -40,7 +38,7 @@
           />
         </el-form-item>
       </el-col>
-      <el-col v-bind="styleResponsive ?  { md: 6 } : { span: 6 }">
+      <el-col v-bind="styleResponsive ? { md: 6 } : { span: 6 }">
         <el-form-item>
           <el-button
             size="small"
@@ -69,6 +67,8 @@
         name: ''
       };
       return {
+        // 默认表单数据
+        defaultWhere,
         // 表单数据
         where: { ...defaultWhere }
       };

+ 170 - 11
src/components/user-select/user-select.vue

@@ -24,16 +24,20 @@
         </div>
         <!-- 表格 -->
         <template v-slot:content>
-          <user-select-search @search="reload"></user-select-search>
+          <user-select-search
+            ref="search"
+            @search="reload"
+          ></user-select-search>
           <ele-pro-table
             ref="table"
             :columns="currentColumns"
             :datasource="datasource"
             row-key="id"
-            height="calc(100vh - 430px)"
+            :height="tableHeight"
             class="dict-table"
             @cell-click="cellClick"
             @selection-change="handleSelectionChange"
+            @done="handleTableDone"
           >
             <!-- 表头工具栏 -->
             <template v-slot:action="{ row }" v-if="!isMultiple">
@@ -47,6 +51,9 @@
     </el-card>
 
     <div slot="footer" class="dialog-footer">
+      <span v-if="isMultiple" class="selected-count"
+        >已选 {{ selectedCount }} 人</span
+      >
       <el-button type="primary" size="small" @click="selected">选择</el-button>
       <el-button size="small" @click="handleClose">关闭</el-button>
     </div>
@@ -67,6 +74,13 @@
         default: () => {
           return [];
         }
+      },
+      // 已选人员(打开时回显,确定后与本次勾选合并返回)
+      selectedList: {
+        type: Array,
+        default: () => {
+          return [];
+        }
       }
     },
     data() {
@@ -76,6 +90,12 @@
         visible: false,
         isMultiple: false,
         selectedRows: [],
+        // 本次打开时已选人员(在表格中选择框中勾选回显)
+        preselected: [],
+        // 本次打开后被取消勾选的已选人员 id
+        uncheckedIds: [],
+        // 当前选中的部门节点
+        curNodeData: null,
         current: null,
         statusOptions: [
           { value: 1, label: '全职' },
@@ -152,11 +172,21 @@
     },
 
     computed: {
+      // 表格高度
+      tableHeight() {
+        return 'calc(100vh - 430px)';
+      },
+      // 已选人数
+      selectedCount() {
+        return this.mergeSelected().length;
+      },
       currentColumns() {
         if (this.isMultiple) {
           return [
             {
               type: 'selection',
+              // 跨页/重新加载后保留勾选状态,配合 row-key 使用
+              reserveSelection: true,
               width: 55,
               align: 'center',
               selectable: (row, index) => {
@@ -177,8 +207,19 @@
       open(item, index = 0, multiple = false) {
         this.isMultiple = multiple;
         this.selectedRows = [];
+        this.uncheckedIds = [];
+        this.current = null;
+        this.radio = null;
+        // 已选人员回显(打开时在表格的选择框中勾选)
+        this.preselected = [...(this.selectedList || [])];
         if (item) {
           this.radio = item.id;
+          // 单选时保持原有选中,未重新选择则沿用
+          this.current = item;
+        } else if (!multiple && this.preselected.length) {
+          const first = this.preselected[0];
+          this.radio = first.id;
+          this.current = first;
         }
         this.currentIndex = index;
         // 先亮起弹窗再排 nextTick:首次打开时对话框内容尚未挂载,
@@ -187,22 +228,120 @@
         this.$nextTick(() => {
           if (this.$refs.table) {
             this.$refs.table.clearSelection();
+            // 已选人员勾选到表格的选择框中
+            this.syncPreselectedSelection();
           }
+          // 每次打开重置搜索条件与部门筛选
+          this.resetFilter();
         });
       },
 
+      /* 重置搜索条件与部门树选中 */
+      resetFilter() {
+        this.curNodeData = null;
+        const tree = this.$refs.treeList && this.$refs.treeList.getInstance();
+        if (tree) {
+          tree.setCurrentKey(null);
+        }
+        // 每页条数回初始值
+        const table = this.$refs.table;
+        if (table) {
+          const pageSize = table.pageSize || 10;
+          if (table.tableLimit !== pageSize) {
+            table.tableLimit = pageSize;
+          }
+        }
+        if (this.$refs.search) {
+          // 清空搜索条件并重新加载表格
+          this.$refs.search.reset();
+        } else {
+          this.reload();
+        }
+      },
+
+      /* 表格数据加载完成:保证已选人员仍处于勾选状态 */
+      handleTableDone() {
+        this.syncPreselectedSelection();
+      },
+
+      /* 将已选人员同步为表格中的勾选状态 */
+      syncPreselectedSelection() {
+        const table = this.$refs.table;
+        if (!table || !this.isMultiple) {
+          return;
+        }
+        const dataMap = new Map();
+        this.getTableData().forEach((row) => dataMap.set(row.id, row));
+        const selectedIds = new Set(
+          (this.selectedRows || []).map((row) => row.id)
+        );
+        this.preselected.forEach((item) => {
+          if (
+            !item ||
+            item.id == null ||
+            selectedIds.has(item.id) ||
+            // 本次打开后被取消勾选的,不再重新勾选
+            this.uncheckedIds.includes(item.id)
+          ) {
+            return;
+          }
+          // 优先使用表格中的行对象,保证复选框能正常回显
+          const row = dataMap.get(item.id) || item;
+          table.toggleRowSelection(row, true);
+          selectedIds.add(item.id);
+        });
+      },
+
+      /* 表格当前数据 */
+      getTableData() {
+        const table = this.$refs.table;
+        if (table && typeof table.getData === 'function') {
+          return table.getData() || [];
+        }
+        return [];
+      },
+
+      /* 合并已选人员与本次勾选的人员 */
+      mergeSelected() {
+        const map = new Map();
+        const dataMap = new Map(
+          this.getTableData().map((row) => [row.id, row])
+        );
+        // 已选人员(未被取消勾选的)
+        this.preselected.forEach((item) => {
+          if (!item || item.id == null || this.uncheckedIds.includes(item.id)) {
+            return;
+          }
+          map.set(item.id, dataMap.get(item.id) || item);
+        });
+        // 本次勾选的人员
+        (this.selectedRows || []).forEach((row) => {
+          if (!row || row.id == null) {
+            return;
+          }
+          map.set(row.id, dataMap.get(row.id) || row);
+        });
+        return Array.from(map.values());
+      },
+
       /* 表格数据源 */
-      datasource({ page, limit, where, order }) {
-        return getUserPage({
+      datasource({ page, limit, where }) {
+        const params = {
           pageNum: page,
-          size: limit,
-          ...where
+          size: limit
+        };
+        Object.keys(where || {}).forEach((key) => {
+          const value = where[key];
+          if (value !== undefined && value !== null && value !== '') {
+            params[key] = value;
+          }
         });
+        return getUserPage(params);
       },
 
       /* 刷新表格 */
       reload(where) {
-        this.$refs.table.reload({ pageNum: 1, where: where });
+        this.$refs.table.reload({ page: 1, where: where });
       },
 
       handleNodeClick(data, node) {
@@ -218,22 +357,36 @@
       },
 
       handleSelectionChange(rows) {
-        this.selectedRows = rows;
+        const list = rows || [];
+        const prevIds = new Set((this.selectedRows || []).map((row) => row.id));
+        const curIds = new Set(list.map((row) => row.id));
+        // 被取消勾选的人员
+        prevIds.forEach((id) => {
+          if (!curIds.has(id) && !this.uncheckedIds.includes(id)) {
+            this.uncheckedIds.push(id);
+          }
+        });
+        // 被重新勾选的人员
+        this.uncheckedIds = this.uncheckedIds.filter((id) => !curIds.has(id));
+        this.selectedRows = list;
       },
 
       handleClose() {
         this.visible = false;
         this.current = null;
-        this.radio = '';
+        this.radio = null;
         this.selectedRows = [];
+        this.uncheckedIds = [];
+        this.preselected = [];
       },
 
       selected() {
         if (this.isMultiple) {
-          if (!this.selectedRows.length) {
+          const rows = this.mergeSelected();
+          if (!rows.length) {
             return this.$message.warning('请至少选择一项');
           }
-          this.$emit('changeParent', this.selectedRows, this.currentIndex);
+          this.$emit('changeParent', rows, this.currentIndex);
         } else {
           if (!this.current) {
             return this.$message.warning('请至少选择一项');
@@ -247,6 +400,12 @@
 </script>
 
 <style lang="scss" scoped>
+  .selected-count {
+    float: left;
+    line-height: 32px;
+    color: #606266;
+    font-size: 13px;
+  }
   .tree_col {
     border: 1px solid #eee;
     padding: 10px 0;