apple 1 天之前
父节点
当前提交
b9827fc4a3
共有 11 个文件被更改,包括 371 次插入107 次删除
  1. 4 18
      api/device.js
  2. 2 15
      api/grfw.js
  3. 29 15
      api/user.js
  4. 156 31
      components/SsSelect/index.vue
  5. 1 1
      config/env.js
  6. 1 1
      pages/common/webview.vue
  7. 1 1
      pages/device/index.vue
  8. 1 1
      pages/device/notice.vue
  9. 2 2
      pages/main/index.vue
  10. 113 19
      pages/my/index.vue
  11. 61 3
      pages/parent/message.vue

+ 4 - 18
api/device.js

@@ -88,9 +88,9 @@ export const deviceApi = {
    * 查询留言成员(设备端)
    * @returns {Promise} 返回 chatMbrList
    */
-  grfw_selChatMbr() {
+  mp_telHomep_load() {
     return deviceRequest.post(
-      `/service?ssServ=grfw_selChatMbr`,
+      `/service?ssServ=mp_telHomep_load`,
       {},
       {
         loading: { title: '获取留言联系人...' },
@@ -101,27 +101,13 @@ export const deviceApi = {
       }
     )
   },
-  grfw_loadChatMbr() {
-    return deviceRequest.post(
-      `/service?ssServ=grfw_selChatMbr`,
-      {},
-      {
-        loading: { title: '获取留言联系人...' },
-        formData: true,
-        request: {
-          timeout: 120000
-        }
-      }
-    )
-  },
-
   /**
    * 刷新电话机首页留言提醒(未刷卡态)
    * @returns {Promise} 返回 xyList(家长来信学员) / jzList(电话机发出未读家长)
    */
-  grfw_refreshDhjHome() {
+  tel_refreshLogin() {
     return deviceRequest.post(
-      `/service?ssServ=grfw_refreshDhjHome`,
+      `/service?ssServ=tel_refreshLogin`,
       {},
       {
         loading: false,

+ 2 - 15
api/grfw.js

@@ -7,22 +7,9 @@ export const grfwApi = {
      * @param {object} data - 查询参数(按后端需要透传)
      * @returns {Promise} 返回原始响应
      */
-    grfw_selChatMbr: (data = {}) => {
+    btc_selChatMbr: (data = {}) => {
         return request.post(
-            `/service?ssServ=grfw_selChatMbr`,
-            data,
-            {
-                loading: false,
-                formData: true,
-                request: {
-                    timeout: 120000
-                }
-            }
-        );
-    },
-    grfw_loadChatMbr: (data = {}) => {
-        return request.post(
-            `/service?ssServ=grfw_selChatMbr`,
+            `/service?ssServ=btc_selChatMbr`,
             data,
             {
                 loading: false,

+ 29 - 15
api/user.js

@@ -88,21 +88,35 @@ export const userApi = {
     return promise;
   },
 
-  // 获取小程序用户信息
-  mp_infoHomep_load(data){
-    return request.post( `/service?ssServ=mp_infoHomep_load`,
-      data,
-      {
-        loading: true,
-        formData: true,
-      }
-    );  
-  },
-  // 更新用户密码和信息
-  updPwd(data){
-    return request.post( `/service?ssServ=updPwd`,
-      data,
-      {
+  // 获取小程序用户信息
+  mp_infoHomep_load(data){
+    return request.post( `/service?ssServ=mp_infoHomep_load`,
+      data,
+      {
+        loading: true,
+        formData: true,
+      }
+    );  
+  },
+
+  // 通用服务调用(用于首页动态按钮状态查询)
+  callService(ssServ, data = {}, options = {}) {
+    return request.post(
+      `/service?ssServ=${ssServ}`,
+      data,
+      {
+        loading: false,
+        formData: true,
+        ...options
+      }
+    );
+  },
+
+  // 更新用户密码和信息
+  updPwd(data){
+    return request.post( `/service?ssServ=updPwd`,
+      data,
+      {
         loading: true,
         formData: true,
       }

+ 156 - 31
components/SsSelect/index.vue

@@ -11,10 +11,10 @@
     <!-- 选项列表 -->
     <view class="ss-options" v-show="isOpen">
       <!-- 加载状态 -->
-      <view
-        v-if="loading"
-        class="option-item loading-item"
-      >
+      <view
+        v-if="finalLoading"
+        class="option-item loading-item"
+      >
         <text class="loading-text">加载中...</text>
       </view>
       <!-- 无选项 -->
@@ -41,9 +41,10 @@
   </view>
 </template>
 
-<script setup>
-import { ref, computed, watch, onMounted, onUnmounted } from 'vue'
-import Icon from '@/components/icon/index.vue';
+<script setup>
+import { ref, computed, watch, onMounted, onUnmounted } from 'vue'
+import request from '@/utils/request'
+import Icon from '@/components/icon/index.vue';
 // Props 定义
 const props = defineProps({
   // 选项数组
@@ -96,22 +97,135 @@ const props = defineProps({
     type: String,
     default: '100%'
   },
-  minWidth:{
-    type:String,
-    default:'unset'
-  }
-})
+  minWidth:{
+    type:String,
+    default:'unset'
+  },
+  // 对齐PC端 ss-objp:支持组件内部按 codebook 拉取下拉选项
+  cb: {
+    type: String,
+    default: ''
+  },
+  url: {
+    type: String,
+    default: '/service?ssServ=loadObjpOpt&objectpickerdropdown1=1'
+  },
+  inp: {
+    type: [Boolean, String],
+    default: false
+  },
+  filter: {
+    type: [Object, String],
+    default: null
+  },
+  autoSelectFirst: {
+    type: Boolean,
+    default: false
+  }
+})
 
 // Emits 定义
 const emit = defineEmits(['update:modelValue', 'change', 'search', 'clear'])
 
 // 响应式数据
-const isOpen = ref(false)
-const selectedValue = ref(props.modelValue)
-const searchKeyword = ref('')
-
-// 计算属性
-const optionsList = computed(() => props.options || [])
+const isOpen = ref(false)
+const selectedValue = ref(props.modelValue)
+const searchKeyword = ref('')
+const remoteOptions = ref([])
+const internalLoading = ref(false)
+
+const parseFilterObj = () => {
+  if (!props.filter) return {}
+  if (typeof props.filter === 'object') return props.filter
+  if (typeof props.filter === 'string') {
+    try {
+      const obj = JSON.parse(props.filter)
+      return obj && typeof obj === 'object' ? obj : {}
+    } catch (_) {
+      return {}
+    }
+  }
+  return {}
+}
+
+const normalizeResultToOptions = (respData) => {
+  const raw = respData || {}
+  if (Array.isArray(raw.resultList)) {
+    return raw.resultList.map((it) => {
+      if (it && typeof it === 'object') return it
+      return { n: String(it || ''), v: String(it || '') }
+    })
+  }
+  if (raw.result && typeof raw.result === 'object') {
+    return Object.keys(raw.result).map((key) => ({
+      n: raw.result[key],
+      v: key
+    }))
+  }
+  if (Array.isArray(raw.objectList)) {
+    return raw.objectList.map((it) => {
+      if (it && typeof it === 'object') return it
+      return { n: String(it || ''), v: String(it || '') }
+    })
+  }
+  return []
+}
+
+const maybeAutoSelectFirst = (opts) => {
+  if (!props.autoSelectFirst) return
+  if (!Array.isArray(opts) || opts.length === 0) return
+  if (selectedValue.value !== undefined && selectedValue.value !== null && selectedValue.value !== '') return
+  const first = opts[0]
+  if (!first || typeof first !== 'object') return
+  const value = first[props.mapping.value]
+  if (value === undefined || value === null || value === '') return
+  selectedValue.value = value
+  emit('update:modelValue', value)
+  emit('change', value, first)
+}
+
+const needRemoteData = computed(() => !!String(props.cb || '').trim())
+
+const loadRemoteOptions = async () => {
+  if (!needRemoteData.value) return
+
+  internalLoading.value = true
+  try {
+    const data = {
+      objectpickerparam: JSON.stringify({
+        input: String(props.inp === true || props.inp === 'true'),
+        codebook: String(props.cb || ''),
+        ...parseFilterObj()
+      }),
+      objectpickertype: 1,
+      objectpickersearchAll: 1
+    }
+    const resp = await request.post(
+      props.url || '/service?ssServ=loadObjpOpt&objectpickerdropdown1=1',
+      data,
+      {
+        loading: false,
+        formData: true,
+      }
+    )
+    const opts = normalizeResultToOptions(resp?.data)
+    remoteOptions.value = opts
+    maybeAutoSelectFirst(opts)
+  } catch (error) {
+    remoteOptions.value = []
+    console.error('[SsSelect] loadRemoteOptions failed', props.cb, error)
+  } finally {
+    internalLoading.value = false
+  }
+}
+
+// 计算属性
+const optionsList = computed(() => {
+  if (Array.isArray(props.options) && props.options.length > 0) return props.options
+  if (needRemoteData.value) return remoteOptions.value
+  return []
+})
+const finalLoading = computed(() => !!props.loading || internalLoading.value)
 
 const displayText = computed(() => {
   if (!selectedValue.value) return props.placeholder
@@ -182,12 +296,22 @@ const handleGlobalClose = () => {
   closeDropdown()
 }
 
-onMounted(() => {
-  uni.$on('closeAllSelects', handleGlobalClose)
-  
-  // 点击页面其他地方关闭下拉框
-  uni.$on('pageClick', closeDropdown)
-})
+onMounted(() => {
+  uni.$on('closeAllSelects', handleGlobalClose)
+  
+  // 点击页面其他地方关闭下拉框
+  uni.$on('pageClick', closeDropdown)
+
+  loadRemoteOptions()
+})
+
+watch(
+  () => [props.cb, props.url, props.filter],
+  () => {
+    if (!needRemoteData.value) return
+    loadRemoteOptions()
+  }
+)
 
 onUnmounted(() => {
   uni.$off('closeAllSelects', handleGlobalClose)
@@ -198,13 +322,14 @@ onUnmounted(() => {
 defineExpose({
   setValue,
   getValue: () => selectedValue.value,
-  getSelectedOption: () => {
-    return optionsList.value.find(
-      option => option[props.mapping.value] === selectedValue.value
-    )
-  }
-})
-</script>
+  getSelectedOption: () => {
+    return optionsList.value.find(
+      option => option[props.mapping.value] === selectedValue.value
+    )
+  },
+  reloadOptions: loadRemoteOptions
+})
+</script>
 
 <style lang="scss" scoped>
 /* ss暂用下拉框样式 */

+ 1 - 1
config/env.js

@@ -1,5 +1,5 @@
 export default {
-  // baseUrl: 'http://192.168.220.13:8080',
+  // baseUrl: 'http://192.168.3.186:8080',
   baseUrl: 'https://m.hfdcschool.com',
   // baseUrl: 'https://yx.newfeifan.cn',
 }

+ 1 - 1
pages/common/webview.vue

@@ -33,7 +33,7 @@ const buildWebviewUrl = (params) => {
   // const baseUrl = 'http://127.0.0.1:5501/page/'
   // const baseUrl = 'https://yx.newfeifan.cn/page/'
   const baseUrl = 'https://m.hfdcschool.com/page'
-  // const baseUrl = 'http://192.168.220.13:8080/page'
+  // const baseUrl = 'http://192.168.3.186:8080/page'
   
   
 

+ 1 - 1
pages/device/index.vue

@@ -702,7 +702,7 @@ export default {
 			},
 		async getContactList() {
 			try {
-				const result = await deviceApi.grfw_selChatMbr()
+				const result = await deviceApi.mp_telHomep_load()
 				const data = result && result.data ? result.data : {}
 				const chatMbrList = Array.isArray(data.chatMbrList) ? data.chatMbrList : []
 				const flatChatMbrList = chatMbrList.flatMap((item) => (Array.isArray(item) ? item : [item])).filter(Boolean)

+ 1 - 1
pages/device/notice.vue

@@ -313,7 +313,7 @@ function resolveDeviceSn(options = {}) {
 
 async function refreshNoticeUsers() {
 	try {
-		const result = await deviceApi.grfw_refreshDhjHome()
+		const result = await deviceApi.tel_refreshLogin()
 		const payload = extractRefreshPayload(result?.data || {})
 		let xyList = Array.isArray(payload.xyList) ? payload.xyList : []
 		let jzList = Array.isArray(payload.jzList) ? payload.jzList : []

+ 2 - 2
pages/main/index.vue

@@ -272,8 +272,8 @@ function buildPagesFromAuth() {
 // 主容器的生命周期
 onLoad((options) => {
     // 测试写死
-    options.sn = 'ssDevId_a'
-    options.cardNo = 'E00401532101245F'
+    // options.sn = 'ssDevId_a'
+    // options.cardNo = 'E00401532101245F'
     if(options.cardNo == 'E004015316BE6182'){
         options.cardNo = 'E004015316BE61821'
     }

+ 113 - 19
pages/my/index.vue

@@ -24,14 +24,6 @@
 		<!-- 登录之后 -->
 		<view class="container" v-if="isLogin">
 			<view class="function-list">
-				<!-- 充值功能 -->
-				<view class="function-item" @click="goToRecharge">
-					<view class="function-icon recharge-icon">
-						<Icon name="icon-qianbao" size="60" color="#07c160" />
-					</view>
-					<text class="function-name">订阅</text>
-				</view>
-
 				<!-- 校车位置(家长) -->
 				<!-- <view class="function-item" @click="goToBusLocation">
 					<view class="function-icon">
@@ -40,14 +32,6 @@
 					<text class="function-name">校车位置</text>
 				</view> -->
 
-				<!-- 留言 -->
-				<view class="function-item" @click="goToMessage">
-					<view class="function-icon">
-						<Icon name="icon-duihuapaopao2" size="60" color="#ff9f43" />
-					</view>
-					<text class="function-name">留言</text>
-				</view>
-
 				<!-- 留言功能 -->
 				<!-- <view class="function-item" @click="goToMessage">
 					<view class="function-icon">
@@ -75,9 +59,10 @@
 					</view>
 					<text class="function-name">班主任点名</text>
 				</view> -->
-				<view class="function-item" v-for="item in btnList" :key="item.id" @click="commonBtnClick(item)">
+				<view class="function-item" v-for="item in btnList" :key="item.id || item.xh || item.dest" @click="commonBtnClick(item)">
 					<view class="function-icon">
 						<image :src="getFullIconUrl(item.icon)" mode="aspectFill"></image>
+						<view class="badge" v-if="item.hasNewDot"></view>
 					</view>
 					<text class="function-name">{{ item.desc }}</text>
 				</view>
@@ -216,6 +201,101 @@ const LOGIN_TYPE_MAP = {
 
 const btnList = ref([])
 
+const toNumber = (value) => {
+	if (value === null || value === undefined || value === '') {
+		return 0
+	}
+	const numberValue = Number(value)
+	return Number.isNaN(numberValue) ? 0 : numberValue
+}
+
+const extractCountFromResponse = (payload) => {
+	if (payload === null || payload === undefined) {
+		return 0
+	}
+
+	if (typeof payload === 'number' || typeof payload === 'string') {
+		return toNumber(payload)
+	}
+
+	if (Array.isArray(payload)) {
+		for (const element of payload) {
+			const count = extractCountFromResponse(element)
+			if (count > 0) {
+				return count
+			}
+		}
+		return 0
+	}
+
+	if (typeof payload === 'object') {
+		if (payload.ssCode !== undefined) {
+			return payload.ssCode === 0 ? toNumber(payload.ssData) : 0
+		}
+
+		const preferredKeys = ['newMsgNum', 'msgNum', 'count', 'total', 'num', 'value']
+		for (const key of preferredKeys) {
+			if (payload[key] !== undefined) {
+				const count = extractCountFromResponse(payload[key])
+				if (count > 0) {
+					return count
+				}
+			}
+		}
+
+		for (const key of Object.keys(payload)) {
+			const count = extractCountFromResponse(payload[key])
+			if (count > 0) {
+				return count
+			}
+		}
+	}
+
+	return 0
+}
+
+const fetchBtnStateCount = async (stateService) => {
+	if (!stateService) {
+		return 0
+	}
+
+	try {
+		const response = await userApi.callService(stateService)
+		const count = extractCountFromResponse(response?.data)
+		console.log(`[my] state接口 ${stateService} 返回数量:`, count, response?.data)
+		return count
+	} catch (error) {
+		console.error(`[my] state接口 ${stateService} 调用失败:`, error)
+		return 0
+	}
+}
+
+const updateBtnBadgeStatus = async () => {
+	if (!btnList.value.length) {
+		return
+	}
+
+	const nextBtnList = await Promise.all(
+		btnList.value.map(async (item) => {
+			const stateService = (item.state || '').trim()
+			if (!stateService) {
+				return {
+					...item,
+					hasNewDot: false
+				}
+			}
+
+			const count = await fetchBtnStateCount(stateService)
+			return {
+				...item,
+				hasNewDot: count > 0
+			}
+		})
+	)
+
+	btnList.value = nextBtnList
+}
+
 const getFullIconUrl = (icon) => {
 	return `${env.baseUrl}/skin/mp_easy/icon/${icon}.svg`
 }
@@ -226,13 +306,23 @@ const commonBtnClick = (item) => {
 		return
 	}
 
+	if (item.dest === 'chat') {
+		goToMessage()
+		return
+	}
+
+	if (item.dest === 'buy') {
+		goToRecharge()
+		return
+	}
+
 	console.log(`准备进入 ${item.desc},先检查订阅消息`)
 
 	// 先处理订阅,订阅完成后再跳转
 	openPopup(() => {
 		console.log('订阅处理完成,准备跳转')
 		goTo('/pages/common/webview', {
-			dest: item.dest,
+			dest: item.dest || "mp_objList",
 			title: item.desc,
 			service: item.init
 		})
@@ -715,7 +805,11 @@ const getUserInfo = async () => {
 	}
 	const userMoreInfo = await userApi.mp_infoHomep_load(userInfo.userId)
 	console.log('userMoreInfo', userMoreInfo.data.ryInfo, typeof userMoreInfo.data.ryInfo)
-	btnList.value = JSON.parse(userMoreInfo.data.btnList) // 更新按钮列表
+	btnList.value = JSON.parse(userMoreInfo.data.btnList || '[]').sort((a, b) => (a.xh || 0) - (b.xh || 0)).map(item => ({
+		...item,
+		hasNewDot: false
+	})) // 更新按钮列表
+	await updateBtnBadgeStatus()
 	
 	const rylbm = userMoreInfo.data.ryInfo?.rylbm;
 	let huixian = '已登录';

+ 61 - 3
pages/parent/message.vue

@@ -449,6 +449,7 @@ const miniprogramState = (() => {
 				serviceStatus: null,
 				inactivityTimer: null,
 				inactivityTimeout: 180000,
+				isCalling: false,
 				modalVisible: false,
 				modalTitle: '提示',
 				modalContent: '',
@@ -586,7 +587,27 @@ const miniprogramState = (() => {
 					const snFromOption = String(options.sn || '').trim()
 					const cardNo = String(options.cardNo || '').trim()
 					const sn = snFromOption || String(userInfo.devId || '').trim()
-					if (!sn || !cardNo) return false
+					if (!sn) {
+						uni.showToast({ title: '缺少设备标识,请重新刷卡登录', icon: 'none' })
+						setTimeout(() => {
+							uni.reLaunch({ url: '/pages/device/notice' })
+						}, 1200)
+						return false
+					}
+					if (!cardNo) {
+						const hasSession = !!(userInfo.yhsbToken || userInfo.onlineToken || userInfo.sessId)
+						if (!hasSession) {
+							uni.showToast({ title: '登录已失效,请重新刷卡登录', icon: 'none' })
+							setTimeout(() => {
+								uni.reLaunch({ url: '/pages/device/notice' })
+							}, 1200)
+							return false
+						}
+						this.refreshCurrentUserMeta()
+						this.resolveWsIdentity()
+						this.currentWsConfig = this.buildWsConnectOptions()
+						return true
+					}
 					try {
 						const result = await deviceApi.login(sn, cardNo)
 						const loginData = result?.data || {}
@@ -602,6 +623,7 @@ const miniprogramState = (() => {
 							xm: loginData.xm || '',
 							yhsbToken: loginData.yhsbToken || '',
 							onlineToken: loginData.onlineToken || '',
+							cardNo,
 							syList: loginData.sylist || loginData.syList || [],
 							yhid: loginData.yhid || '',
 							yhm: loginData.yhm || ''
@@ -634,7 +656,7 @@ const miniprogramState = (() => {
 				},
 				async loadParentChatMembers() {
 					try {
-						const result = await grfwApi.grfw_selChatMbr({})
+						const result = await grfwApi.btc_selChatMbr({})
 						const data = result?.data || {}
 						const list = this.normalizeChatMemberList(data.chatMbrList)
 						this.parentChatMembers = list
@@ -660,7 +682,7 @@ const miniprogramState = (() => {
 				},
 				async loadDeviceChatMembers() {
 					try {
-						const result = await deviceApi.grfw_selChatMbr()
+						const result = await deviceApi.mp_telHomep_load()
 						const data = result?.data || {}
 						const list = this.normalizeChatMemberList(data.chatMbrList)
 						this.parentChatMembers = list
@@ -890,6 +912,11 @@ const miniprogramState = (() => {
 							uni.showToast({ title: '请让家长先关注公众号,登陆小程序后再发起', icon: 'none' })
 							return
 						}
+						this.isCalling = true
+						if (this.inactivityTimer) {
+							clearTimeout(this.inactivityTimer)
+							this.inactivityTimer = null
+						}
 						uni.showLoading({ title: '呼叫中...', mask: true })
 						uni.setStorageSync(CALL_END_STORAGE_KEY, {
 							name: contact?.username || '家长',
@@ -919,10 +946,14 @@ const miniprogramState = (() => {
 								uni.redirectTo({ url: wmpfVoip.CALL_PAGE_PATH })
 								return
 							}
+							this.isCalling = false
+							this.resetInactivityTimer()
 							uni.hideLoading()
 							uni.showToast({ title: '呼叫失败', icon: 'error' })
 							return
 						} catch (error) {
+							this.isCalling = false
+							this.resetInactivityTimer()
 							uni.hideLoading()
 							console.error('设备端通话异常:', error)
 							uni.showToast({ title: '发起通话失败', icon: 'none' })
@@ -1008,6 +1039,7 @@ const miniprogramState = (() => {
 				},
 				resetInactivityTimer() {
 					if (this.sessionRole !== 'device') return
+					if (this.isCalling) return
 					if (this.inactivityTimer) {
 						clearTimeout(this.inactivityTimer)
 					}
@@ -1067,6 +1099,9 @@ const miniprogramState = (() => {
 						if (userInfo.devId) {
 							query.push(`sn=${encodeURIComponent(userInfo.devId)}`)
 						}
+						if (userInfo.cardNo) {
+							query.push(`cardNo=${encodeURIComponent(userInfo.cardNo)}`)
+						}
 					}
 					if (callInfo.name) {
 						query.push(`name=${encodeURIComponent(callInfo.name)}`)
@@ -1087,6 +1122,14 @@ const miniprogramState = (() => {
 					if (this._voipEventRegistered || !wmpfVoip) return
 					wmpfVoip.onVoipEvent((event) => {
 						const eventName = event.eventName
+						console.log('[VoIP] event:', eventName, event.data || {})
+						if (eventName === 'startVoip') {
+							this.isCalling = true
+							if (this.inactivityTimer) {
+								clearTimeout(this.inactivityTimer)
+								this.inactivityTimer = null
+							}
+						}
 						const hangupEvent = ['hangUpVoip', 'endVoip']
 						const cancelEvent = ['cancelVoip']
 						const timeoutEvent = ['timeout']
@@ -1109,11 +1152,26 @@ const miniprogramState = (() => {
 							callInfo.status = '已拒绝'
 						}
 						if ([...hangupEvent, ...cancelEvent, ...timeoutEvent, ...rejectEvent].includes(eventName)) {
+							this.isCalling = false
 							callInfo.endType = eventName
 							callInfo.endTime = Date.now()
 							uni.hideLoading()
 							uni.setStorageSync(CALL_END_STORAGE_KEY, callInfo)
 							this.setVoipEndPagePath(true)
+							if (this.sessionRole === 'device') {
+								let toastTitle = '通话已结束'
+								if (cancelEvent.includes(eventName)) {
+									toastTitle = '已取消呼叫'
+								} else if (timeoutEvent.includes(eventName)) {
+									toastTitle = '对方暂未接听'
+								} else if (rejectEvent.includes(eventName)) {
+									toastTitle = '对方已拒绝'
+								} else if (hangupEvent.includes(eventName)) {
+									const keepTime = Number(event?.data?.keepTime || 0)
+									toastTitle = keepTime > 0 ? '通话已结束' : '未接通已结束'
+								}
+								uni.showToast({ title: toastTitle, icon: 'none' })
+							}
 							if (this.sessionRole === 'device' && callInfo.needRecord && callInfo.duration > 0) {
 								this.recordCallAndRefresh({
 									duration: callInfo.duration,