主题
首页模块与极速问诊(二)
本页对应 首页模块与极速问诊,共 7 段视频,约 1 小时 18 分。
学习目标
- 能够将关注医生/文章逻辑抽取为
useFollow组合式函数,支持type参数与默认值,并在医生卡片、文章卡片中复用,验证关注与取消关注请求参数正确。 - 能够说明极速问诊完整流程,识别选择问诊类型、级别、科室、病情描述、就诊患者、支付等环节分别需要收集的数据。
- 能够使用
enum定义问诊类型、疾病时间等枚举,正确使用默认自增、指定初始值和字符串枚举,并区分枚举与字面量联合类型的适用场景。 - 能够根据接口文档定义问诊信息对象及科室数据类型,使用
Partial、Required转换可选/必选属性,并正确定义图片数组等嵌套字段。 - 能够创建持久化问诊仓库,提供并调用记录问诊类型、问诊级别、科室 ID 的函数,通过本地存储验证数据记录结果。
- 能够完成选择问诊级别与科室页面:配置路由、渲染一级科室、通过计算属性切换二级科室,并在点击二级科室时记录对应科室 ID。
知识点
本节目标:把“关注医生”抽成可复用的关注逻辑
本节要完成的是首页模块里的“关注文章”。上一节已经实现了“关注医生”,而关注医生和关注文章的核心逻辑一致:点击按钮后进入加载状态,调用关注/取消关注接口,再根据当前 likeFlag 切换状态。
为什么需要复用? 如果医生卡片和文章卡片各写一遍关注逻辑,后续改接口、改 loading、改取消关注都会重复修改。
怎么想? 先把组件里的关注逻辑抽成一个函数,再把它放到公共模块,最后通过参数区分“医生”和“文章”。
易错点: 不要一上来就复制两份逻辑;先抽函数并验证,再参数化。
先抽函数:从 doctor-card 里提取 loading 和 follow
在 doctor-card 组件中,关注需要两样东西:loading 用于按钮加载状态,follow 用于触发关注。
怎么做? 选中这两段逻辑,用编辑器提示“提取到函数”,先得到一个普通函数,返回 loading 和 follow。转写稿里说“楼顶是支持加载中状态的,弗洛是支持关注的”,这里对应就是 loading 和 follow。
为什么先抽函数? 先保证原有关注医生功能不变,验证通过后再移动位置,降低出错范围。
易错点: 抽完后要在浏览器刷新页面,点击“关注/取消关注”验证,不要直接大改。
ts
// 先形成函数雏形
const useFollow = () => {
const loading = ref(false)
const follow = async (item: { id: string; likeFlag: 0 | 1 }) => {
// 调用关注接口,处理关注/取消关注
}
return { loading, follow }
}函数名建议改成 useFollow。为什么? Vue3 里把“响应式数据 + 逻辑”封装成函数时,习惯用 use 开头,表示“使用某某逻辑”。这里就是“使用关注逻辑”。
移到公共模块:composables/index.ts
医生卡片能用还不够,文章卡片也要用,所以要把函数放到公共模块。转写稿里提到的 comppost、compposts,实际是 composables 文件夹。
怎么想? 这类“组合式函数”统一放到 composables 下,并新建 index.ts 导出。
怎么做? 把 useFollow 剪切到 composables/index.ts,用 export const useFollow = ... 导出,并补齐 ref、类型、接口函数等导入。然后回到 doctor-card 导入 useFollow,删除无用导入。
易错点: 忘记导出/导入;移动后原组件还保留旧逻辑;无用导入未清理。
ts
// composables/index.ts
import { ref } from 'vue'
export type FollowType = 'DOC' | 'knowledge'
export const useFollow = (type: FollowType = 'DOC') => {
const loading = ref(false)
const follow = async (item: { id: string; likeFlag: 0 | 1 }) => {
// 调用项目已有接口:把 item.id 和 type 传给后台
}
return { loading, follow }
}参数化:用 type 区分医生和文章
现在函数只支持医生,因为调用接口时类型是医生。关注文章时,后台需要知道当前关注的是文章,所以要让 useFollow 支持第二个参数 type。
怎么想? 医生卡片不传时默认 DOC;文章卡片传 knowledge。这样医生卡片不用改调用方式,文章卡片也能复用。
怎么做? 给 useFollow 增加参数 type: FollowType = 'DOC',调用接口时把 type 一起传给后台。
易错点: 文章卡片忘记传 knowledge,后台仍按医生处理;默认值不要丢,否则医生卡片要处处传参。
item 类型收窄:只要 id 和 likeFlag
医生对象和文章对象字段不同,但关注逻辑只用到 id 和 likeFlag。转写稿中特别强调:doctor 里有 id、likeFlag,knowledge 里也有 id、likeFlag,所以函数参数只需要约束这两个属性。
为什么? 避免把整个医生类型或文章类型写死,才能同时兼容两类对象。
怎么做? 参数写成 { id: string; likeFlag: 0 | 1 }。调用时传整个 item 也可以,因为结构兼容。
易错点: 不要把参数写成某一个具体医生类型;不要使用未用到的字段。
文章卡片复用与调试
在 knowledge-card 中导入 useFollow,调用 useFollow('knowledge') 得到 loading 和 follow,按钮绑定 loading,点击时执行 follow(item)。
调试过程: 刷新页面后点击医生关注,观察请求里 type 为 DOC;再点击文章关注/取消关注,观察请求里 type 为 knowledge。转写稿中“发的呢就是 knowledge”就是验证点。
易错点: 按钮没绑定 loading;点击事件没传当前 item;文章卡片调用时漏写 knowledge;取消关注和关注共用逻辑时没根据 likeFlag 正确处理。
小结
本节的核心不是写两套关注,而是:先把关注逻辑抽成 useFollow,再放到 composables/index.ts,最后用 type 参数和通用的 { id, likeFlag } 支持医生与文章。这样医生卡片和文章卡片都只负责调用 useFollow,关注逻辑只维护一份。
极速问诊功能演示:先建立流程全景
为什么需要:极速问诊不是一个页面能完成的功能,而是从首页入口开始,经过问诊级别、科室、病情描述、就诊患者,最后到支付页的一条完整链路。如果不先看全景,直接写页面,很容易出现“页面做了、数据接不上”的问题。
怎么想:转写稿里把流程拆得很清楚:第一步选择问诊类型,第二步选择问诊级别,第三步选择科室,第四步填写病情描述,第五步选择就诊患者,最后进入支付页选择支付方式。每个环节只收集一部分信息,最终汇总成问诊订单。
怎么做:演示时从首页点击“极速问诊”,进入急速问诊页面;选择三甲图文问诊或普通图文问诊;再选择科室;填写患病时间、是否就诊过、上传化验单或片子等病情描述;点击下一步选择家庭档案中的患者;进入支付页,勾选协议后点击立即支付,选择支付宝完成支付。转写稿明确说支付环节暂不完整演示,走到支付即认为流程完整走完;支付完成后会跳到问诊室。
易错点:不要把“极速问诊模块”和“急速问诊这种问诊类型”混为一谈;问诊方式实际有三种:问医生、急速问诊、开药问诊,本节选的是急速问诊流程来演示。另注意“二甲主治医生或以上”“三甲医生”等文案不要写成同音错字。
选择问诊类型与问诊级别:先记录,再跳转
为什么需要:用户点“急速问诊”时,系统必须知道本次问诊类型;点“三甲/普通”时,必须知道问诊级别。否则最后生成订单时信息不完整。
怎么想:转写稿的推导是准备一个问诊仓库,每个环节往仓库里记录一部分数据。第一个环节记录问诊类型,第二个环节记录问诊级别。这样后续页面不用重复传参,最后支付前统一读取。
怎么做:演示中先点击“极速问诊”,页面进入急速问诊组件;再点击“三甲图文问诊”或“普通图文问诊”,进入科室选择页。后续实现调试时可观察到:点击极速问诊后,本地存储的 consult 对象中出现 type=2,表示急速问诊;点击三甲或普通后,仓库记录对应问诊级别。
易错点:问诊类型用 1、2、3 表示时语义很差,转写稿因此引入枚举;问诊级别 0/1 相对容易判断,口述中没有强行用枚举。记录函数要返回给组件使用,否则页面点击后无法写入仓库。
选择科室:一级切换二级,点击二级记录 ID
为什么需要:科室数据分一级科室和二级科室。用户点“外科”时,右侧要切换出外科下的二级科室;点“消化内科”等二级科室时,要记录具体科室 ID。
怎么想:一级科室数据里包含 id、name 和 child;二级科室包含 id、name。页面默认激活第一个一级科室,再根据当前激活项计算二级科室列表。
怎么做:演示时进入科室页,默认展示第一个一级科室下的二级科室;点击“外科”可切换右侧列表;点击“消化内科”“儿科—新生儿科”等二级科室时,仓库记录对应科室 ID。转写稿调试中能看到本地存储里出现 depId 的变化。
易错点:一级科室数据还没返回时,直接用 allDept[active.value].child 可能报错,需要可选链或判空;科室 ID 要记录二级科室的 ID,不是一级科室 ID。
病情描述、患者与支付:把零散信息收拢到订单
为什么需要:病情描述包含患病时间、是否就诊、图片等;患者来自家庭档案;支付页还要处理协议和支付方式。它们都属于订单信息的一部分。
怎么想:因为这些信息不是在一个组件里收集完的,所以问诊信息对象不能一开始就要求所有字段必填,否则每个页面都会类型报错。转写稿用 Partial 把对象属性变成可选,后续再逐步补充。
怎么做:演示中填写患病时间、是否就诊过,上传图片;点击下一步选择患者;进入支付页勾选协议,点击立即支付,弹出支付方式后选择支付宝,进入支付页面。转写稿说明支付环节暂不演示,流程到支付完成即结束。
易错点:不要把“问诊信息对象”定义成所有字段必选;图片字段是数组,里面包含图片 ID 和地址;支付页的协议勾选、支付方式选择不能漏。
演示验证与调试观察
本节笔记未附图片与代码,因此不额外插入素材;实现代码在后续各环节展开。演示验证按转写稿走:首页点极速问诊 → 选级别 → 选科室 → 填病情 → 选患者 → 支付页。调试时重点看页面跳转是否正确、仓库是否逐步记录 type、问诊级别、科室 ID 等信息;若点击后无记录,先检查点击事件是否调用仓库函数,再检查仓库是否通过统一入口导出。
为什么这一节先学枚举
在实现“极速问诊”时,第一步要选择问诊类型:问医生、极速问诊、开药问诊。后台约定分别传 1、2、3。如果直接用 1 | 2 | 3 约束类型,代码能跑,但语义很差:看到 2 不能立刻知道它代表极速问诊。枚举就是为这种“一组命名常量”准备的语法。
为什么需要:数字可选值语义弱,容易写错、读错。
怎么想:把 1、2、3 命名成有含义的常量,再用枚举约束参数。
怎么做:用 enum 定义一组常量,通过 枚举名.成员 取值。
易错点:枚举不是普通对象,成员既有名字也有值;不要把它和字面量联合类型混用。
枚举的基本定义与使用
转写稿中先新建 en/index.ts,并先写 export {} 解除“必须是一个模块”的报错。然后定义方向枚举:
ts
export {}
export enum Direction {
up = 0,
down = 1,
left = 2,
right = 3
}接着定义改变方向的函数,并用枚举约束参数:
ts
export const changeDirection = (direction: Direction) => {
console.log(direction)
}调用时不要写 3,而是写 Direction.right:
ts
changeDirection(Direction.right)在 main.ts 中导入测试:
ts
import './en'浏览器打印 3,说明 Direction.right 的值就是 3。
为什么需要:调用方只关心“右”,不关心右到底是几。
怎么想:参数类型用 Direction,可选范围就被限制在枚举成员内。
怎么做:定义枚举 → 函数参数标注枚举类型 → 调用时点出成员。
易错点:Direction.right 是值;direction: Direction 是类型位置,二者不要写反。
默认值与自增规则
枚举成员可以不给初始值:
ts
export enum Direction {
up,
down,
left,
right
}它默认从 0 开始,依次为 0、1、2、3,和上面手写值效果一致。若给 up = 5,后面的成员会继续自增:up=5、down=6、left=7、right=8。转写稿中通过浏览器验证,right 打印为 8。
为什么需要:连续数字很常见,省略初始值能减少重复。
怎么想:只给第一个值,后续让 TS 自增。
怎么做:up = 5,其余不写值。
易错点:自增只适用于数字枚举;不要以为任意位置断开后还能自动推出合理语义。
字符串枚举与定义位置
枚举也可以是字符串:
ts
export enum Direction {
up = 'up',
down = 'down',
left = 'left',
right = 'right'
}但字符串枚举不能像数字枚举那样自动递增,所以每个成员都要给初始值。转写稿中验证,Direction.right 打印为字符串 'right'。
另一个细节:枚举建议定义在 .ts 文件中,而不是 .d.ts。因为枚举包含值,使用枚举时通常会用到这些值,值要参与运行;.d.ts 只是类型声明,不参与运行。
为什么需要:有些枚举值本身就是有语义的字符串。
怎么想:字符串枚举每个成员显式赋值;需要运行的值放 .ts。
怎么做:成员全部写字符串初始值;文件用 index.ts。
易错点:字符串枚举漏写初始值会报错;把带值枚举放进 .d.ts 不符合运行需求。
字面量联合类型还是枚举
转写稿最后给了判断场景:如果可选值是 'up' | 'down' | 'left' | 'right' 这种本身有语义的字符串,用字面量联合类型更直接;如果是一组数字,比如 1 代表问医生、2 代表极速问诊、3 代表开药问诊,数字本身语义差,就更适合用枚举。
为什么需要:不是所有可选值都适合枚举。
怎么想:先看值是否“自解释”。
怎么做:有语义的字符串用联合类型;数字且语义弱用枚举。
易错点:不要为了用枚举而用枚举,也不要让 1、2、3 裸奔在业务代码里。
调试与验证
转写稿的调试路径很清楚:在 en/index.ts 写枚举和函数,在 main.ts 导入,浏览器看打印值;再分别验证默认自增、指定初始值、字符串枚举。这样能把“枚举成员的值是什么”验证清楚,而不是只停留在语法记忆上。
易错点:改完枚举后要刷新页面看最新打印;验证默认值时先注释掉手写值,避免两套定义混在一起。
代码示例
代码一:用 useFollow 组合式函数实现“关注医生与关注文章”的逻辑复用
ts
// ---------- types/consult.ts ----------
/** 关注类型:医生 / 文章(知识) */
export type FollowType = 'doc' | 'knowledge'
/** 关注逻辑只用到 id 和 likeFlag,所以只要满足这两个属性的对象都能传入 */
export type FollowItem = {
id: string
likeFlag: 0 | 1
}ts
// ---------- composables/index.ts ----------
import { ref } from 'vue'
import type { FollowType, FollowItem } from '@/types/consult'
import { followDoctor } from '@/api/consult' // 关注接口
/**
* 使用关注逻辑(组合式函数,命名规范:use 开头)
* @param type 关注类型,默认 'doc' 关注医生,传 'knowledge' 关注文章
*/
export const useFollow = (type: FollowType = 'doc') => {
// 加载中状态:按钮 loading 用
const loading = ref(false)
// 关注 / 取消关注
const follow = async (item: FollowItem) => {
loading.value = true
try {
await followDoctor(item.id, type) // 第二个参数告诉后台关注的是医生还是文章
item.likeFlag = item.likeFlag === 1 ? 0 : 1 // 切换关注状态
} finally {
loading.value = false
}
}
return { loading, follow }
}vue
<!-- ---------- DoctorCard.vue:关注医生 ---------- -->
<script setup lang="ts">
import { useFollow } from '@/composables'
const { loading, follow } = useFollow() // 不传参,默认关注医生
</script>
<template>
<button :loading="loading" @click="follow(item)">关注</button>
</template>vue
<!-- ---------- KnowledgeCard.vue:关注文章 ---------- -->
<script setup lang="ts">
import { useFollow } from '@/composables'
const { loading, follow } = useFollow('knowledge') // 传 knowledge,关注文章
</script>
<template>
<button :loading="loading" @click="follow(item)">关注</button>
</template>要点解释
- 先把
doctor-card里“loading + 关注”两段逻辑抽到一个函数里,再提到公共模块composables/index.ts,这就是逻辑复用。 - 函数名用
use开头(useFollow),因为它是组合式函数:把响应式数据和逻辑封装进函数,所以放composables文件夹。 follow的参数用FollowItem而不是Doctor:医生对象和文章对象都有id、likeFlag,逻辑里也只用到这两个属性,所以能同时兼容两种对象。type参数必须有默认值'doc',否则DoctorCard不传参时就会出问题。
易错点
- 调用接口时一定要把
type传进去(followDoctor(item.id, type)),否则后台不知道你关注的是医生还是文章。 loading必须用try/finally复位,否则接口报错后按钮会一直转圈。- 抽完函数记得删掉组件里残留的旧导入,否则会有未使用变量的报错。
代码二:TS 枚举语法(enum)定义问诊类型与患病时间
ts
// ---------- src/enum/index.ts ----------
export {} // 解除“必须是一个模块”的错误
// 一组可选常量,默认从 0 开始自增:Up=0, Down=1, Left=2, Right=3
export enum Direction {
Up,
Down,
Left,
Right
}
// 值也可以手动指定,后面依旧依次自增
export enum Direction2 {
Up = 5, // 5
Down, // 6
Left, // 7
Right // 8
}
// 字符串枚举:无法自增,每一项都必须给初始值
export enum Direction3 {
Up = 'up',
Down = 'down',
Left = 'left',
Right = 'right'
}
/** 问诊类型:1 找医生、2 极速问诊、3 开药问诊 */
export enum ConsultType {
Doctor = 1,
Fast = 2,
Medication = 3
}
/** 患病时间:1 一周内、2 一个月内、3 半年内、4 半年以上 */
export enum IllnessTime {
Week = 1,
Month, // 2
HalfYear, // 3
More // 4
}
// 使用:枚举既能当值,也能当类型
export const changeDirection = (direction: Direction) => {
console.log(direction)
}
changeDirection(Direction.Right) // 打印 3,不用关心 3 代表什么要点解释
enum用来定义“一组命名常量”,解决纯数字不够语义化的问题:看到ConsultType.Fast就知道是极速问诊,看到2什么也不知道。- 不写初始值时,从
0开始依次加一;写了一个数字初始值,后面的值继续在其基础上加一(Up = 5时Right就是 8)。 - 枚举既可以当类型约束参数范围(
direction: Direction),也可以当值使用(Direction.Right)。 - 枚举包含运行时的值,所以要定义在
index.ts里,而不是只做类型声明的.d.ts文件里。
易错点
- 字符串枚举不能自增,每一项都必须写初始值。
- 像
0/1/2/3这种纯数字且无语义的场景用枚举;像'doc' | 'knowledge'这种本身就有语义的,仍用字面量联合类型更合适。
代码三:问诊仓库(Pinia)+ Partial 可选属性 + 首页选择问诊类型
ts
// ---------- types/consult.ts ----------
import type { ConsultType, IllnessTime } from '@/enum'
export type Image = {
id: string
url: string
}
/** 问诊信息对象(生成问诊订单时提交) */
export type Consult = {
type: ConsultType // 问诊类型
illnessType: 0 | 1 // 问诊级别:0 普通 / 1 三甲
depId: string // 科室 id
illnessDesc: string // 病情描述
illnessTime: IllnessTime // 患病时间
consultFlag: 0 | 1 // 是否就诊过
pictures: Image[] // 化验单/图片数组
patientId: string // 就诊患者 id
couponId?: string // 优惠券 id
}
/** 分步收集,所以全部转成可选属性 */
export type PartialConsult = Partial<Consult>ts
// ---------- stores/modules/consult.ts ----------
import { ref } from 'vue'
import { defineStore } from 'pinia'
import type { PartialConsult } from '@/types/consult'
import type { ConsultType } from '@/enum'
export const useConsultStore = defineStore(
'consult',
() => {
// 问诊信息仓库,默认空对象,每个环节往里塞一部分
const consult = ref<PartialConsult>({})
// 记录问诊类型
const setType = (type: ConsultType) => {
consult.value.type = type
}
// 记录问诊级别:0 普通 / 1 三甲
const setIllnessType = (type: 0 | 1) => {
consult.value.illnessType = type
}
// 记录问诊科室 id
const setDep = (depId: string) => {
consult.value.depId = depId
}
return { consult, setType, setIllnessType, setDep }
},
{ persist: { key: 'consult' } } // 持久化存储
)vue
<!-- ---------- views/Home/index.vue:选择极速问诊 ---------- -->
<script setup lang="ts">
import { useConsultStore } from '@/stores'
import { ConsultType } from '@/enum'
const store = useConsultStore()
</script>
<template>
<!-- 点击极速问诊,把问诊类型记录到仓库 -->
<div @click="store.setType(ConsultType.Fast)">极速问诊</div>
</template>要点解释
- 问诊流程分很多步(选类型 → 选级别 → 选科室 → 填病情 → 选患者 → 支付),信息是分步收集的,所以仓库里存
PartialConsult(所有属性可选),避免初始化时就要求填满所有字段。 Partial<T>是 TS 内置类型,把对象所有属性一次性转成可选,比手动一个个加?方便;反向的Required<T>可把属性全部转回必选。defineStore第三个参数{ persist: { key: 'consult' } }让仓库持久化,刷新页面数据不丢。setType的参数类型用ConsultType枚举约束,调用时传ConsultType.Fast,语义清晰。
易错点
- 仓库里
consult必须用ref<PartialConsult>({})声明类型,否则.type、.depId都会报类型错误。 - 枚举要定义在
.ts里(参与运行),从@/enum导入;如果放进.d.ts只做类型声明,运行时取值会拿不到。 - 每次新增仓库都要在
stores/index.ts统一导出,页面里才能import { useConsultStore } from '@/stores'。
常见错误
1. 组合式函数建在组件目录里,别的模块引用不到
- 现象:
knowledge-card里import { useFollow } from './useFollow'一路往上找,路径报红;或者复制一份逻辑到文章卡片里,改一处忘一处。 - 原因:只在
doctor-card内部"提取到函数",没有真正提取到公用模块。 - 修法:统一放到
composables/index.ts并export,组件只负责调用,不负责实现。
2. 关注文章时请求仍发 type: 'doc'
- 现象:点文章的关注,Network 里
like请求的type还是 doc,后端把记录记到了医生身上。 - 原因:
useFollow加了第二个参数但调用时没传,或默认值写在了错误的位置(写成了必填)。 - 修法:
type: FollowType = 'doc'给默认值,文章卡片显式传useFollow('knowledge')。
3. item 参数类型写死成 Doctor,文章卡片调用报类型错误
- 现象:
useFollow里item: Doctor,knowledge-card传Knowledge时 TS 报"缺少 likeFlag/属性不匹配"。 - 原因:按具体业务对象建模,而不是按逻辑实际用到的字段建模。
- 修法:只声明真正用到的字段
{ id: string; likeFlag: 0 | 1 },两种对象自然都满足。
4. 枚举写在 .d.ts 里,运行时取到 undefined
- 现象:
ConsultType.Fast打印出来是undefined,接口参数变成type=undefined。 - 原因:
.d.ts只是类型声明,不参与编译产出,枚举是要带值参与运行的。 - 修法:枚举放到普通
.ts(如enums/index.ts),.d.ts只放纯类型。
5. 字符串枚举漏写初始值
- 现象:
enum Direction { Up = 'up', Down, Left }编译报错。 - 原因:字符串成员不能自增,只有数字枚举才支持省略后续值。
- 修法:字符串枚举每个成员都写全;数字枚举才可以只写第一个。
6. 问诊信息对象保持全必选,分步收集时报类型错误
- 现象:
consult.value.type = ...之后 TS 提示"缺少 deptId / illnessTime 等属性"。 - 原因:一个对象被拆到多个页面收集,任何一步都不完整,用完整类型约束必然报错。
- 修法:仓库里声明为
Partial<Consult>,只在最终提交订单前做一次完整性校验(或断言成Consult)。
7. 二级科室计算属性在数据回来前就取索引
- 现象:首屏刷新控制台报
Cannot read properties of undefined (reading 'child'),或页面白屏一瞬。 - 原因:
allDept.value[active.value].child在allDept还是空数组时直接取[0]。 - 修法:
allDept.value[active.value]?.child ?? [],保证计算属性永远返回数组。
8. 仓库里赋值漏 .value,页面不更新
- 现象:点了三甲,本地存储里
illnessType一直是旧值/空,页面数据没变。 - 原因:
consult是ref,写成consult.type = type。 - 修法:
consult.value.type = type;同时确认persist: true已配置。
练习
1. 扩展关注类型 现在 useFollow 支持 doc 和 knowledge。要求再支持"关注药品"(type: 'medicine'),且药品对象字段是 { id, likeFlag }。写出 FollowType、useFollow 签名、以及药品卡片里的调用代码。思考:这次改动动了几个文件?如果当初没抽组合式函数,要动几个文件?
2. 枚举 vs 字面量联合类型 下面三组值,分别选择"枚举"还是"字面量联合类型"并说明理由:
- A:
'pending' | 'paid' | 'cancelled' | 'refunded' - B:
1 | 2 | 3 | 4(1=一周内,2=一个月内,3=半年内,4=半年以上) - C:
'male' | 'female'
3. 类型体操 已知 interface Consult { type: ConsultType; deptId: string; patientId: string; pictures: Image[] }。写出两个类型别名,分别表示"全部可选"和"在全部可选基础上再把 deptId 恢复为必选"。后者只能用 Partial / Required / 交叉类型组合完成。
4. 修复下面这段科室组件
ts
const active = ref(0)
const allDept = ref<TopDept[]>([])
const subDept = computed(() => allDept.value[active.value].child)
onMounted(async () => { const res = await getAllDept(); allDept.value = res.data })指出至少 2 处会导致运行时或首屏问题的写法并改正。
5. 补全仓库consult.ts 里现在有 setType 和 setIllnessType,请补 setDept(deptId: string),并说明:
- 参数为什么用
string而不是Dept对象; - 在
consult-dept.vue里点击二级科室时如何调用; - 如何在浏览器里验证写入成功。
课件图示






