职位页微调
This commit is contained in:
@@ -108,6 +108,10 @@ export interface JobIntention {
|
||||
employmentType?: number | null
|
||||
/** 招聘分类:0=社招,1=校招,2=实习,3=其他 */
|
||||
recruitCategory?: number | null
|
||||
/** AI 推荐的岗位分类 ID 列表 */
|
||||
aiCategoryIds?: string[]
|
||||
/** AI 推荐的行业 ID 列表 */
|
||||
aiIndustryIds?: string[]
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 3.9 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 191 KiB |
@@ -85,7 +85,7 @@
|
||||
min-width: 0.20rem;
|
||||
height: 0.20rem;
|
||||
padding: 0 0.05rem;
|
||||
border-radius: 50%;
|
||||
border-radius: 0.1rem;
|
||||
background: #F3F4F6;
|
||||
font-size: 0.11rem;
|
||||
font-weight: 500;
|
||||
|
||||
@@ -82,7 +82,85 @@
|
||||
// 筛选条件外层白色背景条
|
||||
&__filters-bar {
|
||||
border-radius: 0.12rem;
|
||||
margin: 0 0.24rem 0.2rem 0.24rem;
|
||||
margin: 0 0.24rem 0.5rem 0.24rem;
|
||||
}
|
||||
|
||||
// AI 推荐提示条外层
|
||||
&__ai-tip-wrapper {
|
||||
position: relative;
|
||||
margin: -0.38rem 0.24rem 0.12rem 0.24rem;
|
||||
}
|
||||
|
||||
// 尖角容器(用双伪元素实现:::before 画品牌色外三角,::after 画白色内三角遮住底边)
|
||||
&__ai-tip-arrow {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
transform: translateX(-50%);
|
||||
width: 0;
|
||||
height: 0;
|
||||
z-index: 2;
|
||||
|
||||
// 外层三角(品牌色边框)
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: 100%;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: 0.07rem solid transparent;
|
||||
border-right: 0.07rem solid transparent;
|
||||
border-bottom: 0.12rem solid $accent;
|
||||
}
|
||||
|
||||
// 内层白色三角(覆盖外层内部,只露出约0.5px边框)
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: 100%;
|
||||
left: 50%;
|
||||
transform: translateX(-50%) translateY(1px);
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: 0.065rem solid transparent;
|
||||
border-right: 0.065rem solid transparent;
|
||||
border-bottom: 0.112rem solid $bg-white;
|
||||
}
|
||||
}
|
||||
|
||||
// 提示条主体
|
||||
&__ai-tip {
|
||||
position: relative;
|
||||
background: $bg-white;
|
||||
border: 1px solid $accent;
|
||||
border-radius: 0.08rem;
|
||||
padding: 0.08rem 0.12rem 0.07rem 0.12rem;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
// 提示条内容行
|
||||
&__ai-tip-content {
|
||||
|
||||
}
|
||||
|
||||
// Nova logo
|
||||
&__ai-tip-logo {
|
||||
width: 0.16rem;
|
||||
height: 0.16rem;
|
||||
flex: none;
|
||||
}
|
||||
|
||||
// 提示文字
|
||||
&__ai-tip-text {
|
||||
font-size: 0.12rem;
|
||||
line-height: 0.16rem;
|
||||
color: $accent;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
vertical-align: text-top;
|
||||
margin-left: 0.03rem;
|
||||
}
|
||||
|
||||
// 筛选条件
|
||||
@@ -171,14 +249,11 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: $bg-white;
|
||||
border: 1px solid #E8E8E8;
|
||||
border-radius: 0.2rem;
|
||||
padding: 0.07rem 0.14rem;
|
||||
border-radius: 0.08rem;
|
||||
padding: 0.08rem 0.14rem;
|
||||
min-width: 1.6rem;
|
||||
transition: border-color 0.2s ease;
|
||||
|
||||
&:focus-within {
|
||||
border-color: $accent;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -321,7 +396,7 @@
|
||||
display: flex;
|
||||
gap: 0.08rem;
|
||||
flex-wrap: wrap;
|
||||
margin-top: 0.08rem;
|
||||
margin-top: 0.14rem;
|
||||
}
|
||||
|
||||
&__job-tag {
|
||||
@@ -429,7 +504,7 @@
|
||||
gap: 0.08rem;
|
||||
flex-wrap: wrap;
|
||||
min-width: 0; // 允许内容收缩触发换行
|
||||
margin-top: 0.06rem;
|
||||
margin-top: 0.14rem;
|
||||
}
|
||||
|
||||
&__company-tag {
|
||||
|
||||
@@ -171,6 +171,8 @@ export default createStore<RootState>({
|
||||
industryIds: [],
|
||||
employmentType: null,
|
||||
recruitCategory: null,
|
||||
aiCategoryIds: [],
|
||||
aiIndustryIds: [],
|
||||
},
|
||||
userInfo: null,
|
||||
showSettings: false,
|
||||
@@ -229,6 +231,8 @@ export default createStore<RootState>({
|
||||
industryIds: data.industryIds ?? [],
|
||||
employmentType: data.employmentType ?? null,
|
||||
recruitCategory: data.recruitCategory ?? null,
|
||||
aiCategoryIds: data.aiCategoryIds ?? [],
|
||||
aiIndustryIds: data.aiIndustryIds ?? [],
|
||||
}
|
||||
},
|
||||
SET_USER_INFO(state, data: UserInfo | null) {
|
||||
@@ -315,6 +319,8 @@ export default createStore<RootState>({
|
||||
industryIds: [],
|
||||
employmentType: null,
|
||||
recruitCategory: null,
|
||||
aiCategoryIds: [],
|
||||
aiIndustryIds: [],
|
||||
})
|
||||
},
|
||||
|
||||
|
||||
+116
-3
@@ -20,11 +20,12 @@
|
||||
<!-- 筛选条件 -->
|
||||
<div v-if="isRecommendTab" class="jobs-page__filters-bar">
|
||||
<div class="jobs-page__filters">
|
||||
<div class="jobs-page__filter-group">
|
||||
<div ref="filterGroupRef" class="jobs-page__filter-group">
|
||||
<!-- 筛选条件按钮列表(行业单独用组件) -->
|
||||
<template v-for="filter in filters" :key="filter.label">
|
||||
<!-- 城市筛选:使用地区选择组件(选到市级) -->
|
||||
<RegionSelector
|
||||
ref="regionSelectorRef"
|
||||
v-if="filter.key === 'city'"
|
||||
:regionCodes="selectedRegionCodes"
|
||||
:level="2"
|
||||
@@ -36,6 +37,7 @@
|
||||
/>
|
||||
<!-- 岗位筛选:使用岗位选择组件 -->
|
||||
<JobCategorySelector
|
||||
ref="jobCategorySelectorRef"
|
||||
v-else-if="filter.key === 'position'"
|
||||
:categoryIds="selectedCategoryIds"
|
||||
:maxSelect="3"
|
||||
@@ -48,6 +50,7 @@
|
||||
/>
|
||||
<!-- 行业筛选:使用行业选择组件 -->
|
||||
<IndustrySelector
|
||||
ref="industrySelectorRef"
|
||||
v-else-if="filter.key === 'industry'"
|
||||
:industryIds="selectedIndustryIds"
|
||||
:maxSelect="3"
|
||||
@@ -102,6 +105,21 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 推荐的行业和岗位提示条 -->
|
||||
<div v-if="showAiRecommendTip" class="jobs-page__ai-tip-wrapper">
|
||||
<!-- 提示条主体 -->
|
||||
<div class="jobs-page__ai-tip">
|
||||
<!-- 尖角:用::before(边框色)和::after(白色)双伪元素实现,通过动态left定位 -->
|
||||
<span class="jobs-page__ai-tip-arrow" :style="{ left: aiTipArrowLeft + 'px' }"></span>
|
||||
<div class="jobs-page__ai-tip-content">
|
||||
<img class="jobs-page__ai-tip-logo" src="@/assets/images/logo-offerpai-2.png" alt="Nova" />
|
||||
<span class="jobs-page__ai-tip-text">
|
||||
Nova根据你的个人背景建议的行业:{{ aiIndustryNames }}<template v-if="aiCategoryNames">;岗位:{{ aiCategoryNames }}</template>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 职位列表 -->
|
||||
<div ref="jobListRef" class="jobs-page__list pr5 scrollbar-thin" :style="restoring ? { visibility: 'hidden' } : {}" @scroll="onListScroll">
|
||||
<div
|
||||
@@ -137,9 +155,21 @@
|
||||
<!-- 底部:地区 + 学历 + 招聘分类 -->
|
||||
<div class="jobs-page__job-meta-bottom">
|
||||
<span class="jobs-page__meta-location">
|
||||
<svg viewBox="0 0 18 18" fill="none" class="jobs-page__location-icon">
|
||||
<path d="M9 1.5C5.96 1.5 3.5 3.96 3.5 7c0 4.13 5.5 9.5 5.5 9.5s5.5-5.37 5.5-9.5c0-3.04-2.46-5.5-5.5-5.5zm0 7.5a2 2 0 110-4 2 2 0 010 4z" fill="currentColor"/>
|
||||
<!-- <svg viewBox="0 0 18 18" fill="none" class="jobs-page__location-icon">-->
|
||||
<!-- <path d="M9 1.5C5.96 1.5 3.5 3.96 3.5 7c0 4.13 5.5 9.5 5.5 9.5s5.5-5.37 5.5-9.5c0-3.04-2.46-5.5-5.5-5.5zm0 7.5a2 2 0 110-4 2 2 0 010 4z" fill="currentColor"/>-->
|
||||
<!-- </svg>-->
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_217_327)">
|
||||
<path d="M10.6666 6.66667C10.6666 5.19333 9.47325 4 7.99992 4C6.52659 4 5.33325 5.19333 5.33325 6.66667C5.33325 8.14 6.52659 9.33333 7.99992 9.33333C9.47325 9.33333 10.6666 8.14 10.6666 6.66667ZM6.66659 6.66667C6.66659 5.93333 7.26659 5.33333 7.99992 5.33333C8.73325 5.33333 9.33325 5.93333 9.33325 6.66667C9.33325 7.4 8.73325 8 7.99992 8C7.26659 8 6.66659 7.4 6.66659 6.66667Z" fill="#6A7282"/>
|
||||
<path d="M7.61323 14.54C7.72657 14.62 7.86657 14.6667 7.9999 14.6667C8.13323 14.6667 8.27323 14.6267 8.38657 14.54C8.58657 14.3933 13.3532 10.96 13.3332 6.65999C13.3332 3.71999 10.9399 1.32666 7.9999 1.32666C5.0599 1.32666 2.66657 3.71999 2.66657 6.65999C2.64657 10.9533 7.41323 14.3933 7.61323 14.54ZM7.9999 2.66666C10.2066 2.66666 11.9999 4.45999 11.9999 6.66666C12.0132 9.62666 9.07323 12.2867 7.9999 13.16C6.92657 12.2867 3.98657 9.63333 3.9999 6.66666C3.9999 4.45999 5.79323 2.66666 7.9999 2.66666Z" fill="#6A7282"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_217_327">
|
||||
<rect width="16" height="16" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
{{ job.regionName }}
|
||||
</span>
|
||||
<span v-if="formatEducation(job.education)" class="jobs-page__meta-edu">{{ formatEducation(job.education) }}</span>
|
||||
@@ -272,6 +302,8 @@ import RegionSelector from '@/components/tools/RegionSelector.vue'
|
||||
import { fetchJobList, fetchFavoriteList, toggleJobFavorite, removeJobFavorite, fetchFavoriteCount, fetchApplyList, fetchApplyCount, removeJobFromList } from '@/api/jobs'
|
||||
import type { JobListItem, JobListParams, FavoriteListParams, ApplyListParams, ApplyCountData } from '@/api/jobs'
|
||||
import { getTimeEvent, setTimeEvent, timestampToLocalDateTime } from '@/utils/time'
|
||||
import { resolveJobCategoryName } from '@/utils/jobCategory'
|
||||
import { resolveIndustryName } from '@/utils/industry'
|
||||
|
||||
|
||||
// 2. 注意:这里的字符串不需要再手动转义双引号了
|
||||
@@ -474,6 +506,39 @@ const selectedRegionCodes = computed<string[]>(
|
||||
() => store.state.jobIntention.regionCodes || [],
|
||||
)
|
||||
|
||||
/** AI 推荐的行业 ID 列表 */
|
||||
const aiIndustryIds = computed<string[]>(
|
||||
() => store.state.jobIntention.aiIndustryIds || [],
|
||||
)
|
||||
|
||||
/** AI 推荐的岗位 ID 列表 */
|
||||
const aiCategoryIds = computed<string[]>(
|
||||
() => store.state.jobIntention.aiCategoryIds || [],
|
||||
)
|
||||
|
||||
/** AI 推荐的行业名称(ID 转文字) */
|
||||
const aiIndustryNames = computed<string>(() => {
|
||||
if (!aiIndustryIds.value.length) return ''
|
||||
return aiIndustryIds.value
|
||||
.map(id => resolveIndustryName(Number(id)))
|
||||
.filter(name => !!name)
|
||||
.join('、')
|
||||
})
|
||||
|
||||
/** AI 推荐的岗位名称(ID 转文字) */
|
||||
const aiCategoryNames = computed<string>(() => {
|
||||
if (!aiCategoryIds.value.length) return ''
|
||||
return aiCategoryIds.value
|
||||
.map(id => resolveJobCategoryName(Number(id)))
|
||||
.filter(name => !!name)
|
||||
.join('、')
|
||||
})
|
||||
|
||||
/** 是否显示 AI 推荐提示条 */
|
||||
const showAiRecommendTip = computed(() => {
|
||||
return isRecommendTab.value && (aiIndustryNames.value || aiCategoryNames.value)
|
||||
})
|
||||
|
||||
/** 行业选择变更回调 */
|
||||
function onIndustryChange(ids: number[]) {
|
||||
store.dispatch('saveJobIntention', {
|
||||
@@ -631,6 +696,8 @@ function goToAgent() {
|
||||
|
||||
onMounted(async () => {
|
||||
document.addEventListener('click', closeDropdownOnClickOutside)
|
||||
// 窗口 resize 时更新尖角位置
|
||||
window.addEventListener('resize', updateAiTipArrowPosition)
|
||||
|
||||
// 先判断是否有缓存(从详情页返回),在任何异步操作之前取出
|
||||
const cache = store.state.jobListCache
|
||||
@@ -685,12 +752,15 @@ onMounted(async () => {
|
||||
nextTick(() => {
|
||||
nextTick(() => {
|
||||
initializing = false
|
||||
// 数据加载完成后计算尖角位置
|
||||
updateAiTipArrowPosition()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
document.removeEventListener('click', closeDropdownOnClickOutside)
|
||||
window.removeEventListener('resize', updateAiTipArrowPosition)
|
||||
})
|
||||
|
||||
// ==================== 职位列表项(扩展接口字段,增加前端交互状态) ====================
|
||||
@@ -732,6 +802,47 @@ const jobList = ref<JobItem[]>([])
|
||||
/** 列表容器 ref,用于监听滚动和恢复滚动位置 */
|
||||
const jobListRef = ref<HTMLElement | null>(null)
|
||||
|
||||
/** 岗位筛选组件 ref(保留给模板绑定) */
|
||||
const jobCategorySelectorRef = ref<InstanceType<typeof JobCategorySelector> | null>(null)
|
||||
|
||||
/** 行业筛选组件 ref(保留给模板绑定) */
|
||||
const industrySelectorRef = ref<InstanceType<typeof IndustrySelector> | null>(null)
|
||||
|
||||
/** 城市筛选组件 ref(保留给模板绑定) */
|
||||
const regionSelectorRef = ref<InstanceType<typeof RegionSelector> | null>(null)
|
||||
|
||||
/** 筛选条件按钮容器 ref,用于 DOM 查询获取 trigger 宽度 */
|
||||
const filterGroupRef = ref<HTMLElement | null>(null)
|
||||
|
||||
/** AI 提示条尖角的 left 偏移量(像素),动态对齐岗位和行业选择器中间 */
|
||||
const aiTipArrowLeft = ref(120)
|
||||
|
||||
/** 计算尖角位置:从 filter-group 容器直接查找 trigger DOM 获取宽度 */
|
||||
function updateAiTipArrowPosition() {
|
||||
nextTick(() => {
|
||||
const group = filterGroupRef.value
|
||||
if (!group) return
|
||||
const regionTrigger = group.querySelector('.region-selector__trigger') as HTMLElement | null
|
||||
const categoryTrigger = group.querySelector('.job-category-selector__trigger') as HTMLElement | null
|
||||
if (!regionTrigger || !categoryTrigger) return
|
||||
// filter-group gap 为 0.08rem = 8px
|
||||
const gap = 8
|
||||
// 尖角 left = 城市trigger宽度 + 1gap + 岗位trigger宽度 + 半gap
|
||||
const left = regionTrigger.offsetWidth + gap + categoryTrigger.offsetWidth + gap / 2
|
||||
aiTipArrowLeft.value = left
|
||||
})
|
||||
}
|
||||
|
||||
/** 监听选择器数据变化和提示条显示状态,DOM 更新后重新计算尖角位置 */
|
||||
watch(
|
||||
[showAiRecommendTip, selectedCategoryIds, selectedRegionCodes,
|
||||
() => store.state.jobCategories, () => store.state.regions],
|
||||
() => {
|
||||
updateAiTipArrowPosition()
|
||||
},
|
||||
{ flush: 'post', deep: true },
|
||||
)
|
||||
|
||||
/** 记录离开页面前的滚动位置 */
|
||||
let savedScrollTop = 0
|
||||
|
||||
@@ -995,6 +1106,8 @@ watch(
|
||||
// 初始化阶段(loadJobIntention 导致的变化)跳过,避免清掉缓存
|
||||
if (initializing) return
|
||||
reloadFirstPage()
|
||||
// 岗位选择器宽度可能变化,重新计算尖角位置
|
||||
updateAiTipArrowPosition()
|
||||
},
|
||||
{ deep: true },
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user