You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Laravel中集成Plivo SDK实现网页端点击通话功能?

如何在Laravel中集成Plivo SDK实现网页端点击通话功能?

我正在Laravel中集成Plivo,想在网页端实现「点击通话」功能,需要支持呼叫、挂断、静音、取消静音这些操作,还要让用户能直接通过浏览器和对方通话。但现在遇到了报错:"Failed to load Plivo SDK: Plivo SDK failed to load",以下是我编写的代码:

Plivo SDK in Laravel

show.blade.php

@extends('layouts.app')
@section('title', "Contact: {$contact->first_name} {$contact->last_name}")
@section('styles')
<script type="text/javascript" src="https://cdn.plivo.com/sdk/browser/v2/plivo.min.js"> </script>
<style>
    .call-interface {
    padding: 20px;
    border: 1px solid #ddd;
    border-radius: 8px;
    margin-bottom: 20px;
    }
    .call-status {
    font-size: 18px;
    margin-bottom: 15px;
    }
    .call-controls button {
    margin-right: 10px;
    margin-bottom: 10px;
    }
    .call-timer {
    font-size: 24px;
    font-weight: bold;
    margin: 15px 0;
    }
    .call-logs {
    margin-top: 30px;
    }
</style>
@endsection
@section('content')
<div class="col-md-4">
    <div class="card mb-3">
        <div class="card-header">
            <h5 class="card-title mb-0">
                Contact Details
            </h5>
        </div>
        <div class="card-body">
            <div class="mb-1">
                <strong>Name:</strong> {{ $contact->first_name }} {{ $contact->last_name }}
            </div>
            <div class="mb-1">
                <strong>Phone:</strong> {{ $contact->phone }}
            </div>
        </div>
        <div class="card-footer">
            <div class="row g-3 align-items-center">
                <div class="col-12">
                    <label for="note" class="form-label">Note</label>
                    <textarea name="note" class="form-control" id="note" rows="3">{!! $disposition ? $disposition->note : '' !!}</textarea>
                </div>
                <div class="col-auto">
                    <select name="disposition" class="form-select" id="disposition">
                        <option value="">Select disposition</option>
                        @foreach (config('data.disposition_status') as $key => $item)
                        <option value="{{ $key }}" {{ $disposition && $disposition->status === $key ? 'selected' : '' }}>{{ $item }}</option>
                        @endforeach
                    </select>
                </div>
                <div class="col-auto">
                    <button onclick="makeCall('{{ $contact->phone }}')" class="btn btn-dark">Call</button>
                </div>
            </div>
        </div>
    </div>
</div>
<div class="col-md-8">
    <div class="card">
        <div class="card-header">
            <h3>Call Interface</h3>
        </div>
        <div class="card-body">
            <div class="call-interface">
                <div id="call-status" class="call-status alert alert-info">
                    Ready
                </div>
                <div class="call-timer" id="call-timer">
                    00:00:00
                </div>
                <div class="call-controls">
                    <button id="makeCall" class="btn btn-success" disabled>
                    <i class="fas fa-phone"></i> Call
                    </button>
                    <button id="hangupCall" class="btn btn-danger" disabled>
                    <i class="fas fa-phone-slash"></i> Hangup
                    </button>
                    <button id="muteCall" class="btn btn-warning" disabled>
                    <i class="fas fa-microphone-slash"></i> Mute
                    </button>
                    <button id="unmuteCall" class="btn btn-info" disabled>
                    <i class="fas fa-microphone"></i> Unmute
                    </button>
                </div>
            </div>
        </div>
    </div>
