md
This commit is contained in:
parent
1ee3e46d36
commit
af1d3d4fa4
277
src/components/XdComboGrid/index copy.vue
Normal file
277
src/components/XdComboGrid/index copy.vue
Normal file
@ -0,0 +1,277 @@
|
|||||||
|
<template>
|
||||||
|
<div :id="createdId">
|
||||||
|
<div class="input-select el-input__inner" v-if="showSelect">
|
||||||
|
<div class="tags">
|
||||||
|
<el-tag v-for="(item, index) in multipleSelectionTags" :key="index" closable @close="handleCloseTag(item)" size="small"
|
||||||
|
type="info" effect="plain">{{item[keywordKey] || ''}}
|
||||||
|
</el-tag>
|
||||||
|
<input type="text" ref="selectInput" :value="value" :placeholder="placeholderForTag" @focus="onfocus" @input="changeValue($event.target.value)">
|
||||||
|
<i class="el-icon-arrow-down select-down" :class="visible&&'rotate180'"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<el-input v-else :value="value" @input="changeValue" @focus="onfocus" @blur="onblur" clearable :placeholder="placeholder" ref="inputValue"></el-input>
|
||||||
|
<div :popId="popId" tabindex="0" class="el-picker-panel" :style="pStyle" v-show="visible" ref="elcombogrid" >
|
||||||
|
<div class="table-container">
|
||||||
|
<el-table v-loading="listLoading" :data="list" @row-click="rowClick" stripe size="mini"
|
||||||
|
element-loading-text="Loading" ref="comboGridTable" fit border highlight-current-row
|
||||||
|
@selection-change="handleSelectionChange" @select="handleSingelSelectionChange">
|
||||||
|
<el-table-column type="selection" v-if="showSelect" width="55" align="center" />
|
||||||
|
<!-- <el-table-column v-if="showIndex" label="序号" type="index" align="center" width="50"></el-table-column> -->
|
||||||
|
<!-- <el-table-column v-for="item in columns" :type="item.type" :key="item.key" :label="item.label"
|
||||||
|
:prop="item.key" :align="item.align" :width="item.width" :header-align="item.headerAlign">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span>{{ scope.row[item.key] }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column> -->
|
||||||
|
|
||||||
|
<el-table-column v-for="item in columns" :key="item.key" :label="item.label" :prop="item.key" :align="item.align" :min-width="item.width" :header-align="item.headerAlign">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span>{{ scope.row[item.key] }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<el-pagination v-show="pagination" small :total="total" :page-size="5" layout="prev, pager, next, total"
|
||||||
|
@current-change="changePage" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import request from '@/utils/request';
|
||||||
|
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "XdComboGrid",
|
||||||
|
props: {
|
||||||
|
placeholder: { type: String },
|
||||||
|
value: { type: String },
|
||||||
|
requestUrl:{ type: String },
|
||||||
|
method: { type: String },
|
||||||
|
columns: { type: Array },
|
||||||
|
panelStyle: { type: String },
|
||||||
|
keywordKey: { type: String },
|
||||||
|
showIndex: { type: Boolean },
|
||||||
|
pagination: { type: Boolean, default: true },
|
||||||
|
queryParams: { default: () => { } },
|
||||||
|
showSelect: { default: false }
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
visible: false,
|
||||||
|
pStyle: 'width:500px',
|
||||||
|
list: [],
|
||||||
|
total: 0,
|
||||||
|
listLoading: true,
|
||||||
|
listQuery: {
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 5
|
||||||
|
},
|
||||||
|
keyword: '',
|
||||||
|
createdId: '0',
|
||||||
|
popId: '0',
|
||||||
|
multipleSelection: [],
|
||||||
|
multipleSelectionTags: [],
|
||||||
|
placeholderForTag: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.createdId = String(new Date().getTime()) // 给个随机id 判断是否clickoutside
|
||||||
|
this.popId = String(new Date().getTime()) // 给个随机id 判断是否clickoutside
|
||||||
|
|
||||||
|
this.placeholderForTag = this.placeholder
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
changePage(cur) {
|
||||||
|
this.listQuery.pageNum = cur
|
||||||
|
this.getList()
|
||||||
|
},
|
||||||
|
changeValue(val) {
|
||||||
|
this.$emit('input', val)//向上级传送数据
|
||||||
|
this.keyword = val
|
||||||
|
this.listQuery.pageNum = 1
|
||||||
|
this.getList()
|
||||||
|
},
|
||||||
|
onfocus(el) {
|
||||||
|
this.pStyle = this.panelStyle + ';position:absolute;z-index:999999;'
|
||||||
|
this.visible = true
|
||||||
|
this.keyword = el.target.value
|
||||||
|
// document.removeEventListener('click', this.clickOutFn)
|
||||||
|
// document.addEventListener('click', this.clickOutFn)
|
||||||
|
this.getList()
|
||||||
|
},
|
||||||
|
onblur(el) {
|
||||||
|
this.visible = false
|
||||||
|
},
|
||||||
|
getList() {
|
||||||
|
for (let key in this.queryParams) {
|
||||||
|
this.listQuery[key] = this.queryParams[key]
|
||||||
|
}
|
||||||
|
if (this.pagination) {
|
||||||
|
this.listQuery[this.keywordKey] = this.keyword
|
||||||
|
} else {
|
||||||
|
if (this.keyword) {
|
||||||
|
this.listQuery[this.keywordKey] = this.keyword
|
||||||
|
} else {
|
||||||
|
this.listLoading = false
|
||||||
|
return //如果不分页,无keyword不查询数据(避免大数据量)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.listLoading = false
|
||||||
|
// this.listLoading = true
|
||||||
|
this.multipleSelection.length = 0
|
||||||
|
this.queryTableData(this.listQuery).then(response => {
|
||||||
|
this.list = response.rows
|
||||||
|
this.total = response.total
|
||||||
|
// this.listLoading = false
|
||||||
|
this.$nextTick(() => {
|
||||||
|
// multipleSelectionTags 引用问题,无法与table数据全等。。。
|
||||||
|
let ids = this.multipleSelectionTags.map(item => item.id)
|
||||||
|
this.list.length && this.list.forEach((item, index) => {
|
||||||
|
if (ids.includes(item.id)) {
|
||||||
|
this.$refs.comboGridTable.toggleRowSelection(item)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
queryTableData(params) {
|
||||||
|
const requestOption = {
|
||||||
|
url: this.requestUrl,
|
||||||
|
method: this.method,
|
||||||
|
}
|
||||||
|
requestOption[this.method === 'get' ? 'params' : 'data'] = params
|
||||||
|
console.log(requestOption);
|
||||||
|
|
||||||
|
return request(requestOption)
|
||||||
|
|
||||||
|
},
|
||||||
|
rowClick: function (row, column, event) {
|
||||||
|
if (this.showSelect) {
|
||||||
|
this.$refs.comboGridTable.toggleRowSelection(row);
|
||||||
|
this.$emit('input', '')
|
||||||
|
} else {
|
||||||
|
this.visible = false
|
||||||
|
this.$emit('row-select-event', row, column, event)
|
||||||
|
this.$emit('input', row[this.keywordKey])
|
||||||
|
document.removeEventListener('click', this.clickOutFn)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
clickOutFn(e) {
|
||||||
|
// console.log(e);
|
||||||
|
// console.log(e.path);
|
||||||
|
// debugger
|
||||||
|
if (e.path.some(item => item.id === this.createdId)) return
|
||||||
|
this.visible = false
|
||||||
|
document.removeEventListener('click', this.clickOutFn)
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 根据选择与上次的选择进行对比,处理tags
|
||||||
|
* todo 聚焦状态。。。触发太多,暂不处理
|
||||||
|
*/
|
||||||
|
handleSelectionChange(val) {
|
||||||
|
if (this.multipleSelection.length === 0 && val.length === 0) return
|
||||||
|
if (val.length > this.multipleSelection.length) {
|
||||||
|
for (let item of val) {
|
||||||
|
if (!this.multipleSelection.some(mitem => mitem.id === item.id)) {
|
||||||
|
let ids = this.multipleSelectionTags.map(item => item.id)
|
||||||
|
!ids.includes(item.id) && (this.multipleSelectionTags = this.multipleSelectionTags.concat(item))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (let item of this.multipleSelection) {
|
||||||
|
if (!val.some(mitem => mitem.id === item.id)) {
|
||||||
|
let index = this.multipleSelectionTags.findIndex(mitem => mitem.id === item.id)
|
||||||
|
index > -1 && this.multipleSelectionTags.splice(index, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.multipleSelection = val
|
||||||
|
this.placeholderForTag = this.multipleSelectionTags.length ? '' : this.placeholder
|
||||||
|
this.$emit('getSelect', this.multipleSelectionTags)
|
||||||
|
// this.$refs.selectInput.focus() todo..
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 处理单个复选框选择
|
||||||
|
*/
|
||||||
|
handleSingelSelectionChange() {
|
||||||
|
this.$emit('input', '')
|
||||||
|
},
|
||||||
|
handleCloseTag(item) {
|
||||||
|
this.multipleSelectionTags.splice(this.multipleSelectionTags.findIndex(mitem => mitem.id === item.id), 1)
|
||||||
|
this.$refs.comboGridTable.toggleRowSelection(item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.input-select {
|
||||||
|
display: inline-block;
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
height: auto;
|
||||||
|
min-width: 240px;
|
||||||
|
-webkit-appearance: none;
|
||||||
|
background-color: #fff;
|
||||||
|
background-image: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 1px solid #dcdfe6;
|
||||||
|
box-sizing: border-box;
|
||||||
|
color: #606266;
|
||||||
|
font-size: inherit;
|
||||||
|
outline: none;
|
||||||
|
// padding: 0 15px;
|
||||||
|
transition: border-color 0.2s cubic-bezier(0.645, 0.045, 0.355, 1);
|
||||||
|
.select-down {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
right: 10px;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
transition: all 0.5s;
|
||||||
|
&.rotate180 {
|
||||||
|
transform: translateY(-50%) rotate(180deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.tags {
|
||||||
|
// min-height: 36px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
line-height: normal;
|
||||||
|
white-space: normal;
|
||||||
|
z-index: 1;
|
||||||
|
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
:deep .el-tag {
|
||||||
|
margin-right: 3px;
|
||||||
|
margin-bottom: 1px;
|
||||||
|
}
|
||||||
|
input {
|
||||||
|
flex: 1;
|
||||||
|
border: none;
|
||||||
|
outline: none;
|
||||||
|
padding: 0;
|
||||||
|
color: #666;
|
||||||
|
font-size: 14px;
|
||||||
|
appearance: none;
|
||||||
|
height: 28px;
|
||||||
|
background-color: transparent;
|
||||||
|
&::-webkit-input-placeholder {
|
||||||
|
color: #c0c4cc;
|
||||||
|
}
|
||||||
|
&::-moz-placeholder {
|
||||||
|
/* Mozilla Firefox 19+ */
|
||||||
|
color: #c0c4cc;
|
||||||
|
}
|
||||||
|
&:-moz-placeholder {
|
||||||
|
/* Mozilla Firefox 4 to 18 */
|
||||||
|
color: #c0c4cc;
|
||||||
|
}
|
||||||
|
&:-ms-input-placeholder {
|
||||||
|
/* Internet Explorer 10-11 */
|
||||||
|
color: #c0c4cc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
230
src/components/XdSelectV2/index.vue
Normal file
230
src/components/XdSelectV2/index.vue
Normal file
@ -0,0 +1,230 @@
|
|||||||
|
<template>
|
||||||
|
<el-select
|
||||||
|
ref="select"
|
||||||
|
v-model="localValue"
|
||||||
|
class="el-select-v2"
|
||||||
|
:autocomplete="autocomplete"
|
||||||
|
:automatic-dropdown="automaticDropdown"
|
||||||
|
:size="size"
|
||||||
|
:disabled="disabled"
|
||||||
|
:clearable="clearable"
|
||||||
|
:filterable="filterable"
|
||||||
|
:allow-create="allowCreate"
|
||||||
|
:loading="loading"
|
||||||
|
:popper-class="`el-select-v2__popper ${popperClass || ''}`"
|
||||||
|
:remote="remote"
|
||||||
|
:loading-text="loadingText"
|
||||||
|
:no-match-text="noMatchText"
|
||||||
|
:no-data-text="noDataText"
|
||||||
|
:remote-method="remoteMethod"
|
||||||
|
:filter-method="filterMethod || localFilterMethod"
|
||||||
|
:multiple="multiple"
|
||||||
|
:multiple-limit="multipleLimit"
|
||||||
|
:placeholder="placeholder"
|
||||||
|
:default-first-option="defaultFirstOption"
|
||||||
|
:reserve-keyword="reserveKeyword"
|
||||||
|
:collapse-tags="collapseTags"
|
||||||
|
:popper-append-to-body="popperAppendToBody"
|
||||||
|
v-bind="$attrs"
|
||||||
|
v-on="$listeners"
|
||||||
|
>
|
||||||
|
<RecycleScroller
|
||||||
|
v-if="localOptions.length"
|
||||||
|
ref="scroller"
|
||||||
|
v-slot="{ item }"
|
||||||
|
class="scroller"
|
||||||
|
:items="localOptions"
|
||||||
|
:min-item-size="minItemSize"
|
||||||
|
:key-field="valueKey"
|
||||||
|
@visible="handleScrollerVisible"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
:key="item[valueKey]"
|
||||||
|
:value="item[valueKey]"
|
||||||
|
:label="item[labelKey]"
|
||||||
|
:disabled="item.disabled"
|
||||||
|
>
|
||||||
|
<slot name="default" :item="item" />
|
||||||
|
</el-option>
|
||||||
|
</RecycleScroller>
|
||||||
|
<template v-if="$slots.prefix" slot="prefix">
|
||||||
|
<slot name="prefix" />
|
||||||
|
</template>
|
||||||
|
<template v-if="$slots.empty" slot="empty">
|
||||||
|
<slot name="empty" />
|
||||||
|
</template>
|
||||||
|
</el-select>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { RecycleScroller } from 'vue-virtual-scroller';
|
||||||
|
import 'vue-virtual-scroller/dist/vue-virtual-scroller.css';
|
||||||
|
import isEqual from 'lodash/isEqual';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'XdSelectV2',
|
||||||
|
components: {
|
||||||
|
RecycleScroller,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
value: {
|
||||||
|
type: [Array, String, Number, Boolean, Object],
|
||||||
|
default: undefined,
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
type: Array,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
valueKey: {
|
||||||
|
type: String,
|
||||||
|
default: 'value',
|
||||||
|
},
|
||||||
|
labelKey: {
|
||||||
|
type: String,
|
||||||
|
default: 'label',
|
||||||
|
},
|
||||||
|
autocomplete: {
|
||||||
|
type: String,
|
||||||
|
default: 'off',
|
||||||
|
},
|
||||||
|
automaticDropdown: Boolean,
|
||||||
|
size: String,
|
||||||
|
disabled: Boolean,
|
||||||
|
clearable: Boolean,
|
||||||
|
filterable: Boolean,
|
||||||
|
allowCreate: Boolean,
|
||||||
|
loading: Boolean,
|
||||||
|
popperClass: String,
|
||||||
|
remote: Boolean,
|
||||||
|
loadingText: String,
|
||||||
|
noMatchText: String,
|
||||||
|
noDataText: String,
|
||||||
|
remoteMethod: Function,
|
||||||
|
filterMethod: Function,
|
||||||
|
multiple: Boolean,
|
||||||
|
multipleLimit: {
|
||||||
|
type: Number,
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
placeholder: {
|
||||||
|
type: String,
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
|
defaultFirstOption: Boolean,
|
||||||
|
reserveKeyword: Boolean,
|
||||||
|
collapseTags: Boolean,
|
||||||
|
popperAppendToBody: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
minItemSize: {
|
||||||
|
type: Number,
|
||||||
|
default: 34,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
localValue: '',
|
||||||
|
localOptions: [],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.updateSelectedLabel();
|
||||||
|
if (this.$refs.select) {
|
||||||
|
this.$watch(() => this.$refs.select.visible, value => {
|
||||||
|
if (value) {
|
||||||
|
this.updateOptions();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
updateSelectedLabel() {
|
||||||
|
if (!this.$refs.select) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const { setSelected, cachedOptions } = this.$refs.select;
|
||||||
|
const values = this.multiple ? this.localValue : [this.localValue];
|
||||||
|
const selectedOptions = this.options.filter(option => values?.includes(option[this.valueKey])).map(option => ({
|
||||||
|
value: option[this.valueKey],
|
||||||
|
currentLabel: option[this.labelKey],
|
||||||
|
}));
|
||||||
|
selectedOptions.forEach(option => {
|
||||||
|
const cachedOption = cachedOptions.find(cachedOption => cachedOption.value === option.value);
|
||||||
|
if (cachedOption) {
|
||||||
|
cachedOptions.splice(cachedOptions.indexOf(cachedOption), 1, option);
|
||||||
|
} else {
|
||||||
|
cachedOptions.push(option);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
setSelected();
|
||||||
|
},
|
||||||
|
handleScrollerVisible() {
|
||||||
|
const firstValue = this.multiple ? this.localValue?.[0] : this.localValue;
|
||||||
|
const index = this.localOptions.findIndex(option => option[this.valueKey] === firstValue);
|
||||||
|
this.$refs.scroller.scrollToItem(index);
|
||||||
|
},
|
||||||
|
localFilterMethod(query) {
|
||||||
|
this.localOptions = this.options.filter(option => option[this.labelKey].toLowerCase().includes(query.toLowerCase()));
|
||||||
|
},
|
||||||
|
updateOptions() {
|
||||||
|
this.localOptions = this.options;
|
||||||
|
},
|
||||||
|
focus() {
|
||||||
|
this.$refs.select.focus();
|
||||||
|
},
|
||||||
|
blur() {
|
||||||
|
this.$refs.select.blur();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
value: {
|
||||||
|
handler() {
|
||||||
|
if (!isEqual(this.value, this.localValue)) {
|
||||||
|
this.localValue = this.value;
|
||||||
|
this.updateSelectedLabel();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
deep: true,
|
||||||
|
immediate: true,
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
handler() {
|
||||||
|
this.updateOptions();
|
||||||
|
const inputs = this.$el.querySelectorAll('input');
|
||||||
|
if ([].indexOf.call(inputs, document.activeElement) === -1) {
|
||||||
|
this.updateSelectedLabel();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
deep: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.el-select-v2__popper {
|
||||||
|
.scroller {
|
||||||
|
max-height: 238px;
|
||||||
|
|
||||||
|
&::-webkit-scrollbar {
|
||||||
|
width: 6px;
|
||||||
|
height: 6px;
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::-webkit-scrollbar-thumb {
|
||||||
|
border-radius: 4px;
|
||||||
|
background-color: rgba(144, 147, 153, .3);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: rgba(144, 147, 153, .5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-scrollbar__bar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Loading…
Reference in New Issue
Block a user