mdm-client 1.0.2 → 1.0.4

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 (43) hide show
  1. package/package.json +1 -1
  2. package/src/App.vue +72 -61
  3. package/src/assets/image/common/layout-active16.png +0 -0
  4. package/src/assets/image/common/layout-active25.png +0 -0
  5. package/src/assets/image/common/layout-active3.png +0 -0
  6. package/src/assets/image/common/layout-active9.png +0 -0
  7. package/src/assets/image/common/layout16.png +0 -0
  8. package/src/assets/image/common/layout25.png +0 -0
  9. package/src/assets/image/common/layout3.png +0 -0
  10. package/src/assets/image/common/layout9.png +0 -0
  11. package/src/assets/image/common/mirror.png +0 -0
  12. package/src/assets/image/common/platform_app_icon.png +0 -0
  13. package/src/assets/image/common/platform_mini_icon.png +0 -0
  14. package/src/assets/image/common/platform_pc_icon.png +0 -0
  15. package/src/assets/image/common/platform_volte_icon.png +0 -0
  16. package/src/assets/image/common/rotate_icon1.png +0 -0
  17. package/src/assets/image/common/rotate_icon2.png +0 -0
  18. package/src/assets/image/common/rotate_icon3.png +0 -0
  19. package/src/assets/image/common/rotate_icon4.png +0 -0
  20. package/src/assets/style/base.scss +5 -0
  21. package/src/components/LiveMulti/LiveMulti.vue +26 -15
  22. package/src/components/LiveMultipleMeeting/LiveMultipleMeeting.vue +1443 -228
  23. package/src/components/LiveMultipleMeeting/style/index.scss +145 -14
  24. package/src/components/LivePoint/LivePoint.vue +49 -211
  25. package/src/components/LivePointMeeting/LivePointMeeting.vue +159 -10
  26. package/src/components/LivePointMeeting/style/index.scss +35 -0
  27. package/src/components/MeetingReadyDialog/MeetingReadyDialog.vue +96 -14
  28. package/src/components/other/addressBook.vue +274 -37
  29. package/src/components/other/appointDialog.vue +1 -1
  30. package/src/components/other/customGroupDialog.vue +2 -1
  31. package/src/components/other/customLayout.vue +368 -202
  32. package/src/components/other/editGroupDialog.vue +2 -0
  33. package/src/components/other/layoutSwitch.vue +253 -37
  34. package/src/components/other/leadershipFocus.vue +422 -0
  35. package/src/components/other/leaveOptionDialog.vue +1 -1
  36. package/src/components/other/memberManage.vue +61 -4
  37. package/src/components/other/moreOptionDialog.vue +17 -1
  38. package/src/components/other/selectDialog.vue +1 -1
  39. package/src/components/other/selectSpecialDialog.vue +1 -1
  40. package/src/main.js +4 -4
  41. package/src/utils/api.js +28 -0
  42. package/src/utils/livekit/live-client-esm.js +1 -1
  43. package/src/utils/livekit/live-client-esm-old.js +0 -1
@@ -142,7 +142,8 @@ export default {
142
142
  isDurationCalc: true,
143
143
  isFootShow: false,
144
144
  footVisibleDuration: 3,
145
- isRoomConnectedHandled: false
145
+ isRoomConnectedHandled: false,
146
+ rotateDegreeMap: new Map(), // 存储与会者identity对应的旋转角度
146
147
  }
147
148
  },
148
149
  computed: {
@@ -164,6 +165,47 @@ export default {
164
165
  }
165
166
  },
