重构页面模块,新增求职辅助工具、招聘信息浏览与交互、个人信息管理和智能岗位推荐功能,优化用户体验和界面布局。
This commit is contained in:
@@ -1,24 +1,428 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="page">
|
<div class="page">
|
||||||
<h2>数据分析页</h2>
|
<h2>智能岗位推荐模块</h2>
|
||||||
<p>后续可以在这里放图表、统计数据等可视化内容。</p>
|
<p class="intro">
|
||||||
|
基于毕业生的专业背景、技能画像与求职偏好,实时生成匹配度更高的岗位推荐列表。
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<section class="card">
|
||||||
|
<div class="card-header">
|
||||||
|
<h3>1. 精准推荐列表</h3>
|
||||||
|
<div class="filters">
|
||||||
|
<label>
|
||||||
|
过滤专业:
|
||||||
|
<select v-model="filter.major">
|
||||||
|
<option value="">全部</option>
|
||||||
|
<option value="计算机">计算机相关</option>
|
||||||
|
<option value="机械">机械相关</option>
|
||||||
|
<option value="电子">电子信息</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
最低匹配度:
|
||||||
|
<input
|
||||||
|
v-model.number="filter.minScore"
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
max="100"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="job-list">
|
||||||
|
<div
|
||||||
|
v-for="job in filteredJobs"
|
||||||
|
:key="job.id"
|
||||||
|
class="job-item"
|
||||||
|
:class="{ active: selectedJob && selectedJob.id === job.id }"
|
||||||
|
@click="selectJob(job)"
|
||||||
|
>
|
||||||
|
<div class="job-main">
|
||||||
|
<div class="job-title-row">
|
||||||
|
<span class="job-title">{{ job.title }}</span>
|
||||||
|
<span class="job-company">{{ job.company }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="job-tags">
|
||||||
|
<span class="tag">{{ job.majorTag }}</span>
|
||||||
|
<span class="tag" v-for="skill in job.skills" :key="skill">
|
||||||
|
{{ skill }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="job-side">
|
||||||
|
<div class="match-score">
|
||||||
|
匹配度
|
||||||
|
<span class="score-value">{{ job.matchScore }}%</span>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="small-btn"
|
||||||
|
@click.stop="toggleCompare(job)"
|
||||||
|
>
|
||||||
|
{{ isInCompare(job.id) ? '取消对比' : '加入对比' }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p v-if="!filteredJobs.length" class="empty-text">
|
||||||
|
当前条件下没有推荐岗位,可以调低匹配度或清空筛选。
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="two-columns">
|
||||||
|
<!-- 推荐理由解析 -->
|
||||||
|
<section class="card">
|
||||||
|
<h3>2. 推荐理由解析</h3>
|
||||||
|
<div v-if="selectedJob" class="reason-panel">
|
||||||
|
<h4>{{ selectedJob.title }} · {{ selectedJob.company }}</h4>
|
||||||
|
<ul>
|
||||||
|
<li>专业契合度:{{ selectedJob.reasons.major }}</li>
|
||||||
|
<li>技能匹配度:{{ selectedJob.reasons.skills }}</li>
|
||||||
|
<li>求职偏好匹配:{{ selectedJob.reasons.preference }}</li>
|
||||||
|
</ul>
|
||||||
|
<p class="tip">
|
||||||
|
建议:可根据推荐理由优化个人信息填写,例如补充缺失技能或调整求职偏好。
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<p v-else class="empty-text">点击左侧推荐列表中的岗位查看推荐理由。</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- 岗位对比功能 -->
|
||||||
|
<section class="card">
|
||||||
|
<h3>3. 岗位对比功能</h3>
|
||||||
|
<div v-if="compareList.length" class="compare-table-wrapper">
|
||||||
|
<table class="compare-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>岗位</th>
|
||||||
|
<th>公司</th>
|
||||||
|
<th>匹配度</th>
|
||||||
|
<th>城市</th>
|
||||||
|
<th>薪资</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr v-for="job in compareList" :key="job.id">
|
||||||
|
<td>{{ job.title }}</td>
|
||||||
|
<td>{{ job.company }}</td>
|
||||||
|
<td>{{ job.matchScore }}%</td>
|
||||||
|
<td>{{ job.city }}</td>
|
||||||
|
<td>{{ job.salary }}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<p class="tip">
|
||||||
|
你可以通过匹配度、城市和薪资综合对比,选择更适合当前阶段发展的岗位。
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<p v-else class="empty-text">
|
||||||
|
在上方推荐列表中点击“加入对比”,即可在此处查看横向对比结果。
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
</section>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts"></script>
|
<script setup lang="ts">
|
||||||
|
import { computed, reactive, ref } from 'vue'
|
||||||
|
|
||||||
|
type Reason = {
|
||||||
|
major: string
|
||||||
|
skills: string
|
||||||
|
preference: string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Job = {
|
||||||
|
id: number
|
||||||
|
title: string
|
||||||
|
company: string
|
||||||
|
majorTag: string
|
||||||
|
skills: string[]
|
||||||
|
matchScore: number
|
||||||
|
city: string
|
||||||
|
salary: string
|
||||||
|
reasons: Reason
|
||||||
|
}
|
||||||
|
|
||||||
|
const jobs = ref<Job[]>([
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
title: '后端开发工程师',
|
||||||
|
company: '某互联网科技公司',
|
||||||
|
majorTag: '计算机',
|
||||||
|
skills: ['Java', 'SpringBoot', 'MySQL'],
|
||||||
|
matchScore: 92,
|
||||||
|
city: '北京',
|
||||||
|
salary: '18-25K',
|
||||||
|
reasons: {
|
||||||
|
major: '与你的“计算机科学与技术”专业高度匹配',
|
||||||
|
skills: 'Java / 数据库 等技能与岗位需求高度重合',
|
||||||
|
preference: '工作城市、行业方向均与求职偏好一致'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
title: '测试开发工程师',
|
||||||
|
company: '智能硬件企业',
|
||||||
|
majorTag: '电子',
|
||||||
|
skills: ['Python', '自动化测试', '嵌入式'],
|
||||||
|
matchScore: 85,
|
||||||
|
city: '深圳',
|
||||||
|
salary: '15-20K',
|
||||||
|
reasons: {
|
||||||
|
major: '与“电子信息工程”专业方向契合',
|
||||||
|
skills: '具备脚本开发与自动化测试经验',
|
||||||
|
preference: '符合你设置的南方沿海城市与制造业方向'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
title: '机械设计工程师',
|
||||||
|
company: '装备制造龙头企业',
|
||||||
|
majorTag: '机械',
|
||||||
|
skills: ['SolidWorks', '机械设计', '有限元分析'],
|
||||||
|
matchScore: 78,
|
||||||
|
city: '上海',
|
||||||
|
salary: '12-18K',
|
||||||
|
reasons: {
|
||||||
|
major: '你的机械相关课程背景与岗位需求较为匹配',
|
||||||
|
skills: '具备 3D 建模与结构分析能力',
|
||||||
|
preference: '行业方向匹配“高端制造业”'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
const selectedJob = ref<Job | null>(null)
|
||||||
|
const compareIds = ref<number[]>([])
|
||||||
|
|
||||||
|
const filter = reactive({
|
||||||
|
major: '',
|
||||||
|
minScore: 70
|
||||||
|
})
|
||||||
|
|
||||||
|
const filteredJobs = computed(() =>
|
||||||
|
jobs.value.filter((job) => {
|
||||||
|
const passMajor = filter.major ? job.majorTag.includes(filter.major) : true
|
||||||
|
const passScore = job.matchScore >= (filter.minScore || 0)
|
||||||
|
return passMajor && passScore
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
const compareList = computed(() =>
|
||||||
|
jobs.value.filter((job) => compareIds.value.includes(job.id))
|
||||||
|
)
|
||||||
|
|
||||||
|
function selectJob(job: Job) {
|
||||||
|
selectedJob.value = job
|
||||||
|
}
|
||||||
|
|
||||||
|
function isInCompare(id: number) {
|
||||||
|
return compareIds.value.includes(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleCompare(job: Job) {
|
||||||
|
if (isInCompare(job.id)) {
|
||||||
|
compareIds.value = compareIds.value.filter((v) => v !== job.id)
|
||||||
|
} else if (compareIds.value.length < 3) {
|
||||||
|
compareIds.value.push(job.id)
|
||||||
|
} else {
|
||||||
|
alert('最多同时对比 3 个岗位(示例限制)')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.page h2 {
|
.page h2 {
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
font-size: 18px;
|
font-size: 20px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
.page p {
|
.intro {
|
||||||
margin: 0;
|
margin: 0 0 16px;
|
||||||
color: #4b5563;
|
color: #4b5563;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
background: #f9fafb;
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 14px 16px;
|
||||||
|
border: 1px solid #e5e7eb;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filters {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 8px;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filters label {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
select,
|
||||||
|
input[type='number'] {
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 1px solid #d1d5db;
|
||||||
|
padding: 4px 6px;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.job-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.job-item {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 10px 12px;
|
||||||
|
border-radius: 10px;
|
||||||
|
background: #ffffff;
|
||||||
|
border: 1px solid #e5e7eb;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.job-item.active {
|
||||||
|
border-color: #2563eb;
|
||||||
|
box-shadow: 0 0 0 1px rgba(37, 99, 235, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.job-main {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.job-title-row {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 8px;
|
||||||
|
align-items: baseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.job-title {
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.job-company {
|
||||||
|
font-size: 13px;
|
||||||
|
color: #6b7280;
|
||||||
|
}
|
||||||
|
|
||||||
|
.job-tags {
|
||||||
|
margin-top: 4px;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tag {
|
||||||
|
font-size: 11px;
|
||||||
|
padding: 2px 6px;
|
||||||
|
border-radius: 999px;
|
||||||
|
background: #e0f2fe;
|
||||||
|
color: #0369a1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.job-side {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-end;
|
||||||
|
gap: 6px;
|
||||||
|
margin-left: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.match-score {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #6b7280;
|
||||||
|
}
|
||||||
|
|
||||||
|
.score-value {
|
||||||
|
margin-left: 4px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #16a34a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.small-btn {
|
||||||
|
border-radius: 999px;
|
||||||
|
border: none;
|
||||||
|
background: #2563eb;
|
||||||
|
color: #ffffff;
|
||||||
|
padding: 4px 10px;
|
||||||
|
font-size: 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.small-btn:hover {
|
||||||
|
background: #1d4ed8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.two-columns {
|
||||||
|
margin-top: 16px;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.reason-panel h4 {
|
||||||
|
margin: 0 0 8px;
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.reason-panel ul {
|
||||||
|
padding-left: 18px;
|
||||||
|
margin: 0 0 8px;
|
||||||
|
font-size: 13px;
|
||||||
|
color: #4b5563;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-text {
|
||||||
|
margin-top: 6px;
|
||||||
|
font-size: 13px;
|
||||||
|
color: #9ca3af;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tip {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #6b7280;
|
||||||
|
}
|
||||||
|
|
||||||
|
.compare-table-wrapper {
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.compare-table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.compare-table th,
|
||||||
|
.compare-table td {
|
||||||
|
padding: 6px 8px;
|
||||||
|
border: 1px solid #e5e7eb;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.compare-table th {
|
||||||
|
background: #eff6ff;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -1,24 +1,422 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="page">
|
<div class="page">
|
||||||
<h2>欢迎来到首页</h2>
|
<h2>个人信息管理模块</h2>
|
||||||
<p>这里可以展示项目的总体介绍、关键指标和快捷入口。</p>
|
<p class="intro">
|
||||||
|
面向理工科毕业生的核心个人画像入口,为后续岗位推荐和匹配提供基础数据支持。
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="grid">
|
||||||
|
<!-- 基础信息录入 -->
|
||||||
|
<section class="card">
|
||||||
|
<h3>1. 基础信息录入</h3>
|
||||||
|
<form class="form" @submit.prevent="saveBasicInfo">
|
||||||
|
<div class="form-row">
|
||||||
|
<label>专业</label>
|
||||||
|
<input v-model="basic.major" placeholder="例如:计算机科学与技术" />
|
||||||
|
</div>
|
||||||
|
<div class="form-row">
|
||||||
|
<label>学历</label>
|
||||||
|
<select v-model="basic.education">
|
||||||
|
<option value="">请选择</option>
|
||||||
|
<option value="本科">本科</option>
|
||||||
|
<option value="硕士">硕士</option>
|
||||||
|
<option value="博士">博士</option>
|
||||||
|
<option value="专科">专科</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="form-row">
|
||||||
|
<label>毕业院校</label>
|
||||||
|
<input v-model="basic.university" placeholder="请输入学校名称" />
|
||||||
|
</div>
|
||||||
|
<div class="form-row">
|
||||||
|
<label>技能证书</label>
|
||||||
|
<input
|
||||||
|
v-model="basic.certificates"
|
||||||
|
placeholder="如:CET-6、计算机二级、软考等"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="form-row">
|
||||||
|
<label>项目经历</label>
|
||||||
|
<textarea
|
||||||
|
v-model="basic.projects"
|
||||||
|
rows="3"
|
||||||
|
placeholder="简单描述你做过的项目或科研经历"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="form-actions">
|
||||||
|
<button type="submit">保存基础信息</button>
|
||||||
|
<span class="hint">仅本地演示,后续可接入后端存储。</span>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- 求职偏好设置 -->
|
||||||
|
<section class="card">
|
||||||
|
<h3>2. 求职偏好设置</h3>
|
||||||
|
<form class="form" @submit.prevent="saveJobPreference">
|
||||||
|
<div class="form-row">
|
||||||
|
<label>期望岗位类型</label>
|
||||||
|
<input
|
||||||
|
v-model="preference.positionType"
|
||||||
|
placeholder="如:后端开发工程师、测试工程师"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="form-row">
|
||||||
|
<label>期望工作地点</label>
|
||||||
|
<input
|
||||||
|
v-model="preference.location"
|
||||||
|
placeholder="如:北京 / 上海 / 杭州,可填写多个"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="form-row salary-row">
|
||||||
|
<label>期望薪资范围(K/月)</label>
|
||||||
|
<div class="salary-inputs">
|
||||||
|
<input
|
||||||
|
v-model.number="preference.salaryMin"
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
placeholder="最低"
|
||||||
|
/>
|
||||||
|
<span class="separator">-</span>
|
||||||
|
<input
|
||||||
|
v-model.number="preference.salaryMax"
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
placeholder="最高"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-row">
|
||||||
|
<label>行业方向</label>
|
||||||
|
<input
|
||||||
|
v-model="preference.industry"
|
||||||
|
placeholder="如:互联网、新能源、制造业等"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="form-actions">
|
||||||
|
<button type="submit">保存求职偏好</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- 简历管理 -->
|
||||||
|
<section class="card resume-card">
|
||||||
|
<h3>3. 简历管理</h3>
|
||||||
|
<div class="upload-area">
|
||||||
|
<label class="upload-label">
|
||||||
|
上传简历文件(PDF / Word)
|
||||||
|
<input type="file" class="file-input" @change="handleUpload" />
|
||||||
|
</label>
|
||||||
|
<p class="hint">当前仅做界面演示,文件不会真正上传。</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="resume-list" v-if="resumes.length">
|
||||||
|
<div class="resume-item" v-for="item in resumes" :key="item.id">
|
||||||
|
<div>
|
||||||
|
<div class="resume-title">
|
||||||
|
{{ item.name }}
|
||||||
|
<span v-if="item.isDefault" class="tag-default">默认</span>
|
||||||
|
</div>
|
||||||
|
<div class="resume-desc">{{ item.description }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="resume-actions">
|
||||||
|
<button type="button" @click="setDefault(item.id)">
|
||||||
|
设为默认
|
||||||
|
</button>
|
||||||
|
<button type="button" @click="editResume(item.id)">在线编辑</button>
|
||||||
|
<button type="button" class="primary" @click="quickApply(item.id)">
|
||||||
|
一键投递
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p v-else class="empty-text">暂时还没有简历,请先上传或创建一份。</p>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts"></script>
|
<script setup lang="ts">
|
||||||
|
import { reactive, ref } from 'vue'
|
||||||
|
|
||||||
|
const basic = reactive({
|
||||||
|
major: '',
|
||||||
|
education: '',
|
||||||
|
university: '',
|
||||||
|
certificates: '',
|
||||||
|
projects: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
const preference = reactive({
|
||||||
|
positionType: '',
|
||||||
|
location: '',
|
||||||
|
salaryMin: undefined as number | undefined,
|
||||||
|
salaryMax: undefined as number | undefined,
|
||||||
|
industry: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
type ResumeItem = {
|
||||||
|
id: number
|
||||||
|
name: string
|
||||||
|
description: string
|
||||||
|
isDefault: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
const resumes = ref<ResumeItem[]>([
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
name: '通用技术岗简历-v1',
|
||||||
|
description: '适用于后端 / 测试 / 运维等通用技术岗位',
|
||||||
|
isDefault: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
name: '算法岗位简历-v1',
|
||||||
|
description: '突出数学与算法竞赛经历,适合算法与数据岗位',
|
||||||
|
isDefault: false
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
function saveBasicInfo() {
|
||||||
|
console.log('保存基础信息', { ...basic })
|
||||||
|
alert('基础信息已保存(示例)')
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveJobPreference() {
|
||||||
|
console.log('保存求职偏好', { ...preference })
|
||||||
|
alert('求职偏好已保存(示例)')
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleUpload(event: Event) {
|
||||||
|
const target = event.target as HTMLInputElement
|
||||||
|
const file = target.files?.[0]
|
||||||
|
if (!file) return
|
||||||
|
|
||||||
|
const id = Date.now()
|
||||||
|
resumes.value.push({
|
||||||
|
id,
|
||||||
|
name: file.name,
|
||||||
|
description: '从本地上传的简历文件,仅作示例展示。',
|
||||||
|
isDefault: false
|
||||||
|
})
|
||||||
|
target.value = ''
|
||||||
|
}
|
||||||
|
|
||||||
|
function setDefault(id: number) {
|
||||||
|
resumes.value = resumes.value.map((item) => ({
|
||||||
|
...item,
|
||||||
|
isDefault: item.id === id
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
function editResume(id: number) {
|
||||||
|
const item = resumes.value.find((r) => r.id === id)
|
||||||
|
if (item) {
|
||||||
|
alert(`打开在线编辑器(示例):${item.name}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function quickApply(id: number) {
|
||||||
|
const item = resumes.value.find((r) => r.id === id)
|
||||||
|
if (item) {
|
||||||
|
alert(`使用【${item.name}】进行一键投递(示例)`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.page h2 {
|
.page h2 {
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
font-size: 18px;
|
font-size: 20px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
.page p {
|
.intro {
|
||||||
margin: 0;
|
margin: 0 0 16px;
|
||||||
color: #4b5563;
|
color: #4b5563;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
background: #f9fafb;
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 14px 16px;
|
||||||
|
border: 1px solid #e5e7eb;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card h3 {
|
||||||
|
margin: 0 0 12px;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-row {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-row label {
|
||||||
|
font-size: 13px;
|
||||||
|
color: #4b5563;
|
||||||
|
}
|
||||||
|
|
||||||
|
input,
|
||||||
|
select,
|
||||||
|
textarea {
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 1px solid #d1d5db;
|
||||||
|
padding: 6px 8px;
|
||||||
|
font-size: 13px;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
input:focus,
|
||||||
|
select:focus,
|
||||||
|
textarea:focus {
|
||||||
|
border-color: #2563eb;
|
||||||
|
box-shadow: 0 0 0 1px rgba(37, 99, 235, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.salary-row label {
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.salary-inputs {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.salary-inputs input {
|
||||||
|
width: 80px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.separator {
|
||||||
|
color: #6b7280;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-actions {
|
||||||
|
margin-top: 4px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
border-radius: 999px;
|
||||||
|
border: none;
|
||||||
|
background: #2563eb;
|
||||||
|
color: #ffffff;
|
||||||
|
padding: 6px 14px;
|
||||||
|
font-size: 13px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:hover {
|
||||||
|
background: #1d4ed8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hint {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #9ca3af;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resume-card {
|
||||||
|
grid-column: 1 / -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-area {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-label {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
padding: 6px 12px;
|
||||||
|
border-radius: 999px;
|
||||||
|
border: 1px dashed #9ca3af;
|
||||||
|
font-size: 13px;
|
||||||
|
color: #4b5563;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-input {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resume-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resume-item {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 10px 12px;
|
||||||
|
border-radius: 10px;
|
||||||
|
background: #ffffff;
|
||||||
|
border: 1px solid #e5e7eb;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resume-title {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tag-default {
|
||||||
|
padding: 2px 6px;
|
||||||
|
border-radius: 999px;
|
||||||
|
background: #0ea5e9;
|
||||||
|
color: #f9fafb;
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resume-desc {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #6b7280;
|
||||||
|
margin-top: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resume-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resume-actions button {
|
||||||
|
padding-inline: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resume-actions .primary {
|
||||||
|
background: #f97316;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resume-actions .primary:hover {
|
||||||
|
background: #ea580c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-text {
|
||||||
|
font-size: 13px;
|
||||||
|
color: #9ca3af;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -1,24 +1,423 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="page">
|
<div class="page">
|
||||||
<h2>报告中心</h2>
|
<h2>招聘信息浏览与交互模块</h2>
|
||||||
<p>在这里管理、查看和导出各类报告。</p>
|
<p class="intro">
|
||||||
|
为理工科毕业生提供多维度的岗位检索能力,以及与企业进行高效互动的入口。
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<!-- 多维度搜索 -->
|
||||||
|
<section class="card">
|
||||||
|
<h3>1. 多维度搜索</h3>
|
||||||
|
<form class="filter-bar" @submit.prevent>
|
||||||
|
<input v-model="filter.keyword" placeholder="输入岗位关键词,例如:Java、测试" />
|
||||||
|
<select v-model="filter.major">
|
||||||
|
<option value="">专业方向</option>
|
||||||
|
<option value="计算机">计算机相关</option>
|
||||||
|
<option value="机械">机械相关</option>
|
||||||
|
<option value="电子">电子信息</option>
|
||||||
|
</select>
|
||||||
|
<select v-model="filter.companyType">
|
||||||
|
<option value="">企业类型</option>
|
||||||
|
<option value="国企">国企</option>
|
||||||
|
<option value="外企">外企</option>
|
||||||
|
<option value="民企">民企</option>
|
||||||
|
</select>
|
||||||
|
<select v-model="filter.hot">
|
||||||
|
<option value="">岗位热度</option>
|
||||||
|
<option value="高">高</option>
|
||||||
|
<option value="中">中</option>
|
||||||
|
<option value="低">低</option>
|
||||||
|
</select>
|
||||||
|
</form>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="layout">
|
||||||
|
<!-- 岗位列表 -->
|
||||||
|
<section class="card job-list-card">
|
||||||
|
<h3>2. 岗位列表</h3>
|
||||||
|
<div class="job-list">
|
||||||
|
<div
|
||||||
|
v-for="job in filteredJobs"
|
||||||
|
:key="job.id"
|
||||||
|
class="job-item"
|
||||||
|
:class="{ active: selectedJob && selectedJob.id === job.id }"
|
||||||
|
@click="selectJob(job)"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<div class="job-title-row">
|
||||||
|
<span class="job-title">{{ job.title }}</span>
|
||||||
|
<span class="job-company">{{ job.company }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="job-meta">
|
||||||
|
<span>{{ job.city }}</span>
|
||||||
|
<span>{{ job.salary }}</span>
|
||||||
|
<span>{{ job.companyType }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="job-actions">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
@click.stop="toggleFavorite(job.id)"
|
||||||
|
:class="{ secondary: isFavorite(job.id) }"
|
||||||
|
>
|
||||||
|
{{ isFavorite(job.id) ? '已收藏' : '收藏' }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="primary"
|
||||||
|
@click.stop="apply(job.id)"
|
||||||
|
>
|
||||||
|
{{ isApplied(job.id) ? '已投递' : '投递简历' }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p v-if="!filteredJobs.length" class="empty-text">
|
||||||
|
暂无符合当前筛选条件的岗位,可以尝试调整搜索条件。
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- 岗位详情页 -->
|
||||||
|
<section class="card detail-card">
|
||||||
|
<h3>3. 岗位详情</h3>
|
||||||
|
<div v-if="selectedJob" class="detail">
|
||||||
|
<h4>{{ selectedJob.title }} · {{ selectedJob.company }}</h4>
|
||||||
|
<p class="detail-sub">
|
||||||
|
{{ selectedJob.city }} · {{ selectedJob.salary }} ·
|
||||||
|
{{ selectedJob.companyType }}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h5>岗位职责</h5>
|
||||||
|
<ul>
|
||||||
|
<li v-for="(duty, index) in selectedJob.duties" :key="index">
|
||||||
|
{{ duty }}
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h5>任职要求</h5>
|
||||||
|
<ul>
|
||||||
|
<li v-for="(req, index) in selectedJob.requirements" :key="index">
|
||||||
|
{{ req }}
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h5>企业介绍与薪资福利</h5>
|
||||||
|
<p class="detail-desc">{{ selectedJob.companyIntro }}</p>
|
||||||
|
</div>
|
||||||
|
<p v-else class="empty-text">
|
||||||
|
在左侧列表中选择一个岗位,查看详细信息与岗位要求。
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
</section>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts"></script>
|
<script setup lang="ts">
|
||||||
|
import { computed, reactive, ref } from 'vue'
|
||||||
|
|
||||||
|
type JobItem = {
|
||||||
|
id: number
|
||||||
|
title: string
|
||||||
|
company: string
|
||||||
|
city: string
|
||||||
|
salary: string
|
||||||
|
companyType: string
|
||||||
|
majorTag: string
|
||||||
|
hot: '高' | '中' | '低'
|
||||||
|
duties: string[]
|
||||||
|
requirements: string[]
|
||||||
|
companyIntro: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const jobs = ref<JobItem[]>([
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
title: 'Java 后端开发工程师',
|
||||||
|
company: '云计算科技有限公司',
|
||||||
|
city: '北京',
|
||||||
|
salary: '18-25K',
|
||||||
|
companyType: '民企',
|
||||||
|
majorTag: '计算机',
|
||||||
|
hot: '高',
|
||||||
|
duties: [
|
||||||
|
'参与后端服务的设计与开发,保证系统稳定性与性能。',
|
||||||
|
'参与需求评审,与前端及产品协作完成业务功能。',
|
||||||
|
'编写相关技术文档与单元测试。'
|
||||||
|
],
|
||||||
|
requirements: [
|
||||||
|
'计算机或相关理工科专业,本科及以上学历。',
|
||||||
|
'熟悉 Java 语言及 SpringBoot 框架。',
|
||||||
|
'掌握 MySQL、Redis 等常用中间件,具备良好的编码习惯。'
|
||||||
|
],
|
||||||
|
companyIntro: '专注云计算与大数据解决方案,为各行业提供技术服务。'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
title: '自动化测试工程师',
|
||||||
|
company: '智能硬件科技集团',
|
||||||
|
city: '深圳',
|
||||||
|
salary: '15-20K',
|
||||||
|
companyType: '民企',
|
||||||
|
majorTag: '电子',
|
||||||
|
hot: '中',
|
||||||
|
duties: [
|
||||||
|
'参与产品测试方案设计及测试用例编写。',
|
||||||
|
'搭建和维护自动化测试框架。',
|
||||||
|
'跟踪和推动缺陷修复,保障产品质量。'
|
||||||
|
],
|
||||||
|
requirements: [
|
||||||
|
'电子信息、计算机等相关理工科专业。',
|
||||||
|
'熟悉 Python、Shell 等脚本语言。',
|
||||||
|
'了解常见自动化测试工具和方法。'
|
||||||
|
],
|
||||||
|
companyIntro: '国内领先的智能硬件设计与生产企业,产品覆盖多领域。'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
title: '机械设计工程师',
|
||||||
|
company: '高端装备制造股份有限公司',
|
||||||
|
city: '上海',
|
||||||
|
salary: '12-18K',
|
||||||
|
companyType: '国企',
|
||||||
|
majorTag: '机械',
|
||||||
|
hot: '高',
|
||||||
|
duties: [
|
||||||
|
'负责机械部件及整机结构的方案设计与优化。',
|
||||||
|
'输出二维工程图与三维模型,并跟进生产制造。',
|
||||||
|
'参与样机装配与测试验证。'
|
||||||
|
],
|
||||||
|
requirements: [
|
||||||
|
'机械设计相关专业,本科及以上学历。',
|
||||||
|
'熟练使用 SolidWorks、CATIA 等三维设计软件。',
|
||||||
|
'具备良好的沟通协调能力和团队协作精神。'
|
||||||
|
],
|
||||||
|
companyIntro: '国有大型装备制造企业,深耕高端装备领域多年。'
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
const filter = reactive({
|
||||||
|
keyword: '',
|
||||||
|
major: '',
|
||||||
|
companyType: '',
|
||||||
|
hot: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
const selectedJob = ref<JobItem | null>(null)
|
||||||
|
const favoriteIds = ref<number[]>([])
|
||||||
|
const appliedIds = ref<number[]>([])
|
||||||
|
|
||||||
|
const filteredJobs = computed(() =>
|
||||||
|
jobs.value.filter((job) => {
|
||||||
|
const byKeyword = filter.keyword
|
||||||
|
? job.title.includes(filter.keyword) ||
|
||||||
|
job.company.includes(filter.keyword)
|
||||||
|
: true
|
||||||
|
const byMajor = filter.major ? job.majorTag === filter.major : true
|
||||||
|
const byCompanyType = filter.companyType
|
||||||
|
? job.companyType === filter.companyType
|
||||||
|
: true
|
||||||
|
const byHot = filter.hot ? job.hot === filter.hot : true
|
||||||
|
|
||||||
|
return byKeyword && byMajor && byCompanyType && byHot
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
function selectJob(job: JobItem) {
|
||||||
|
selectedJob.value = job
|
||||||
|
}
|
||||||
|
|
||||||
|
function isFavorite(id: number) {
|
||||||
|
return favoriteIds.value.includes(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleFavorite(id: number) {
|
||||||
|
if (isFavorite(id)) {
|
||||||
|
favoriteIds.value = favoriteIds.value.filter((v) => v !== id)
|
||||||
|
} else {
|
||||||
|
favoriteIds.value.push(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function isApplied(id: number) {
|
||||||
|
return appliedIds.value.includes(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
function apply(id: number) {
|
||||||
|
if (!isApplied(id)) {
|
||||||
|
appliedIds.value.push(id)
|
||||||
|
alert('简历已投递(示例),可在个人中心查看投递状态。')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.page h2 {
|
.page h2 {
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
font-size: 18px;
|
font-size: 20px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
.page p {
|
.intro {
|
||||||
margin: 0;
|
margin: 0 0 16px;
|
||||||
color: #4b5563;
|
color: #4b5563;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
background: #f9fafb;
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 14px 16px;
|
||||||
|
border: 1px solid #e5e7eb;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-bar {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 8px;
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-bar input,
|
||||||
|
.filter-bar select {
|
||||||
|
border-radius: 999px;
|
||||||
|
border: 1px solid #d1d5db;
|
||||||
|
padding: 6px 10px;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.layout {
|
||||||
|
margin-top: 16px;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: minmax(0, 1.4fr) minmax(0, 1.2fr);
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 900px) {
|
||||||
|
.layout {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.job-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.job-item {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 10px 12px;
|
||||||
|
border-radius: 10px;
|
||||||
|
background: #ffffff;
|
||||||
|
border: 1px solid #e5e7eb;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.job-item.active {
|
||||||
|
border-color: #2563eb;
|
||||||
|
box-shadow: 0 0 0 1px rgba(37, 99, 235, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.job-title-row {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 8px;
|
||||||
|
align-items: baseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.job-title {
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.job-company {
|
||||||
|
font-size: 13px;
|
||||||
|
color: #6b7280;
|
||||||
|
}
|
||||||
|
|
||||||
|
.job-meta {
|
||||||
|
margin-top: 4px;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 8px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #6b7280;
|
||||||
|
}
|
||||||
|
|
||||||
|
.job-actions {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 6px;
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
border-radius: 999px;
|
||||||
|
border: none;
|
||||||
|
padding: 5px 12px;
|
||||||
|
font-size: 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
background: #2563eb;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:hover {
|
||||||
|
background: #1d4ed8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.primary {
|
||||||
|
background: #f97316;
|
||||||
|
}
|
||||||
|
|
||||||
|
.primary:hover {
|
||||||
|
background: #ea580c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.secondary {
|
||||||
|
background: #e5e7eb;
|
||||||
|
color: #374151;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-card {
|
||||||
|
min-height: 260px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail h4 {
|
||||||
|
margin: 0 0 4px;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-sub {
|
||||||
|
margin: 0 0 10px;
|
||||||
|
font-size: 13px;
|
||||||
|
color: #6b7280;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail h5 {
|
||||||
|
margin: 10px 0 6px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail ul {
|
||||||
|
padding-left: 18px;
|
||||||
|
margin: 0;
|
||||||
|
font-size: 13px;
|
||||||
|
color: #4b5563;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-desc {
|
||||||
|
font-size: 13px;
|
||||||
|
color: #4b5563;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-text {
|
||||||
|
margin-top: 6px;
|
||||||
|
font-size: 13px;
|
||||||
|
color: #9ca3af;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -1,24 +1,398 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="page">
|
<div class="page">
|
||||||
<h2>系统设置</h2>
|
<h2>求职辅助工具模块</h2>
|
||||||
<p>这里可以配置系统参数、主题、用户等。</p>
|
<p class="intro">
|
||||||
|
通过测评、经验库与行业资讯,为理工科毕业生提供更全面的求职决策支持。
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<section class="card">
|
||||||
|
<h3>1. 能力测评工具</h3>
|
||||||
|
<div class="assessment-grid">
|
||||||
|
<div
|
||||||
|
class="assessment-card"
|
||||||
|
:class="{ active: activeAssessment === 'skill' }"
|
||||||
|
@click="setAssessment('skill')"
|
||||||
|
>
|
||||||
|
<h4>专业技能测评</h4>
|
||||||
|
<p>评估编程、工程设计、数据分析等理工科核心技能掌握情况。</p>
|
||||||
|
<button type="button">开始测评</button>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="assessment-card"
|
||||||
|
:class="{ active: activeAssessment === 'personality' }"
|
||||||
|
@click="setAssessment('personality')"
|
||||||
|
>
|
||||||
|
<h4>职业性格测评</h4>
|
||||||
|
<p>了解更适合你的岗位类型与团队环境,辅助职业定位。</p>
|
||||||
|
<button type="button">开始测评</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="activeAssessment" class="assessment-panel">
|
||||||
|
<h4>测评问题示例({{ activeAssessmentLabel }})</h4>
|
||||||
|
<p class="question">
|
||||||
|
Q1:你更倾向于哪种工作内容?
|
||||||
|
</p>
|
||||||
|
<ul class="options">
|
||||||
|
<li><label><input type="radio" name="q1" /> A. 深度编码与技术攻关</label></li>
|
||||||
|
<li>
|
||||||
|
<label><input type="radio" name="q1" /> B. 与人沟通、需求分析与协调</label>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<label><input type="radio" name="q1" /> C. 数据分析、建模与实验验证</label>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<button type="button" class="submit-btn">提交测评(示例)</button>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="card">
|
||||||
|
<h3>2. 面试经验库</h3>
|
||||||
|
<div class="experience-filter">
|
||||||
|
<input v-model="experienceKeyword" placeholder="按岗位搜索面试经验,例如:测试 / 算法" />
|
||||||
|
</div>
|
||||||
|
<div class="experience-list">
|
||||||
|
<div
|
||||||
|
v-for="item in filteredExperiences"
|
||||||
|
:key="item.id"
|
||||||
|
class="experience-item"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<div class="exp-title">
|
||||||
|
{{ item.position }} · {{ item.company }}
|
||||||
|
</div>
|
||||||
|
<div class="exp-tags">
|
||||||
|
<span class="tag" v-for="tag in item.tags" :key="tag">
|
||||||
|
{{ tag }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<p class="exp-brief">{{ item.brief }}</p>
|
||||||
|
</div>
|
||||||
|
<button type="button" class="secondary-btn">查看详情</button>
|
||||||
|
</div>
|
||||||
|
<p v-if="!filteredExperiences.length" class="empty-text">
|
||||||
|
暂无相关面试经验,可以尝试更换关键词。
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="card">
|
||||||
|
<h3>3. 就业资讯</h3>
|
||||||
|
<div class="news-list">
|
||||||
|
<div v-for="news in newsList" :key="news.id" class="news-item">
|
||||||
|
<div class="news-main">
|
||||||
|
<div class="news-title">{{ news.title }}</div>
|
||||||
|
<div class="news-meta">
|
||||||
|
<span>{{ news.tag }}</span>
|
||||||
|
<span>{{ news.date }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button type="button" class="link-btn">查看详情</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts"></script>
|
<script setup lang="ts">
|
||||||
|
import { computed, ref } from 'vue'
|
||||||
|
|
||||||
|
type AssessmentType = 'skill' | 'personality' | null
|
||||||
|
|
||||||
|
type ExperienceItem = {
|
||||||
|
id: number
|
||||||
|
position: string
|
||||||
|
company: string
|
||||||
|
tags: string[]
|
||||||
|
brief: string
|
||||||
|
}
|
||||||
|
|
||||||
|
type NewsItem = {
|
||||||
|
id: number
|
||||||
|
title: string
|
||||||
|
tag: string
|
||||||
|
date: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const activeAssessment = ref<AssessmentType>(null)
|
||||||
|
|
||||||
|
const experienceKeyword = ref('')
|
||||||
|
|
||||||
|
const experiences = ref<ExperienceItem[]>([
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
position: '后端开发工程师',
|
||||||
|
company: '互联网大厂一面 + 二面',
|
||||||
|
tags: ['Java', '计网', '操作系统'],
|
||||||
|
brief: '主要考察基础算法、项目细节以及高并发相关问题。'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
position: '测试开发工程师',
|
||||||
|
company: '智能设备公司现场面试',
|
||||||
|
tags: ['测试用例', 'Python', '自动化测试'],
|
||||||
|
brief: '以场景题为主,需要设计完整的测试方案和脚本思路。'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
position: '算法工程师实习',
|
||||||
|
company: 'AI 创业公司线上面试',
|
||||||
|
tags: ['机器学习', '数学基础', '项目经历'],
|
||||||
|
brief: '注重项目中模型选择理由以及效果对比。'
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
const newsList = ref<NewsItem[]>([
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
title: '2026 届理工科毕业生就业形势分析报告',
|
||||||
|
tag: '行业报告',
|
||||||
|
date: '2026-01-15'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
title: '国家发布关于促进高校毕业生就业的新政策',
|
||||||
|
tag: '政策解读',
|
||||||
|
date: '2025-12-30'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
title: '多家头部科技企业启动春季校园招聘',
|
||||||
|
tag: '招聘动态',
|
||||||
|
date: '2026-02-01'
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
const activeAssessmentLabel = computed(() => {
|
||||||
|
if (activeAssessment.value === 'skill') return '专业技能测评'
|
||||||
|
if (activeAssessment.value === 'personality') return '职业性格测评'
|
||||||
|
return ''
|
||||||
|
})
|
||||||
|
|
||||||
|
const filteredExperiences = computed(() => {
|
||||||
|
if (!experienceKeyword.value.trim()) return experiences.value
|
||||||
|
return experiences.value.filter(
|
||||||
|
(item) =>
|
||||||
|
item.position.includes(experienceKeyword.value) ||
|
||||||
|
item.company.includes(experienceKeyword.value) ||
|
||||||
|
item.tags.some((t) => t.includes(experienceKeyword.value))
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
function setAssessment(type: AssessmentType) {
|
||||||
|
activeAssessment.value = type
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.page h2 {
|
.page h2 {
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
font-size: 18px;
|
font-size: 20px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
.page p {
|
.intro {
|
||||||
margin: 0;
|
margin: 0 0 16px;
|
||||||
color: #4b5563;
|
color: #4b5563;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
background: #f9fafb;
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 14px 16px;
|
||||||
|
border: 1px solid #e5e7eb;
|
||||||
|
margin-bottom: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.assessment-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
|
||||||
|
gap: 12px;
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.assessment-card {
|
||||||
|
padding: 10px 12px;
|
||||||
|
border-radius: 10px;
|
||||||
|
background: #ffffff;
|
||||||
|
border: 1px solid #e5e7eb;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.assessment-card.active {
|
||||||
|
border-color: #2563eb;
|
||||||
|
box-shadow: 0 0 0 1px rgba(37, 99, 235, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.assessment-card h4 {
|
||||||
|
margin: 0 0 6px;
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.assessment-card p {
|
||||||
|
margin: 0 0 8px;
|
||||||
|
font-size: 13px;
|
||||||
|
color: #4b5563;
|
||||||
|
}
|
||||||
|
|
||||||
|
.assessment-card button {
|
||||||
|
border-radius: 999px;
|
||||||
|
border: none;
|
||||||
|
padding: 5px 12px;
|
||||||
|
font-size: 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
background: #2563eb;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.assessment-panel {
|
||||||
|
margin-top: 12px;
|
||||||
|
padding: 10px 12px;
|
||||||
|
border-radius: 10px;
|
||||||
|
background: #eff6ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.assessment-panel h4 {
|
||||||
|
margin: 0 0 6px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.question {
|
||||||
|
margin: 0 0 6px;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.options {
|
||||||
|
list-style: none;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0 0 8px;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.options li + li {
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.submit-btn {
|
||||||
|
border-radius: 999px;
|
||||||
|
border: none;
|
||||||
|
padding: 5px 12px;
|
||||||
|
font-size: 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
background: #f97316;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.experience-filter {
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.experience-filter input {
|
||||||
|
width: 100%;
|
||||||
|
border-radius: 999px;
|
||||||
|
border: 1px solid #d1d5db;
|
||||||
|
padding: 6px 10px;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.experience-list {
|
||||||
|
margin-top: 10px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.experience-item {
|
||||||
|
padding: 10px 12px;
|
||||||
|
border-radius: 10px;
|
||||||
|
background: #ffffff;
|
||||||
|
border: 1px solid #e5e7eb;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 10px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.exp-title {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.exp-tags {
|
||||||
|
margin-top: 4px;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tag {
|
||||||
|
font-size: 11px;
|
||||||
|
padding: 2px 6px;
|
||||||
|
border-radius: 999px;
|
||||||
|
background: #e0f2fe;
|
||||||
|
color: #0369a1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.exp-brief {
|
||||||
|
margin: 4px 0 0;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #6b7280;
|
||||||
|
}
|
||||||
|
|
||||||
|
.secondary-btn {
|
||||||
|
border-radius: 999px;
|
||||||
|
border: none;
|
||||||
|
padding: 5px 12px;
|
||||||
|
font-size: 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
background: #e5e7eb;
|
||||||
|
color: #374151;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.news-list {
|
||||||
|
margin-top: 8px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.news-item {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 8px 10px;
|
||||||
|
border-radius: 10px;
|
||||||
|
background: #ffffff;
|
||||||
|
border: 1px solid #e5e7eb;
|
||||||
|
}
|
||||||
|
|
||||||
|
.news-title {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.news-meta {
|
||||||
|
margin-top: 2px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #6b7280;
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.link-btn {
|
||||||
|
border-radius: 999px;
|
||||||
|
border: none;
|
||||||
|
padding: 5px 10px;
|
||||||
|
font-size: 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
background: #2563eb;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-text {
|
||||||
|
font-size: 13px;
|
||||||
|
color: #9ca3af;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user