Files
Neo-ZQYY/apps/miniprogram/doc/ABANDON_MODAL_COMPONENT.md
2026-03-15 10:15:02 +08:00

7.4 KiB
Raw Permalink Blame History

放弃弹窗组件化改进说明

更新日期2026-03-14 改进内容:创建可复用的放弃弹窗组件,修复首次输入不触发交互的问题


问题分析

原问题

  1. 首次输入不触发交互:任务列表页的放弃弹窗,首次点击 textarea 时不会触发键盘弹出事件
  2. 代码重复:任务列表页和任务详情页都有独立的放弃弹窗实现,代码重复

问题原因

原放弃弹窗的 overlay 层使用了 bindtap="onCloseAbandonModal",导致点击事件冒泡问题:

<view class="abandon-overlay" bindtap="onCloseAbandonModal">
  <view class="abandon-modal" catchtap="noop">
    <textarea ... />
  </view>
</view>

当首次点击 textarea 时,事件可能被 overlay 的 bindtap 干扰,导致 textarea 的 focus 事件不能正常触发。


解决方案

1. 创建可复用的放弃弹窗组件

组件路径components/abandon-modal/

组件特点

  • 完整的键盘交互支持
  • 自动验证输入内容
  • 统一的样式和交互
  • 可在多个页面复用

组件文件

  • abandon-modal.wxml - 模板
  • abandon-modal.ts - 逻辑
  • abandon-modal.wxss - 样式
  • abandon-modal.json - 配置

2. 修复事件冒泡问题

改进前

<view class="abandon-overlay" bindtap="onCloseAbandonModal">

改进后

<view class="modal-overlay" catchtap="onCancel" catchtouchmove="noop">

关键改进

  • 使用 catchtap 替代 bindtap,阻止事件冒泡
  • 添加 catchtouchmove="noop" 防止滚动穿透
  • 内部容器使用 catchtap="noop" 阻止点击关闭

组件使用方法

在页面中注册组件

JSON 配置

{
  "usingComponents": {
    "abandon-modal": "/components/abandon-modal/abandon-modal"
  }
}

在页面中使用组件

WXML

<abandon-modal
  visible="{{abandonModalVisible}}"
  customerName="{{customerName}}"
  bind:confirm="onAbandonConfirm"
  bind:cancel="onAbandonCancel"
/>

事件处理

TypeScript

// 打开弹窗
onOpenAbandon() {
  this.setData({ abandonModalVisible: true })
}

// 确认放弃
onAbandonConfirm(e: WechatMiniprogram.CustomEvent<{ reason: string }>) {
  const { reason } = e.detail
  // 处理放弃逻辑
  this.setData({ abandonModalVisible: false })
}

// 取消
onAbandonCancel() {
  this.setData({ abandonModalVisible: false })
}

组件属性

属性 类型 必填 说明
visible Boolean 是否显示弹窗
customerName String 客户名称

组件事件

事件名 参数 说明
confirm { reason: string } 确认放弃,返回放弃原因
cancel - 取消操作

页面改进

任务列表页

改进内容

  1. 移除内联放弃弹窗代码
  2. 使用 abandon-modal 组件
  3. 简化事件处理逻辑
  4. 移除不需要的 data 字段abandonReason, abandonError, keyboardHeight

改进文件

  • pages/task-list/task-list.wxml
  • pages/task-list/task-list.ts
  • pages/task-list/task-list.json

任务详情页

改进内容

  1. 移除内联放弃弹窗代码
  2. 使用 abandon-modal 组件
  3. 简化事件处理逻辑
  4. 移除不需要的 data 字段和方法

改进文件

  • pages/task-detail/task-detail.wxml
  • pages/task-detail/task-detail.ts
  • pages/task-detail/task-detail.json

技术细节

1. 事件冒泡控制

关键点

  • overlay 使用 catchtap 阻止事件冒泡
  • 内部容器使用 catchtap="noop" 防止关闭
  • textarea 的 focus/blur 事件正常触发

2. 键盘交互

