怎么用 这是格式与深度的参考,不是让你照抄的模板。数字全部是示例,直接抄进简历一问就穿——必须换成你自己的真实数据,而且要能说清是怎么测出来的。下面每个模块都有「数字是怎么测的」和「面试追问」两段,写不出那两段的模块就不要往简历上写

面试题库(电商 / 短视频 / 社区 / 本地生活 / 旅游 / 企业服务 多业务场景)

实习级这一档是干什么的 工作流引擎的移动端表单渲染、离线数据同步、即时消息长连接这些核心台面,实习生大概率碰不到。这一档收的是企业移动端里实习生真的会被分派、但仍然有明确技术含量的活。下面三个模块彼此独立——只做过一个就只写一个。
怎么讲才不显得 low 「我做了个选人组件,拉组织树渲染出来」和「几千人的组织不能一次全拉,要按层级懒加载;而且『勾选一个部门』的语义必须定清楚——是选中当前这些人,还是选中这个部门(以后新入职的人也算)?两种语义在审批场景下结果完全不同」——同一件事,后者面试官会顺着追问。这一档的破解办法是认清「这些都是入口型功能,本身不产生业务价值,但卡住了整个流程就断在这里」:扫不上码进不去、选不到人发不出审批、预览打不开看不了附件。
三条自检 一、能说出不这么做会怎样(二维码没有确认环节会被钓鱼利用、组织树全量拉在大企业会卡死、预览失败不给下载入口用户就看不了文件);二、能说出你踩过的具体坑;三、能说出量级(多少人的组织、几层结构、文件多大几种格式)。三条都有就能写。
项目背景设定 面向企业的 SaaS 产品移动端(uni-app,微信小程序 + iOS/Android),Vue 3 组合式 API。我负责的是手机端扫码登录确认、组织通讯录选人组件、附件预览这三块入口型功能——它们本身不产生业务价值,但每个业务流程都要经过它们。
为什么这三块值得写 它们共同的特点是成功依赖一连串外部条件,而失败原因往往不在我们这一侧:扫码要网络、相机权限、二维码未过期;选人要组织数据、可见范围权限;预览要转换服务、格式支持。所以这三块的技术含量都在「让用户知道卡在哪、以及还有什么别的路」——而不是把正常路径实现出来。

