From ae6878c00ba0b3daf2154cdd36ca98c02ae38404 Mon Sep 17 00:00:00 2001
From: jhd <1684074631@qq.com>
Date: Thu, 21 May 2026 18:33:44 +0800
Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E5=A4=8D=E5=90=8D=E7=A7=B0=E5=8F=AF?=
=?UTF-8?q?=E9=80=89?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../src/components/EmployeeSelector/index.vue | 373 ++++++++++++++++--
1 file changed, 330 insertions(+), 43 deletions(-)
diff --git a/klp-ui/src/components/EmployeeSelector/index.vue b/klp-ui/src/components/EmployeeSelector/index.vue
index 8d46322a..65ac6f68 100644
--- a/klp-ui/src/components/EmployeeSelector/index.vue
+++ b/klp-ui/src/components/EmployeeSelector/index.vue
@@ -16,23 +16,76 @@
-
-
+
+
+
+
+
+
+
+
+
+
+
+ {{ item.dept }} - {{ item.name }} ({{ item.jobType }})
+ {{ item.infoId }}
+ 已选
+
+
暂无数据
+
+
+
+
+
+
+ 添加
+
+
+ 移除
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ item.dept }} - {{ item.name }} ({{ item.jobType }})
+
+
暂无数据
+
+
+
@@ -103,7 +156,10 @@ export default {
searchQuery: '',
selectedIds: [],
selectedId: '',
- selectedEmployee: {}
+ selectedEmployee: {},
+ // 用于记录选中状态(解决重名问题)
+ leftSelectedKeys: [],
+ rightSelectedKeys: []
}
},
computed: {
@@ -132,12 +188,49 @@ export default {
disabled: this.disabledIdList.includes(employee[this.keyField].toString())
}))
},
- transferData() {
- return this.employeeList.map(employee => ({
- [this.keyField]: employee[this.keyField],
- label: `${employee.dept} - ${employee.name} (${employee.jobType})`,
- disabled: employee.disabled
- }))
+ // 获取重名的姓名列表
+ duplicateNames() {
+ const nameCount = {}
+ this.rawEmployeeList.forEach(employee => {
+ const name = employee.name || ''
+ nameCount[name] = (nameCount[name] || 0) + 1
+ })
+ return Object.keys(nameCount).filter(name => nameCount[name] > 1)
+ },
+ // 可选列表(显示所有员工,包括已选的)
+ availableList() {
+ const duplicates = this.duplicateNames
+ return this.rawEmployeeList.map(employee => {
+ // 直接使用 infoId 作为唯一标识
+ const employeeId = employee.infoId
+ const idStr = String(employeeId)
+ return {
+ ...employee,
+ disabled: this.disabledIdList.includes(idStr),
+ isDuplicate: duplicates.includes(employee.name || ''),
+ isSelected: this.selectedIds.some(id => String(id) === idStr)
+ }
+ })
+ },
+ // 已选列表
+ selectedList() {
+ const duplicates = this.duplicateNames
+ // 使用 infoId 作为唯一标识,支持同名不同ID的员工
+ return this.selectedIds
+ .map(id => {
+ const idStr = String(id)
+ const matched = this.rawEmployeeList.filter(item => String(item.infoId) === idStr)
+ const employee = matched.length > 0 ? matched[0] : null
+ if (employee) {
+ // 创建新对象,避免修改原始数据
+ return {
+ ...employee,
+ isDuplicate: duplicates.includes(employee.name || '')
+ }
+ }
+ return null
+ })
+ .filter(item => item !== null)
}
},
watch: {
@@ -145,7 +238,13 @@ export default {
handler(newVal) {
if (this.multiple) {
if (newVal) {
- this.selectedIds = newVal.split(',').map(id => id.trim()).filter(id => id).map(id => isNaN(Number(id)) ? id : Number(id))
+ // 处理重名问题:确保ID是唯一标识,转换为正确类型
+ this.selectedIds = newVal.split(',').map(id => {
+ const trimmedId = id.trim()
+ if (!trimmedId) return null
+ const numId = Number(trimmedId)
+ return isNaN(numId) ? trimmedId : numId
+ }).filter(id => id !== null)
} else {
this.selectedIds = []
}
@@ -164,6 +263,9 @@ export default {
methods: {
toggleDialog() {
if (!this.open) {
+ // 打开时清空选中状态
+ this.leftSelectedKeys = []
+ this.rightSelectedKeys = []
this.getEmployeeList()
}
this.open = !this.open
@@ -177,7 +279,8 @@ export default {
}
return new Promise((resolve) => {
listEmployeeInfo(params).then(response => {
- this.rawEmployeeList = response.rows;
+ // 处理重名情况:确保每个员工有唯一标识
+ this.rawEmployeeList = response.rows || []
this.loading = false
resolve()
}).catch(() => {
@@ -235,14 +338,104 @@ export default {
}
},
- transferFilterMethod(query, item) {
- return item.label.toLowerCase().includes(query.toLowerCase())
+ // ========== 多选模式方法 ==========
+
+ // 判断左侧是否选中
+ isLeftSelected(item) {
+ return this.leftSelectedKeys.includes(item.infoId)
+ },
+
+ // 判断右侧是否选中
+ isRightSelected(item) {
+ return this.rightSelectedKeys.includes(item.infoId)
+ },
+
+ // 切换左侧选中状态
+ toggleItem(item) {
+ if (item.disabled) return
+ const key = item.infoId
+ const index = this.leftSelectedKeys.indexOf(key)
+ if (index > -1) {
+ this.leftSelectedKeys.splice(index, 1)
+ } else {
+ this.leftSelectedKeys.push(key)
+ }
+ },
+
+ // 切换右侧选中状态
+ toggleSelectedItem(item) {
+ const key = item.infoId
+ const index = this.rightSelectedKeys.indexOf(key)
+ if (index > -1) {
+ this.rightSelectedKeys.splice(index, 1)
+ } else {
+ this.rightSelectedKeys.push(key)
+ }
+ },
+
+ // 添加单个到已选
+ addToSelected(item) {
+ if (item.disabled) return
+ // 直接使用 infoId 作为唯一标识
+ const key = item.infoId
+ const keyStr = String(key)
+ // 检查是否已存在(字符串比较)
+ const exists = this.selectedIds.some(id => String(id) === keyStr)
+ if (!exists) {
+ this.selectedIds.push(key)
+ console.log('添加员工:', item.name, 'ID:', key, 'selectedIds:', this.selectedIds)
+ } else {
+ console.log('员工已存在:', item.name, 'ID:', key)
+ }
+ },
+
+ // 从已选移除单个
+ removeFromSelected(item) {
+ // 直接使用 infoId 作为唯一标识
+ const key = item.infoId
+ const keyStr = String(key)
+ const index = this.selectedIds.findIndex(id => String(id) === keyStr)
+ if (index > -1) {
+ this.selectedIds.splice(index, 1)
+ }
+ },
+
+ // 添加所有左侧选中的
+ addAll() {
+ const toAdd = this.leftSelectedKeys.length > 0
+ ? this.leftSelectedKeys
+ : this.availableList.filter(item => !item.disabled).map(item => item.infoId)
+
+ toAdd.forEach(key => {
+ const keyStr = String(key)
+ if (!this.selectedIds.some(id => String(id) === keyStr)) {
+ this.selectedIds.push(key)
+ }
+ })
+ this.leftSelectedKeys = []
+ },
+
+ // 移除所有右侧选中的
+ removeAll() {
+ const toRemove = this.rightSelectedKeys.length > 0
+ ? this.rightSelectedKeys
+ : this.selectedIds.slice()
+
+ toRemove.forEach(key => {
+ const keyStr = String(key)
+ const index = this.selectedIds.findIndex(id => String(id) === keyStr)
+ if (index > -1) {
+ this.selectedIds.splice(index, 1)
+ }
+ })
+ this.rightSelectedKeys = []
},
confirmSelection() {
if (this.multiple) {
+ // 使用ID作为唯一标识,避免重名问题
this.$emit('input', this.selectedIds.join(','))
- const selectedEmployees = this.rawEmployeeList.filter(item => this.selectedIds.includes(item[this.keyField]))
+ const selectedEmployees = this.selectedList
this.$emit('change', selectedEmployees)
}
this.open = false
@@ -251,10 +444,17 @@ export default {
cancelSelection() {
if (this.multiple) {
if (this.value) {
- this.selectedIds = this.value.split(',').map(id => id.trim()).filter(id => id).map(id => isNaN(Number(id)) ? id : Number(id))
+ this.selectedIds = this.value.split(',').map(id => {
+ const trimmedId = id.trim()
+ if (!trimmedId) return null
+ const numId = Number(trimmedId)
+ return isNaN(numId) ? trimmedId : numId
+ }).filter(id => id !== null)
} else {
this.selectedIds = []
}
+ this.leftSelectedKeys = []
+ this.rightSelectedKeys = []
} else {
if (this.value) {
this.findSelectedEmployee(this.value)
@@ -295,29 +495,120 @@ export default {
margin-top: 15px;
}
-::v-deep .el-transfer {
+/* 自定义穿梭框样式 */
+.custom-transfer {
display: flex;
justify-content: center;
+ align-items: flex-start;
+ gap: 20px;
+ padding: 10px;
+}
+
+.transfer-panel {
+ width: 350px;
+ border: 1px solid #dcdfe6;
+ border-radius: 4px;
+ background: #fff;
+ display: flex;
+ flex-direction: column;
+}
+
+.panel-header {
+ padding: 12px 15px;
+ font-weight: 600;
+ border-bottom: 1px solid #dcdfe6;
+ background: #f5f7fa;
+}
+
+.panel-body {
+ flex: 1;
+ overflow-y: auto;
+ max-height: 400px;
+}
+
+.transfer-item {
+ display: flex;
align-items: center;
+ padding: 10px 15px;
+ cursor: pointer;
+ border-bottom: 1px solid #f2f6fc;
+ transition: background-color 0.2s;
}
-::v-deep .el-transfer-panel {
- width: 380px;
+.transfer-item:hover:not(.disabled) {
+ background-color: #ecf5ff;
}
-::v-deep .el-transfer-panel__body {
- height: 450px;
+.transfer-item.disabled {
+ color: #c0c4cc;
+ cursor: not-allowed;
}
-::v-deep .el-transfer__buttons {
- padding: 0 15px;
+.transfer-item.selected {
+ background-color: #e8f4fd;
+}
+
+.item-label {
+ flex: 1;
+ margin-left: 4px;
+ font-size: 13px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.duplicate-icon {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ width: 18px;
+ height: 18px;
+ background-color: #ffb800;
+ color: #fff;
+ border-radius: 50%;
+ font-size: 12px;
+ margin-left: 2px;
+}
+
+.duplicate-icon i {
+ font-size: 10px;
+}
+
+.already-selected {
+ opacity: 0.6;
+}
+
+.selected-tag {
+ font-size: 11px;
+ color: #67c23a;
+ background-color: #f0f9eb;
+ padding: 1px 4px;
+ border-radius: 2px;
+ margin-left: 4px;
+}
+
+.id-badge {
+ font-size: 10px;
+ color: #909399;
+ background-color: #f5f7fa;
+ padding: 1px 3px;
+ border-radius: 2px;
+ margin-left: 4px;
+ font-family: monospace;
+}
+
+.transfer-buttons {
display: flex;
flex-direction: column;
justify-content: center;
+ gap: 10px;
+ padding: 10px;
}
-::v-deep .el-transfer__button {
- margin: 10px 0;
+.empty-tip {
+ text-align: center;
+ color: #c0c4cc;
+ padding: 40px 0;
}
::v-deep .el-table .selected-row {
@@ -327,8 +618,4 @@ export default {
::v-deep .el-table .selected-row:hover {
background-color: #ecf5ff !important;
}
-
-::v-deep .el-transfer-panel__list.is-filterable {
- height: calc(100% - 60px)
-}