cloud-web-corejs 1.0.54-dev.636 → 1.0.54-dev.637

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.
@@ -0,0 +1,768 @@
1
+ <template>
2
+ <div class="call-center-float" :style="panelStyle">
3
+ <div class="call-center-float__header" @mousedown="onDragStart">
4
+ <div class="call-center-float__status-row">
5
+ <span class="call-center-float__label">座席状态:</span>
6
+ <span class="call-center-float__status" :class="statusClass">{{
7
+ agentStatusText
8
+ }}</span>
9
+ <span
10
+ class="call-center-float__label call-center-float__label--incoming"
11
+ >来电号码:</span
12
+ >
13
+ <span class="call-center-float__incoming">{{
14
+ incomingNumber || "—"
15
+ }}</span>
16
+ </div>
17
+ <button
18
+ type="button"
19
+ class="call-center-float__toggle"
20
+ :title="collapsed ? '展开' : '收起'"
21
+ @mousedown.stop
22
+ @click="collapsed = !collapsed"
23
+ >
24
+ {{ collapsed ? "▲" : "▼" }}
25
+ </button>
26
+ </div>
27
+
28
+ <div v-show="!collapsed" class="call-center-float__body">
29
+ <input
30
+ v-model.trim="dialNumber"
31
+ class="call-center-float__input"
32
+ type="text"
33
+ placeholder="请输入外呼号码"
34
+ :disabled="!isOnWork"
35
+ @keyup.enter="handleDial"
36
+ />
37
+ <button
38
+ type="button"
39
+ class="call-center-float__btn call-center-float__btn--dial"
40
+ :disabled="!canDial"
41
+ title="外呼"
42
+ @click="handleDial"
43
+ >
44
+ <i class="call-center-float__phone-icon" />
45
+ </button>
46
+ <button
47
+ v-for="btn in actionButtons"
48
+ :key="btn.key"
49
+ type="button"
50
+ class="call-center-float__btn call-center-float__btn--action"
51
+ :disabled="btn.disabled"
52
+ @click="btn.handler"
53
+ >
54
+ {{ btn.label }}
55
+ </button>
56
+ </div>
57
+
58
+ <audio id="webcam" autoplay style="display: none" />
59
+ </div>
60
+ </template>
61
+
62
+ <script>
63
+ import {
64
+ getAgentStateMeta,
65
+ canAgentDial,
66
+ isRestAgentState,
67
+ resolveAgentStateFromPayload,
68
+ } from "./agentState";
69
+
70
+ const RING_MSG_TYPES = [
71
+ "PhoneStateUserRing",
72
+ "PhoneStateAgentRing",
73
+ "PhoneStateRing",
74
+ "StartService",
75
+ ];
76
+
77
+ const CALL_ACTIVE_MSG_TYPES = [
78
+ "StartService",
79
+ "PhoneStateAgentRing",
80
+ "PhoneStateAgentConnected",
81
+ "PhoneStateUserConnected",
82
+ "PhoneStateUserRing",
83
+ "PhoneStateRing",
84
+ "PhoneStateConnected",
85
+ ];
86
+
87
+ const CALL_END_MSG_TYPES = [
88
+ "StopService",
89
+ "AgentHangUp",
90
+ "PhoneStateDisConnected",
91
+ ];
92
+
93
+ export default {
94
+ name: "CallCenterFloat",
95
+ props: {
96
+ option: Object,
97
+ },
98
+ data() {
99
+ return {
100
+ sdkReady: false,
101
+ configReady: false,
102
+ collapsed: false,
103
+ agentState: "-1",
104
+ incomingNumber: "",
105
+ dialNumber: "",
106
+ currentCall: null,
107
+ isOnWork: false,
108
+ position: { left: null, right: 24, bottom: 24 },
109
+ dragging: false,
110
+ dragOffset: { x: 0, y: 0 },
111
+ pendingAgentState: null,
112
+ pendingAgentStateTimer: null,
113
+ };
114
+ },
115
+ computed: {
116
+ agentStatusText() {
117
+ if (!this.configReady) {
118
+ return "未配置";
119
+ }
120
+ if (!this.sdkReady) {
121
+ return "未就绪";
122
+ }
123
+ return getAgentStateMeta(this.agentState).label;
124
+ },
125
+ statusClass() {
126
+ return getAgentStateMeta(this.agentState).className;
127
+ },
128
+ panelStyle() {
129
+ const style = {
130
+ bottom: `${this.position.bottom}px`,
131
+ zIndex: 10000,
132
+ };
133
+ if (this.position.left !== null) {
134
+ style.left = `${this.position.left}px`;
135
+ style.right = "auto";
136
+ } else {
137
+ style.right = `${this.position.right}px`;
138
+ style.left = "auto";
139
+ }
140
+ return style;
141
+ },
142
+ canDial() {
143
+ return (
144
+ this.isOnWork &&
145
+ !!this.dialNumber &&
146
+ canAgentDial(this.agentState)
147
+ );
148
+ },
149
+ actionButtons() {
150
+ return [
151
+ {
152
+ key: "hangup",
153
+ label: "挂机",
154
+ disabled: !this.currentCall,
155
+ handler: this.handleHangUp,
156
+ },
157
+ {
158
+ key: "onWork",
159
+ label: "上班",
160
+ disabled: this.isOnWork,
161
+ handler: this.handleOnWork,
162
+ },
163
+ {
164
+ key: "offWork",
165
+ label: "下班",
166
+ disabled: !this.isOnWork,
167
+ handler: this.handleOffWork,
168
+ },
169
+ {
170
+ key: "idle",
171
+ label: "空闲",
172
+ disabled: !this.isOnWork,
173
+ handler: () => this.handleUpdateState("1"),
174
+ },
175
+ {
176
+ key: "busy",
177
+ label: "忙碌",
178
+ disabled: !this.isOnWork,
179
+ handler: () => this.handleUpdateState("0"),
180
+ },
181
+ {
182
+ key: "rest",
183
+ label: "小休",
184
+ disabled: !this.isOnWork,
185
+ handler: () => this.handleUpdateState("114"),
186
+ },
187
+ {
188
+ key: "meal",
189
+ label: "用餐",
190
+ disabled: !this.isOnWork,
191
+ handler: () => this.handleUpdateState("118"),
192
+ },
193
+ ];
194
+ },
195
+ },
196
+ mounted() {
197
+ this.initCallCenter();
198
+ document.addEventListener("mousemove", this.onDragMove);
199
+ document.addEventListener("mouseup", this.onDragEnd);
200
+ },
201
+ beforeDestroy() {
202
+ document.removeEventListener("mousemove", this.onDragMove);
203
+ document.removeEventListener("mouseup", this.onDragEnd);
204
+ this.clearPendingAgentStateTimer();
205
+ },
206
+ methods: {
207
+ getServ() {
208
+ return window.zcVertoServ;
209
+ },
210
+ isConfigReady() {
211
+ const cfg = this.option;
212
+ return !!(
213
+ cfg.client_id &&
214
+ cfg.client_secret &&
215
+ cfg.companyId &&
216
+ cfg.agentId
217
+ );
218
+ },
219
+ initCallCenter() {
220
+ this.configReady = this.isConfigReady();
221
+ if (!this.configReady) {
222
+ return;
223
+ }
224
+ const serv = this.getServ();
225
+ if (!serv || typeof serv.ininServ !== "function") {
226
+ console.warn(
227
+ "[CallCenter] zcVertoServ 未加载,请检查 index.html 脚本引用"
228
+ );
229
+ return;
230
+ }
231
+
232
+ this.sdkReady = true;
233
+ const option = this.option;
234
+ const params = {
235
+ client_id: option.client_id,
236
+ client_secret: option.client_secret,
237
+ companyId: option.companyId,
238
+ appId: option.appId,
239
+
240
+ agentId: option.agentId,
241
+ groupId: option.groupId,
242
+ voipAccount: option.voipAccount,
243
+ callWay: option.callWay,
244
+ };
245
+
246
+ serv.ininServ(params, this.onSocketMessage, this.onSdkError);
247
+ serv.getToken((res) => {
248
+ if (!res || !res.access_token) {
249
+ this.notifyError("获取呼叫中心 Token 失败");
250
+ return;
251
+ }
252
+ serv.queryAgentState((rs) => {
253
+ if (rs && String(rs.retCode) === "000000" && rs.item) {
254
+ const state = resolveAgentStateFromPayload(rs.item);
255
+ if (state !== null) {
256
+ this.applyAgentState(state);
257
+ }
258
+ if (Number(rs.item.agentState) >= 0) {
259
+ this.isOnWork = true;
260
+ serv.reConnect();
261
+ }
262
+ }
263
+ });
264
+ });
265
+ /* serv.queryAgentState((rs) => {
266
+ debugger;
267
+ if (rs && rs.retCode === "000000" && rs.item) {
268
+ this.applyAgentState(rs.item.agentState);
269
+ if (Number(rs.item.agentState) >= 0) {
270
+ this.isOnWork = true;
271
+ serv.reConnect();
272
+ }
273
+ }
274
+ }); */
275
+ },
276
+ onSdkError(msg) {
277
+ this.notifyError(msg || "呼叫中心操作失败");
278
+ },
279
+ onSocketMessage(res) {
280
+ if (!res || !res.data) {
281
+ return;
282
+ }
283
+ let data;
284
+ try {
285
+ data = JSON.parse(res.data);
286
+ } catch (e) {
287
+ return;
288
+ }
289
+
290
+ if (!data.msgType) {
291
+ return;
292
+ }
293
+
294
+ let option = this.option;
295
+
296
+ switch (data.msgType) {
297
+ case "onLine":
298
+ break;
299
+ case "offLine":
300
+ case "repeat":
301
+ this.isOnWork = false;
302
+ this.applyAgentState("-1");
303
+ this.currentCall = null;
304
+ if (data.msgType === "repeat") {
305
+ this.notifyError("呼叫中心账号已在其他窗口登录");
306
+ }
307
+ break;
308
+ case "AgentStateIdle":
309
+ this.applyAgentState(
310
+ this.resolveAgentStateFromSocket(data) || "1"
311
+ );
312
+ break;
313
+ case "AgentStateBusy":
314
+ this.applyAgentStateFromBusyMessage(data);
315
+ break;
316
+ case "AgentStateAfterWorking":
317
+ this.applyAgentState(
318
+ this.resolveAgentStateFromSocket(data) || "115"
319
+ );
320
+ break;
321
+ case "AgentStateTalking":
322
+ this.applyCallActiveState(data);
323
+ break;
324
+ case "AgentStateLogOut":
325
+ this.isOnWork = false;
326
+ this.applyAgentState("-1");
327
+ this.currentCall = null;
328
+ break;
329
+ case "StopService":
330
+ case "AgentHangUp":
331
+ this.restoreAgentStateAfterCall(data);
332
+ break;
333
+ case "PhoneStateDisConnected":
334
+ if (this.currentCall) {
335
+ this.restoreAgentStateAfterCall(data);
336
+ }
337
+ break;
338
+ default:
339
+ if (CALL_ACTIVE_MSG_TYPES.includes(data.msgType)) {
340
+ this.applyCallActiveState(data);
341
+ }
342
+ break;
343
+ }
344
+
345
+ if (RING_MSG_TYPES.includes(data.msgType)) {
346
+ this.incomingNumber = this.resolveIncomingNumber(data);
347
+ if (
348
+ String(this.option.callWay) === "1" &&
349
+ data.direction === 1 &&
350
+ this.voipAccount
351
+ ) {
352
+ this.incomingNumber = this.voipAccount;
353
+ }
354
+ }
355
+
356
+ if (data.callId && !CALL_END_MSG_TYPES.includes(data.msgType)) {
357
+ this.currentCall = data;
358
+ if (!this.incomingNumber) {
359
+ this.incomingNumber = this.resolveIncomingNumber(data);
360
+ }
361
+ if (
362
+ data.msgType === "PhoneStateAgentRing" &&
363
+ String(this.option.callWay) === "1"
364
+ ) {
365
+ this.getServ().agentListen();
366
+ }
367
+ }
368
+ },
369
+ resolveIncomingNumber(data) {
370
+ const direction = Number(data.direction);
371
+ if (direction === 0) {
372
+ return data.caller || data.callee || "";
373
+ }
374
+ if (direction === 1) {
375
+ return data.callee || data.caller || "";
376
+ }
377
+ return data.caller || data.callee || "";
378
+ },
379
+ applyAgentState(state) {
380
+ this.agentState =
381
+ state === undefined || state === null ? "-1" : String(state);
382
+ },
383
+ resolveAgentStateFromSocket(data) {
384
+ return resolveAgentStateFromPayload(data);
385
+ },
386
+ applyCallActiveState(data) {
387
+ this.pendingAgentState = null;
388
+ this.clearPendingAgentStateTimer();
389
+ const socketState = data ? this.resolveAgentStateFromSocket(data) : null;
390
+ this.applyAgentState(socketState === "3" ? socketState : "3");
391
+ },
392
+ restoreAgentStateAfterCall(data) {
393
+ this.currentCall = null;
394
+ this.incomingNumber = "";
395
+ const socketState = data ? this.resolveAgentStateFromSocket(data) : null;
396
+ if (socketState !== null && socketState !== "3") {
397
+ this.applyAgentState(socketState);
398
+ return;
399
+ }
400
+ this.syncAgentStateAfterCall();
401
+ },
402
+ syncAgentStateAfterCall() {
403
+ const serv = this.getServ();
404
+ if (!serv || !this.isOnWork) {
405
+ return;
406
+ }
407
+ serv.queryAgentState((rs) => {
408
+ if (rs && String(rs.retCode) === "000000" && rs.item) {
409
+ const state = resolveAgentStateFromPayload(rs.item);
410
+ if (state !== null && state !== "3") {
411
+ this.applyAgentState(state);
412
+ return;
413
+ }
414
+ }
415
+ this.applyAgentState("1");
416
+ });
417
+ },
418
+ applyAgentStateFromBusyMessage(data) {
419
+ if (this.currentCall || String(this.agentState) === "3") {
420
+ const talkingState = this.resolveAgentStateFromSocket(data);
421
+ if (talkingState === "3") {
422
+ this.applyAgentState("3");
423
+ }
424
+ return;
425
+ }
426
+ let socketState = this.resolveAgentStateFromSocket(data);
427
+ if (socketState === "0") {
428
+ if (
429
+ this.pendingAgentState &&
430
+ isRestAgentState(this.pendingAgentState)
431
+ ) {
432
+ socketState = this.pendingAgentState;
433
+ } else if (isRestAgentState(this.agentState)) {
434
+ return;
435
+ }
436
+ }
437
+ if (socketState !== null) {
438
+ this.applyAgentState(socketState);
439
+ return;
440
+ }
441
+ if (isRestAgentState(this.agentState)) {
442
+ return;
443
+ }
444
+ this.applyAgentState("0");
445
+ },
446
+ setPendingAgentState(state) {
447
+ this.pendingAgentState = String(state);
448
+ this.clearPendingAgentStateTimer();
449
+ this.pendingAgentStateTimer = setTimeout(() => {
450
+ this.pendingAgentState = null;
451
+ this.pendingAgentStateTimer = null;
452
+ }, 4000);
453
+ },
454
+ clearPendingAgentStateTimer() {
455
+ if (this.pendingAgentStateTimer) {
456
+ clearTimeout(this.pendingAgentStateTimer);
457
+ this.pendingAgentStateTimer = null;
458
+ }
459
+ },
460
+ handleOnWork() {
461
+ const serv = this.getServ();
462
+ if (!serv || !this.sdkReady) {
463
+ return;
464
+ }
465
+ const option = this.option;
466
+ const payload = {
467
+ displayNumber: option.displayNumber || undefined,
468
+ isRecordStereo: false,
469
+ agentState: "1",
470
+ };
471
+ serv.agentLogin(payload, (res) => {
472
+ this.handleApiResult(res, "上班", () => {
473
+ this.isOnWork = true;
474
+ this.applyAgentState("1");
475
+ });
476
+ });
477
+ },
478
+ handleOffWork() {
479
+ const serv = this.getServ();
480
+ if (!serv) {
481
+ return;
482
+ }
483
+ serv.agentLoginout((res) =>
484
+ this.handleApiResult(res, "下班", () => {
485
+ this.isOnWork = false;
486
+ this.applyAgentState("-1");
487
+ this.currentCall = null;
488
+ this.incomingNumber = "";
489
+ })
490
+ );
491
+ },
492
+ handleDial() {
493
+ const serv = this.getServ();
494
+ if (!serv || !this.sdkReady) {
495
+ this.notifyError("呼叫中心未就绪");
496
+ return;
497
+ }
498
+ if (!this.isOnWork) {
499
+ this.notifyError("请先点击「上班」");
500
+ return;
501
+ }
502
+ if (!this.dialNumber) {
503
+ this.notifyError("请输入外呼号码");
504
+ return;
505
+ }
506
+ if (!canAgentDial(this.agentState)) {
507
+ this.notifyError("当前座席状态不可外呼");
508
+ return;
509
+ }
510
+ const option = this.option;
511
+ serv.agentCallOut(
512
+ {
513
+ displayNumber: option.displayNumber || undefined,
514
+ destinationNumber: this.dialNumber,
515
+ params: "",
516
+ },
517
+ (res) =>
518
+ this.handleApiResult(res, "外呼", () => {
519
+ if (res && res.callId) {
520
+ this.currentCall = { callId: res.callId, direction: 1 };
521
+ this.applyCallActiveState();
522
+ }
523
+ })
524
+ );
525
+ },
526
+ handleHangUp() {
527
+ const serv = this.getServ();
528
+ if (!serv || !this.currentCall || !this.currentCall.callId) {
529
+ return;
530
+ }
531
+ serv.agentHangUp({ callId: this.currentCall.callId }, (res) =>
532
+ this.handleApiResult(res, "挂机", () => {
533
+ this.restoreAgentStateAfterCall();
534
+ })
535
+ );
536
+ },
537
+ handleUpdateState(agentState) {
538
+ const serv = this.getServ();
539
+ if (!serv || !this.isOnWork) {
540
+ return;
541
+ }
542
+ const targetState = String(agentState);
543
+ this.setPendingAgentState(targetState);
544
+ serv.updateAgentState({ agentState: targetState }, (res) => {
545
+ if (!res || String(res.retCode) !== "000000") {
546
+ this.pendingAgentState = null;
547
+ this.clearPendingAgentStateTimer();
548
+ }
549
+ this.handleApiResult(res, "修改状态", () => {
550
+ this.applyAgentState(targetState);
551
+ });
552
+ });
553
+ },
554
+ handleApiResult(res, action, onSuccess) {
555
+ if (res && String(res.retCode) === "000000") {
556
+ if (typeof onSuccess === "function") {
557
+ onSuccess(res);
558
+ }
559
+ return;
560
+ }
561
+ const msg = (res && (res.retMsg || res.msg)) || `${action}失败`;
562
+ this.notifyError(msg);
563
+ },
564
+ notifyError(message) {
565
+ if (this.$message) {
566
+ this.$message.error(message);
567
+ } else {
568
+ console.error("[CallCenter]", message);
569
+ }
570
+ },
571
+ onDragStart(event) {
572
+ if (event.button !== 0) {
573
+ return;
574
+ }
575
+ const rect = this.$el.getBoundingClientRect();
576
+ this.dragging = true;
577
+ this.dragOffset = {
578
+ x: event.clientX - rect.left,
579
+ y: event.clientY - rect.top,
580
+ };
581
+ if (this.position.left === null) {
582
+ this.position.left = rect.left;
583
+ this.position.bottom = window.innerHeight - rect.bottom;
584
+ }
585
+ },
586
+ onDragMove(event) {
587
+ if (!this.dragging) {
588
+ return;
589
+ }
590
+ const panelWidth = this.$el ? this.$el.offsetWidth : 720;
591
+ const panelHeight = this.$el ? this.$el.offsetHeight : 80;
592
+ const edge = 8;
593
+ const maxLeft = Math.max(edge, window.innerWidth - panelWidth - edge);
594
+ const left = Math.min(
595
+ maxLeft,
596
+ Math.max(edge, event.clientX - this.dragOffset.x)
597
+ );
598
+ const maxTop = window.innerHeight - panelHeight - edge;
599
+ const top = Math.min(
600
+ maxTop,
601
+ Math.max(edge, event.clientY - this.dragOffset.y)
602
+ );
603
+ const bottom = window.innerHeight - top - panelHeight;
604
+ this.position.left = left;
605
+ this.position.bottom = bottom;
606
+ },
607
+ onDragEnd() {
608
+ this.dragging = false;
609
+ },
610
+ },
611
+ };
612
+ </script>
613
+
614
+ <style lang="scss" scoped>
615
+ .call-center-float {
616
+ position: fixed;
617
+ min-width: 680px;
618
+ max-width: calc(100vw - 32px);
619
+ border-radius: 4px;
620
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.25);
621
+ background: linear-gradient(90deg, #3d8fd8 0%, #2b6cb8 55%, #245da8 100%);
622
+ color: #fff;
623
+ font-size: 13px;
624
+ user-select: none;
625
+ }
626
+
627
+ .call-center-float__header {
628
+ display: flex;
629
+ align-items: center;
630
+ justify-content: space-between;
631
+ padding: 8px 10px 4px;
632
+ cursor: move;
633
+ }
634
+
635
+ .call-center-float__status-row {
636
+ display: flex;
637
+ align-items: center;
638
+ flex-wrap: wrap;
639
+ gap: 4px 12px;
640
+ flex: 1;
641
+ min-width: 0;
642
+ }
643
+
644
+ .call-center-float__label {
645
+ color: rgba(255, 255, 255, 0.92);
646
+ white-space: nowrap;
647
+
648
+ &--incoming {
649
+ margin-left: 8px;
650
+ }
651
+ }
652
+
653
+ .call-center-float__status {
654
+ font-weight: 600;
655
+ margin-right: 4px;
656
+
657
+ &.is-idle {
658
+ color: #b8f06a;
659
+ }
660
+
661
+ &.is-busy,
662
+ &.is-talking {
663
+ color: #ffd666;
664
+ }
665
+
666
+ &.is-rest {
667
+ color: #91d5ff;
668
+ }
669
+
670
+ &.is-offline {
671
+ color: #d9d9d9;
672
+ }
673
+ }
674
+
675
+ .call-center-float__incoming {
676
+ color: #fff;
677
+ font-weight: 600;
678
+ min-width: 80px;
679
+ }
680
+
681
+ .call-center-float__toggle {
682
+ border: none;
683
+ background: rgba(255, 255, 255, 0.2);
684
+ color: #fff;
685
+ width: 24px;
686
+ height: 24px;
687
+ line-height: 22px;
688
+ border-radius: 2px;
689
+ cursor: pointer;
690
+ flex-shrink: 0;
691
+ margin-left: 8px;
692
+
693
+ &:hover {
694
+ background: rgba(255, 255, 255, 0.35);
695
+ }
696
+ }
697
+
698
+ .call-center-float__body {
699
+ display: flex;
700
+ align-items: center;
701
+ gap: 6px;
702
+ padding: 6px 10px 10px;
703
+ flex-wrap: wrap;
704
+ }
705
+
706
+ .call-center-float__input {
707
+ width: 140px;
708
+ height: 30px;
709
+ padding: 0 8px;
710
+ border: none;
711
+ border-radius: 2px;
712
+ outline: none;
713
+ font-size: 13px;
714
+ color: #333;
715
+
716
+ &:disabled {
717
+ background: #f0f0f0;
718
+ color: #999;
719
+ }
720
+ }
721
+
722
+ .call-center-float__btn {
723
+ height: 30px;
724
+ border: none;
725
+ border-radius: 2px;
726
+ cursor: pointer;
727
+ font-size: 13px;
728
+ color: #fff;
729
+ white-space: nowrap;
730
+ padding: 0 10px;
731
+ transition: opacity 0.15s;
732
+
733
+ &:disabled {
734
+ opacity: 0.45;
735
+ cursor: not-allowed;
736
+ }
737
+
738
+ &--dial {
739
+ width: 34px;
740
+ padding: 0;
741
+ background: #f5a623;
742
+ display: inline-flex;
743
+ align-items: center;
744
+ justify-content: center;
745
+
746
+ &:not(:disabled):hover {
747
+ background: #e69512;
748
+ }
749
+ }
750
+
751
+ &--action {
752
+ min-width: 52px;
753
+ background: #52a845;
754
+
755
+ &:not(:disabled):hover {
756
+ background: #46963b;
757
+ }
758
+ }
759
+ }
760
+
761
+ .call-center-float__phone-icon {
762
+ display: inline-block;
763
+ width: 16px;
764
+ height: 16px;
765
+ background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='white'%3E%3Cpath d='M6.6 10.8c1.4 2.8 3.8 5.1 6.6 6.6l2.2-2.2c.3-.3.7-.4 1-.2 1.1.4 2.3.6 3.6.6.6 0 1 .4 1 1V20c0 .6-.4 1-1 1C10.3 21 3 13.7 3 4c0-.6.4-1 1-1h3.5c.6 0 1 .4 1 1 0 1.3.2 2.5.6 3.6.1.3 0 .7-.2 1L6.6 10.8z'/%3E%3C/svg%3E")
766
+ center / contain no-repeat;
767
+ }
768
+ </style>