diff --git a/src/api/jobs.ts b/src/api/jobs.ts index 80d4e4d..50f5726 100644 --- a/src/api/jobs.ts +++ b/src/api/jobs.ts @@ -3,6 +3,16 @@ import aiService from '@/utils/aiRequest' import type { ApiResult } from '@/api/auth' import type { AiResult } from '@/utils/aiRequest' +// ==================== 岗位总数 ==================== + +/** + * 获取全网实时岗位总数 + * GET /job/count + */ +export function fetchJobCount() { + return request.get>('/job/count') +} + // ==================== 匹配详情 ==================== /** 匹配度详情 */ diff --git a/src/assets/styles/components/job-page-header.scss b/src/assets/styles/components/job-page-header.scss index a3dcce5..e55c0d9 100644 --- a/src/assets/styles/components/job-page-header.scss +++ b/src/assets/styles/components/job-page-header.scss @@ -1,5 +1,86 @@ @use '../variables' as *; +/* ==================== 岗位统计卡片 ==================== */ +.job-stats-cards { + display: flex; + align-items: center; + gap: 0.16rem; + margin-left: auto; + filter: drop-shadow(0px 2px 10px rgba(0, 221, 179, 0.16)); + + /* 全网实时岗位卡片 */ + &__count-card { + position: relative; + height: 0.43rem; + background: #0A171C; + border: 1px solid #1C4C55; + border-radius: 0.05rem; + } + + /* 状态指示圆点 */ + &__dot { + //position: absolute; + width: 0.08rem; + height: 0.08rem; + background: #2FE9F4; + border-radius: 50%; + box-shadow: 0px 0px 4px rgba(47, 233, 244, 0.75); + } + + /* 岗位数字 */ + &__count-num { + + font-family: 'MiSans'; + font-weight: 630; + font-size: 0.16rem; + line-height: 0.21rem; + color: #F4FFFF; + } + + /* 岗位标签文字 */ + &__count-label { + font-family: 'MiSans'; + font-weight: 330; + font-size: 0.10rem; + line-height: 0.13rem; + color: #8AA8AF; + } + + /* 可投率卡片 */ + &__match-card { + position: relative; + width: 0.52rem; + height: 0.42rem; + background: #08161B; + border: 1px solid #20626D; + border-radius: 0.05rem; + } + + /* 可投率数字 */ + &__match-num { + position: absolute; + left: 0.08rem; + top: 0.04rem; + font-family: 'MiSans'; + font-weight: 630; + font-size: 0.16rem; + line-height: 0.21rem; + color: #2FE9F4; + } + + /* 可投率标签文字 */ + &__match-label { + position: absolute; + left: 0.08rem; + top: 0.245rem; + font-family: 'MiSans'; + font-weight: 520; + font-size: 0.10rem; + line-height: 0.13rem; + color: #8FB7BE; + } +} + .job-page-header { diff --git a/src/components/AgentSetupWizard.vue b/src/components/AgentSetupWizard.vue index 73ebd76..a8a2b8b 100644 --- a/src/components/AgentSetupWizard.vue +++ b/src/components/AgentSetupWizard.vue @@ -76,7 +76,8 @@ :categoryIds="step2CategoryIds" :maxSelect="3" :level="3" - :allowParentSelect="true" + :allowParentSelect="false" + :allowParentSelectClick="true" :triggerStyle="prefFilterTriggerStyle" :displayStyle="prefFilterDisplayStyle" @update:categoryIds="onStep2CategoryChange" @@ -86,7 +87,8 @@ :industryIds="step2IndustryIds" :maxSelect="3" :level="2" - :allowParentSelect="true" + :allowParentSelect="false" + :allowParentSelectClick="true" :triggerStyle="prefFilterTriggerStyle" :displayStyle="prefFilterDisplayStyle" @update:industryIds="onStep2IndustryChange" diff --git a/src/components/AiChat.vue b/src/components/AiChat.vue index 26ae1e0..ad0feb8 100644 --- a/src/components/AiChat.vue +++ b/src/components/AiChat.vue @@ -150,9 +150,9 @@ interface ChatMsg { // ==================== 本地缓存 ==================== -/** 获取当前用户的缓存 key */ +/** 获取当前用户的缓存 key,使用 vuex 中的用户 ID 区分不同账号 */ function getCacheKey(): string { - const userId = sessionStorage.getItem('userId') || 'anonymous' + const userId = store.state.userInfo?.id || 'anonymous' return `nova_chat_${userId}` } @@ -272,7 +272,7 @@ async function sendMessage() { // ==================== 生命周期 ==================== onMounted(() => { - // 从缓存加载聊天记录 + // 从缓存加载聊天记录(如果用户信息已就绪则按用户隔离) messages.value = loadChatFromCache() if (messages.value.length > 0) { scrollToBottom() @@ -283,6 +283,17 @@ onMounted(() => { } }) +// 监听 userInfo 变化:用户登录后 userInfo 从 null 变为有值时重新加载对应用户的缓存 +watch(() => store.state.userInfo, (newInfo, oldInfo) => { + if (newInfo && newInfo.id && (!oldInfo || !oldInfo.id)) { + // 用户信息就绪,重新加载该用户的聊天记录 + messages.value = loadChatFromCache() + if (messages.value.length > 0) { + scrollToBottom() + } + } +}) + // 监听 jobId 变化 watch(() => props.jobId, (newId, oldId) => { if (newId && newId !== oldId && !isJobDetailPage.value) { diff --git a/src/components/JobGoalDialog.vue b/src/components/JobGoalDialog.vue index 097a999..3e7f757 100644 --- a/src/components/JobGoalDialog.vue +++ b/src/components/JobGoalDialog.vue @@ -19,6 +19,7 @@ :maxSelect="3" :level="3" :allowParentSelect="false" + :allowParentSelectClick="true" :triggerStyle="selectorTriggerStyle" :displayStyle="selectorDisplayStyle" @update:categoryIds="onCategoryChange" @@ -33,6 +34,7 @@ :maxSelect="3" :level="2" :allowParentSelect="false" + :allowParentSelectClick="true" :triggerStyle="selectorTriggerStyle" :displayStyle="selectorDisplayStyle" @update:industryIds="onIndustryChange" diff --git a/src/components/JobPageHeader.vue b/src/components/JobPageHeader.vue index 645f52b..6727d81 100644 --- a/src/components/JobPageHeader.vue +++ b/src/components/JobPageHeader.vue @@ -1,5 +1,5 @@ @@ -25,7 +41,7 @@ import { ref, computed, onMounted } from 'vue' import { useRouter, useRoute } from 'vue-router' import { useStore } from 'vuex' -import { fetchFavoriteCount, fetchApplyCount } from '@/api/jobs' +import { fetchFavoriteCount, fetchApplyCount, fetchJobCount } from '@/api/jobs' // ==================== Props & Emits ==================== @@ -50,12 +66,32 @@ const isAuthenticated = computed(() => store.state.isAuthenticated) // ==================== 收藏 & 投递统计 ==================== +/** 全网岗位总数 */ +const jobCount = ref(0) + +/** 格式化岗位总数(每3位加逗号) */ +const formattedJobCount = computed(() => { + return jobCount.value.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',') +}) + /** 收藏总数 */ const favoriteTotal = ref(0) /** 投递总数 */ const applyTotal = ref(0) +/** 加载岗位总数 */ +async function loadJobCount() { + try { + const res = await fetchJobCount() + if (res.code === '0' && res.data !== undefined) { + jobCount.value = Number(res.data) + } + } catch (e) { + console.error('加载岗位总数失败', e) + } +} + /** 加载收藏统计 */ async function loadFavoriteCount() { try { @@ -82,12 +118,20 @@ async function loadApplyCount() { /** 组件挂载时请求收藏和投递统计 */ onMounted(() => { + // 岗位总数不需要登录态 + loadJobCount() if (isAuthenticated.value) { loadFavoriteCount() loadApplyCount() } }) +/** 暴露刷新方法给父组件调用,以便收藏/投递操作后同步刷新计数 */ +defineExpose({ + loadFavoriteCount, + loadApplyCount, +}) + // ==================== 常量数据 ==================== /** Tab 选项列表,count 用于显示圆形徽章计数 */ diff --git a/src/components/tools/IndustrySelector.vue b/src/components/tools/IndustrySelector.vue index 7bca178..2d3931e 100644 --- a/src/components/tools/IndustrySelector.vue +++ b/src/components/tools/IndustrySelector.vue @@ -54,7 +54,7 @@ class="industry-selector__search-item" v-for="r in searchResults" :key="r.node.id" - @click="toggleItem(r.node)" + @click="handleSearchItemClick(r.node)" > {{ r.path }} @@ -69,8 +69,8 @@
无匹配结果
- -
(双击可选中一级行业分类)
+ +
(双击可选中一级行业分类)
@@ -80,14 +80,14 @@ class="industry-selector__col-item" :class="{ 'industry-selector__col-item--active': level > 1 && activeParentId === parent.id, - 'industry-selector__col-item--selected': (level === 1 || allowParentSelect) && isSelected(parent.id) + 'industry-selector__col-item--selected': (level === 1 || allowParentSelect || allowParentSelectClick) && isSelected(parent.id) }" v-for="parent in industries" :key="parent.id" @click="selectParent(parent.id)" > {{ parent.name }} - +
@@ -100,7 +100,7 @@ :class="{ 'industry-selector__col-item--selected': isSelected(child.id) }" v-for="child in activeChildren" :key="child.id" - @click="toggleItem({ id: child.id, name: child.name, level: child.level })" + @click="selectChild(child)" > {{ child.name }} @@ -144,6 +144,8 @@ const props = withDefaults( maxSelect?: number /** 是否允许双击选中一级行业 */ allowParentSelect?: boolean + /** 是否允许单击选中父级(选子级时自动移除已选的父级) */ + allowParentSelectClick?: boolean /** 展示/选择到第几级:1=只选一级,2=选到二级 */ level?: 1 | 2 /** 父组件传入的触发按钮自定义样式,用于在不同场景下覆盖默认外观 */ @@ -156,6 +158,7 @@ const props = withDefaults( { maxSelect: 3, allowParentSelect: false, + allowParentSelectClick: false, level: 2, } ) @@ -212,14 +215,16 @@ const activeChildren = computed(() => { return parent ? parent.children : [] }) -/** 搜索结果:根据 level 和 allowParentSelect 匹配对应级别 */ +/** 搜索结果:根据 level 和 allowParentSelect/allowParentSelectClick 匹配对应级别 */ const searchResults = computed(() => { if (searchText.value.length < 2) return [] const keyword = searchText.value.toLowerCase() const results: { path: string; node: SelectedNode }[] = [] + /** 是否允许选中父级 */ + const canSelectParent = props.allowParentSelect || props.allowParentSelectClick for (const parent of industries.value) { - // 一级:level=1 时作为末级可搜索,或 allowParentSelect 且 level=2 时可搜索 - if ((props.level === 1 || props.allowParentSelect) && parent.name.toLowerCase().includes(keyword)) { + // 一级:level=1 时作为末级可搜索,或允许选中父级且 level=2 时可搜索 + if ((props.level === 1 || canSelectParent) && parent.name.toLowerCase().includes(keyword)) { results.push({ path: parent.name, node: { id: parent.id, name: parent.name, level: parent.level } }) } if (props.level === 2) { @@ -248,6 +253,13 @@ function selectParent(id: string) { if (l1) toggleItem({ id: l1.id, name: l1.name, level: l1.level }) return } + // allowParentSelectClick 模式:单击直接选中一级,同时展开子级 + if (props.allowParentSelectClick) { + const l1 = industries.value.find(c => c.id === id) + if (l1) toggleItemWithHierarchy({ id: l1.id, name: l1.name, level: l1.level }) + activeParentId.value = id + return + } if (props.allowParentSelect) { const now = Date.now() const lastTime = level1LastClickTime.value[id] || 0 @@ -279,6 +291,79 @@ function toggleItem(node: SelectedNode) { } } +/** + * allowParentSelectClick 模式下的选中/取消逻辑: + * - 选中子级时,移除已选中的父级 + * - 选中父级时,移除已选中的该父级下的所有子级 + */ +function toggleItemWithHierarchy(node: SelectedNode) { + const idx = selectedItems.value.findIndex(i => i.id === node.id) + if (idx >= 0) { + // 取消选中 + selectedItems.value.splice(idx, 1) + return + } + // 获取祖先和后代 ID + const ancestorIds = getAncestorIds(node.id) + const descendantIds = getDescendantIds(node.id) + // 移除已选中的祖先和后代 + selectedItems.value = selectedItems.value.filter( + i => !ancestorIds.has(i.id) && !descendantIds.has(i.id) + ) + // 检查数量限制 + if (selectedItems.value.length >= props.maxSelect) { + ElMessage.warning(`最多只能选择${props.maxSelect}个行业`) + return + } + selectedItems.value.push({ ...node }) +} + +/** 获取某个节点的所有祖先 ID(向上查找) */ +function getAncestorIds(nodeId: string): Set { + const ancestors = new Set() + for (const parent of industries.value) { + for (const child of parent.children) { + if (child.id === nodeId) { + ancestors.add(parent.id) + return ancestors + } + } + } + return ancestors +} + +/** 获取某个节点的所有后代 ID(向下查找) */ +function getDescendantIds(nodeId: string): Set { + const descendants = new Set() + for (const parent of industries.value) { + if (parent.id === nodeId) { + for (const child of parent.children) { + descendants.add(child.id) + } + return descendants + } + } + return descendants +} + +/** 点击右栏二级行业 */ +function selectChild(child: { id: string; name: string; level: number }) { + if (props.allowParentSelectClick) { + toggleItemWithHierarchy({ id: child.id, name: child.name, level: child.level }) + } else { + toggleItem({ id: child.id, name: child.name, level: child.level }) + } +} + +/** 搜索结果项点击处理 */ +function handleSearchItemClick(node: SelectedNode) { + if (props.allowParentSelectClick) { + toggleItemWithHierarchy(node) + } else { + toggleItem(node) + } +} + /** 从选中区移除指定行业 */ function removeItem(item: SelectedNode) { selectedItems.value = selectedItems.value.filter(i => i.id !== item.id) diff --git a/src/components/tools/JobCategorySelector.vue b/src/components/tools/JobCategorySelector.vue index 4a8b3f1..36e47b7 100644 --- a/src/components/tools/JobCategorySelector.vue +++ b/src/components/tools/JobCategorySelector.vue @@ -57,7 +57,7 @@ class="job-category-selector__search-item" v-for="r in searchResults" :key="r.node.id" - @click="toggleItem(r.node)" + @click="handleSearchItemClick(r.node)" > {{ r.path }} @@ -71,8 +71,8 @@
无匹配结果
- -
(双击可选中一级/二级岗位分类)
+ +
(双击可选中一级/二级岗位分类)
@@ -82,14 +82,14 @@ class="job-category-selector__col-item" :class="{ 'job-category-selector__col-item--active': level > 1 && activeLevel1Id === item.id, - 'job-category-selector__col-item--selected': (level === 1 || allowParentSelect) && isSelected(item.id) + 'job-category-selector__col-item--selected': (level === 1 || allowParentSelect || allowParentSelectClick) && isSelected(item.id) }" v-for="item in categories" :key="item.id" @click="selectLevel1(item.id)" > {{ item.name }} - +
@@ -101,14 +101,14 @@ class="job-category-selector__col-item" :class="{ 'job-category-selector__col-item--active': level > 2 && activeLevel2Id === item.id, - 'job-category-selector__col-item--selected': (level === 2 || allowParentSelect) && isSelected(item.id) + 'job-category-selector__col-item--selected': (level === 2 || allowParentSelect || allowParentSelectClick) && isSelected(item.id) }" v-for="item in level2List" :key="item.id" @click="selectLevel2(item.id)" > {{ item.name }} - + @@ -123,7 +123,7 @@ :class="{ 'job-category-selector__col-item--selected': isSelected(item.id) }" v-for="item in level3List" :key="item.id" - @click="toggleItem({ id: item.id, name: item.name, level: item.level })" + @click="selectLevel3(item)" > {{ item.name }} @@ -167,6 +167,8 @@ const props = withDefaults( maxSelect?: number /** 是否允许双击选中一二级分类 */ allowParentSelect?: boolean + /** 是否允许单击选中父级(选子级时自动移除已选的父级) */ + allowParentSelectClick?: boolean /** 展示/选择到第几级:1=只选一级,2=选到二级,3=选到三级 */ level?: 1 | 2 | 3 /** 父组件传入的触发按钮自定义样式,用于在不同场景下覆盖默认外观 */ @@ -179,6 +181,7 @@ const props = withDefaults( { maxSelect: 3, allowParentSelect: false, + allowParentSelectClick: false, level: 3, } ) @@ -248,20 +251,22 @@ const level3List = computed(() => { return mid ? mid.children : [] }) -/** 搜索结果:根据 level 和 allowParentSelect 匹配对应级别 */ +/** 搜索结果:根据 level 和 allowParentSelect/allowParentSelectClick 匹配对应级别 */ const searchResults = computed(() => { if (searchText.value.length < 2) return [] const keyword = searchText.value.toLowerCase() const results: { path: string; node: SelectedNode }[] = [] + /** 是否允许选中父级(双击或单击模式均可) */ + const canSelectParent = props.allowParentSelect || props.allowParentSelectClick for (const l1 of categories.value) { - // 一级:level=1 时作为末级可搜索,或 allowParentSelect 且 level>1 时可搜索 - if ((props.level === 1 || props.allowParentSelect) && l1.name.toLowerCase().includes(keyword)) { + // 一级:level=1 时作为末级可搜索,或允许选中父级且 level>1 时可搜索 + if ((props.level === 1 || canSelectParent) && l1.name.toLowerCase().includes(keyword)) { results.push({ path: l1.name, node: { id: l1.id, name: l1.name, level: l1.level } }) } if (props.level >= 2) { for (const l2 of l1.children) { - // 二级:level=2 时作为末级可搜索,或 allowParentSelect 且 level=3 时可搜索 - if ((props.level === 2 || props.allowParentSelect) && l2.name.toLowerCase().includes(keyword)) { + // 二级:level=2 时作为末级可搜索,或允许选中父级且 level=3 时可搜索 + if ((props.level === 2 || canSelectParent) && l2.name.toLowerCase().includes(keyword)) { results.push({ path: `${l1.name} → ${l2.name}`, node: { id: l2.id, name: l2.name, level: l2.level } }) } if (props.level === 3) { @@ -292,6 +297,14 @@ function selectLevel1(id: string) { if (l1) toggleItem({ id: l1.id, name: l1.name, level: l1.level }) return } + // allowParentSelectClick 模式:单击直接选中一级,同时展开子级 + if (props.allowParentSelectClick) { + const l1 = categories.value.find(c => c.id === id) + if (l1) toggleItemWithHierarchy({ id: l1.id, name: l1.name, level: l1.level }) + activeLevel1Id.value = id + activeLevel2Id.value = '' + return + } if (props.allowParentSelect) { const now = Date.now() const lastTime = level1LastClickTime.value[id] || 0 @@ -319,6 +332,13 @@ function selectLevel2(id: string) { if (l2) toggleItem({ id: l2.id, name: l2.name, level: l2.level }) return } + // allowParentSelectClick 模式:单击直接选中二级,同时展开子级 + if (props.allowParentSelectClick) { + const l2 = level2List.value.find(c => c.id === id) + if (l2) toggleItemWithHierarchy({ id: l2.id, name: l2.name, level: l2.level }) + activeLevel2Id.value = id + return + } if (props.allowParentSelect) { const now = Date.now() const lastTime = level2LastClickTime.value[id] || 0 @@ -350,6 +370,100 @@ function toggleItem(node: SelectedNode) { } } +/** + * allowParentSelectClick 模式下的选中/取消逻辑: + * - 选中子级时,移除已选中的父级(因为选了更具体的) + * - 选中父级时,移除已选中的该父级下的所有子级 + */ +function toggleItemWithHierarchy(node: SelectedNode) { + const idx = selectedItems.value.findIndex(i => i.id === node.id) + if (idx >= 0) { + // 取消选中 + selectedItems.value.splice(idx, 1) + return + } + // 选中前先做层级互斥处理 + // 获取当前节点的祖先 ID 列表和后代 ID 列表 + const ancestorIds = getAncestorIds(node.id) + const descendantIds = getDescendantIds(node.id) + // 移除已选中的祖先和后代 + selectedItems.value = selectedItems.value.filter( + i => !ancestorIds.has(i.id) && !descendantIds.has(i.id) + ) + // 检查数量限制 + if (selectedItems.value.length >= props.maxSelect) { + ElMessage.warning(`最多只能选择${props.maxSelect}个岗位`) + return + } + selectedItems.value.push({ ...node }) +} + +/** 获取某个节点的所有祖先 ID(向上查找) */ +function getAncestorIds(nodeId: string): Set { + const ancestors = new Set() + for (const l1 of categories.value) { + for (const l2 of l1.children) { + for (const l3 of l2.children) { + if (l3.id === nodeId) { + ancestors.add(l2.id) + ancestors.add(l1.id) + return ancestors + } + } + if (l2.id === nodeId) { + ancestors.add(l1.id) + return ancestors + } + } + } + return ancestors +} + +/** 获取某个节点的所有后代 ID(向下查找) */ +function getDescendantIds(nodeId: string): Set { + const descendants = new Set() + for (const l1 of categories.value) { + if (l1.id === nodeId) { + // 是一级,收集所有二级和三级 + for (const l2 of l1.children) { + descendants.add(l2.id) + for (const l3 of l2.children) { + descendants.add(l3.id) + } + } + return descendants + } + for (const l2 of l1.children) { + if (l2.id === nodeId) { + // 是二级,收集所有三级 + for (const l3 of l2.children) { + descendants.add(l3.id) + } + return descendants + } + } + } + return descendants +} + +/** 点击右栏三级岗位 */ +function selectLevel3(item: { id: string; name: string; level: number }) { + if (props.allowParentSelectClick) { + toggleItemWithHierarchy({ id: item.id, name: item.name, level: item.level }) + } else { + toggleItem({ id: item.id, name: item.name, level: item.level }) + } +} + +/** 搜索结果项点击处理 */ +function handleSearchItemClick(node: SelectedNode) { + if (props.allowParentSelectClick) { + toggleItemWithHierarchy(node) + } else { + toggleItem(node) + } +} + /** 从选中区移除指定岗位 */ function removeItem(item: SelectedNode) { selectedItems.value = selectedItems.value.filter(i => i.id !== item.id) diff --git a/src/views/Home.vue b/src/views/Home.vue index 6dce5e3..870e18b 100644 --- a/src/views/Home.vue +++ b/src/views/Home.vue @@ -409,6 +409,7 @@ :level="2" :maxSelect="3" :allowParentSelect="false" + :allowParentSelectClick="true" :triggerStyle="filterTriggerStyle" :displayStyle="filterDisplayStyle" @update:industryIds="onHomeIndustryChange" @@ -428,6 +429,7 @@ :level="3" :maxSelect="3" :allowParentSelect="false" + :allowParentSelectClick="true" :triggerStyle="filterTriggerStyle" :displayStyle="filterDisplayStyle" @update:categoryIds="onHomeCategoryChange" diff --git a/src/views/JobDetail.vue b/src/views/JobDetail.vue index 32c012a..b8fe595 100644 --- a/src/views/JobDetail.vue +++ b/src/views/JobDetail.vue @@ -3,7 +3,7 @@
- +
@@ -443,6 +443,9 @@ function goBack() { /** AiChat 组件引用 */ const aiChatRef = ref | null>(null) +/** JobPageHeader 组件引用,用于刷新收藏/投递计数 */ +const jobPageHeaderRef = ref | null>(null) + /** 问 Nova — 触发 AI 聊天,推送提示消息 */ function handleAskNova() { aiChatRef.value?.triggerAsk() @@ -556,6 +559,8 @@ async function toggleFavorite() { const wasFavorite = job.isFavorite job.isFavorite = !job.isFavorite ElMessage.success(wasFavorite ? '已取消收藏' : '收藏成功') + // 同步刷新 JobPageHeader 组件内的收藏计数 + jobPageHeaderRef.value?.loadFavoriteCount() // 同步收藏状态到 Jobs 页面缓存,返回时无需刷新列表即可看到最新状态 const cache = store.state.jobListCache if (cache) { diff --git a/src/views/Jobs.vue b/src/views/Jobs.vue index 8bdd4d8..a1bf2ae 100644 --- a/src/views/Jobs.vue +++ b/src/views/Jobs.vue @@ -15,7 +15,7 @@
- +
@@ -42,7 +42,8 @@ :categoryIds="selectedCategoryIds" :maxSelect="3" :level="3" - :allowParentSelect="true" + :allowParentSelect="false" + :allowParentSelectClick="true" :triggerStyle="selectorTriggerStyle" :hoverStyle="selectorHoverStyle" :displayStyle="selectorDisplayStyle" @@ -55,7 +56,8 @@ :industryIds="selectedIndustryIds" :maxSelect="3" :level="2" - :allowParentSelect="true" + :allowParentSelect="false" + :allowParentSelectClick="true" :triggerStyle="selectorTriggerStyle" :hoverStyle="selectorHoverStyle" :displayStyle="selectorDisplayStyle" @@ -655,7 +657,7 @@ const aiCategoryNames = computed(() => { /** 是否显示 AI 推荐提示条 */ const showAiRecommendTip = computed(() => { - return isRecommendTab.value && (aiIndustryNames.value !== null || aiCategoryNames.value !== null) + return isRecommendTab.value && (aiIndustryNames.value || aiCategoryNames.value) }) /** 行业选择变更回调 */ @@ -1459,6 +1461,9 @@ function goToDetail(job: JobItem) { const dislikeDialogRef = ref | null>(null) const feedbackDialogRef = ref | null>(null) +/** JobPageHeader 组件引用,用于刷新收藏/投递计数 */ +const jobPageHeaderRef = ref | null>(null) + /** 收藏/取消收藏岗位 */ async function toggleFavorite(job: JobItem) { try { @@ -1471,6 +1476,8 @@ async function toggleFavorite(job: JobItem) { ElMessage.success(wasFavorite ? '已取消收藏' : '收藏成功') // 刷新收藏统计 loadFavoriteCount() + // 同步刷新 JobPageHeader 组件内的收藏计数 + jobPageHeaderRef.value?.loadFavoriteCount() } } catch (e) { console.error('收藏操作失败', e) @@ -1486,6 +1493,8 @@ async function removeFavoriteFromList(job: JobItem, index: number) { total.value = Math.max(0, total.value - 1) ElMessage.success('已取消收藏') loadFavoriteCount() + // 同步刷新 JobPageHeader 组件内的收藏计数 + jobPageHeaderRef.value?.loadFavoriteCount() } } catch (e) { console.error('取消收藏失败', e) diff --git a/src/views/Login.vue b/src/views/Login.vue index 5091636..91ac88e 100644 --- a/src/views/Login.vue +++ b/src/views/Login.vue @@ -126,7 +126,7 @@ -
跳过,稍后填写
+ @@ -210,7 +210,9 @@