移动端首页
This commit is contained in:
Vendored
-2
@@ -25,8 +25,6 @@ declare module 'vue' {
|
||||
AiChat: typeof import('./src/components/AiChat.vue')['default']
|
||||
AiThinkingIndicator: typeof import('./src/components/tools/AiThinkingIndicator.vue')['default']
|
||||
ElButton: typeof import('element-plus/es')['ElButton']
|
||||
ElCarousel: typeof import('element-plus/es')['ElCarousel']
|
||||
ElCarouselItem: typeof import('element-plus/es')['ElCarouselItem']
|
||||
ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider']
|
||||
ElDatePicker: typeof import('element-plus/es')['ElDatePicker']
|
||||
ElDialog: typeof import('element-plus/es')['ElDialog']
|
||||
|
||||
@@ -715,3 +715,23 @@ export function resolveDiagnosisIssue(issueId: string) {
|
||||
}],
|
||||
}).then(res => res.data)
|
||||
}
|
||||
|
||||
// ==================== 修改简历名称和目标岗位(新 Java 后端接口) ====================
|
||||
|
||||
/** 修改简历名称/目标岗位 请求参数 */
|
||||
export interface UpdateResumeInfoParams {
|
||||
/** 简历 ID(必需,字符串避免大整数精度丢失) */
|
||||
resumeId: string
|
||||
/** 简历名称(可选,<= 100 字符) */
|
||||
resumeName?: string
|
||||
/** 目标岗位(可选,<= 100 字符) */
|
||||
targetPosition?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改简历名称和目标岗位
|
||||
* POST /resume/updateInfo
|
||||
*/
|
||||
export function updateResumeInfo(data: UpdateResumeInfoParams) {
|
||||
return request.post<any, ApiResult>('/resume/updateInfo', data)
|
||||
}
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 7.4 KiB |
@@ -2,6 +2,7 @@
|
||||
|
||||
// 页面样式
|
||||
@use './pages/home.scss';
|
||||
@use './pages/home-mobile.scss';
|
||||
@use './pages/agent.scss';
|
||||
@use './pages/jobs.scss';
|
||||
@use './pages/resume.scss';
|
||||
|
||||
@@ -0,0 +1,623 @@
|
||||
@use '../variables' as *;
|
||||
|
||||
/* ==================== 移动端首页样式 ==================== */
|
||||
.home-mobile {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
margin: 0 auto;
|
||||
background: #FFFFFF;
|
||||
font-size: 0.14rem;
|
||||
padding-bottom: 0.94rem; /* 底部固定按钮的高度 */
|
||||
overflow-x: hidden;
|
||||
|
||||
/* 背景装饰色块 */
|
||||
&__orb {
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
|
||||
&--top {
|
||||
width: 5.40rem;
|
||||
height: 3.00rem;
|
||||
left: -0.80rem;
|
||||
top: -1.20rem;
|
||||
background: rgba(183, 237, 233, 0.58);
|
||||
opacity: 0.5;
|
||||
filter: blur(0.35rem);
|
||||
}
|
||||
|
||||
&--hero {
|
||||
width: 3.00rem;
|
||||
height: 2.20rem;
|
||||
left: 0.64rem;
|
||||
top: 2.10rem;
|
||||
background: #EAF8F7;
|
||||
opacity: 0.55;
|
||||
filter: blur(0.275rem);
|
||||
}
|
||||
}
|
||||
|
||||
/* 顶部 Logo */
|
||||
&__header {
|
||||
position: relative;
|
||||
padding: 0.16rem 0.20rem;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
&__logo {
|
||||
width: 0.95rem;
|
||||
height: 0.23rem;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
/* Hero 区域 */
|
||||
&__hero {
|
||||
position: relative;
|
||||
padding: 0.30rem 0.24rem 0;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
&__eyebrow {
|
||||
font-weight: 700;
|
||||
font-size: 0.12rem;
|
||||
line-height: 0.15rem;
|
||||
color: #42A8B3;
|
||||
margin-bottom: 0.10rem;
|
||||
}
|
||||
|
||||
&__headline {
|
||||
font-weight: 700;
|
||||
font-size: 0.38rem;
|
||||
line-height: 0.46rem;
|
||||
color: #12383A;
|
||||
margin-bottom: 0.32rem;
|
||||
}
|
||||
|
||||
&__subtitle {
|
||||
font-weight: 400;
|
||||
font-size: 0.14rem;
|
||||
line-height: 0.17rem;
|
||||
color: #687074;
|
||||
}
|
||||
|
||||
/* 指令卡片 */
|
||||
&__command-card {
|
||||
position: relative;
|
||||
width: 3.42rem;
|
||||
margin: 0.40rem auto 0;
|
||||
padding: 0.29rem 0.24rem 0.28rem;
|
||||
background: #12383A;
|
||||
box-shadow: 0px 0.16rem 0.28rem rgba(79, 194, 201, 0.16);
|
||||
border-radius: 0.18rem;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
&__command-title {
|
||||
font-weight: 700;
|
||||
font-size: 0.16rem;
|
||||
line-height: 0.19rem;
|
||||
color: #FFFFFF;
|
||||
margin-bottom: 0.11rem;
|
||||
}
|
||||
|
||||
&__command-sub {
|
||||
font-weight: 400;
|
||||
font-size: 0.12rem;
|
||||
line-height: 0.15rem;
|
||||
color: #B7EDE9;
|
||||
margin-bottom: 0.24rem;
|
||||
}
|
||||
|
||||
&__progress {
|
||||
margin-bottom: 0.18rem;
|
||||
}
|
||||
|
||||
&__progress-rail {
|
||||
width: 100%;
|
||||
height: 0.08rem;
|
||||
background: #0F2F32;
|
||||
border-radius: 0.05rem;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
&__progress-fill {
|
||||
width: 74%;
|
||||
height: 0.08rem;
|
||||
background: #4FC2C9;
|
||||
border-radius: 0.05rem;
|
||||
}
|
||||
|
||||
&__command-chips {
|
||||
font-weight: 700;
|
||||
font-size: 0.11rem;
|
||||
line-height: 0.13rem;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
/* 数据统计卡片 */
|
||||
&__stats-card {
|
||||
position: relative;
|
||||
width: 3.42rem;
|
||||
margin: 0.23rem auto 0;
|
||||
padding: 0.30rem 0.20rem;
|
||||
background: #FFFFFF;
|
||||
box-shadow: 0px 0.12rem 0.22rem rgba(17, 17, 17, 0.07);
|
||||
border-radius: 0.18rem;
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
&__stat {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&__stat-num {
|
||||
display: block;
|
||||
font-weight: 700;
|
||||
font-size: 0.28rem;
|
||||
line-height: 0.34rem;
|
||||
color: #2A8186;
|
||||
margin-bottom: 0.04rem;
|
||||
}
|
||||
|
||||
&__stat-label {
|
||||
font-weight: 400;
|
||||
font-size: 0.11rem;
|
||||
line-height: 0.13rem;
|
||||
color: #8D9399;
|
||||
}
|
||||
|
||||
/* 功能介绍区 */
|
||||
&__functions {
|
||||
position: relative;
|
||||
padding: 0.60rem 0.24rem 0;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
&__section-title {
|
||||
font-weight: 700;
|
||||
font-size: 0.24rem;
|
||||
line-height: 0.29rem;
|
||||
color: #111111;
|
||||
margin-bottom: 0.09rem;
|
||||
}
|
||||
|
||||
&__section-sub {
|
||||
font-weight: 400;
|
||||
font-size: 0.13rem;
|
||||
line-height: 0.16rem;
|
||||
color: #687074;
|
||||
margin-bottom: 0.26rem;
|
||||
}
|
||||
|
||||
&__feature-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.20rem;
|
||||
}
|
||||
|
||||
&__feature-card {
|
||||
width: 100%;
|
||||
padding: 0.22rem 0.20rem;
|
||||
background: #FFFFFF;
|
||||
box-shadow: 0px 0.10rem 0.18rem rgba(17, 17, 17, 0.06);
|
||||
border-radius: 0.18rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.18rem;
|
||||
}
|
||||
|
||||
&__feature-icon {
|
||||
width: 0.42rem;
|
||||
height: 0.42rem;
|
||||
border-radius: 0.12rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
|
||||
span {
|
||||
font-size: 0.20rem;
|
||||
}
|
||||
}
|
||||
|
||||
&__feature-text {
|
||||
h3 {
|
||||
font-weight: 700;
|
||||
font-size: 0.17rem;
|
||||
line-height: 0.21rem;
|
||||
color: #111111;
|
||||
margin-bottom: 0.06rem;
|
||||
}
|
||||
|
||||
p {
|
||||
font-weight: 400;
|
||||
font-size: 0.12rem;
|
||||
line-height: 0.15rem;
|
||||
color: #687074;
|
||||
}
|
||||
}
|
||||
|
||||
/* 每天都有新机会 */
|
||||
&__jobs-section {
|
||||
position: relative;
|
||||
margin-top: 0.50rem;
|
||||
padding: 0.40rem 0.24rem;
|
||||
background: #DDF4F2;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
&__job-chips {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.16rem;
|
||||
}
|
||||
|
||||
&__job-chip {
|
||||
padding: 0.14rem 0.20rem;
|
||||
border-radius: 0.14rem;
|
||||
|
||||
&--dark {
|
||||
width: 100%;
|
||||
background: #12383A;
|
||||
|
||||
.home-mobile__job-chip-company {
|
||||
font-weight: 700;
|
||||
font-size: 0.12rem;
|
||||
line-height: 0.15rem;
|
||||
color: #4FC2C9;
|
||||
}
|
||||
|
||||
.home-mobile__job-chip-role {
|
||||
font-weight: 400;
|
||||
font-size: 0.13rem;
|
||||
line-height: 0.16rem;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
}
|
||||
|
||||
&--light {
|
||||
flex: 1;
|
||||
background: #FFFFFF;
|
||||
box-shadow: 0px 0.08rem 0.16rem rgba(17, 17, 17, 0.06);
|
||||
|
||||
.home-mobile__job-chip-company {
|
||||
font-weight: 700;
|
||||
font-size: 0.12rem;
|
||||
line-height: 0.15rem;
|
||||
color: #42A8B3;
|
||||
}
|
||||
|
||||
.home-mobile__job-chip-role {
|
||||
font-weight: 400;
|
||||
font-size: 0.12rem;
|
||||
line-height: 0.15rem;
|
||||
color: #111111;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__job-chip-company,
|
||||
&__job-chip-role {
|
||||
display: block;
|
||||
}
|
||||
|
||||
&__job-chip-company {
|
||||
margin-bottom: 0.04rem;
|
||||
}
|
||||
|
||||
&__job-chip-row {
|
||||
display: flex;
|
||||
gap: 0.16rem;
|
||||
}
|
||||
|
||||
/* 万千毕业生的信赖之选 */
|
||||
&__proof {
|
||||
position: relative;
|
||||
padding: 0.50rem 0.24rem 0;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
&__quote-card {
|
||||
width: 100%;
|
||||
padding: 0.30rem 0.24rem;
|
||||
background: #12383A;
|
||||
border-radius: 0.18rem;
|
||||
margin-top: 0.20rem;
|
||||
}
|
||||
|
||||
&__quote-text {
|
||||
font-weight: 700;
|
||||
font-size: 0.15rem;
|
||||
line-height: 0.18rem;
|
||||
color: #FFFFFF;
|
||||
margin-bottom: 0.24rem;
|
||||
}
|
||||
|
||||
&__quote-author {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.14rem;
|
||||
}
|
||||
|
||||
&__quote-avatar {
|
||||
width: 0.38rem;
|
||||
height: 0.38rem;
|
||||
background: #D9E3EA;
|
||||
border-radius: 50%;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
&__quote-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.04rem;
|
||||
}
|
||||
|
||||
&__quote-name {
|
||||
font-weight: 400;
|
||||
font-size: 0.12rem;
|
||||
line-height: 0.15rem;
|
||||
color: #B7EDE9;
|
||||
}
|
||||
|
||||
&__quote-school {
|
||||
font-weight: 400;
|
||||
font-size: 0.12rem;
|
||||
line-height: 0.15rem;
|
||||
color: #B7EDE9;
|
||||
}
|
||||
|
||||
/* 常见问题 */
|
||||
&__faq {
|
||||
position: relative;
|
||||
padding: 0.50rem 0.24rem 0;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
&__faq-list {
|
||||
margin-top: 0.20rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.14rem;
|
||||
}
|
||||
|
||||
&__faq-item {
|
||||
width: 100%;
|
||||
background: #FFFFFF;
|
||||
box-shadow: 0px 0.08rem 0.16rem rgba(17, 17, 17, 0.06);
|
||||
border-radius: 0.14rem;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
&__faq-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0.18rem 0.20rem;
|
||||
cursor: pointer;
|
||||
|
||||
span:first-child {
|
||||
font-weight: 700;
|
||||
font-size: 0.14rem;
|
||||
line-height: 0.17rem;
|
||||
color: #111111;
|
||||
}
|
||||
}
|
||||
|
||||
&__faq-icon {
|
||||
font-weight: 700;
|
||||
font-size: 0.18rem;
|
||||
line-height: 0.22rem;
|
||||
color: #111111;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
&__faq-answer {
|
||||
padding: 0 0.20rem 0.18rem;
|
||||
|
||||
p {
|
||||
font-size: 0.12rem;
|
||||
line-height: 0.18rem;
|
||||
color: #687074;
|
||||
}
|
||||
}
|
||||
|
||||
/* 底部 CTA */
|
||||
&__bottom-cta {
|
||||
position: relative;
|
||||
margin-top: 0.50rem;
|
||||
padding: 0.40rem 0.24rem;
|
||||
background: #12383A;
|
||||
z-index: 1;
|
||||
|
||||
h2 {
|
||||
font-weight: 700;
|
||||
font-size: 0.26rem;
|
||||
line-height: 0.31rem;
|
||||
color: #FFFFFF;
|
||||
margin-bottom: 0.12rem;
|
||||
}
|
||||
|
||||
p {
|
||||
font-weight: 400;
|
||||
font-size: 0.13rem;
|
||||
line-height: 0.16rem;
|
||||
color: #9AA0A6;
|
||||
margin-bottom: 0.32rem;
|
||||
}
|
||||
}
|
||||
|
||||
&__bottom-footer {
|
||||
font-weight: 700;
|
||||
font-size: 0.13rem;
|
||||
line-height: 0.16rem;
|
||||
color: #4FC2C9;
|
||||
}
|
||||
|
||||
/* 固定底部按钮 */
|
||||
&__fixed-bar {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
padding: 0.12rem 0.24rem;
|
||||
padding-bottom: calc(0.12rem + env(safe-area-inset-bottom, 0px));
|
||||
background: #FFFFFF;
|
||||
z-index: 100;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
&__pc-hint {
|
||||
font-weight: 400;
|
||||
font-size: 0.12rem;
|
||||
line-height: 0.15rem;
|
||||
color: #2A8186;
|
||||
margin-bottom: 0.06rem;
|
||||
}
|
||||
|
||||
&__cta-btn {
|
||||
width: calc(100% - 0.48rem);
|
||||
max-width: 3.42rem;
|
||||
height: 0.54rem;
|
||||
background: #4FC2C9;
|
||||
box-shadow: 0px 0.10rem 0.24rem rgba(0, 0, 0, 0.18);
|
||||
border-radius: 0.27rem;
|
||||
border: none;
|
||||
font-weight: 700;
|
||||
font-size: 0.16rem;
|
||||
line-height: 0.19rem;
|
||||
color: #FFFFFF;
|
||||
cursor: pointer;
|
||||
|
||||
&:active {
|
||||
opacity: 0.85;
|
||||
}
|
||||
}
|
||||
|
||||
/* 弹窗遮罩 */
|
||||
&__overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
z-index: 200;
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
/* 弹窗内容 */
|
||||
&__dialog {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
background: #FFFFFF;
|
||||
border-radius: 0.24rem;
|
||||
padding: 0.16rem 0.20rem 0.2rem 0.2rem;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
margin: 0 0.16rem 0.24rem 0.16rem;
|
||||
}
|
||||
|
||||
&__dialog-handle {
|
||||
width: 0.60rem;
|
||||
height: 0.04rem;
|
||||
background: #DDF4F2;
|
||||
border-radius: 0.02rem;
|
||||
margin: 0 auto 0.20rem;
|
||||
}
|
||||
|
||||
&__dialog-close {
|
||||
position: absolute;
|
||||
top: 0.16rem;
|
||||
right: 0.20rem;
|
||||
font-size: 0.24rem;
|
||||
color: #42A8B3;
|
||||
background: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
&__dialog h3 {
|
||||
font-weight: 700;
|
||||
font-size: 0.22rem;
|
||||
line-height: 0.28rem;
|
||||
color: #111111;
|
||||
margin-bottom: 0.14rem;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
&__dialog-desc {
|
||||
font-weight: 400;
|
||||
font-size: 0.13rem;
|
||||
line-height: 0.20rem;
|
||||
color: #687074;
|
||||
margin-bottom: 0.20rem;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
&__dialog-url {
|
||||
width: 100%;
|
||||
padding: 0.14rem 0.16rem;
|
||||
background: #EAF8F7;
|
||||
border-radius: 0.12rem;
|
||||
font-weight: 400;
|
||||
font-size: 0.14rem;
|
||||
color: #111111;
|
||||
text-align: left;
|
||||
margin-bottom: 0.24rem;
|
||||
}
|
||||
|
||||
&__dialog-qr-title {
|
||||
font-weight: 700;
|
||||
font-size: 0.14rem;
|
||||
color: #111111;
|
||||
text-align: left;
|
||||
margin-bottom: 0.14rem;
|
||||
}
|
||||
|
||||
&__dialog-qr {
|
||||
width: 2.00rem;
|
||||
height: 2.00rem;
|
||||
margin: 0 auto 0.12rem;
|
||||
border-radius: 0.08rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: #EAF8F7;
|
||||
}
|
||||
|
||||
&__dialog-qr-img {
|
||||
width: 1.60rem;
|
||||
height: 1.60rem;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
&__dialog-qr-hint {
|
||||
font-size: 0.11rem;
|
||||
color: #8D9399;
|
||||
margin-bottom: 0.14rem;
|
||||
}
|
||||
|
||||
&__dialog-btn {
|
||||
width: 100%;
|
||||
height: 0.50rem;
|
||||
background: #4FC2C9;
|
||||
border-radius: 0.25rem;
|
||||
border: none;
|
||||
font-weight: 700;
|
||||
font-size: 0.16rem;
|
||||
color: #FFFFFF;
|
||||
cursor: pointer;
|
||||
|
||||
&:active {
|
||||
opacity: 0.85;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -50,7 +50,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, watch } from 'vue'
|
||||
import { saveResumeMain } from '@/api/resume'
|
||||
import { updateResumeInfo } from '@/api/resume'
|
||||
import { ElMessage } from 'element-plus'
|
||||
|
||||
// ==================== Props & Emits ====================
|
||||
@@ -108,7 +108,7 @@ function handleClose() {
|
||||
visible.value = false
|
||||
}
|
||||
|
||||
/** 保存简历名称和目标岗位 */
|
||||
/** 保存简历名称和目标岗位(调用新 Java 后端接口 /resume/updateInfo) */
|
||||
async function handleSave() {
|
||||
if (!form.resumeName.trim()) {
|
||||
ElMessage.warning('请输入简历名称')
|
||||
@@ -117,7 +117,7 @@ async function handleSave() {
|
||||
|
||||
saving.value = true
|
||||
try {
|
||||
const res = await saveResumeMain({
|
||||
const res = await updateResumeInfo({
|
||||
resumeId: props.resumeId,
|
||||
resumeName: form.resumeName.trim(),
|
||||
targetPosition: form.targetPosition.trim(),
|
||||
|
||||
+21
-2
@@ -28,7 +28,26 @@ const remAdaptPlugin: Plugin = {
|
||||
if (!body) return
|
||||
|
||||
if (isMobile()) {
|
||||
// 移动端:使用 transform scale 缩放整个页面
|
||||
// 移动端首页(/m):不使用 scale 缩放,直接以移动端原生尺寸渲染
|
||||
const isMobileHome = window.location.pathname === '/m'
|
||||
if (isMobileHome) {
|
||||
body.style.width = ''
|
||||
body.style.height = ''
|
||||
body.style.minHeight = ''
|
||||
body.style.overflowY = ''
|
||||
body.style.transform = ''
|
||||
body.style.transformOrigin = ''
|
||||
docEl.style.height = ''
|
||||
docEl.style.overflow = ''
|
||||
// 移动端首页 1rem = 视口宽度 / 3.90(设计稿 390px)
|
||||
const mobileFontSize = clientWidth / 3.90
|
||||
docEl.style.fontSize = mobileFontSize + 'px'
|
||||
docEl.style.setProperty('--app-height', '100vh')
|
||||
docEl.style.setProperty('--chat-width', '100%')
|
||||
return
|
||||
}
|
||||
|
||||
// 其他页面:使用 transform scale 缩放整个页面
|
||||
const scale = clientWidth / designWidth
|
||||
body.style.width = designWidth + 'px'
|
||||
body.style.transform = `scale(${scale})`
|
||||
@@ -36,7 +55,7 @@ const remAdaptPlugin: Plugin = {
|
||||
|
||||
// 屏幕越小 scale 越小,适当增大 rem 基准让字体视觉上更大
|
||||
// 首页和其他页面使用不同的缩放系数
|
||||
const isHomePage = window.location.pathname === '/' || window.location.pathname === '/index.html'
|
||||
const isHomePage = window.location.pathname === '/' || window.location.pathname === '/index.html' || window.location.pathname === '/m'
|
||||
const homeMultiplier = 0.6 // 首页字体放大系数,可单独调整
|
||||
const otherMultiplier = 2.0 // 其他页面字体放大系数
|
||||
const multiplier = isHomePage ? homeMultiplier : otherMultiplier
|
||||
|
||||
@@ -23,7 +23,7 @@ const componentMap: Record<string, () => Promise<any>> = {
|
||||
* 如果后端返回了一个 component 字符串在映射表里找不到,会跳过该条路由并打印警告
|
||||
* 已在静态路由中注册的路径(如 /jobs)不再重复注册
|
||||
*/
|
||||
const STATIC_PATHS = ['/', '/jobs', '/resume/:id', '/jobs/:id', '/mentor/:id']
|
||||
const STATIC_PATHS = ['/', '/m', '/jobs', '/resume/:id', '/jobs/:id', '/mentor/:id']
|
||||
|
||||
export function buildDynamicRoutes(menus: MenuItemRaw[]): RouteRecordRaw[] {
|
||||
const routes: RouteRecordRaw[] = []
|
||||
|
||||
@@ -8,6 +8,7 @@ import { checkLogin } from '@/api/auth'
|
||||
*/
|
||||
const staticRoutes: RouteRecordRaw[] = [
|
||||
{ path: '/', name: 'Home', component: () => import('@/views/Home.vue') },
|
||||
{ path: '/m', name: 'HomeMobile', component: () => import('@/views/HomeMobile.vue') },
|
||||
{ path: '/login', name: 'Login', component: () => import('@/views/Login.vue') },
|
||||
{
|
||||
path: '/jobs',
|
||||
@@ -38,6 +39,14 @@ const router = createRouter({
|
||||
routes: staticRoutes,
|
||||
})
|
||||
|
||||
/**
|
||||
* 检测是否为移动端设备
|
||||
*/
|
||||
function isMobileDevice(): boolean {
|
||||
return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)
|
||||
|| window.innerWidth < 768
|
||||
}
|
||||
|
||||
/**
|
||||
* 上次静默校验登录状态的时间戳,用于节流(5 分钟内不重复请求)
|
||||
*/
|
||||
@@ -55,6 +64,17 @@ const CHECK_INTERVAL = 5 * 60 * 1000 // 5 分钟
|
||||
* 4. 不需要鉴权的路由 → 静默同步登录状态(不阻止导航、不弹登录框),5 分钟内节流
|
||||
*/
|
||||
router.beforeEach(async (to, _from, next) => {
|
||||
// 移动端访问首页时重定向到移动版首页
|
||||
if (to.name === 'Home' && isMobileDevice()) {
|
||||
next({ name: 'HomeMobile', replace: true })
|
||||
return
|
||||
}
|
||||
// PC端访问移动版首页时重定向到PC首页
|
||||
if (to.name === 'HomeMobile' && !isMobileDevice()) {
|
||||
next({ name: 'Home', replace: true })
|
||||
return
|
||||
}
|
||||
|
||||
// 动态路由只需加载一次,与登录状态无关
|
||||
if (!store.state.routesLoaded) {
|
||||
try {
|
||||
@@ -62,8 +82,15 @@ router.beforeEach(async (to, _from, next) => {
|
||||
next({ ...to, replace: true })
|
||||
} catch (err) {
|
||||
console.error('[router] 加载动态路由失败', err)
|
||||
// 即使接口失败也标记路由已加载,确保首页正常展示
|
||||
store.commit('SET_ROUTES_LOADED', true)
|
||||
// 首页不需要依赖动态路由接口,正常放行
|
||||
if (to.name === 'Home' || to.name === 'HomeMobile') {
|
||||
next()
|
||||
} else {
|
||||
next({ name: 'Home' })
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* 每日岗位数量哈希算法工具
|
||||
* 根据当前日期生成 3000~4000 之间的确定性数值
|
||||
* 同一天所有用户看到的数字一致
|
||||
*/
|
||||
|
||||
/** 根据当前日期生成确定性岗位数量 */
|
||||
export function getDailyJobCount(): number {
|
||||
const today = new Date()
|
||||
const seed = today.getFullYear() * 10000 + (today.getMonth() + 1) * 100 + today.getDate()
|
||||
let hash = seed
|
||||
hash = ((hash >> 16) ^ hash) * 0x45d9f3b
|
||||
hash = ((hash >> 16) ^ hash) * 0x45d9f3b
|
||||
hash = (hash >> 16) ^ hash
|
||||
return 3000 + (Math.abs(hash) % 1001)
|
||||
}
|
||||
+2
-12
@@ -493,6 +493,7 @@ import IndustrySelector from '@/components/tools/IndustrySelector.vue'
|
||||
import RegionSelector from '@/components/tools/RegionSelector.vue'
|
||||
import JobCategorySelector from '@/components/tools/JobCategorySelector.vue'
|
||||
import AgreementPreviewDialog from '@/components/tools/AgreementPreviewDialog.vue'
|
||||
import { getDailyJobCount } from '@/utils/dailyJobCount'
|
||||
|
||||
/** 路由实例 */
|
||||
const router = useRouter()
|
||||
@@ -502,18 +503,7 @@ const store = useStore()
|
||||
/** 页面是否已向下滚动超过 20px — 用于导航栏背景切换 */
|
||||
const isScrolled = ref(false)
|
||||
|
||||
/** 根据当前日期生成 3000~4000 之间的确定性岗位数量(同一天所有用户看到的数字一致) */
|
||||
function getDailyJobCount(): number {
|
||||
const today = new Date()
|
||||
const seed = today.getFullYear() * 10000 + (today.getMonth() + 1) * 100 + today.getDate()
|
||||
let hash = seed
|
||||
hash = ((hash >> 16) ^ hash) * 0x45d9f3b
|
||||
hash = ((hash >> 16) ^ hash) * 0x45d9f3b
|
||||
hash = (hash >> 16) ^ hash
|
||||
return 3000 + (Math.abs(hash) % 1001)
|
||||
}
|
||||
|
||||
/** 今日更新岗位数量 — 每日确定性变化 */
|
||||
/** 今日更新岗位数量 — 每日确定性变化(使用公共哈希工具) */
|
||||
const dailyJobCount = getDailyJobCount()
|
||||
|
||||
/** 当前展开的 FAQ 索引,-1 表示全部收起 */
|
||||
|
||||
@@ -0,0 +1,198 @@
|
||||
<template>
|
||||
<!-- 移动端首页 -->
|
||||
<div class="home-mobile">
|
||||
<!-- 背景装饰色块 -->
|
||||
<div class="home-mobile__orb home-mobile__orb--top"></div>
|
||||
<div class="home-mobile__orb home-mobile__orb--hero"></div>
|
||||
|
||||
<!-- 顶部 Logo -->
|
||||
<header class="home-mobile__header">
|
||||
<img src="@/assets/images/logo.png" alt="Offer派" class="home-mobile__logo" />
|
||||
</header>
|
||||
|
||||
<!-- Hero 区域 -->
|
||||
<section class="home-mobile__hero">
|
||||
<p class="home-mobile__eyebrow">大学生AI求职平台</p>
|
||||
<h1 class="home-mobile__headline">一站式求职辅助</h1>
|
||||
<p class="home-mobile__subtitle">
|
||||
与"大海捞针"式求职说再见。<br/>
|
||||
Offer派用AI技术为你精准匹配岗位、自动投递、量身定制简历,<br/>
|
||||
让每一步都更快更准。
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<!-- 指令卡片 -->
|
||||
<section class="home-mobile__command-card">
|
||||
<p class="home-mobile__command-title">Offer 派工作模式</p>
|
||||
<p class="home-mobile__command-sub">网投正在进行中...</p>
|
||||
<div class="home-mobile__progress">
|
||||
<div class="home-mobile__progress-rail">
|
||||
<div class="home-mobile__progress-fill"></div>
|
||||
</div>
|
||||
</div>
|
||||
<p class="home-mobile__command-chips">网投成功 32 · 投递中 5 · 已完成 82%</p>
|
||||
</section>
|
||||
|
||||
<!-- 数据统计卡片 -->
|
||||
<section class="home-mobile__stats-card">
|
||||
<div class="home-mobile__stat">
|
||||
<span class="home-mobile__stat-num">40万+</span>
|
||||
<span class="home-mobile__stat-label">岗位总数</span>
|
||||
</div>
|
||||
<div class="home-mobile__stat">
|
||||
<span class="home-mobile__stat-num">{{ dailyJobCount }}</span>
|
||||
<span class="home-mobile__stat-label">今日更新</span>
|
||||
</div>
|
||||
<div class="home-mobile__stat">
|
||||
<span class="home-mobile__stat-num">82%</span>
|
||||
<span class="home-mobile__stat-label">节省时间</span>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 功能介绍区 -->
|
||||
<section class="home-mobile__functions">
|
||||
<h2 class="home-mobile__section-title">Offer派能帮你做什么</h2>
|
||||
<p class="home-mobile__section-sub">AI驱动的一站式求职解决方案</p>
|
||||
|
||||
<!-- 功能卡片列表 -->
|
||||
<div class="home-mobile__feature-list">
|
||||
<div class="home-mobile__feature-card" v-for="(feat, i) in features" :key="i">
|
||||
<div class="home-mobile__feature-icon" :style="{ background: feat.iconBg }">
|
||||
<span>{{ feat.icon }}</span>
|
||||
</div>
|
||||
<div class="home-mobile__feature-text">
|
||||
<h3>{{ feat.title }}</h3>
|
||||
<p>{{ feat.desc }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 每天都有新机会 -->
|
||||
<section class="home-mobile__jobs-section">
|
||||
<h2 class="home-mobile__section-title">每天都有新机会</h2>
|
||||
<p class="home-mobile__section-sub">实时汇集海量优质校招职位</p>
|
||||
|
||||
<div class="home-mobile__job-chips">
|
||||
<div class="home-mobile__job-chip home-mobile__job-chip--dark">
|
||||
<span class="home-mobile__job-chip-company">字节跳动 · 15分钟前</span>
|
||||
<span class="home-mobile__job-chip-role">高级产品经理</span>
|
||||
</div>
|
||||
<div class="home-mobile__job-chip-row">
|
||||
<div class="home-mobile__job-chip home-mobile__job-chip--light">
|
||||
<span class="home-mobile__job-chip-company">腾讯</span>
|
||||
<span class="home-mobile__job-chip-role">后端开发工程师</span>
|
||||
</div>
|
||||
<div class="home-mobile__job-chip home-mobile__job-chip--light">
|
||||
<span class="home-mobile__job-chip-company">阿里巴巴</span>
|
||||
<span class="home-mobile__job-chip-role">产品经理</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 万千毕业生的信赖之选 -->
|
||||
<section class="home-mobile__proof">
|
||||
<h2 class="home-mobile__section-title">万千毕业生的信赖之选</h2>
|
||||
<div class="home-mobile__quote-card">
|
||||
<p class="home-mobile__quote-text">
|
||||
"真没想到校招居然可以这么轻松高效!一周就收到了3个面试邀约。"
|
||||
</p>
|
||||
<div class="home-mobile__quote-author">
|
||||
<div class="home-mobile__quote-avatar"></div>
|
||||
<div class="home-mobile__quote-info">
|
||||
<span class="home-mobile__quote-name">李同学</span>
|
||||
<span class="home-mobile__quote-school">西安交通大学</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 常见问题 -->
|
||||
<section class="home-mobile__faq">
|
||||
<h2 class="home-mobile__section-title">常见问题</h2>
|
||||
<div class="home-mobile__faq-list">
|
||||
<div
|
||||
v-for="(faq, i) in faqs"
|
||||
:key="i"
|
||||
class="home-mobile__faq-item"
|
||||
:class="{ 'home-mobile__faq-item--open': faqOpen === i }"
|
||||
>
|
||||
<div class="home-mobile__faq-header" @click="faqOpen = faqOpen === i ? -1 : i">
|
||||
<span>{{ faq.q }}</span>
|
||||
<span class="home-mobile__faq-icon">{{ faqOpen === i ? '×' : '+' }}</span>
|
||||
</div>
|
||||
<div v-show="faqOpen === i" class="home-mobile__faq-answer">
|
||||
<p>{{ faq.a }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 底部 CTA -->
|
||||
<section class="home-mobile__bottom-cta">
|
||||
<h2>准备开始投递?</h2>
|
||||
<p>自动网申、简历生成和岗位投递需要在 PC 官网完成。<br/>手机端可先了解功能,正式操作请用电脑访问官网。</p>
|
||||
<span class="home-mobile__bottom-footer">Offer派 · 大学生AI求职首选平台</span>
|
||||
</section>
|
||||
|
||||
<!-- 固定底部按钮 -->
|
||||
<div class="home-mobile__fixed-bar">
|
||||
<span class="home-mobile__pc-hint">完整产品请前往电脑端</span>
|
||||
<button class="home-mobile__cta-btn" @click="showDialog = true">免费使用</button>
|
||||
</div>
|
||||
|
||||
<!-- 弹窗:引导去电脑端 -->
|
||||
<div v-if="showDialog" class="home-mobile__overlay" @click.self="showDialog = false">
|
||||
<div class="home-mobile__dialog">
|
||||
<div class="home-mobile__dialog-handle"></div>
|
||||
<button class="home-mobile__dialog-close" @click="showDialog = false">×</button>
|
||||
<h3>请在电脑端使用 Offer派</h3>
|
||||
<p class="home-mobile__dialog-desc">
|
||||
自动网申、简历生成和岗位投递需要在 PC 官网完成。<br/>
|
||||
手机端可先了解功能,正式操作请用电脑访问官网。
|
||||
</p>
|
||||
<div class="home-mobile__dialog-url">
|
||||
PC 官网:www.offerpai.com.cn
|
||||
</div>
|
||||
<p class="home-mobile__dialog-qr-title">保存二维码,扫码加入求职交流群</p>
|
||||
<div class="home-mobile__dialog-qr">
|
||||
<img src="@/assets/images/home/mobile-qcCode.png" alt="二维码" class="home-mobile__dialog-qr-img" />
|
||||
</div>
|
||||
<p class="home-mobile__dialog-qr-hint">长按保存二维码获取更多求职资料,公开课</p>
|
||||
<button class="home-mobile__dialog-btn" @click="showDialog = false">知道了</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { getDailyJobCount } from '@/utils/dailyJobCount'
|
||||
|
||||
/** 今日更新岗位数量 */
|
||||
const dailyJobCount = getDailyJobCount()
|
||||
|
||||
/** 弹窗显示状态 */
|
||||
const showDialog = ref(false)
|
||||
|
||||
/** 当前展开的 FAQ 索引 */
|
||||
const faqOpen = ref(-1)
|
||||
|
||||
/** 功能卡片数据 */
|
||||
const features = [
|
||||
{ icon: '🎯', iconBg: '#4FC2C9', title: '个性化岗位匹配', desc: '基于简历精准推荐最合适的岗位' },
|
||||
{ icon: '⚡', iconBg: '#12383A', title: '一键自动网申', desc: '投递一键搞定,覆盖各大网申系统' },
|
||||
{ icon: '📄', iconBg: '#42A8B3', title: '岗位定制简历', desc: '根据每个岗位智能优化简历内容' },
|
||||
{ icon: '🤝', iconBg: '#80D8C8', title: '名企内推人脉直通', desc: '实时获取名企最新内推信息' },
|
||||
{ icon: '🤖', iconBg: '#2A8186', title: '24h AI求职助手', desc: '随时提供求职指导和面试技巧' },
|
||||
]
|
||||
|
||||
/** 常见问题数据 — 与PC端保持一致 */
|
||||
const faqs = [
|
||||
{ q: '平台免费使用吗?', a: 'Offer派为所有用户免费开放3天会员权益,帮你省心省力找工作。3天后,依然可以免费使用各项功能,但有使用次数限制。如果你需要不受限制地使用所有功能,更快更高效地获得Offer,建议您升级为付费会员,让求职效率翻倍。' },
|
||||
{ q: '我的信息会被分享吗?', a: 'Offer派高度重视你的隐私安全。未经你明确同意,我们绝不会将你的个人信息提供给任何第三方。你的所有数据仅用于在平台内为你提供AI智能岗位匹配、简历优化等个性化求职服务。' },
|
||||
{ q: '平台的岗位来源是什么?', a: 'Offer派的岗位来自上万家企业的官方招聘网站,同时聚合了各大主流平台的最新职位——你不需要在多个网站之间来回切换,就能一站式搜罗全网高质量的工作机会。' },
|
||||
{ q: '平台与其他求职平台有什么不同?', a: '不同于BOSS直聘等传统平台的"手动式"求职,Offer派如同一位经验丰富的职业导师,通过AI能力搭建智能工具,自动处理简历优化、岗位匹配、申请投递等环节,大幅度提升你的求职效率。' },
|
||||
]
|
||||
</script>
|
||||
@@ -37,9 +37,6 @@
|
||||
>
|
||||
<td class="resume-page__td">
|
||||
<div class="resume-page__name-cell">
|
||||
<!-- <span class="resume-page__avatar" :style="{ background: item.avatarColor }">-->
|
||||
<!-- {{ item.avatarLetter }}-->
|
||||
<!-- </span>-->
|
||||
<span class="resume-page__name">{{ item.name }}</span>
|
||||
<span v-if="item.isDefault" class="resume-page__default-tag">默认</span>
|
||||
<!-- 编辑名称岗位图标 -->
|
||||
|
||||
Reference in New Issue
Block a user