@flexem/chat-box 1.0.2 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -1
- package/miniprogram_dist/components/message/index.js +129 -1
- package/miniprogram_dist/components/message/index.wxml +22 -2
- package/miniprogram_dist/components/message/index.wxss +60 -0
- package/miniprogram_dist/components/sidebar/index.js +32 -1
- package/miniprogram_dist/components/sidebar/index.wxml +2 -2
- package/miniprogram_dist/components/sidebar/index.wxss +3 -5
- package/miniprogram_dist/index.js +33 -1
- package/miniprogram_dist/index.wxml +20 -0
- package/miniprogram_dist/index.wxss +82 -0
- package/miniprogram_dist/package.json +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -74,7 +74,10 @@ npm run build
|
|
|
74
74
|
**request 合法域名:**
|
|
75
75
|
- `https://ids-dev.platform.flexem.net`(IDS 服务)
|
|
76
76
|
- `https://chatweb-dev.platform.flexem.net`(AI 聊天服务)
|
|
77
|
-
- `https://
|
|
77
|
+
- `https://chatbg.fbox360.com`(线上AI 聊天服务)
|
|
78
|
+
- `https://usso.fbox360.com`(线上IDS 服务)
|
|
79
|
+
- `https://oss-dev.platform.flexem.net`(线上OSS 服务)
|
|
80
|
+
- `https://os.fbox360.com`(线上OSS 服务)
|
|
78
81
|
- `https://up-z2.qiniup.com`(七牛云上传)
|
|
79
82
|
|
|
80
83
|
**uploadFile 合法域名:**
|
|
@@ -82,6 +85,7 @@ npm run build
|
|
|
82
85
|
|
|
83
86
|
**downloadFile 合法域名:**
|
|
84
87
|
- `https://blobstorage-dev.platform.flexem.net`(文件下载服务)
|
|
88
|
+
- `https://fuf.flexem.net`(线上文件下载服务)
|
|
85
89
|
|
|
86
90
|
### 2. 页面配置
|
|
87
91
|
|
|
@@ -42,7 +42,10 @@ Component({
|
|
|
42
42
|
showThinking: false, // 是否显示思考过程
|
|
43
43
|
lastParseTime: 0, // 上次解析时间
|
|
44
44
|
pendingParse: false, // 是否有待处理的解析
|
|
45
|
-
elementIdCounter: 0
|
|
45
|
+
elementIdCounter: 0, // 元素 ID 计数器,用于生成唯一 ID
|
|
46
|
+
// 长按气泡菜单
|
|
47
|
+
showBubbleMenu: false,
|
|
48
|
+
bubbleMenuPosition: { top: 0, left: 0 }
|
|
46
49
|
},
|
|
47
50
|
|
|
48
51
|
observers: {
|
|
@@ -983,6 +986,131 @@ Component({
|
|
|
983
986
|
if (option) {
|
|
984
987
|
this.triggerEvent('quickreply', { content: option });
|
|
985
988
|
}
|
|
989
|
+
},
|
|
990
|
+
|
|
991
|
+
// ==================== 长按气泡菜单 ====================
|
|
992
|
+
|
|
993
|
+
/**
|
|
994
|
+
* 长按消息显示气泡菜单
|
|
995
|
+
*/
|
|
996
|
+
onLongPress(e) {
|
|
997
|
+
// 只对 AI 消息显示气泡菜单
|
|
998
|
+
if (this.properties.message.role !== 'assistant') {
|
|
999
|
+
return;
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
// 获取触摸位置
|
|
1003
|
+
const touch = e.touches[0] || e.changedTouches[0];
|
|
1004
|
+
if (!touch) return;
|
|
1005
|
+
|
|
1006
|
+
// 计算气泡菜单位置(在触摸点上方)
|
|
1007
|
+
const menuHeight = 120; // 气泡菜单大约高度
|
|
1008
|
+
const top = touch.clientY - menuHeight - 20;
|
|
1009
|
+
const left = touch.clientX;
|
|
1010
|
+
|
|
1011
|
+
this.setData({
|
|
1012
|
+
showBubbleMenu: true,
|
|
1013
|
+
bubbleMenuPosition: {
|
|
1014
|
+
top: Math.max(top, 50), // 确保不超出顶部
|
|
1015
|
+
left: left
|
|
1016
|
+
}
|
|
1017
|
+
});
|
|
1018
|
+
|
|
1019
|
+
// 震动反馈
|
|
1020
|
+
wx.vibrateShort({ type: 'medium' });
|
|
1021
|
+
},
|
|
1022
|
+
|
|
1023
|
+
/**
|
|
1024
|
+
* 隐藏气泡菜单
|
|
1025
|
+
*/
|
|
1026
|
+
hideBubbleMenu() {
|
|
1027
|
+
this.setData({ showBubbleMenu: false });
|
|
1028
|
+
},
|
|
1029
|
+
|
|
1030
|
+
/**
|
|
1031
|
+
* 阻止事件冒泡
|
|
1032
|
+
*/
|
|
1033
|
+
stopPropagation() {
|
|
1034
|
+
// 空函数,用于阻止事件冒泡
|
|
1035
|
+
},
|
|
1036
|
+
|
|
1037
|
+
/**
|
|
1038
|
+
* 复制全文
|
|
1039
|
+
*/
|
|
1040
|
+
onCopyAll() {
|
|
1041
|
+
const content = this.properties.message.content;
|
|
1042
|
+
wx.setClipboardData({
|
|
1043
|
+
data: content,
|
|
1044
|
+
success: () => {
|
|
1045
|
+
wx.showToast({
|
|
1046
|
+
title: '内容已复制',
|
|
1047
|
+
icon: 'success'
|
|
1048
|
+
});
|
|
1049
|
+
this.hideBubbleMenu();
|
|
1050
|
+
}
|
|
1051
|
+
});
|
|
1052
|
+
},
|
|
1053
|
+
|
|
1054
|
+
/**
|
|
1055
|
+
* 节选复制 - 触发事件让父组件显示弹框
|
|
1056
|
+
*/
|
|
1057
|
+
onSelectCopy() {
|
|
1058
|
+
const content = this.properties.message.content;
|
|
1059
|
+
// 按段落分割内容
|
|
1060
|
+
const segments = this.splitContentToSegments(content);
|
|
1061
|
+
|
|
1062
|
+
this.setData({
|
|
1063
|
+
showBubbleMenu: false
|
|
1064
|
+
});
|
|
1065
|
+
|
|
1066
|
+
// 触发事件,让父组件显示弹框
|
|
1067
|
+
this.triggerEvent('selectcopy', { segments });
|
|
1068
|
+
},
|
|
1069
|
+
|
|
1070
|
+
/**
|
|
1071
|
+
* 将内容按段落分割
|
|
1072
|
+
*/
|
|
1073
|
+
splitContentToSegments(content) {
|
|
1074
|
+
if (!content) return [];
|
|
1075
|
+
|
|
1076
|
+
// 按换行符分割,过滤空行,合并短段落
|
|
1077
|
+
const lines = content.split('\n');
|
|
1078
|
+
const segments = [];
|
|
1079
|
+
let currentSegment = '';
|
|
1080
|
+
|
|
1081
|
+
for (const line of lines) {
|
|
1082
|
+
const trimmedLine = line.trim();
|
|
1083
|
+
if (!trimmedLine) {
|
|
1084
|
+
// 空行,保存当前段落
|
|
1085
|
+
if (currentSegment) {
|
|
1086
|
+
segments.push({ text: currentSegment.trim() });
|
|
1087
|
+
currentSegment = '';
|
|
1088
|
+
}
|
|
1089
|
+
} else {
|
|
1090
|
+
// 非空行
|
|
1091
|
+
if (currentSegment) {
|
|
1092
|
+
currentSegment += '\n' + trimmedLine;
|
|
1093
|
+
} else {
|
|
1094
|
+
currentSegment = trimmedLine;
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
// 保存最后一个段落
|
|
1100
|
+
if (currentSegment) {
|
|
1101
|
+
segments.push({ text: currentSegment.trim() });
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1104
|
+
return segments;
|
|
1105
|
+
},
|
|
1106
|
+
|
|
1107
|
+
/**
|
|
1108
|
+
* 气泡菜单 - 语音播放
|
|
1109
|
+
*/
|
|
1110
|
+
onBubblePlayVoice() {
|
|
1111
|
+
this.hideBubbleMenu();
|
|
1112
|
+
const message = this.properties.message;
|
|
1113
|
+
this.triggerEvent('playvoice', { id: message.id, content: message.content });
|
|
986
1114
|
}
|
|
987
1115
|
}
|
|
988
1116
|
});
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<!-- 消息内容区域 -->
|
|
3
3
|
<view class="message-content">
|
|
4
4
|
<!-- 用户消息 -->
|
|
5
|
-
<view wx:if="{{message.role === 'user'}}" class="message-bubble user-bubble">
|
|
5
|
+
<view wx:if="{{message.role === 'user'}}" class="message-bubble user-bubble" bindlongpress="onLongPress">
|
|
6
6
|
<text class="message-text">{{message.content}}</text>
|
|
7
7
|
|
|
8
8
|
<!-- 附件预览 -->
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
</view>
|
|
40
40
|
|
|
41
41
|
<!-- AI 消息 -->
|
|
42
|
-
<view wx:else class="message-bubble ai-bubble {{message.isError ? 'error-bubble' : ''}}">
|
|
42
|
+
<view wx:else class="message-bubble ai-bubble {{message.isError ? 'error-bubble' : ''}}" bindlongpress="onLongPress">
|
|
43
43
|
<!-- 思考过程(可折叠) -->
|
|
44
44
|
<view wx:if="{{message.thinkingText}}" class="thinking-section">
|
|
45
45
|
<view class="thinking-header" bindtap="toggleThinking">
|
|
@@ -243,6 +243,26 @@
|
|
|
243
243
|
</block>
|
|
244
244
|
</view>
|
|
245
245
|
|
|
246
|
+
<!-- 长按气泡菜单 -->
|
|
247
|
+
<view wx:if="{{showBubbleMenu}}" class="bubble-menu-overlay" catchtap="hideBubbleMenu">
|
|
248
|
+
<view class="bubble-menu" catchtap="stopPropagation" style="top: {{bubbleMenuPosition.top}}px; left: {{bubbleMenuPosition.left}}px;">
|
|
249
|
+
<view class="bubble-menu-item" bindtap="onCopyAll">
|
|
250
|
+
<image class="bubble-menu-icon" src="../../assets/icons/icon-copy.svg" mode="aspectFit" />
|
|
251
|
+
<text class="bubble-menu-text">复制全文</text>
|
|
252
|
+
</view>
|
|
253
|
+
<view class="bubble-menu-item" bindtap="onSelectCopy">
|
|
254
|
+
<image class="bubble-menu-icon" src="../../assets/icons/icon-edit-msg.svg" mode="aspectFit" />
|
|
255
|
+
<text class="bubble-menu-text">节选复制</text>
|
|
256
|
+
</view>
|
|
257
|
+
<view class="bubble-menu-item" bindtap="onBubblePlayVoice">
|
|
258
|
+
<image class="bubble-menu-icon" src="../../assets/icons/icon-play-voice.svg" mode="aspectFit" />
|
|
259
|
+
<text class="bubble-menu-text">语音播放</text>
|
|
260
|
+
</view>
|
|
261
|
+
<!-- 气泡箭头 -->
|
|
262
|
+
<view class="bubble-menu-arrow"></view>
|
|
263
|
+
</view>
|
|
264
|
+
</view>
|
|
265
|
+
|
|
246
266
|
<!-- 正在输入指示器 -->
|
|
247
267
|
<view wx:if="{{message.isStreaming}}" class="typing-indicator">
|
|
248
268
|
<!-- 语音播放中时显示播放动画 -->
|
|
@@ -573,3 +573,63 @@
|
|
|
573
573
|
opacity: 0.5;
|
|
574
574
|
pointer-events: none;
|
|
575
575
|
}
|
|
576
|
+
|
|
577
|
+
/* 长按气泡菜单 */
|
|
578
|
+
.bubble-menu-overlay {
|
|
579
|
+
position: fixed;
|
|
580
|
+
top: 0;
|
|
581
|
+
left: 0;
|
|
582
|
+
right: 0;
|
|
583
|
+
bottom: 0;
|
|
584
|
+
z-index: 1000;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
.bubble-menu {
|
|
588
|
+
position: absolute;
|
|
589
|
+
display: flex;
|
|
590
|
+
flex-direction: row;
|
|
591
|
+
background-color: #333;
|
|
592
|
+
border-radius: 16rpx;
|
|
593
|
+
box-shadow: 0 8rpx 32rpx rgba(0, 0, 0, 0.3);
|
|
594
|
+
padding: 16rpx 8rpx;
|
|
595
|
+
transform: translateX(-50%);
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
.bubble-menu-item {
|
|
599
|
+
display: flex;
|
|
600
|
+
flex-direction: column;
|
|
601
|
+
align-items: center;
|
|
602
|
+
justify-content: center;
|
|
603
|
+
padding: 16rpx 24rpx;
|
|
604
|
+
min-width: 120rpx;
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
.bubble-menu-item:active {
|
|
608
|
+
background-color: #444;
|
|
609
|
+
border-radius: 12rpx;
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
.bubble-menu-icon {
|
|
613
|
+
width: 48rpx;
|
|
614
|
+
height: 48rpx;
|
|
615
|
+
margin-bottom: 8rpx;
|
|
616
|
+
filter: brightness(0) invert(1);
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
.bubble-menu-text {
|
|
620
|
+
font-size: 24rpx;
|
|
621
|
+
color: #fff;
|
|
622
|
+
white-space: nowrap;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
.bubble-menu-arrow {
|
|
626
|
+
position: absolute;
|
|
627
|
+
bottom: -16rpx;
|
|
628
|
+
left: 50%;
|
|
629
|
+
transform: translateX(-50%);
|
|
630
|
+
width: 0;
|
|
631
|
+
height: 0;
|
|
632
|
+
border-left: 16rpx solid transparent;
|
|
633
|
+
border-right: 16rpx solid transparent;
|
|
634
|
+
border-top: 16rpx solid #333;
|
|
635
|
+
}
|
|
@@ -49,7 +49,17 @@ Component({
|
|
|
49
49
|
hasMore: true, // 是否有更多数据
|
|
50
50
|
currentPage: 1, // 当前页码
|
|
51
51
|
pageSize: 20, // 每页数量
|
|
52
|
-
searchKeyword: ''
|
|
52
|
+
searchKeyword: '', // 搜索关键词
|
|
53
|
+
// 胶囊按钮位置
|
|
54
|
+
menuButtonTop: 0,
|
|
55
|
+
menuButtonHeight: 0
|
|
56
|
+
},
|
|
57
|
+
|
|
58
|
+
lifetimes: {
|
|
59
|
+
attached() {
|
|
60
|
+
// 获取胶囊按钮位置,确保关闭按钮与其对齐
|
|
61
|
+
this.initMenuButtonInfo();
|
|
62
|
+
}
|
|
53
63
|
},
|
|
54
64
|
|
|
55
65
|
observers: {
|
|
@@ -61,6 +71,27 @@ Component({
|
|
|
61
71
|
},
|
|
62
72
|
|
|
63
73
|
methods: {
|
|
74
|
+
/**
|
|
75
|
+
* 获取胶囊按钮位置信息
|
|
76
|
+
*/
|
|
77
|
+
initMenuButtonInfo() {
|
|
78
|
+
try {
|
|
79
|
+
const menuButton = wx.getMenuButtonBoundingClientRect();
|
|
80
|
+
this.setData({
|
|
81
|
+
menuButtonTop: menuButton.top,
|
|
82
|
+
menuButtonHeight: menuButton.height
|
|
83
|
+
});
|
|
84
|
+
} catch (e) {
|
|
85
|
+
// 降级处理
|
|
86
|
+
const systemInfo = wx.getSystemInfoSync();
|
|
87
|
+
const statusBarHeight = systemInfo.statusBarHeight || 20;
|
|
88
|
+
this.setData({
|
|
89
|
+
menuButtonTop: statusBarHeight + 4,
|
|
90
|
+
menuButtonHeight: 32
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
|
|
64
95
|
/**
|
|
65
96
|
* 加载会话列表
|
|
66
97
|
*/
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
<view class="sidebar-wrapper {{visible ? 'show' : ''}}">
|
|
2
2
|
<view class="sidebar-container" catchtap="preventBubble">
|
|
3
3
|
<!-- 顶部标题栏(只有关闭按钮) -->
|
|
4
|
-
<view class="sidebar-header">
|
|
5
|
-
<view class="close-btn" bindtap="onClose">
|
|
4
|
+
<view class="sidebar-header" style="padding-top: {{menuButtonTop}}px; height: {{menuButtonHeight}}px;">
|
|
5
|
+
<view class="close-btn" style="height: {{menuButtonHeight}}px;" bindtap="onClose">
|
|
6
6
|
<image class="close-icon" src="/miniprogram_npm/@flexem/chat-box/assets/icons/icon-close.svg" mode="aspectFit" />
|
|
7
7
|
</view>
|
|
8
8
|
</view>
|
|
@@ -30,21 +30,19 @@
|
|
|
30
30
|
display: flex;
|
|
31
31
|
align-items: center;
|
|
32
32
|
padding: 0 16rpx;
|
|
33
|
-
|
|
34
|
-
height: 88rpx;
|
|
33
|
+
box-sizing: content-box;
|
|
35
34
|
}
|
|
36
35
|
|
|
37
36
|
.close-btn {
|
|
38
37
|
width: 64rpx;
|
|
39
|
-
height: 64rpx;
|
|
40
38
|
display: flex;
|
|
41
39
|
align-items: center;
|
|
42
40
|
justify-content: center;
|
|
43
41
|
}
|
|
44
42
|
|
|
45
43
|
.close-icon {
|
|
46
|
-
width:
|
|
47
|
-
height:
|
|
44
|
+
width: 50rpx;
|
|
45
|
+
height: 50rpx;
|
|
48
46
|
}
|
|
49
47
|
|
|
50
48
|
/* 标题行(对话列表 + 编辑按钮) */
|
|
@@ -110,6 +110,10 @@ Component({
|
|
|
110
110
|
editingContent: '', // 编辑中的内容
|
|
111
111
|
editingMessage: null, // 正在编辑的消息
|
|
112
112
|
|
|
113
|
+
// 节选复制弹框
|
|
114
|
+
showSelectCopyModal: false, // 是否显示节选复制弹框
|
|
115
|
+
selectCopySegments: [], // 节选内容段落
|
|
116
|
+
|
|
113
117
|
// 服务配置(从 serviceUrls 获取)
|
|
114
118
|
serviceUrls: {
|
|
115
119
|
aiChatUrl: '', // AI 聊天 API 地址
|
|
@@ -194,7 +198,6 @@ Component({
|
|
|
194
198
|
console.log('[ChatBox] 未提供 data 参数');
|
|
195
199
|
return;
|
|
196
200
|
}
|
|
197
|
-
|
|
198
201
|
if (isToken) {
|
|
199
202
|
// data 就是 token,直接使用
|
|
200
203
|
this.setData({ userToken: data });
|
|
@@ -1120,6 +1123,35 @@ Component({
|
|
|
1120
1123
|
});
|
|
1121
1124
|
},
|
|
1122
1125
|
|
|
1126
|
+
// ==================== 节选复制弹框 ====================
|
|
1127
|
+
|
|
1128
|
+
/**
|
|
1129
|
+
* 打开节选复制弹框
|
|
1130
|
+
*/
|
|
1131
|
+
onSelectCopy(e) {
|
|
1132
|
+
const { segments } = e.detail;
|
|
1133
|
+
this.setData({
|
|
1134
|
+
showSelectCopyModal: true,
|
|
1135
|
+
selectCopySegments: segments
|
|
1136
|
+
});
|
|
1137
|
+
},
|
|
1138
|
+
|
|
1139
|
+
/**
|
|
1140
|
+
* 关闭节选复制弹框
|
|
1141
|
+
*/
|
|
1142
|
+
hideSelectCopyModal() {
|
|
1143
|
+
this.setData({
|
|
1144
|
+
showSelectCopyModal: false
|
|
1145
|
+
});
|
|
1146
|
+
},
|
|
1147
|
+
|
|
1148
|
+
/**
|
|
1149
|
+
* 阻止事件冒泡
|
|
1150
|
+
*/
|
|
1151
|
+
stopPropagation() {
|
|
1152
|
+
// 空函数,用于阻止事件冒泡
|
|
1153
|
+
},
|
|
1154
|
+
|
|
1123
1155
|
// ==================== 语音播放 ====================
|
|
1124
1156
|
|
|
1125
1157
|
/**
|
|
@@ -115,6 +115,7 @@
|
|
|
115
115
|
bind:edit="onEditMessage"
|
|
116
116
|
bind:quickreply="onQuickReply"
|
|
117
117
|
bind:imageload="onImageLoad"
|
|
118
|
+
bind:selectcopy="onSelectCopy"
|
|
118
119
|
/>
|
|
119
120
|
</block>
|
|
120
121
|
|
|
@@ -169,4 +170,23 @@
|
|
|
169
170
|
</view>
|
|
170
171
|
</view>
|
|
171
172
|
</view>
|
|
173
|
+
|
|
174
|
+
<!-- 节选复制弹框(放在最外层,确保层级最高) -->
|
|
175
|
+
<view wx:if="{{showSelectCopyModal}}" class="select-copy-modal-overlay" catchtap="hideSelectCopyModal">
|
|
176
|
+
<view class="select-copy-modal" catchtap="stopPropagation">
|
|
177
|
+
<view class="select-copy-header">
|
|
178
|
+
<text class="select-copy-title">选择文字复制</text>
|
|
179
|
+
</view>
|
|
180
|
+
<scroll-view class="select-copy-content" scroll-y="true">
|
|
181
|
+
<block wx:for="{{selectCopySegments}}" wx:key="index">
|
|
182
|
+
<view class="select-copy-segment">
|
|
183
|
+
<text user-select="{{true}}" space="nbsp">{{item.text}}</text>
|
|
184
|
+
</view>
|
|
185
|
+
</block>
|
|
186
|
+
</scroll-view>
|
|
187
|
+
<view class="select-copy-footer">
|
|
188
|
+
<text class="select-copy-cancel" bindtap="hideSelectCopyModal">取消</text>
|
|
189
|
+
</view>
|
|
190
|
+
</view>
|
|
191
|
+
</view>
|
|
172
192
|
</view>
|
|
@@ -289,3 +289,85 @@
|
|
|
289
289
|
font-size: 22rpx;
|
|
290
290
|
color: #999;
|
|
291
291
|
}
|
|
292
|
+
|
|
293
|
+
/* 节选复制弹框 */
|
|
294
|
+
.select-copy-modal-overlay {
|
|
295
|
+
position: fixed;
|
|
296
|
+
top: 0;
|
|
297
|
+
left: 0;
|
|
298
|
+
right: 0;
|
|
299
|
+
bottom: 0;
|
|
300
|
+
background-color: rgba(0, 0, 0, 0.5);
|
|
301
|
+
z-index: 9999;
|
|
302
|
+
display: flex;
|
|
303
|
+
align-items: flex-end;
|
|
304
|
+
justify-content: center;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
.select-copy-modal {
|
|
308
|
+
width: 100%;
|
|
309
|
+
max-height: 70vh;
|
|
310
|
+
background-color: #fff;
|
|
311
|
+
border-radius: 24rpx 24rpx 0 0;
|
|
312
|
+
display: flex;
|
|
313
|
+
flex-direction: column;
|
|
314
|
+
overflow: hidden;
|
|
315
|
+
animation: slideUp 0.3s ease-out;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
@keyframes slideUp {
|
|
319
|
+
from {
|
|
320
|
+
transform: translateY(100%);
|
|
321
|
+
}
|
|
322
|
+
to {
|
|
323
|
+
transform: translateY(0);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
.select-copy-header {
|
|
328
|
+
padding: 32rpx;
|
|
329
|
+
border-bottom: 1rpx solid #eee;
|
|
330
|
+
text-align: center;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
.select-copy-title {
|
|
334
|
+
font-size: 32rpx;
|
|
335
|
+
font-weight: 500;
|
|
336
|
+
color: #333;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
.select-copy-content {
|
|
340
|
+
flex: 1;
|
|
341
|
+
padding: 24rpx 32rpx;
|
|
342
|
+
max-height: 50vh;
|
|
343
|
+
overflow-y: auto;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
.select-copy-segment {
|
|
347
|
+
padding: 20rpx 24rpx;
|
|
348
|
+
margin-bottom: 16rpx;
|
|
349
|
+
background-color: #f8f9fa;
|
|
350
|
+
border-radius: 12rpx;
|
|
351
|
+
font-size: 28rpx;
|
|
352
|
+
line-height: 1.6;
|
|
353
|
+
color: #333;
|
|
354
|
+
-webkit-user-select: text;
|
|
355
|
+
user-select: text;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
.select-copy-footer {
|
|
359
|
+
padding: 16rpx 32rpx;
|
|
360
|
+
padding-bottom: calc(16rpx + env(safe-area-inset-bottom));
|
|
361
|
+
display: flex;
|
|
362
|
+
justify-content: center;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
.select-copy-cancel {
|
|
366
|
+
font-size: 28rpx;
|
|
367
|
+
color: #007aff;
|
|
368
|
+
padding: 16rpx 32rpx;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
.select-copy-cancel:active {
|
|
372
|
+
opacity: 0.6;
|
|
373
|
+
}
|