</div>
@endsection
@push('scripts')
<script>
    let client;
    let currentCall = null;
    let callTimer = null;
    let callStartTime = null;
    
    async function initializePlivoClient() {
        try {
            console.log('Initializing Plivo client...');
            
            if (typeof window.plivoBrowserSdk === 'undefined') {
                throw new Error('Plivo Browser SDK not loaded');
            }
    
            const response = await fetch('{{ route("plivo.token") }}');
            const data = await response.json();
            
            if (data.error) {
                throw new Error(data.error);
            }
            
            console.log('Token received, initializing client...');
    
            try {
                const options = {
                    debug: "DEBUG",
                    permOnClick: true,
                    enableTracking: true,
                    audioConstraints: {
                        optional: [
                            { echoCancellation: true },
                            { noiseSuppression: true }
                        ]
                    }
                };
    
                // Create client using the correct SDK reference
                client = new window.plivoBrowserSdk.Client(options);
                
                console.log('Client created, attempting login...');
                
                // Login
                await client.login(data.username, data.password);
                
                console.log('Login successful');
                document.getElementById('makeCall').disabled = false;
                document.getElementById('call-status').innerHTML = 'Ready to call';
                document.getElementById('call-status').className = 'alert alert-success';
                
                setupEventListeners();
                
            } catch (error) {
                throw new Error(`Client initialization failed: ${error.message}`);
            }
    
        } catch (error) {
            console.error('Failed to initialize Plivo client:', error);
            document.getElementById('call-status').innerHTML = 'Failed to initialize phone: ' + error.message;
            document.getElementById('call-status').className = 'alert alert-danger';
        }
    }
    
    // Wait for SDK to load
    function waitForPlivoSDK() {
        return new Promise((resolve, reject) => {
            let attempts = 0;
            const maxAttempts = 20;
            
            const checkSDK = setInterval(() => {
                if (typeof window.plivoBrowserSdk !== 'undefined') {
                    clearInterval(checkSDK);
                    resolve();
                } else {
                    attempts++;
                    if (attempts >= maxAttempts) {
                        clearInterval(checkSDK);
                        reject(new Error('Plivo SDK failed to load'));
                    }
                }
            }, 250);
        });
    }
    
    // Initialize when document is ready
    document.addEventListener('DOMContentLoaded', async () => {
        try {
            await waitForPlivoSDK();
            await initializePlivoClient();
        } catch (error) {
            console.error('Initialization failed:', error);
            document.getElementById('call-status').innerHTML = 'Failed to load Plivo SDK: ' + error.message;
            document.getElementById('call-status').className = 'alert alert-danger';
        }
    });
    
    function setupEventListeners() {
        if (!client) {
            console.error('Client not initialized');
            return;
        }
    
        client.on('onLogin', () => {
            console.log('Successfully logged in to Plivo');
            document.getElementById('call-status').innerHTML = 'Ready to make calls';
            document.getElementById('call-status').className = 'alert alert-success';
        });
    
        client.on('onLoginFailed', (error) => {
            console.error('Login failed:', error);
        });
    
        client.on('onCallRemoteRinging', () => {
            document.getElementById('call-status').innerHTML = 'Ringing...';
            document.getElementById('call-status').className = 'alert alert-warning';
        });
    
        client.on('onCallAnswered', () => {
            document.getElementById('call-status').innerHTML = 'Call in progress';
            document.getElementById('call-status').className = 'alert alert-success';
            document.getElementById('hangupCall').disabled = false;
            document.getElementById('muteCall').disabled = false;
            startCallTimer();
        });
    
        client.on('onCallTerminated', (reason) => {
            document.getElementById('call-status').innerHTML = `Call ended: ${reason}`;
            document.getElementById('call-status').className = 'alert alert-info';
            document.getElementById('hangupCall').disabled = true;
            document.getElementById('muteCall').disabled = true;
            document.getElementById('unmuteCall').disabled = true;
            document.getElementById('makeCall').disabled = false;
            stopCallTimer();
            currentCall = null;
        });
    
        client.on('onMuteChanged', (isMuted) => {
            if (isMuted) {
                document.getElementById('muteCall').disabled = true;
                document.getElementById('unmuteCall').disabled = false;
            } else {
                document.getElementById('muteCall').disabled = false;
                document.getElementById('unmuteCall').disabled = true;
            }
        });
    }
    
    async function makeCall(phoneNumber) {
        if (!client) {
            alert('Plivo client not initialized');
            return;
        }
    
        try {
            currentCall = await client.call(phoneNumber, {
                from: '{{ auth()->user()->phone }}',
                answerOnBridge: true,
                ringtone: 'default'
            });
            
            document.getElementById('makeCall').disabled = true;
            document.getElementById('call-status').innerHTML = 'Calling...';
            document.getElementById('call-status').className = 'alert alert-warning';
            
        } catch (error) {
            console.error('Failed to make call:', error);
            document.getElementById('call-status').innerHTML = `Call failed: ${error.message}`;
            document.getElementById('call-status').className = 'alert alert-danger';
            document.getElementById('makeCall').disabled = false;
        }
    }
    
    function hangupCall() {
        if (currentCall) {
            currentCall.hangup();
        }
    }
    
    function muteCall() {
        if (currentCall) {
            currentCall.mute(true);
        }
    }
    
    function unmuteCall() {
        if (currentCall) {
            currentCall.mute(false);
        }
    }
    
    function startCallTimer() {
        callStartTime = Date.now();
        callTimer = setInterval(() => {
            const elapsed = Math.floor((Date.now() - callStartTime) / 1000);
            const hours = Math.floor(elapsed / 3600).toString().padStart(2, '0');
            const minutes = Math.floor((elapsed % 3600) / 60).toString().padStart(2, '0');
            const seconds = (elapsed % 60).toString().padStart(2, '0');
            document.getElementById('call-timer').innerHTML = `${hours}:${minutes}:${seconds}`;
        }, 1000);
    }
    
    function stopCallTimer() {
        if (callTimer) {
            clearInterval(callTimer);
            callTimer = null;
        }
    }
</script>
@endpush

备注:内容来源于stack exchange,提问作者Hilmi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.14 15:43:14