diff --git a/packages/pro-components/chat/chat-actionbar/README.md b/packages/pro-components/chat/chat-actionbar/README.md index 8b8e0fe4a..4c2f58267 100644 --- a/packages/pro-components/chat/chat-actionbar/README.md +++ b/packages/pro-components/chat/chat-actionbar/README.md @@ -52,10 +52,11 @@ comment | String | - | 评价内容 | N content | String | - | 被复制的内容 | N copy-mode | String | markdown | 【实验】复制内容的模式,可选 'markdown'(复制markdown原文)或 'text'(复制纯文本)。可选项:markdown/text | N disabled | Boolean | false | 【讨论中】操作按钮是否可点击 | N -placement | String | start | 【实验】操作栏位置。可选项:start/end/space-around/space-between | N +long-press-position | Object | - | 【实验】长按触发点的位置信息,用于定位 popover。TS 类型:`{pageX: number; pageY: number; clientX: number; clientY: number; x: number; y: number}` | N +placement | String | start | 【实验】操作栏位置。可选项:start/end/space-around/space-between/longpress | N ### ChatActionbar Events 名称 | 参数 | 描述 -- | -- | -- -actions | `(detail: {name: string, active: boolean})` | 点击点赞,点踩,复制,分享,重新生成按钮时触发发 +actions | `(detail: {name: string, active: boolean, chatId: string})` | 点击点赞,点踩,复制,分享,重新生成按钮时触发发 diff --git a/packages/pro-components/chat/chat-actionbar/chat-actionbar.json b/packages/pro-components/chat/chat-actionbar/chat-actionbar.json index bd62bed5a..433f5cf9a 100644 --- a/packages/pro-components/chat/chat-actionbar/chat-actionbar.json +++ b/packages/pro-components/chat/chat-actionbar/chat-actionbar.json @@ -2,6 +2,7 @@ "component": true, "styleIsolation": "apply-shared", "usingComponents": { - "t-icon": "tdesign-miniprogram/icon/icon" + "t-icon": "tdesign-miniprogram/icon/icon", + "t-popover": "tdesign-miniprogram/popover/popover" } -} +} \ No newline at end of file diff --git a/packages/pro-components/chat/chat-actionbar/chat-actionbar.less b/packages/pro-components/chat/chat-actionbar/chat-actionbar.less index 05d6960a2..08cc60dff 100644 --- a/packages/pro-components/chat/chat-actionbar/chat-actionbar.less +++ b/packages/pro-components/chat/chat-actionbar/chat-actionbar.less @@ -6,11 +6,17 @@ @chat-actionbar-item-padding: var(--chat-actionbar-item-padding, 16rpx 28rpx); @chat-actionbar-item-color: @text-color-primary; @chat-actionbar-item-active: @brand-color; +@chat-actionbar-item-gap: 4rpx; // TODO: 长按弹出层样式 -@chat-actionbar-popover-background: @mask-active; -@chat-actionbar-popover-radius: 32rpx; -@chat-actionbar-popover-padding: 45rpx; +@chat-actionbar-popover-background: rgba(0, 0, 0, 0.9); +@chat-actionbar-popover-radius: 6rpx; +@chat-actionbar-popover-padding: 8rpx 16rpx; +@chat-actionbar-popover-inner-size: 20rpx; +@chat-actionbar-popover-item-width: 128rpx; +@chat-actionbar-popover-item-height: 156rpx; +@chat-actionbar-popover-item-gap: 8rpx; +@chat-actionbar-popover-item-inner-gap: 8rpx; .@{chat-actionbar} { display: flex; @@ -25,6 +31,40 @@ justify-content: flex-end; } + // 弹出层 + &--popover { + color: @font-white-1; + border-radius: @chat-actionbar-popover-radius; + + .@{chat-actionbar}__inner { + background-color: unset; + border: none; + display: flex; + flex-wrap: wrap; + gap: @chat-actionbar-item-gap; + + &--column { + gap: @chat-actionbar-popover-item-gap; + } + } + + .@{chat-actionbar}__item--popover { + color: #fff; + background-color: unset; + padding: 0; + margin: 0; + font-size: 28rpx; + line-height: 42rpx; + width: @chat-actionbar-popover-item-width; + height: @chat-actionbar-popover-item-height; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: @chat-actionbar-popover-item-inner-gap; + } + } + // 内部容器 &__inner { background-color: @bg-color-secondarycontainer; @@ -42,14 +82,6 @@ align-items: center; justify-content: space-between; } - - // 弹出层 - &--popover { - padding: @chat-actionbar-popover-padding; - background-color: @chat-actionbar-popover-background; - border-radius: @chat-actionbar-popover-radius; - color: @font-white-1; - } } // 左侧内容 @@ -62,8 +94,7 @@ // 操作项 &__item { color: @chat-actionbar-item-color; - margin: 12rpx 0; - padding: 4rpx 28rpx; + padding: @chat-actionbar-item-padding; border-right: 2rpx solid @component-stroke; background-color: unset; outline: none; @@ -81,4 +112,14 @@ color: @chat-actionbar-item-active; } } + + &__popover-skeleton { + position: fixed; + --td-popover-padding: @chat-actionbar-popover-padding; + + &__inner { + width: @chat-actionbar-popover-inner-size; + height: @chat-actionbar-popover-inner-size; + } + } } diff --git a/packages/pro-components/chat/chat-actionbar/chat-actionbar.ts b/packages/pro-components/chat/chat-actionbar/chat-actionbar.ts index 2a8a560eb..f2fe1ea8b 100644 --- a/packages/pro-components/chat/chat-actionbar/chat-actionbar.ts +++ b/packages/pro-components/chat/chat-actionbar/chat-actionbar.ts @@ -9,6 +9,7 @@ const name = `${prefix}-chat-actionbar`; export default class ChatActionbar extends SuperComponent { options: ComponentsOptionsType = { multipleSlots: true, + styleIsolation: 'shared', }; properties = props; @@ -23,22 +24,34 @@ export default class ChatActionbar extends SuperComponent { replay: 'refresh', copy: 'copy', share: 'share-1', + quote: 'enter', }, iconActiveMap: { good: 'thumb-up-filled', bad: 'thumb-down-filled', }, + widthStyle: '', + popoverStyle: 'transition: none;position: fixed;', + popoverPosition: '', + longpressVisible: false, }; observers = { comment(newVal) { - this.setData({ - pComment: newVal || '', - }); + this.setPComment(newVal); }, - 'actionBar, pComment'() { + 'actionBar, pComment, placement'() { this.setActions(); }, + longPressPosition(newVal) { + if (this.properties.placement === 'longpress') { + if (newVal) { + this.showPopover(newVal); + } else { + this.hidePopover(); + } + } + }, }; methods = { @@ -95,6 +108,7 @@ export default class ChatActionbar extends SuperComponent { this.triggerEvent('actions', { name, active: !isActive, + chatId: this.properties.chatId, }); } else if (name === 'bad') { const isActive = this.data.pComment === 'bad'; @@ -104,12 +118,15 @@ export default class ChatActionbar extends SuperComponent { this.triggerEvent('actions', { name, active: !isActive, + chatId: this.properties.chatId, }); } else { this.triggerEvent('actions', { name, + chatId: this.properties.chatId, }); } + this.onVisibleChange({ detail: { visible: false } }); }, handleCopy() { @@ -123,25 +140,95 @@ export default class ChatActionbar extends SuperComponent { }, setActions() { + const text = { + replay: '刷新', + copy: '复制', + good: '点赞', + bad: '点踩', + share: '分享', + quote: '引用', + }; + const baseActions = []; - if (Array.isArray(this.properties.actionBar)) { - this.properties.actionBar.forEach((item) => { - if (item === 'good' || item === 'bad') { - baseActions.push({ - name: item, - isActive: this.data.pComment === item, - }); - } else { - baseActions.push({ - name: item, - isActive: false, + let dataActions = []; + if (this.properties.placement === 'longpress') { + dataActions = ['quote', 'copy', 'share']; + } else if (Array.isArray(this.properties.actionBar)) { + dataActions = this.properties.actionBar; + } + dataActions.forEach((item) => { + if (item === 'good' || item === 'bad') { + baseActions.push({ + name: item, + isActive: this.data.pComment === item, + text: text[item] || item, + }); + } else { + baseActions.push({ + name: item, + isActive: false, + text: text[item] || item, + }); + } + }); + this.setData({ + actions: baseActions, + }); + }, + + setPComment(newVal) { + this.setData({ + pComment: newVal || '', + }); + }, + + showPopover(pos) { + this.setData({ + widthStyle: `width: ${this.data.actions.length * 128 + (this.data.actions.length - 1) * 8}rpx`, + popoverPosition: `top:${pos.y}px;left:${pos.x}px`, + longpressVisible: true, + }); + + setTimeout(() => { + const child = this.selectComponent('.popover'); + const query = this.createSelectorQuery().in(child); + + query.select('.t-popover').boundingClientRect(); + query.exec((res) => { + const [rect] = res; + + // 新增:检查元素是否超出屏幕宽度 + const { screenWidth } = wx.getWindowInfo(); + const elementRightEdge = rect.left + rect.width; + + if (elementRightEdge > screenWidth) { + this.setData({ + popoverStyle: `transition: none;position:fixed; left: unset !important; right: 16rpx !important;`, }); + } else if (rect.left <= 0) { + this.setData({ popoverStyle: `transition: none;position:fixed; left: 16rpx !important;` }); } }); - } + }, 200); + }, + + hidePopover() { + this.onVisibleChange({ detail: { visible: false } }); + }, + + onVisibleChange(e) { + const { visible } = e.detail; this.setData({ - actions: baseActions, + longpressVisible: visible, }); + if (!visible) { + setTimeout(() => { + this.setData({ + popoverPosition: '', + popoverStyle: 'transition: none;position: fixed;', + }); + }, 200); + } }, }; @@ -150,6 +237,9 @@ export default class ChatActionbar extends SuperComponent { this.data.filterSpecialChars = this.filterSpecialChars.bind(this); this.data.handleActionClick = this.handleActionClick.bind(this); this.data.handleCopy = this.handleCopy.bind(this); + this.data.showPopover = this.showPopover.bind(this); + this.data.hidePopover = this.hidePopover.bind(this); + this.data.setPComment = this.setPComment.bind(this); }, attached() { diff --git a/packages/pro-components/chat/chat-actionbar/chat-actionbar.wxml b/packages/pro-components/chat/chat-actionbar/chat-actionbar.wxml index af86db22b..a16cb58c3 100644 --- a/packages/pro-components/chat/chat-actionbar/chat-actionbar.wxml +++ b/packages/pro-components/chat/chat-actionbar/chat-actionbar.wxml @@ -1,11 +1,12 @@ - + @@ -13,7 +14,7 @@ + + + + + {{item.text}} + + + + + + diff --git a/packages/pro-components/chat/chat-actionbar/props.ts b/packages/pro-components/chat/chat-actionbar/props.ts index f75f62193..4df0e18b4 100644 --- a/packages/pro-components/chat/chat-actionbar/props.ts +++ b/packages/pro-components/chat/chat-actionbar/props.ts @@ -41,6 +41,11 @@ const props: TdChatActionbarProps = { type: String, value: 'start', }, + /** 【实验】长按触发点的位置信息,用于定位 popover */ + longPressPosition: { + type: Object, + value: null, + }, }; export default props; diff --git a/packages/pro-components/chat/chat-actionbar/type.ts b/packages/pro-components/chat/chat-actionbar/type.ts index f600f0d52..5a6d1a920 100644 --- a/packages/pro-components/chat/chat-actionbar/type.ts +++ b/packages/pro-components/chat/chat-actionbar/type.ts @@ -59,6 +59,20 @@ export interface TdChatActionbarProps { */ placement?: { type: StringConstructor; - value?: 'start' | 'end' | 'space-around' | 'space-between'; + value?: 'start' | 'end' | 'space-around' | 'space-between' | 'longpress'; + }; + /** + * 【实验】长按触发点的位置信息,用于定位 popover + */ + longPressPosition?: { + type: ObjectConstructor; + value?: { + pageX: number; + pageY: number; + clientX: number; + clientY: number; + x: number; + y: number; + }; }; } diff --git a/packages/pro-components/chat/chat-list/_example/base/index.js b/packages/pro-components/chat/chat-list/_example/base/index.js index 0ee1a13b1..bd7bdaeae 100644 --- a/packages/pro-components/chat/chat-list/_example/base/index.js +++ b/packages/pro-components/chat/chat-list/_example/base/index.js @@ -1,6 +1,12 @@ import Toast from 'tdesign-miniprogram/toast'; import { getNavigationBarHeight } from '../../../utils/utils'; +let uniqueId = 0; +const getUniqueKey = () => { + uniqueId += 1; + return `key-${uniqueId}`; +}; + const mockData = `南极的自动提款机并没有一个特定的专属名称,但历史上确实有一台ATM机曾短暂存在于南极的**麦克默多站**(McMurdo Station)。这台ATM由美国**富兰克林国家银行**(Wells Fargo)于1998年安装,主要供驻扎在该站的科研人员使用。不过,由于南极的极端环境和极低的人口密度,这台ATM机并未长期运行,最终被移除。 **背景补充:** @@ -40,20 +46,23 @@ Component({ role: 'assistant', status: 'complete', content: [ - { - type: 'text', - data: '它叫 McMurdo Station ATM,是美国富国银行安装在南极洲最大科学中心麦克默多站的一台自动提款机。', - }, - ], + { + type: 'text', + data: '它叫 McMurdo Station ATM,是美国富国银行安装在南极洲最大科学中心麦克默多站的一台自动提款机。', + }, + ], + chatId: getUniqueKey(), + comment: '', }, { - role: 'user', - content: [ - { - type: 'text', - data: '牛顿第一定律是否适用于所有参考系?', - }, - ], + role: 'user', + content: [ + { + type: 'text', + data: '牛顿第一定律是否适用于所有参考系?', + }, + ], + chatId: getUniqueKey(), }, ], value: '', // 输入框的值 @@ -62,6 +71,8 @@ Component({ inputStyle: '', // 输入框样式 contentHeight: '100vh', // 内容高度 animation: 'dots', + activePopoverId: '', // 当前打开悬浮actionbar的chatId + longPressPosition: null, // 长按位置对象 }, methods: { @@ -89,6 +100,7 @@ Component({ data: value.trim(), }, ], + chatId: getUniqueKey(), }; // 将用户消息插入到chatList的开头(因为reverse为true,所以用unshift) @@ -122,7 +134,7 @@ Component({ // 模拟助手回复 simulateAssistantReply() { - this.setData({ loading: true}); + this.setData({ loading: true }); // 请求中 const assistantMessage = { role: 'assistant', @@ -134,6 +146,8 @@ Component({ ], avatar: 'https://tdesign.gtimg.com/site/chat-avatar.png', status: 'pending', + chatId: getUniqueKey(), + comment: '', }; this.setData({ chatList: [assistantMessage, ...this.data.chatList], @@ -141,15 +155,15 @@ Component({ const that = this; wx.nextTick(() => { fetchStream(mockData, { - success(result) { - // 生文中 - that.data.chatList[0].status = 'streaming'; - if (!that.data.loading) return; - that.data.chatList[0].content[0].data += result; - that.setData({ - chatList: that.data.chatList, - }); - }, + success(result) { + // 生文中 + that.data.chatList[0].status = 'streaming'; + if (!that.data.loading) return; + that.data.chatList[0].content[0].data += result; + that.setData({ + chatList: that.data.chatList, + }); + }, complete() { that.data.chatList[0].status = 'complete'; that.setData({ @@ -163,7 +177,7 @@ Component({ }); }, handleAction(e) { - const { name, active, data } = e.detail; + const { name, active, data, chatId } = e.detail; let message = ''; switch (name) { @@ -193,6 +207,41 @@ Component({ message, theme: 'success', }); + + if (name === 'good' || name === 'bad') { + this.data.chatList.forEach((item) => { + if (item.chatId === chatId) { + item.comment = active ? name : ''; + } + }); + this.setData({ + chatList: this.data.chatList, + }); + } + }, + showPopover(e) { + const { id, longPressPosition } = e.detail; + + let role = ''; + this.data.chatList.forEach((item) => { + if (item.chatId === id) { + role = item.role; + } + }); + + // 仅当 role 为 user 时才显示 popover + if (role !== 'user') { + return; + } + + this.setData({ + activePopoverId: id, + longPressPosition, + }); + }, + handlePopoverAction(e) { + e.detail.chatId = this.data.activePopoverId; + this.handleAction(e); }, }, lifetimes: { diff --git a/packages/pro-components/chat/chat-list/_example/base/index.wxml b/packages/pro-components/chat/chat-list/_example/base/index.wxml index 9dc4cfa35..a4e41cb41 100644 --- a/packages/pro-components/chat/chat-list/_example/base/index.wxml +++ b/packages/pro-components/chat/chat-list/_example/base/index.wxml @@ -2,6 +2,7 @@ + diff --git a/packages/pro-components/chat/chat-message/README.md b/packages/pro-components/chat/chat-message/README.md index aaca5e286..efd35fa74 100644 --- a/packages/pro-components/chat/chat-message/README.md +++ b/packages/pro-components/chat/chat-message/README.md @@ -79,7 +79,7 @@ variant | String | base | 气泡框样式,支持基础、线框、文字三种 名称 | 参数 | 描述 -- | -- | -- -longpress | `(detail: { id: string })` | \- +message-longpress | `(detail: { id: string, longPressPosition: { x: number, y: number } })` | 长按事件 ### ChatMessage Slots diff --git a/packages/pro-components/chat/chat-message/chat-message.ts b/packages/pro-components/chat/chat-message/chat-message.ts index 8fbb36542..9f3337199 100644 --- a/packages/pro-components/chat/chat-message/chat-message.ts +++ b/packages/pro-components/chat/chat-message/chat-message.ts @@ -43,9 +43,13 @@ export default class ChatMessage extends SuperComponent { methods = { handleLongPress(e) { - this.triggerEvent('longpress', { + this.triggerEvent('message-longpress', { e, id: this.data.chatId, + longPressPosition: { + x: e.detail.x, + y: e.detail.y, + }, }); }, setShowAvatar() { diff --git a/packages/pro-components/chat/chat-sender/_example/attachments/index.wxml b/packages/pro-components/chat/chat-sender/_example/attachments/index.wxml index 6b5f70cb3..ad7d29793 100644 --- a/packages/pro-components/chat/chat-sender/_example/attachments/index.wxml +++ b/packages/pro-components/chat/chat-sender/_example/attachments/index.wxml @@ -32,11 +32,11 @@ > - + 深度思考 - + diff --git a/packages/pro-components/chat/chat-sender/_example/base/index.wxml b/packages/pro-components/chat/chat-sender/_example/base/index.wxml index 6cc3463ea..f3270e0a4 100644 --- a/packages/pro-components/chat/chat-sender/_example/base/index.wxml +++ b/packages/pro-components/chat/chat-sender/_example/base/index.wxml @@ -33,11 +33,11 @@ > - + 深度思考 - + diff --git a/packages/pro-components/chat/chat-sender/_example/content-citation/index.wxml b/packages/pro-components/chat/chat-sender/_example/content-citation/index.wxml index 380ac3f68..de25dc45e 100644 --- a/packages/pro-components/chat/chat-sender/_example/content-citation/index.wxml +++ b/packages/pro-components/chat/chat-sender/_example/content-citation/index.wxml @@ -41,11 +41,11 @@ - + 深度思考 - + diff --git a/packages/pro-components/chat/chat-sender/_example/file-citation/index.wxml b/packages/pro-components/chat/chat-sender/_example/file-citation/index.wxml index 3874da7d8..2baf2595b 100644 --- a/packages/pro-components/chat/chat-sender/_example/file-citation/index.wxml +++ b/packages/pro-components/chat/chat-sender/_example/file-citation/index.wxml @@ -49,11 +49,11 @@ - + 深度思考 - +