实现动态“展开”,取值自定义。
封装组件
<template> <div class='w100'> <div class="w100 br-2 search-bar-comp-box show-flex-box-c" :class="{'is-expanded': isExpand}"> <dt class="collapse simple-search-bar show-flex-box-r w100 p-20 pb-0 normal-bg"> <ul class="show-flex-box-c w100"> <li class="show-flex-box-r" :class="`${isExpand ? 'mb-8' : 'mb-4'} ${item.type == 'timePicker' ? 'mb-24' : ''}`" v-for="(item, index) in searchList" :key="index" > <span class="title mr-16 white-space" :class="{'time-picker-title': item.type == 'timePicker'}">{{item.title}}</span> <ol class="search-option-list show-flex-box-r" v-if="item.type != 'timePicker'"> <li :class="{'selected': checkIsSelectedAll(item.options)}" @click="!checkIsSelectedAll(item.options) && chooseAllType(item, index)" > <strong>全部</strong> </li> <li :class="{'selected': subItem.selected}" v-for="(subItem, subIndex) in item.options" :key="subIndex" @click="chooseThisItem(index, subIndex, item, subItem)" > <strong>{{subItem.name}}</strong> </li> </ol> <div v-if="item.type == 'timePicker'" class="fs-0 time-picker-bar"> <el-date-picker style="width: 200px;" type="date" placeholder="请选择预警日期" v-model="choosedTime" :editable="false" size="small" clearable format="yyyy-MM-dd" value-format="timestamp" @change="changeChoosedTime" ></el-date-picker> </div> <div class="handle-bar show-flex-box-r" v-if="item.type != 'timePicker'"> <div v-if="item.showBtn" class="expand-btn-bar" :class="{ 'mr-20': index == 0 && !isExpand }" > <span v-if="item.isExpand" @click="changeClasslyItemExpandStatus(false, index)">收起</span> <span v-else @click="changeClasslyItemExpandStatus(true, index)">展开</span> </div> <div class="search-btn-bar" v-if="index == 0 && !isExpand"> <div class="search" @click="clickBtnBar('search')">查 询</div> <div class="reset" @click="clickBtnBar('reset')">重 置</div> </div> </div> </li> </ul> <div class="search-btn-bar mb-20" v-show="isExpand"> <div class="search" @click="clickBtnBar('search')">查 询</div> <div class="reset" @click="clickBtnBar('reset')">重 置</div> </div> <div class="expand-btn" @click="changeExpandStatus()"> <img :src="expandIconUrl" > </div> </dt> <dd v-if="selectedList.length > 0" class="border-box w100 selected-item-list show-flex-box-r mt-24 br-2 normal-bg pt-20 pl-20" > <strong class="fs-0 white-space">已选择项</strong> <ul class="show-flex-box-r"> <li class="show-flex-box-r mr-20 mb-20" v-for="(item, index) in selectedList" :key="index" > <strong class="mr-6 white-space">{{item.name}}</strong> <em class="fs-0 cursor-pointer" @click="removeChoosedTag(item, index)"></em> </li> </ul> </dd> </div> </div> </template> <script> import $ from "jquery"; import '@/../static/js/jquery.actual.js'; const expandIconUrl = require('@/assets/images/cockpit-risk/expand-icon.png'); import CommonUtils from '@/utils/commonUtils'; export default { props: { showList: { type: Array, default: [], }, }, data() { return { expandIconUrl, isExpand: false, searchList: [], selectedList: [], choosedTime: '', classlyItemHeightList: [], }; }, computed: { sidebar() { return this.$store.state.app.sidebar; }, }, watch: { showList(){ this.setShowSearchList(); }, sidebar: { handler(newName, oldName) { this.windowResizeToResetCheck(); }, deep: true } }, mounted() { window.onresize = () => { this.windowResizeToResetCheck(); }; this.setShowSearchList(); }, methods: { windowResizeToResetCheck(){ setTimeout(() => { this.resetCheck(); }, 300); }, resetCheck(){ $(`.simple-search-bar > ul > li`).each(function(i){ let olDom = `.simple-search-bar > ul > li:eq(${i}) > .search-option-list`; $(olDom).css('max-height', 'unset'); $(olDom).css('overflow-y', 'unset'); }); this.setCheckItemHeightIsOver(); }, setShowSearchList(notCheck){ let circulationList = []; if (notCheck) { circulationList = this.searchList; } else { circulationList = this.showList; } let list = circulationList.map(item => { let options = item.options || []; let newOptions = options.map(subItem => { return Object.assign(subItem, { selected: false }); }); const obj = { ...item, showBtn: item.showBtn || false, isExpand: item.isExpand || false, }; obj.options = newOptions; return obj; }); this.searchList = list; if (!notCheck) { this.setCheckItemHeightIsOver(); } }, // 检查是否要显示'展开'或'收起'按钮栏 setCheckItemHeightIsOver(){ let list = JSON.parse(JSON.stringify(this.searchList)); const _this = this; _this.classlyItemHeightList = []; this.$nextTick(()=>{ $(`.simple-search-bar > ul > li`).each(function(i){ let classlyItemHeight = $(this).actual('height'); _this.classlyItemHeightList.push(classlyItemHeight); if (classlyItemHeight > 40) { list[i].showBtn = true; } else { list[i].showBtn = false; } _this.changeClasslyItemExpandStatus(list[i].isExpand, i); // let olDom = `.simple-search-bar > ul > li:eq(${i}) > .search-option-list`; // if (list[i].isExpand) { // $(olDom).css('max-height', '200px'); // unset // $(olDom).css('overflow-y', 'unset'); // } else { // $(olDom).css('max-height', '40px'); // $(olDom).css('overflow-y', 'hidden'); // } }); this.searchList = list; }); }, // 展开和收起所有筛选栏 changeExpandStatus(){ $(`.simple-search-bar > ul > li`).each(function(i){ let olDom = `.simple-search-bar > ul > li:eq(${i}) > .search-option-list`; $(olDom).css('max-height', 'unset'); $(olDom).css('overflow-y', 'unset'); }); this.isExpand = !this.isExpand; this.setCheckItemHeightIsOver(true); }, // 点击展开或者收起的按钮 changeClasslyItemExpandStatus(isExpand, index){ if (index == 0) { if (isExpand) { $('.simple-search-bar').css('max-height', this.classlyItemHeightList[0] + 24 + 'px'); } else { $('.simple-search-bar').css('max-height', '64px'); } } let curClickLiDom = `.simple-search-bar > ul > li:eq(${index}) > .search-option-list`; if (isExpand) { $(curClickLiDom).css('max-height', '800px'); // unset 200px $(curClickLiDom).css('overflow-y', 'unset'); } else { $(curClickLiDom).css('max-height', '40px'); $(curClickLiDom).css('overflow-y', 'hidden'); } this.searchList[index].isExpand = isExpand; }, // 是否是选中全部 checkIsSelectedAll(options = [], returnList = false){ let list = options || []; let result = list.filter(item => { return item.selected; }); return returnList ? result : result.length == 0; }, // 删除已选择项 removeChoosedTag(tag, index){ this.selectedList.splice(index, 1); let list = this.searchList; let firstIndex, secondIndex, optionItem; for (let i = 0; i < list.length; i++) { const item = list[i]; if (tag.type == item.type) { firstIndex = i; const options = item.options || []; for (let j = 0; j < options.length; j++) { const subItem = options[j]; if (subItem.id == tag.id) { secondIndex = j; optionItem = subItem; break; } } break; } } this.changeThisItemSelectedStatus(firstIndex, secondIndex, optionItem); }, // 日期选择器 changeChoosedTime(){}, // 选择全部的这一分类选项 chooseAllType(item, index){ let list = item.options || []; let newOptions = list.map(item=>{ return Object.assign(item, { selected: false }); }); this.searchList[index].options = newOptions; this.deleteThisTypeOptions(item.type); }, // 选中某一个选项 chooseThisItem(index, subIndex, item, subItem){ const flag = !subItem.selected; this.changeThisItemSelectedStatus(index, subIndex, subItem); this.setSelectedItem(flag, item.type, subItem.id, subItem.name); }, changeThisItemSelectedStatus(index, subIndex, subItem){ const flag = !subItem.selected; this.searchList[index].options[subIndex].selected = flag; this.$forceUpdate(); }, setSelectedItem(isSelected, type, id, name){ if (isSelected) { this.selectedList.push({ type: type, id: id, name: name, }); } else { let list = this.selectedList; let index = list.findIndex(item => { return item.type == type && item.id == id; }); this.selectedList.splice(index, 1); } }, // 删除该分类的选项 deleteThisTypeOptions(type){ this.selectedList = this.selectedList.filter(item=>{ return item.type != type; }) }, // 查询 重置 clickBtnBar(type){ if (type == 'search') { this.toSearch(); } else { this.toReset(); } }, // 查询 toSearch(){ let params = {}; let list = this.searchList; list.forEach(item => { let options = item.options || []; let selectedOptions = this.checkIsSelectedAll(options, true); // 是否有选中改分类的项 if (selectedOptions.length > 0) { let ids = CommonUtils.getIdsFromList(selectedOptions, item.field || 'id'); params[item.type] = ids; } }); if (this.choosedTime) { params.warningDate = this.choosedTime; } this.sendInfoOutside(params); }, // 重置 toReset(){ this.setShowSearchList(true); this.resetCheck(); this.choosedTime = ''; this.selectedList = []; this.sendInfoOutside({}); }, sendInfoOutside(info = {}){ this.$emit('handle-search', info); }, }, } </script> <style lang='scss' scoped> .search-bar-comp-box{ .simple-search-bar{ position: relative; max-height: 64px; transition: all 0.3s linear; > ul{ > li{ // display: none; visibility: hidden; opacity: 0; transition: all 0.3s linear; > .time-picker-title{ line-height: 32px; height: 32px; } } > li:first-child{ // display: flex; visibility: visible; opacity: 1; } } .search-option-list{ // width: 0; flex-grow: 1; flex-wrap: wrap; transition: all 0.3s linear; > li{ display: flex; margin-bottom: 16px; margin-right: 24px; > strong{ cursor: pointer; box-sizing: border-box; white-space: nowrap; border: 1px solid #C9CDD4; border-radius: 2px; padding: 3px 12px; font-family: 'PingFang SC'; font-style: normal; font-weight: 500; font-size: 12px; line-height: 16px; color: #4E5969; } } > .selected{ > strong{ border: 1px solid transparent; background: #00A3E0; color: #FFFFFF; } } } .expand-btn{ display: flex; align-items: center; justify-content: center; cursor: pointer; position: absolute; bottom: -18px; left: 50%; width: 100px; height: 18px; background: url('../../../assets/images/cockpit-risk/expand-bg-icon.png') center center no-repeat; background-size: 100% 100%; > img{ width: 10px; height: 12px; transition: all ease-in-out 0.3s; transform: rotate(0deg); user-select:none; } } } .selected-item-list{ overflow: hidden; > strong{ width: 76px; font-family: 'PingFang SC'; font-style: normal; font-weight: 400; font-size: 14px; line-height: 24px; color: #3E3E3E; } > ul{ flex-wrap: wrap; margin-right: -20px; > li{ padding: 4px 8px; align-items: center; background: rgba(0, 163, 224, 0.08); border-radius: 2px; > strong{ font-family: 'PingFang SC'; font-style: normal; font-weight: 600; font-size: 12px; line-height: 16px; color: #00A3E0; } > em{ width: 12px; height: 12px; background: url('../../../assets/images/cockpit-risk/close-icon.png') center center no-repeat; background-size: 100% 100%; } } } } } .is-expanded{ .simple-search-bar{ flex-direction: column; max-height: 3000px !important; > ul{ > li{ // display: flex; visibility: visible; opacity: 1; } } } > .simple-search-bar{ > .expand-btn{ > img{ transform: rotate(180deg); } } } } .title{ // width: 115px; width: 130px; flex-shrink: 0; font-family: 'PingFang SC'; font-style: normal; font-weight: 400; font-size: 14px; line-height: 24px; height: 24px; color: #3E3E3E; } .search-btn-bar{ display: flex; > div{ cursor: pointer; display: flex; flex-shrink: 0; justify-content: center; align-items: center; width: 78px; height: 24px; border-radius: 2px; font-family: 'PingFang SC'; font-style: normal; font-weight: 400; font-size: 14px; line-height: 20px; } > .search{ margin-right: 12px; background: #012169; color: #FFFFFF; } > .reset{ background: #E5E6EB; color: #012169; } } .expand-btn-bar{ cursor: pointer; white-space: nowrap; font-family: 'PingFang SC'; font-style: normal; font-weight: 500; font-size: 12px; line-height: 24px; color: #00A3E0; } .time-picker-bar ::v-deep .el-input__prefix{ left: unset; top: 9px; right: 9px; width: 14px; height: 14px; background: url('../../../assets/images/cockpit-risk/date-picker-icon.png') center center no-repeat; background-size: 100% 100%; } .time-picker-bar ::v-deep .el-input__suffix{ right: 24px; } .time-picker-bar ::v-deep .el-icon-date:before{ content: ''; } .time-picker-bar ::v-deep .el-input__inner{ border: 1px solid #E5E6EB; background-color: #F8F9FB; border-radius: 0px; padding-left: 12px; padding-right: 0px; font-family: 'PingFang SC'; font-style: normal; font-weight: 400; font-size: 14px; line-height: 20px; letter-spacing: -0.01em; color: #86909C; } </style>
getIdsFromList(list, field = 'id') { let result = []; list.forEach(item => { result.push(item[field]); }); return result; },
使用组件
<template>
<div class='demo'>
<search-bar :showList="searchList" @handle-search="handleSearch" />
</div>
</template>
<script>
import searchBar from './components/search-bar-comp';
export default {
components: {
searchBar
},
data() {
return {
searchFieldList: [
{ field: 'industry', dataFixed: false, name: '行业分类', searchField: 'industry', valueField: 'code', },
{ field: 'area', dataFixed: false, name: '所属区域', searchField: 'area', valueField: 'code', },
{ field: 'conceptualBlock', dataFixed: false, name: '概念板块', searchField: 'concept', valueField: 'name', },
{ field: 'shortTermRiskLevel', dataFixed: false, name: '短期风险等级', searchField: 'shortTermRiskLevel', valueField: 'code', },
{ field: 'longTermRiskLevel', dataFixed: false, name: '长期风险等级', searchField: 'longTermRiskLevel', valueField: 'code', },
{ field: 'isTwoFinancialTargets', dataFixed: true, name: '是否交易所两融标的', searchField: 'isTwoFinancialTargets', options: [{ id: 1, name: '是', }, { id: 0, name: '否', },], valueField: 'id', },
{ field: 'marginSecurities', dataFixed: true, name: '保证金证券', searchField: 'marginSecurities', options: [{ id: 1, name: '是', }, { id: 0, name: '否', },], valueField: 'id', },
{ field: 'tradingMarket', dataFixed: false, name: '交易市场', searchField: 'tradingMarket', valueField: 'name', },
{ field: 'timePicker', dataFixed: true, name: '预警日期', searchField: 'timePicker', valueField: 'id', },
],
searchList: [],
data: { //后端给的数据
"code": 0,
"message": "成功",
"data": {
"industry": [
{
"id": 1,
"type": 1,
"name": "医药生物",
"code": "2",
"createdAt": 1676454312000,
"updatedAt": 1676454312000
},
],
"area": [],
"conceptualBlock": [],
"shortTermRiskLevel": [],
"longTermRiskLevel": [],
"tradingMarket": []
}
}
};
},
mounted() {
this.configData();
},
methods: {
configData() {
this.reqFilterList();
},
// 列表页筛选项
reqFilterList() {
let list = [];
for (let i = 0; i < this.searchFieldList.length; i++) {
const item = this.searchFieldList[i];
let obj = {
title: item.name,
type: item.searchField,
field: item.valueField,
asdasd: '123',
options: [],
};
if (item.dataFixed) {
obj.options = item.options || [];
} else {
obj.options = this.data[item.field] || [];
}
list.push(obj);
}
this.searchList = list;
},
handleSearch(params){
this.filterForm = params;
// 调接口
},
},
}
</script>