Compare commits
2
Commits
5f4aab8d04
...
27d2592d06
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
27d2592d06 | ||
|
|
c81c9b79dd |
@@ -251,17 +251,15 @@
|
|||||||
|
|
||||||
&__tag {
|
&__tag {
|
||||||
font-size: 0.12rem;
|
font-size: 0.12rem;
|
||||||
color: $text-dark;
|
color: $accent;
|
||||||
background: $bg-main;
|
background: #fff;
|
||||||
border: 1px solid $border-color;
|
border: 1px solid $accent;
|
||||||
border-radius: 0.16rem;
|
border-radius: 0.04rem;
|
||||||
padding: 0.05rem 0.16rem;
|
padding: 0.05rem 0.08rem;
|
||||||
transition: all 0.2s;
|
transition: all 0.2s;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
border-color: $accent;
|
|
||||||
color: $accent;
|
|
||||||
background: $theme-color;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -148,6 +148,9 @@
|
|||||||
letter-spacing: 0.03rem;
|
letter-spacing: 0.03rem;
|
||||||
color: #111;
|
color: #111;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
>span{
|
||||||
|
width: 1.51rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 副标题描述
|
// 副标题描述
|
||||||
|
|||||||
@@ -726,7 +726,7 @@
|
|||||||
background: $bg-white;
|
background: $bg-white;
|
||||||
border: 1px solid $accent;
|
border: 1px solid $accent;
|
||||||
border-radius: 0.04rem;
|
border-radius: 0.04rem;
|
||||||
padding: 0.05rem 0.14rem;
|
padding: 0.05rem 0.08rem;
|
||||||
transition: all 0.2s;
|
transition: all 0.2s;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
/**
|
||||||
|
* 业务配置文件 — 集中管理可开关的业务策略
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** 是否开启"个人资料为空时强制跳转到登录页简历上传"机制 */
|
||||||
|
export const ENABLE_FORCE_RESUME_UPLOAD = true
|
||||||
|
|
||||||
|
/** 浏览器插件目标版本(用于判断用户是否需要更新插件) */
|
||||||
|
export const PLUGIN_TARGET_VERSION = '0.1.0'
|
||||||
@@ -245,7 +245,7 @@
|
|||||||
<div class="h1 border-EDF9FA-bottom-2 mrl14"></div>
|
<div class="h1 border-EDF9FA-bottom-2 mrl14"></div>
|
||||||
|
|
||||||
<!-- 技能 -->
|
<!-- 技能 -->
|
||||||
<div ref="skillsRef" class="profile-page-content__card" :class="{ 'profile-page-content__card-hover': editingModule !== 'skills' }">
|
<div ref="skillsRef" class="profile-page-content__card mtb16" :class="{ 'profile-page-content__card-hover': editingModule !== 'skills' }">
|
||||||
<template v-if="editingModule !== 'skills'">
|
<template v-if="editingModule !== 'skills'">
|
||||||
<div class="profile-page-content__card-header">
|
<div class="profile-page-content__card-header">
|
||||||
<h3 class="profile-page-content__card-title">技能</h3>
|
<h3 class="profile-page-content__card-title">技能</h3>
|
||||||
@@ -270,7 +270,7 @@
|
|||||||
<div class="h1 border-EDF9FA-bottom-2 mrl14"></div>
|
<div class="h1 border-EDF9FA-bottom-2 mrl14"></div>
|
||||||
|
|
||||||
<!-- 证书 -->
|
<!-- 证书 -->
|
||||||
<div ref="certificateRef" class="profile-page-content__card" :class="{ 'profile-page-content__card-hover': editingModule !== 'certificate' }">
|
<div ref="certificateRef" class="profile-page-content__card mtb16" :class="{ 'profile-page-content__card-hover': editingModule !== 'certificate' }">
|
||||||
<template v-if="editingModule !== 'certificate'">
|
<template v-if="editingModule !== 'certificate'">
|
||||||
<div class="profile-page-content__card-header">
|
<div class="profile-page-content__card-header">
|
||||||
<h3 class="profile-page-content__card-title">证书</h3>
|
<h3 class="profile-page-content__card-title">证书</h3>
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ watch(() => props.modelValue, async (val) => {
|
|||||||
const inviteCode = computed(() => store.state.userInfo?.inviteCode || 'NOVA2026')
|
const inviteCode = computed(() => store.state.userInfo?.inviteCode || 'NOVA2026')
|
||||||
|
|
||||||
/** 邀请链接地址 */
|
/** 邀请链接地址 */
|
||||||
const inviteUrl = computed(() => `https://www.offerpai.com.cn/invite?code=${inviteCode.value}`)
|
const inviteUrl = computed(() => `https://www.offerpai.com.cn/?invite_code=${inviteCode.value}`)
|
||||||
|
|
||||||
/** 复制链接到剪贴板 */
|
/** 复制链接到剪贴板 */
|
||||||
async function handleCopy() {
|
async function handleCopy() {
|
||||||
|
|||||||
+47
-2
@@ -3,6 +3,8 @@ import type { RouteRecordRaw } from 'vue-router'
|
|||||||
import store from '@/stores'
|
import store from '@/stores'
|
||||||
import { checkLogin } from '@/api/auth'
|
import { checkLogin } from '@/api/auth'
|
||||||
import { fetchMemberStatus } from '@/api/member'
|
import { fetchMemberStatus } from '@/api/member'
|
||||||
|
import { fetchProfile } from '@/api/profile'
|
||||||
|
import { ENABLE_FORCE_RESUME_UPLOAD } from '@/business.config'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 静态路由 — 不需要权限,应用启动就注册
|
* 静态路由 — 不需要权限,应用启动就注册
|
||||||
@@ -98,9 +100,13 @@ router.beforeEach(async (to, _from, next) => {
|
|||||||
// 每次页面切换时静默刷新路由菜单数据(更新权限状态),不阻塞导航
|
// 每次页面切换时静默刷新路由菜单数据(更新权限状态),不阻塞导航
|
||||||
store.dispatch('refreshDynamicMenus')
|
store.dispatch('refreshDynamicMenus')
|
||||||
|
|
||||||
// 已登录用户访问登录页,直接跳转首页
|
// 已登录用户访问登录页,如果带 resumeUpload=1 参数则放行(简历上传流程),否则跳转首页
|
||||||
if (to.name === 'Login' && store.state.isAuthenticated) {
|
if (to.name === 'Login' && store.state.isAuthenticated) {
|
||||||
next({ name: 'Home' })
|
if (to.query.resumeUpload === '1') {
|
||||||
|
next()
|
||||||
|
} else {
|
||||||
|
next({ name: 'Home' })
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,6 +118,16 @@ router.beforeEach(async (to, _from, next) => {
|
|||||||
if (res.code === '0' && res.data === true) {
|
if (res.code === '0' && res.data === true) {
|
||||||
// Cookie 有效,同步前端状态并放行
|
// Cookie 有效,同步前端状态并放行
|
||||||
store.commit('SET_AUTHENTICATED', true)
|
store.commit('SET_AUTHENTICATED', true)
|
||||||
|
// 检查个人资料是否为空(强制简历上传机制)
|
||||||
|
if (ENABLE_FORCE_RESUME_UPLOAD) {
|
||||||
|
try {
|
||||||
|
const profileRes = await fetchProfile()
|
||||||
|
if (profileRes.code === '0' && !profileRes.data) {
|
||||||
|
next({ name: 'Login', query: { resumeUpload: '1' } })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} catch { /* 接口异常不阻断 */ }
|
||||||
|
}
|
||||||
next()
|
next()
|
||||||
} else {
|
} else {
|
||||||
// 未登录或 Cookie 失效 — 跳转登录页
|
// 未登录或 Cookie 失效 — 跳转登录页
|
||||||
@@ -129,6 +145,16 @@ router.beforeEach(async (to, _from, next) => {
|
|||||||
// Agent 页面需要会员权限 — 等待会员信息加载后判断
|
// Agent 页面需要会员权限 — 等待会员信息加载后判断
|
||||||
if (to.name === 'Agent') {
|
if (to.name === 'Agent') {
|
||||||
try {
|
try {
|
||||||
|
// 先检查个人资料是否为空(强制简历上传机制)
|
||||||
|
if (ENABLE_FORCE_RESUME_UPLOAD) {
|
||||||
|
try {
|
||||||
|
const profileRes = await fetchProfile()
|
||||||
|
if (profileRes.code === '0' && !profileRes.data) {
|
||||||
|
next({ name: 'Login', query: { resumeUpload: '1' } })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} catch { /* 接口异常不阻断 */ }
|
||||||
|
}
|
||||||
// 确保会员信息已加载到 store
|
// 确保会员信息已加载到 store
|
||||||
if (!store.state.memberStatus) {
|
if (!store.state.memberStatus) {
|
||||||
const res = await fetchMemberStatus()
|
const res = await fetchMemberStatus()
|
||||||
@@ -169,6 +195,25 @@ router.beforeEach(async (to, _from, next) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 已登录 + 非首页/登录页/移动首页:检查个人资料是否为空,为空则强制跳转简历上传
|
||||||
|
const skipProfileCheck = ['Home', 'HomeMobile', 'Login']
|
||||||
|
if (
|
||||||
|
ENABLE_FORCE_RESUME_UPLOAD &&
|
||||||
|
store.state.isAuthenticated &&
|
||||||
|
!skipProfileCheck.includes(to.name as string)
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
const profileRes = await fetchProfile()
|
||||||
|
if (profileRes.code === '0' && !profileRes.data) {
|
||||||
|
// 个人资料为空 — 跳转到登录页简历上传步骤
|
||||||
|
next({ name: 'Login', query: { resumeUpload: '1' } })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// 接口异常不阻断导航
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
next()
|
next()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
+2
-1
@@ -12,6 +12,7 @@ import type { UserInfo } from '@/api/auth'
|
|||||||
import { fetchMemberStatus } from '@/api/member'
|
import { fetchMemberStatus } from '@/api/member'
|
||||||
import type { MemberStatus } from '@/api/member'
|
import type { MemberStatus } from '@/api/member'
|
||||||
import { createChannelBridge } from '@/utils/channelBridge'
|
import { createChannelBridge } from '@/utils/channelBridge'
|
||||||
|
import { PLUGIN_TARGET_VERSION } from '@/business.config'
|
||||||
|
|
||||||
/** 招聘分类选项:label → 接口参数 recruitCategory */
|
/** 招聘分类选项:label → 接口参数 recruitCategory */
|
||||||
export const JOB_TYPE_OPTIONS: { label: string; value: number }[] = [
|
export const JOB_TYPE_OPTIONS: { label: string; value: number }[] = [
|
||||||
@@ -218,7 +219,7 @@ export default createStore<RootState>({
|
|||||||
/** 插件当前使用版本 */
|
/** 插件当前使用版本 */
|
||||||
plugUseVersion: '',
|
plugUseVersion: '',
|
||||||
/** 插件目标版本 */
|
/** 插件目标版本 */
|
||||||
plugTargetVersion: '0.1.0',
|
plugTargetVersion: PLUGIN_TARGET_VERSION,
|
||||||
/** 插件是否是最新版本 */
|
/** 插件是否是最新版本 */
|
||||||
isLatestPlugin: true,
|
isLatestPlugin: true,
|
||||||
},
|
},
|
||||||
|
|||||||
+4
-4
@@ -13,8 +13,8 @@
|
|||||||
<img src="@/assets/images/logo.png" alt="Offer派" class="home-nav__logo-img" />
|
<img src="@/assets/images/logo.png" alt="Offer派" class="home-nav__logo-img" />
|
||||||
</div>
|
</div>
|
||||||
<div class="dflex-end aliite-c">
|
<div class="dflex-end aliite-c">
|
||||||
<!-- 未登录时显示登陆和立即加入按钮 -->
|
<!-- 未登录时显示登录和立即加入按钮 -->
|
||||||
<div v-if="!store.state.isAuthenticated" class="fs14 color-1 mr40 fw600 cursor-po" @click="handleLoginClick">登陆</div>
|
<div v-if="!store.state.isAuthenticated" class="fs14 color-1 mr40 fw600 cursor-po" @click="handleLoginClick">登录</div>
|
||||||
<button v-if="!store.state.isAuthenticated" class="home-nav__btn" @click="router.push('/jobs')">立即加入</button>
|
<button v-if="!store.state.isAuthenticated" class="home-nav__btn" @click="router.push('/jobs')">立即加入</button>
|
||||||
<!-- 已登录时显示进入平台按钮 -->
|
<!-- 已登录时显示进入平台按钮 -->
|
||||||
<button v-else class="home-nav__btn" @click="router.push('/jobs')">进入平台</button>
|
<button v-else class="home-nav__btn" @click="router.push('/jobs')">进入平台</button>
|
||||||
@@ -23,7 +23,7 @@
|
|||||||
</header>
|
</header>
|
||||||
<div class="home-hero__inner">
|
<div class="home-hero__inner">
|
||||||
<div class="home-hero__left">
|
<div class="home-hero__left">
|
||||||
<h1 class="home-hero__title">AI驱动的<br/><span class="dib w56"></span>一站式求职辅助平台</h1>
|
<h1 class="home-hero__title">Offer派,<br/><span class="dib"></span>收offer就是快!</h1>
|
||||||
<p class="home-hero__desc">智能匹配职位、自动填写申请、量身定制简历、推荐内部人脉——<br/>不到1分钟,统统搞定!</p>
|
<p class="home-hero__desc">智能匹配职位、自动填写申请、量身定制简历、推荐内部人脉——<br/>不到1分钟,统统搞定!</p>
|
||||||
<button class="home-hero__cta" @click="router.push('/jobs')">免费体验</button>
|
<button class="home-hero__cta" @click="router.push('/jobs')">免费体验</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -687,7 +687,7 @@ onMounted(() => {
|
|||||||
store.dispatch('loadCommonData')
|
store.dispatch('loadCommonData')
|
||||||
})
|
})
|
||||||
|
|
||||||
/** 点击登陆按钮 — 跳转到登录页面,登录成功后跳转到 jobs 页面 */
|
/** 点击登录按钮 — 跳转到登录页面,登录成功后跳转到 jobs 页面 */
|
||||||
function handleLoginClick() {
|
function handleLoginClick() {
|
||||||
router.push({ name: 'Login', query: { redirect: '/jobs' } })
|
router.push({ name: 'Login', query: { redirect: '/jobs' } })
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -941,6 +941,13 @@ function handleVisibilityChange() {
|
|||||||
|
|
||||||
/** 挂载时初始化动画 */
|
/** 挂载时初始化动画 */
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
// 如果路由带 resumeUpload=1 参数,直接跳到简历上传步骤
|
||||||
|
if (route.query.resumeUpload === '1') {
|
||||||
|
step.value = 3
|
||||||
|
resumeState.value = 'upload'
|
||||||
|
loginRedirect = (route.query.redirect as string) || '/jobs'
|
||||||
|
}
|
||||||
|
|
||||||
// 设置背景图
|
// 设置背景图
|
||||||
if (stageRef.value) {
|
if (stageRef.value) {
|
||||||
stageRef.value.style.backgroundImage = `url(${bgSrc})`
|
stageRef.value.style.backgroundImage = `url(${bgSrc})`
|
||||||
|
|||||||
@@ -498,7 +498,7 @@
|
|||||||
<!-- 技能 -->
|
<!-- 技能 -->
|
||||||
<div ref="resumeSectionSkillsRef" class="resume-detail__card" :class="{ 'resume-detail__card-hover': editModule !== 'skills' || !showEditDrawer }">
|
<div ref="resumeSectionSkillsRef" class="resume-detail__card" :class="{ 'resume-detail__card-hover': editModule !== 'skills' || !showEditDrawer }">
|
||||||
<template v-if="editModule !== 'skills' || !showEditDrawer">
|
<template v-if="editModule !== 'skills' || !showEditDrawer">
|
||||||
<div class="resume-detail__section-header">
|
<div class="resume-detail__section-header pl0">
|
||||||
<h3 class="resume-detail__section-title">技能</h3>
|
<h3 class="resume-detail__section-title">技能</h3>
|
||||||
<!-- 编辑按钮 — hover时显示 -->
|
<!-- 编辑按钮 — hover时显示 -->
|
||||||
<button class="resume-detail__edit-btn-inline" @click="openEditDrawer('skills')" aria-label="编辑技能">
|
<button class="resume-detail__edit-btn-inline" @click="openEditDrawer('skills')" aria-label="编辑技能">
|
||||||
@@ -529,7 +529,7 @@
|
|||||||
<!-- 证书 -->
|
<!-- 证书 -->
|
||||||
<div ref="resumeSectionCertificateRef" class="resume-detail__card" :class="{ 'resume-detail__card-hover': editModule !== 'certificate' || !showEditDrawer }">
|
<div ref="resumeSectionCertificateRef" class="resume-detail__card" :class="{ 'resume-detail__card-hover': editModule !== 'certificate' || !showEditDrawer }">
|
||||||
<template v-if="editModule !== 'certificate' || !showEditDrawer">
|
<template v-if="editModule !== 'certificate' || !showEditDrawer">
|
||||||
<div class="resume-detail__section-header">
|
<div class="resume-detail__section-header pl0">
|
||||||
<h3 class="resume-detail__section-title">证书</h3>
|
<h3 class="resume-detail__section-title">证书</h3>
|
||||||
<!-- 编辑按钮 — hover时显示 -->
|
<!-- 编辑按钮 — hover时显示 -->
|
||||||
<button class="resume-detail__edit-btn-inline" @click="openEditDrawer('certificate')" aria-label="编辑证书">
|
<button class="resume-detail__edit-btn-inline" @click="openEditDrawer('certificate')" aria-label="编辑证书">
|
||||||
|
|||||||
Reference in New Issue
Block a user