im-ui-mobile 0.0.42 → 0.0.44

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.
Files changed (42) hide show
  1. package/components/im-arrow-bar/im-arrow-bar.vue +59 -0
  2. package/components/im-bar-group/im-bar-group.vue +17 -0
  3. package/components/im-btn-bar/im-btn-bar.vue +60 -0
  4. package/components/im-chat-at-box/im-chat-at-box.vue +189 -0
  5. package/components/im-chat-group-readed/im-chat-group-readed.vue +172 -0
  6. package/components/im-chat-item/im-chat-item.vue +215 -0
  7. package/components/im-chat-message-item/im-chat-message-item.vue +559 -0
  8. package/components/im-chat-record/im-chat-record.vue +303 -0
  9. package/components/im-file-upload/im-file-upload.vue +300 -0
  10. package/components/im-friend-item/im-friend-item.vue +74 -0
  11. package/components/im-group-item/im-group-item.vue +60 -0
  12. package/components/im-group-member-selector/im-group-member-selector.vue +199 -0
  13. package/components/im-group-rtc-join/im-group-rtc-join.vue +112 -0
  14. package/components/im-head-image/im-head-image.vue +122 -0
  15. package/components/im-image-upload/im-image-upload.vue +90 -0
  16. package/components/im-loading/im-loading.vue +63 -0
  17. package/components/im-long-press-menu/im-long-press-menu.vue +137 -0
  18. package/components/im-nav-bar/im-nav-bar.vue +99 -0
  19. package/components/im-switch-bar/im-switch-bar.vue +60 -0
  20. package/components/im-virtual-scroller/im-virtual-scroller.vue +52 -0
  21. package/index.js +37 -27
  22. package/package.json +8 -2
  23. package/plugins/pinia.js +19 -0
  24. package/types/components/arrow-bar.d.ts +14 -0
  25. package/types/components/bar-group.d.ts +14 -0
  26. package/types/components/btn-bar.d.ts +16 -0
  27. package/types/components/chat-at-box.d.ts +22 -0
  28. package/types/components/chat-group-readed.d.ts +30 -0
  29. package/types/components/chat-item.d.ts +21 -0
  30. package/types/components/chat-message-item.d.ts +28 -0
  31. package/types/components/chat-record.d.ts +14 -0
  32. package/types/components/chat-upload.d.ts +58 -0
  33. package/types/components/friend-item.d.ts +19 -0
  34. package/types/components/group-item.d.ts +18 -0
  35. package/types/components/group-member-selector.d.ts +31 -0
  36. package/types/components/group-rtc-join.d.ts +31 -0
  37. package/types/components/head-image.d.ts +18 -0
  38. package/types/components/im-loading.d.ts +20 -0
  39. package/types/components/long-press-menu.d.ts +23 -0
  40. package/types/components/sample.d.ts +1 -3
  41. package/types/components/switch-bar.d.ts +19 -0
  42. package/types/components/virtual-scroller.d.ts +20 -0
