generated from kgod/ai-review-template
自内部仓库剥离深度优化与 RAG 知识库后的交付版本: - Builder 对话式简历生成(FSM + 意图路由 LLM 兜底增强) - 条目级轻度优化:事实覆盖门禁 + STAR/bullet 修复链,功能/简介/成果与技术栈同级保护 - 简历导入:DOCX/PDF 解析、结构归一、手机号脱敏 - PostgreSQL 运行时 + Alembic 迁移链 Co-Authored-By: Claude <noreply@anthropic.com>
114 lines
3.2 KiB
Vue
114 lines
3.2 KiB
Vue
<script setup lang="ts">
|
|
import { computed, ref, watch } from 'vue'
|
|
import type { ChoiceOption, ComponentSubmission } from '../types/resumeAgent'
|
|
import { initialValue, optionList, stringValue } from '../utils/componentData'
|
|
import FormCard from './shared/FormCard.vue'
|
|
|
|
const props = withDefaults(
|
|
defineProps<{
|
|
data: Record<string, unknown>
|
|
value?: unknown
|
|
readOnly?: boolean
|
|
pending?: boolean
|
|
}>(),
|
|
{ value: undefined, readOnly: false, pending: false },
|
|
)
|
|
|
|
const emit = defineEmits<{ submit: [submission: ComponentSubmission] }>()
|
|
const fallbackOptions: ChoiceOption[] = [
|
|
{ value: 'doctor', label: '博士' },
|
|
{ value: 'master', label: '硕士' },
|
|
{ value: 'bachelor', label: '本科' },
|
|
{ value: 'associate', label: '大专' },
|
|
{ value: 'high_school', label: '高中 / 中专' },
|
|
{ value: 'other', label: '其他' },
|
|
]
|
|
const options = computed(() => optionList(props.data.options ?? props.data.choices, fallbackOptions))
|
|
const sourceValue = computed(() =>
|
|
typeof props.value === 'string' ? props.value : initialValue(props.data),
|
|
)
|
|
const selected = ref(sourceValue.value)
|
|
|
|
watch(sourceValue, (next) => {
|
|
selected.value = next
|
|
})
|
|
|
|
const selectedLabel = computed(
|
|
() => options.value.find((option) => option.value === selected.value)?.label || selected.value,
|
|
)
|
|
|
|
function submit() {
|
|
if (!selected.value || props.readOnly || props.pending) return
|
|
emit('submit', {
|
|
event: 'select',
|
|
payload: { value: selected.value, degree: selected.value },
|
|
})
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<FormCard
|
|
eyebrow="教育背景"
|
|
:title="stringValue(data.title, '你的最高学历是?')"
|
|
:description="stringValue(data.description, '选择已经获得或正在就读的最高学历。')"
|
|
:read-only="readOnly"
|
|
:pending="pending"
|
|
>
|
|
<div v-if="readOnly" class="read-only-value">{{ selectedLabel || '学历已记录' }}</div>
|
|
<form v-else @submit.prevent="submit">
|
|
<div class="degree-grid" role="radiogroup" aria-label="最高学历">
|
|
<button
|
|
v-for="option in options"
|
|
:key="option.value"
|
|
class="degree-option"
|
|
:class="{ 'degree-option--selected': selected === option.value }"
|
|
type="button"
|
|
role="radio"
|
|
:aria-checked="selected === option.value"
|
|
:disabled="pending || option.disabled"
|
|
@click="selected = option.value"
|
|
>
|
|
{{ option.label }}
|
|
</button>
|
|
</div>
|
|
<div class="component-actions">
|
|
<button class="primary-button" type="submit" :disabled="!selected || pending">
|
|
确认学历
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</FormCard>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.degree-grid {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 9px;
|
|
}
|
|
|
|
.degree-option {
|
|
min-height: 44px;
|
|
padding: 0 16px;
|
|
border: 1px solid var(--line);
|
|
border-radius: 999px;
|
|
color: var(--ink-soft);
|
|
background: #f8fbfa;
|
|
font-size: 14px;
|
|
font-weight: 680;
|
|
transition: color 160ms ease, border-color 160ms ease, background 160ms ease, transform 160ms ease;
|
|
}
|
|
|
|
.degree-option:hover:not(:disabled) {
|
|
transform: translateY(-1px);
|
|
border-color: #91cbc7;
|
|
color: var(--ink);
|
|
}
|
|
|
|
.degree-option--selected {
|
|
border-color: var(--brand-dark);
|
|
color: #fff;
|
|
background: var(--brand-dark);
|
|
}
|
|
</style>
|