ソースを参照

组件优化:重构用户选择组件,实现部门树形选择功能

刘博博 1 週間 前
コミット
1639713c99

+ 658 - 0
src/components/UserSelect/index.vue

@@ -0,0 +1,658 @@
+<template>
+    <el-dialog :title="title" :visible.sync="dialogVisible" :width="width" :top="top" append-to-body
+        @close="handleClose">
+        <div class="user-select-container">
+            <div class="department-panel">
+                <div class="panel-header">
+                    <h4>请选择部门</h4>
+                </div>
+                <el-tree ref="tree" v-loading="deptLoading" :data="departmentData" :props="defaultProps" node-key="id"
+                    @node-click="handleNodeClick" :highlight-current="true"
+                    :default-expand-all="defaultExpandAll"
+                    class="department-tree"></el-tree>
+            </div>
+
+            <div class="user-panel">
+                <div class="selected-users" v-if="selectedUsers && selectedUsers.length > 0">
+                    <div class="selected-header">
+                        <span>已选中人员 ({{ selectedUsers.length }})</span>
+                    </div>
+                    <div class="selected-list">
+                        <el-tag v-for="(user, index) in selectedUsers" :key="user.userId" closable
+                            @close="removeSelectedUser(user, index)" class="selected-tag">
+                            {{ user.nickName || user.userName }}
+                        </el-tag>
+                    </div>
+                </div>
+
+                <!-- 暂时注释掉搜索功能 -->
+                <!-- <div class="search-form">
+                    <el-form :model="queryParams" ref="queryForm" size="small" :inline="true">
+                        <el-form-item label="用户名称" prop="userName">
+                            <el-input v-model="searchText" placeholder="请输入用户名称" clearable
+                                @keyup.enter.native="handleSearch" />
+                        </el-form-item>
+                        <el-form-item>
+                            <el-button type="primary" icon="el-icon-search" size="mini"
+                                @click="handleSearch">搜索</el-button>
+                            <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+                        </el-form-item>
+                    </el-form>
+                </div> -->
+
+                <!-- 暂时注释掉搜索结果展示 -->
+                <!-- <div class="search-results" v-if="searchText && filteredUsers.length > 0">
+                    <div class="result-header">
+                        <span>搜索结果 ({{ filteredUsers.length }})</span>
+                    </div>
+                    <el-table v-loading="userLoading" @row-click="clickRow" ref="table" :data="paginatedSearchResults"
+                        @selection-change="handleSelectionChange" style="flex: 1">
+                        <el-table-column type="selection" width="55"></el-table-column>
+                        <el-table-column label="用户名称" prop="userName" :show-overflow-tooltip="true" />
+                        <el-table-column label="用户昵称" prop="nickName" :show-overflow-tooltip="true" />
+                        <el-table-column label="邮箱" prop="email" :show-overflow-tooltip="true" />
+                        <el-table-column label="手机" prop="phonenumber" :show-overflow-tooltip="true" />
+                        <el-table-column label="状态" align="center" prop="status">
+                            <template slot-scope="scope">
+                                <dict-tag :options="dict.type.sys_normal_disable" :value="scope.row.status" />
+                            </template>
+                        </el-table-column>
+                    </el-table>
+                    <div class="pagination-container">
+                        <pagination v-show="searchTotal > 0" :total="searchTotal" :page.sync="queryParams.pageNum"
+                            :limit.sync="queryParams.pageSize" @pagination="() => { }" />
+                    </div>
+                </div>
+
+                <div class="empty-state" v-else-if="searchText">
+                    <i class="el-icon-warning"></i>
+                    <p>未找到匹配的用户</p>
+                </div> -->
+
+                <div class="user-table" v-if="currentDepartment">
+                    <el-table v-loading="userLoading" @row-click="clickRow" ref="table" :data="userList"
+                        @selection-change="handleSelectionChange" style="flex: 1">
+                        <el-table-column type="selection" width="55"></el-table-column>
+                        <el-table-column label="用户名称" prop="userName" :show-overflow-tooltip="true" />
+                        <el-table-column label="用户昵称" prop="nickName" :show-overflow-tooltip="true" />
+                        <el-table-column label="邮箱" prop="email" :show-overflow-tooltip="true" />
+                        <el-table-column label="手机" prop="phonenumber" :show-overflow-tooltip="true" />
+                        <el-table-column label="状态" align="center" prop="status">
+                            <template slot-scope="scope">
+                                <dict-tag :options="dict.type.sys_normal_disable" :value="scope.row.status" />
+                            </template>
+                        </el-table-column>
+                    </el-table>
+                    <div class="pagination-container">
+                        <pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNum"
+                            :limit.sync="queryParams.pageSize" @pagination="getUsers" />
+                    </div>
+                </div>
+
+                <div class="empty-state" v-if="!currentDepartment">
+                    <i class="el-icon-warning"></i>
+                    <p>请先选择一个部门</p>
+                </div>
+            </div>
+        </div>
+
+        <div slot="footer" class="dialog-footer">
+            <el-button type="primary" @click="handleConfirm">确 定</el-button>
+            <el-button @click="handleCancel">取 消</el-button>
+        </div>
+    </el-dialog>
+</template>
+
+<script>
+import { listUser, deptTreeSelect } from "@/api/system/user";
+export default {
+    name: "DepartmentUserSelect",
+    dicts: ['sys_normal_disable'],
+    props: {
+        visible: {
+            type: Boolean,
+            default: false
+        },
+        title: {
+            type: String,
+            default: "分配用户"
+        },
+        width: {
+            type: String,
+            default: "900px"
+        },
+        top: {
+            type: String,
+            default: "5vh"
+        },
+        defaultExpandAll: {
+            type: Boolean,
+            default: true
+        },
+        departments: {
+            type: Array,
+            default: () => []
+        },
+        extraParams: {
+            type: Object,
+            default: () => ({})
+        }
+    },
+    data() {
+        return {
+            dialogVisible: false,
+            selectedUserIds: [],
+            selectedUsers: [],
+            total: 0,
+            userList: [],
+            currentDepartment: null,
+            defaultProps: {
+                children: 'children',
+                label: 'label'
+            },
+            queryParams: {
+                pageNum: 1,
+                pageSize: 10,
+                userName: undefined,
+                deptId: undefined
+            },
+            // 加载状态标识
+            deptLoading: false,
+            userLoading: false,
+            // 内部部门数据存储
+            internalDepartments: [],
+            // 搜索关键词(暂时不用)
+            // searchText: '',
+            // 搜索结果总数(暂时不用)
+            // searchTotal: 0
+        };
+    },
+    computed: {
+        // 部门数据
+        departmentData() {
+            return this.departments.length > 0 ? this.departments : this.internalDepartments;
+        },
+        // 以下计算属性暂时注释(搜索相关)
+        // filteredUsers() {
+        //     if (!this.searchText) return [];
+
+        //     const results = [];
+        //     this.findUsersInDept(this.departmentData, this.searchText, results);
+        //     return results;
+        // },
+        // keysToExpand() {
+        //     if (!this.searchText) return [];
+
+        //     const keys = [];
+        //     this.findDeptKeysToExpand(this.departmentData, this.searchText, keys);
+        //     return keys;
+        // },
+        // paginatedSearchResults() {
+        //     if (!this.filteredUsers || this.filteredUsers.length === 0) return [];
+
+        //     // 计算分页范围
+        //     const start = (this.queryParams.pageNum - 1) * this.queryParams.pageSize;
+        //     const end = start + this.queryParams.pageSize;
+        //     this.searchTotal = this.filteredUsers.length;
+        //     return this.filteredUsers.slice(start, end);
+        // }
+    },
+    watch: {
+        visible(val) {
+            this.dialogVisible = val;
+            if (val) {
+                if (this.departmentData.length === 0) {
+                    // 首次打开时加载部门数据
+                    this.loadDepartmentData();
+                }
+            }
+        },
+        // 监听内部状态变化,同步到外部
+        dialogVisible(val) {
+            this.$emit('update:visible', val);
+        },
+        // 暂时注释掉搜索相关的监听
+        // searchText(val) {
+        //     this.$nextTick(() => {
+        //         if (this.$refs.tree) {
+        //             this.$refs.tree.store.getNodeMap().forEach(node => {
+        //                 if (node.visible) {
+        //                     this.$refs.tree.store.setCurrentNode(node);
+        //                 }
+        //             });
+        //         }
+
+        //         // 恢复表格选中状态
+        //         if (this.$refs.table) {
+        //             this.restoreSelection();
+        //         }
+        //     });
+        // }
+    },
+    methods: {
+        // 使用真实API加载部门数据
+        async loadDepartmentData() {
+            this.deptLoading = true;
+            try {
+                const response = await deptTreeSelect();
+                if (response.code === 200) {
+                    this.internalDepartments = response.data || [];
+                } else {
+                    this.$modal.msgError("加载部门数据失败: " + response.msg);
+                }
+            } catch (error) {
+                this.$modal.msgError("加载部门数据失败");
+                console.error("加载部门数据失败:", error);
+            } finally {
+                this.deptLoading = false;
+            }
+        },
+        // 只有点击叶子节点时才加载用户
+        handleNodeClick(data) {
+            this.currentDepartment = data;
+            this.queryParams.deptId = data.id;
+            this.queryParams.pageNum = 1;
+            this.queryParams.userName = undefined;
+            this.getUsers();
+
+            // 加载用户后恢复选中状态
+            this.$nextTick(() => {
+                this.restoreSelection();
+            });
+        },
+        // 处理行点击事件,切换选中状态
+        clickRow(row) {
+            // 获取当前表格选中状态
+            const selection = this.$refs.table.selection || [];
+            const isSelected = selection.some(item => item.userId === row.userId);
+
+            // 切换选中状态
+            if (isSelected) {
+                // 如果已选中,从选择中移除
+                this.$refs.table.toggleRowSelection(row, false);
+                const newSelection = selection.filter(item => item.userId !== row.userId);
+                this.handleSelectionChange(newSelection);
+            } else {
+                // 如果未选中,添加到选择中
+                this.$refs.table.toggleRowSelection(row, true);
+                const newSelection = [...selection, row];
+                this.handleSelectionChange(newSelection);
+            }
+        },
+
+        handleSelectionChange(selection) {
+            // 获取当前表格中选中的用户ID
+            const currentTableSelectedIds = selection.map(item => item.userId);
+
+            // 获取当前表格中所有用户的ID
+            const currentTableUserIds = this.userList.map(user => user.userId);
+
+            // 保留不在当前表格中的已选用户(来自其他部门的选择)
+            const otherSelectedUsers = this.selectedUsers.filter(user =>
+                !currentTableUserIds.includes(user.userId)
+            );
+            const otherSelectedUserIds = this.selectedUserIds.filter(id =>
+                !currentTableUserIds.includes(id)
+            );
+
+            // 合并结果:其他部门的已选用户 + 当前表格选中的用户
+            this.selectedUsers = [...otherSelectedUsers, ...selection];
+            this.selectedUserIds = [...otherSelectedUserIds, ...currentTableSelectedIds];
+        },
+        // 使用真实API获取部门用户数据:/system/user/list?pageNum=1&pageSize=10&deptId=xxx
+        async getUsers() {
+            if (!this.queryParams.deptId) return;
+
+            this.userLoading = true;
+            try {
+                const response = await listUser({
+                    pageNum: this.queryParams.pageNum,
+                    pageSize: this.queryParams.pageSize,
+                    deptId: this.queryParams.deptId
+                });
+
+                if (response.code === 200) {
+                    this.userList = response.rows || [];
+                    this.total = response.total || 0;
+                } else {
+                    this.$modal.msgError("获取用户数据失败: " + response.msg);
+                    this.userList = [];
+                    this.total = 0;
+                }
+            } catch (error) {
+                this.$modal.msgError("获取用户数据失败");
+                console.error("获取用户数据失败:", error);
+                this.userList = [];
+                this.total = 0;
+            } finally {
+                this.userLoading = false;
+            }
+        },
+        // 暂时注释掉搜索相关的方法
+        // async getList() {
+        //     if (!this.queryParams.deptId) return;
+
+        //     this.userLoading = true;
+        //     this.userList = [];
+        //     this.total = 0;
+        //     this.userLoading = false;
+        // },
+        // /* 查找所有部门中匹配的用户 */
+        // findUsersInDept(depts, searchText, results) {
+        //     if (!depts || !Array.isArray(depts)) return;
+
+        //     depts.forEach(dept => {
+        //         // 检查当前部门的用户
+        //         if (dept.users && dept.users.length > 0) {
+        //             dept.users.forEach(user => {
+        //                 if (user.userName.includes(searchText) || user.nickName.includes(searchText)) {
+        //                     results.push({
+        //                         ...user,
+        //                         deptId: dept.id,
+        //                         deptName: dept.label
+        //                     });
+        //                 }
+        //             });
+        //         }
+
+        //         if (dept.children && dept.children.length > 0) {
+        //             this.findUsersInDept(dept.children, searchText, results);
+        //         }
+        //     });
+        // },
+        // /* 递归查找包含匹配用户的部门节点 */
+        // findDeptKeysToExpand(depts, searchText, keys) {
+        //     if (!depts || !Array.isArray(depts)) return;
+
+        //     depts.forEach(dept => {
+        //         let hasMatch = false;
+
+        //         // 检查当前部门是否有匹配用户
+        //         if (dept.users && dept.users.length > 0) {
+        //             hasMatch = dept.users.some(user =>
+        //                 user.userName.includes(searchText) || user.nickName.includes(searchText)
+        //             );
+        //         }
+
+        //         // 检查子部门是否有匹配用户
+        //         if (!hasMatch && dept.children && dept.children.length > 0) {
+        //             hasMatch = this.deptContainsMatch(dept.children, searchText);
+        //         }
+
+        //         if (hasMatch) {
+        //             keys.push(dept.id);
+        //         }
+
+        //         // 递归处理子部门
+        //         if (dept.children && dept.children.length > 0) {
+        //             this.findDeptKeysToExpand(dept.children, searchText, keys);
+        //         }
+        //     });
+        // },
+        // deptContainsMatch(depts, searchText) {
+        //     if (!depts || !Array.isArray(depts)) return false;
+
+        //     for (const dept of depts) {
+        //         if (dept.users && dept.users.length > 0) {
+        //             if (dept.users.some(user =>
+        //                 user.userName.includes(searchText) || user.nickName.includes(searchText)
+        //             )) {
+        //                 return true;
+        //             }
+        //         }
+
+        //         if (dept.children && dept.children.length > 0) {
+        //             if (this.deptContainsMatch(dept.children, searchText)) {
+        //                 return true;
+        //             }
+        //         }
+        //     }
+
+        //     return false;
+        // },
+        // handleSearch() {
+        //     this.queryParams.pageNum = 1;
+        //     this.$nextTick(() => {
+        //         this.restoreSelection();
+        //     });
+        // },
+        // resetQuery() {
+        //     this.searchText = '';
+        //     this.queryParams.pageNum = 1;
+        // },
+        // 确认选择
+        handleConfirm() {
+            if (this.selectedUserIds.length === 0) {
+                this.$modal.msgError("请选择要分配的用户");
+                return;
+            }
+
+            const actualSelectedUsers = this.selectedUsers.filter(user =>
+                this.selectedUserIds.includes(user.userId)
+            );
+
+            // 传递完整的选择信息
+            this.$emit('confirm', {
+                userIds: this.selectedUserIds,
+                users: actualSelectedUsers,
+                deptId: this.queryParams.deptId,
+                deptName: this.currentDepartment ? this.currentDepartment.label : '',
+                ...this.extraParams
+            });
+
+            this.dialogVisible = false;
+        },
+        handleCancel() {
+            this.dialogVisible = false;
+        },
+        /* 恢复表格中的选中状态 */
+        restoreSelection() {
+            if (!this.$refs.table || !this.userList || !this.selectedUsers) return;
+
+
+            this.$refs.table.clearSelection();
+            const originalSelectedUserIds = [...this.selectedUserIds];
+
+
+            this.userList.forEach(row => {
+                if (originalSelectedUserIds.includes(row.userId)) {
+                    this.$refs.table.toggleRowSelection(row, true);
+                }
+            });
+        },
+        // 移除已选中用户
+        removeSelectedUser(user, index) {
+            this.selectedUsers.splice(index, 1);
+            this.selectedUserIds.splice(this.selectedUserIds.indexOf(user.userId), 1);
+            // 同步取消表格中的选中状态,需要找到表格中对应的行对象
+            if (this.$refs.table) {
+                const tableRow = this.userList.find(row => row.userId === user.userId);
+                if (tableRow) {
+                    this.$refs.table.toggleRowSelection(tableRow, false);
+                }
+            }
+        },
+        handleClose() {
+            this.currentDepartment = null;
+            this.userList = [];
+            this.selectedUsers = [];
+            this.selectedUserIds = [];
+            // this.searchText = '';
+            this.queryParams.pageNum = 1;
+        }
+    }
+};
+</script>
+
+<style scoped>
+.user-select-container {
+    display: flex;
+    height: 500px;
+    min-height: 500px;
+    border: 1px solid #e4e7ed;
+    border-radius: 4px;
+}
+
+.department-panel {
+    width: 280px;
+    border-right: 2px solid #d1d5db;
+    padding: 10px;
+    overflow-y: auto;
+    background-color: #f9fafb;
+    box-shadow: 2px 0 5px rgba(0, 0, 0, 0.05);
+}
+
+.user-panel {
+    flex: 1;
+    display: flex;
+    flex-direction: column;
+    padding: 10px;
+}
+
+.panel-header {
+    padding-bottom: 10px;
+    border-bottom: 1px solid #e4e7ed;
+    margin-bottom: 15px;
+}
+
+.panel-header h4 {
+    margin: 0;
+    font-size: 16px;
+    font-weight: 600;
+    color: #1f2937;
+    padding-bottom: 6px;
+    border-bottom: 2px solid #1890ff;
+    display: inline-block;
+}
+
+.selected-users {
+    margin-bottom: 15px;
+    border: 1px solid #e4e7ed;
+    border-radius: 4px;
+    padding: 10px;
+    background-color: #f5f7fa;
+}
+
+.selected-header {
+    font-size: 14px;
+    font-weight: bold;
+    color: #303133;
+    margin-bottom: 10px;
+    border-bottom: 1px solid #ebeef5;
+    padding-bottom: 5px;
+}
+
+.selected-list {
+    display: flex;
+    flex-wrap: wrap;
+    gap: 8px;
+}
+
+.selected-tag {
+    margin: 0;
+}
+
+.search-form {
+    margin-bottom: 15px;
+    flex-shrink: 0;
+}
+
+.search-results {
+    flex: 1;
+    display: flex;
+    flex-direction: column;
+    overflow: hidden;
+}
+
+.result-header {
+    padding: 8px 0;
+    font-weight: bold;
+    color: #303133;
+    border-bottom: 1px solid #e4e7ed;
+    margin-bottom: 10px;
+}
+
+.user-table {
+    flex: 1;
+    display: flex;
+    flex-direction: column;
+    overflow: hidden;
+}
+
+.pagination-container {
+    flex-shrink: 0;
+    padding-top: 10px;
+    text-align: right;
+}
+
+.empty-state {
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    justify-content: center;
+    height: 100%;
+    color: #909399;
+}
+
+.empty-state i {
+    font-size: 48px;
+    margin-bottom: 15px;
+}
+
+.empty-state p {
+    margin: 0;
+    font-size: 14px;
+}
+
+.department-tree {
+    background: transparent;
+}
+
+.department-tree /deep/ .el-tree-node {
+    margin-bottom: 4px;
+}
+
+.department-tree /deep/ .el-tree-node__content {
+    padding: 8px 10px;
+    border-radius: 4px;
+    transition: all 0.3s;
+}
+
+.department-tree /deep/ .el-tree-node__content:hover {
+    background-color: #e6f7ff;
+}
+
+/* 当前选中节点高亮样式 */
+.department-tree /deep/ .el-tree-node.is-current>.el-tree-node__content {
+    background-color: #1890ff !important;
+    color: #fff !important;
+    font-weight: 600;
+}
+
+/* 展开图标样式 */
+.department-tree /deep/ .el-tree-node__expand-icon {
+    color: #606266;
+}
+
+.department-tree /deep/ .el-tree-node__expand-icon.expanded {
+    transform: rotate(90deg);
+}
+
+/* 隐藏没有子节点的展开图标 */
+.department-tree /deep/ .el-tree-node__expand-icon.is-leaf {
+    display: none;
+}
+
+.department-tree /deep/ .el-tree-node__children .el-tree-node__content {
+    padding-left: 30px;
+}
+
+.department-tree /deep/ .el-tree-node__children .el-tree-node__children .el-tree-node__content {
+    padding-left: 50px;
+}
+
+.department-tree /deep/ .el-tree-node__label {
+    font-size: 14px;
+    color: #303133;
+}
+</style>