模块一:手机端扫码登录确认

  1. 手机端扫码登录确认(确认环节不可省与登录目标信息展示 + 二维码状态机与有效期 + 令牌一次性使用 + 相机权限与失败路径的明确出路)★★★
    简历这样写 手机端扫码登录确认(uni-app + 扫码能力 + 状态机 + 一次性令牌):扫码后原为直接完成登录,改为必须经过手机端确认,且确认页显式展示本次登录的目标端、大致位置与时间,避免用户扫到伪造二维码后无感知地把账号授权出去;二维码状态收敛为待扫描、已扫描待确认、已确认、已过期、已取消五态,手机端按状态渲染对应界面,过期给出「请在电脑上刷新二维码」的明确指引而非笼统报错;扫码令牌一次性使用且有较短有效期,确认后立即失效,重复提交不会二次登录;对相机权限被拒、扫到非本产品二维码、网络失败等路径分别给出可操作的出路(引导开启权限、说明这不是本产品的码、重试);确认与取消都会即时反馈到电脑端。上线后扫码登录相关的求助明显减少,且通过了安全评审对授权确认的要求。
    展开完整拆解
    为什么要这么设计

    扫码登录的需求是:用户在电脑上打开管理后台,用手机 App 扫一下就登录,不用输密码。我负责手机端这一侧。第一版做得很顺——扫到码、调接口、电脑端就登录了。四个问题,其中一个是安全评审直接打回的。

    一是没有确认环节,这是被安全评审打回的原因。我的实现是扫到码就直接完成登录问题是用户可能扫到一个伪造的二维码——攻击者在自己的电脑上打开登录页、把二维码截图发给受害者(伪装成别的用途),受害者一扫,攻击者的浏览器就登录了受害者的账号而受害者全程不知道自己做了什么。

    二是二维码状态处理不全,报错笼统。二维码有有效期。过期之后我只是返回「登录失败」——用户不知道是网络问题、还是码不对、还是要去电脑上刷新。他的做法是反复扫同一个已过期的码。

    三是令牌可以重复使用。我没有把扫码令牌设为一次性。理论上同一个码被确认之后还能再次提交——虽然实际很难利用,但这是明确的设计缺陷。

    四是相机权限被拒之后是一个死胡同。用户之前拒绝过相机权限,点「扫一扫」没有任何反应(或者只弹一个「无法使用相机」)。他不知道要去系统设置里开,也不知道有没有别的登录方式。

    所以四个改动:加必须的确认环节并展示登录目标信息二维码状态收敛为五态并按状态给明确指引令牌一次性且短有效期各失败路径给出可操作的出路

    这个模块最想说的一句话是:扫码登录里那个「在手机上点一下确认」的步骤,看起来是多余的一步,实际上它是整个方案安全性的核心。因为「扫码」这个动作本身不代表用户理解了自己在授权什么——他可能是被骗着扫的。确认页的价值不在于「再点一次」,而在于它把「你正在授权某个设备登录你的账号」这件事明确告诉用户,让他有机会说不。我最初把它当成了一次多余的交互,想省掉它来让流程更顺。

    整体链路
    整体时序(手机端只负责其中两步,但要理解全流程) │ ├─ 电脑端:请求生成二维码 → 展示 → 轮询状态 │ 二维码内容是一个短时效的一次性标识,不含任何凭据 │ ├─ 手机端:扫码 → 上报「已扫描」→ 展示确认页 → 用户确认 │ ├─ 电脑端:轮询到「已确认」→ 用换取的凭据完成登录 │ └─ 关键:登录凭据始终由服务端签发给电脑端 二维码里绝不能直接放凭据 —— 截图转发就等于账号泄露 二维码状态机(五态,手机端按状态渲染) │ ├─ 待扫描 → 正常进入确认页 ├─ 已扫描待确认 → 若被其他设备先扫,提示已被扫描 ├─ 已确认 → 提示已完成,不重复处理 ├─ 已过期 → 明确提示「请在电脑上刷新二维码」 │ 只报「登录失败」的后果:用户反复扫同一个过期码 └─ 已取消 → 提示已取消,可重新扫码 确认页(这一步是安全性的核心,不能省) │ ├─ 必须有一次显式确认,不能扫到就登录 │ 直接登录的风险 │ 攻击者在自己电脑上打开登录页、把二维码发给受害者 │ 受害者一扫 → 攻击者的浏览器登录了受害者的账号 │ 而受害者全程不知道自己做了什么 │ ├─ 确认页必须展示「本次登录的对象」 │ 目标端(管理后台 / 某个客户端)· 大致位置 · 时间 │ 展示这些信息才能让用户判断「这是我自己发起的吗」 │ ├─ 位置只做粗粒度展示并标明是估算(精确了会误导也涉及隐私) │ ├─ 异地或异常时加醒目提示(不是自己操作请点取消) │ └─ 取消要和确认一样显眼,不要把取消做成小字 默认倾向应该是「谨慎」,而不是引导他快速点确认 令牌与时效 ├─ 二维码标识短有效期(过期需重新生成) ├─ 一次性使用:确认后立即失效 ├─ 重复提交确认要幂等,不产生二次登录 └─ 手机端页面停留过久要主动重新校验状态(可能已过期) 失败路径(每一条都要有出路) │ ├─ 相机权限被拒 → 说明用途 + 引导到系统设置 + 给出替代登录方式 │ 只弹「无法使用相机」的后果:用户进入死胡同 │ ├─ 扫到非本产品二维码 → 明确说明「这不是本产品的登录码」 │ 笼统报错的后果:用户以为是我们的功能坏了 │ ├─ 网络失败 → 保留在确认页并提供重试,不要退回上一页 │ 退回的后果:他要重新扫一次(码可能已经过期) │ └─ 账号无权限访问目标端 → 明确说明原因而不是「登录失败」 反馈闭环 ├─ 确认与取消都要即时反馈到电脑端(电脑端轮询能立刻感知) ├─ 手机端确认成功后给明确的成功态,并提示可以关闭 └─ 登录成功要在账号的登录记录里留档(设备 · 时间 · 位置)
    分步拆解
    1. 二维码里绝不能直接放登录凭据,只放一个短时效的一次性标识。放凭据的话截图转发就等于账号泄露。凭据必须由服务端签发给电脑端。
    2. 必须有一次显式确认,不能扫到就登录。这是安全评审打回我第一版的原因——攻击者可以把自己电脑上的二维码发给受害者,受害者一扫,攻击者就登录了受害者的账号。
    3. 确认页必须展示「本次登录的对象」:目标端、大致位置、时间。只有展示这些信息,用户才能判断「这是我自己发起的吗」——没有这些信息的确认页只是多点一下,起不到保护作用。
    4. 位置只做粗粒度展示并标明是估算。写精确了会误导(估算本身有误差),也涉及隐私。
    5. 异地或异常时要加醒目提示。「不是自己操作请点取消」——把判断的线索给到用户。
    6. 取消按钮要和确认一样显眼。不要把取消做成小字——这类页面的默认倾向应该是谨慎,而不是引导他快速点确认。
    7. 二维码状态要收敛成五态并按状态渲染不同界面。待扫描、已扫描待确认、已确认、已过期、已取消。
    8. 过期要明确提示「请在电脑上刷新二维码」。只报「登录失败」的后果是用户反复扫同一个已过期的码。
    9. 令牌必须一次性且短有效期,确认后立即失效。虽然重复利用的实际难度大,但这是明确的设计缺陷,评审会问。
    10. 重复提交确认要幂等,不产生二次登录。用户会因为页面没反应而重复点。
    11. 手机端页面停留过久要主动重新校验状态。他可能扫完放下手机去干别的,回来时码已经过期了——这时候点确认应该提示过期而不是报错。
    12. 相机权限被拒要给完整出路:说明用途、引导到系统设置、给出替代登录方式。只弹「无法使用相机」会让用户进入死胡同。
    13. 扫到非本产品的二维码要明确说明。笼统报错的后果是用户以为是我们的功能坏了。
    14. 网络失败时要保留在确认页并提供重试,不要退回上一页。退回的后果是他要重新扫一次,而码可能已经过期了。
    15. 账号无权限访问目标端时要说明具体原因。「您的账号没有管理后台的访问权限」比「登录失败」有用得多。
    16. 确认与取消都要即时反馈到电脑端。取消之后电脑端应该立刻显示「已取消」,而不是继续转圈等超时。
    17. 手机端确认成功后要给明确的成功态并提示可以关闭。不给成功态的话他不确定有没有成功,会再点一次。
    18. 登录成功要在账号的登录记录里留档(设备、时间、位置)。这是用户事后自查的依据,也和管理后台的会话列表配套。
    关键决策与取舍

    保留确认环节,即使它让流程多了一步。产品最初希望「扫完直接登录」以追求流畅。但「扫码」这个动作本身不代表用户理解了自己在授权什么——他可能是被骗着扫的确认页的价值不在于「再点一次」,而在于它把「你正在授权某个设备登录你的账号」明确告诉用户,让他有机会说不。判据是「省掉这一步之后,用户还有没有机会发现自己被骗」:没有,那这一步就不能省。而且我做了一件让这个取舍更站得住的事:确认页展示登录目标端、位置和时间——如果确认页什么信息都不给,那它确实只是多点一下,产品的质疑就是对的。

    位置信息做粗粒度并标明估算,而不是精确定位。精确位置更有助于用户判断,但基于网络的位置估算本身误差很大,显示得太精确会造成误判(他看到一个陌生的街道名会以为被攻击了,实际只是估算偏差);而且这涉及隐私判据是「这个信息的精度是否可靠」:不可靠就不要假装精确。

    失败路径全部给「可操作的出路」而不是统一的错误提示。统一提示实现简单,但每种失败的正确应对完全不同:权限被拒要去系统设置、码过期要去电脑刷新、非本产品的码要换个 App 扫、无权限要联系管理员。给统一提示等于把「判断该怎么办」这件事推给了用户,而他判断不了。这一条的成本主要是要枚举失败情况,实现本身不复杂。

    踩过的坑一:第一版没有确认环节,被安全评审打回。评审同学演示了攻击路径:他在自己的电脑上打开登录页,把二维码截图发给我,说「帮我看下这个码能不能扫」——我一扫,他的浏览器就登录了我的账号而我全程不知道自己做了什么。教训是:我把「扫码」当成了「用户同意」,但这两者不是一回事——扫码只是一个物理动作,而同意需要用户理解自己在同意什么。这个认知偏差在很多授权类功能里都会出现(点了链接不等于同意授权)。

    踩过的坑二:二维码过期只报「登录失败」,用户反复扫同一个码。客服收到的反馈是「你们的扫码登录一直失败」。而正确的做法只需要一句话:「请在电脑上刷新二维码」教训是:错误提示的价值取决于它是否指向了正确的下一步动作——「失败」只是描述了状态,没有给出动作。

    踩过的坑三:相机权限被拒之后是死胡同。用户点「扫一扫」没反应,他不知道要去系统设置开权限,也不知道有没有别的登录方式教训是:任何依赖系统权限的入口,都要处理「权限已被永久拒绝」这个状态——它和「首次未授权」不同,重新请求不会再弹窗,只能引导用户去系统设置。而且必须同时给出不依赖该权限的替代路径。

    没做的部分:没做「附近设备免扫码确认」这类更便捷的方案。它体验更好,但依赖蓝牙或声波等近场能力,兼容性与可靠性都不确定,而且「更便捷」的方向和这个模块的安全目标是有张力的我的判断是在扫码登录这件事上,不该为了省一步而削弱确认环节——这也是我提这个建议时的保留意见。

    数字是怎么测的

    安全性这一项不适合报数字,该报「通过了安全评审对授权确认的要求」以及具体做了什么。确认环节存在、确认页展示登录目标信息、令牌一次性且短有效期、二维码不含凭据——这四条是可核对的事实,比任何数字都有说服力。

    状态机的正确性用断言型用例:五种状态各构造一次,断言手机端渲染对应界面且指引文案正确过期状态断言提示的是「请在电脑上刷新二维码」而不是笼统失败。

    令牌一次性:确认成功后再次提交同一令牌,断言被拒绝且不产生二次登录;并发提交两次确认,断言只成功一次。后者要真并发。

    停留过久的重新校验:扫码后等待超过有效期再点确认,断言提示已过期而不是报未知错误。

    失败路径要逐条验证并列成清单:相机权限被拒、非本产品二维码、网络失败、账号无权限——断言每一条都给出了可操作的指引报法是「四条失败路径逐一验证并列出各自的指引文案」,列清单本身说明你想全了。

    权限被永久拒绝这一条要单独测:它和「首次未授权」行为不同(重新请求不会弹窗),断言此时引导到系统设置并给出替代登录方式。

    反馈及时性:手机端确认或取消后,断言电脑端在可接受时间内感知到状态变化(取决于轮询间隔,要报出这个间隔)。

    求助工单:可报「扫码登录相关求助工单下降」,但要说明分类方式,并注意这类问题的绝对量本身不大。

    不要报什么:不要报「扫码成功率」——它受相机、光线、屏幕反光影响,而且过期和取消都算「不成功」但那是正常行为。该报的是「四条安全要求的具体落实」「五种状态渲染与指引正确」「令牌一次性与并发只成功一次」「四条失败路径各有可操作指引」这几件可核对的事。

    面试追问
    Q:扫码登录扫完直接登进去不是最流畅吗?为什么还要在手机上点一次确认? A:我第一版就是扫完直接登录,被安全评审打回了——而且评审同学是用一次现场演示说服我的。他在自己的电脑上打开登录页,把二维码截图发给我,说「帮我看下这个码能不能扫」——我一扫,他的浏览器就登录了我的账号,而我全程不知道自己做了什么问题的根源是我把「扫码」当成了「用户同意」,但这两者不是一回事:扫码只是一个物理动作,而同意需要用户理解自己在同意什么。他可能是被骗着扫的。所以确认环节是这个方案安全性的核心,不是多余的一步。但我想强调一个配套的点:如果确认页什么信息都不给,那它确实只是多点一下,产品质疑「为什么要多一步」就是对的所以确认页必须展示「本次登录的对象」——目标端是什么、大致位置、时间,只有这些信息才能让用户判断「这是我自己发起的吗」。异常时还要加醒目提示,而且取消按钮要和确认一样显眼,不能做成小字——这类页面的默认倾向应该是谨慎,而不是引导他快速点确认。我的判据是:省掉这一步之后,用户还有没有机会发现自己被骗?没有,那这一步就不能省。另外还有几条相关的硬要求:二维码里绝不能放登录凭据(截图转发就等于账号泄露),凭据必须由服务端签发给电脑端;扫码令牌要一次性且短有效期,确认后立即失效。
    Q:相机权限被拒这种情况,弹个提示不就行了? A:弹一个「无法使用相机」会让用户进入死胡同——这是我踩过的坑。用户点「扫一扫」没反应或者只看到一个提示,他不知道要去系统设置里开权限,也不知道有没有别的登录方式,就卡在这里了。而这里有一个技术上容易被忽略的点:「权限首次未授权」和「权限已被永久拒绝」是两个不同的状态——首次未授权时重新请求会弹系统弹窗,但如果用户之前选了「拒绝」(尤其是勾了不再询问),重新请求不会再弹任何东西,代码看起来调用了但什么都没发生。所以必须区分处理:永久拒绝的情况只能引导用户去系统设置手动开启,并且要告诉他大致路径同时必须给出不依赖相机的替代登录方式(账号密码登录),否则他今天就是进不来。更一般地说,我在这个页面上把每一条失败路径都单独给了「可操作的出路」:权限被拒去系统设置、二维码过期去电脑上刷新、扫到非本产品的码说明这不是我们的码(否则他以为是我们功能坏了)、网络失败保留在确认页提供重试(不要退回去让他重新扫,因为码可能已经过期)、账号无目标端权限就说明「您的账号没有管理后台访问权限」。为什么不用统一的错误提示?因为每种失败的正确应对完全不同,给统一提示等于把「判断该怎么办」推给了用户,而他判断不了。我总结的是:错误提示的价值取决于它是否指向了正确的下一步动作——「失败」只描述了状态,没有给出动作。

