导师列表导师详情右侧广告,去投递弹窗等
This commit is contained in:
+208
-75
@@ -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 () => {
|
||||
|
||||
+18
-16
@@ -69,19 +69,20 @@
|
||||
</div>
|
||||
<!-- 导师基本信息 -->
|
||||
<div class="mentor-page__card-info">
|
||||
<div class="mentor-page__card-name">{{ mentor.platformName }}</div>
|
||||
<div class="mentor-page__card-meta">
|
||||
<span v-if="mentor.agencyName" class="mentor-page__card-agency">{{ mentor.agencyName }}</span>
|
||||
<span v-if="mentor.agencyName && mentor.position" class="mentor-page__card-sep">|</span>
|
||||
<span v-if="mentor.position" class="mentor-page__card-position">{{ mentor.position }}</span>
|
||||
<div class="dflex-start aliite-e flex-warp">
|
||||
<div class="mentor-page__card-name mr10">{{ mentor.platformName }}</div>
|
||||
<div class="mentor-page__card-meta">
|
||||
<span v-if="mentor.agencyName" class="mentor-page__card-agency">{{ mentor.agencyName }}</span>
|
||||
<span v-if="mentor.agencyName && mentor.position" class="mentor-page__card-sep">|</span>
|
||||
<span v-if="mentor.position" class="mentor-page__card-position">{{ mentor.position }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 标签 -->
|
||||
<div class="mentor-page__card-labels" v-if="mentor.labels && mentor.labels.length">
|
||||
<span
|
||||
v-for="(label, li) in mentor.labels.slice(0, 4)"
|
||||
:key="li"
|
||||
class="mentor-page__card-label"
|
||||
>{{ label }}</span>
|
||||
|
||||
<!-- 咨询价格 -->
|
||||
<div class="mentor-page__card-price" v-if="mentor.price">
|
||||
<span class="mentor-page__card-price-label">咨询价</span>
|
||||
<span class="mentor-page__card-price-value">¥{{ mentor.price }}</span>
|
||||
<span class="mentor-page__card-price-suffix">起</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -101,7 +102,7 @@
|
||||
class="mentor-page__detail-btn"
|
||||
@click="goToDetail(mentor)"
|
||||
><span>查看详情</span> <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.51612 4.07575C7.38279 3.94242 7.31879 3.78686 7.32412 3.60908C7.32945 3.43131 7.39901 3.27575 7.53278 3.14241C7.66612 3.02019 7.82167 2.95619 7.99946 2.95041C8.17723 2.94464 8.33278 3.00864 8.46612 3.14241L12.8661 7.54241C12.9328 7.60908 12.9801 7.6813 13.0081 7.75908C13.0361 7.83686 13.0497 7.92019 13.0488 8.00908C13.0479 8.09797 13.0339 8.18131 13.0068 8.25908C12.9797 8.33686 12.9326 8.40908 12.8654 8.47575L8.46545 12.8757C8.34323 12.998 8.19034 13.0591 8.00679 13.0591C7.82323 13.0591 7.66501 12.998 7.53212 12.8757C7.39879 12.7424 7.33212 12.584 7.33212 12.4004C7.33212 12.2169 7.39879 12.0586 7.53212 11.9257L10.7828 8.67575H3.33279C3.1439 8.67575 2.98546 8.61175 2.85745 8.48375C2.72945 8.35575 2.66567 8.19753 2.66612 8.00908C2.66656 7.82064 2.73056 7.6622 2.85812 7.53375C2.98568 7.40531 3.1439 7.34153 3.33279 7.34242L10.7828 7.34242L7.51612 4.07575Z" fill="#52CAD1"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.51612 4.07575C7.38279 3.94242 7.31879 3.78686 7.32412 3.60908C7.32945 3.43131 7.39901 3.27575 7.53278 3.14241C7.66612 3.02019 7.82167 2.95619 7.99946 2.95041C8.17723 2.94464 8.33278 3.00864 8.46612 3.14241L12.8661 7.54241C12.9328 7.60908 12.9801 7.6813 13.0081 7.75908C13.0361 7.83686 13.0497 7.92019 13.0488 8.00908C13.0479 8.09797 13.0339 8.18131 13.0068 8.25908C12.9797 8.33686 12.9326 8.40908 12.8654 8.47575L8.46545 12.8757C8.34323 12.998 8.19034 13.0591 8.00679 13.0591C7.82323 13.0591 7.66501 12.998 7.53212 12.8757C7.39879 12.7424 7.33212 12.584 7.33212 12.4004C7.33212 12.2169 7.39879 12.0586 7.53212 11.9257L10.7828 8.67575H3.33279C3.1439 8.67575 2.98546 8.61175 2.85745 8.48375C2.72945 8.35575 2.66567 8.19753 2.66612 8.00908C2.66656 7.82064 2.73056 7.6622 2.85812 7.53375C2.98568 7.40531 3.1439 7.34153 3.33279 7.34242L10.7828 7.34242L7.51612 4.07575Z" fill="currentColor"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
@@ -125,9 +126,9 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 右侧:广告区域(固定宽度 4rem) -->
|
||||
<!-- 右侧:广告区域 -->
|
||||
<div class="mentor-page__aside">
|
||||
<!-- 广告内容占位,后续填充 -->
|
||||
<MentorAsidePanel />
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@@ -139,6 +140,7 @@ import { ref, computed, onMounted, nextTick } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useStore } from 'vuex'
|
||||
import SideNav from '@/components/SideNav.vue'
|
||||
import MentorAsidePanel from '@/components/MentorAsidePanel.vue'
|
||||
import { fetchMentorList } from '@/api/mentor'
|
||||
import type { MentorItem, MentorListParams } from '@/api/mentor'
|
||||
import { toOssUrl } from '@/utils/image'
|
||||
@@ -361,7 +363,7 @@ function goToDetail(mentor: MentorItem) {
|
||||
pageNum: currentPage.value,
|
||||
scrollTop: listRef.value?.scrollTop ?? 0,
|
||||
})
|
||||
router.push(`/mentor/${mentor.id}`)
|
||||
router.push({ path: `/mentor/${mentor.id}`, query: { price: String(mentor.price) } })
|
||||
}
|
||||
|
||||
// ==================== 生命周期 ====================
|
||||
|
||||
+167
-74
@@ -1,90 +1,142 @@
|
||||
<template>
|
||||
<div class="mentor-detail dflex">
|
||||
<SideNav />
|
||||
<div class="mentor-detail__body">
|
||||
<!-- 页面标题 -->
|
||||
<div class="mentor-detail__page-title">导师详情</div>
|
||||
<div class="mentor-detail__body dflex">
|
||||
<!-- 左侧主内容区 -->
|
||||
<div class="mentor-detail__main">
|
||||
<!-- 页面标题 -->
|
||||
<div class="mentor-detail__page-title">导师详情</div>
|
||||
|
||||
<!-- 返回按钮 -->
|
||||
<div class="mentor-detail__back" @click="goBack">
|
||||
<svg viewBox="0 0 16 16" fill="none" class="mentor-detail__back-icon">
|
||||
<path d="M10 12L6 8l4-4" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
<span>返回</span>
|
||||
</div>
|
||||
<!-- 返回按钮 -->
|
||||
<div class="mentor-detail__back" @click="goBack">
|
||||
<svg viewBox="0 0 16 16" fill="none" class="mentor-detail__back-icon">
|
||||
<path d="M10 12L6 8l4-4" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
<span>返回</span>
|
||||
</div>
|
||||
|
||||
<!-- 主内容滚动区域 -->
|
||||
<div class="mentor-detail__scroll">
|
||||
<!-- 主内容滚动区域 -->
|
||||
<div class="mentor-detail__scroll">
|
||||
|
||||
<!-- 导师信息卡片 -->
|
||||
<div class="mentor-detail__card mentor-detail__info-card">
|
||||
<div class="mentor-detail__info-left">
|
||||
<!-- 头像 -->
|
||||
<div class="mentor-detail__avatar-wrap">
|
||||
<img
|
||||
v-if="mentorInfo?.picture"
|
||||
:src="toOssUrl(mentorInfo.picture)"
|
||||
class="mentor-detail__avatar"
|
||||
:alt="mentorInfo.platformName"
|
||||
/>
|
||||
<div v-else class="mentor-detail__avatar-placeholder">
|
||||
{{ mentorInfo?.platformName?.charAt(0) || '师' }}
|
||||
</div>
|
||||
</div>
|
||||
<!-- 基本信息 -->
|
||||
<div class="mentor-detail__info-text">
|
||||
<div class="mentor-detail__name">
|
||||
<span>{{ mentorInfo?.platformName }}</span>
|
||||
<div class="mentor-detail__meta">
|
||||
<span v-if="agencyName">{{ agencyName }}</span>
|
||||
<span v-if="agencyName && position"> | </span>
|
||||
<span v-if="position">{{ position }}</span>
|
||||
<!-- 导师信息卡片 -->
|
||||
<div class="mentor-detail__card mentor-detail__info-card">
|
||||
<div class="mentor-detail__info-left">
|
||||
<!-- 头像 -->
|
||||
<div class="mentor-detail__avatar-wrap">
|
||||
<img
|
||||
v-if="mentorInfo?.picture"
|
||||
:src="toOssUrl(mentorInfo.picture)"
|
||||
class="mentor-detail__avatar"
|
||||
:alt="mentorInfo.platformName"
|
||||
/>
|
||||
<div v-else class="mentor-detail__avatar-placeholder">
|
||||
{{ mentorInfo?.platformName?.charAt(0) || '师' }}
|
||||
</div>
|
||||
</div>
|
||||
<!-- 基本信息 -->
|
||||
<div class="mentor-detail__info-text">
|
||||
<div class="mentor-detail__name">
|
||||
<span>{{ mentorInfo?.platformName }}</span>
|
||||
<div class="mentor-detail__meta">
|
||||
<span v-if="agencyName">{{ agencyName }}</span>
|
||||
<span v-if="agencyName && position"> | </span>
|
||||
<span v-if="position">{{ position }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mentor-detail__location" v-if="locationText">
|
||||
所在城市 <span class="">{{ locationText }}</span>
|
||||
</div>
|
||||
<div class="mentor-detail__services" v-if="serviceLabels.length">
|
||||
服务类型 <span>{{ serviceLabels.join('、') }}</span>
|
||||
<div class="mentor-detail__location" v-if="locationText">
|
||||
所在城市 <span class="">{{ locationText }}</span>
|
||||
</div>
|
||||
<div class="mentor-detail__services" v-if="serviceLabels.length">
|
||||
服务类型 <span class="ml10">{{ serviceLabels.join('、') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 右侧:咨询价 + 立即咨询按钮 -->
|
||||
<div class="mentor-detail__consult-wrap">
|
||||
<!-- 咨询价格(从列表页传入) -->
|
||||
<div class="mentor-detail__price" v-if="mentorPrice">
|
||||
<span class="mentor-detail__price-label">咨询价</span>
|
||||
<span class="mentor-detail__price-value">¥{{ mentorPrice }}</span>
|
||||
<span class="mentor-detail__price-suffix">起</span>
|
||||
</div>
|
||||
<!-- 立即咨询按钮 -->
|
||||
<button
|
||||
class="mentor-detail__consult-btn"
|
||||
@click="openQrDialog"
|
||||
>立即咨询 →</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 立即咨询按钮 -->
|
||||
<a
|
||||
href="https://www.jianshixingqiu.com"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="mentor-detail__consult-btn"
|
||||
>立即咨询 →</a>
|
||||
</div>
|
||||
|
||||
<!-- 擅长话题卡片 -->
|
||||
<div class="mentor-detail__card" v-if="talks.length">
|
||||
<div class="mentor-detail__section-title">擅长话题</div>
|
||||
<div
|
||||
v-for="(talk, index) in talks"
|
||||
:key="talk.id"
|
||||
class="mentor-detail__talk-item"
|
||||
>
|
||||
<div class="mentor-detail__talk-title">{{ index + 1 }}.{{ talk.title }}</div>
|
||||
<div class="mentor-detail__talk-content" v-if="talk.content">{{ talk.content }}</div>
|
||||
<!-- 擅长话题卡片 -->
|
||||
<div class="mentor-detail__card" v-if="talks.length">
|
||||
<div class="mentor-detail__section-title">擅长话题</div>
|
||||
<div
|
||||
v-for="(talk, index) in talks"
|
||||
:key="talk.id"
|
||||
class="mentor-detail__talk-item"
|
||||
>
|
||||
<div class="mentor-detail__talk-title">{{ index + 1 }}.{{ talk.title }}</div>
|
||||
<div class="mentor-detail__talk-content" v-if="talk.content">{{ talk.content }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 个人介绍卡片 -->
|
||||
<div class="mentor-detail__card" v-if="mentorInfo?.introduce">
|
||||
<div class="mentor-detail__section-title">个人介绍</div>
|
||||
<div class="mentor-detail__introduce">{{ mentorInfo.introduce }}</div>
|
||||
</div>
|
||||
<!-- 个人介绍卡片 -->
|
||||
<div class="mentor-detail__card" v-if="mentorInfo?.introduce">
|
||||
<div class="mentor-detail__section-title">个人介绍</div>
|
||||
<div class="mentor-detail__introduce">{{ mentorInfo.introduce }}</div>
|
||||
</div>
|
||||
|
||||
<!-- 加载中 -->
|
||||
<div v-if="loading" class="mentor-detail__loading">
|
||||
<div class="mentor-detail__loading-spinner"></div>
|
||||
<span>加载中...</span>
|
||||
<!-- 加载中 -->
|
||||
<div v-if="loading" class="mentor-detail__loading">
|
||||
<div class="mentor-detail__loading-spinner"></div>
|
||||
<span>加载中...</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 右侧:广告区域 -->
|
||||
<div class="mentor-detail__aside">
|
||||
<MentorAsidePanel mode="detail" :mentorId="mentorId" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ==================== 二维码弹窗(遮罩 + 居中卡片) ==================== -->
|
||||
<teleport to="body">
|
||||
<div v-if="showQrDialog" class="mentor-qr-overlay" @click.self="closeQrDialog">
|
||||
<div class="mentor-qr-card">
|
||||
<!-- 头部:标题 + 关闭按钮 -->
|
||||
<div class="mentor-qr-card__header">
|
||||
<div class="mentor-qr-card__title-wrap">
|
||||
<div class="mentor-qr-card__title">扫码进入小程序预约导师咨询</div>
|
||||
<div class="mentor-qr-card__desc">查看导师价格、过往评价,并完成预约</div>
|
||||
</div>
|
||||
<div class="mentor-qr-card__close" @click="closeQrDialog">
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none">
|
||||
<path d="M18 6L6 18M6 6l12 12" stroke="#6A7282" stroke-width="2" stroke-linecap="round"/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 二维码展示区域 -->
|
||||
<div class="mentor-qr-card__qr-area">
|
||||
<div v-if="qrLoading" class="mentor-qr-card__loading">
|
||||
<div class="mentor-qr-card__spinner"></div>
|
||||
</div>
|
||||
<img
|
||||
v-else-if="qrCodeBase64"
|
||||
:src="qrCodeBase64"
|
||||
alt="小程序二维码"
|
||||
class="mentor-qr-card__qr-img"
|
||||
/>
|
||||
<div v-else class="mentor-qr-card__placeholder">二维码加载失败</div>
|
||||
</div>
|
||||
<!-- 底部提示 -->
|
||||
<div class="mentor-qr-card__footer">
|
||||
<div class="mentor-qr-card__footer-text">小程序名称:见识星球 | 大学生求职神器</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</teleport>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -92,7 +144,8 @@
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
import SideNav from '@/components/SideNav.vue'
|
||||
import { fetchMentorFullInfo, fetchMentorTalks } from '@/api/mentor'
|
||||
import MentorAsidePanel from '@/components/MentorAsidePanel.vue'
|
||||
import { fetchMentorFullInfo, fetchMentorTalks, fetchMentorQrCode } from '@/api/mentor'
|
||||
import type { MentorFullInfo, MentorTalk } from '@/api/mentor'
|
||||
import { toOssUrl } from '@/utils/image'
|
||||
|
||||
@@ -104,6 +157,9 @@ const route = useRoute()
|
||||
/** 导师 ID,从路由参数获取(字符串,避免大数精度丢失) */
|
||||
const mentorId = route.params.id as string
|
||||
|
||||
/** 咨询价格,从列表页 query 传入 */
|
||||
const mentorPrice = route.query.price as string || ''
|
||||
|
||||
// ==================== 页面状态 ====================
|
||||
|
||||
/** 是否正在加载 */
|
||||
@@ -149,12 +205,11 @@ const locationText = computed(() => {
|
||||
return parts.join(' · ')
|
||||
})
|
||||
|
||||
/** 服务类型标签 */
|
||||
/** 服务类型标签 — 取 labelRelations 中每项的 labelName */
|
||||
const serviceLabels = computed(() => {
|
||||
const supports = fullInfo.value?.supports
|
||||
if (!supports || !supports.length) return []
|
||||
const typeMap: Record<number, string> = { 0: '远程实习', 1: '简历内推', 2: '定向内推辅导' }
|
||||
return supports.map(s => typeMap[s.type] || '').filter(Boolean)
|
||||
const labels = fullInfo.value?.labelRelations
|
||||
if (!labels || !labels.length) return []
|
||||
return labels.map(l => l.labelName).filter(Boolean)
|
||||
})
|
||||
|
||||
// ==================== 数据加载 ====================
|
||||
@@ -198,4 +253,42 @@ function goBack() {
|
||||
onMounted(() => {
|
||||
loadDetail()
|
||||
})
|
||||
|
||||
// ==================== 二维码弹窗 ====================
|
||||
|
||||
/** 弹窗是否显示 */
|
||||
const showQrDialog = ref(false)
|
||||
|
||||
/** 二维码图片 base64 */
|
||||
const qrCodeBase64 = ref('')
|
||||
|
||||
/** 二维码加载状态 */
|
||||
const qrLoading = ref(false)
|
||||
|
||||
/**
|
||||
* 打开二维码弹窗,调接口获取当前导师的小程序二维码
|
||||
*/
|
||||
async function openQrDialog() {
|
||||
showQrDialog.value = true
|
||||
qrLoading.value = true
|
||||
qrCodeBase64.value = ''
|
||||
try {
|
||||
const res: any = await fetchMentorQrCode(mentorId)
|
||||
const base64 = res?.data ?? res
|
||||
if (typeof base64 === 'string' && base64.length > 0) {
|
||||
qrCodeBase64.value = base64.startsWith('data:image')
|
||||
? base64
|
||||
: `data:image/png;base64,${base64}`
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('[二维码] 获取失败', e)
|
||||
} finally {
|
||||
qrLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 关闭弹窗 */
|
||||
function closeQrDialog() {
|
||||
showQrDialog.value = false
|
||||
}
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user