+ 89 - 26
src/views/system/auditConfig/index.vue

@@ -92,20 +92,20 @@
           <el-input type="number" v-model="currentForm.nodeOrder" placeholder="请输入审核顺序" :disabled="false" />
         </el-form-item>
         <el-form-item label="审核人" prop="approver">
-          <el-select v-model="currentForm.approver" multiple placeholder="请选择审核人" filterable  >
-            <el-option v-for="item in userList" :key="item.userId" :label="item.userName" :value="item.userId" />
-          </el-select>
-          <!-- <div style="display: flex; align-items: center; gap: 10px">
-            <el-input v-model="form.approver" placeholder="请输入审核人" style="flex: 1" @focus="handleFocusUserInput" /> -->
-          <!-- <el-button type="primary" @click="addApprover">新增</el-button>
-            <el-button type="danger" @click="removeApprover" :disabled="!currentApprover">删除</el-button> -->
-          <!-- </div> -->
-          <!-- <div style="margin-top: 10px">
-            <el-tag v-for="(approver, index) in this.approverOptions" :key="index" closable @close="handleCloseTag(approver)"
-              style="margin-right: 10px; margin-bottom: 5px">
-              {{ approver }}
-            </el-tag>
-          </div> -->
+          <div class="approver-selector">
+            <div class="approver-input" @click="openUserSelect">
+              <span v-if="selectedUsers.length === 0" class="placeholder">请选择审核人</span>
+              <el-tag 
+                v-for="(user, index) in selectedUsers" 
+                :key="user.userId" 
+                closable 
+                @close="removeUser(user, index)"
+                style="margin-right: 8px; margin-bottom: 5px"
+              >
+                {{ user.nickName || user.userName }}
+              </el-tag>
+            </div>
+          </div>
         </el-form-item>
         <!-- <el-form-item label="审核链" prop="chain">
           <el-input v-model="form.chain" placeholder="请输入审核链" :disabled="true" />