@@ -0,0 +1,59 @@
1
+ <template>
2
+ <view class="arrow-bar">
3
+ <text class="icon iconfont" :class="icon" :style="{color: textColor}"></text>
4
+ <text class="title">{{ title }}</text>
5
+ <u-icon class="arrow" name="arrow-right" size="16"></u-icon>
6
+ </view>
7
+ </template>
8
+
9
+ <script setup lang="ts">
10
+ import { computed, ref } from 'vue';
11
+
12
+ interface Props {
13
+ title: string;
14
+ icon?: string;
15
+ }
16
+
17
+ const props = withDefaults(defineProps<Props>(), {
18
+ icon: ''
19
+ });
20
+
21
+ const colors = ref([
22
+ "#5daa31", "#c7515a", "#e03697", "#85029b",
23
+ "#c9b455", "#326eb6"
24
+ ]);
25
+
26
+ const textColor = computed(() => {
27
+ let hash = 0;
28
+ for (let i = 0; i < props.title.length; i++) {
29
+ hash += props.title.charCodeAt(i);
30
+ }
31
+ return colors.value[hash % colors.value.length];
32
+ });
33
+ </script>
34
+
35
+ <style lang="scss" scoped>
36
+ .arrow-bar {
37
+ width: 100%;
38
+ height: 90rpx;
39
+ font-size: $im-font-size;
40
+ color: $im-text-color;
41
+ margin-top: 5rpx;
42
+ background-color: white;
43
+ line-height: 90rpx;
44
+ display: flex;
45
+
46
+ .icon {
47
+ margin-left: 40rpx;
48
+ }
49
+
50
+ .title {
51
+ flex: 1;
52
+ margin-left: 10rpx;
53
+ }
54
+
55
+ .arrow {
56
+ margin-right: 40rpx;
57
+ }
58
+ }
59
+ </style>
@@ -0,0 +1,17 @@
1
+ <template>
2
+ <view class="bar-group">
3
+ <slot></slot>
4
+ </view>
5
+ </template>
6
+
7
+ <script setup lang="ts">
8
+ defineOptions({
9
+ name: "bar-group"
10
+ });
11
+ </script>
12
+
13
+ <style lang="scss" scoped>
14
+ .bar-group {
15
+ margin: 20rpx 0;
16
+ }
17
+ </style>
@@ -0,0 +1,60 @@
1
+ <template>
2
+ <view class="btn-bar" :style="style">
3
+ <text v-if="icon" class="icon iconfont" :class="icon"></text>
4
+ <text class="title">{{ title }}</text>
5
+ </view>
6
+ </template>
7
+
8
+ <script setup lang="ts">
9
+ interface Props {
10
+ title: string;
11
+ icon?: string;
12
+ type?: 'normal' | 'danger' | 'primary';
13
+ color?: string;
14
+ }
15
+
16
+ const props = withDefaults(defineProps<Props>(), {
17
+ icon: '',
18
+ type: 'normal',
19
+ color: '#000'
20
+ });
21
+
22
+ const style = computed(() => {
23
+ let color = props.color;
24
+
25
+ switch (props.type) {
26
+ case 'danger':
27
+ color = '#f14747';
28
+ break;
29
+ case 'primary':
30
+ color = '#35567f';
31
+ break;
32
+ }
33
+
34
+ return `color: ${color};`;
35
+ });
36
+ </script>
37
+
38
+ <style lang="scss" scoped>
39
+ .btn-bar {
40
+ width: 100%;
41
+ height: 100rpx;
42
+ margin-top: 5rpx;
43
+ background-color: white;
44
+ line-height: 100rpx;
45
+ text-align: center;
46
+ display: flex;
47
+ justify-content: center;
48
+
49
+ .icon {
50
+ font-size: 40rpx;
51
+ font-weight: 600;
52
+ margin-right: 10rpx;
53
+ }
54
+
55
+ .title {
56
+ font-size: 32rpx;
57
+ font-weight: 600;
58
+ }
59
+ }
60
+ </style>
@@ -0,0 +1,189 @@
1
+ <template>
2
+ <u-popup ref="popup" mode="bottom" @open="onChange" @close="onChange">
3
+ <view class="chat-at-box">
4
+ <view class="chat-at-top">
5
+ <view class="chat-at-tip"> 选择要提醒的人</view>
6
+ <button class="chat-at-btn" size="mini" @click="onClean()">清空 </button>
7
+ <button class="chat-at-btn" size="mini" @click="onOk()">确定({{ atUserIds.length }})
8
+ </button>
9
+ </view>
10
+ <scroll-view v-show="atUserIds.length > 0" scroll-x="true" scroll-left="120">
11
+ <view class="at-user-items">
12
+ <view v-for="m in checkedMembers" class="at-user-item" :key="m.userId">
13
+ <head-image :name="m.showNickName" :url="m.headImage" size="mini"></head-image>
14
+ </view>
15
+ </view>
16
+ </scroll-view>
17
+ <view class="search-bar">
18
+ <u-search v-model="searchText" :show-action="false" shape="round" placeholder="搜索"></u-search>
19
+ </view>
20
+ <view class="member-items">
21
+ <virtual-scroller :items="memberItems">
22
+ <template v-slot="{ item }">
23
+ <view class="member-item" @click="onSwitchChecked(item)">
24
+ <head-image :name="item.showNickName" :online="item.online" :url="item.headImage"
25
+ size="small"></head-image>
26
+ <view class="member-name">{{ item.showNickName }}</view>
27
+ <radio :checked="item.checked" :disabled="item.locked"
28
+ @click.stop="onSwitchChecked(item)" />
29
+ </view>
30
+ </template>
31
+ </virtual-scroller>
32
+ </view>
33
+ </view>
34
+ </u-popup>
35
+ </template>
36
+
37
+ <script setup lang="ts">
38
+ import { GroupMember } from '../../libs';
39
+
40
+ interface Props {
41
+ ownerId?: number;
42
+ members?: any[];
43
+ }
44
+
45
+ interface Emits {
46
+ (e: 'complete', userIds: number[]): void;
47
+ }
48
+
49
+ const props = withDefaults(defineProps<Props>(), {
50
+ ownerId: 0,
51
+ members: () => []
52
+ });
53
+
54
+ const emit = defineEmits<Emits>();
55
+
56
+ const searchText = ref("");
57
+ const showMembers = ref<GroupMember[]>([]);
58
+ const popup = ref()
59
+
60
+ const init = (userId: number, atUserIds: number[]) => {
61
+ showMembers.value = [];
62
+ // const userId = useUserStore().userInfo.id;
63
+
64
+ if (props.ownerId === userId) {
65
+ showMembers.value.push({
66
+ userId: -1,
67
+ showNickName: "全体成员"
68
+ });
69
+ }
70
+
71
+ props.members.forEach((m: GroupMember) => {
72
+ if (!m.quit && m.userId !== userId) {
73
+ m.checked = atUserIds.includes(m.userId);
74
+ showMembers.value.push(m);
75
+ }
76
+ });
77
+ };
78
+
79
+ const open = () => {
80
+ // 需要根据实际的 popup 引用调整
81
+ popup.value.open();
82
+ };
83
+
84
+ const onSwitchChecked = (member: GroupMember) => {
85
+ member.checked = !member.checked;
86
+ };
87
+
88
+ const onClean = () => {
89
+ showMembers.value.forEach((m) => {
90
+ m.checked = false;
91
+ });
92
+ };
93
+
94
+ const onOk = () => {
95
+ popup.value.close();
96
+ };
97
+
98
+ const onChange = (e: any) => {
99
+ if (!e.show) {
100
+ emit("complete", atUserIds.value);
101
+ }
102
+ };
103
+
104
+ const atUserIds = computed(() => {
105
+ return showMembers.value.filter(m => m.checked).map(m => m.userId);
106
+ });
107
+
108
+ const checkedMembers = computed(() => {
109
+ return showMembers.value.filter(m => m.checked);
110
+ });
111
+
112
+ const memberItems = computed(() => {
113
+ return showMembers.value.filter(m => m.showNickName.includes(searchText.value));
114
+ });
115
+
116
+ defineExpose({
117
+ init,
118
+ open
119
+ })
120
+ </script>
121
+
122
+ <style lang="scss" scoped>
123
+ .chat-at-box {
124
+ position: relative;
125
+ display: flex;
126
+ flex-direction: column;
127
+ background-color: white;
128
+ padding: 10rpx;
129
+ //border-radius: 15rpx;
130
+
131
+ .chat-at-top {
132
+ display: flex;
133
+ align-items: center;
134
+ height: 70rpx;
135
+ padding: 10rpx;
136
+
137
+ .chat-at-tip {
138
+ flex: 1;
139
+ }
140
+
141
+ .chat-at-btn {
142
+ margin-left: 10rpx;
143
+ }
144
+ }
145
+
146
+
147
+ .at-user-items {
148
+ display: flex;
149
+ align-items: center;
150
+ height: 90rpx;
151
+
152
+ .at-user-item {
153
+ padding: 3rpx;
154
+ }
155
+ }
156
+
157
+
158
+
159
+ .member-items {
160
+ position: relative;
161
+ flex: 1;
162
+ overflow: hidden;
163
+
164
+ .member-item {
165
+ height: 110rpx;
166
+ display: flex;
167
+ position: relative;
168
+ padding: 0 30rpx;
169
+ align-items: center;
170
+ background-color: white;
171
+ white-space: nowrap;
172
+ margin-bottom: 1px;
173
+
174
+ &:hover {
175
+ background-color: $im-bg-active;
176
+ }
177
+
178
+ .member-name {
179
+ flex: 1;
180
+ padding-left: 20rpx;
181
+ font-size: $im-font-size;
182
+ line-height: 60rpx;
183
+ white-space: nowrap;
184
+ overflow: hidden;
185
+ }
186
+ }
187
+ }
188
+ }
189
+ </style>
@@ -0,0 +1,172 @@
1
+ <template>
2
+ <u-popup ref="popup" mode="bottom">
3
+ <view class="chat-group-readed">
4
+ <view class="uni-padding-wrap uni-common-mt">
5
+ <u-tabs :current="current" :list="items" :show-bar="false" @change="onClickItem"></u-tabs>
6
+ </view>
7
+ <view class="content">
8
+ <view v-if="current === 0">
9
+ <virtual-scroller :items="readedMembers">
10
+ <template v-slot="{ item }">
11
+ <view class="member-item">
12
+ <head-image :name="item.showNickName" :online="item.online" :url="item.headImage"
13
+ :size="90"></head-image>
14
+ <view class="member-name">{{ item.showNickName }}</view>
15
+ </view>
16
+ </template>
17
+ </virtual-scroller>
18
+ </view>
19
+ <view v-if="current === 1">
20
+ <virtual-scroller :items="unreadMembers">
21
+ <template v-slot="{ item }">
22
+ <view class="member-item">
23
+ <head-image :name="item.showNickName" :online="item.online" :url="item.headImage"
24
+ :size="90"></head-image>
25
+ <view class="member-name">{{ item.showNickName }}</view>
26
+ </view>
27
+ </template>
28
+ </virtual-scroller>
29
+ </view>
30
+ </view>
31
+ </view>
32
+ </u-popup>
33
+ </template>
34
+
35
+ <script setup lang="ts">
36
+ import { Chat, Message } from '../../libs';
37
+
38
+ interface Props {
39
+ msgInfo: {
40
+ groupId: number;
41
+ id: number;
42
+ sendId: number;
43
+ };
44
+ groupMembers: any[];
45
+ }
46
+
47
+ interface Member {
48
+ userId: number;
49
+ quit?: boolean;
50
+ }
51
+
52
+ const props = defineProps<Props>();
53
+
54
+ const items = ref([{ name: '已读' }, { name: '未读' }]);
55
+ const current = ref(0);
56
+ const readedMembers = ref<Member[]>([]);
57
+ const unreadMembers = ref<Member[]>([]);
58
+ const popup = ref()
59
+
60
+ interface Emits {
61
+ (e: 'loaded', chatInfo: Chat, msgInfo: Message): void
62
+ }
63
+
64
+ const emit = defineEmits<Emits>()
65
+
66
+ /**
67
+ * @param userIds 已读用户IDs
68
+ */
69
+ const open = (userIds: number[]) => {
70
+ popup.value.open();
71
+ loadReadedUser(userIds);
72
+ };
73
+
74
+ const loadReadedUser = async (userIds: number[]) => {
75
+ readedMembers.value = [];
76
+ unreadMembers.value = [];
77
+
78
+ try {
79
+ // TODO:组件化
80
+ // const response = await requestx.get<number[]>(`/message/group/findReadedUsers?groupId=${props.msgInfo.groupId}&messageId=${props.msgInfo.id}`);
81
+ // const userIds = response.data;
82
+
83
+ props.groupMembers.forEach((member: Member) => {
84
+ // 发送者和已退群的不显示
85
+ if (member.userId === props.msgInfo.sendId || member.quit) {
86
+ return;
87
+ }
88
+ // 区分已读还是未读
89
+ if (userIds.find((userId: number) => member.userId === userId)) {
90
+ readedMembers.value.push(member);
91
+ } else {
92
+ unreadMembers.value.push(member);
93
+ }
94
+ });
95
+
96
+ items.value[0] = { name: `已读(${readedMembers.value.length})` };
97
+ items.value[1] = { name: `未读(${unreadMembers.value.length})` };
98
+
99
+ const chatInfo: Chat = {
100
+ type: 'GROUP',
101
+ targetId: props.msgInfo.groupId,
102
+ showName: '',
103
+ headImage: '',
104
+ isDnd: false,
105
+ lastContent: '',
106
+ unreadCount: 0,
107
+ hotMinIdx: 0,
108
+ readedMessageIdx: 0,
109
+ messages: [],
110
+ atMe: false,
111
+ atAll: false,
112
+ stored: false,
113
+ delete: false
114
+ };
115
+
116
+ const msgInfo: Message = {
117
+ id: props.msgInfo.id,
118
+ groupId: props.msgInfo.groupId,
119
+ readedCount: readedMembers.value.length,
120
+ type: 0,
121
+ content: ''
122
+ };
123
+
124
+ // TODO:组件化
125
+ // 更新已读人数
126
+ // chatStore.updateMessage(msgInfo, chatInfo);
127
+ emit('loaded', chatInfo, msgInfo)
128
+ } catch (error) {
129
+ console.error('加载已读用户失败:', error);
130
+ }
131
+ };
132
+
133
+ const onClickItem = (e: any) => {
134
+ current.value = e.currentIndex;
135
+ };
136
+
137
+ defineExpose({
138
+ open
139
+ })
140
+ </script>
141
+
142
+ <style lang="scss" scoped>
143
+ .chat-group-readed {
144
+ position: relative;
145
+ display: flex;
146
+ flex-direction: column;
147
+ background-color: white;
148
+ padding: 10rpx;
149
+
150
+ .member-item {
151
+ height: 120rpx;
152
+ display: flex;
153
+ position: relative;
154
+ padding: 0 30rpx;
155
+ align-items: center;
156
+ background-color: white;
157
+ white-space: nowrap;
158
+
159
+ .member-name {
160
+ flex: 1;
161
+ padding-left: 20rpx;
162
+ font-size: 30rpx;
163
+ font-weight: 600;
164
+ line-height: 60rpx;
165
+ white-space: nowrap;
166
+ overflow: hidden;
167
+ }
168
+ }
169
+
170
+
171
+ }
172
+ </style>
@@ -0,0 +1,215 @@
1
+ <template>
2
+ <view class="chat-item" :class="active ? 'active' : ''">
3
+ <!--rich-text中的表情包会屏蔽事件,所以这里用一个遮罩层捕获点击事件 -->
4
+ <view class="mask" @tap="showChatBox()"></view>
5
+ <view class="left">
6
+ <head-image :url="chat.headImage" :name="chat.showName" :online="online"></head-image>
7
+ </view>
8
+ <view class="chat-right">
9
+ <view class="chat-name">
10
+ <view class="chat-name-text">
11
+ <view>{{ chat.showName }}</view>
12
+ </view>
13
+ <view class="chat-time">{{ $datetime.toTimeText(chat.lastSendTime, true) }}</view>
14
+ </view>
15
+ <view class="chat-content">
16
+ <view class="chat-at-text">{{ atText }}</view>
17
+ <view class="chat-send-name" v-if="isShowSendName">{{ chat.sendNickName + ':&nbsp;' }}</view>
18
+ <view v-if="!isTextMessage" class="chat-content-text">{{ chat.lastContent }}</view>
19
+ <rich-text v-else class="chat-content-text" :nodes="nodesText"></rich-text>
20
+ <view v-if="chat.isDnd" class="icon iconfont icon-dnd"></view>
21
+ <u-badge v-else-if="chat.unreadCount > 0" :max="99" :value="chat.unreadCount" />
22
+ </view>
23
+ </view>
24
+ </view>
25
+ </template>
26
+
27
+ <script setup lang="ts">
28
+ import datetime from '../../utils/datetime'
29
+ import dom from '../../utils/dom'
30
+ import messageType from '../../utils/messageType'
31
+ import { MESSAGE_TYPE } from '../../utils/enums'
32
+ import emoji from '../../utils/emoji';
33
+
34
+ interface Props {
35
+ chat?: any;
36
+ index?: number;
37
+ active?: boolean;
38
+ online:boolean
39
+ }
40
+
41
+ const $datetime = datetime
42
+
43
+ const props = withDefaults(defineProps<Props>(), {
44
+ active: false
45
+ });
46
+
47
+ interface Emits {
48
+ (e: 'click', targetId: number): void
49
+ }
50
+
51
+ const emit = defineEmits<Emits>()
52
+
53
+ const isShowSendName = computed(() => {
54
+ if (!props.chat?.sendNickName) {
55
+ return false;
56
+ }
57
+ const size = props.chat.messages.length;
58
+ if (size === 0) {
59
+ return false;
60
+ }
61
+ // 只有群聊的普通消息需要显示名称
62
+ const lastMsg = props.chat.messages[size - 1];
63
+ return messageType.isNormal(lastMsg.type);
64
+ });
65
+
66
+ const atText = computed(() => {
67
+ if (props.chat?.atMe) {
68
+ return "[有人@我]";
69
+ } else if (props.chat?.atAll) {
70
+ return "[@全体成员]";
71
+ }
72
+ return "";
73
+ });
74
+
75
+ const isTextMessage = computed(() => {
76
+ if (!props.chat?.messages.length) {
77
+ return false;
78
+ }
79
+ const idx = props.chat.messages.length - 1;
80
+ const messageType = props.chat.messages[idx].type;
81
+ return messageType === MESSAGE_TYPE.TEXT;
82
+ });
83
+
84
+ const nodesText = computed(() => {
85
+ const text = dom.html2Escape(props.chat?.lastContent || '');
86
+ return emoji.transform(text, 'emoji-small');
87
+ });
88
+
89
+ // TODO:组件化(换为props.online)
90
+ // const online = computed(() => {
91
+ // if (props.chat?.type === 'PRIVATE') {
92
+ // const friend = useFriendStore().findFriend(props.chat.targetId);
93
+ // return friend && friend.online;
94
+ // }
95
+ // return false;
96
+ // });
97
+
98
+
99
+ // 方法
100
+
101
+ const showChatBox = () => {
102
+ // TODO:组件化
103
+ // 初始化期间进入会话会导致消息不刷新
104
+ // if (!useConfigStore().appInit || useChatStore().loading) {
105
+ // uni.showToast({
106
+ // title: "正在初始化页面,请稍后...",
107
+ // icon: 'none'
108
+ // });
109
+ // return;
110
+ // }
111
+ // uni.navigateTo({
112
+ // url: "/pages-chat/pages/chat/chat-box?targetId=" + props.chat.targetId
113
+ // });
114
+
115
+ emit('click',props.chat.targetId)
116
+ };
117
+ </script>
118
+
119
+ <style scoped lang="scss">
120
+ .chat-item {
121
+ height: 96rpx;
122
+ display: flex;
123
+ margin-bottom: 2rpx;
124
+ position: relative;
125
+ padding: 18rpx 20rpx;
126
+ align-items: center;
127
+ background-color: white;
128
+ white-space: nowrap;
129
+
130
+ &:hover {
131
+ background-color: $im-bg-active;
132
+ }
133
+
134
+ &.active {
135
+ background-color: $im-bg-active;
136
+ }
137
+
138
+ .mask {
139
+ position: absolute;
140
+ width: 100%;
141
+ height: 100%;
142
+ left: 0;
143
+ right: 0;
144
+ z-index: 99;
145
+ }
146
+
147
+ .left {
148
+ position: relative;
149
+ display: flex;
150
+ justify-content: center;
151
+ align-items: center;
152
+ width: 100rpx;
153
+ height: 100rpx;
154
+ }
155
+
156
+ .chat-right {
157
+ height: 100%;
158
+ flex: 1;
159
+ display: flex;
160
+ flex-direction: column;
161
+ justify-content: center;
162
+ padding-left: 20rpx;
163
+ text-align: left;
164
+ overflow: hidden;
165
+
166
+ .chat-name {
167
+ display: flex;
168
+
169
+ .chat-name-text {
170
+ flex: 1;
171
+ font-size: $im-font-size-large;
172
+ white-space: nowrap;
173
+ overflow: hidden;
174
+ display: flex;
175
+ align-items: center;
176
+ }
177
+
178
+ .chat-time {
179
+ font-size: $im-font-size-smaller-extra;
180
+ color: $im-text-color-lighter;
181
+ text-align: right;
182
+ white-space: nowrap;
183
+ overflow: hidden;
184
+ }
185
+ }
186
+
187
+ .chat-content {
188
+ display: flex;
189
+ font-size: $im-font-size-smaller;
190
+ color: $im-text-color-lighter;
191
+ padding-top: 8rpx;
192
+ align-items: center;
193
+
194
+ .chat-at-text {
195
+ color: $im-color-danger;
196
+ }
197
+
198
+ .chat-send-name {
199
+ font-size: $im-font-size-smaller;
200
+ }
201
+
202
+ .chat-content-text {
203
+ flex: 1;
204
+ white-space: nowrap;
205
+ overflow: hidden;
206
+ text-overflow: ellipsis;
207
+ }
208
+
209
+ .icon {
210
+ font-size: $im-font-size;
211
+ }
212
+ }
213
+ }
214
+ }
215
+ </style>