dezhu-icall-vue 2.0.2 → 4.0.0

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 (2) hide show
  1. package/package.json +1 -1
  2. package/src/DeZhuICall.vue +76 -143
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dezhu-icall-vue",
3
- "version": "2.0.2",
3
+ "version": "4.0.0",
4
4
  "description": "得助智能 iCall 外呼组件 - Vue 封装",
5
5
  "main": "src/index.js",
6
6
  "keywords": [
@@ -1,6 +1,5 @@
1
1
  <template>
2
- <!-- 拨号盘容器,通过 visible 控制显示/隐藏 -->
3
- <div ref="callContainer" :style="containerStyle" class="dezhu-icall-container" />
2
+ <div ref="callContainer" class="dezhu-icall-container" style="display: none;" />
4
3
  </template>
5
4
 
6
5
  <style>
@@ -11,64 +10,17 @@
11
10
  </style>
12
11
 
13
12
  <script>
14
- /**
15
- * 得助外呼组件
16
- * @description 封装得助智能 iCall 外呼功能 - 开箱即用版本
17
- * @example
18
- * <DeZhuICall
19
- * server-url="https://hnzz.dezhuyun.com"
20
- * :token="deZhuToken"
21
- * :auto-init="true"
22
- * @ready="onReady"
23
- * @error="onError"
24
- * />
25
- */
26
-
27
13
  export default {
28
14
  name: 'DeZhuICall',
29
15
 
30
16
  props: {
31
- /** 得助服务器地址,如:https://hnzz.dezhuyun.com */
32
17
  serverUrl: {
33
18
  type: String,
34
19
  required: true
35
20
  },
36
- /** 代理前缀,用于本地开发跨域,如:'/dzfront' */
37
- proxyPrefix: {
38
- type: String,
39
- default: ''
40
- },
41
- /** 认证 Token,通过 SSO 接入获取 */
42
21
  token: {
43
22
  type: String,
44
23
  required: true
45
- },
46
- /** 是否自动初始化 */
47
- autoInit: {
48
- type: Boolean,
49
- default: true
50
- },
51
- /** 是否显示拨号盘界面 */
52
- visible: {
53
- type: Boolean,
54
- default: false
55
- }
56
- },
57
-
58
- computed: {
59
- containerStyle() {
60
- return {
61
- display: this.visible ? 'block' : 'none'
62
- }
63
- }
64
- },
65
-
66
- watch: {
67
- serverUrl() {
68
- if (this.autoInit) this.checkAndInit()
69
- },
70
- token() {
71
- if (this.autoInit) this.checkAndInit()
72
24
  }
73
25
  },
74
26
 
@@ -76,55 +28,50 @@ export default {
76
28
  return {
77
29
  iCall: null,
78
30
  isInitialized: false,
79
- isInitializing: false,
80
- isCalling: false,
81
- reconnectTimer: null,
31
+ scriptLoaded: false,
82
32
  reconnectCount: 0,
83
- maxReconnectCount: 5
33
+ maxReconnectCount: 5,
34
+ reconnectTimer: null
84
35
  }
85
36
  },
86
37
 
87
38
  mounted() {
88
- if (this.autoInit) this.checkAndInit()
89
- document.addEventListener('visibilitychange', this.handleVisibilityChange)
39
+ if (this.token) {
40
+ this.loadSDKAndToken()
41
+ }
42
+ },
43
+
44
+ watch: {
45
+ token(newVal) {
46
+ if (newVal && !this.isInitialized) {
47
+ this.loadSDKAndToken()
48
+ }
49
+ }
90
50
  },
91
51
 
92
52
  beforeDestroy() {
93
- document.removeEventListener('visibilitychange', this.handleVisibilityChange)
94
- this.clearReconnectTimer()
95
53
  this.destroy()
96
54
  },
97
55
 
98
56
  methods: {
99
- async checkAndInit() {
100
- if (this.isInitializing || this.isInitialized) return
101
- if (!this.serverUrl || !this.token) return
102
-
103
- this.isInitializing = true
57
+ async loadSDKAndToken() {
104
58
  try {
105
59
  await this.loadSDK()
106
60
  await this.initICall()
107
61
  } catch (error) {
108
62
  this.$emit('error', error)
109
- this.scheduleReconnect()
110
- } finally {
111
- this.isInitializing = false
112
63
  }
113
64
  },
114
65
 
115
66
  loadSDK() {
116
67
  if (window.DeZhuFrontIcall) {
68
+ this.scriptLoaded = true
117
69
  return Promise.resolve()
118
70
  }
119
71
 
120
72
  return new Promise((resolve, reject) => {
121
73
  const script = document.createElement('script')
122
- // SDK 加载路径:如果有代理前缀,直接使用代理前缀(浏览器会拦截并转发)
123
- // 否则使用完整 serverUrl
124
- const baseUrl = this.proxyPrefix || this.serverUrl
125
- // 注意:proxyPrefix 已经包含 /dzfront,所以不需要再拼接
126
- const path = this.proxyPrefix ? '/icall/lib/icall.js' : '/dzfront/icall/lib/icall.js'
127
- script.src = `${baseUrl}${path}?time=${Date.now()}`
74
+ script.src = `${this.serverUrl}/dzfront/icall/lib/icall.js?time=${Date.now()}`
128
75
  script.async = true
129
76
 
130
77
  const timeout = setTimeout(() => {
@@ -133,6 +80,7 @@ export default {
133
80
 
134
81
  script.onload = () => {
135
82
  clearTimeout(timeout)
83
+ this.scriptLoaded = true
136
84
  resolve()
137
85
  }
138
86
 
@@ -153,35 +101,25 @@ export default {
153
101
  return
154
102
  }
155
103
 
156
- if (this.iCall && typeof this.iCall.signOut === 'function') {
157
- this.iCall.signOut()
104
+ if (this.iCall) {
105
+ if (typeof this.iCall.signOut === 'function') {
106
+ this.iCall.signOut()
107
+ }
158
108
  this.iCall = null
159
109
  }
160
110
 
161
- // 如果有代理前缀,server 配置为空(SDK 会基于当前域名请求)
162
- const serverConfig = this.proxyPrefix ? '' : this.serverUrl
163
111
  this.iCall = new window.DeZhuFrontIcall(container, {
164
112
  option: {
165
- server: serverConfig,
113
+ server: this.serverUrl,
166
114
  token: this.token
167
115
  },
168
116
  event: {
169
- onCallStatusChange: (status, params) => {
170
- this.$emit('statusChange', status, params)
171
- switch (status) {
172
- case 'MANUAL_DIALING':
173
- this.$emit('dialing', params)
174
- break
175
- case 'MANUAL_EARLY_MEDIA':
176
- this.$emit('ringing', params)
177
- break
178
- case 'MANUAL_ACTIVE':
179
- this.$emit('connected', params)
180
- break
181
- case 'MANUAL_NOT_ACTIVE':
182
- this.handleHangup(params)
183
- break
184
- }
117
+ onCallStatusChange: (callStatus, params) => {
118
+ this.$emit('statusChange', callStatus, params)
119
+ if (callStatus === 'MANUAL_DIALING') this.$emit('dialing', params)
120
+ if (callStatus === 'MANUAL_EARLY_MEDIA') this.$emit('ringing', params)
121
+ if (callStatus === 'MANUAL_ACTIVE') this.$emit('connected', params)
122
+ if (callStatus === 'MANUAL_NOT_ACTIVE') this.$emit('hangup', params)
185
123
  },
186
124
  onFinishedAcw: (acwDuration) => {
187
125
  this.$emit('finishedAcw', acwDuration)
@@ -190,18 +128,17 @@ export default {
190
128
  this.$emit('error', error)
191
129
  },
192
130
  onDisconnect: () => {
193
- this.isInitialized = false
194
131
  this.$emit('offline')
195
- this.scheduleReconnect()
132
+ this.isInitialized = false
133
+ this.handleReconnect()
196
134
  },
197
135
  onReconnect: () => {
198
- this.reconnectCount = 0
199
- this.clearReconnectTimer()
200
136
  this.$emit('reconnect')
201
- },
202
- onWillHangup: (callStatus, isAutoAnswer) => {
203
- this.$emit('willHangup', callStatus, isAutoAnswer)
204
- return Promise.resolve(true)
137
+ this.reconnectCount = 0
138
+ if (this.reconnectTimer) {
139
+ clearTimeout(this.reconnectTimer)
140
+ this.reconnectTimer = null
141
+ }
205
142
  }
206
143
  }
207
144
  }, async () => {
@@ -219,74 +156,70 @@ export default {
219
156
  })
220
157
  },
221
158
 
222
- handleHangup(params) {
223
- this.isCalling = false
224
- const isPeerHangup = params?.isPeerHangup || params?.endReason === 2
225
- if (isPeerHangup) {
226
- this.$emit('peerHangup', params)
159
+ async handleReconnect() {
160
+ if (this.reconnectTimer) {
161
+ return
162
+ }
163
+
164
+ if (this.reconnectCount >= this.maxReconnectCount) {
165
+ this.$emit('error', new Error(`重连失败,已达最大重连次数: ${this.maxReconnectCount}`))
166
+ return
227
167
  }
228
- this.$emit('hangup', {
229
- isSelfHangup: params?.isSelfHangup || params?.endReason === 1,
230
- isPeerHangup,
231
- isFailed: params?.isCallFail,
232
- reason: params?.callFailReason,
233
- params
234
- })
168
+
169
+ this.reconnectCount++
170
+ const delay = Math.min(1000 * Math.pow(2, this.reconnectCount), 30000)
171
+
172
+ this.reconnectTimer = setTimeout(async () => {
173
+ this.reconnectTimer = null
174
+ try {
175
+ this.$emit('reconnecting', this.reconnectCount, this.maxReconnectCount)
176
+ await this.initICall()
177
+ } catch (error) {
178
+ this.$emit('error', error)
179
+ this.handleReconnect()
180
+ }
181
+ }, delay)
235
182
  },
236
183
 
237
184
  async call(phoneNumber) {
238
185
  if (!this.iCall || !this.isInitialized) {
239
186
  throw new Error('拨号盘未初始化')
240
187
  }
241
- this.isCalling = true
242
188
  await this.iCall.call(phoneNumber)
243
189
  },
244
190
 
245
191
  async hangup() {
246
- if (this.iCall && this.isCalling) {
192
+ if (this.iCall) {
247
193
  await this.iCall.hangup()
248
194
  }
249
195
  },
250
196
 
251
- destroy() {
252
- if (this.iCall) {
253
- if (typeof this.iCall.signOut === 'function') {
254
- this.iCall.signOut()
255
- }
256
- this.iCall = null
197
+ async signIn() {
198
+ if (this.iCall && this.isInitialized) {
199
+ await this.iCall.signIn()
257
200
  }
258
- this.isInitialized = false
259
- this.isCalling = false
260
201
  },
261
202
 
262
- handleVisibilityChange() {
263
- if (!document.hidden && !this.isInitialized && this.autoInit) {
264
- this.checkAndInit()
203
+ async signOut() {
204
+ if (this.iCall && this.isInitialized) {
205
+ await this.iCall.signOut()
265
206
  }
266
207
  },
267
208
 
268
- scheduleReconnect() {
269
- if (this.reconnectTimer || this.reconnectCount >= this.maxReconnectCount) return
270
-
271
- this.reconnectCount++
272
- const delay = Math.min(3000 * this.reconnectCount, 15000)
273
-
274
- this.reconnectTimer = setTimeout(async () => {
275
- this.reconnectTimer = null
276
- try {
277
- await this.checkAndInit()
278
- this.$emit('reconnect')
279
- } catch (error) {
280
- this.scheduleReconnect()
281
- }
282
- }, delay)
283
- },
284
-
285
- clearReconnectTimer() {
209
+ destroy() {
286
210
  if (this.reconnectTimer) {
287
211
  clearTimeout(this.reconnectTimer)
288
212
  this.reconnectTimer = null
289
213
  }
214
+ if (this.iCall) {
215
+ if (typeof this.iCall.signOut === 'function') {
216
+ this.iCall.signOut()
217
+ }
218
+ this.iCall = null
219
+ }
220
+ this.isInitialized = false
221
+ const scripts = document.querySelectorAll('script[src*="icall.js"]')
222
+ scripts.forEach(s => s.remove())
290
223
  }
291
224
  }
292
225
  }