导师列表导师详情右侧广告,去投递弹窗等

This commit is contained in:
2026-07-08 22:48:58 +08:00
parent c94a96d14e
commit f0b29d9abb
13 changed files with 1504 additions and 209 deletions
+208 -75
View File
@@ -203,7 +203,7 @@
<!-- 按钮行 -->
<div class="jobs-page__job-action-right">
<button class="jobs-page__job-helper" @click.stop="askAssistant(job)">问Nova</button>
<button @click.stop="handleReport(job.sourceUrl)" class="jobs-page__job-apply-btn" :class="{ 'jobs-page__job-apply-btn--active': job.applied }">去投递</button>
<button @click.stop="handleReport(job)" class="jobs-page__job-apply-btn" :class="{ 'jobs-page__job-apply-btn--active': job.applied }">去投递</button>
</div>
<!-- 不再显示 -->
<span v-if="isRecommendTab" class="jobs-page__hide-btn" @click.stop="openDislikeDialog(job)">不再显示</span>
@@ -270,17 +270,55 @@
<!-- 职位问题反馈弹窗 -->
<JobFeedbackDialog ref="feedbackDialogRef" v-model="showFeedbackDialog" :job-id="feedbackJobId" />
<!-- AI助手投递提醒弹窗 -->
<div v-if="showAgentRemindDialog" class="jobs-page__agent-remind-overlay" @click.self="closeAgentRemind">
<div class="jobs-page__agent-remind-dialog">
<p class="jobs-page__agent-remind-text">去AI助手投递岗位效果更好哦</p>
<div class="jobs-page__agent-remind-actions">
<button class="jobs-page__agent-remind-btn jobs-page__agent-remind-btn--secondary" @click="directApply">直接投</button>
<button class="jobs-page__agent-remind-btn jobs-page__agent-remind-btn--primary" @click="goToAgent">去AI助手</button>
<!-- 选择投递方式弹窗 -->
<div v-if="showAgentRemindDialog" class="jobs-page__apply-method-overlay" @click.self="closeAgentRemind">
<div class="jobs-page__apply-method-dialog">
<!-- 渐变背景层 -->
<div class="jobs-page__apply-method-bg"></div>
<!-- 标题 -->
<p class="jobs-page__apply-method-title">选择投递方式</p>
<!-- 关闭按钮 -->
<span class="jobs-page__apply-method-close" @click="closeAgentRemind">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none"><path d="M18 6L6 18M6 6l12 12" stroke="#6A7282" stroke-width="1.5" stroke-linecap="round"/></svg>
</span>
<!-- 两个选项卡 -->
<div class="jobs-page__apply-method-options">
<!-- 直接前往官网 -->
<div class="jobs-page__apply-method-card" :class="{ 'jobs-page__apply-method-card--active': applyMethodChoice === 'direct' }" @click="applyMethodChoice = 'direct'">
<p class="jobs-page__apply-method-card-title">直接前往官网</p>
<p class="jobs-page__apply-method-card-desc">手动填写网申信息<br>自行记录投递进度</p>
</div>
<!-- 智能投递助手 -->
<div class="jobs-page__apply-method-card" :class="{ 'jobs-page__apply-method-card--active': applyMethodChoice === 'agent' }" @click="applyMethodChoice = 'agent'">
<!-- 左下角圆形装饰 -->
<div class="jobs-page__apply-method-card-circle"></div>
<div class="jobs-page__apply-method-card-header">
<svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M8 1L10 5.5L15 6.5L11.5 10L12.5 15L8 12.5L3.5 15L4.5 10L1 6.5L6 5.5L8 1Z" fill="#52CAD1"/></svg>
<span class="jobs-page__apply-method-card-title jobs-page__apply-method-card-title--accent">智能投递助手</span>
</div>
<p class="jobs-page__apply-method-card-desc">{{ agentCardDesc }}</p>
</div>
</div>
<!-- 底部操作按钮 -->
<button class="jobs-page__apply-method-action" :class="applyMethodActionClass" :disabled="!applyMethodChoice" @click="handleApplyMethodAction">
<svg v-if="applyMethodActionIcon" width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_166_1554)">
<path d="M6.0714 3.63204C6.47007 2.46537 8.08207 2.43004 8.55474 3.52604L8.59474 3.63271L9.13274 5.20604C9.25603 5.56686 9.45527 5.89705 9.71702 6.17432C9.97877 6.4516 10.2969 6.66952 10.6501 6.81337L10.7947 6.86737L12.3681 7.40471C13.5347 7.80337 13.5701 9.41537 12.4747 9.88804L12.3681 9.92804L10.7947 10.466C10.4338 10.5893 10.1035 10.7885 9.82608 11.0502C9.54869 11.312 9.33067 11.6302 9.18674 11.9834L9.13274 12.1274L8.5954 13.7014C8.19674 14.868 6.58474 14.9034 6.11274 13.808L6.0714 13.7014L5.53407 12.128C5.41086 11.7671 5.21165 11.4368 4.9499 11.1594C4.68814 10.882 4.36993 10.664 4.01674 10.52L3.87274 10.466L2.2994 9.92871C1.13207 9.53004 1.09674 7.91804 2.19274 7.44604L2.2994 7.40471L3.87274 6.86737C4.23356 6.74408 4.56374 6.54484 4.84102 6.28309C5.11829 6.02134 5.33621 5.70317 5.48007 5.35004L5.53407 5.20604L6.0714 3.63204ZM7.3334 4.06271L6.79607 5.63604C6.60833 6.18624 6.30292 6.68891 5.90109 7.10904C5.49927 7.52917 5.0107 7.85666 4.4694 8.06871L4.30274 8.12937L2.7294 8.66671L4.30274 9.20404C4.85294 9.39178 5.35561 9.6972 5.77573 10.099C6.19586 10.5008 6.52335 10.9894 6.7354 11.5307L6.79607 11.6974L7.3334 13.2707L7.87074 11.6974C8.05848 11.1472 8.36389 10.6445 8.76571 10.2244C9.16753 9.80425 9.65611 9.47676 10.1974 9.26471L10.3641 9.20471L11.9374 8.66671L10.3641 8.12937C9.81387 7.94163 9.3112 7.63622 8.89107 7.2344C8.47094 6.83258 8.14345 6.34401 7.9314 5.80271L7.8714 5.63604L7.3334 4.06271ZM12.6667 1.33337C12.7915 1.33337 12.9137 1.36836 13.0195 1.43436C13.1253 1.50035 13.2105 1.59471 13.2654 1.70671L13.2974 1.78471L13.5307 2.46871L14.2154 2.70204C14.3404 2.7445 14.45 2.82312 14.5302 2.92792C14.6105 3.03272 14.6578 3.15899 14.6663 3.29072C14.6747 3.42246 14.6438 3.55373 14.5775 3.6679C14.5113 3.78207 14.4126 3.874 14.2941 3.93204L14.2154 3.96404L13.5314 4.19737L13.2981 4.88204C13.2555 5.00699 13.1769 5.11651 13.072 5.1967C12.9672 5.2769 12.8409 5.32417 12.7092 5.33252C12.5774 5.34088 12.4462 5.30994 12.3321 5.24363C12.2179 5.17732 12.1261 5.07862 12.0681 4.96004L12.0361 4.88204L11.8027 4.19804L11.1181 3.96471C10.9931 3.92224 10.8835 3.84363 10.8033 3.73883C10.723 3.63403 10.6756 3.50776 10.6672 3.37603C10.6588 3.24429 10.6897 3.11302 10.7559 2.99885C10.8222 2.88468 10.9208 2.79275 11.0394 2.73471L11.1181 2.70271L11.8021 2.46937L12.0354 1.78471C12.0804 1.65299 12.1654 1.53865 12.2786 1.45771C12.3918 1.37677 12.5276 1.33329 12.6667 1.33337Z" fill="white"/>
</g>
<defs>
<clipPath id="clip0_166_1554">
<rect width="16" height="16" fill="white"/>
</clipPath>
</defs>
</svg>
{{ applyMethodActionText }}
</button>
</div>
</div>
<!-- 会员购买弹窗 -->
<MemberDialog v-model="showMemberDialog" />
</div>
</template>
@@ -294,6 +332,7 @@ import AiChat from '@/components/AiChat.vue'
import JobPageHeader from '@/components/JobPageHeader.vue'
import JobDislikeDialog from '@/components/JobDislikeDialog.vue'
import JobFeedbackDialog from '@/components/JobFeedbackDialog.vue'
import MemberDialog from '@/components/MemberDialog.vue'
import IndustrySelector from '@/components/tools/IndustrySelector.vue'
import JobCategorySelector from '@/components/tools/JobCategorySelector.vue'
import RegionSelector from '@/components/tools/RegionSelector.vue'
@@ -304,6 +343,8 @@ import type { JobListItem, JobListParams, FavoriteListParams, ApplyListParams, A
import { getTimeEvent, setTimeEvent, timestampToLocalDateTime } from '@/utils/time'
import { fetchIndustryTree, fetchJobCategoryTree } from '@/api/common'
import type { IndustryItem, JobCategoryItem } from '@/api/common'
import { fetchAgentConfig, applyJob } from '@/api/agent'
import { ElMessage } from 'element-plus'
// 2. 注意:这里的字符串不需要再手动转义双引号了
@@ -668,9 +709,9 @@ function closeDropdownOnClickOutside(e: MouseEvent) {
}
}
/** 跳转到原链接 — 带 AI 助手提醒弹窗频率控制 */
/** 跳转到原链接 — 选择投递方式弹窗 */
/**
* 提醒弹窗频率逻辑说明
* 旧的提醒弹窗频率逻辑(已注销,保留不删)
* 1. 未登录用户:直接跳转外部链接,不弹窗
* 2. 已登录用户首次点击(无 ai_agent_remind_first 缓存):弹窗提醒,记录第一次提醒时间
* 3. 有第一次记录但无第二次记录:
@@ -680,89 +721,181 @@ function closeDropdownOnClickOutside(e: MouseEvent) {
* - 距第二次记录 ≤ 7天:直接跳转,不弹窗
* - 距第二次记录 > 7天:弹窗提醒,更新第二次记录时间为当前时间
*/
function handleReport(url: string) {
// 暂存当前要跳转的链接
pendingApplyUrl.value = url
// function handleReportOld(url: string) {
// pendingApplyUrl.value = url
// if (!isAuthenticated.value) { window.open(url, '_blank'); return }
// const userId = store.state.userInfo?.id
// if (!userId) { window.open(url, '_blank'); return }
// const now = Date.now()
// const firstTime = getTimeEvent('ai_agent_remind_first', userId)
// const secondTime = getTimeEvent('ai_agent_remind_second', userId)
// if (!firstTime) {
// setTimeEvent('ai_agent_remind_first', userId, timestampToLocalDateTime(now))
// showAgentRemindDialog.value = true; return
// }
// if (!secondTime) {
// const firstTimestamp = new Date(firstTime).getTime()
// const diffHours = (now - firstTimestamp) / (1000 * 60 * 60)
// if (diffHours <= 1) { window.open(url, '_blank') }
// else { setTimeEvent('ai_agent_remind_second', userId, timestampToLocalDateTime(now)); showAgentRemindDialog.value = true }
// return
// }
// const secondTimestamp = new Date(secondTime).getTime()
// const diffDays = (now - secondTimestamp) / (1000 * 60 * 60 * 24)
// if (diffDays <= 7) { window.open(url, '_blank') }
// else { setTimeEvent('ai_agent_remind_second', userId, timestampToLocalDateTime(now)); showAgentRemindDialog.value = true }
// }
// 未登录直接跳转
if (!isAuthenticated.value) {
window.open(url, '_blank')
return
}
const userId = store.state.userInfo?.id
if (!userId) {
window.open(url, '_blank')
return
}
const now = Date.now()
const firstTime = getTimeEvent('ai_agent_remind_first', userId)
const secondTime = getTimeEvent('ai_agent_remind_second', userId)
if (!firstTime) {
// 情况1:从未弹过 → 弹窗,记录第一次时间
setTimeEvent('ai_agent_remind_first', userId, timestampToLocalDateTime(now))
showAgentRemindDialog.value = true
return
}
if (!secondTime) {
// 情况2:有第一次记录,无第二次记录 → 检查是否超过1小时
const firstTimestamp = new Date(firstTime).getTime()
const diffHours = (now - firstTimestamp) / (1000 * 60 * 60)
if (diffHours <= 1) {
// 1小时内不弹窗,直接跳转
window.open(url, '_blank')
} else {
// 超过1小时 → 弹窗,记录第二次时间
setTimeEvent('ai_agent_remind_second', userId, timestampToLocalDateTime(now))
showAgentRemindDialog.value = true
}
return
}
// 情况3:有第二次记录 → 检查是否超过7天
const secondTimestamp = new Date(secondTime).getTime()
const diffDays = (now - secondTimestamp) / (1000 * 60 * 60 * 24)
if (diffDays <= 7) {
// 7天内不弹窗,直接跳转
window.open(url, '_blank')
} else {
// 超过7天 → 弹窗,更新第二次记录时间
setTimeEvent('ai_agent_remind_second', userId, timestampToLocalDateTime(now))
showAgentRemindDialog.value = true
}
/** 点击"去投递"按钮 — 每次都弹出选择投递方式弹窗 */
function handleReport(job: JobListItem) {
// 暂存当前岗位信息
pendingApplyUrl.value = job.sourceUrl
pendingApplyJobId.value = job.id
// 重置选项状态
applyMethodChoice.value = null
// 每次触发都展示弹窗
showAgentRemindDialog.value = true
// 异步加载 AI 助手配置状态
loadAgentConfigStatus()
}
// ==================== AI助手提醒弹窗状态 ====================
// ==================== 选择投递方式弹窗状态 ====================
/** AI助手提醒弹窗是否显示 */
/** 弹窗是否显示 */
const showAgentRemindDialog = ref(false)
/** 暂存待跳转的外部链接 */
const pendingApplyUrl = ref('')
/** 关闭AI助手提醒弹窗 */
/** 暂存当前触发弹窗的岗位 ID */
const pendingApplyJobId = ref('')
/** 用户在弹窗中选择的投递方式:null=未选择, 'direct'=直接前往官网, 'agent'=智能投递助手 */
const applyMethodChoice = ref<'direct' | 'agent' | null>(null)
/** AI助手是否已完成配置(status === 1 */
const agentConfigReady = ref(false)
/** 是否正在加载 AI 助手配置 */
const agentConfigLoading = ref(false)
/** 会员购买弹窗显示状态 */
const showMemberDialog = ref(false)
/** 用户会员状态 — 从 store 读取 */
const memberStatus = computed(() => store.state.memberStatus)
/** 用户是否是会员(正式或试用) */
const isMember = computed(() => !!memberStatus.value?.isMember)
/** 智能投递助手选项卡内的描述文字 — 根据会员和配置状态动态显示 */
const agentCardDesc = computed(() => {
if (!isMember.value) {
return '自动填写网申信息\n统一管理投递进度'
}
if (!agentConfigReady.value) {
return '完成助手设置后\n可使用智能投递'
}
return '将该岗位加入待投递列表\n在投递助手中统一处理'
})
/** 底部操作按钮文字 */
const applyMethodActionText = computed(() => {
if (!applyMethodChoice.value) return '请选择投递方式'
if (applyMethodChoice.value === 'direct') return '去官网投递'
// agent 分支
if (!isMember.value) return '开通会员使用'
if (!agentConfigReady.value) return '去完成设置'
return '加入待投递列表'
})
/** 底部操作按钮是否显示 icon */
const applyMethodActionIcon = computed(() => {
if (!applyMethodChoice.value) return false
if (applyMethodChoice.value === 'direct') return false
// agent 分支都有 icon
return true
})
/** 底部操作按钮样式 class */
const applyMethodActionClass = computed(() => {
if (!applyMethodChoice.value) return 'jobs-page__apply-method-action--disabled'
return 'jobs-page__apply-method-action--active'
})
/** 加载 AI 助手配置状态 */
async function loadAgentConfigStatus() {
if (agentConfigLoading.value) return
agentConfigLoading.value = true
try {
const res = await fetchAgentConfig()
if (res.code === '0' && res.data && res.data.status === 1) {
agentConfigReady.value = true
} else {
agentConfigReady.value = false
}
} catch {
agentConfigReady.value = false
} finally {
agentConfigLoading.value = false
}
}
/** 关闭选择投递方式弹窗 */
function closeAgentRemind() {
showAgentRemindDialog.value = false
pendingApplyUrl.value = ''
pendingApplyJobId.value = ''
}
/** 点击"直接投"按钮 — 关闭弹窗并跳转外部链接 */
function directApply() {
showAgentRemindDialog.value = false
if (pendingApplyUrl.value) {
window.open(pendingApplyUrl.value, '_blank')
/** 底部操作按钮点击处理 */
async function handleApplyMethodAction() {
if (!applyMethodChoice.value) return
if (applyMethodChoice.value === 'direct') {
// 直接前往官网
showAgentRemindDialog.value = false
if (pendingApplyUrl.value) {
window.open(pendingApplyUrl.value, '_blank')
}
pendingApplyUrl.value = ''
pendingApplyJobId.value = ''
return
}
pendingApplyUrl.value = ''
}
/** 点击"去AI助手"按钮 — 关闭弹窗并跳转到 Agent 页面 */
function goToAgent() {
// 智能投递助手分支
if (!isMember.value) {
// 非会员 — 打开会员购买弹窗
showAgentRemindDialog.value = false
showMemberDialog.value = true
pendingApplyUrl.value = ''
pendingApplyJobId.value = ''
return
}
if (!agentConfigReady.value) {
// 有会员但没完成 AI 助手配置 — 跳转 Agent 页面
showAgentRemindDialog.value = false
pendingApplyUrl.value = ''
pendingApplyJobId.value = ''
router.push('/agent')
return
}
// 有会员且配置完成 — 加入待投递列表
try {
const res = await applyJob({ jobId: pendingApplyJobId.value, status: -1 })
if (res.code === '0') {
ElMessage.success('已加入待投递列表')
} else {
ElMessage.error(res.msg || '加入待投递列表失败')
}
} catch {
ElMessage.error('加入待投递列表失败')
}
showAgentRemindDialog.value = false
pendingApplyUrl.value = ''
router.push('/agent')
pendingApplyJobId.value = ''
}
onMounted(async () => {