多选框组件重写
This commit is contained in:
@@ -21,26 +21,34 @@
|
||||
<!-- 左侧:可选列表 -->
|
||||
<div class="transfer-panel">
|
||||
<div class="panel-header">可选员工</div>
|
||||
<div class="panel-search">
|
||||
<input
|
||||
type="text"
|
||||
v-model="leftSearchQuery"
|
||||
placeholder="搜索姓名/部门"
|
||||
class="search-input"
|
||||
/>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div
|
||||
v-for="item in availableList"
|
||||
v-for="item in filteredAvailableList"
|
||||
:key="`left-${item.infoId}`"
|
||||
:class="['transfer-item', { disabled: item.disabled, selected: isLeftSelected(item), 'already-selected': item.isSelected }]"
|
||||
@click="addToSelected(item)"
|
||||
>
|
||||
<el-checkbox
|
||||
<input
|
||||
type="checkbox"
|
||||
:checked="item.isSelected"
|
||||
:disabled="item.disabled"
|
||||
@click.stop="toggleItem(item)"
|
||||
class="custom-checkbox"
|
||||
/>
|
||||
<span v-if="item.isDuplicate" class="duplicate-icon" title="重名">
|
||||
<i class="el-icon-user" />
|
||||
</span>
|
||||
<span class="item-label">{{ item.dept }} - {{ item.name }} ({{ item.jobType }})</span>
|
||||
<span class="id-badge" :title="'ID: ' + item.infoId">{{ item.infoId }}</span>
|
||||
<span v-if="item.isSelected" class="selected-tag">已选</span>
|
||||
</div>
|
||||
<div v-if="availableList.length === 0" class="empty-tip">暂无数据</div>
|
||||
<div v-if="filteredAvailableList.length === 0" class="empty-tip">暂无数据</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -49,15 +57,15 @@
|
||||
<el-button
|
||||
type="primary"
|
||||
icon="el-icon-arrow-right"
|
||||
@click="addAll"
|
||||
:disabled="availableList.filter(i => !i.disabled).length === 0"
|
||||
@click="addSelected"
|
||||
:disabled="leftSelectedKeys.length === 0"
|
||||
>
|
||||
添加
|
||||
</el-button>
|
||||
<el-button
|
||||
icon="el-icon-arrow-left"
|
||||
@click="removeAll"
|
||||
:disabled="selectedList.length === 0"
|
||||
@click="removeSelected"
|
||||
:disabled="rightSelectedKeys.length === 0"
|
||||
>
|
||||
移除
|
||||
</el-button>
|
||||
@@ -66,23 +74,32 @@
|
||||
<!-- 右侧:已选列表 -->
|
||||
<div class="transfer-panel">
|
||||
<div class="panel-header">已选员工</div>
|
||||
<div class="panel-search">
|
||||
<input
|
||||
type="text"
|
||||
v-model="rightSearchQuery"
|
||||
placeholder="搜索姓名/部门"
|
||||
class="search-input"
|
||||
/>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div
|
||||
v-for="item in selectedList"
|
||||
v-for="item in filteredSelectedList"
|
||||
:key="`right-${item.infoId}`"
|
||||
:class="['transfer-item', 'selected-item', { selected: isRightSelected(item) }]"
|
||||
@click="removeFromSelected(item)"
|
||||
>
|
||||
<el-checkbox
|
||||
<input
|
||||
type="checkbox"
|
||||
:checked="isRightSelected(item)"
|
||||
@click.stop="toggleSelectedItem(item)"
|
||||
class="custom-checkbox"
|
||||
/>
|
||||
<span v-if="item.isDuplicate" class="duplicate-icon" title="重名">
|
||||
<i class="el-icon-user" />
|
||||
</span>
|
||||
<span class="item-label">{{ item.dept }} - {{ item.name }} ({{ item.jobType }})</span>
|
||||
</div>
|
||||
<div v-if="selectedList.length === 0" class="empty-tip">暂无数据</div>
|
||||
<div v-if="filteredSelectedList.length === 0" class="empty-tip">暂无数据</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -154,6 +171,8 @@ export default {
|
||||
open: false,
|
||||
loading: false,
|
||||
searchQuery: '',
|
||||
leftSearchQuery: '',
|
||||
rightSearchQuery: '',
|
||||
selectedIds: [],
|
||||
selectedId: '',
|
||||
selectedEmployee: {},
|
||||
@@ -231,6 +250,30 @@ export default {
|
||||
return null
|
||||
})
|
||||
.filter(item => item !== null)
|
||||
},
|
||||
// 左侧过滤后的列表
|
||||
filteredAvailableList() {
|
||||
if (!this.leftSearchQuery) {
|
||||
return this.availableList
|
||||
}
|
||||
const query = this.leftSearchQuery.toLowerCase()
|
||||
return this.availableList.filter(item => {
|
||||
const name = (item.name || '').toLowerCase()
|
||||
const dept = (item.dept || '').toLowerCase()
|
||||
return name.includes(query) || dept.includes(query)
|
||||
})
|
||||
},
|
||||
// 右侧过滤后的列表
|
||||
filteredSelectedList() {
|
||||
if (!this.rightSearchQuery) {
|
||||
return this.selectedList
|
||||
}
|
||||
const query = this.rightSearchQuery.toLowerCase()
|
||||
return this.selectedList.filter(item => {
|
||||
const name = (item.name || '').toLowerCase()
|
||||
const dept = (item.dept || '').toLowerCase()
|
||||
return name.includes(query) || dept.includes(query)
|
||||
})
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
@@ -280,7 +323,10 @@ export default {
|
||||
return new Promise((resolve) => {
|
||||
listEmployeeInfo(params).then(response => {
|
||||
// 处理重名情况:确保每个员工有唯一标识
|
||||
this.rawEmployeeList = response.rows || []
|
||||
// 过滤掉已离职的员工
|
||||
this.rawEmployeeList = (response.rows || []).filter(employee => {
|
||||
return !this.isEmployeeResigned(employee)
|
||||
})
|
||||
this.loading = false
|
||||
resolve()
|
||||
}).catch(() => {
|
||||
@@ -289,6 +335,11 @@ export default {
|
||||
})
|
||||
})
|
||||
},
|
||||
// 判断员工是否已离职
|
||||
isEmployeeResigned(employee) {
|
||||
// 支持多种可能的离职状态字段
|
||||
return employee.status === 1 || employee.isLeave === true || employee.resignStatus === 1 || employee.leaveStatus === 1
|
||||
},
|
||||
|
||||
handleSearch() {
|
||||
},
|
||||
@@ -311,6 +362,12 @@ export default {
|
||||
return isDisabled
|
||||
},
|
||||
|
||||
// 判断员工是否已离职
|
||||
isEmployeeResigned(employee) {
|
||||
// isLeave: 1 表示已离职,0 表示在职
|
||||
return employee.isLeave === 1 || employee.isLeave === '1'
|
||||
},
|
||||
|
||||
rowClassName({ row }) {
|
||||
if (this.isSelected(row)) {
|
||||
return 'selected-row'
|
||||
@@ -400,13 +457,9 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
// 添加所有左侧选中的
|
||||
addAll() {
|
||||
const toAdd = this.leftSelectedKeys.length > 0
|
||||
? this.leftSelectedKeys
|
||||
: this.availableList.filter(item => !item.disabled).map(item => item.infoId)
|
||||
|
||||
toAdd.forEach(key => {
|
||||
// 添加左侧勾选的员工
|
||||
addSelected() {
|
||||
this.leftSelectedKeys.forEach(key => {
|
||||
const keyStr = String(key)
|
||||
if (!this.selectedIds.some(id => String(id) === keyStr)) {
|
||||
this.selectedIds.push(key)
|
||||
@@ -415,13 +468,9 @@ export default {
|
||||
this.leftSelectedKeys = []
|
||||
},
|
||||
|
||||
// 移除所有右侧选中的
|
||||
removeAll() {
|
||||
const toRemove = this.rightSelectedKeys.length > 0
|
||||
? this.rightSelectedKeys
|
||||
: this.selectedIds.slice()
|
||||
|
||||
toRemove.forEach(key => {
|
||||
// 移除右侧勾选的员工
|
||||
removeSelected() {
|
||||
this.rightSelectedKeys.forEach(key => {
|
||||
const keyStr = String(key)
|
||||
const index = this.selectedIds.findIndex(id => String(id) === keyStr)
|
||||
if (index > -1) {
|
||||
@@ -431,6 +480,24 @@ export default {
|
||||
this.rightSelectedKeys = []
|
||||
},
|
||||
|
||||
// 添加所有可选员工(未禁用且未选中的)
|
||||
addAll() {
|
||||
this.availableList.forEach(item => {
|
||||
if (!item.disabled && !item.isSelected) {
|
||||
const keyStr = String(item.infoId)
|
||||
if (!this.selectedIds.some(id => String(id) === keyStr)) {
|
||||
this.selectedIds.push(item.infoId)
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 移除所有已选员工
|
||||
removeAll() {
|
||||
this.selectedIds = []
|
||||
this.rightSelectedKeys = []
|
||||
},
|
||||
|
||||
confirmSelection() {
|
||||
if (this.multiple) {
|
||||
// 使用ID作为唯一标识,避免重名问题
|
||||
@@ -520,6 +587,25 @@ export default {
|
||||
background: #f5f7fa;
|
||||
}
|
||||
|
||||
.panel-search {
|
||||
padding: 10px;
|
||||
border-bottom: 1px solid #f2f6fc;
|
||||
}
|
||||
|
||||
.search-input {
|
||||
width: 100%;
|
||||
padding: 6px 10px;
|
||||
border: 1px solid #dcdfe6;
|
||||
border-radius: 4px;
|
||||
font-size: 13px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.search-input:focus {
|
||||
outline: none;
|
||||
border-color: #409eff;
|
||||
}
|
||||
|
||||
.panel-body {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
@@ -587,6 +673,15 @@ export default {
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
.resigned-tag {
|
||||
font-size: 11px;
|
||||
color: #f56c6c;
|
||||
background-color: #fef0f0;
|
||||
padding: 1px 4px;
|
||||
border-radius: 2px;
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
.id-badge {
|
||||
font-size: 10px;
|
||||
color: #909399;
|
||||
@@ -597,6 +692,19 @@ export default {
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
/* 自定义 checkbox 样式 */
|
||||
.custom-checkbox {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
cursor: pointer;
|
||||
accent-color: #409eff;
|
||||
}
|
||||
|
||||
.custom-checkbox:disabled {
|
||||
cursor: not-allowed;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.transfer-buttons {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
Reference in New Issue
Block a user