实现方式

// 键盘弹出
onTextareaFocus(e: WechatMiniprogram.InputEvent) {
  const height = (e as any).detail?.height ?? 0
  this.setData({ keyboardHeight: height })
}

// 键盘收起
onTextareaBlur() {
  this.setData({ keyboardHeight: 0 })
}

样式适配

/* 键盘弹出时,弹窗移到顶部 */
.modal-overlay--keyboard-open {
  align-items: flex-start;
}

/* 按钮固定在键盘上方 */
.modal-footer--float {
  position: fixed;
  bottom: [keyboardHeight]px;
}

3. 输入验证

自动验证

observers: {
  content(val: string) {
    this.setData({
      canSave: val.trim().length > 0,
    })
  },
}

提交验证

onConfirm() {
  if (!this.data.canSave) {
    this.setData({ error: true })
    return
  }
  // 触发确认事件
}

代码对比

改进前(任务列表页)

WXML约40行

<view class="abandon-overlay" wx:if="{{abandonModalVisible}}" bindtap="onCloseAbandonModal">
  <view class="abandon-modal" catchtap="noop">
    <!-- 大量内联代码 -->
  </view>
</view>

TypeScript约50行

data: {
  abandonReason: '',
  abandonError: false,
  keyboardHeight: 0,
},

onAbandonInput() { ... }
onAbandonTextareaFocus() { ... }
onAbandonTextareaBlur() { ... }
onAbandonConfirm() { ... }
onCloseAbandonModal() { ... }

改进后(任务列表页)

WXML5行

<abandon-modal
  visible="{{abandonModalVisible}}"
  customerName="{{abandonTarget.customerName}}"
  bind:confirm="onAbandonConfirm"
  bind:cancel="onAbandonCancel"
/>

TypeScript约15行

onAbandonConfirm(e: WechatMiniprogram.CustomEvent<{ reason: string }>) {
  const { reason } = e.detail
  // 处理逻辑
}

onAbandonCancel() {
  this.setData({ abandonModalVisible: false })
}

代码减少约70行 → 约20行减少70%


测试验证

功能测试

  • 首次点击 textarea 正常触发键盘弹出
  • 键盘弹出时弹窗自动上移
  • 按钮固定在键盘上方
  • 输入框获得焦点时边框变蓝
  • 空内容时显示错误提示
  • 确认后正确返回放弃原因
  • 取消后正确关闭弹窗

兼容性测试

  • 任务列表页使用正常
  • 任务详情页使用正常
  • 两个页面交互一致

性能测试

  • 组件加载速度正常
  • 键盘弹出流畅
  • 无内存泄漏

优势总结

1. 代码复用

  • 一次编写,多处使用
  • 减少代码重复
  • 降低维护成本

2. 问题修复

  • 修复首次输入不触发交互的问题
  • 统一事件处理逻辑
  • 改善用户体验

3. 易于维护

  • 组件化设计
  • 清晰的接口定义
  • 完整的文档说明

4. 扩展性强

  • 可轻松添加新功能
  • 可在其他页面复用
  • 可根据需求定制

后续优化建议

  1. 添加动画效果:弹窗打开/关闭时的过渡动画
  2. 支持自定义标题:允许传入自定义标题文本
  3. 支持自定义按钮文本:允许自定义确认/取消按钮文本
  4. 添加最大长度提示:显示剩余可输入字符数
  5. 支持多行输入优化:自动调整 textarea 高度

相关文件清单

新增文件

  • components/abandon-modal/abandon-modal.wxml
  • components/abandon-modal/abandon-modal.ts
  • components/abandon-modal/abandon-modal.wxss
  • components/abandon-modal/abandon-modal.json

修改文件

  • pages/task-list/task-list.wxml
  • pages/task-list/task-list.ts
  • pages/task-list/task-list.json
  • pages/task-detail/task-detail.wxml
  • pages/task-detail/task-detail.ts
  • pages/task-detail/task-detail.json

文档维护者AI Assistant
最后更新2026-03-14