模块二:组织通讯录选人

  1. 组织通讯录选人(勾选部门的语义显式化 + 按层级懒加载与搜索结果带完整路径 + 已选浮层与树的双向联动 + 可见范围限制与失效成员处理)★★★
    简历这样写 组织通讯录选人组件(uni-app + 按层级懒加载 + 双向联动已选态 + 权限过滤):勾选部门原按展开时的成员快照处理,与业务预期不符(审批范围应随部门成员变动),改为在交互上显式区分「选择该部门」与「选择其中若干人」两种语义并分别落库,消除了后续新入职成员不在范围内的问题;组织数千人时原为一次性拉取全量树导致首次打开明显卡顿,改为按层级懒加载并对已展开节点做缓存;跨层级搜索的结果带完整部门路径(同名成员在不同部门,仅显示姓名无法区分);已选内容用浮层集中展示并与树双向联动(在浮层移除会同步取消树上的勾选),解决跨多层选择后无法回看已选的问题;按可见范围过滤可选人员,并对已停用或离职成员在历史选择中标注失效而非直接消失。上线后选人相关的操作反馈与错选情况明显改善。
    展开完整拆解
    为什么要这么设计

    选人组件是审批、任务分派、通知范围这些功能的公共依赖。第一版我按最直觉的方式做:拉一棵组织树、渲染成可勾选的列表、勾完返回选中的人员标识数组。四个问题,其中第一个是业务语义错了。

    一是勾选部门的语义我理解错了。用户勾选了「技术部」,我的实现是展开这个部门、把当时的成员全部加入选中列表但业务上他的意思是「技术部的人都要审批」——包括以后新入职的结果是一个月后新入职的成员不在审批范围内,流程漏了他这不是 bug,是我把两种不同的语义合成了一种。

    二是全量拉树在大企业里卡死。我一次性拉全部组织和成员。小客户几十人没问题,一个几千人的客户打开选人页要转好几秒,低端机上直接卡住。

    三是跨层级选完之后看不到自己选了谁。用户在好几个部门下各勾了几个人,树折叠起来之后完全看不到已选的是谁他要一层层展开去找,或者干脆全部取消重新选。

    四是搜索结果只显示姓名,同名的分不清。几千人的组织里同名很常见。搜「张伟」出来三个,我只显示姓名和头像——他不知道该选哪个,只能猜。

    所以四个改动:显式区分「选择该部门」与「选择其中若干人」两种语义并分别落库按层级懒加载并缓存已展开节点已选内容用浮层集中展示并与树双向联动搜索结果带完整部门路径,另外补了可见范围过滤失效成员的标注

    这个模块最想说的一句话是:选人组件最容易出错的地方不是交互,是「勾选一个部门到底是什么意思」这个语义问题——而它决定了几个月后业务会不会出漏洞。我把「选部门」实现成了「选当时那些人」,功能测试完全正常(选完确实是那些人),问题在一个月后新入职成员没被包含时才暴露这类语义错误比代码 bug 危险,因为它不会报错,只会静默地给出不符合预期的结果。

    整体链路
    两种选择语义(这是最容易做错的地方) │ ├─ 语义一「选择该部门」 │ 落库存的是部门标识,使用时动态展开为当前成员 │ 适用:审批范围 · 通知范围 —— 新入职成员应自动包含 │ ├─ 语义二「选择其中若干人」 │ 落库存的是具体成员标识列表 │ 适用:指定审批人 · 指派负责人 —— 就是这几个人 │ ├─ 交互上必须显式区分 │ 勾选部门本身 = 语义一(并在已选区显示为「技术部(全部)」) │ 展开部门后勾选个人 = 语义二 │ 合成一种的后果 │ 我把「选部门」实现成「选当时那些人」 │ 一个月后新入职成员不在审批范围内,流程漏了他 │ 而功能测试完全正常 —— 选完确实是那些人 │ └─ 调用方要能声明只接受哪种语义 「指定审批人」这类场景不该允许选部门 数据加载:按层级懒加载 │ ├─ 首次只拉顶层部门(不拉成员) ├─ 展开某部门时才拉它的子部门与直属成员 ├─ 已展开的节点缓存,二次展开不重复请求 ├─ 全量拉取的后果:几千人的客户打开要转好几秒,低端机卡住 └─ 节点上显示成员数(含子部门合计),让用户知道展开会有多少人 搜索(跨层级) │ ├─ 服务端搜索(本地只有已加载的部分,搜不全) │ ├─ 结果必须带完整部门路径 │ 几千人的组织里同名很常见 │ 只显示姓名的后果:搜出三个「张伟」,他不知道选哪个 │ ├─ 结果可直接勾选,并同步到树上的已选状态 │ └─ 搜索请求要做防抖与响应竞态处理(丢弃过期响应) 已选态:浮层集中展示 + 双向联动 │ ├─ 底部浮层显示已选数量,点击展开完整已选列表 │ 跨多层选择后树一折叠就看不到已选的是谁 │ 他要一层层展开去找,或者全部取消重新选 │ ├─ 浮层里可单个移除,移除后树上的勾选同步取消(双向联动) │ 单向的后果:浮层移除了但树上还打着勾,状态自相矛盾 │ ├─ 部门型选择在浮层里显示为「某部门(全部)」并可整体移除 │ └─ 多选上限要实时显示「已选 N/上限」,不要选超了才报错 可见范围与失效成员 │ ├─ 按当前用户的可见范围过滤(不是所有人都能看到全公司通讯录) │ 过滤要在服务端做 —— 前端过滤等于数据已经发出去了 │ ├─ 已停用或离职成员:不出现在新的可选列表中 │ ├─ 但历史选择里的失效成员要标注失效而不是直接消失 │ 直接消失的后果:审批人少了一个,而没人知道发生了什么 │ └─ 编辑历史选择时提示「其中 N 人已失效,请重新指定」 其他 ├─ 常用与最近选择置顶(实际工作里高频选的就那几个人) ├─ 支持按姓名首字母快速定位(长列表) └─ 空态区分「该部门无成员」与「加载失败」
    分步拆解
    1. 必须显式区分「选择该部门」和「选择其中若干人」两种语义。前者存部门标识、使用时动态展开;后者存具体成员列表。合成一种的后果是新入职成员不在审批范围内,而功能测试完全正常。
    2. 交互上要让两种语义可分辨:勾选部门本身是语义一,展开后勾选个人是语义二。已选区要显示成「技术部(全部)」,让用户看得出自己选的是动态范围。
    3. 调用方要能声明只接受哪种语义。「指定审批人」这类场景不该允许选部门——把语义约束交给调用方,组件才能通用。
    4. 首次只拉顶层部门,不拉成员。全量拉取的后果是几千人的客户打开要转好几秒,低端机直接卡住。
    5. 展开时才拉子部门与直属成员,已展开的节点要缓存。二次展开不重复请求。
    6. 节点上要显示成员数(含子部门合计)。让用户知道展开会有多少人,也帮他判断勾选这个部门的影响范围。
    7. 搜索必须走服务端。本地只有已加载的部分,本地搜索会搜不全而用户以为「查无此人」。
    8. 搜索结果必须带完整部门路径。几千人的组织里同名很常见,只显示姓名的话搜出三个同名的他不知道选哪个。
    9. 搜索要做防抖与响应竞态处理。快速输入时先发的请求可能后返回,丢弃过期响应,只认最新请求的结果。
    10. 已选内容要用浮层集中展示。跨多层选择后树一折叠就看不到已选的是谁,他只能一层层展开去找或者全部取消重选。
    11. 浮层里移除要同步取消树上的勾选(双向联动)。单向的后果是浮层移除了但树上还打着勾,状态自相矛盾。
    12. 部门型选择在浮层里要显示为「某部门(全部)」并可整体移除。不能拆成一堆个人展示——那会让用户误以为自己选的是固定的人。
    13. 多选上限要实时显示「已选 N/上限」。不要选超了才报错,那时他已经在「选谁」上花了时间。
    14. 可见范围过滤必须在服务端做。前端过滤等于数据已经发到客户端了,这是越权问题不是显示问题。
    15. 已停用或离职成员不出现在新的可选列表中。避免选到无效的人。
    16. 但历史选择里的失效成员要标注失效,不能直接消失。直接消失的后果是审批人少了一个而没人知道发生了什么——流程会静默地卡住或者跳过。
    17. 编辑历史选择时要提示「其中 N 人已失效,请重新指定」。把问题显式暴露给用户处理。
    18. 常用与最近选择要置顶。实际工作里高频选的就那几个人,这个成本很低但覆盖了大部分操作。
    19. 空态要区分「该部门无成员」与「加载失败」。加载失败显示成「无成员」会让用户以为部门是空的。
    关键决策与取舍

    两种语义都保留并显式区分,而不是只选一种。只做「选具体人」实现最简单,但审批范围、通知范围这类场景确实需要「随部门成员变动」的动态语义;只做「选部门」则无法指定具体的人。代价是落库结构要支持两种(部门标识 + 成员标识列表)、使用时要能展开、界面上要能区分判据是「业务上这两种需求是否都真实存在」:都存在,那就不能用一种去覆盖另一种——而我最初的错误恰恰是用一种覆盖了另一种,还是用错的那一种。

    按层级懒加载而不是全量拉取加前端虚拟列表。全量拉取加虚拟渲染也能解决卡顿(渲染层面),但拉取本身的耗时和流量省不掉——几千人的组织数据在移动网络下就是明显的等待。懒加载的代价是展开每一层都有一次请求(要处理加载态),而且本地搜索不可用(只能走服务端搜索)判据是「瓶颈在渲染还是在传输」:在传输,那就必须减少一次要传的数据量。

    可见范围过滤放在服务端,即使前端过滤更简单。前端过滤的问题不是性能,是数据已经发到客户端了——抓包就能看到全公司通讯录这是越权问题不是显示问题。判据很明确:只要「不该看到」是一个安全要求而不是体验优化,过滤就必须在服务端。

    失效成员在历史选择里标注而不是移除。移除更「干净」,但它会让审批人静默少一个,而没人知道发生了什么——流程可能卡住或者被跳过。标注失效的代价是界面上多了一种状态要处理判据是「静默变化的后果是否可察觉」:不可察觉,就必须显式暴露。这一条和「估清冲突要提示而不是静默覆盖」是同一个原则。

    踩过的坑一:把「选部门」实现成了「选当时那些人」。用户勾选「技术部」,我展开并把当时的成员加入选中列表。一个月后新入职的成员不在审批范围内,流程漏了他而功能测试完全正常——选完确实是那些人。教训是:这类语义错误比代码 bug 危险,因为它不会报错,只会静默地给出不符合预期的结果,而且要等时间过去才暴露。我后来的习惯是遇到「选择一个集合」的交互,先问清楚「这个集合是快照还是动态的」——这个问题在选人、选商品范围、选门店范围时都要问。

    踩过的坑二:全量拉组织树,大客户打开就卡。我用自己公司的测试数据(几十人)开发,完全没有暴露。是一个几千人的客户反馈「选人页打不开」。教训还是那条:性能问题必须用真实的极端数据验收——最大的组织、最深的层级、最差的机型。我现在会主动去问「我们最大的客户有多少人」再决定实现方式。

    踩过的坑三:跨层级选完看不到已选,用户全部取消重选。这个反馈是「你们这个选人很难用」,说不出具体哪里不好。我是自己按真实场景操作了一遍(在四个部门下各选两个人)才体会到问题。教训是:组件类功能要自己按真实使用规模走一遍,而不是只验证「能选中」——选一个人和跨四层选八个人的体验完全不同。

    没做的部分:没做「按角色或标签选人」(比如选中所有「部门负责人」)。客户提过,它比按部门选更灵活但需要先有稳定的角色与标签体系,而当时这块本身还在演进取舍依据是「依赖的基础能力还不稳定,先做会返工」;我在数据结构上把选择类型留成了可扩展的枚举,将来加一种选择类型不需要改表。

    数字是怎么测的

    语义正确性是这个模块最重要的验证,用断言型用例:以「选择该部门」方式选中一个部门后新增一名成员,断言该成员被包含在范围内;以「选择其中若干人」方式选中同样的人后新增成员,断言不被包含这两条一起才说明两种语义都对。

    加载性能要报清组织规模:「某个 N 人、M 层的组织,首屏加载耗时从 X 降到 Y」,并说明在什么机型上测的还要报「首屏请求的数据量对比」——这个数字解释了为什么快了。

    懒加载与缓存:断言展开节点触发一次请求、二次展开不再请求;断言展开中的加载态可见。

    搜索:断言走服务端、结果带完整部门路径、同名成员可区分;快速输入时断言过期响应被丢弃(只显示最新关键词的结果)。

    已选联动:跨多层选择后,断言浮层数量与实际选中一致;在浮层移除一项,断言树上对应勾选被取消。

    可见范围:用可见范围受限的账号请求,断言接口返回的数据本身已被过滤(而不是前端隐藏)。这条要看接口响应,不能只看界面。

    失效成员:把历史选择中的一名成员停用,断言编辑时该成员显示为失效并有提示,而不是消失。

    不要报什么:不要报「选人效率提升 N%」——没有可靠的度量方式。该报的是「两种语义在新增成员后的行为符合各自定义」「某规模组织的首屏耗时与数据量对比」「浮层与树的双向联动一致」「可见范围在服务端过滤」这几件可核对的事。

    面试追问
    Q:选人组件不就是渲染一棵树让用户勾选吗?你说的语义问题是什么意思? A:问题是「勾选一个部门」到底是什么意思,我第一版理解错了,而这个错误在一个月后才暴露。用户勾选了「技术部」,我的实现是展开这个部门、把当时的成员全部加入选中列表,返回一个成员标识数组但业务上他的意思是「技术部的人都要审批」——包括以后新入职的。结果是一个月后新入职的成员不在审批范围内,流程漏了他而功能测试完全正常,因为选完确实就是那些人。这类语义错误比代码 bug 危险,因为它不会报错,只会静默地给出不符合预期的结果,而且要等时间过去才暴露。修法是显式区分两种语义:「选择该部门」落库存部门标识、使用时动态展开为当前成员(适用于审批范围、通知范围);「选择其中若干人」落库存具体成员列表(适用于指定审批人、指派负责人)。交互上勾选部门本身是第一种,展开后勾选个人是第二种,已选区里第一种显示为「技术部(全部)」让用户看得出这是动态范围另外我让调用方能声明只接受哪种语义——「指定审批人」这类场景不该允许选部门,把语义约束交给调用方,组件才能通用。我后来形成的习惯是:遇到「选择一个集合」的交互,先问清楚「这个集合是快照还是动态的」——这个问题在选人、选商品范围、选门店范围时都要问,而它通常不会写在需求文档里,因为提需求的人认为答案是显然的。
    Q:组织树为什么不一次拉完然后前端做虚拟列表?那样搜索还能在本地做,更快。 A:因为瓶颈不在渲染,在传输。全量拉取加虚拟渲染确实能解决滚动卡顿,但几千人的组织数据本身在移动网络下就是明显的等待——我踩的坑正是这个:我用自己公司的测试数据(几十人)开发,完全没暴露问题,是一个几千人的客户反馈「选人页打不开」,首屏要转好几秒、低端机上直接卡住。所以判据是「瓶颈在渲染还是在传输」:在传输,那就必须减少一次要传的数据量,只能懒加载。代价我也说清楚:展开每一层都有一次请求(要处理加载态),而且本地搜索不可用——只能走服务端搜索。后者其实带来了一个必须处理的新问题:服务端搜索有防抖和响应竞态,快速输入时先发的请求可能后返回,要用请求序号丢弃过期响应,否则会显示上一个关键词的结果。而搜索结果还必须带完整部门路径——几千人的组织里同名很常见,搜「张伟」出来三个,只显示姓名他不知道该选哪个另外我在节点上显示了成员数(含子部门合计),这个小改动有两个作用:让用户知道展开会有多少人,也帮他判断「勾选这个部门」的影响范围有多大我从这件事记住的教训是:性能问题必须用真实的极端数据验收——最大的组织、最深的层级、最差的机型。现在我会主动去问「我们最大的客户有多少人」再决定实现方式。

