Files
ks-app-employment-service/components/selectFilter/selectFilter.vue
2025-06-26 08:56:42 +08:00

375 lines
10 KiB
Vue

<template>
<uni-popup
ref="popup"
type="bottom"
borderRadius="10px 10px 0 0"
background-color="#FFFFFF"
@maskClick="maskClickFn"
:mask-click="maskClick"
class="popup-fix"
>
<view class="popup-content">
<view class="popup-header">
<view class="btn-cancel" @click="cancel">取消</view>
<view class="title">
<text>{{ title }}</text>
<text style="color: #256bfa">·{{ count }}</text>
</view>
<view class="btn-confirm" @click="confirm">&nbsp;&nbsp;</view>
</view>
<view class="popup-list">
<view class="content-wrapper">
<!-- 左侧筛选类别 -->
<scroll-view class="filter-nav" scroll-y>
<view
v-for="(item, index) in filterOptions"
:key="index"
class="nav-item button-click"
:class="{ active: activeTab === item.key }"
@click="scrollTo(item.key)"
>
{{ item.label }}
</view>
</scroll-view>
<!-- 右侧筛选内容 -->
<scroll-view class="filter-content" :scroll-into-view="activeTab" scroll-y>
<template v-for="(item, index) in filterOptions" :key="index">
<view class="content-item">
<view class="item-title" :id="item.key">{{ item.label }}</view>
<checkbox-group class="check-content" @change="(e) => handleSelect(item.key, e)">
<label
v-for="option in item.options"
:key="option.value"
class="checkbox-item button-click"
:class="{
checkedstyle: selectedValues[item.key]?.includes(String(option.value)),
}"
>
<checkbox
style="display: none"
:value="String(option.value)"
:checked="selectedValues[item.key]?.includes(String(option.value))"
/>
<text class="option-label">{{ option.label }}</text>
</label>
</checkbox-group>
</view>
</template>
</scroll-view>
</view>
</view>
<view class="popup-bottom">
<view class="btn-cancel btn-feel" @click="cleanup">清除</view>
<view class="btn-confirm btn-feel" @click="confirm">确认</view>
</view>
</view>
</uni-popup>
</template>
<script setup>
import { ref, reactive, nextTick, onBeforeMount } from 'vue';
import useDictStore from '@/stores/useDictStore';
const { getTransformChildren } = useDictStore();
const area = ref(true);
const maskClick = ref(false);
const maskClickFn = ref(null);
const title = ref('标题');
const confirmCallback = ref(null);
const cancelCallback = ref(null);
const changeCallback = ref(null);
const popup = ref(null);
const selectedValues = reactive({});
const count = ref(0);
// 当前激活的筛选类别
const activeTab = ref('');
const filterOptions = ref([]);
const open = (newConfig = {}) => {
const { title: configTitle, success, cancel, change, data, maskClick: configMaskClick = false } = newConfig;
// reset();
if (configTitle) title.value = configTitle;
if (typeof success === 'function') confirmCallback.value = success;
if (typeof cancel === 'function') cancelCallback.value = cancel;
if (typeof change === 'function') changeCallback.value = change;
if (configMaskClick) {
maskClick.value = configMaskClick;
maskClickFn.value = cancel;
}
getoptions();
nextTick(() => {
popup.value?.open();
});
};
const close = () => {
popup.value?.close();
};
const cancel = () => {
handleClick(cancelCallback.value);
};
const confirm = () => {
handleClick(confirmCallback.value);
};
const handleClick = async (callback) => {
if (typeof callback !== 'function') {
close();
return;
}
try {
const result = await callback(selectedValues);
if (result !== false) close();
} catch (error) {
console.error('confirmCallback 执行出错:', error);
}
};
// 处理选项选择
const handleSelect = (key, e) => {
selectedValues[key] = e.detail.value.map(String);
let va = 0;
for (const [key, value] of Object.entries(selectedValues)) {
va += value.length;
}
count.value = va;
};
const cleanup = () => {
Object.keys(selectedValues).forEach((key) => {
delete selectedValues[key];
});
};
const scrollTo = (key) => {
activeTab.value = key;
};
function getoptions() {
const arr = [
getTransformChildren('education', '学历要求'),
getTransformChildren('experience', '工作经验'),
getTransformChildren('scale', '公司规模'),
];
if (area.value) {
arr.push(getTransformChildren('area', '区域'));
}
filterOptions.value = arr;
activeTab.value = 'education';
}
const reset = () => {
maskClick.value = false;
confirmCallback.value = null;
cancelCallback.value = null;
changeCallback.value = null;
Object.keys(selectedValues).forEach((key) => delete selectedValues[key]);
};
// 暴露方法给父组件
defineExpose({
open,
close,
});
</script>
<style lang="scss" scoped>
.popup-fix {
position: fixed !important;
left: 0;
right: 0;
bottom: 0;
top: 0;
height: 100vh;
z-index: 9999;
}
.popup-content {
color: #000000;
height: 80vh;
}
.popup-bottom {
padding: 40rpx 28rpx 20rpx 28rpx;
display: flex;
justify-content: space-between;
.btn-cancel {
font-weight: 400;
font-size: 32rpx;
color: #666d7f;
line-height: 90rpx;
width: 33%;
min-width: 222rpx;
height: 90rpx;
background: #f5f5f5;
border-radius: 12rpx 12rpx 12rpx 12rpx;
text-align: center;
}
.btn-confirm {
font-weight: 400;
font-size: 32rpx;
color: #ffffff;
text-align: center;
width: 67%;
height: 90rpx;
margin-left: 28rpx;
line-height: 90rpx;
background: #256bfa;
min-width: 444rpx;
border-radius: 12rpx 12rpx 12rpx 12rpx;
}
}
.popup-list {
display: flex;
flex-direction: row;
flex-wrap: nowrap;
align-items: center;
justify-content: space-evenly;
height: calc(80vh - 100rpx - 150rpx);
overflow: hidden;
.picker-view {
width: 100%;
height: 500rpx;
margin-top: 20rpx;
.uni-picker-view-mask {
background: rgba(0, 0, 0, 0);
}
.item {
line-height: 84rpx;
height: 84rpx;
text-align: center;
font-weight: 400;
font-size: 32rpx;
color: #cccccc;
}
.item-active {
color: #333333;
}
.uni-picker-view-indicator:after {
border-color: #e3e3e3;
}
.uni-picker-view-indicator:before {
border-color: #e3e3e3;
}
}
// .list {
// .row {
// font-weight: 400;
// font-size: 32rpx;
// color: #333333;
// line-height: 84rpx;
// text-align: center;
// }
// }
}
.popup-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 40rpx 40rpx 10rpx 40rpx;
.title {
font-weight: 500;
font-size: 36rpx;
color: #333333;
text-align: center;
}
.btn-cancel {
font-weight: 400;
font-size: 32rpx;
color: #666d7f;
line-height: 38rpx;
}
.btn-confirm {
font-weight: 400;
font-size: 32rpx;
color: #256bfa;
}
}
.content-wrapper {
flex: 1;
display: flex;
overflow: hidden;
height: 100%;
}
.filter-nav {
width: 200rpx;
background-color: #ffffff;
.nav-item {
height: 100rpx;
line-height: 100rpx;
text-align: center;
font-weight: 400;
font-size: 28rpx;
color: #666d7f;
&.active {
font-weight: 500;
font-size: 28rpx;
color: #256bfa;
}
}
}
.filter-content {
flex: 1;
padding: 20rpx;
background-color: #f6f6f6;
.content-item {
margin-top: 30rpx;
.item-title {
font-weight: 400;
font-size: 28rpx;
color: #333333;
margin-bottom: 15rpx;
}
}
.content-item:first-child {
margin-top: 0rpx;
}
.check-content {
display: grid;
gap: 16rpx;
grid-template-columns: repeat(auto-fill, minmax(180rpx, 1fr));
place-items: stretch;
.checkbox-item {
display: flex;
align-items: center;
text-align: center;
background-color: #d9d9d9;
min-width: 0;
padding: 0 10rpx;
height: 80rpx;
background: #e8eaee;
border-radius: 12rpx 12rpx 12rpx 12rpx;
.option-label {
font-size: 28rpx;
width: 100%;
white-space: nowrap;
overflow: hidden;
}
}
.checkedstyle {
height: 76rpx;
background: rgba(37, 107, 250, 0.06);
border-radius: 12rpx 12rpx 12rpx 12rpx;
border: 2rpx solid #256bfa;
color: #256bfa;
}
}
}
</style>