diff --git a/app/(tabs)/profile.tsx b/app/(tabs)/profile.tsx index 560dd59..f17c4eb 100644 --- a/app/(tabs)/profile.tsx +++ b/app/(tabs)/profile.tsx @@ -15,18 +15,18 @@ import { UNSET_LABEL } from '../../src/constants'; import { Avatar } from '../../components/onboarding/ui/Avatar'; import { isAvatarPreset } from '../../components/onboarding/assets'; import { useRole } from '../../src/context/RoleContext'; -import { colors, headerGradientSolid } from '../../src/theme'; +import { colors, headerGradientSolid, jobLevelTextColors } from '../../src/theme'; import type { Badge, DimensionScore, EnterpriseEntry, JobPreference, MenuItem, UserStats } from '../../src/types'; /* ===================== 展示层映射(全部使用主题令牌) ===================== */ -/** 维度等级 → 展示配置(优秀=品牌蓝,良好=灰色) */ +/** 维度等级 → 展示配置(优秀/良好 均取品牌蓝,与 jobLevelTextColors 对齐,消除两处取色分歧) */ const DIMENSION_LEVEL: Record< DimensionScore['level'], { label: string; bar: string; bg: string; text: string } > = { excellent: { label: '优秀', bar: colors.primary, bg: colors.primaryLight, text: colors.primaryDark }, - good: { label: '良好', bar: colors.textHint, bg: colors.pageBg, text: colors.textSecondary }, + good: { label: '良好', bar: colors.textHint, bg: colors.pageBg, text: jobLevelTextColors.good }, }; /** 求职偏好字段 → 展示标签 */ diff --git a/app/job-publish.tsx b/app/job-publish.tsx index 788addd..d1de561 100644 --- a/app/job-publish.tsx +++ b/app/job-publish.tsx @@ -24,19 +24,16 @@ const WORK_TYPE_OPTIONS = WORK_TYPE_LABELS; * 经验要求选项 —— **刻意不从 constants 的 `EXPERIENCE_OPTIONS` 取**。 * * 两张表不是同一个事实,名字像而已: - * · constants 的 `EXPERIENCE_OPTIONS` 是**筛选项**,含「不限」(表示不加限制), - * 且不含「1年以内」; - * · 本表是**企业发布岗位时的必填要求**,没有「不限」(必须明确要求), - * 且含「1年以内」—— 与 `JobExperienceRequirement` 的成员逐一对应。 + * · constants 的 `EXPERIENCE_OPTIONS` 是**筛选项**,首项为「不限」(表示不加限制); + * · 本表是**企业发布岗位时的必填要求**,没有「不限」(必须明确要求)。 * 强行合并会让发布页出现「不限」这个不该有的选项。 * - * 已知遗留(非本批处理):`JobExperienceRequirement` 的「1年以内」在 mock 岗位数据里 - * **一次都没用到**,而筛选项表里也没有它 —— 即这个取值目前既筛不到、也没有样本。 - * 属数据/类型层面的取舍,需产品定夺,已记入《项目开发状态.md》4.16。 + * 两者的取值档位现已对齐(同为 应届生 / 1-3年 / 3-5年 / 5-10年 / 10年以上), + * 与 `JobExperienceRequirement` 的成员逐一对应。原先发布端多出的「1年以内」属悬空值 + * (筛选侧无对应档位、mock 数据也未使用),已按产品口径移除。 */ const EXPERIENCE_OPTIONS: JobPublishFormData['experience'][] = [ '应届生', - '1年以内', '1-3年', '3-5年', '5-10年', diff --git a/app/progress.tsx b/app/progress.tsx index f08c7fc..2913597 100644 --- a/app/progress.tsx +++ b/app/progress.tsx @@ -1,7 +1,7 @@ import { Ionicons } from '@expo/vector-icons'; import { LinearGradient } from 'expo-linear-gradient'; import { useRouter } from 'expo-router'; -import { useMemo, useState } from 'react'; +import { useEffect, useMemo, useState } from 'react'; import { ActivityIndicator, Pressable, @@ -231,13 +231,28 @@ function ErrorState({ onRetry }: { onRetry: () => void }) { export default function ProgressScreen() { const router = useRouter(); const { progress, loading, error, refresh } = useJobProgress(); + // 公司列表首屏抽样条数:列表过长时只列前 N 家,并提供「查看全部」入口(移动端长列表标准做法) + const PREVIEW_COUNT = 5; const [activeTab, setActiveTab] = useState<'all' | JobProgressStage>('all'); + const [showAll, setShowAll] = useState(false); + + const totalCompanies = progress?.companies.length ?? 0; const companies = useMemo(() => { if (!progress) return []; - if (activeTab === 'all') return progress.companies; - return progress.companies.filter((c) => c.stage === activeTab); - }, [progress, activeTab]); + const list = + activeTab === 'all' + ? progress.companies + : progress.companies.filter((c) => c.stage === activeTab); + // 「全部」页首屏只抽样前 N 家;切到具体阶段或展开后展示全量 + if (activeTab === 'all' && !showAll) return list.slice(0, PREVIEW_COUNT); + return list; + }, [progress, activeTab, showAll]); + + // 切换 Tab 时重置「展开全部」,回到抽样视图 + useEffect(() => { + setShowAll(false); + }, [activeTab]); // 标题文字跟随当前 Tab,与下方列表同源(见渲染处注释) const activeLabel = TABS.find((tab) => tab.key === activeTab)?.label ?? '全部'; @@ -286,7 +301,18 @@ export default function ProgressScreen() { 投递岗位后,进度会在这里按阶段更新 ) : ( - companies.map((company) => ) + <> + {companies.map((company) => ( + + ))} + {/* 全部页且公司数超过抽样条数:提供「查看全部 N 家」入口,展开后渲染完整列表 */} + {activeTab === 'all' && totalCompanies > PREVIEW_COUNT && !showAll && ( + setShowAll(true)} hitSlop={4}> + 查看全部 {totalCompanies} 家 + + + )} + )} {progress.offers.length > 0 && ( @@ -319,6 +345,19 @@ const styles = StyleSheet.create({ headerBtn: { width: 40, alignItems: 'center' }, headerTitle: { fontSize: 17, fontWeight: '700', color: colors.textPrimary }, body: { padding: 8, paddingBottom: 32 }, + viewAllBtn: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'center', + marginTop: 8, + paddingVertical: 12, + }, + viewAllText: { + fontSize: 14, + fontWeight: '600', + color: colors.primary, + marginRight: 2, + }, statsCard: { borderRadius: 8, padding: 8, diff --git a/src/__tests__/shared-color-maps.test.ts b/src/__tests__/shared-color-maps.test.ts index 0ce2276..667a051 100644 --- a/src/__tests__/shared-color-maps.test.ts +++ b/src/__tests__/shared-color-maps.test.ts @@ -246,12 +246,14 @@ describe('选项词表:企业端发布页与 constants 的关系', () => { }); /** - * 经验要求的两张表**不是**同一个事实,本批刻意不合并(合并会让发布页多出一个 - * 不该有的「不限」选项)。这例把差异锁住,防止后来者按名字想当然地合并。 + * 经验要求的两张表**不是**同一个事实,不能合并:发布页是必填要求(无「不限」兜底), + * 筛选页是可选过滤(首项为「不限」)。但两者的取值档位现已对齐(同为 + * 应届生 / 1-3年 / 3-5年 / 5-10年 / 10年以上),发布页不再保留筛选页没有的「1年以内」。 + * 这例把「发布页不含「不限」、且已去掉「1年以内」」锁住,防止后来者误合并或回填悬空值。 */ - it('发布页的经验要求与筛选项表本就该不同:前者含「1年以内」不含「不限」,后者反之', () => { + it('发布页经验要求与筛选档位已对齐:不含「不限」也不含「1年以内」', () => { const src = read('app/job-publish.tsx'); - expect(src).toContain("'1年以内'"); + expect(src).not.toContain("'1年以内'"); // 发布页那张表里不该出现「不限」这个筛选项专用的兜底值 const localExperience = src.slice(src.indexOf('const EXPERIENCE_OPTIONS')); expect(localExperience.slice(0, 400)).not.toContain("'不限'"); diff --git a/src/types/enterprise.ts b/src/types/enterprise.ts index b3cac3c..bf03846 100644 --- a/src/types/enterprise.ts +++ b/src/types/enterprise.ts @@ -103,7 +103,6 @@ export type JobEducationRequirement = '不限' | '大专' | '本科' | '硕士' /** 经验要求 */ export type JobExperienceRequirement = | '应届生' - | '1年以内' | '1-3年' | '3-5年' | '5-10年' diff --git a/项目开发状态.md b/项目开发状态.md index 5a78a58..112e749 100644 --- a/项目开发状态.md +++ b/项目开发状态.md @@ -1838,6 +1838,27 @@ npx tsc --noEmit # 代码规范 npx eslint . +### 4.22 产品待定三项落地(2026-09-21 批次,接 4.21,消解 4.16/4.19 待定口径) + +此前 4.16/4.19 遗留三项需产品定夺的口径,本批由产品拍板后落地,均为低风险纯前端改动,门禁与 365 测试全绿。 + +**① 「良好」档取色统一为蓝(消解 4.16 配色分歧)** +- 原分歧:`jobLevelTextColors.good = colors.primary`(蓝),但 `app/(tabs)/profile.tsx` 的 `DIMENSION_LEVEL.good.text = colors.textSecondary`(灰),两处对「良好」取色不一致。 +- 决策:统一用蓝 primary。改动 `app/(tabs)/profile.tsx`:导入 `jobLevelTextColors` 并令 `DIMENSION_LEVEL.good.text = jobLevelTextColors.good`(单源派生,bar/bg 仍保留灰阶以区别于「优秀」的实心蓝条)。 +- 文件:`app/(tabs)/profile.tsx`(改)。 + +**② 发布端去掉「1年以内」经验档(消解悬空值)** +- 原问题:`JobExperienceRequirement` 含 `'1年以内'`,但求职端筛选 `EXPERIENCE_OPTIONS` 无此档(应届生直接跳 1-3年);该值发布能选、筛选过滤不到,且 mock 数据从未使用。 +- 决策:从类型与发布选项移除「1年以内」,与筛选档位对齐(应届生/1-3年/3-5年/5-10年/10年以上),「不限」仍仅筛选侧有。 +- 文件:`src/types/enterprise.ts`(改,移除联合成员)、`app/job-publish.tsx`(改,移除选项 + 更新注释)、`src/__tests__/shared-color-maps.test.ts`(改,断言由「含1年以内」改为「不含1年以内且不含不限」)。 + +**③ 求职进度页 companies 抽样 + 查看全部入口** +- 原问题:`app/progress.tsx` 公司列表为 28 条投递的抽样(mock 仅 4 条),此前缺数量/空态保护(offers 有、companies 漏了,已在 4.21 前后补齐空态)。 +- 决策:首屏抽样前 5 家,超量时提供「查看全部 N 家」入口展开全量;切换 Tab 重置为抽样视图。 +- 文件:`app/progress.tsx`(改:新增 `showAll` 状态 + `PREVIEW_COUNT=5` 抽样逻辑 + `useEffect` 重置 + 「查看全部」按钮 + `viewAllBtn/viewAllText` 样式)。 + +**验收**:tsc 0 · eslint 0 · check:spacing 0 · check:routes 0 · check:affordances 0 · check:icons 0 · check:backend-stubs 0 · 365 测试全绿(26 套件)。 + # 路由闭合审计(改动路由后必跑) node scripts/check-routes.mjs