模块三:附件预览

  1. 附件预览(按格式分策略与转换状态轮询 + 任何失败都降级到下载入口 + 大文件按页加载 + 预览鉴权与水印)★★★
    简历这样写 附件预览(uni-app + 分格式预览策略 + 转换状态轮询 + 时效访问凭据):预览原为统一交给一个组件处理,遇到需要转换的文档格式直接白屏,改为按格式分策略(图片直接渲染、可分页文档按页加载、办公文档走服务端转换并轮询转换状态)并在转换期间展示进度而非空白;任何失败路径都收敛到「下载原文件」这一个明确出路(格式不支持、转换失败、超时),解决用户遇到预览失败后无法获取文件的问题;大文档改为按页请求并预取相邻页,首屏时间与文档总页数解耦;预览链接改为按需签发时效访问凭据并做鉴权(原为长期有效地址,转发即可绕过权限),涉及敏感文档时叠加可见水印(操作人与时间)以约束二次传播;转换结果按文件指纹缓存,同一文件二次预览不重复转换。上线后附件打不开的反馈明显下降。
    展开完整拆解
    为什么要这么设计

    附件预览的需求听起来很小:审批单、任务、通知里都能带附件,用户点一下要能看。第一版我找了一个通用的预览方式,把全部格式都交给它。四个问题。

    一是办公文档格式直接白屏。图片和部分格式能看,而客户最常上传的办公文档打开就是白屏——没有报错、没有提示,就是一片空白。用户以为文件坏了,去问上传的人;上传的人在电脑上能打开,双方都困惑。

    二是失败之后没有任何出路。白屏或者报错之后,页面上没有「下载」入口用户拿不到这个文件——而这个文件可能是他审批所必需的。他的做法是让对方通过别的方式(社交软件)再发一遍,那反而绕过了我们的权限控制。

    三是大文档首屏很慢。一份几十页的文档,我是整份加载完才渲染。用户等很久,而他往往只想看第一页。

    四是预览链接是长期有效的公开地址。企业文档里有合同、报表这类敏感内容。我拿到的是一个长期有效的地址,直接给了预览组件——这个地址被转发出去,任何人都能打开,完全绕过了权限控制。

    所以四个改动:按格式分策略并在转换期间展示进度任何失败都收敛到「下载原文件」这一个明确出路大文档按页请求并预取相邻页预览链接改为按需签发时效凭据并鉴权,另外补了敏感文档水印转换结果按文件指纹缓存

    这个模块最想说的一句话是:预览是一个「大概率成功但注定有失败」的功能——格式千奇百怪、转换服务会失败、文件可能损坏——所以它的设计重点不是提高成功率,是保证「失败时用户仍然能拿到文件」。我第一版把全部精力放在了「让更多格式能预览」上,而真正让用户卡住的是「预览失败之后没有出路」后者的实现成本远低于前者,价值却更高。

    整体链路
    按格式分策略(不要交给一个通用组件处理全部) │ ├─ 图片 直接渲染 + 手势缩放 + 多图左右切换 ├─ 可分页文档 按页请求渲染(见下方大文件处理) ├─ 办公文档 服务端转换为可预览格式 → 轮询转换状态 → 渲染 ├─ 音视频 交给平台播放能力 ├─ 压缩包等 不预览,直接给下载入口(并说明原因) │ └─ 未知格式 不要尝试预览,直接给下载入口 硬试的后果:白屏,而白屏比明确的「不支持」更让人困惑 转换流程(办公文档,首次预览会慢) │ ├─ 请求预览 → 服务端返回「转换中」+ 任务标识 │ ├─ 前端轮询转换状态,界面展示进度或明确的等待提示 │ 直接白屏等待的后果:用户以为文件坏了 │ ├─ 转换完成 → 拿到可预览资源 → 渲染 │ ├─ 转换失败 / 超时 → 降级到下载(见下) │ └─ 转换结果按文件指纹缓存 同一文件被多人预览只转换一次 指纹用内容摘要而不是文件名 —— 同名不同内容是常见情况 失败降级(这是这个模块最重要的一环) │ ├─ 全部失败路径都收敛到一个明确出路:下载原文件 │ 格式不支持 · 转换失败 · 转换超时 · 网络失败 · 文件损坏 │ ├─ 每种失败给不同的说明文案,但下载入口位置一致 │ 说明要具体(「该格式暂不支持预览」而不是「加载失败」) │ ├─ 没有下载入口的后果 │ 用户拿不到审批所必需的文件 │ 他会让对方通过社交软件再发一遍 → 反而绕过了我们的权限控制 │ └─ 下载也要走鉴权,并记录下载行为 大文件处理 │ ├─ 按页请求,先渲染第一页 │ 整份加载完才渲染的后果:几十页的文档要等很久 │ 而用户往往只想看第一页 │ ├─ 预取相邻页(前后各一到两页),滑动时无感 │ ├─ 已加载页做数量上限的缓存,超出释放(避免内存持续增长) │ └─ 显示总页数与当前页,支持跳页 鉴权与水印 │ ├─ 预览资源改为按需签发时效访问凭据,不使用长期有效地址 │ 长期地址的后果:转发出去任何人都能打开,绕过权限控制 │ ├─ 每次预览都做鉴权(当前用户对该附件所在业务对象是否有权限) │ 只校验「附件标识存在」是不够的 │ ├─ 敏感文档叠加可见水印(操作人 · 时间) │ 水印不能防截图,但能约束二次传播并在泄露后定位来源 │ └─ 预览与下载行为都记录(谁 · 何时 · 看了哪个附件) 体验细节 ├─ 首屏给骨架或进度,不要空白 ├─ 手势缩放与双击放大(移动端必备) ├─ 横竖屏切换后保持当前页与缩放比例 └─ 从预览返回时保持列表位置
    分步拆解
    1. 按格式分策略,不要把全部格式交给一个通用组件。我第一版这么做的结果是客户最常上传的办公文档直接白屏——没有报错、没有提示,就是一片空白。
    2. 未知格式不要硬试预览,直接给下载入口并说明原因。硬试的后果是白屏,而白屏比明确的「不支持预览」更让人困惑(用户以为文件坏了)。
    3. 办公文档走服务端转换,转换期间要展示进度或明确的等待提示。直接白屏等待让用户以为文件坏了,去问上传的人,而对方在电脑上能打开,双方都困惑。
    4. 转换状态要轮询,并设超时。无限等待和立刻失败都不对,超时后降级到下载。
    5. 转换结果要按文件指纹缓存。同一文件被多人预览只转换一次。指纹要用内容摘要而不是文件名——同名不同内容是很常见的情况。
    6. 全部失败路径都要收敛到「下载原文件」这一个明确出路。这是这个模块最重要的一环——没有下载入口的话,用户拿不到审批所必需的文件。
    7. 每种失败要给不同的具体说明,但下载入口的位置保持一致。「该格式暂不支持预览」比「加载失败」有用得多;位置一致是为了让用户形成预期。
    8. 要意识到「没有下载入口」会把用户推向更不安全的路径。他会让对方通过社交软件再发一遍,那反而绕过了我们的权限控制——所以提供下载其实是更安全的选择。
    9. 下载也要走鉴权并记录行为。不能因为是「兜底路径」就放松管控。
    10. 大文档要按页请求,先渲染第一页。整份加载完才渲染的后果是几十页的文档要等很久,而用户往往只想看第一页。
    11. 要预取相邻页(前后各一到两页)。让滑动翻页无感,否则每翻一页都要等。
    12. 已加载页要做数量上限的缓存并超出释放。不释放的话翻得越多内存占用越高,长文档会导致崩溃。
    13. 预览资源必须按需签发时效凭据,不能用长期有效地址。长期地址被转发出去任何人都能打开,完全绕过权限控制——这是我踩的最严重的一个坑。
    14. 每次预览都要做业务维度的鉴权,不能只校验附件标识存在。要判断当前用户对该附件所在的业务对象(审批单、任务)有没有访问权限。
    15. 敏感文档要叠加可见水印(操作人、时间)。水印不能防截图,但能约束二次传播并在泄露后定位来源——这一点要如实说明,不要夸大它的作用。
    16. 预览与下载行为都要记录(谁、何时、看了哪个附件)。这是事后追溯的依据。
    17. 首屏要给骨架或进度,不要空白。空白是最糟的加载态。
    18. 移动端要支持手势缩放与双击放大。文档在手机上字小,不能缩放等于看不了。
    19. 横竖屏切换后要保持当前页与缩放比例。切回去从第一页开始会让用户重新找位置。
    关键决策与取舍

    把「失败降级到下载」当成核心功能而不是兜底细节。我第一版把全部精力放在「让更多格式能预览」上,而真正让用户卡住的是「预览失败之后没有出路」后者的实现成本远低于前者,价值却更高。判据是「这个功能有没有注定无法消除的失败率」:预览天然有(格式千奇百怪、转换服务会失败、文件可能损坏),那失败路径的设计就和成功路径一样重要。

    提供下载入口,而不是「为了防泄露不给下载」。安全侧最初倾向于只允许预览不允许下载。但现实是预览失败时用户拿不到文件,他会让对方通过社交软件再发一遍——那份文件就完全脱离了我们的管控(没有鉴权、没有水印、没有记录)所以提供受控的下载(鉴权 + 记录 + 水印)实际上比不提供下载更安全这个论证我认为是这个模块里最重要的一次沟通——它把「安全」和「可用」从对立关系变成了一致的。

    转换结果按内容指纹缓存而不是按文件标识。按文件标识缓存更简单,但同一份文件被多个人分别上传时会重复转换(浪费转换资源)而按文件名缓存则会出错——同名不同内容是很常见的情况。用内容摘要做指纹兼顾了两者。

    水印做「可见水印」而不是隐式水印。隐式水印不影响阅读体验、能在泄露后定位来源,但它对使用者没有约束作用(他不知道文件被标记了)可见水印的价值一半在事后追溯、一半在事前约束——他看到自己的名字在上面,转发的意愿会下降。我在说明这个功能时会明确「水印不能防截图」,不夸大它的作用。

    踩过的坑一:办公文档直接白屏,没有任何提示。用户以为文件坏了去问上传的人,而对方在电脑上能打开,双方都困惑,最后来问客服教训是:白屏是最糟的失败方式——它没有传达任何信息,用户只能猜而「明确告诉他不支持」的实现成本几乎为零,我当时只是没想到要区分格式。

    踩过的坑二:预览用的是长期有效的公开地址。是安全评审发现的——这个地址转发出去任何人都能打开,完全绕过了权限控制而企业文档里有合同、报表这类内容。教训和资质材料那次一样:「难以猜到的长地址」不构成访问控制,含敏感内容的资源必须私有存储加时效凭据。我在同一类问题上踩过两次,说明这不是知识问题而是习惯问题——所以我后来把「这个资源的地址泄露了会怎样」加进了自检清单。

    踩过的坑三:只校验了附件标识存在,没校验业务权限。知道附件标识就能预览,而附件标识在某些接口里是会返回的教训是:鉴权要针对「业务对象」而不是「资源标识」——正确的问题是「这个用户能不能看这张审批单」,而不是「这个附件存在吗」。

    没做的部分:没做在线编辑与批注。客户提过(希望能在附件上直接批注意见)但它需要接入完整的在线文档能力、处理并发编辑与版本,是一个独立方向我做的是让批注意见走审批流程的评论区并支持引用页码——用现有能力覆盖了核心诉求。

    数字是怎么测的

    核心指标是「附件打不开的反馈数」,并按原因分类。格式不支持、转换失败、权限问题各占多少。分类之后才知道该继续补格式支持还是该优化转换服务。

    失败降级的覆盖要列成清单逐条验证:格式不支持、转换失败、转换超时、网络失败、文件损坏,断言每一条都展示了具体说明且下载入口可用、位置一致报法是「五条失败路径逐一验证」并列出清单。

    首屏时间要报清文档规模:「N 页文档的首屏渲染时间从 X 降到 Y」,并说明改造后首屏时间与总页数基本无关——这个「无关」才是按页加载的核心收益。

    转换缓存效果报「同一文件二次预览的耗时对比」以及「转换任务的去重比例」。后者说明缓存真的在生效。

    指纹正确性要专门测:两个同名但内容不同的文件,断言各自转换、不会串到同一份缓存。这条容易被忽略但一旦错就是很严重的数据串号。

    鉴权:用无权访问该审批单的账号请求预览,断言被拒绝(即使他知道附件标识);断言时效凭据过期后失效。前一条是踩坑的直接回归。

    内存:连续翻阅长文档若干页,断言内存占用不持续增长(缓存有上限并释放)。

    水印:断言敏感文档的预览含操作人与时间水印;报告时要注明水印不能防截图,不要把它说成防泄露手段。

    不要报什么:不要报「预览成功率」并把它当成质量指标——格式不支持是设计上的正常结果而不是失败。该报的是「按原因分类的打不开反馈数下降」「五条失败路径均有下载出路」「首屏时间与总页数解耦」「无权账号即使知道附件标识也被拒绝」这几件可核对的事。

    面试追问
    Q:文件预览就是把文件渲染出来,你觉得难点在哪? A:难点不在「让更多格式能预览」,在「预览失败之后用户还能不能拿到文件」——而我第一版把全部精力放错了地方。我找了一个通用的预览方式,把全部格式都交给它,结果客户最常上传的办公文档直接白屏——没有报错、没有提示,就是一片空白。用户以为文件坏了,去问上传的人,对方在电脑上能打开,双方都困惑,最后来问客服更关键的是页面上没有「下载」入口:他拿不到这个文件,而这个文件可能是他审批所必需的。他的做法是让对方通过社交软件再发一遍——那份文件就完全脱离了我们的管控(没有鉴权、没有水印、没有记录)。所以我的结论是:预览是一个「大概率成功但注定有失败」的功能(格式千奇百怪、转换服务会失败、文件可能损坏),它的设计重点不是提高成功率,而是保证失败时用户仍然能拿到文件。具体做法是把全部失败路径(格式不支持、转换失败、超时、网络失败、文件损坏)都收敛到「下载原文件」这一个明确出路,每种失败给不同的具体说明但下载入口位置一致。而这个论证还帮我说服了安全侧——他们最初倾向于只允许预览不允许下载,而我指出「不给下载会把用户推向更不安全的路径」,所以提供受控的下载(鉴权 + 记录 + 水印)实际上比不提供更安全这次沟通把「安全」和「可用」从对立变成了一致,我觉得比任何技术细节都重要。
    Q:预览链接直接用文件存储给的地址有什么问题? A:问题是那是一个长期有效的地址,转发出去任何人都能打开,完全绕过了权限控制——而企业文档里有合同、报表这类内容。这是安全评审发现的。而我尤其想说的是:这是我在同一类问题上踩的第二次——之前做商家资质材料时也犯过一次(把营业执照存在公开可读的地址上,当时的想法是「链接很长猜不到」)。「难以猜到的长地址」不构成访问控制,因为地址会出现在日志、浏览器历史、转发的截图里。踩两次说明这不是知识问题而是习惯问题,所以我后来把「这个资源的地址泄露了会怎样」加进了自己的自检清单。修法是按需签发时效访问凭据,凭据短有效期、用完失效。另外还有一个更隐蔽的问题:我最初只校验了「附件标识存在」,没校验业务权限——也就是说知道附件标识就能预览,而附件标识在某些接口的返回里是会带出来的教训是鉴权要针对「业务对象」而不是「资源标识」:正确的问题是「这个用户能不能看这张审批单」,而不是「这个附件存在吗」。配套还做了两件事:敏感文档叠加可见水印(操作人和时间)——我会明确说明水印不能防截图,它的作用一半在事后追溯、一半在事前约束(他看到自己名字在上面,转发意愿会下降);以及预览和下载行为都记录(谁、何时、看了哪个附件)。

没有匹配的内容,换个关键词试试。

项目拆解 · 企业移动端常用页(uni-app · 实习级)· 共 3 个模块 · 数字均为示例,需替换成自己项目的真实数据