262 lines
6.7 KiB
Vue
262 lines
6.7 KiB
Vue
![]() |
<template>
|
||
|
<view v-if="show" class="modal-mask">
|
||
|
<view class="modal-container">
|
||
|
<!-- 头部 -->
|
||
|
<view class="modal-header">
|
||
|
<text class="back-btn" @click="handleClose">
|
||
|
<uni-icons type="left" size="24"></uni-icons>
|
||
|
</text>
|
||
|
<text class="modal-title">{{ title }}</text>
|
||
|
<view class="back-btn"></view>
|
||
|
</view>
|
||
|
|
||
|
<!-- 内容区域 -->
|
||
|
<view class="content-wrapper">
|
||
|
<!-- 左侧筛选类别 -->
|
||
|
<scroll-view class="filter-nav" scroll-y>
|
||
|
<view
|
||
|
v-for="(item, index) in filterOptions"
|
||
|
:key="index"
|
||
|
class="nav-item"
|
||
|
: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"
|
||
|
: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 class="modal-footer">
|
||
|
<button class="footer-btn" type="default" @click="handleClear">清除</button>
|
||
|
<button class="footer-btn" type="primary" @click="handleConfirm">确认</button>
|
||
|
</view>
|
||
|
</view>
|
||
|
</view>
|
||
|
</template>
|
||
|
|
||
|
<script setup>
|
||
|
import { ref, reactive, computed, onBeforeMount } from 'vue';
|
||
|
import useDictStore from '@/stores/useDictStore';
|
||
|
const { getTransformChildren } = useDictStore();
|
||
|
const props = defineProps({
|
||
|
show: Boolean,
|
||
|
title: {
|
||
|
type: String,
|
||
|
default: '筛选',
|
||
|
},
|
||
|
area: {
|
||
|
type: Boolean,
|
||
|
default: true,
|
||
|
},
|
||
|
});
|
||
|
|
||
|
const emit = defineEmits(['confirm', 'close', 'update:show']);
|
||
|
|
||
|
// 当前激活的筛选类别
|
||
|
const activeTab = ref('');
|
||
|
|
||
|
// 存储已选中的值 {key: [selectedValues]}
|
||
|
const selectedValues = reactive({});
|
||
|
|
||
|
const filterOptions = ref([]);
|
||
|
|
||
|
onBeforeMount(() => {
|
||
|
const arr = [
|
||
|
getTransformChildren('education', '学历要求'),
|
||
|
getTransformChildren('experience', '工作经验'),
|
||
|
getTransformChildren('scale', '公司规模'),
|
||
|
];
|
||
|
if (props.area) {
|
||
|
arr.push(getTransformChildren('area', '区域'));
|
||
|
}
|
||
|
filterOptions.value = arr;
|
||
|
activeTab.value = 'education';
|
||
|
});
|
||
|
|
||
|
// 处理选项选择
|
||
|
const handleSelect = (key, e) => {
|
||
|
selectedValues[key] = e.detail.value.map(String);
|
||
|
};
|
||
|
const scrollTo = (key) => {
|
||
|
activeTab.value = key;
|
||
|
};
|
||
|
// 清除所有选择
|
||
|
const handleClear = () => {
|
||
|
Object.keys(selectedValues).forEach((key) => {
|
||
|
selectedValues[key] = [];
|
||
|
});
|
||
|
};
|
||
|
|
||
|
// 确认筛选
|
||
|
function handleConfirm() {
|
||
|
emit('confirm', selectedValues);
|
||
|
handleClose();
|
||
|
}
|
||
|
|
||
|
// 关闭弹窗
|
||
|
const handleClose = () => {
|
||
|
emit('update:show', false);
|
||
|
emit('close');
|
||
|
};
|
||
|
</script>
|
||
|
|
||
|
<style lang="scss" scoped>
|
||
|
.modal-mask {
|
||
|
position: fixed;
|
||
|
top: 0;
|
||
|
left: 0;
|
||
|
width: 100vw;
|
||
|
height: 100vh;
|
||
|
background-color: rgba(0, 0, 0, 0.5);
|
||
|
display: flex;
|
||
|
justify-content: center;
|
||
|
align-items: center;
|
||
|
z-index: 9999;
|
||
|
}
|
||
|
|
||
|
.modal-container {
|
||
|
width: 100vw;
|
||
|
height: 100vh;
|
||
|
background-color: #fff;
|
||
|
overflow: hidden;
|
||
|
display: flex;
|
||
|
flex-direction: column;
|
||
|
}
|
||
|
|
||
|
.modal-header {
|
||
|
height: 88rpx;
|
||
|
display: flex;
|
||
|
align-items: center;
|
||
|
justify-content: space-between;
|
||
|
padding: 0 32rpx;
|
||
|
border-bottom: 1rpx solid #eee;
|
||
|
position: relative;
|
||
|
|
||
|
.back-btn {
|
||
|
font-size: 36rpx;
|
||
|
width: 48rpx;
|
||
|
}
|
||
|
|
||
|
.modal-title {
|
||
|
font-size: 32rpx;
|
||
|
font-weight: 500;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
.content-wrapper {
|
||
|
flex: 1;
|
||
|
display: flex;
|
||
|
overflow: hidden;
|
||
|
}
|
||
|
|
||
|
.filter-nav {
|
||
|
width: 200rpx;
|
||
|
background-color: #f5f5f5;
|
||
|
|
||
|
.nav-item {
|
||
|
height: 100rpx;
|
||
|
padding: 0 20rpx;
|
||
|
line-height: 100rpx;
|
||
|
font-size: 28rpx;
|
||
|
color: #666;
|
||
|
|
||
|
&.active {
|
||
|
background-color: #fff;
|
||
|
color: #007aff;
|
||
|
font-weight: bold;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
.filter-content {
|
||
|
flex: 1;
|
||
|
padding: 20rpx;
|
||
|
|
||
|
.content-item {
|
||
|
margin-top: 40rpx;
|
||
|
.item-title {
|
||
|
width: 281rpx;
|
||
|
height: 52rpx;
|
||
|
font-family: Inter, Inter;
|
||
|
font-weight: 400;
|
||
|
font-size: 35rpx;
|
||
|
color: #000000;
|
||
|
line-height: 41rpx;
|
||
|
text-align: left;
|
||
|
font-style: normal;
|
||
|
text-transform: none;
|
||
|
}
|
||
|
}
|
||
|
.content-item:first-child {
|
||
|
margin-top: 0rpx;
|
||
|
}
|
||
|
|
||
|
.check-content {
|
||
|
display: grid;
|
||
|
grid-template-columns: 50% 50%;
|
||
|
place-items: center;
|
||
|
|
||
|
.checkbox-item {
|
||
|
display: flex;
|
||
|
align-items: center;
|
||
|
width: 228rpx;
|
||
|
height: 65rpx;
|
||
|
margin: 20rpx 20rpx 0 0;
|
||
|
text-align: center;
|
||
|
background-color: #d9d9d9;
|
||
|
|
||
|
.option-label {
|
||
|
font-size: 28rpx;
|
||
|
width: 100%;
|
||
|
}
|
||
|
}
|
||
|
.checkedstyle {
|
||
|
background-color: #007aff;
|
||
|
color: #ffffff;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
.modal-footer {
|
||
|
height: 100rpx;
|
||
|
display: flex;
|
||
|
border-top: 1rpx solid #eee;
|
||
|
|
||
|
.footer-btn {
|
||
|
flex: 1;
|
||
|
margin: 0;
|
||
|
border-radius: 0;
|
||
|
line-height: 100rpx;
|
||
|
|
||
|
&:first-child {
|
||
|
border-right: 1rpx solid #eee;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
</style>
|