chore: establish V1.1 recovery baseline
This commit is contained in:
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* MessageBubble 聊天气泡(规范 §61-63)
|
||||
*
|
||||
* bubbleType: self / other / system
|
||||
* self:右对齐,主红气泡
|
||||
* other:左对齐,白色气泡
|
||||
* system:居中,灰色文字(如"成功牵手"系统提示)
|
||||
*
|
||||
* 失败状态:sendStatus='failed' 时显示重试图标。
|
||||
*/
|
||||
Component({
|
||||
options: { addGlobalClass: true },
|
||||
properties: {
|
||||
bubbleType: { type: String, value: 'other' },
|
||||
content: { type: String, value: '' },
|
||||
sentAt: { type: Number, value: 0 },
|
||||
sentAtText: { type: String, value: '' },
|
||||
avatarUrl: { type: String, value: '' },
|
||||
showAvatar: { type: Boolean, value: true },
|
||||
showTime: { type: Boolean, value: false },
|
||||
sendStatus: { type: String, value: 'sent' }, // sending|sent|failed
|
||||
},
|
||||
methods: {
|
||||
onRetry() {
|
||||
if (this.data.sendStatus !== 'failed') return;
|
||||
this.triggerEvent('retry', {});
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"component": true,
|
||||
"usingComponents": {
|
||||
"ui-avatar": "../avatar/index"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<view class="ui-bubble ui-bubble--{{bubbleType}}">
|
||||
<view wx:if="{{showTime && sentAtText}}" class="ui-bubble__time">
|
||||
<text>{{sentAtText}}</text>
|
||||
</view>
|
||||
<block wx:if="{{bubbleType === 'system'}}">
|
||||
<view class="ui-bubble__system">{{content}}</view>
|
||||
</block>
|
||||
<block wx:else>
|
||||
<view class="ui-bubble__row">
|
||||
<ui-avatar
|
||||
wx:if="{{bubbleType === 'other' && showAvatar}}"
|
||||
src="{{avatarUrl}}"
|
||||
size="sm"
|
||||
/>
|
||||
<view class="ui-bubble__content-wrap">
|
||||
<view class="ui-bubble__content">{{content}}</view>
|
||||
<view
|
||||
wx:if="{{bubbleType === 'self' && sendStatus === 'failed'}}"
|
||||
class="ui-bubble__retry"
|
||||
catchtap="onRetry"
|
||||
>重发</view>
|
||||
<view wx:if="{{bubbleType === 'self' && sendStatus === 'sending'}}" class="ui-bubble__sending">
|
||||
<view class="ui-bubble__sending-dot"></view>
|
||||
</view>
|
||||
</view>
|
||||
<ui-avatar
|
||||
wx:if="{{bubbleType === 'self' && showAvatar}}"
|
||||
src="{{avatarUrl}}"
|
||||
size="sm"
|
||||
/>
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
@@ -0,0 +1,78 @@
|
||||
.ui-bubble {
|
||||
padding: var(--spacing-1) var(--spacing-page-x);
|
||||
}
|
||||
.ui-bubble__time {
|
||||
text-align: center;
|
||||
margin: var(--spacing-3) 0;
|
||||
font-size: var(--font-size-xs);
|
||||
color: var(--color-text-tertiary);
|
||||
}
|
||||
.ui-bubble__system {
|
||||
text-align: center;
|
||||
font-size: var(--font-size-sm);
|
||||
color: var(--color-text-tertiary);
|
||||
background: var(--color-bg-disabled);
|
||||
padding: var(--spacing-1) var(--spacing-3);
|
||||
border-radius: var(--radius-sm);
|
||||
display: inline-block;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.ui-bubble__row {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
}
|
||||
.ui-bubble--other .ui-bubble__row {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
.ui-bubble--self .ui-bubble__row {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
.ui-bubble__content-wrap {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
max-width: 70%;
|
||||
margin: 0 var(--spacing-2);
|
||||
}
|
||||
.ui-bubble--self .ui-bubble__content-wrap {
|
||||
align-items: flex-end;
|
||||
}
|
||||
.ui-bubble__content {
|
||||
padding: var(--spacing-2) var(--spacing-3);
|
||||
border-radius: var(--radius-lg);
|
||||
font-size: var(--font-size-base);
|
||||
line-height: var(--line-height-normal);
|
||||
word-break: break-all;
|
||||
}
|
||||
.ui-bubble--other .ui-bubble__content {
|
||||
background: var(--color-bg-card);
|
||||
color: var(--color-text-primary);
|
||||
border-top-left-radius: var(--radius-sm);
|
||||
}
|
||||
.ui-bubble--self .ui-bubble__content {
|
||||
background: var(--color-primary);
|
||||
color: #FFFFFF;
|
||||
border-top-right-radius: var(--radius-sm);
|
||||
}
|
||||
.ui-bubble__retry {
|
||||
margin-top: var(--spacing-1);
|
||||
font-size: var(--font-size-xs);
|
||||
color: var(--color-danger);
|
||||
align-self: flex-end;
|
||||
}
|
||||
.ui-bubble__sending {
|
||||
margin-top: var(--spacing-1);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
.ui-bubble__sending-dot {
|
||||
width: 16rpx;
|
||||
height: 16rpx;
|
||||
border-radius: 50%;
|
||||
background: var(--color-text-disabled);
|
||||
animation: ui-bubble-pulse 1s ease-in-out infinite;
|
||||
}
|
||||
@keyframes ui-bubble-pulse {
|
||||
0%, 100% { opacity: 0.3; }
|
||||
50% { opacity: 1; }
|
||||
}
|
||||
Reference in New Issue
Block a user