166
167
  methods: {
168
+ // 根据 identity 和度数应用旋转到视频元素,并切换旋转按钮图标
169
+ applyRotation(identity, degree) {
170
+ try {
171
+ const norm = ((degree % 360) + 360) % 360; // 归一化到0-359
172
+ const videoElm = document.getElementById(`video-${identity}`);
173
+ if (videoElm) {
174
+ videoElm.style.transformOrigin = 'center center';
175
+ if (norm === 90 || norm === 270) {
176
+ const parent = videoElm.parentElement;
177
+ if (parent) {
178
+ const rect = parent.getBoundingClientRect();
179
+ videoElm.style.top = '50%';
180
+ videoElm.style.left = '50%';
181
+ videoElm.style.width = `${rect.height}px`;
182
+ videoElm.style.height = `${rect.width}px`;
183
+ videoElm.style.transform = `translate(-50%, -50%) rotate(${norm}deg)`;
184
+ videoElm.style.objectFit = 'cover';
185
+ }
186
+ } else {
187
+ videoElm.style.top = '0';
188
+ videoElm.style.left = '0';
189
+ videoElm.style.width = '100%';
190
+ videoElm.style.height = '100%';
191
+ videoElm.style.transform = `rotate(${norm}deg)`;
192
+ videoElm.style.objectFit = 'contain';
193
+ }
194
+ }
195
+
196
+ const rotateElm = document.getElementById(`rotate-${identity}`);
197
+ if (rotateElm) {
198
+ let clsIndex = 1;
199
+ if (norm === 0) clsIndex = 1;
200
+ else if (norm === 90) clsIndex = 2;
201
+ else if (norm === 180) clsIndex = 3;
202
+ else if (norm === 270) clsIndex = 4;
203
+ rotateElm.className = `rotate-icon rotate-icon${clsIndex}`;
204
+ }
205
+ } catch (err) {
206
+ console.error('applyRotation error', err);
207
+ }
208
+ },
167
209
  async createRoom() {
168
210
  if (!this.liveClient) {
169
211
  return;
@@ -719,6 +761,8 @@ export default {
719
761
  // 与会者状态变更
720
762
  // 视频元素
721
763
  let videoElm = document.getElementById(`video-${videoItem.identity}`);
764
+ // 旋转按钮元素
765
+ let rotateElm = document.getElementById(`rotate-${videoItem.identity}`);
722
766
  // 当与会者断开会议链接即remove为true
723
767
  if (videoItem.remove) {
724
768
  if (videoElm) {
@@ -731,6 +775,15 @@ export default {
731
775
  }
732
776
  this.removeFromParticipantList(videoItem);
733
777
 
778
+ // 清理旋转按钮与状态
779
+ if (rotateElm) {
780
+ try { videoDiv && videoDiv.removeChild(rotateElm); } catch (e) {}
781
+ rotateElm = null;
782
+ }
783
+ if (this.rotateDegreeMap.has(videoItem.identity)) {
784
+ this.rotateDegreeMap.delete(videoItem.identity);
785
+ }
786
+
734
787
  // 音视频轨道与video元素解绑
735
788
  if (videoItem.videoTrack) {
736
789
  videoItem.videoTrack.detach();
@@ -760,6 +813,22 @@ export default {
760
813
  let boardElm = document.getElementById(`board-${videoItem.identity}`);
761
814
  // 底部麦克风图标元素
762
815
  let microElm = document.getElementById(`microphone-${videoItem.identity}`);
816
+ // 根据platformID设定元素样式(与多方会议保持一致)
817
+ if (videoElm) {
818
+ if (
819
+ videoItem.metadata?.platformID == 1 ||
820
+ videoItem.metadata?.platformID == 4 ||
821
+ videoItem.metadata?.platformID == 7
822
+ ) {
823
+ videoElm.style.objectFit = 'contain';
824
+ } else {
825
+ if (videoItem?.source == 'camera') {
826
+ videoElm.style.objectFit = 'cover';
827
+ } else {
828
+ videoElm.style.objectFit = 'contain';
829
+ }
830
+ }
831
+ }
763
832
  // 声明麦克风按钮点击事件回调
764
833
  const unableMicrophone = () => {
765
834
  this.liveClient.changeParticipantMicrophoneStatus(videoItem.identity, true);
@@ -776,17 +845,43 @@ export default {
776
845
  let layoutNormalElm = document.querySelector("#point-meeting-contain .layout-normal");
777
846
  let localChild = layoutBlurElm.firstElementChild;
778
847
  let currentEle = document.getElementById(`participant-${videoItem.identity}`);
779
- if (localChild) {
780
- if (localChild.id === `participant-${this.localIdentity}`) {
781
- return;
848
+ // 核心逻辑:点击任意非焦点或焦点元素,切换位置
849
+ // 期望行为:layoutBlurElm 始终只保留一个“缩略/小画面”元素(本地),点击其他元素切换焦点并将之前焦点移到小画面
850
+ if (!layoutBlurElm || !layoutNormalElm || !currentEle) return;
851
+
852
+ const isCurrentInBlur = currentEle.parentElement === layoutBlurElm;
853
+ const isCurrentInNormal = currentEle.parentElement === layoutNormalElm;
854
+ const blurFirst = layoutBlurElm.firstElementChild;
855
+ const normalFirst = layoutNormalElm.firstElementChild;
856
+
857
+ // 如果当前元素已经在小画面区域(layoutBlurElm),则尝试与 normalFirst 交换(如果存在)
858
+ if (isCurrentInBlur) {
859
+ if (normalFirst) {
860
+ // 将 normalFirst 移到小画面
861
+ layoutBlurElm.appendChild(normalFirst);
862
+ // 将当前小画面移到大画面顶部
863
+ layoutNormalElm.insertBefore(currentEle, layoutNormalElm.firstChild ?? null);
782
864
  } else {
783
- layoutNormalElm.insertBefore(localChild, layoutNormalElm.firstChild ?? null);
784
- if (currentEle) {
785
- layoutBlurElm.appendChild(currentEle);
786
- }
865
+ // 没有大画面,直接保持当前不动
866
+ return;
787
867
  }
788
- } else {
868
+ return;
869
+ }
870
+
871
+ // 当前在大画面区域
872
+ if (isCurrentInNormal) {
873
+ // 如果小画面里有元素,则和它交换
874
+ if (blurFirst) {
875
+ layoutNormalElm.insertBefore(blurFirst, layoutNormalElm.firstChild ?? null);
876
+ }
877
+ // 将点击的大画面元素移入小画面
789
878
  layoutBlurElm.appendChild(currentEle);
879
+ return;
880
+ }
881
+
882
+ // 兜底:如果元素不在两者内(理论上不发生),直接放入大画面
883
+ if (!isCurrentInBlur && !isCurrentInNormal) {
884
+ layoutNormalElm.insertBefore(currentEle, layoutNormalElm.firstChild ?? null);
790
885
  }
791
886
  };
792
887
  // 为与会者元素绑定事件
@@ -810,6 +905,55 @@ export default {
810
905
  `;
811
906
  videoDiv.appendChild(describeElm);
812
907
  }
908
+ // 仅在视频通话(roomMode=auto => 此组件中对应 meetingType==='video')、且为摄像头画面、且 platformID 为 4 时显示旋转按钮
909
+ if (
910
+ videoItem.isCameraEnabled &&
911
+ !videoItem.isScreenShareEnabled &&
912
+ videoItem?.metadata?.platformID === 4
913
+ ) {
914
+ if (!rotateElm) {
915
+ rotateElm = document.createElement('div');
916
+ rotateElm.id = `rotate-${videoItem.identity}`;
917
+ rotateElm.className = 'rotate-icon rotate-icon1';
918
+ videoDiv && videoDiv.appendChild(rotateElm);
919
+ }
920
+ // 绑定(或重绑)事件处理,阻止冒泡,避免触发父容器的setBlurVideoLeft
921
+ const handleRotateClick = (event) => {
922
+ event?.stopPropagation?.();
923
+ event?.preventDefault?.();
924
+ const current = this.rotateDegreeMap.get(videoItem.identity) || 0;
925
+ const next = (current + 90) % 360;
926
+ this.rotateDegreeMap.set(videoItem.identity, next);
927
+ this.applyRotation(videoItem.identity, next);
928
+ };
929
+ rotateElm.onclick = handleRotateClick;
930
+ // 进一步保险:阻止按下就冒泡(处理某些浏览器差异与长按)
931
+ rotateElm.onmousedown = (e) => { e?.stopPropagation?.(); };
932
+ rotateElm.ontouchstart = (e) => { e?.stopPropagation?.(); };
933
+ // 初始化角度并应用
934
+ if (!this.rotateDegreeMap.has(videoItem.identity)) {
935
+ this.rotateDegreeMap.set(videoItem.identity, 0);
936
+ }
937
+ this.applyRotation(videoItem.identity, this.rotateDegreeMap.get(videoItem.identity));
938
+ } else {
939
+ // 不满足条件则移除旋转按钮并清除状态
940
+ if (rotateElm) {
941
+ try { videoDiv && videoDiv.removeChild(rotateElm); } catch (e) {}
942
+ rotateElm = null;
943
+ }
944
+ if (this.rotateDegreeMap.has(videoItem.identity)) {
945
+ this.rotateDegreeMap.delete(videoItem.identity);
946
+ }
947
+ // 同时复原视频元素的旋转
948
+ if (videoElm) {
949
+ videoElm.style.transform = '';
950
+ videoElm.style.transformOrigin = '';
951
+ videoElm.style.top = '';
952
+ videoElm.style.left = '';
953
+ videoElm.style.width = '';
954
+ videoElm.style.height = '';
955
+ }
956
+ }
813
957
  } else {
814
958
  if (describeElm) {
815
959
  videoDiv.removeChild(describeElm);
@@ -851,6 +995,10 @@ export default {
851
995
  }
852
996
  }
853
997
  this.addToParticipantList(videoItem);
998
+ // 每次渲染时,如存在旋转角度记录,则应用旋转(避免重新渲染后丢失旋转效果)
999
+ if (this.rotateDegreeMap.has(videoItem.identity)) {
1000
+ this.applyRotation(videoItem.identity, this.rotateDegreeMap.get(videoItem.identity));
1001
+ }
854
1002
  },
855
1003
  renderAudioItem(audioItem) {
856
1004
  console.log("语音通话渲染" + audioItem.identity + ":", audioItem);
@@ -887,7 +1035,8 @@ export default {
887
1035
  }
888
1036
  this.addToParticipantList(audioItem);
889
1037
  }
890
- let audioElm = document.getElementById(`audio=${audioItem.identity}`);
1038
+ // 修复ID选择器错误:应为 audio-${identity}
1039
+ let audioElm = document.getElementById(`audio-${audioItem.identity}`);
891
1040
  if (audioItem.remove) {
892
1041
  if (audioElm) {
893
1042
  audioElm.srcObject = null;
@@ -110,6 +110,8 @@
110
110
  height: 100%;
111
111
  position: relative;
112
112
  border-radius: 6px;
113
+ background: var(--dialog-bg);
114
+ overflow: hidden; // 旋转后裁剪溢出内容,避免页面出现“怪异”形态
113
115
  .p-video {
114
116
  position: absolute;
115
117
  width: 100%;
@@ -119,6 +121,39 @@
119
121
  z-index: 10;
120
122
  object-fit: contain;
121
123
  border-radius: 6px;
124
+ backface-visibility: hidden; // 避免3D旋转引发的锯齿闪烁
125
+ will-change: transform; // 提示浏览器优化旋转
126
+ }
127
+ // 参会者视频旋转按钮(与 LiveMultipleMeeting 保持一致)
128
+ .rotate-icon {
129
+ visibility: hidden; // 默认隐藏,悬浮显示
130
+ cursor: pointer;
131
+ position: absolute;
132
+ top: 10px;
133
+ left: 10px;
134
+ z-index: 50;
135
+ aspect-ratio: 1/1;
136
+ width: auto;
137
+ height: 3%;
138
+ min-height: 30px;
139
+ &1 {
140
+ background: var(--rotate-icon1) no-repeat center / 100% 100%;
141
+ }
142
+ &2 {
143
+ background: var(--rotate-icon2) no-repeat center / 100% 100%;
144
+ }
145
+ &3 {
146
+ background: var(--rotate-icon3) no-repeat center / 100% 100%;
147
+ }
148
+ &4 {
149
+ background: var(--rotate-icon4) no-repeat center / 100% 100%;
150
+ }
151
+ }
152
+ // 悬浮显示旋转按钮
153
+ &:hover {
154
+ .rotate-icon {
155
+ visibility: visible;
156
+ }
122
157
  }
123
158
  .describe {
124
159
  position: absolute;
@@ -19,6 +19,10 @@
19
19
  <div class="video-contain">
20
20
  <div class="avatar"></div>
21
21
  <video id="video-ele" class="video-ele" muted autoplay v-show="cameraStatus"></video>
22
+ <div class="mirror-switch" @click="setMirror">
23
+ <img class="mirror-switch-icon" src="../../assets/image/common/mirror.png" />
24
+ <span>镜像</span>
25
+ </div>
22
26
  </div>
23
27
  <div class="tool-bar">
24
28
  <div class="tool-bar-group">
@@ -124,6 +128,7 @@ export default {
124
128
  videoSelectShow: false,
125
129
  outputSelectShow: false,
126
130
  allTracks: [],
131
+ isMirror: false,
127
132
  audioDevices: [],
128
133
  outputDevices: [],
129
134
  videoDevices: [],
@@ -150,16 +155,16 @@ export default {
150
155
  if (newVal) {
151
156
  constrict = {
152
157
  audio: this.activeDevice?.audioInputDevice
153
- ? { deviceId: this.activeDevice.audioInputDevice }
158
+ ? { deviceId: { exact: this.activeDevice.audioInputDevice } }
154
159
  : true,
155
160
  video: this.activeDevice?.videoDevice
156
- ? { deviceId: this.activeDevice.videoDevice }
161
+ ? { deviceId: { exact: this.activeDevice.videoDevice } }
157
162
  : { facingMode: this.curFacingMode },
158
163
  };
159
164
  } else {
160
165
  constrict = {
161
166
  audio: this.activeDevice?.audioInputDevice
162
- ? { deviceId: this.activeDevice.audioInputDevice }
167
+ ? { deviceId: { exact: this.activeDevice.audioInputDevice } }
163
168
  : true,
164
169
  video: false,
165
170
  };
@@ -167,6 +172,33 @@ export default {
167
172
  await this.getDefaultDeviceStream(constrict);
168
173
  },
169
174
  },
175
+ microphoneStatus: {
176
+ handler: async function (newVal) {
177
+ let constrict = null;
178
+ if (newVal) {
179
+ constrict = {
180
+ audio: this.activeDevice?.audioInputDevice
181
+ ? { deviceId: { exact: this.activeDevice.audioInputDevice } }
182
+ : true,
183
+ video: this.cameraStatus
184
+ ? this.activeDevice?.videoDevice
185
+ ? { deviceId: { exact: this.activeDevice.videoDevice } }
186
+ : { facingMode: this.curFacingMode }
187
+ : false,
188
+ };
189
+ } else {
190
+ constrict = {
191
+ audio: false,
192
+ video: this.cameraStatus
193
+ ? this.activeDevice?.videoDevice
194
+ ? { deviceId: { exact: this.activeDevice.videoDevice } }
195
+ : { facingMode: this.curFacingMode }
196
+ : false,
197
+ };
198
+ }
199
+ await this.getDefaultDeviceStream(constrict);
200
+ },
201
+ },
170
202
  },
171
203
  mounted() {
172
204
  this.init();
@@ -198,6 +230,7 @@ export default {
198
230
  this.showMessageInstance.message("error", "会议名称不能超过20个字符");
199
231
  return;
200
232
  }
233
+ this.streamPause();
201
234
  this.$emit("launchMeeting", {
202
235
  roomName: this.roomName,
203
236
  cameraStatus: this.cameraStatus,
@@ -206,6 +239,7 @@ export default {
206
239
  audioInputDevice: this.activeDevice.audioInputDevice,
207
240
  audioOutputDevice: this.activeDevice.audioOutputDevice,
208
241
  videoDevice: this.activeDevice.videoDevice,
242
+ isMirror: this.isMirror,
209
243
  });
210
244
  this.$emit("close");
211
245
  },
@@ -215,6 +249,7 @@ export default {
215
249
  this.showMessageInstance.message("error", "会议ID不能为空");
216
250
  return;
217
251
  }
252
+ this.streamPause();
218
253
  this.$emit("joinMeeting", {
219
254
  roomNum: this.roomNum,
220
255
  cameraStatus: this.cameraStatus,
@@ -223,10 +258,23 @@ export default {
223
258
  audioInputDevice: this.activeDevice.audioInputDevice,
224
259
  audioOutputDevice: this.activeDevice.audioOutputDevice,
225
260
  videoDevice: this.activeDevice.videoDevice,
261
+ isMirror: this.isMirror,
226
262
  });
227
263
  this.$emit("close");
228
264
  },
229
265
 
266
+ setMirror() {
267
+ this.isMirror = !this.isMirror;
268
+ this.$nextTick(() => {
269
+ let videoDom = document.getElementById("video-ele");
270
+ if (videoDom) {
271
+ this.isMirror
272
+ ? (videoDom.style.transform = "rotateY(180deg)")
273
+ : (videoDom.style.transform = "rotateY(0deg)");
274
+ }
275
+ });
276
+ },
277
+
230
278
  async getDeviceList() {
231
279
  try {
232
280
  const { audioDevices, videoDevices, outputDevices } = await LiveClient.getDeviceList();
@@ -312,11 +360,16 @@ export default {
312
360
  }
313
361
  },
314
362
 
363
+ sleep(ms) {
364
+ return new Promise((resolve) => setTimeout(resolve, ms));
365
+ },
366
+
315
367
  async getDefaultDeviceStream(constrict) {
316
368
  try {
369
+ this.clearAllTrack();
370
+ await this.sleep(500);
317
371
  const stream = await navigator.mediaDevices.getUserMedia(constrict);
318
372
  let videoEle = document.querySelector("#video-ele");
319
- this.clearAllTrack();
320
373
  if (videoEle) {
321
374
  this.allTracks = stream.getTracks();
322
375
  this.getActiveDeviceId(stream);
@@ -325,8 +378,10 @@ export default {
325
378
  videoEle.play();
326
379
  };
327
380
  }
381
+ return stream;
328
382
  } catch (err) {
329
- throw new Error(err);
383
+ console.error("Failed to get media stream:", err);
384
+ this.showMessageInstance.message("error", "获取设备媒体流失败,请检查浏览器权限状态或者设备是否可用");
330
385
  }
331
386
  },
332
387
 
@@ -343,12 +398,16 @@ export default {
343
398
  async initVideoStream() {
344
399
  this.curFacingMode = "user";
345
400
  const constrict = {
346
- audio: this.microphoneStatus,
401
+ audio: this.microphoneStatus
402
+ ? this.activeDevice?.audioInputDevice
403
+ ? { deviceId: { exact: this.activeDevice.audioInputDevice } }
404
+ : true
405
+ : false,
347
406
  video: this.cameraStatus
348
- ? {
349
- facingMode: this.curFacingMode,
350
- }
351
- : this.cameraStatus,
407
+ ? this.activeDevice?.videoDevice
408
+ ? { deviceId: { exact: this.activeDevice.videoDevice } }
409
+ : { facingMode: this.curFacingMode }
410
+ : false,
352
411
  };
353
412
  await this.getDefaultDeviceStream(constrict);
354
413
  },
@@ -356,10 +415,10 @@ export default {
356
415
  async handleAudioInputDeviceChoice(e) {
357
416
  if (e && e !== this.activeDevice?.audioInputDevice) {
358
417
  const constrict = {
359
- audio: this.microphoneStatus ? { deviceId: e } : false,
418
+ audio: this.microphoneStatus ? { deviceId: { exact: e } } : false,
360
419
  video: this.cameraStatus
361
420
  ? this.activeDevice?.videoDevice
362
- ? { deviceId: this.activeDevice.videoDevice }
421
+ ? { deviceId: { exact: this.activeDevice.videoDevice } }
363
422
  : { facingMode: this.curFacingMode }
364
423
  : this.cameraStatus,
365
424
  };
@@ -378,10 +437,10 @@ export default {
378
437
  const constrict = {
379
438
  audio: this.microphoneStatus
380
439
  ? this.activeDevice?.audioInputDevice
381
- ? { deviceId: this.activeDevice.audioInputDevice }
440
+ ? { deviceId: { exact: this.activeDevice.audioInputDevice } }
382
441
  : this.microphoneStatus
383
442
  : this.microphoneStatus,
384
- video: this.cameraStatus ? { deviceId: e } : this.cameraStatus,
443
+ video: this.cameraStatus ? { deviceId: { exact: e } } : this.cameraStatus,
385
444
  };
386
445
  await this.getDefaultDeviceStream(constrict);
387
446
  }
@@ -493,6 +552,29 @@ export default {
493
552
  top: 0;
494
553
  object-fit: cover;
495
554
  }
555
+
556
+ .mirror-switch {
557
+ cursor: pointer;
558
+ position: absolute;
559
+ top: 23px;
560
+ right: 17px;
561
+ background: rgba(28, 36, 47, 0.6);
562
+ border-radius: 8px 8px 8px 8px;
563
+ display: flex;
564
+ align-items: center;
565
+ height: 40px;
566
+ padding: 0 10px;
567
+ font-weight: 400;
568
+ font-size: 12px;
569
+ z-index: 10;
570
+ color: #fff;
571
+
572
+ &-icon {
573
+ width: 16px;
574
+ height: 14px;
575
+ margin-right: 6px;
576
+ }
577
+ }
496
578
  }
497
579
 
498
580
  .tool-bar {