@@ -117,6 +117,12 @@
       </div>
     </el-dialog>
 
+    <!-- 用户选择组件 -->
+    <user-select
+      :visible.sync="showUserSelectDialog"
+      title="选择审核人"
+      @confirm="handleUserSelectConfirm"
+    />
 
     <el-dialog title="选择用户" :visible.sync="showUserDialog" width="500" append-to-body>
       <el-form :model="queryUserParams" ref="queryUserForm" size="small" :inline="true" v-show="showSearch"
@@ -146,9 +152,13 @@
 <script>
 import { listConfig, getConfig, delConfig, addConfig, updateConfig } from "@/api/system/auditConfig";
 import { listUser } from "@/api/system/user";
+import UserSelect from "@/components/UserSelect";
 
 export default {
   name: "Config",
+  components: {
+    UserSelect
+  },
   data() {
     return {
       // 遮罩层
@@ -209,6 +219,9 @@ export default {
       list: [],
       appList: [],
       userTotal: 0,
+      // 用户选择相关数据
+      showUserSelectDialog: false,
+      selectedUsers: [], // 当前选中的用户列表
     };
   },
   created() {
@@ -355,7 +368,7 @@ export default {
     handleAdd() {
       this.reset();
       this.open = true;
-      this.approverOptions = [];
+      this.selectedUsers = [];
       this.title = "添加审核配置";
       this.getUserList();
     },
@@ -364,20 +377,18 @@ export default {
       console.log('初始')
       this.reset();
       const id = row.id || this.ids
-      // this.queryUserParams.userId = row.approver;
-      this.approverOptions = [];
       this.getUserList();
-
-      this.currentForm.approver = row.approverList;
+      
+      // 设置当前表单数据
       this.currentForm.nodeOrder = row.nodeOrder;
       this.currentForm.id = row.id;
-      // getConfig(id).then(response => {
-      //   this.form = response.data;
-      //   this.approverOptions = this.form.approver.split(',');
-        this.open = true;
-        this.title = "修改审核配置";
-      // });
-      // this.queryUserParams.userId = '';
+      
+      // 设置选中的用户
+      this.selectedUsers = row.approverList || [];
+      this.currentForm.approver = row.approverList ? row.approverList.map(user => user.userId) : [];
+      
+      this.open = true;
+      this.title = "修改审核配置";
     },
     /** 提交按钮 */
     submitForm() {
@@ -421,7 +432,59 @@ export default {
       this.download('system/config/export', {
         ...this.queryParams
       }, `config_${new Date().getTime()}.xlsx`)
+    },
+    // 打开用户选择对话框
+    openUserSelect() {
+      this.showUserSelectDialog = true;
+    },
+    // 处理用户选择确认
+    handleUserSelectConfirm(data) {
+      // 将选中的用户添加到已选列表中
+      const newUsers = data.users.filter(user => 
+        !this.selectedUsers.some(selected => selected.userId === user.userId)
+      );
+      
+      this.selectedUsers = [...this.selectedUsers, ...newUsers];
+      this.currentForm.approver = this.selectedUsers.map(user => user.userId);
+    },
+    // 移除选中的用户
+    removeUser(user, index) {
+      this.selectedUsers.splice(index, 1);
+      this.currentForm.approver = this.selectedUsers.map(user => user.userId);
     }
   }
 };
 </script>
+
+<style scoped>
+.approver-selector {
+  width: 100%;
+}
+
+.approver-input {
+  min-height: 32px;
+  padding: 0 15px;
+  border: 1px solid #DCDFE6;
+  border-radius: 4px;
+  cursor: pointer;
+  transition: border-color 0.2s;
+  display: flex;
+  flex-wrap: wrap;
+  align-items: center;
+  gap: 5px;
+}
+
+.approver-input:hover {
+  border-color: #C0C4CC;
+}
+
+.approver-input:focus {
+  border-color: #409EFF;
+  outline: none;
+}
+
+.placeholder {
+  color: #C0C4CC;
+  font-size: 14px;
+}
+</style>

+ 33 - 6
src/views/system/role/authUser.vue

@@ -95,18 +95,23 @@
       :limit.sync="queryParams.pageSize"
       @pagination="getList"
     />
-    <select-user ref="select" :roleId="queryParams.roleId" @ok="handleQuery" />
+    <department-user-select
+      ref="userSelect"
+      :visible.sync="selectUserVisible"
+      :extra-params="{ roleId: queryParams.roleId }"
+      @confirm="handleConfirm"
+    />
   </div>
 </template>
 
 <script>
-import { allocatedUserList, authUserCancel, authUserCancelAll } from "@/api/system/role";
-import selectUser from "./selectUser";
+import { allocatedUserList, authUserCancel, authUserCancelAll, authUserSelectAll } from "@/api/system/role";
+import DepartmentUserSelect from "@/components/UserSelect";
 
 export default {
   name: "AuthUser",
   dicts: ['sys_normal_disable'],
-  components: { selectUser },
+  components: { DepartmentUserSelect },
   data() {
     return {
       // 遮罩层
@@ -121,6 +126,8 @@ export default {
       total: 0,
       // 用户表格数据
       userList: [],
+      // 用户选择器可见性
+      selectUserVisible: false,
       // 查询参数
       queryParams: {
         pageNum: 1,
@@ -171,7 +178,27 @@ export default {
     },
     /** 打开授权用户表弹窗 */
     openSelectUser() {
-      this.$refs.select.show();
+      this.selectUserVisible = true;
+    },
+    /** 处理用户选择确认 */
+    handleConfirm(data) {
+      const { roleId, userIds } = data;
+
+      if (!userIds || userIds.length === 0) {
+        this.$modal.msgError("请选择要分配的用户");
+        return;
+      }
+
+      const userIdsStr = Array.isArray(userIds) ? userIds.join(",") : userIds;
+
+      // 调用API授权用户
+      authUserSelectAll({ roleId: roleId, userIds: userIdsStr }).then(res => {
+        this.$modal.msgSuccess(res.msg);
+        if (res.code === 200) {
+          this.selectUserVisible = false;
+          this.getList();
+        }
+      });
     },
     /** 取消授权按钮操作 */
     cancelAuthUser(row) {
@@ -196,4 +223,4 @@ export default {
     }
   }
 };
-</script>
+</script>

+ 0 - 138
src/views/system/role/selectUser.vue

@@ -1,138 +0,0 @@
-<template>
-  <!-- 授权用户 -->
-  <el-dialog title="选择用户" :visible.sync="visible" width="800px" top="5vh" append-to-body>
-    <el-form :model="queryParams" ref="queryForm" size="small" :inline="true">
-      <el-form-item label="用户名称" prop="userName">
-        <el-input
-          v-model="queryParams.userName"
-          placeholder="请输入用户名称"
-          clearable
-          @keyup.enter.native="handleQuery"
-        />
-      </el-form-item>
-      <el-form-item label="手机号码" prop="phonenumber">
-        <el-input
-          v-model="queryParams.phonenumber"
-          placeholder="请输入手机号码"
-          clearable
-          @keyup.enter.native="handleQuery"
-        />
-      </el-form-item>
-      <el-form-item>
-        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
-        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
-      </el-form-item>
-    </el-form>
-    <el-row>
-      <el-table @row-click="clickRow" ref="table" :data="userList" @selection-change="handleSelectionChange" height="260px">
-        <el-table-column type="selection" width="55"></el-table-column>
-        <el-table-column label="用户名称" prop="userName" :show-overflow-tooltip="true" />
-        <el-table-column label="用户昵称" prop="nickName" :show-overflow-tooltip="true" />
-        <el-table-column label="邮箱" prop="email" :show-overflow-tooltip="true" />
-        <el-table-column label="手机" prop="phonenumber" :show-overflow-tooltip="true" />
-        <el-table-column label="状态" align="center" prop="status">
-          <template slot-scope="scope">
-            <dict-tag :options="dict.type.sys_normal_disable" :value="scope.row.status"/>
-          </template>
-        </el-table-column>
-        <el-table-column label="创建时间" align="center" prop="createTime" width="180">
-          <template slot-scope="scope">
-            <span>{{ parseTime(scope.row.createTime) }}</span>
-          </template>
-        </el-table-column>
-      </el-table>
-      <pagination
-        v-show="total>0"
-        :total="total"
-        :page.sync="queryParams.pageNum"
-        :limit.sync="queryParams.pageSize"
-        @pagination="getList"
-      />
-    </el-row>
-    <div slot="footer" class="dialog-footer">
-      <el-button type="primary" @click="handleSelectUser">确 定</el-button>
-      <el-button @click="visible = false">取 消</el-button>
-    </div>
-  </el-dialog>
-</template>
-
-<script>
-import { unallocatedUserList, authUserSelectAll } from "@/api/system/role";
-export default {
-  dicts: ['sys_normal_disable'],
-  props: {
-    // 角色编号
-    roleId: {
-      type: [Number, String]
-    }
-  },
-  data() {
-    return {
-      // 遮罩层
-      visible: false,
-      // 选中数组值
-      userIds: [],
-      // 总条数
-      total: 0,
-      // 未授权用户数据
-      userList: [],
-      // 查询参数
-      queryParams: {
-        pageNum: 1,
-        pageSize: 10,
-        roleId: undefined,
-        userName: undefined,
-        phonenumber: undefined
-      }
-    };
-  },
-  methods: {
-    // 显示弹框
-    show() {
-      this.queryParams.roleId = this.roleId;
-      this.getList();
-      this.visible = true;
-    },
-    clickRow(row) {
-      this.$refs.table.toggleRowSelection(row);
-    },
-    // 多选框选中数据
-    handleSelectionChange(selection) {
-      this.userIds = selection.map(item => item.userId);
-    },
-    // 查询表数据
-    getList() {
-      unallocatedUserList(this.queryParams).then(res => {
-        this.userList = res.rows;
-        this.total = res.total;
-      });
-    },
-    /** 搜索按钮操作 */
-    handleQuery() {
-      this.queryParams.pageNum = 1;
-      this.getList();
-    },
-    /** 重置按钮操作 */
-    resetQuery() {
-      this.resetForm("queryForm");
-      this.handleQuery();
-    },
-    /** 选择授权用户操作 */
-    handleSelectUser() {
-      const roleId = this.queryParams.roleId;
-      const userIds = this.userIds.join(",");
-      if (userIds == "") {
-        this.$modal.msgError("请选择要分配的用户");
-        return;
-      }
-      authUserSelectAll({ roleId: roleId, userIds: userIds }).then(res => {
-        this.$modal.msgSuccess(res.msg);
-        if (res.code === 200) {
-          this.visible = false;
-          this.$emit("ok");
-        }
-      });
-    }
-  }
-};
-</script>