Files
ks-app-employment-service/components/new-filter-page/new-filter-page.vue
2026-07-15 20:36:23 +08:00

466 lines
13 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view v-if="show" class="filter-container">
<!-- 头部 -->
<view class="filter-header">
<view class="back-btn" @click="handleClose">
<uni-icons type="left" size="24" color="#333"></uni-icons>
</view>
<text class="filter-title">筛选</text>
<view class="back-btn"></view>
</view>
<!-- 标签页 + 内容区域 -->
<view class="filter-body">
<!-- 标签页 -->
<view class="filter-tabs">
<view
v-for="(tab, index) in tabs"
:key="index"
class="tab-item"
:class="{ active: activeTab === tab.key }"
@click="activeTab = tab.key"
>
{{ tab.label }}
</view>
</view>
<!-- 内容区域 -->
<view class="filter-content">
<!-- 学历要求 -->
<view v-if="activeTab === 'education'" class="content-section">
<radio-group @change="(e) => handleSelect('education', e)">
<label
v-for="option in educationOptions"
:key="option.value"
class="radio-item"
:class="{ checked: selectedValues['education'] === String(option.value) }"
>
<radio
:value="String(option.value)"
:checked="selectedValues['education'] === String(option.value)"
/>
<text class="option-label">{{ option.label }}</text>
</label>
</radio-group>
</view>
<!-- 工作经验 -->
<view v-if="activeTab === 'experience'" class="content-section">
<radio-group @change="(e) => handleSelect('experience', e)">
<label
v-for="option in experienceOptions"
:key="option.value"
class="radio-item"
:class="{ checked: selectedValues['experience'] === String(option.value) }"
>
<radio
:value="String(option.value)"
:checked="selectedValues['experience'] === String(option.value)"
/>
<text class="option-label">{{ option.label }}</text>
</label>
</radio-group>
</view>
<!-- 公司规模 -->
<view v-if="activeTab === 'scale'" class="content-section">
<radio-group @change="(e) => handleSelect('scale', e)">
<label
v-for="option in scaleOptions"
:key="option.value"
class="radio-item"
:class="{ checked: selectedValues['scale'] === String(option.value) }"
>
<radio
:value="String(option.value)"
:checked="selectedValues['scale'] === String(option.value)"
/>
<text class="option-label">{{ option.label }}</text>
</label>
</radio-group>
</view>
<!-- 岗位类型 -->
<view v-if="activeTab === 'jobType'" class="content-section">
<radio-group @change="(e) => handleSelect('jobType', e)">
<label
v-for="option in jobTypeOptions"
:key="option.value"
class="radio-item"
:class="{ checked: selectedValues['jobType'] === String(option.value) }"
>
<radio
:value="String(option.value)"
:checked="selectedValues['jobType'] === String(option.value)"
/>
<text class="option-label">{{ option.label }}</text>
</label>
</radio-group>
</view>
<!-- 地区多选 -->
<view v-if="activeTab === 'area'" class="content-section">
<label
v-for="option in areaOptions"
:key="option.value"
class="radio-item"
:class="{ checked: selectedAreaValues.includes(String(option.value)) }"
@click="toggleArea(String(option.value))"
>
<checkbox
:value="String(option.value)"
:checked="selectedAreaValues.includes(String(option.value))"
/>
<text class="option-label">{{ option.label }}</text>
</label>
</view>
<!-- 底部按钮 -->
<view class="filter-footer">
<button class="footer-btn clear-btn" @click="handleClear">清除</button>
<button class="footer-btn confirm-btn" @click="handleConfirm">确认</button>
</view>
</view>
</view><!-- end filter-body -->
</view>
</template>
<script setup>
import { ref, reactive, onBeforeMount, watch } from 'vue';
import useDictStore from '@/stores/useDictStore';
const { getTransformChildren } = useDictStore();
const props = defineProps({
show: Boolean,
});
const emit = defineEmits(['confirm', 'close', 'update:show']);
// 岗位类型数据
const getJobTypeData = () => {
return [
{ label: '常规岗位', value: 0, text: '常规岗位' },
{ label: '就业见习岗位', value: 1, text: '就业见习岗位' },
{ label: '实习实训岗位', value: 2, text: '实习实训岗位' },
{ label: '社区实践岗位', value: 3, text: '社区实践岗位' }
];
};
// 标签页数据
const tabs = [
{ key: 'education', label: '学历要求' },
{ key: 'experience', label: '工作经验' },
{ key: 'scale', label: '公司规模' },
{ key: 'jobType', label: '岗位类型' },
{ key: 'area', label: '地区' }
];
// 当前激活的标签
const activeTab = ref('education');
// 存储已选中的值
const selectedValues = reactive({
education: '',
experience: '',
scale: '',
jobType: '',
area: ''
});
// 地区多选值(数组)
const selectedAreaValues = ref([]);
// 从字典获取的选项数据
const educationOptions = ref([]);
const experienceOptions = ref([]);
const scaleOptions = ref([]);
const jobTypeOptions = ref([]);
const areaOptions = ref([]);
// 初始化获取数据
const initData = () => {
educationOptions.value = getTransformChildren('education', '学历要求').options || [];
experienceOptions.value = getTransformChildren('experience', '工作经验').options || [];
scaleOptions.value = getTransformChildren('scale', '公司规模').options || [];
jobTypeOptions.value = getJobTypeData();
areaOptions.value = getTransformChildren('area', '地区').options || [];
};
// 组件挂载时初始化数据
onBeforeMount(() => {
initData();
});
// 监听组件显示状态,当显示时重新初始化数据
watch(() => props.show, (newVal) => {
if (newVal) {
initData();
}
});
// 处理选项选择
const handleSelect = (key, e) => {
selectedValues[key] = e.detail.value;
};
// 切换地区多选
const toggleArea = (value) => {
const index = selectedAreaValues.value.indexOf(value);
if (index === -1) {
selectedAreaValues.value.push(value);
} else {
selectedAreaValues.value.splice(index, 1);
}
};
// 清除所有选择
const handleClear = () => {
Object.keys(selectedValues).forEach((key) => {
selectedValues[key] = '';
});
selectedAreaValues.value = [];
};
// 确认筛选
const handleConfirm = () => {
const result = { ...selectedValues };
result.area = selectedAreaValues.value.join(',');
console.log('selectedValues:', result);
emit('confirm', result);
handleClose();
};
// 关闭弹窗
const handleClose = () => {
emit('update:show', false);
emit('close');
};
</script>
<style lang="scss" scoped>
.filter-container {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background-color: #fff;
z-index: 9999;
display: flex;
flex-direction: column;
box-sizing: border-box;
}
.filter-body {
display: flex;
flex-direction: row;
flex: 1;
overflow: hidden;
}
.filter-header {
height: 144rpx;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 48rpx;
border-bottom: 1rpx solid #eee;
background-color: #fff;
box-shadow: 0 3rpx 15rpx rgba(0, 0, 0, 0.05);
.back-btn {
font-size: 54rpx;
width: 72rpx;
display: flex;
align-items: center;
justify-content: center;
padding: 15rpx;
border-radius: 50%;
transition: all 0.3s ease;
&:active {
background-color: rgba(37, 107, 250, 0.1);
}
}
.filter-title {
font-size: 51rpx;
font-weight: 600;
color: #333;
}
}
.filter-tabs {
display: flex;
flex-direction: column;
width: 250rpx;
border-right: 1rpx solid #eee;
background-color: #f9f9f9;
box-shadow: 3rpx 0 15rpx rgba(0, 0, 0, 0.05);
height: 100%;
box-sizing: border-box;
.tab-item {
height: 130rpx;
line-height: 130rpx;
text-align: center;
font-size: 42rpx;
color: #666;
position: relative;
transition: all 0.3s ease;
border-bottom: 1rpx solid #f0f0f0;
&.active {
color: #256BFA;
font-weight: 600;
background-color: #fff;
box-shadow: 3rpx 0 10rpx rgba(37, 107, 250, 0.1);
&::after {
content: '';
position: absolute;
right: 0;
top: 25%;
width: 6rpx;
height: 50%;
background-color: #256BFA;
border-radius: 3rpx 0 0 3rpx;
}
}
&:active {
background-color: rgba(37, 107, 250, 0.05);
}
}
}
.filter-content {
flex: 1;
padding: 60rpx 0rpx;
// overflow-y: auto;
display: flex;
flex-direction: column;
padding-top: 100rpx;
padding-bottom: 160rpx;
box-sizing: border-box;
}
.content-section {
padding: 0 4%;
overflow-y: auto;
flex: 12;
.radio-item {
display: flex;
align-items: center;
padding: 45rpx 0;
border-bottom: 1rpx solid #f0f0f0;
transition: all 0.3s ease;
position: relative;
&:last-child {
border-bottom: none;
}
&:active {
background-color: rgba(37, 107, 250, 0.05);
}
radio {
width: 42rpx;
height: 42rpx;
margin-right: 36rpx;
transform: scale(1);
display: flex;
align-items: center;
justify-content: center;
}
radio .wx-radio-input {
width: 42rpx;
height: 42rpx;
border-radius: 50%;
border: 3rpx solid #ccc;
background: transparent;
}
radio .wx-radio-input.wx-radio-input-checked {
border-color: #256BFA !important;
background: #256BFA !important;
}
radio .wx-radio-input::before {
width: 24rpx;
height: 24rpx;
line-height: 24rpx;
text-align: center;
font-size: 18rpx;
color: #fff;
background: transparent;
transform: translate(-50%, -50%) scale(0);
-webkit-transform: translate(-50%, -50%) scale(0);
}
radio .wx-radio-input.wx-radio-input-checked::before {
transform: translate(-50%, -50%) scale(1);
-webkit-transform: translate(-50%, -50%) scale(1);
}
.option-label {
font-size: 45rpx;
color: #333;
flex: 1;
font-weight: 400;
line-height: 60rpx;
}
}
}
.filter-footer {
height: 200rpx;
flex: 1;
display: flex;
border-top: 1rpx solid #eee;
background-color: #fff;
box-shadow: 0 -3rpx 15rpx rgba(0, 0, 0, 0.05);
padding: 30rpx 0;
flex-shrink: 0;
z-index: 10;
margin-top: auto;
box-sizing: border-box;
.footer-btn {
flex: 1;
margin: 0 40rpx;
border-radius: 24rpx;
height: 100rpx;
line-height: 100rpx;
font-size: 45rpx;
transition: all 0.3s ease;
font-weight: 500;
&:first-child {
margin-right: 30rpx;
background-color: #f5f5f5;
color: #666;
border: 1rpx solid #e0e0e0;
&:active {
background-color: #e0e0e0;
transform: scale(0.98);
}
}
&:last-child {
background-color: #256BFA;
color: #fff;
border: none;
&:active {
background-color: #1a56d9;
transform: scale(0.98);
}
}
}
}
</style>