call.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475
  1. //#ifdef APP-PLUS
  2. import {
  3. startPhoneListener,
  4. stopPhoneListener,
  5. checkIsAutoRecord,
  6. toCallAutoRecorderPage,
  7. navigateToCallRecordingSettings,
  8. jumpToPermissionPage,
  9. allRecorderFilesAction
  10. } from '@/uni_modules/yao-lister';
  11. // #endif
  12. import permision from "@/js_sdk/wa-permission/permission.js";
  13. import {
  14. formatPhoneNumber
  15. } from '../../utils/util';
  16. export default {
  17. namespaced: true,
  18. state: {
  19. filelist: [],
  20. isAutoRecord: false, // 通话自动录音状态
  21. hasStoragePermission: false, // 文件访问权限状态
  22. hasReadPhoneStatePermission: false, // 读取手机状态权限
  23. hasCallPhonePermission: false, // 拨打电话权限
  24. hasReadCallLogPermission: false, // 读取通话记录权限
  25. hasReadPhoneNumbersPermission: false, // 读取电话号码权限
  26. isPhoneListening: false, // 电话监听状态
  27. form: {
  28. clueId: undefined,
  29. fileUrl: undefined,
  30. fileName: undefined,
  31. type: '3',
  32. list: [],
  33. caller: '',
  34. callee: ''
  35. },
  36. },
  37. mutations: {
  38. SET_FILE_LIST: (state, data) => {
  39. state.filelist = data.data
  40. },
  41. SET_AUTO_RECORD: (state, value) => {
  42. state.isAutoRecord = value;
  43. },
  44. SET_STORAGE_PERMISSION: (state, value) => {
  45. state.hasStoragePermission = value;
  46. },
  47. SET_READ_PHONE_STATE_PERMISSION: (state, value) => {
  48. state.hasReadPhoneStatePermission = value;
  49. },
  50. SET_CALL_PHONE_PERMISSION: (state, value) => {
  51. state.hasCallPhonePermission = value;
  52. },
  53. SET_READ_CALL_LOG_PERMISSION: (state, value) => {
  54. state.hasReadCallLogPermission = value;
  55. },
  56. SET_READ_PHONE_NUMBERS_PERMISSION: (state, value) => {
  57. state.hasReadPhoneNumbersPermission = value;
  58. },
  59. SET_PHONE_LISTENING_STATUS: (state, value) => {
  60. state.isPhoneListening = value;
  61. },
  62. SET_CALLER_PHONE: (state, phoneNumber) => {
  63. state.form.caller = phoneNumber;
  64. },
  65. SET_FORM: (state, data) => {
  66. state.form = data;
  67. },
  68. },
  69. actions: {
  70. // 获取文件列表 - 返回Promise方式
  71. getFileList({
  72. dispatch,
  73. state,
  74. commit
  75. }) {
  76. return new Promise((resolve, reject) => {
  77. dispatch("checkStoragePermission").then(() => {
  78. if (state.hasStoragePermission) {
  79. allRecorderFilesAction(res => {
  80. if (res && res.length > 0) {
  81. // 将文件路径数组转换为包含filePath和fileName的对象数组
  82. const fileListPromise = res.map(filePath => {
  83. return new Promise((resolve) => {
  84. // 从路径中提取文件名
  85. const fileName = filePath.split('/')
  86. .pop();
  87. // 获取文件信息,包括创建时间
  88. plus.io.resolveLocalFileSystemURL(
  89. filePath, (entry) => {
  90. entry.getMetadata((
  91. metadata) => {
  92. resolve({
  93. filePath: filePath,
  94. fileName: fileName,
  95. createTime: metadata
  96. .modificationTime ?
  97. metadata
  98. .modificationTime :
  99. 0, // 文件创建时间
  100. });
  101. }, (error) => {
  102. // 如果获取metadata失败,返回基本信息
  103. resolve({
  104. filePath: filePath,
  105. fileName: fileName,
  106. createTime: 0, // 设为0,排序时会在最前面
  107. });
  108. });
  109. }, (error) => {
  110. // 如果解析文件路径失败,返回基本信息
  111. resolve({
  112. filePath: filePath,
  113. fileName: fileName,
  114. createTime: 0,
  115. size: 0
  116. });
  117. });
  118. });
  119. });
  120. // 等待所有文件信息获取完成后进行排序
  121. Promise.all(fileListPromise).then((fileList) => {
  122. // 按创建时间排序,最新的文件排在前面(降序)
  123. fileList.sort((a, b) => {
  124. return b.createTime - a.createTime;
  125. });
  126. // 提交文件列表到state
  127. commit('SET_FILE_LIST', {
  128. data: fileList
  129. });
  130. // 返回文件列表
  131. resolve(fileList);
  132. }).catch((error) => {
  133. console.error('获取文件列表失败:', error);
  134. // 即使排序失败,也尝试提交原始列表
  135. const fallbackFileList = res.map(filePath => {
  136. const fileName = filePath.split('/')
  137. .pop();
  138. return {
  139. filePath: filePath,
  140. fileName: fileName
  141. };
  142. });
  143. commit('SET_FILE_LIST', {
  144. data: fallbackFileList
  145. });
  146. resolve(fallbackFileList);
  147. });
  148. } else {
  149. // 没有文件时返回空数组
  150. commit('SET_FILE_LIST', {
  151. data: []
  152. });
  153. resolve([]);
  154. }
  155. })
  156. } else {
  157. dispatch("requestStoragePermission");
  158. reject(new Error('没有文件访问权限'));
  159. }
  160. }).catch(error => {
  161. reject(error);
  162. });
  163. });
  164. },
  165. // 获取用户手机号码
  166. getUserPhoneNumber({
  167. commit
  168. }) {
  169. return new Promise((resolve, reject) => {
  170. // 首先检查权限
  171. permision.requestAndroidPermission('android.permission.READ_PHONE_NUMBERS')
  172. .then(result => {
  173. if (result === 1) {
  174. // 权限被授予,获取手机号码
  175. try {
  176. const main = plus.android.runtimeMainActivity();
  177. const Context = plus.android.importClass('android.content.Context');
  178. const TelephonyManager = plus.android.importClass(
  179. 'android.telephony.TelephonyManager');
  180. const telephonyManager = main.getSystemService(Context.TELEPHONY_SERVICE);
  181. const phoneNumber = telephonyManager.getLine1Number();
  182. if (phoneNumber && phoneNumber.trim() !== '') {
  183. // 移除国际区号,保留手机号码
  184. const cleanPhoneNumber = formatPhoneNumber(phoneNumber);
  185. commit('SET_CALLER_PHONE', cleanPhoneNumber);
  186. resolve(cleanPhoneNumber);
  187. } else {
  188. // 获取不到手机号码
  189. reject(new Error('无法获取手机号码:设备可能不支持'));
  190. }
  191. } catch (error) {
  192. console.error('获取手机号码失败:', error);
  193. reject(new Error('获取手机号码失败: ' + error.message));
  194. }
  195. } else {
  196. reject(new Error('没有获取手机号码的权限,请前往设置开启权限'));
  197. }
  198. })
  199. .catch(error => {
  200. console.error('请求权限失败:', error);
  201. reject(error);
  202. });
  203. });
  204. },
  205. resetForm({
  206. commit
  207. }) {
  208. commit("SET_FORM", {
  209. clueId: undefined,
  210. fileUrl: undefined,
  211. fileName: undefined,
  212. type: '3',
  213. list: [],
  214. caller: '',
  215. callee: ''
  216. })
  217. },
  218. // 检查所有权限状态
  219. checkAllPermissions({
  220. commit
  221. }) {
  222. return new Promise((resolve, reject) => {
  223. try {
  224. // 检查通话自动录音状态
  225. const autoRecordStatus = checkIsAutoRecord();
  226. commit('SET_AUTO_RECORD', autoRecordStatus);
  227. // 检查文件访问权限
  228. this.dispatch('call/checkStoragePermission').then(storagePermission => {
  229. commit('SET_STORAGE_PERMISSION', storagePermission);
  230. // 检查其他权限
  231. const permissions = [{
  232. id: 'android.permission.READ_PHONE_STATE',
  233. mutation: 'SET_READ_PHONE_STATE_PERMISSION'
  234. },
  235. {
  236. id: 'android.permission.CALL_PHONE',
  237. mutation: 'SET_CALL_PHONE_PERMISSION'
  238. },
  239. {
  240. id: 'android.permission.READ_CALL_LOG',
  241. mutation: 'SET_READ_CALL_LOG_PERMISSION'
  242. },
  243. {
  244. id: 'android.permission.READ_PHONE_NUMBERS',
  245. mutation: 'SET_READ_PHONE_NUMBERS_PERMISSION'
  246. }
  247. ];
  248. const permissionPromises = permissions.map(perm => {
  249. return permision.requestAndroidPermission(perm.id).then(result => {
  250. commit(perm.mutation, result === 1);
  251. return {
  252. id: perm.id,
  253. granted: result === 1
  254. };
  255. });
  256. });
  257. Promise.all(permissionPromises).then(results => {
  258. resolve({
  259. autoRecordStatus,
  260. storagePermission,
  261. permissions: results
  262. });
  263. });
  264. });
  265. } catch (error) {
  266. reject(error);
  267. }
  268. });
  269. },
  270. // 检查文件访问权限
  271. checkStoragePermission({
  272. commit
  273. }) {
  274. return new Promise((resolve) => {
  275. let hasPermission = false;
  276. try {
  277. // 获取Android版本号
  278. let androidVersion = 0;
  279. uni.getSystemInfo({
  280. success: (res) => {
  281. if (res.system && res.system.includes('Android')) {
  282. const versionMatch = res.system.match(/Android (\d+\.?\d*)/);
  283. if (versionMatch && versionMatch[1]) {
  284. androidVersion = parseFloat(versionMatch[1]);
  285. }
  286. }
  287. if (androidVersion >= 11) {
  288. // Android 11及以上版本,使用MANAGE_EXTERNAL_STORAGE权限
  289. const Environment = plus.android.importClass(
  290. 'android.os.Environment');
  291. hasPermission = Environment.isExternalStorageManager();
  292. commit('SET_STORAGE_PERMISSION', hasPermission);
  293. resolve(hasPermission);
  294. } else {
  295. // Android 11以下版本,使用READ_EXTERNAL_STORAGE权限
  296. permision.requestAndroidPermission(
  297. 'android.permission.READ_EXTERNAL_STORAGE')
  298. .then(result => {
  299. hasPermission = result === 1;
  300. commit('SET_STORAGE_PERMISSION', hasPermission);
  301. resolve(hasPermission);
  302. });
  303. }
  304. }
  305. });
  306. } catch (e) {
  307. hasPermission = false;
  308. commit('SET_STORAGE_PERMISSION', hasPermission);
  309. resolve(hasPermission);
  310. }
  311. });
  312. },
  313. // 请求文件访问权限
  314. requestStoragePermission() {
  315. jumpToPermissionPage();
  316. },
  317. // 跳转到通话录音设置页面
  318. toCallRecorderSettings() {
  319. toCallAutoRecorderPage();
  320. },
  321. // 跳转到系统通话录音界面
  322. toSystemRecorderSettings() {
  323. navigateToCallRecordingSettings();
  324. },
  325. // 打开系统设置
  326. handleOpenSet() {
  327. permision.gotoAppPermissionSetting('android.permission.RECORD_AUDIO');
  328. },
  329. // 启动电话监听
  330. startPhoneListener({
  331. commit,
  332. dispatch,
  333. state
  334. }) {
  335. try {
  336. if (state.isPhoneListening) {
  337. // 已经开启监听就不要了
  338. return;
  339. }
  340. // 用于记录上一个状态,检测通话结束
  341. let previousState = null;
  342. dispatch("checkAllPermissions").finally(() => {
  343. startPhoneListener(res => {
  344. if (state.form.clueId) {
  345. // 是通过app监听打出去触发;
  346. dispatch("handleCallBack", {
  347. currentState: res,
  348. previousState
  349. });
  350. }
  351. // 更新上一个状态
  352. previousState = res;
  353. commit('SET_PHONE_LISTENING_STATUS', true);
  354. });
  355. });
  356. } catch (error) {
  357. console.error('启动电话监听失败:', error);
  358. commit('SET_PHONE_LISTENING_STATUS', false);
  359. throw error;
  360. }
  361. },
  362. handleCallBack({
  363. dispatch,
  364. commit,
  365. state
  366. }, {
  367. currentState,
  368. previousState
  369. } = {}) {
  370. if (previousState && currentState) {
  371. if (previousState === '通话中') {
  372. if (currentState === '空闲状态') {
  373. // 延迟一下再获取,确保录音文件已经生成
  374. setTimeout(() => {
  375. dispatch('getLatestRecordingFile').then((file) => {
  376. uni.showModal({
  377. title: '检测您的拨号行为,是否上传录音?',
  378. content: '最新一条电话录音:' + file.fileName +",是否前往关联?",
  379. success: function (res) {
  380. if (res.confirm) {
  381. uni.navigateTo({
  382. url: `/pages/uploadRecord/index?clueId=${state.form.clueId}`
  383. })
  384. } else if (res.cancel) {
  385. dispatch("resetForm");
  386. }
  387. }
  388. });
  389. }).catch(error => {
  390. uni.$u.toast('获取录音文件失败:' + error);
  391. });
  392. }, 2000); // 延迟1秒获取
  393. }
  394. }
  395. }
  396. },
  397. // 获取最新的录音文件并赋值给form
  398. getLatestRecordingFile({
  399. commit,
  400. state
  401. }) {
  402. return new Promise((resolve, reject) => {
  403. // 获取文件列表 - 使用return Promise的方式
  404. this.dispatch('call/getFileList').then((fileList) => {
  405. if (fileList && fileList.length > 0) {
  406. // 获取最新创建的录音文件(索引0是最新的)
  407. const latestFile = fileList[0];
  408. // 更新form中的fileUrl和fileName
  409. const updatedForm = {
  410. ...state.form,
  411. fileUrl: latestFile.filePath,
  412. fileName: latestFile.fileName
  413. };
  414. commit('SET_FORM', updatedForm);
  415. resolve(latestFile);
  416. } else {
  417. reject(new Error('未找到录音文件'));
  418. }
  419. }).catch(error => {
  420. console.error('获取文件列表失败:', error);
  421. reject(error);
  422. });
  423. });
  424. },
  425. // 停止电话监听
  426. stopPhoneListener({
  427. commit
  428. }) {
  429. try {
  430. stopPhoneListener(res => {
  431. commit('SET_PHONE_LISTENING_STATUS', false);
  432. });
  433. } catch (error) {
  434. console.error('停止电话监听失败:', error);
  435. commit('SET_PHONE_LISTENING_STATUS', false);
  436. throw error;
  437. }
  438. },
  439. // 切换电话监听状态
  440. togglePhoneListener({
  441. state,
  442. dispatch
  443. }) {
  444. if (state.isPhoneListening) {
  445. return dispatch('stopPhoneListener');
  446. } else {
  447. return dispatch('startPhoneListener');
  448. }
  449. }
  450. },
  451. }