From bf6396089d525dc9919d7f78fd95aa0a6596cf08 Mon Sep 17 00:00:00 2001 From: zhaichao Date: Thu, 24 Sep 2026 10:04:34 +0800 Subject: [PATCH] =?UTF-8?q?=E8=81=8A=E5=A4=A9=E7=95=8C=E9=9D=A2=E4=BF=A1?= =?UTF-8?q?=E6=81=AF=E9=95=BF=E6=8C=89=E6=93=8D=E4=BD=9C=E9=A3=8E=E6=A0=BC?= =?UTF-8?q?=E5=8F=98=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pages/chat/chat.vue | 137 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 106 insertions(+), 31 deletions(-) diff --git a/pages/chat/chat.vue b/pages/chat/chat.vue index 1ec87bd..2136bd4 100644 --- a/pages/chat/chat.vue +++ b/pages/chat/chat.vue @@ -31,7 +31,7 @@ class="row" :class="{ mine: m.mine, pick: selecting }" @click="selecting ? togglePick(m) : tapMsg(m)" - @longpress="act(m)" + @longpress="act(m, i)" > @@ -41,7 +41,7 @@ {{ sender(m) }} - + {{ quoteLine(m.quote) }} @@ -81,20 +81,13 @@ - - - 消息操作 - {{ actPreview }} - - 播放 - 复制 - 转发 - 引用 - 多选 - 撤回 - 删除 + + + + + {{ it.label }} - 取消 + @@ -202,6 +195,12 @@ export default { recWillCancel: false, recSec: 0, actionMsg: null, + popTop: 0, + popLeft: 8, + popW: 224, + caretLeft: 24, + popBelow: false, + popAt: 0, quoting: null, selecting: false, picked: [], @@ -232,8 +231,25 @@ export default { const m = this.actionMsg return !!(m && m.mine && !m.recalled && Date.now() - Number(m.time) < 120000) }, - actPreview() { - return previewOf(this.actionMsg) + actItems() { + const m = this.actionMsg + if (!m) return [] + const items = [] + if (m.kind === 'voice') items.push({ k: 'play', icon: 'play', label: '播放' }) + else items.push({ k: 'copy', icon: 'copy', label: '复制' }) + items.push({ k: 'forward', icon: 'share', label: '转发' }) + items.push({ k: 'quote', icon: 'chat', label: '引用' }) + items.push({ k: 'select', icon: 'view-list', label: '多选' }) + if (this.canRecall) items.push({ k: 'recall', icon: 'rollback', label: '撤回' }) + items.push({ k: 'delete', icon: 'delete', label: '删除' }) + return items + }, + popBoxStyle() { + return { + top: this.popTop + 'px', + left: this.popLeft + 'px', + width: this.popW + 'px' + } } }, onLoad(q) { @@ -592,14 +608,53 @@ export default { } }) }, - act(m) { + act(m, index) { if (!m || m.recalled || this.selecting) return this.more = false this.emoji = false this.hideKb() + this.popAt = Date.now() this.actionMsg = m + this.$nextTick(() => this.placePop(index)) + }, + placePop(index) { + const q = uni.createSelectorQuery().in(this) + q.select('#bubble-' + index).boundingClientRect() + q.exec((res) => { + const r = res && res[0] + if (!r || !r.width) return + const sys = uni.getSystemInfoSync() + const n = this.actItems.length + const cols = Math.min(n, 5) + const itemW = 56 + const w = cols * itemW + const rows = Math.ceil(n / cols) + const h = rows * 62 + 8 + let left = r.left + r.width / 2 - w / 2 + left = Math.max(8, Math.min(left, sys.windowWidth - w - 8)) + const below = r.top < h + 16 + const top = below ? r.bottom + 10 : r.top - h - 10 + const caret = r.left + r.width / 2 - left - 6 + this.popW = w + this.popLeft = left + this.popTop = Math.max(8, top) + this.popBelow = below + this.caretLeft = Math.max(14, Math.min(caret, w - 26)) + }) + }, + runAct(k) { + if (k === 'copy') return this.doCopy() + if (k === 'play') return this.doPlay() + if (k === 'forward') return this.doForward() + if (k === 'quote') return this.doQuote() + if (k === 'select') return this.enterSelect() + if (k === 'recall') return this.doRecall() + if (k === 'delete') return this.doDelete() + }, + closeAct() { + if (Date.now() - this.popAt < 400) return + this.actionMsg = null }, - closeAct() { this.actionMsg = null }, liveMsg() { const c = store.findConv(this.sid) const m = this.actionMsg @@ -840,19 +895,39 @@ export default { .em { width: 12.5%; text-align: center; font-size: 44rpx; line-height: 88rpx; } .hint { text-align: center; font-size: 24rpx; color: #9CA3AF; padding: 24rpx; background: #fff; } .ico { padding: 8rpx 8rpx; font-size: 40rpx; color: #111827; letter-spacing: 2rpx; } -.mask { position: fixed; left: 0; right: 0; top: 0; bottom: 0; background: rgba(17, 24, 39, 0.45); z-index: 40; display: flex; align-items: flex-end; } -.sheet { width: 100%; background: #fff; border-radius: 28rpx 28rpx 0 0; padding: 28rpx 28rpx 12rpx; } -.sheet-t { display: block; text-align: center; font-size: 30rpx; font-weight: 650; } -.sheet-s { display: block; text-align: center; font-size: 24rpx; color: #9CA3AF; margin: 8rpx 0 24rpx; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } -.sheet-grid { display: flex; flex-wrap: wrap; gap: 16rpx; } -.cell { - width: calc(25% - 12rpx); height: 140rpx; background: #F5F7FA; border-radius: 16rpx; - display: flex; flex-direction: column; align-items: center; justify-content: center; font-size: 22rpx; color: #374151; +.pop-mask { position: fixed; left: 0; right: 0; top: 0; bottom: 0; z-index: 40; } +.pop { + position: fixed; + display: flex; + flex-wrap: wrap; + background: #4C4C4C; + border-radius: 8px; + padding: 4px 0; + box-sizing: border-box; } -.ce { font-size: 32rpx; margin-bottom: 8rpx; color: #111827; font-weight: 650; } -.sheet-cancel { - margin: 20rpx 0 8rpx; height: 88rpx; border-radius: 16rpx; background: #F5F7FA; - display: flex; align-items: center; justify-content: center; font-size: 28rpx; color: #111827; +.pop-item { + width: 56px; + height: 62px; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + color: #fff; +} +.pop-lab { margin-top: 4px; font-size: 12px; line-height: 1; color: #fff; } +.pop-caret { + position: absolute; + width: 0; + height: 0; + border: 6px solid transparent; + border-top-color: #4C4C4C; + bottom: -12px; +} +.pop.below .pop-caret { + top: -12px; + bottom: auto; + border-top-color: transparent; + border-bottom-color: #4C4C4C; } .selbar { display: flex; background: #fff; border-top: 1rpx solid #E8ECF2; } .selbtn { flex: 1; height: 96rpx; display: flex; align-items: center; justify-content: center; font-size: 28rpx; color: #1677FF; }