原视频对照
| # | 视频 | 时长 |
|---|---|---|
| 1 | 首页模块-逻辑复用关注文章 | 12:59 |
| 2 | 极速问诊-功能演示 | 04:04 |
| 3 | 极速问诊-枚举语法 | 13:40 |
| 4 | 极速问诊-问诊信息类型定义 | 11:30 |
| 5 | 极速问诊-选择问诊类型 | 10:01 |
| 6 | 极速问诊-选择问诊级别 | 07:52 |
| 7 | 极速问诊-选择问诊科室 | 18:00 |
小结
- 逻辑复用的分界线不是"这段代码像",而是"这段代码用到的字段和分支是否一致"——
useFollow之所以能同时服务医生和文章,是因为它只依赖id和likeFlag两个字段。 - 组合式函数以
use开头、放composables,本质是"响应式状态 + 逻辑"的打包,不是普通工具函数;判断标准是它内部有没有ref/computed/生命周期。 - 类型参数化的关键动作是"把差异抽成参数":
type抽成入参并给默认值,item从具体对象收窄成最小结构,一次改动就同时满足了两种业务。 - 数字型可选值用枚举,语义型可选值用字面量联合类型;判断依据是"看这个值能不能一眼读懂"。
- 枚举必须住在
.ts里——它带值、参与运行;.d.ts只描述形状。这条决定了"类型放哪"这类问题的一半答案。 - 分步收集的对象一律先声明成
Partial<T>,完整性校验推迟到提交那一刻;反过来,只有真正需要全部字段的地方才用Required。 - 仓库(store)在问诊流程里扮演"跨页面收集器",每个环节只提供
setXxx一个入口、只写自己那一段,页面不直接改state——这条纪律比函数写得好不好更重要。 - 计算属性必须对"数据还没回来"这一状态负责,可选链加兜底数组是标配,而不是靠"用户不会那么快点击"来赌。