| | |
| | | <span class="btn-icon">📞</span> |
| | | {{ callButtonText }} |
| | | </button> |
| | | |
| | | <button |
| | | @click="handleSeatLogout" |
| | | :class="[ |
| | | 'seat-btn', |
| | | 'logout', |
| | | { 'in-call': isInCall || isCalling }, |
| | | ]" |
| | | :disabled="!canLogout" |
| | | > |
| | | <span class="btn-icon">🚪</span> |
| | | {{ isInCall || isCalling ? "强制签出" : "签出" }} |
| | | </button> |
| | | <button |
| | | @click="handleHangup" |
| | | class="hangup-btn" |
| | |
| | | |
| | | <div class="status-item" v-if="callDuration"> |
| | | <span class="status-label">通话时长:</span> |
| | | <span class="duration-display"> |
| | | ⏱️ {{ callDuration }} |
| | | </span> |
| | | <span class="duration-display"> ⏱️ {{ callDuration }} </span> |
| | | </div> |
| | | </div> |
| | | </div> |
| | |
| | | isInCall() { |
| | | return this.callStatus === "connected"; |
| | | }, |
| | | |
| | | canLogout() { |
| | | // 只有在已签入状态才允许签出(无论是否在通话中) |
| | | return this.isSeatLoggedIn && !this.isSeatLoggingOut; |
| | | }, |
| | | canMakeCall() { |
| | | return ( |
| | | this.isSeatLoggedIn && |
| | |
| | | this.$refs.callComponent.callout(this.customerPhone); |
| | | this.startCallTimer(); |
| | | }, |
| | | async handleSeatLogout() { |
| | | if (!this.canLogout) return; |
| | | |
| | | try { |
| | | // 如果正在通话中,先挂断 |
| | | if (this.isInCall || this.isCalling) { |
| | | // 显示确认对话框 |
| | | const confirm = await this.$confirm( |
| | | "确定要签出吗?签出将结束当前通话", |
| | | "提示", |
| | | { |
| | | confirmButtonText: "确定", |
| | | cancelButtonText: "取消", |
| | | type: "warning", |
| | | } |
| | | ); |
| | | await this.handleHangup(); |
| | | } |
| | | console.log(2); |
| | | |
| | | // 执行签出 |
| | | await this.$refs.callComponent.handleSeatLogout(); |
| | | console.log(3); |
| | | this.isSeatLoggedIn = false; |
| | | |
| | | this.$message.success("座席签出成功"); |
| | | } catch (error) { |
| | | if (error !== "cancel") { |
| | | // 忽略用户取消的情况 |
| | | console.error("签出失败:", error); |
| | | this.$message.error("座席签出失败"); |
| | | } |
| | | } |
| | | }, |
| | | handleHangup() { |
| | | this.$refs.callComponent.hangup(); |
| | | this.stopCallTimer(); |
| | |
| | | |
| | | onSeatStatusChange(status) { |
| | | this.isSeatLoggedIn = status.isLoggedIn; |
| | | |
| | | // 如果座席签出,重置通话状态 |
| | | if (!status.isLoggedIn) { |
| | | this.callStatus = "idle"; |
| | | this.stopCallTimer(); |
| | | } |
| | | }, |
| | | |
| | | onCallStatusChange(status) { |
| | | this.callStatus = status.status; |
| | | if (status.status === "connected") { |
| | | if (status.status == "connected") { |
| | | this.startCallTimer(); |
| | | } else if (status.status === "idle") { |
| | | } else if (status.status == "idle") { |
| | | this.stopCallTimer(); |
| | | } |
| | | }, |
| | |
| | | |
| | | beforeUnmount() { |
| | | this.stopCallTimer(); |
| | | |
| | | // 组件销毁前尝试签出 |
| | | if (this.isSeatLoggedIn) { |
| | | this.handleSeatLogout().catch(console.error); |
| | | } |
| | | }, |
| | | }; |
| | | </script> |
| | |
| | | } |
| | | } |
| | | |
| | | .seat-btn.logout { |
| | | background: linear-gradient(135deg, #f97316, #ea580c); |
| | | color: white; |
| | | |
| | | &:hover:not(:disabled) { |
| | | box-shadow: 0 6px 16px rgba(249, 115, 22, 0.4); |
| | | } |
| | | |
| | | // 通话中的特殊样式 |
| | | &.in-call { |
| | | background: linear-gradient(135deg, #ef4444, #dc2626); |
| | | animation: pulse 1.5s infinite; |
| | | |
| | | &:hover:not(:disabled) { |
| | | box-shadow: 0 6px 16px rgba(239, 68, 68, 0.4); |
| | | } |
| | | } |
| | | } |
| | | |
| | | // 通话中签出按钮的特殊动画 |
| | | @keyframes pulse-red { |
| | | 0% { |
| | | box-shadow: 0 4px 12px rgba(239, 68, 68, 0.5); |
| | | } |
| | | 50% { |
| | | box-shadow: 0 4px 20px rgba(239, 68, 68, 0.8); |
| | | } |
| | | 100% { |
| | | box-shadow: 0 4px 12px rgba(239, 68, 68, 0.5); |
| | | } |
| | | } |
| | | .section-title { |
| | | font-size: 16px; |
| | | font-weight: 600; |
| | |
| | | } |
| | | |
| | | .call-btn.calling { |
| | | background: linear-gradient(135deg, #f59e0b, #d97706); /* 呼叫中状态改为橙色渐变 */ |
| | | background: linear-gradient( |
| | | 135deg, |
| | | #f59e0b, |
| | | #d97706 |
| | | ); /* 呼叫中状态改为橙色渐变 */ |
| | | animation: pulse 1.5s infinite; /* 呼叫中添加呼吸脉冲动画 */ |
| | | } |
| | | |
| | |
| | | |
| | | /* 脉冲动画定义 */ |
| | | @keyframes pulse { |
| | | 0% { box-shadow: 0 4px 12px rgba(245, 158, 11, 0.5); } |
| | | 50% { box-shadow: 0 4px 20px rgba(245, 158, 11, 0.8); } |
| | | 100% { box-shadow: 0 4px 12px rgba(245, 158, 11, 0.5); } |
| | | 0% { |
| | | box-shadow: 0 4px 12px rgba(245, 158, 11, 0.5); |
| | | } |
| | | 50% { |
| | | box-shadow: 0 4px 20px rgba(245, 158, 11, 0.8); |
| | | } |
| | | 100% { |
| | | box-shadow: 0 4px 12px rgba(245, 158, 11, 0.5); |
| | | } |
| | | } |
| | | |
| | | // 状态显示样式 |
| | |
| | | } |
| | | |
| | | .duration-display { |
| | | font-family: 'Courier New', monospace; |
| | | font-family: "Courier New", monospace; |
| | | font-weight: 600; |
| | | color: #059669; |
| | | font-size: 14px; |
| | |
| | | |
| | | // 动画定义 |
| | | @keyframes pulse { |
| | | 0% { opacity: 1; } |
| | | 50% { opacity: 0.5; } |
| | | 100% { opacity: 1; } |
| | | 0% { |
| | | opacity: 1; |
| | | } |
| | | 50% { |
| | | opacity: 0.5; |
| | | } |
| | | 100% { |
| | | opacity: 1; |
| | | } |
| | | } |
| | | |
| | | // 响应式设计 |