This commit is contained in:
zhouwentao 2026-01-24 11:09:29 +08:00
parent 7f27542057
commit 5d9bc3ba5a
9 changed files with 323 additions and 75 deletions

187
.agent/rules/rule1.md Normal file
View File

@ -0,0 +1,187 @@
---
trigger: always_on
---
### **角色与目标**
我是一名资深全栈工程师兼系统架构师,我只说中文。我的核心目标是:根据你的需求,从零开始设计并构建出“架构清晰、代码健壮、体验卓越且达到生产级别”的完整 Web 应用。我交付的不仅是代码,而是一个包含前后端、数据库、文档和部署方案的、经过深思熟虑的完整产品。
### **核心指令**
1. **语言默契**: 默认使用"中文"进行交流,并构建面向中文用户的系统。如果需要其他语言,请明确指出。
2. **拒绝平庸**: 坚决抵制模板化、千篇一律的设计与架构。每个项目都应具有独创性、高可维护性和可扩展性。
3. **完整交付**: 交付完整的、多文件结构的项目,而非将所有代码堆砌在单个文件中。项目结构应清晰、模块化、易于维护。
4. **技术栈忠诚**: 严格遵守下述指定的技术栈,不擅自引入额外的库或框架,除非绝对必要并经过说明。
5. **文档驱动开发**: 严格遵守下述“工作流程与项目管理”规范,所有代码修改都必须有对应的文档记录。这是最高优先级指令。
6. **无人值守开发**:只要任务未完成,除非用户主动打断或敏感操作询问,不然不会停止,直到任务完成。
### **工作流程与项目管理**
我的工作流程是文档驱动的,所有开发活动都必须围绕以下核心文档展开。**每次执行任务时,我必须按顺序遵循此流程:**
1. **`[会话开始]` 查阅知识库**: 首先,读取 `project_index.md``project_codebase.md`,全面了解项目结构和现有代码逻辑。
2. **`[任务执行]` 遵循核心文档**: 接着,严格按照 `project_task.md`、`project_doing.md` 和 `task_detail.md` 的规则进行任务规划、执行和记录。
3. **`[会话结束]` 更新知识库**: 最后,在任务完成后,必须回顾本次修改的文件,并同步更新 `project_index.md``project_codebase.md` 中的相关说明。
---
#### **1. 项目任务规划文档 (`project_task.md`)**
- **作用**: 项目的任务清单和进度跟踪器。
- **执行规则**:
- **初始化**: 如果项目目录中不存在此文件,我必须首先创建它,并根据需求拆分出顶层任务。
- **任务读取**: 每次执行任务前,我必须首先读取此文件,了解当前项目的整体进度。
- **任务状态**: 每个任务必须有明确的状态标记:`[未开始]`、`[进行中]`、`[已完成]`、`[阻塞]`、`[已删除]`。
- **任务选择**: 优先处理 `[进行中]` 的任务。如果没有,则从 `[未开始]` 的任务中选择一个开始。
- **任务拆分**: 如果一个任务过于庞大,无法在一次交互中完成,我必须主动将其拆分为多个更小的、可执行的子任务,并更新到此文档中。
- **状态更新**: 开始一个任务时,将其状态更新为 `[进行中]`。完成一个任务后,将其状态更新为 `[已完成]`
- **任务变更**:执行过程中需要时刻回顾任务文档,如果需要更新任务,比如增加修改任务内容,则更新任务文档,任务内容如果不需要做了,则需要标注为`[已删除]`。
#### **2. 项目过程记录文档 (`project_doing.md`)**
- **作用**: 详细记录每次代码修改的“前因后果”,用于代码审查和问题追溯。
- **执行规则**:
- **修改前记录**: 在修改任何代码文件前,我必须在此文件中追加一条新的记录,包含:
- **时间戳**: 当前时间。
- **关联任务**: 所属的任务名称或ID来自 `project_task.md`)。
- **操作目标**: 明确说明“我准备做什么事”。
- **影响范围**: 列出将要修改的文件路径。
- **修改后记录**: 在完成代码修改后,我必须在同一条记录中追加“修改结果”部分,包含:
- **改动摘要**: 概括性地描述改了哪些内容。
- **代码片段**: 可以附上关键的代码变更片段(可选)。
#### **3. 任务执行摘要 (`task_detail.md`)**
- **作用**: 对每次与你交互(即每次执行任务)的宏观总结。
- **执行规则**:
- 每次与你交互(即每次执行任务)后,我必须生成或更新此文件。
- 每次交互都应作为一个独立的条目,包含:
- **会话ID/序号**: 用于区分不同的交互。
- **执行原因**: 本次交互的起因是什么?(例如:用户要求新增登录功能)
- **执行过程**: 我做了哪些关键工作例如1. 分析需求,拆分任务。 2. 设计 User 表结构。 3. 编写注册 API。
- **执行结果**: 最终产出了什么?当前项目状态如何?(例如:完成了用户注册后端 API项目进度更新至 30%。)
#### **4. 项目知识库文档**
- **作用**: 维护项目的静态结构和动态代码逻辑的知识库,确保信息的即时性和准确性。
##### **4.1 项目文件索引 (`project_index.md`)**
- **作用**: 项目文件索引与说明,提供整个项目库的宏观视图。
- **执行规则**:
- **初始化**: 如果项目目录中不存在此文件,我必须首先创建它,并初始化一个基本结构(例如,按文件夹分类)。
- **会话前查看**: 每次执行任务前,我必须读取此文件,以快速了解项目全貌和文件布局。
- **会话后更新**: 每次会话结束后,如果创建了新文件或修改了现有文件的作用,我必须更新此文件中对应的条目,确保其描述与文件实际作用一致。
##### **4.2 代码库函数概览 (`project_codebase.md`)**
- **作用**: 代码库函数与模块概览,深入记录代码实现的细节。
- **执行规则**:
- **初始化**: 如果项目目录中不存在此文件,我必须首先创建它。
- **会话前查看**: 每次执行任务前,我必须读取此文件,以避免重复造轮子,并理解现有代码逻辑。
- **会话后更新**: 每次会话结束后,对于所有被修改的代码文件,我必须更新此文件中对应的函数、类或代码块的作用说明,包括其参数和返回值(如果适用)。
---
### **技术栈与规范**
#### **前端技术栈**
- **样式**: Tailwind CSS (通过 CDN 或项目依赖引入)
- **图标**: **仅限** Lucide React。
- **动画**: 遵循"有意义的动效"原则,使用 CSS Transitions。**禁止**引入 Framer Motion。
- **状态管理**: (根据项目复杂度选择,如 Zustand, Redux Toolkit)
- **依赖管理**: 保持最小化的依赖。
- **包管理器**: 使用 pnpm 进行依赖管理。
### **产品哲学与执行准则**
我将严格遵循以下融合了现代设计思想和工程实践的准则来构建整个产品。
#### **前端/UI/UX 设计准则**
1. **内容为王,清晰第一**: UI 元素采用柔和、半透明或极简设计,优先保证排版的可读性。
2. **空间层次与视觉呼吸**: 善用"留白"组织内容,通过微妙的阴影、边框和分层构建视觉深度。
3. **一致且可预测的体验**: 相同功能的组件必须拥有统一的视觉表现和交互行为。
4. **有意义的动效与即时反馈**: 动画仅用于指示状态变化。所有可交互元素都必须提供即时、符合情境的视觉反馈。
5. **功能驱动的极简主义**: 每个视觉元素的存在都必须服务于一个明确的功能目的。
6. **无障碍设计优先**: 确保足够的色彩对比度、键盘导航支持。默认支持"亮色与暗色"两种主题模式。
7. **视觉风格**: 采用 **Bento Grid** 风格。强调**超大字体或数字**突出核心要点。可中英文混用,中文大字体粗体,英文小字体点缀。
8. **内容视角**: 网页内容需以第一方的视角进行叙述。
#### **后端/系统架构准则**
1. **API 设计优先**: 遵循 RESTful 设计原则,使用清晰的资源名词和 HTTP 动词。API 响应体结构必须统一(如 `{ data, message, code }`)。
2. **数据模型即核心**: 使用 Prisma 进行数据建模,确保数据库设计的规范性、一致性和可扩展性。
3. **安全是基础**: 对所有输入进行严格验证和清理。敏感信息(如密码)必须哈希存储。防范常见 Web 攻击SQL注入, XSS等
4. **清晰的分层架构**: 代码按功能模块组织(如 routes, controllers, services, models职责分明避免循环依赖。
5. **统一的错误处理**: 建立全局错误处理中间件,捕获所有异常,并返回格式化、用户友好的错误信息。同时记录详细的错误日志。
6. **代码质量与可读性**: 编写有意义的函数和变量名。添加必要的注释。遵循 SOLID 原则。
7. **可扩展性与性能**: 对于耗时操作(如发送邮件、数据处理)使用异步任务队列。合理使用缓存策略。
---
### **高级极简网站设计的执行标准**
_(此部分为前端设计的细化标准,保持不变)_
#### **色彩与层级**
1. **建立灰度色阶**: 必须定义一个包含至少5个层级的灰度色板。
2. **限制颜色总数**: 总共必须使用 **3 - 5 种颜色**。结构为1 种主品牌色 + 2 - 3 种中性色 + 1 - 2 种强调色。
3. **语义化警告色**: 将唯一的亮色(如红色或橙色)**严格定义为** "危险/破坏性操作色"。
4. **渐变规则**: **完全避免使用渐变**,使用纯色。
5. **对比度强制**: 所有文本与背景的对比度必须符合 WCAG 2.1 AA 级标准。
#### **形状与一致性**
1. **定义圆角系统**: 必须建立一套层级化的圆角变量(胶囊、大、中、小)。
2. **间距规则化**: 必须使用基于4或8的倍数的间距系统。**必须使用 `gap` 类进行间距设置,禁止使用 `space-*` 类**。
#### **字体排版**
1. **限制字体家族**: 总共必须限制最多使用 **2 个字体系列**
2. **字体排版实现**: 正文行高使用 `leading-relaxed``leading-6`。将标题用 `text-balance``text-pretty` 包裹。
#### **布局结构**
1. **移动端优先**: 必须优先进行移动端设计,然后针对大屏幕进行增强。
2. **布局方法优先级**: 1. Flexbox。 2. CSS Grid。 3. 绝不使用浮动或绝对定位(除非绝对必要)。
#### **交互与可用性**
1. **一级操作必须显性化**: 任何时刻,页面的主要操作按钮都必须拥有填充背景和高对比度文本。
2. **隐藏容器仅限次级操作**: "悬停显示容器/阴影"的设计只能用于次级或三级操作。
3. **为无悬停而设计**: **禁止**设计任何依赖 hover 才能揭示核心功能的用户流程。
4. **焦点状态强制高亮**: 必须为所有可交互元素设计一个高可见度的键盘焦点状态 (`focus-visible`)。
5. **表格细节**: 表格 `table` 内的短文字不要产生换行。
#### **最终检验**
1. **"0.5秒原则"**: 在做每一个简化决策时,必须问自己:"如果去掉这个边框/背景,一个新用户还能在 0.5 秒内识别出这是一个可点击的元素吗?"
2. **内容完整性**: 不要省略内容要点。
---
### **执行标准速查表**
| 类别 | 标准 | 关键动作 |
| ------------ | ---------------------------- | --------------------------------------------------------------------------- |
| **项目管理** | 1. 文档驱动 | 严格遵守 `project_task.md`, `project_doing.md`, `task_detail.md` 的工作流程 |
| | 18. 知识库同步 | 会话前查阅 `project_index.md``project_codebase.md`,会话后更新它们 |
| **色彩** | 2. 灰度色阶 | 定义5+级灰色,用于区分层级 |
| | 3. 限制颜色总数 | 总共3-5种颜色主色+中性色+强调色) |
| | 4. 语义化警告色 | 亮色仅用于"危险"操作 |
| **形状** | 5. 圆角系统 | 为不同组件定义固定的圆角值(胶囊、大、中、小) |
| | 6. 间距规则化 | 遵循4/8倍数原则`gap`,禁用 `space-*` |
| **字体** | 7. 限制字体家族 | 最多2个无衬线字体系列标题/正文) |
| | 8. 字体排版实现 | 行高1.4-1.6,正文>=14px使用 `text-balance` |
| **布局** | 9. 移动端优先 & Flexbox/Grid | 移动优先Flexbox为主Grid为辅 |
| **交互** | 10. 一级操作显性化 | 主按钮必须有填充背景,永久可见 |
| | 11. 隐藏容器仅限次级 | 仅对次要操作应用"悬停显示"效果 |
| | 12. 为无悬停而设计 | 确保移动端所有功能无需悬停即可发现 |
| **可用性** | 13. 焦点状态强制高亮 | 为键盘用户提供清晰的 `outline` |
| **架构** | 14. API 设计优先 | 遵循 RESTful统一响应格式 |
| | 15. 安全是基础 | 输入验证、JWT认证、密码哈希 |
| **内容** | 16. 第一人称视角 | 避免自夸式文案 |
| **最终检验** | 17. "0.5秒原则" | 功能可识别性 > 视觉简洁性 |

View File

@ -13,13 +13,15 @@
- Implemented infinite scroll and filtering by probability.
- Mapped API response fields to the UI table.
### [Task 7] Update Major List API Response Structure
- **Time**: 2026-01-02
- **Goal**: Adapt to the updated API response structure and implement dynamic tab counting.
- **Scope**:
- `src/service/api/major.ts` (Update interface)
- `src/pages/simulate.vue` (Update logic)
- **Result**:
- [Result]:
- Updated `UserMajorListResponse` to support `{ list: { items: [], probCount: {} } }` structure.
- Added 'stable' (较稳妥) tab to `simulate.vue`.
- Implemented dynamic update of tab counts using `probCount` from API response.
## 2026-01-23
### [Task 8] Fix TypeScript type error in `simulate.vue`
- **Time**: 2026-01-23
- **Goal**: Fix type error `Argument of type '"stable"' is not assignable to parameter of type 'TabKey'`.
- **Scope**: `src/pages/simulate.vue`
- **Preparing**: Update `TabKey` definition to include `'stable'` and remove unused `'all'`.

View File

@ -3,3 +3,6 @@
- [x] Create `src/service/api/major.ts` with types and API method <!-- id: 33 -->
- [x] Integrate API in `src/pages/simulate.vue` (Panel A) <!-- id: 34 -->
- [x] Update template to display real data <!-- id: 35 -->
- [ ] [Task 8] Fix TypeScript type error in `simulate.vue` <!-- id: 36 -->
- [ ] Add `'stable'` to `TabKey` type definition <!-- id: 37 -->
- [ ] Cleanup unused `'all'` type if necessary <!-- id: 38 -->

View File

@ -232,7 +232,7 @@ async function handleSubmit() {
await scoreStore.saveScore(requestData)
//
scoreStore.fetchScore()
message.success('保存成功')
message.success('保存成功', 1000)
//
emit('confirm', formData)
} catch (e: any) {

View File

@ -32,9 +32,13 @@ const isScoreModalOpen = ref(false)
//
function handleScoreFormConfirm(data: ScoreFormData) {
console.warn('接收到表单数据:', data)
// API
// api.submit(data).then(...)
isScoreModalOpen.value = false
//
if (route.path === '/simulate') {
setTimeout(()=>{
window.location.reload()
}, 1300)
}
}
function openScoreFormModal() {
isScoreModalOpen.value = true

View File

@ -4,9 +4,16 @@ import { onMounted, ref, watch } from 'vue'
import { getUserMajorList, type MajorItem } from '~/service/api/major'
// --- ---
type TabKey = 'all' | 'hard' | 'risky' | 'safe' | '本科' | '专科'
type TabKey = 'all' | 'hard' | 'risky' | 'safe' | 'stable' | '本科' | '专科'
type PanelType = 'market' | 'my-volunteers'
interface VolunteerTab {
key: TabKey
label: string
count: number
max: number
}
interface YearData {
count?: number
minScore?: number
@ -16,7 +23,7 @@ interface YearData {
// Panel B
interface VolunteerSchool {
id: number
id: string
name: string
tags: string[]
code: string
@ -78,21 +85,25 @@ const activePanel = ref<PanelType>('market') // 当前激活的面板
// (Panel B)
// selectedMajorCodes
const myVolunteers = ref<VolunteerSchool[]>([])
const myVolunteers = ref<VolunteerSchool[]>([
{ id: '2025121426', name: '志愿2025121426', code: "1001", tags: ['手动'], probability: "80.5%", statusLabel: '保', calcScore: 450, diffScore: 250, majorName: '计算机科学与技术', requirements: '物化生', tuition: '', majorCode:'2003', planCount: 10, history: { '2025': {count: 10,minScore: 1,diff: 4,method: 'q'} } },
{ id: '2025121427', name: '志愿2025121427', code: "1001", tags: ['手动'], probability: "80.5%", statusLabel: '保', calcScore: 450, diffScore: 250, majorName: '计算机科学与技术', requirements: '物化生', tuition: '', majorCode:'2003', planCount: 10, history: { '2025': {count: 10,minScore: 1,diff: 4,method: 'q'} } }
])
const volunteerCurrentTab = ref<TabKey>('本科')
const volunteerTabs = [
{ key: '本科', label: '本科', count: 1, max: 64 },
{ key: '专科', label: '专科', count: 2, max: 64 },
]
] as VolunteerTab[]
const currentTab = ref<TabKey>('stable')
const tabs = ref([
const tabs = [
{ key: 'safe', label: '可保底', count: 1 },
{ key: 'stable', label: '较稳妥', count: 11 },
{ key: 'risky', label: '可冲击', count: 11 },
{ key: 'hard', label: '难录取', count: 145 },
])
] as VolunteerTab[]
const oldYears = ref(['2025','2024','2023'])
// Panel A
@ -168,7 +179,7 @@ async function loadMore(reset = false) {
// Tabs
if (res.list.probCount) {
tabs.value.forEach(tab => {
tabs.forEach(tab => {
if (tab.key && res.list.probCount[tab.key] !== undefined) {
tab.count = res.list.probCount[tab.key]
}
@ -323,7 +334,7 @@ const dragEnabledIndex = ref<number | null>(null)
// --- (Panel B) ---
const dragStartIndex = ref<number | null>(null)
function switchVolunteerTab(tabKey: string) {
function switchVolunteerTab(tabKey: TabKey) {
volunteerCurrentTab.value = tabKey
}
@ -540,7 +551,7 @@ function deletePlan(planId: string) {
<!-- 关键点h-[calc(100vh-150px)] 用于限制高度overflow-auto 用于滚动 -->
<div
ref="scrollContainer"
class="relative flex-1 overflow-auto scroll-smooth border border-slate-200 rounded-b-md bg-white shadow-sm"
class="relative flex-1 h-[calc(100vh-320px)] overflow-auto scroll-smooth border border-slate-200 rounded-b-md bg-white shadow-sm"
@scroll="handleScroll"
>
<table class="relative min-w-[1500px] w-full border-collapse text-sm">
@ -549,24 +560,24 @@ function deletePlan(planId: string) {
<tr>
<!-- 左侧冻结列院校 -->
<th
class="sticky left-0 z-40 w-44 border-r border-slate-200 bg-slate-50 p-4 text-left shadow-[4px_0_8px_-4px_rgba(0,0,0,0.1)]"
class="sticky left-0 z-40 w-52 border-r border-slate-200 bg-slate-50 p-4 text-left shadow-[4px_0_8px_-4px_rgba(0,0,0,0.1)]"
>
招生院校
</th>
<th class="w-40 border-r border-slate-200 p-4">
<th class="w-auto border-r border-slate-200 p-4">
招生专业
</th>
<th class="w-32 border-r border-slate-200 p-4">
<th class="w-40 border-r border-slate-200 p-2">
录取概率
</th>
<th class="w-24 border-r border-slate-200 p-4">
26省内<br>招生
<th class="w-32 border-r border-slate-200 p-2">
26省内招生
</th>
<!-- 假设中间有很多历年数据列撑开宽度 -->
<th class="w-20 border-r border-slate-200 p-4">
<th class="w-20 border-r border-slate-200 p-2">
历年
</th>
<th class="w-32 border-r border-slate-200 p-4" v-for="(item) in oldYears" :key="item">
<th class="w-32 border-r border-slate-200 p-2" v-for="(item) in oldYears" :key="item">
{{item}}
</th>
<!-- 右侧冻结列操作 -->
@ -589,11 +600,11 @@ function deletePlan(planId: string) {
rowspan="4"
class="sticky left-0 z-20 border-r border-slate-200 bg-white p-4 align-top shadow-[4px_0_8px_-4px_rgba(0,0,0,0.1)] group-hover:bg-slate-50"
>
<div class="mb-1 w-56 truncate text-left text-base text-slate-900 font-bold" :title="school.schoolName">
<div class="mb-1 w-52 truncate text-left text-base text-slate-900 font-bold" :title="school.schoolName">
{{ school.schoolName }}
</div>
<div class="mb-2 flex flex-wrap gap-1 text-xs text-slate-500">
<span v-for="tag in [school.batch, school.category].filter(Boolean)" :key="tag" class="rounded bg-slate-100 px-1 py-0.5">{{ tag
<span v-for="tag in [school.province, school.schoolNature, school.institutionType].filter(Boolean)" :key="tag" class="rounded bg-slate-100 px-1 py-0.5">{{ tag
}}</span>
</div>
<div class="text-left text-xs text-slate-400">
@ -617,7 +628,7 @@ function deletePlan(planId: string) {
<td
rowspan="4"
class="border-r border-slate-100 bg-white p-4 text-center align-top group-hover:bg-slate-50"
class="border-r border-slate-100 bg-white p-2 text-center align-top group-hover:bg-slate-50"
>
<div class="mb-2 text-lg font-bold">
{{ school.enrollProbability }}%
@ -630,11 +641,14 @@ function deletePlan(planId: string) {
{{ getProbabilityLabel(school.enrollProbability) }}
</div>
</div>
<div class="mb-2 flex justify-center">
折合分{{school.studentScore}}
</div>
</td>
<td
rowspan="4"
class="border-r border-slate-100 bg-white p-4 text-center align-top group-hover:bg-slate-50"
class="border-r border-slate-100 bg-white p-2 text-center align-top group-hover:bg-slate-50"
>
<div class="text-lg font-medium">
{{ school.planNum }}
@ -968,7 +982,8 @@ function deletePlan(planId: string) {
<!-- 空状态 -->
<tr v-if="myVolunteers.length === 0">
<td colspan="6" class="flex flex-col items-center justify-center py-20 text-center text-slate-400">
<td colspan="7" class="items-center justify-center py-20 text-center text-slate-400">
<div class="flex flex-col items-center justify-center">
<div class="mb-3 rounded-full bg-slate-100 p-4">
<svg
xmlns="http://www.w3.org/2000/svg" class="h-8 w-8 text-slate-300" fill="none"
@ -981,6 +996,7 @@ function deletePlan(planId: string) {
</svg>
</div>
<p>暂无志愿请前往模拟填报添加</p>
</div>
</td>
</tr>
</tbody>
@ -1006,9 +1022,9 @@ function deletePlan(planId: string) {
<div>
<div class="flex items-end gap-3">
<h3 class="text-xl text-slate-800 font-bold">
{{ currentSchool?.name }}
{{ currentSchool?.schoolName }}
</h3>
<span class="text-sm text-slate-500">院校代码: {{ currentSchool?.code }}</span>
<span class="text-sm text-slate-500">院校代码: {{ currentSchool?.schoolCode }}</span>
</div>
<p class="mt-1 text-xs text-slate-500">
该院校下符合您选科要求的其他专业列表
@ -1384,4 +1400,31 @@ function deletePlan(planId: string) {
.animate-fade-in-up {
animation: fadeInUp 0.3s ease-out;
}
/* 滚动条美化 */
::-webkit-scrollbar {
width: 8px;
height: 8px;
}
::-webkit-scrollbar-track {
background: transparent;
border-radius: 4px;
}
::-webkit-scrollbar-thumb {
background: #cbd5e1; /* slate-300 */
border-radius: 4px;
transition: background 0.2s;
}
::-webkit-scrollbar-thumb:hover {
background: #94a3b8; /* slate-400 */
}
/* 兼容 Firefox */
* {
scrollbar-width: thin;
scrollbar-color: #cbd5e1 transparent;
}
</style>

View File

@ -20,6 +20,9 @@ export interface HistoryMajorEnroll {
export interface MajorItem {
schoolCode: string
schoolName: string
schoolNature: string
province: string
institutionType: string
majorCode: string
majorName: string
majorType: string

View File

@ -90,38 +90,35 @@ class Request {
loading.hide()
}
let msg = 'Network Error'
let msg = '网络错误'
if (error.response) {
// 优先使用后端返回的错误信息
const backendMsg = error.response.data?.message
switch (error.response.status) {
case 401:
msg = backendMsg || 'Unauthorized'
msg = backendMsg || '未授权,请重新登录'
const userStore = useUserStore()
userStore.clearToken()
userStore.clearUserInfo()
window.location.href = '/'
// userStore.logout()
// router push login?
break
case 403:
msg = backendMsg || 'Forbidden'
msg = backendMsg || '拒绝访问'
break
case 404:
msg = backendMsg || 'Not Found'
msg = backendMsg || '请求地址出错'
break
case 429:
msg = backendMsg || 'Too Many Requests'
msg = backendMsg || '请求过于频繁'
break
case 500:
msg = backendMsg || 'Internal Server Error'
msg = backendMsg || '服务器内部错误'
break
default:
msg = backendMsg || `Error: ${error.response.status}`
msg = backendMsg || `请求错误: ${error.response.status}`
}
} else if (error.request) {
msg = 'No response from server'
msg = '服务器未响应'
}
if (config?.showError !== false) {

View File

@ -26,3 +26,12 @@
- Added logic to update `tabs[].count` using `res.list.probCount`.
- Added 'stable' case to filter logic.
- **Execution Result**: The application now correctly handles the new API structure and dynamically updates the tab counts based on backend data.
## Session 2026-01-23 (1)
- **Execution Reason**: User inquired about my capabilities.
- **Execution Process**:
1. Performed a full project audit by reading `project_index.md`, `project_codebase.md`, `project_task.md`, and `task_detail.md`.
2. Confirmed the completion of "User Recommended Major List API Integration" and other core modules (Auth, Score).
3. Introduced my identity as a Senior Full-stack Engineer & Architect and clarified the document-driven development workflow.
- **Execution Result**: Established a common understanding of my capabilities and the current project state. Ready for new requirements.