<template> <div class="select-item"> <div class="select-checked"> <a-checkbox :indeterminate="indeterminate" :checked="checkAll" @change="onCheckAllChange" /> <span>{{nowSelectKeys.length}} 项</span> </div> <div class="select-content"> <ul class="data-list-content"> <li v-for="(item, index) in dataList" :key="item.key || 'item-' + index" @click="onChange(item,index)" class="data-list-content-item"> <input type="checkbox" class="list-checkbox-input" :checked="item.selected"> <span style="font-size:8pt;" :title="item.description" :class="{'font-red':item.disabled}"> <span>{{item.title}}<span style="font-style: italic;color: #8e99a5;" :class="{'font-red':item.disabled}">({{item.description}})</span></span> </span> </li> </ul> </div> </div> </template> <script> export default { name: 'selectItem', components: { }, data () { return { selectKeys: [], nowSelectKeys: [], indeterminate: false, checkAll: false, } }, props: { value: { type: Array, default: () => { return [] } }, dataList: { type: Array, default: () => { return [] } }, }, created () { this.selectKeys = this.value this.initData() }, methods: { getStyle (value) { return { color: value ? 'red' : 'black' }; }, onChange (e) { if (!e || typeof e !== 'object') { console.warn('尝试操作不存在的元素'); return; } e.selected = !e.selected if (e.selected) { this.selectKeys.push(e) } else { for (let j = 0; j < this.selectKeys.length; j++) { if (e.key && e.key === this.selectKeys[j].key) { this.selectKeys.splice(j, 1) break } } } this.initData() this.$emit("input", this.selectKeys) }, onCheckAllChange (e) { if (e.target.checked) { for (let i = 0; i < this.dataList.length; i++) { if (!this.dataList[i]) continue; this.dataList[i].selected = e.target.checked let state = false; for (let j = 0; j < this.selectKeys.length; j++) { if (this.dataList[i].key && this.selectKeys[j] && this.dataList[i].key === this.selectKeys[j].key) { state = true break } } if (!state) { this.selectKeys.push(this.dataList[i]) } } } else { for (let i = 0; i < this.dataList.length; i++) { if (!this.dataList[i]) continue; this.dataList[i].selected = e.target.checked for (let j = 0; j < this.selectKeys.length; j++) { if (this.selectKeys[j] && this.dataList[i].key && this.dataList[i].key === this.selectKeys[j].key) { this.selectKeys.splice(j, 1); break; } } } } this.initData() this.$emit("input", this.selectKeys) }, initData () { this.nowSelectKeys = [] for (let i = 0; i < this.dataList.length; i++) { if (this.dataList[i] && this.dataList[i].selected) { this.nowSelectKeys.push(this.dataList[i]) } } this.indeterminate = !!this.nowSelectKeys.length && this.nowSelectKeys.length < this.dataList.length this.checkAll = this.nowSelectKeys.length === this.dataList.length && this.nowSelectKeys.length > 0 }, ensureUniqueKeys() { if (!Array.isArray(this.dataList)) { console.warn('dataList 不是数组'); return; } const keyMap = new Map(); let hasDuplicates = false; const validDataList = this.dataList.filter(item => item && typeof item === 'object'); validDataList.forEach((item, index) => { if (!item.key) { item.key = 'generated-' + Date.now() + '-' + index; } else if (keyMap.has(item.key)) { console.warn(`发现重复键: ${item.key},已自动修复`); item.key = item.key + '-' + Date.now() + '-' + index; hasDuplicates = true; } keyMap.set(item.key, true); }); if (hasDuplicates) { this.initData(); } }, cleanupSelectKeys() { if (!Array.isArray(this.selectKeys)) return; const validKeys = new Set(); this.dataList.forEach(item => { if (item && item.key) validKeys.add(item.key); }); this.selectKeys = this.selectKeys.filter(item => { return item && item.key && validKeys.has(item.key); }); } }, watch: { dataList: { handler(dataList) { this.ensureUniqueKeys(); this.cleanupSelectKeys(); this.initData(); }, immediate: true }, value: { handler(val) { if (Array.isArray(val)) { this.selectKeys = val; this.cleanupSelectKeys(); this.initData(); } }, immediate: true } } } </script> <style scoped lang="less"> .font-red { color: red !important; } .select-item { position: relative; width: 100%; height: 100%; vertical-align: middle; border-radius: 4px; .select-checked { width: 100%; height: 40px; padding: 8px 5px 9px; overflow: hidden; color: rgba(0, 0, 0, 0.65); background: #fff; border-bottom: 1px solid #e8e8e8; border-radius: 4px 4px 0 0; } .select-content { width: 100%; height: calc(100% - 40px); overflow-x: hidden; overflow-y: auto; ul { height: 100%; margin: 0; padding: 0; overflow: auto; list-style: none; } ul li { height: 24px; padding: 0px 4px; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; transition: all 0.3s; overflow: hidden; cursor: pointer; } ul li:hover { background-color: rgb(230 247 255); } ul li > span { padding-left: 4px; cursor: pointer; margin: 0; vertical-align: middle; } ul li input { display: inline-block; vertical-align: middle; } .list-checkbox-input { width: 14px; height: 14px; cursor: pointer; } } } </style>