123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236 |
- <script setup lang="ts">
- import { computed } from 'vue'
- import { useJobStore, type Job } from '../stores/jobStore'
- import { X, Briefcase, MapPin, Users, Calendar, Star } from 'lucide-vue-next'
- interface Props {
- isOpen: boolean
- job: Job | null
- }
- interface Emits {
- (e: 'close'): void
- }
- const props = defineProps<Props>()
- const emit = defineEmits<Emits>()
- const jobStore = useJobStore()
- const handleClose = () => {
- emit('close')
- }
- const getStatusConfig = (status: Job['status']) => {
- switch (status) {
- case 'active':
- return { label: '招聘中', class: 'bg-success-100 text-success-700' }
- case 'paused':
- return { label: '已暂停', class: 'bg-gray-100 text-gray-700' }
- case 'draft':
- return { label: '草稿', class: 'bg-warning-100 text-warning-700' }
- default:
- return { label: '未知', class: 'bg-gray-100 text-gray-500' }
- }
- }
- const statusConfig = computed(() =>
- props.job ? getStatusConfig(props.job.status) : { label: '', class: '' }
- )
- </script>
- <template>
- <Teleport to="body">
- <Transition name="modal" appear>
- <div
- v-if="isOpen && job"
- class="fixed inset-0 z-50 flex items-end sm:items-center justify-center p-4"
- @click.self="handleClose"
- >
- <!-- 背景遮罩 -->
- <div class="absolute inset-0 bg-black/50 backdrop-blur-sm"></div>
-
- <!-- 模态框内容 -->
- <div
- class="relative w-full max-w-2xl max-h-[90vh] bg-white rounded-t-3xl sm:rounded-3xl shadow-2xl overflow-hidden"
- v-motion-slide-bottom
- >
- <!-- 模态框头部 -->
- <div class="flex items-center justify-between p-6 border-b border-gray-100">
- <div class="flex items-center gap-3">
- <h2 class="text-2xl font-bold text-gray-900">岗位详情</h2>
- <span :class="['px-3 py-1 rounded-full text-xs font-medium', statusConfig.class]">
- {{ statusConfig.label }}
- </span>
- </div>
- <button
- @click="handleClose"
- class="p-2 rounded-full hover:bg-gray-100 transition-colors"
- >
- <X :size="20" class="text-gray-500" />
- </button>
- </div>
- <!-- 模态框内容 -->
- <div class="p-6 overflow-y-auto max-h-[calc(90vh-140px)]">
- <div class="space-y-8">
- <!-- 基础信息 -->
- <section>
- <h3 class="text-lg font-semibold text-gray-900 mb-4">基础信息</h3>
- <div class="grid grid-cols-1 sm:grid-cols-2 gap-6">
- <div class="bg-gray-50 rounded-xl p-4">
- <div class="flex items-center gap-2 mb-2">
- <Briefcase :size="16" class="text-primary-600" />
- <span class="text-sm font-medium text-gray-700">岗位名称</span>
- </div>
- <p class="text-gray-900 font-semibold">{{ job.title }}</p>
- </div>
-
- <div class="bg-gray-50 rounded-xl p-4">
- <div class="flex items-center gap-2 mb-2">
- <Users :size="16" class="text-primary-600" />
- <span class="text-sm font-medium text-gray-700">所属部门</span>
- </div>
- <p class="text-gray-900 font-semibold">{{ job.department }}</p>
- </div>
-
- <div class="bg-gray-50 rounded-xl p-4">
- <div class="flex items-center gap-2 mb-2">
- <MapPin :size="16" class="text-primary-600" />
- <span class="text-sm font-medium text-gray-700">工作地点</span>
- </div>
- <p class="text-gray-900 font-semibold">{{ job.location }}</p>
- </div>
-
- <div class="bg-gray-50 rounded-xl p-4">
- <div class="flex items-center gap-2 mb-2">
- <Calendar :size="16" class="text-primary-600" />
- <span class="text-sm font-medium text-gray-700">创建时间</span>
- </div>
- <p class="text-gray-900 font-semibold">{{ new Date(job.createdAt).toLocaleDateString() }}</p>
- </div>
- </div>
- </section>
- <!-- 岗位描述 -->
- <section>
- <h3 class="text-lg font-semibold text-gray-900 mb-4">岗位描述 (JD)</h3>
- <div class="bg-gray-50 rounded-xl p-6">
- <pre class="whitespace-pre-wrap text-gray-700 leading-relaxed font-sans">{{ job.description }}</pre>
- </div>
- </section>
- <!-- 招聘统计 -->
- <section>
- <h3 class="text-lg font-semibold text-gray-900 mb-4">招聘统计</h3>
- <div class="grid grid-cols-2 gap-4">
- <div class="text-center p-4 bg-gradient-to-br from-warning-50 to-orange-50 rounded-xl border border-warning-100">
- <div class="text-2xl font-bold text-warning-600 mb-1">
- {{ job.pendingResumes }}
- </div>
- <div class="text-xs text-warning-700 font-medium">待处理简历</div>
- </div>
-
- <div class="text-center p-4 bg-gradient-to-br from-success-50 to-green-50 rounded-xl border border-success-100">
- <div class="text-2xl font-bold text-success-600 mb-1">
- {{ job.passedResumes }}
- </div>
- <div class="text-xs text-success-700 font-medium">已通过初筛</div>
- </div>
- </div>
- </section>
- <!-- AI筛选评分标准 -->
- <section>
- <h3 class="text-lg font-semibold text-gray-900 mb-4 flex items-center gap-2">
- <Star :size="20" class="text-primary-600" />
- AI筛选评分标准
- </h3>
-
- <div class="space-y-4">
- <!-- 学历要求 -->
- <div class="p-4 border border-gray-200 rounded-xl">
- <div class="flex items-center justify-between mb-2">
- <h4 class="font-medium text-gray-900">学历要求</h4>
- <span class="text-sm font-medium text-primary-600">
- {{ job.aiCriteria.education.weight }}%
- </span>
- </div>
- <p class="text-gray-700">
- {{ job.aiCriteria.education.condition === '>=' ? '大于等于' :
- job.aiCriteria.education.condition === '=' ? '等于' : '包含' }}
- {{ job.aiCriteria.education.value }}
- </p>
- </div>
- <!-- 工作经验 -->
- <div class="p-4 border border-gray-200 rounded-xl">
- <div class="flex items-center justify-between mb-2">
- <h4 class="font-medium text-gray-900">工作经验</h4>
- <span class="text-sm font-medium text-primary-600">
- {{ job.aiCriteria.experience.weight }}%
- </span>
- </div>
- <p class="text-gray-700">
- {{ job.aiCriteria.experience.condition === '>=' ? '大于等于' :
- job.aiCriteria.experience.condition === '=' ? '等于' : '包含' }}
- {{ job.aiCriteria.experience.value }}
- </p>
- </div>
- <!-- 技术能力 -->
- <div class="p-4 border border-gray-200 rounded-xl">
- <div class="flex items-center justify-between mb-2">
- <h4 class="font-medium text-gray-900">技术能力</h4>
- <span class="text-sm font-medium text-primary-600">
- {{ job.aiCriteria.skills.weight }}%
- </span>
- </div>
- <p class="text-gray-700">
- {{ job.aiCriteria.skills.condition === '>=' ? '大于等于' :
- job.aiCriteria.skills.condition === '=' ? '等于' : '包含' }}
- {{ job.aiCriteria.skills.value || '无特殊要求' }}
- </p>
- </div>
- <!-- 语言要求 -->
- <div class="p-4 border border-gray-200 rounded-xl">
- <div class="flex items-center justify-between mb-2">
- <h4 class="font-medium text-gray-900">语言要求</h4>
- <span class="text-sm font-medium text-primary-600">
- {{ job.aiCriteria.language.weight }}%
- </span>
- </div>
- <p class="text-gray-700">
- {{ job.aiCriteria.language.condition === '>=' ? '大于等于' :
- job.aiCriteria.language.condition === '=' ? '等于' : '包含' }}
- {{ job.aiCriteria.language.value }}
- </p>
- </div>
- </div>
- </section>
- </div>
- </div>
- </div>
- </div>
- </Transition>
- </Teleport>
- </template>
- <style scoped>
- /* 模态框动画 */
- .modal-enter-active,
- .modal-leave-active {
- transition: all 0.3s ease-out;
- }
- .modal-enter-from,
- .modal-leave-to {
- opacity: 0;
- }
- .modal-enter-from .relative,
- .modal-leave-to .relative {
- transform: translateY(100px) scale(0.95);
- }
- </style>
|