refactor: 简化首页为两栏布局(企业列表 + 职位列表)
移除右侧详情面板,只保留企业选择和职位列表展示: - 左栏:全部企业(父公司和子公司树形展示) - 右栏:所选企业的职位列表 - 点击职位链接到详情页面 简化了代码结构,移除了 selectJob、handleApply、handleCollect 等不需要的逻辑。 Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
2b818f1ce7
commit
20f6b188e3
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="three-panel">
|
<div class="two-panel">
|
||||||
|
|
||||||
<!-- ── 左栏:公司列表 ── -->
|
<!-- ── 左栏:公司列表 ── -->
|
||||||
<aside class="panel-left">
|
<aside class="panel-left">
|
||||||
|
|
@ -59,35 +59,33 @@
|
||||||
</div>
|
</div>
|
||||||
</aside>
|
</aside>
|
||||||
|
|
||||||
<!-- ── 中栏:岗位列表 ── -->
|
<!-- ── 右栏:岗位列表 ── -->
|
||||||
<section class="panel-mid">
|
<section class="panel-right">
|
||||||
<div class="mid-header">
|
<div class="right-header">
|
||||||
<span v-if="selectedOrg" class="mid-title">
|
<span class="right-title">职位列表</span>
|
||||||
<span class="mid-title-org">{{ selectedOrg.name }}</span>
|
<span v-if="jobs.length" class="right-count">{{ jobs.length }} 个</span>
|
||||||
<span class="mid-title-sep"> · </span>职位列表
|
|
||||||
</span>
|
|
||||||
<span v-else class="mid-title muted">← 请选择企业</span>
|
|
||||||
<span v-if="jobs.length" class="mid-count">{{ jobs.length }} 个</span>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="mid-body">
|
<div class="right-body">
|
||||||
<template v-if="jobsLoading">
|
<template v-if="jobsLoading">
|
||||||
<div v-for="i in 4" :key="i" class="skeleton-row" />
|
<div v-for="i in 4" :key="i" class="skeleton-row" />
|
||||||
</template>
|
</template>
|
||||||
<template v-else-if="jobsError">
|
<template v-else-if="jobsError">
|
||||||
<div class="state-tip">加载失败,请刷新重试</div>
|
<div class="state-tip">加载失败,请刷新重试</div>
|
||||||
</template>
|
</template>
|
||||||
<template v-else-if="jobs.length === 0 && selectedOrg">
|
<template v-else-if="jobs.length === 0">
|
||||||
<div class="state-tip">暂无在招职位</div>
|
<div class="state-tip">请选择企业查看职位</div>
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<div
|
<div
|
||||||
v-for="job in jobs"
|
v-for="job in jobs"
|
||||||
:key="job.id"
|
:key="job.id"
|
||||||
class="job-row"
|
class="job-row"
|
||||||
:class="{ active: selectedJob?.id === job.id }"
|
@click="goToJobDetail(job)"
|
||||||
@click="selectJob(job)"
|
|
||||||
>
|
>
|
||||||
<div class="job-row-title">{{ job.title }}</div>
|
<div class="job-row-title">{{ job.title }}</div>
|
||||||
|
<div class="job-row-meta">
|
||||||
|
<span class="job-org">{{ job.organization?.name }}</span>
|
||||||
|
</div>
|
||||||
<div class="job-row-tags">
|
<div class="job-row-tags">
|
||||||
<span class="tag tag-loc">{{ job.location }}</span>
|
<span class="tag tag-loc">{{ job.location }}</span>
|
||||||
<span class="tag tag-sal">{{ job.salary }}</span>
|
<span class="tag tag-sal">{{ job.salary }}</span>
|
||||||
|
|
@ -99,138 +97,6 @@
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<!-- ── 右栏:岗位详情 ── -->
|
<!-- ── 右栏:岗位详情 ── -->
|
||||||
<section class="panel-right">
|
|
||||||
<!-- 加载 -->
|
|
||||||
<template v-if="detailLoading">
|
|
||||||
<div class="detail-loading">
|
|
||||||
<div v-for="i in 6" :key="i" class="skeleton-row" style="margin-bottom:12px" />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<!-- 错误 -->
|
|
||||||
<template v-else-if="detailError">
|
|
||||||
<div class="state-tip full-tip">加载失败,请刷新重试</div>
|
|
||||||
</template>
|
|
||||||
<!-- 详情 -->
|
|
||||||
<template v-else-if="selectedJob">
|
|
||||||
<!-- Banner -->
|
|
||||||
<div class="detail-banner">
|
|
||||||
<div class="banner-deco"></div>
|
|
||||||
<div class="banner-content">
|
|
||||||
<div class="banner-left">
|
|
||||||
<div class="banner-category">{{ selectedJob.category }}</div>
|
|
||||||
<h2 class="banner-title">{{ selectedJob.title }}</h2>
|
|
||||||
<div class="banner-salary">{{ selectedJob.salary }}</div>
|
|
||||||
<div class="banner-meta">
|
|
||||||
<span class="bmeta-item">
|
|
||||||
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"/><circle cx="12" cy="10" r="3"/></svg>
|
|
||||||
{{ selectedJob.location }}
|
|
||||||
</span>
|
|
||||||
<span class="bmeta-sep">|</span>
|
|
||||||
<span class="bmeta-item">
|
|
||||||
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><polyline points="9 22 9 12 15 12 15 22"/></svg>
|
|
||||||
{{ selectedJob.organization?.name }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="banner-right">
|
|
||||||
<div class="banner-date">发布于 {{ formatDate(selectedJob.created_at) }}</div>
|
|
||||||
<div class="banner-btns">
|
|
||||||
<button class="btn-collect" @click="handleCollect">
|
|
||||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/></svg>
|
|
||||||
{{ collected ? '已收藏' : '收藏' }}
|
|
||||||
</button>
|
|
||||||
<button class="btn-apply" :class="{ loading: applying }" @click="handleApply">
|
|
||||||
{{ applying ? '提交中…' : '立即投递' }}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<p class="banner-hint" v-if="!auth.isLoggedIn">登录后才能投递</p>
|
|
||||||
<p class="banner-hint success" v-if="applied">✓ 投递成功,可在「我的投递」查看</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 主体 -->
|
|
||||||
<div class="detail-main">
|
|
||||||
<div class="detail-left">
|
|
||||||
<!-- 信息格 -->
|
|
||||||
<div class="info-grid">
|
|
||||||
<div class="info-cell">
|
|
||||||
<span class="info-label">职位类别</span>
|
|
||||||
<span class="info-value">{{ selectedJob.category || '未填写' }}</span>
|
|
||||||
</div>
|
|
||||||
<div class="info-cell">
|
|
||||||
<span class="info-label">工作地点</span>
|
|
||||||
<span class="info-value">{{ selectedJob.location }}</span>
|
|
||||||
</div>
|
|
||||||
<div class="info-cell">
|
|
||||||
<span class="info-label">薪资范围</span>
|
|
||||||
<span class="info-value red-val">{{ selectedJob.salary }}</span>
|
|
||||||
</div>
|
|
||||||
<div class="info-cell">
|
|
||||||
<span class="info-label">发布时间</span>
|
|
||||||
<span class="info-value">{{ formatDate(selectedJob.created_at) }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="section-divider"></div>
|
|
||||||
|
|
||||||
<!-- 职位介绍 -->
|
|
||||||
<div class="content-section">
|
|
||||||
<div class="section-heading"><span class="heading-bar"></span>职位介绍</div>
|
|
||||||
<div class="desc-text" v-html="selectedJob.description"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="section-divider"></div>
|
|
||||||
|
|
||||||
<!-- 工作地点 -->
|
|
||||||
<div class="content-section">
|
|
||||||
<div class="section-heading"><span class="heading-bar"></span>工作地点</div>
|
|
||||||
<div class="location-row">
|
|
||||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#E57373" stroke-width="2"><path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"/><circle cx="12" cy="10" r="3"/></svg>
|
|
||||||
{{ selectedJob.location }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 公司卡片 -->
|
|
||||||
<div class="detail-right">
|
|
||||||
<div class="company-card">
|
|
||||||
<div class="company-card-label">招聘单位</div>
|
|
||||||
<div class="company-card-top">
|
|
||||||
<div class="cc-logo">
|
|
||||||
<img v-if="selectedJob.organization?.logo" :src="selectedJob.organization.logo" alt="" />
|
|
||||||
<span v-else>{{ selectedJob.organization?.name?.[0] }}</span>
|
|
||||||
</div>
|
|
||||||
<div class="cc-info">
|
|
||||||
<div class="cc-name">{{ selectedJob.organization?.name }}</div>
|
|
||||||
<div class="cc-desc" v-if="selectedJob.organization?.description">
|
|
||||||
{{ selectedJob.organization.description.slice(0, 50) }}{{ selectedJob.organization.description.length > 50 ? '…' : '' }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="cc-divider"></div>
|
|
||||||
<div class="cc-contact" v-if="selectedJob.organization?.email">
|
|
||||||
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="#E57373" stroke-width="2"><path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"/><polyline points="22,6 12,13 2,6"/></svg>
|
|
||||||
{{ selectedJob.organization.email }}
|
|
||||||
</div>
|
|
||||||
<button class="btn-apply-card" :class="{ loading: applying }" @click="handleApply">
|
|
||||||
{{ applying ? '提交中…' : '立即投递' }}
|
|
||||||
</button>
|
|
||||||
<p class="apply-success" v-if="applied">✓ 投递成功</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<!-- 空状态 -->
|
|
||||||
<template v-else>
|
|
||||||
<div class="state-tip full-tip">
|
|
||||||
<div class="empty-icon">
|
|
||||||
<svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="#D4A95D" stroke-width="1.5"><rect x="2" y="3" width="20" height="14" rx="2"/><line x1="8" y1="21" x2="16" y2="21"/><line x1="12" y1="17" x2="12" y2="21"/></svg>
|
|
||||||
</div>
|
|
||||||
<p>请从左侧选择企业及职位</p>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</section>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
@ -240,8 +106,7 @@ import { useRouter } from 'vue-router'
|
||||||
import { useAuthStore } from '@/stores/auth'
|
import { useAuthStore } from '@/stores/auth'
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
import { getOrganizations } from '@/api/organizations'
|
import { getOrganizations } from '@/api/organizations'
|
||||||
import { getJobs, getJob, toggleFavorite } from '@/api/jobs'
|
import { getJobs } from '@/api/jobs'
|
||||||
import { applyJob } from '@/api/applications'
|
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const auth = useAuthStore()
|
const auth = useAuthStore()
|
||||||
|
|
@ -255,12 +120,6 @@ const jobs = ref([])
|
||||||
const jobsLoading = ref(false)
|
const jobsLoading = ref(false)
|
||||||
const jobsError = ref(false)
|
const jobsError = ref(false)
|
||||||
|
|
||||||
const selectedJob = ref(null)
|
|
||||||
const detailLoading = ref(false)
|
|
||||||
const detailError = ref(false)
|
|
||||||
const applying = ref(false)
|
|
||||||
const applied = ref(false)
|
|
||||||
const collected = ref(false)
|
|
||||||
|
|
||||||
const totalOrgs = computed(() => {
|
const totalOrgs = computed(() => {
|
||||||
return orgs.value.reduce((n, o) => n + 1 + (o.children?.length || 0), 0)
|
return orgs.value.reduce((n, o) => n + 1 + (o.children?.length || 0), 0)
|
||||||
|
|
@ -284,39 +143,8 @@ async function selectOrg(org) {
|
||||||
finally { jobsLoading.value = false }
|
finally { jobsLoading.value = false }
|
||||||
}
|
}
|
||||||
|
|
||||||
async function selectJob(job) {
|
function goToJobDetail(job) {
|
||||||
detailError.value = false
|
router.push({ name: 'JobDetail', params: { id: job.id } })
|
||||||
detailLoading.value = true
|
|
||||||
selectedJob.value = null
|
|
||||||
applied.value = false
|
|
||||||
collected.value = false
|
|
||||||
try {
|
|
||||||
const { data } = await getJob(job.id)
|
|
||||||
selectedJob.value = data
|
|
||||||
} catch { detailError.value = true }
|
|
||||||
finally { detailLoading.value = false }
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleCollect() {
|
|
||||||
if (!auth.isLoggedIn) return router.push({ name: 'Login' })
|
|
||||||
if (!auth.isSeeker) return ElMessage.warning('管理员账号无法收藏职位')
|
|
||||||
const { data } = await toggleFavorite(selectedJob.value.id)
|
|
||||||
collected.value = data.collected
|
|
||||||
ElMessage.success(collected.value ? '已收藏,可在「关注职位」中查看' : '已取消收藏')
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleApply() {
|
|
||||||
if (!auth.isLoggedIn) return router.push({ name: 'Login' })
|
|
||||||
if (!auth.isSeeker) return ElMessage.warning('管理员账号无法投递职位')
|
|
||||||
applying.value = true
|
|
||||||
try {
|
|
||||||
await applyJob(selectedJob.value.id)
|
|
||||||
applied.value = true
|
|
||||||
ElMessage.success('投递成功!')
|
|
||||||
} catch (e) {
|
|
||||||
if (e.response?.status === 400) ElMessage.warning(e.response.data?.detail || '您已投递过该职位')
|
|
||||||
else ElMessage.error('投递失败,请先完善简历')
|
|
||||||
} finally { applying.value = false }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
|
|
@ -332,7 +160,7 @@ onMounted(async () => {
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
/* ── 变量 ── */
|
/* ── 变量 ── */
|
||||||
.three-panel {
|
.two-panel {
|
||||||
--red: #B8860B;
|
--red: #B8860B;
|
||||||
--dark: #1A1A1A;
|
--dark: #1A1A1A;
|
||||||
--gold: #B8860B;
|
--gold: #B8860B;
|
||||||
|
|
@ -441,16 +269,16 @@ onMounted(async () => {
|
||||||
background: rgba(184,134,11,0.35);
|
background: rgba(184,134,11,0.35);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ── 中栏 ── */
|
/* ── 右栏 (职位列表) ── */
|
||||||
.panel-mid {
|
.panel-right {
|
||||||
width: 268px;
|
flex: 1;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
background: var(--cream);
|
background: var(--cream);
|
||||||
border-right: 1px solid var(--border);
|
border-left: 1px solid var(--border);
|
||||||
}
|
}
|
||||||
.mid-header {
|
.right-header {
|
||||||
padding: 14px 16px;
|
padding: 14px 16px;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
@ -459,14 +287,12 @@ onMounted(async () => {
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
}
|
}
|
||||||
.mid-title { font-size: 13px; font-weight: 600; color: var(--text); }
|
.right-title { font-size: 13px; font-weight: 600; color: var(--text); }
|
||||||
.mid-title-org { color: var(--red); }
|
.right-count {
|
||||||
.mid-title.muted { color: var(--muted); font-weight: 400; }
|
|
||||||
.mid-count {
|
|
||||||
font-size: 11px; color: var(--muted);
|
font-size: 11px; color: var(--muted);
|
||||||
background: #F0F0F0; border-radius: 10px; padding: 1px 8px;
|
background: #F0F0F0; border-radius: 10px; padding: 1px 8px;
|
||||||
}
|
}
|
||||||
.mid-body { flex: 1; overflow-y: auto; }
|
.right-body { flex: 1; overflow-y: auto; }
|
||||||
|
|
||||||
/* 岗位行 */
|
/* 岗位行 */
|
||||||
.job-row {
|
.job-row {
|
||||||
|
|
@ -478,15 +304,14 @@ onMounted(async () => {
|
||||||
background: #fff;
|
background: #fff;
|
||||||
}
|
}
|
||||||
.job-row:hover { background: #F8F8F8; }
|
.job-row:hover { background: #F8F8F8; }
|
||||||
.job-row.active {
|
|
||||||
border-left-color: var(--red);
|
|
||||||
background: #FFFAF9;
|
|
||||||
}
|
|
||||||
.job-row-title {
|
.job-row-title {
|
||||||
font-size: 13px; font-weight: 600;
|
font-size: 13px; font-weight: 600;
|
||||||
color: var(--text); margin-bottom: 7px;
|
color: var(--text); margin-bottom: 5px;
|
||||||
}
|
}
|
||||||
.job-row.active .job-row-title { color: var(--red); }
|
.job-row-meta {
|
||||||
|
font-size: 11px; color: var(--muted); margin-bottom: 6px;
|
||||||
|
}
|
||||||
|
.job-org { color: var(--red); }
|
||||||
.job-row-tags { display: flex; gap: 5px; flex-wrap: wrap; }
|
.job-row-tags { display: flex; gap: 5px; flex-wrap: wrap; }
|
||||||
.tag {
|
.tag {
|
||||||
font-size: 11px; padding: 2px 7px;
|
font-size: 11px; padding: 2px 7px;
|
||||||
|
|
@ -496,190 +321,6 @@ onMounted(async () => {
|
||||||
.tag-sal { background: #FFF3E0; color: #B7610A; }
|
.tag-sal { background: #FFF3E0; color: #B7610A; }
|
||||||
.tag-cat { background: #E8F5E9; color: #2E7D32; }
|
.tag-cat { background: #E8F5E9; color: #2E7D32; }
|
||||||
|
|
||||||
/* ── 右栏 ── */
|
|
||||||
.panel-right {
|
|
||||||
flex: 1;
|
|
||||||
overflow-y: auto;
|
|
||||||
background: #FAFAFA;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Banner */
|
|
||||||
.detail-banner {
|
|
||||||
position: relative;
|
|
||||||
background: linear-gradient(135deg, #1A1A1A 0%, #2A2A2A 50%, #222222 100%);
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
.banner-deco {
|
|
||||||
position: absolute; inset: 0;
|
|
||||||
background:
|
|
||||||
repeating-linear-gradient(45deg, transparent, transparent 20px, rgba(184,134,11,0.03) 20px, rgba(184,134,11,0.03) 21px),
|
|
||||||
repeating-linear-gradient(-45deg, transparent, transparent 20px, rgba(184,134,11,0.03) 20px, rgba(184,134,11,0.03) 21px);
|
|
||||||
}
|
|
||||||
.banner-content {
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: flex-start;
|
|
||||||
gap: 20px;
|
|
||||||
padding: 28px 32px;
|
|
||||||
}
|
|
||||||
.banner-left { flex: 1; }
|
|
||||||
.banner-category {
|
|
||||||
display: inline-block;
|
|
||||||
padding: 3px 12px;
|
|
||||||
background: rgba(184,134,11,0.15);
|
|
||||||
border: 1px solid rgba(184,134,11,0.35);
|
|
||||||
color: var(--gold-lt);
|
|
||||||
font-size: 11px; letter-spacing: 0.1em;
|
|
||||||
border-radius: 2px; margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
.banner-title {
|
|
||||||
font-size: 22px; font-weight: 800;
|
|
||||||
color: #fff; letter-spacing: 0.05em;
|
|
||||||
margin: 0 0 8px;
|
|
||||||
text-shadow: 0 2px 8px rgba(0,0,0,0.3);
|
|
||||||
}
|
|
||||||
.banner-salary {
|
|
||||||
font-size: 18px; font-weight: 700;
|
|
||||||
color: #FF6B6B; margin-bottom: 12px;
|
|
||||||
}
|
|
||||||
.banner-meta { display: flex; align-items: center; gap: 8px; }
|
|
||||||
.bmeta-item {
|
|
||||||
display: flex; align-items: center; gap: 4px;
|
|
||||||
font-size: 13px; color: rgba(255,255,255,0.6);
|
|
||||||
}
|
|
||||||
.bmeta-sep { color: rgba(255,255,255,0.2); }
|
|
||||||
|
|
||||||
.banner-right { text-align: right; flex-shrink: 0; }
|
|
||||||
.banner-date { font-size: 12px; color: rgba(255,255,255,0.38); margin-bottom: 14px; }
|
|
||||||
.banner-btns { display: flex; gap: 10px; justify-content: flex-end; }
|
|
||||||
.btn-collect {
|
|
||||||
display: flex; align-items: center; gap: 6px;
|
|
||||||
padding: 9px 18px;
|
|
||||||
background: transparent;
|
|
||||||
border: 1px solid rgba(184,134,11,0.5);
|
|
||||||
color: var(--gold-lt);
|
|
||||||
border-radius: 3px; font-size: 13px;
|
|
||||||
cursor: pointer; transition: all 0.2s;
|
|
||||||
font-family: inherit;
|
|
||||||
}
|
|
||||||
.btn-collect:hover { background: rgba(184,134,11,0.1); border-color: var(--gold); }
|
|
||||||
.btn-apply {
|
|
||||||
padding: 9px 24px;
|
|
||||||
background: var(--red);
|
|
||||||
border: none; color: #fff;
|
|
||||||
border-radius: 3px; font-size: 13px; font-weight: 700;
|
|
||||||
cursor: pointer; transition: all 0.2s;
|
|
||||||
letter-spacing: 0.05em;
|
|
||||||
font-family: inherit;
|
|
||||||
box-shadow: 0 4px 16px rgba(184,134,11,0.4);
|
|
||||||
}
|
|
||||||
.btn-apply:hover { background: #A67C07; box-shadow: 0 6px 20px rgba(184,134,11,0.5); }
|
|
||||||
.btn-apply.loading { opacity: 0.7; cursor: not-allowed; }
|
|
||||||
.banner-hint { font-size: 12px; color: rgba(255,255,255,0.4); margin-top: 8px; }
|
|
||||||
.banner-hint.success { color: #6FCF97; }
|
|
||||||
|
|
||||||
/* 主体 */
|
|
||||||
.detail-main {
|
|
||||||
display: flex;
|
|
||||||
gap: 20px;
|
|
||||||
padding: 24px 28px;
|
|
||||||
align-items: flex-start;
|
|
||||||
}
|
|
||||||
.detail-left {
|
|
||||||
flex: 1;
|
|
||||||
background: #fff;
|
|
||||||
border-radius: 6px;
|
|
||||||
padding: 24px 28px;
|
|
||||||
box-shadow: 0 2px 12px rgba(0,0,0,0.07);
|
|
||||||
border: 1px solid var(--border);
|
|
||||||
}
|
|
||||||
.detail-right { width: 224px; flex-shrink: 0; }
|
|
||||||
|
|
||||||
/* 信息格 */
|
|
||||||
.info-grid {
|
|
||||||
display: grid; grid-template-columns: 1fr 1fr; gap: 16px 24px;
|
|
||||||
margin-bottom: 4px;
|
|
||||||
}
|
|
||||||
.info-cell { display: flex; flex-direction: column; gap: 4px; }
|
|
||||||
.info-label {
|
|
||||||
font-size: 11px; color: var(--muted); letter-spacing: 0.05em;
|
|
||||||
display: flex; align-items: center; gap: 5px;
|
|
||||||
}
|
|
||||||
.info-label::before {
|
|
||||||
content: ''; width: 5px; height: 5px;
|
|
||||||
background: var(--red); border-radius: 50%;
|
|
||||||
}
|
|
||||||
.info-value { font-size: 14px; font-weight: 600; color: var(--text); }
|
|
||||||
.red-val { color: var(--red); }
|
|
||||||
|
|
||||||
.section-divider {
|
|
||||||
height: 1px; background: var(--border);
|
|
||||||
margin: 20px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 内容区块 */
|
|
||||||
.content-section { }
|
|
||||||
.section-heading {
|
|
||||||
display: flex; align-items: center; gap: 10px;
|
|
||||||
font-size: 15px; font-weight: 700; color: var(--text);
|
|
||||||
margin-bottom: 14px; letter-spacing: 0.05em;
|
|
||||||
}
|
|
||||||
.heading-bar {
|
|
||||||
display: block; width: 4px; height: 16px;
|
|
||||||
background: var(--red); border-radius: 2px;
|
|
||||||
}
|
|
||||||
.desc-text {
|
|
||||||
font-size: 13px; line-height: 2;
|
|
||||||
color: #3A3A4A; white-space: pre-wrap;
|
|
||||||
}
|
|
||||||
.location-row {
|
|
||||||
display: flex; align-items: center; gap: 6px;
|
|
||||||
font-size: 13px; color: #3A3A4A;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 公司卡片 */
|
|
||||||
.company-card {
|
|
||||||
background: #fff;
|
|
||||||
border-radius: 6px;
|
|
||||||
padding: 18px;
|
|
||||||
box-shadow: 0 2px 12px rgba(0,0,0,0.07);
|
|
||||||
border: 1px solid var(--border);
|
|
||||||
}
|
|
||||||
.company-card-label {
|
|
||||||
font-size: 11px; font-weight: 700;
|
|
||||||
color: var(--muted); letter-spacing: 0.15em;
|
|
||||||
margin-bottom: 14px; text-transform: uppercase;
|
|
||||||
}
|
|
||||||
.company-card-top { display: flex; gap: 12px; align-items: flex-start; margin-bottom: 14px; }
|
|
||||||
.cc-logo {
|
|
||||||
width: 48px; height: 48px;
|
|
||||||
border-radius: 8px; overflow: hidden; flex-shrink: 0;
|
|
||||||
background: linear-gradient(135deg, #B8860B, #8B6407);
|
|
||||||
display: flex; align-items: center; justify-content: center;
|
|
||||||
font-size: 20px; font-weight: 900; color: #fff;
|
|
||||||
border: 1px solid var(--border);
|
|
||||||
}
|
|
||||||
.cc-logo img { width: 100%; height: 100%; object-fit: cover; }
|
|
||||||
.cc-name { font-size: 14px; font-weight: 700; color: var(--text); margin-bottom: 4px; }
|
|
||||||
.cc-desc { font-size: 11px; color: var(--muted); line-height: 1.6; }
|
|
||||||
.cc-divider { height: 1px; background: var(--border); margin-bottom: 12px; }
|
|
||||||
.cc-contact {
|
|
||||||
display: flex; align-items: center; gap: 6px;
|
|
||||||
font-size: 12px; color: var(--muted); word-break: break-all;
|
|
||||||
margin-bottom: 14px;
|
|
||||||
}
|
|
||||||
.btn-apply-card {
|
|
||||||
width: 100%; padding: 10px;
|
|
||||||
background: var(--red); border: none;
|
|
||||||
color: #fff; font-size: 13px; font-weight: 700;
|
|
||||||
border-radius: 3px; cursor: pointer;
|
|
||||||
transition: background 0.2s;
|
|
||||||
font-family: inherit; letter-spacing: 0.05em;
|
|
||||||
}
|
|
||||||
.btn-apply-card:hover { background: #A67C07; }
|
|
||||||
.btn-apply-card.loading { opacity: 0.7; cursor: not-allowed; }
|
|
||||||
.apply-success { text-align: center; font-size: 12px; color: #27AE60; margin-top: 8px; }
|
|
||||||
|
|
||||||
/* 空状态 / 骨架 */
|
/* 空状态 / 骨架 */
|
||||||
.state-tip {
|
.state-tip {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue