```mermaid graph LR A[EssayList.vue] -->|选择文章| B[EssayDetail.vue] B -->|开始练习| C[SentenceCard.vue] C -->|第一页| D[WordInput.vue] D -->|输入单词| E[AI 检测、加入 Anki] style D fill:#ffd,stroke:#333,stroke-width:2px ``` ```mermaid graph TD Z((App.vue)) --> A[MainLayout] A --> E[随URL动态路由:ContentArea] A --> D[InnerTopBar] A --> B[SideBar] A --> C[TopBar] E --> F[EssayList] F --> G[EssayDetail] F --> H[AddEssay] B --> I[LoginButton] style E fill:#bbf,stroke:#333,stroke-width:4px; ``` ## 工程结构 ```Java . ├── main.js # Vue 应用的入口文件 ├── App.vue // 路由配置 ├── router │ └── index.js // 页面布局模板 定义整体布局框架 // 多个页面对应一个布局,是 view 的抽象类 ├── layouts │ ├── AuthLayout.vue # 主布局 │ └── MainLayout.vue # 认证页面布局 // 页面视图、与路由对应, 页面级组件,处理业务逻辑 // 每个路由对应一个页面 └── views ├── Home.vue └── essay ├── EssayDetailModal.vue ├── EssayList.vue ├── MappingGridCard.vue └── SentenceCard.vue // 被 views 和 layouts 复用的 UI 组件 // 通过 props 接收数据,通过 emit 发送事件 // 提供具体功能的、较小的组件,通常不与路由直接关联, // 例如按钮、表单输入、卡片 ├── components │ ├── ActiveVocabulary.vue │ ├── AddEssay.vue │ ├── AddEssayModal.vue │ ├── EssayInput.vue │ ├── InnerTopBar.vue │ ├── Portal.vue │ └── layout │ ├── ContentArea.vue │ ├── LoginButton.vue │ ├── SideBar.vue │ ├── Toast.vue │ └── TopBar.vue ├── components.d.ts // 插件配置 ├── plugins │ ├── element-plus.js │ └── googleAuth.js // API 服务 ├── services │ └── api.js // 状态管理 ├── stores │ ├── index.js │ └── user.js ``` ## 渲染 ```Java App.vue └─ <router-view> └─ MainLayout.vue └─ <router-view> └─ EssayList.vue └─ ContentArea └─ 文章列表内容 ``` ## 路由 ```js { // 对应 layouts 中的布局 path: '/', component: () => import('@/layouts/MainLayout.vue'), children: [ // 每个 child 对应 views 中的一个页面 { path: '', name: 'essay-list', component: () => import('@/views/essay/EssayList.vue'), meta: { layout: 'main', requiresAuth: false } }, ] }, ``` - 需要将所有页面内容移到 views 目录下 - 使用 ContentArea 作为页面内容的包装器 - 可以在路由配置中添加 meta 信息来控制权限等 - 可以使用路由守卫来处理权限验证 ## 登录 ```json {"web":{"client_id":"","project_id":"linen-option-420316","auth_uri":"https://accounts.google.com/o/oauth2/auth","token_uri":"https://oauth2.googleapis.com/token","auth_provider_x509_cert_url":"https://www.googleapis.com/oauth2/v1/certs","client_secret":"、","redirect_uris":["http://localhost:5173"],"javascript_origins":["http://localhost:5173"]}} ``` ```js // 添加请求拦截器来设置 JWT token,以后所有的请求都带了能表示用户身份的 token api.interceptors.request.use((config) => { const token = localStorage.getItem('jwt_token'); if (token) { config.headers['Authorization'] = `Bearer ${token}`; } return config; }, (error) => { return Promise.reject(error); }); ``` ### mock 数据 ```shell json-server --watch db.json --port 8000 ``` ### 模态框的使用 模态框(Modal)是一种常见的用户界面元素,在网页设计和应用程序开发中广泛使用。从 AddEssay 组件转变为 AddEssayModal 组件,这是一种模态框(Modal)设计模式。这种设计模式有以下几个特点和优势: - 分离关注点:将添加文章的功能从主界面分离出来,成为一个独立的模态框组件,使得代码结构更清晰,更易于维护。 - 模态框是一个出现在当前页面之上的内容框,通常用于显示重要信息、获取用户输入或确认操作。它的主要特点包括: 1. 覆盖性:模态框会覆盖在主要内容之上,通常会有一个半透明的背景遮罩。 2. 焦点转移:当模态框打开时,用户的注意力会被引导到模态框内容上。 ### 状态管理 - 用户状态(UserStore)是全局状态,只需要在应用的入口点(通常是 App.vue)初始化一次就够了。 - 在 App.vue 中初始化后,所有子组件都可以访问到已经初始化的状态。 ## Vue 和 JavaScript 方法使用频率统计(表格版) ### Vue 相关方法 | 方法 | 使用次数 | | | ----------- | ---- | ----------- | | ref | 18 | | | computed | 2 | | | onMounted | 2 | | | provide | 2 | | | inject | 1 | | | reactive | 1 | | | watch | 1 | | | defineProps | 1 | | | defineEmits | 1 | | | emit | | 本质是发射一个事件出去 | ### JavaScript 数组方法 | 方法 | 使用次数 | |------|----------| | map | 3 | | forEach | 2 | | filter | 1 | ### JavaScript 对象方法 | 方法 | 使用次数 | |------|----------| | Object.keys | 1 | ### DOM 操作方法 | 方法 | 使用次数 | |------|----------| | addEventListener | 1 | | removeEventListener | 1 | | appendChild | 1 | ### 其他常用 JavaScript 方法 | 方法 | 使用次数 | |------|----------| | setTimeout | 2 | | console.log | 多次 (用于调试) | | console.error | 多次 (用于错误处理) | ### Vue Router 相关方法 | 方法 | 使用次数 | |------|----------| | useRouter | 2 | | push (router.push) | 2 | ### Axios 相关方法 | 方法 | 使用次数 | |------|----------| | post | 5 | | get | 3 | | create | 1 | | interceptors.request.use | 1 | ### Pinia 相关方法 | 方法 | 使用次数 | |------|----------| | defineStore | 1 | ### 其他注意到的模式 - 大量使用了 ES6+ 的箭头函数语法 - 使用了解构赋值 - 使用了模板字符串 - 使用了 async/await 进行异步操作