123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215 |
- <template>
- <div class="dialer-container">
- <!-- 显示输入的电话号码 -->
- <el-input type="text" v-model="currentPhoneNumber" readonly />
- <div class="dialer-buttons">
- <!-- 循环生成数字按钮 -->
- <el-button
- v-for="number in numbers"
- type="primary"
- :key="number"
- :class="{ 'special-number': number === '1' }"
- @click="appendNumber(number)"
- >
- {{ number }}
- </el-button>
- <audio v-show="false" controls autoplay="autoplay" id="audioCtl"></audio>
- </div>
- <!-- 语音输入按钮 -->
- <el-button type="info" @click="startVoiceInput"> 语音输入 </el-button>
- <!-- 清除输入的电话号码 -->
- <el-button type="warning" @click="clearNumber">清除</el-button>
- <!-- 模拟拨号操作 -->
- <el-button type="success" @click="callNumber">拨号</el-button>
- <!-- 模拟拨号操作 -->
- <el-button type="danger" @click="downNumber">挂断</el-button>
- </div>
- </template>
-
- <script>
- export default {
- props: {
- phoneNumber: {
- type: String,
- default: "",
- },
- },
- data() {
- return {
- id: this.generateGuid(),
- baseUrl: "api/webrtc/",
- pc: null,
- // 拨号盘上的数字和符号
- numbers: ["1", "2", "3", "4", "5", "6", "7", "8", "9", "*", "0", "#"],
- };
- },
- computed: {
- getOfferUrl() {
- return `${this.baseUrl}getoffer?id=${this.id}`;
- },
- setAnswerUrl() {
- return `${this.baseUrl}setanswer?id=${this.id}`;
- },
- setIceCandidateUrl() {
- return `${this.baseUrl}addicecandidate?id=${this.id}`;
- },
- currentPhoneNumber: {
- get() {
- return this.phoneNumber;
- },
- set(value) {
- // 当输入框的值改变时,通过自定义事件将新值传递给父组件
- this.$emit("update:phoneNumber", value);
- },
- },
- },
- methods: {
- appendNumber(number) {
- this.currentPhoneNumber += number;
- },
- clearNumber() {
- this.currentPhoneNumber = "";
- },
- callNumber() {
- if (this.currentPhoneNumber) {
- alert(`正在拨打号码: ${this.currentPhoneNumber}`);
- this.start();
- } else {
- alert("请输入电话号码");
- }
- },
- downNumber() {
- this.closePeer();
- },
- startVoiceInput() {
- const recognition = new (window.SpeechRecognition ||
- window.webkitSpeechRecognition)();
- recognition.lang = "zh-CN";
- recognition.onresult = (event) => {
- const transcript = event.results[0][0].transcript;
- this.currentPhoneNumber = transcript.replace(/[^0-9*#]/g, "");
- };
- recognition.start();
- },
- generateGuid() {
- return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(
- /[xy]/g,
- function (c) {
- var r = (Math.random() * 16) | 0,
- v = c == "x" ? r : (r & 0x3) | 0x8;
- return v.toString(16);
- }
- );
- },
- async start() {
- this.closePeer();
- let videoControl = document.querySelector("#audioCtl");
- const localStream = await navigator.mediaDevices.getUserMedia({
- video: false,
- audio: true,
- });
- videoControl.srcObject = localStream;
- this.pc = new RTCPeerConnection(null);
- localStream.getTracks().forEach((track) => {
- console.log("add local track " + track.kind + " to peer connection.");
- console.log(track);
- this.pc.addTrack(track, localStream);
- });
- this.pc.onicegatheringstatechange = function () {
- console.log("onicegatheringstatechange: " + this.pc.iceGatheringState);
- }.bind(this);
- this.pc.oniceconnectionstatechange = function () {
- console.log(
- "oniceconnectionstatechange: " + this.pc.iceConnectionState
- );
- }.bind(this);
- this.pc.onsignalingstatechange = function () {
- console.log("onsignalingstatechange: " + this.pc.signalingState);
- }.bind(this);
- this.pc.onicecandidate = async function (event) {
- if (event.candidate) {
- console.log("new-ice-candidate:");
- console.log(event.candidate.candidate);
- console.log(event.candidate);
- await fetch(this.setIceCandidateUrl, {
- method: "POST",
- body: JSON.stringify(event.candidate),
- headers: { "Content-Type": "application/json" },
- });
- }
- }.bind(this);
- let offerResponse = await fetch(this.getOfferUrl);
- let offer = await offerResponse.json();
- console.log("got offer: " + offer.type + " " + offer.sdp + ".");
- await this.pc.setRemoteDescription(offer);
- this.pc
- .createAnswer()
- .then(
- function (answer) {
- return this.pc.setLocalDescription(answer);
- }.bind(this)
- )
- .then(
- async function () {
- console.log("Sending answer SDP.");
- console.log("SDP: " + this.pc.localDescription.sdp);
- await fetch(this.setAnswerUrl, {
- method: "POST",
- body: JSON.stringify(this.pc.localDescription),
- headers: { "Content-Type": "application/json" },
- });
- }.bind(this)
- );
- },
- closePeer() {
- if (this.pc != null) {
- console.log("close peer");
- this.pc.close();
- }
- },
- },
- };
- </script>
-
- <style scoped>
- .dialer-container {
- margin: 20px auto;
- width: 300px;
- text-align: center;
- font-weight: bold; /* 文字加粗 */
- }
- .dialer-buttons {
- margin: 10px auto;
- display: grid;
- grid-template-columns: repeat(3, 3fr);
- gap: 4px;
- }
- .dialer-buttons button {
- padding: 10px;
- font-size: 20px;
- }
- button {
- font-size: 20px;
- margin: 5px;
- }
- /* 定义数字 1 的特殊样式 */
- .special-number {
- margin-left: 10px;
- color: #ffffff; /* 文字颜色为白色 */
- }
- </style>
|