uni-file-picker的mode="grid"仅提供基础九宫格渲染和选图功能,不支持预览精准定位、拖拽排序、长按删除、上传前尺寸压缩等完整交互,需手动接管图片数组并补全生命周期钩子。

直接用 uni-file-picker 的 mode="grid" 是最省心的方案,但真要复刻美团那种「点击预览、长按删除、拖拽排序、上传前压缩」的完整交互链,必须手动接管图片数组、禁用默认行为、补全生命周期钩子——uni-file-picker 的九宫格只是壳,内核还得自己搭。
uni-file-picker mode="grid" 为什么不能直接用
它只负责选图和基础渲染,不提供:previewImage 的精准索引定位、拖拽时的 hover 感知、长按删除的 contextmenu 支持(小程序里需 bindlongtap)、上传前对每张图做尺寸裁剪或 WebP 压缩。更关键的是,它的 @select 回调返回的是临时路径数组,但 uni.previewImage 的 current 参数必须是字符串且严格匹配 urls 中某一项——而 uni-file-picker 不帮你做这个校验和归一化。
- 选图后数组里混入
null或空字符串,uni.previewImage会静默失败,控制台无报错 - 未过滤重复路径,同一张图点两次,数组里出现两个相同
tempFilePath,预览时滑动错位 - 没做
onload+@error双兜底,低网速下图片白屏,用户以为没选上
怎么让九宫格支持点击放大 + 准确定位 current
核心是:别传索引,传真实 src 字符串;预览前先过滤、去重、校验有效性。
- 点击某张图时,取
item.src(即该图在数组中的完整路径),不是index -
urls数组必须是字符串数组,用filter(u => typeof u === 'string' && u.trim())清洗 - 确保
current和urls中某项完全相等(注意 iOS 小程序对大小写和斜杠敏感) - H5 端只认网络图,本地
tempFilePath需提前转成 base64 或走上传后返回的 CDN 地址
拖拽排序必须手写,movable-area 行不通
movable-area 在九宫格场景下本质失效:它不感知列表长度变化,不支持 v-for 动态子项,change 回调只给像素值,无法映射回数组索引。真机上拖着图松手后顺序不变,或者整个页面失焦。
- 用
touchstart/touchmove监听,取event.touches[0].clientX/clientY,永远别碰pageX/pageY - 每次
touchmove都要重新查所有图片容器的boundingClientRect(),缓存会导致位置错乱 - 判断 hover 目标时,比对触摸 Y 坐标与每个容器中线(
rect.top + rect.height / 2) - 交换数组必须用两段
splice:this.list.splice(newIndex, 0, this.list.splice(oldIndex, 1)[0]),并记录lastSwappedIndex防抖
长按删除和上传前压缩怎么落地
微信小程序的 bindlongtap 和 App/H5 的 @contextmenu 行为不一致,得统一用 setTimeout 模拟长按,再配合 clearTimeout。压缩则必须在上传前逐张处理,不能依赖 sizeType="compressed" —— 它只压缩体积,不控尺寸,美团晒图要求缩略图 ≤300×300 且格式优先 WebP。
- 长按逻辑:touchstart 记录时间戳,touchend 判断间隔 ≥600ms,触发删除确认
- 压缩用
uni.compressImage,设quality: 60、width: 300、height: 300,完成后替换原tempFilePath - 服务端接收时,必须校验图片宽高比和 MIME 类型,前端压缩只是第一道防线
- 删除后立即
this.$forceUpdate()或重置key,否则九宫格 DOM 节点残留导致后续拖拽错位
真正卡住上线的,从来不是“怎么排三列”,而是 tempFilePath 生命周期管理、iOS 小程序对路径大小写的敏感、以及拖拽时 boundingClientRect 未实时刷新这三件事。它们不出现在文档里,但真机上一试就崩。



















