history.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502
  1. <!--
  2. 更新内容:解决第一次授予权限获取不到通话记录
  3. 更新人:吴宝松
  4. 更新时间:2024/8/28
  5. 更新内容:点击详情可以获取同一个联系人所有号码
  6. 更新人:方新力
  7. 更新时间:2024/9.1
  8. 更新内容:修改顶部标题样式、通话记录中联系人未接没有标红问题
  9. 更新人:张志宏
  10. 时间:24/9/3
  11. -->
  12. <template>
  13. <view class="content">
  14. <!-- 顶部标题 -->
  15. <view class="head">
  16. <text >通话记录</text>
  17. </view>
  18. <!-- 显示通话记录 -->
  19. <view class="text-area">
  20. <view v-if="telephoneLog.length" v-for="(log, index) in limitedItems" :key="index">
  21. <view class="name-btn">
  22. <view v-if="switchStatus" style="display: flex;flex-direction: row; ">
  23. <view class="loud" @click="play(getContactName(log.number).name||getContactName(log.number).phoneNumbers[0].value)">
  24. <image style="width: 80%;height: 40%;margin-left: 35rpx" src="../../static/pics/laba.png"></image>
  25. </view>
  26. <view class="con-mes" @click="goToDetails(getContactName(log.number))">
  27. <text v-if="!getContactName(log.number).name" :style="{color:getLogDateColor(log.type)}">{{ formatPhoneNumber(getContactName(log.number).phoneNumbers[0].value) }}</text>
  28. <text v-else :style="{color:getLogDateColor(log.type)}">{{ truncate(getContactName(log.number).name) }}</text>
  29. </view>
  30. </view>
  31. <view v-else>
  32. <view class="con-mes-null" @click="goToDetails(getContactName(log.number))">
  33. <text v-if="!getContactName(log.number).name" :style="{color:getLogDateColor(log.type)}">{{ formatPhoneNumber(getContactName(log.number).phoneNumbers[0].value) }}</text>
  34. <text v-else :style="{color:getLogDateColor(log.type)}">{{ truncate(getContactName(log.number).name) }}</text>
  35. </view>
  36. </view>
  37. <view class="btn" @click="CallPhone(log.number)">
  38. <image class="Call" src="../../static/pics/call-out.png"></image>
  39. </view>
  40. </view>
  41. </view>
  42. <view v-else class="notFind">
  43. 最近无通话记录
  44. </view>
  45. </view>
  46. </view>
  47. </template>
  48. <script>
  49. const FvvUniTTS=uni.requireNativePlugin('Fvv-UniTTS');
  50. export default {
  51. data() {
  52. return {
  53. searchText: '', // 用户输入的搜索文本
  54. telephoneLog: [], // 存储通话记录
  55. contacts: [], // 存储联系人数据
  56. searchActive: true,
  57. hasPermission: false, // 用于存储权限状态
  58. switchStatus:false,
  59. isLoudVisible: true, // 初始状态为显示
  60. };
  61. },
  62. mounted() {
  63. this.requestPermissions(); // 请求权限
  64. },
  65. onShow() {
  66. this.init();
  67. this.getM();
  68. this.handleGetCallLogs();
  69. },
  70. computed: {
  71. limitedItems() {
  72. return this.telephoneLog.slice(0, 100); // 只获取前 200 个项目
  73. }//记得v-for的数组改了
  74. },
  75. methods: {
  76. goToDetails(item){
  77. console.error('item',item)
  78. const phoneNumbers=item.phoneNumbers
  79. const name=item.name;
  80. const phoneNumbersString=JSON.stringify(phoneNumbers)
  81. const phoenNumberCount=item.phoneNumbers.length;
  82. console.error("通话记录中的姓名",name)
  83. console.error("phonenumber",phoneNumbers)
  84. console.error("phoenNumberCount",phoenNumberCount)
  85. uni.navigateTo({
  86. url: `/pages/ContactDetails/ContactDetails?name=${encodeURIComponent(name)}&phoneNumbers=${encodeURIComponent(phoneNumbersString)}&count=${phoenNumberCount}`
  87. });
  88. },
  89. // 格式化电话号码
  90. formatPhoneNumber(value) {
  91. // 移除所有非数字字符
  92. value = value.replace(/\D/g, '');
  93. if (value.length === 11) {
  94. // 如果长度为11, 在第4和第9位插入空格
  95. return value.replace(/(\d{3})(\d{4})(\d{4})/, '$1 $2 $3');
  96. } else {
  97. // 如果长度不为11,每4个字符插入一个空格
  98. return value.replace(/(\d{4})(?=\d)/g, '$1 ');
  99. }
  100. },
  101. truncate(value) {
  102. if (value.length > 9) {
  103. return value.slice(0, 5) + '\n' + value.substring(5, 9) + '...';
  104. }
  105. else{
  106. return value.slice(0, 5) + '\n' + value.substring(5, 9)
  107. }
  108. return value;
  109. },
  110. getM(){//获取本地语音开关信息
  111. // 从本地存储中读取开关状态
  112. const storedSwitchStatus = uni.getStorageSync('voiceBroadcastSwitch');
  113. console.log("读取的本地存储状态1:", storedSwitchStatus);
  114. if (typeof storedSwitchStatus === 'boolean') {
  115. this.switchStatus = storedSwitchStatus;
  116. }
  117. },
  118. init(){
  119. FvvUniTTS.init((callback) => {
  120. console.log(callback);
  121. },"com.iflytek.speechcloud");
  122. FvvUniTTS.onStart((res) => {
  123. console.log("onStart:" + res)
  124. });
  125. FvvUniTTS.onDone((res) => {
  126. console.log("onDone:" + res)
  127. });
  128. FvvUniTTS.onError((res) => {
  129. console.log("onError:" + res)
  130. });
  131. FvvUniTTS.getInstallTTS(res => {
  132. console.log(res)
  133. })
  134. },
  135. play(e){
  136. console.log("播放",e)
  137. FvvUniTTS.speak({
  138. text:e,
  139. id:2,
  140. });
  141. FvvUniTTS.getInstallTTS(res => {
  142. console.log(res+"11")
  143. })
  144. },
  145. setLanguage(){
  146. console.log("set lang : " + FvvUniTTS.setLanguage("CHINESE"));
  147. },
  148. setEngines(){
  149. let setEngine = "com.iflytek.speechcloud"
  150. //获取已安装的引擎
  151. FvvUniTTS.getInstallTTS(res => {
  152. if(res == null || res.length <= 0){
  153. return
  154. }
  155. console.log(res)
  156. if(JSON.stringify(res).indexOf(setEngine) < 0){
  157. console.log("未安装该语音引擎")
  158. return
  159. }
  160. console.log("set engine : " + FvvUniTTS.setEngine(setEngine));
  161. FvvUniTTS.speak({
  162. text:"设置成功",
  163. id:2,
  164. });
  165. })
  166. },
  167. saveFile(){
  168. FvvUniTTS.saveAudioFile({
  169. text:"hello",
  170. id:3,
  171. path:"/sdcard/test/1.wav"
  172. })
  173. },
  174. setVoice(){
  175. FvvUniTTS.setPitch(100)
  176. FvvUniTTS.setSpeechRate(100)
  177. },
  178. showBar() {
  179. this.searchActive = !this.searchActive;
  180. },
  181. requestPermissions() {
  182. console.log("start")
  183. if (plus.android && plus.android.runtimeMainActivity()) {
  184. const activity = plus.android.runtimeMainActivity();
  185. const ContextCompat = plus.android.importClass("androidx.core.content.ContextCompat");
  186. const ActivityCompat = plus.android.importClass("androidx.core.app.ActivityCompat");
  187. const PERMISSIONS = [plus.android.importClass("android.Manifest$permission").READ_CALL_LOG];
  188. const PackageManager = plus.android.importClass("android.content.pm.PackageManager");
  189. console.log("end")
  190. const checkPermissions = () => {
  191. // 检查是否已经授予权限
  192. if (PERMISSIONS.every(permission => ContextCompat.checkSelfPermission(activity, permission) === PackageManager.PERMISSION_GRANTED)) {
  193. console.log("权限已授予");
  194. clearInterval(permissionCheckInterval); // 停止重复检查
  195. this.handleGetCallLogs(); // 获取通话记录
  196. this.getContacts(); // 获取通讯录
  197. } else {
  198. console.log("权限未授予,继续请求权限");
  199. ActivityCompat.requestPermissions(activity, PERMISSIONS, 1);
  200. }
  201. };
  202. // 设置一个重复定时器,每2秒检查一次权限
  203. const permissionCheckInterval = setInterval(checkPermissions, 1000);
  204. // 回调函数处理权限请求结果
  205. activity.onRequestPermissionsResult = (requestCode, permissions, grantResults) => {
  206. if (requestCode === 1) {
  207. if (grantResults.length > 0 && grantResults[0] === PackageManager.PERMISSION_GRANTED) {
  208. console.log("权限已成功授予");
  209. } else {
  210. console.error("权限被拒绝");
  211. }
  212. }
  213. };
  214. // 立即检查一次权限
  215. checkPermissions();
  216. }
  217. },
  218. handleGetCallLogs() {
  219. if (plus.android) {
  220. try {
  221. const MainActivity = plus.android.runtimeMainActivity();
  222. const CallLogHelper = plus.android.importClass("com.example.mylibrary.CallLogHelper");
  223. if (CallLogHelper) {
  224. const logsJson = CallLogHelper.getCallLogs(MainActivity);
  225. console.log("Raw logsJson:", logsJson); // 打印原始数据
  226. const logsArray = JSON.parse(logsJson);
  227. console.log("Parsed logsArray:", logsArray); // 打印解析后的数据
  228. if (Array.isArray(logsArray)) {
  229. this.telephoneLog = logsArray;
  230. } else {
  231. console.error("解析后的数据不是数组");
  232. }
  233. } else {
  234. console.error("CallLogHelper 类未加载");
  235. }
  236. } catch (e) {
  237. console.error("获取通话记录失败:", e);
  238. }
  239. } else {
  240. console.error("Android 环境未准备好");
  241. }
  242. },
  243. getContacts() {
  244. const that = this;
  245. plus.contacts.getAddressBook(plus.contacts.ADDRESSBOOK_PHONE, function(addressbook) {
  246. addressbook.find(
  247. ["displayName", "phoneNumbers"],
  248. function(contacts) {
  249. that.contacts = contacts.map(contact => ({
  250. id: contact.id,
  251. name: contact.displayName,
  252. phoneNumbers: contact.phoneNumbers.map(phone => phone.value)
  253. }));
  254. console.log("获取到的联系人数据:", that.contacts);
  255. },
  256. function(error) {
  257. console.error("获取通讯录失败: " + error.message);
  258. },
  259. { multiple: true }
  260. );
  261. });
  262. },
  263. getContactName(number) {
  264. // 清理电话号码,移除所有非数字字符
  265. const cleanedNumber = number.replace(/\D/g, '');
  266. // 查找匹配的联系人
  267. const contact = this.contacts.find(contact =>
  268. contact.phoneNumbers.some(phone => phone.replace(/\D/g, '') === cleanedNumber)
  269. );
  270. // 判断是否找到联系人并设置 phoneNumbersArray
  271. let phoneNumbersArray = contact === undefined ?[number]: contact.phoneNumbers ;
  272. phoneNumbersArray = phoneNumbersArray.map(number => ({
  273. "value": number
  274. }));
  275. // 返回包含姓名和电话号码的对象,其中电话号码是 JSON 格式的字符串
  276. return {
  277. name: contact && contact.name ? contact.name : null,
  278. phoneNumbers: phoneNumbersArray
  279. };
  280. },
  281. CallPhone(number) {
  282. plus.device.dial(number, false);
  283. // 延迟刷新通话记录
  284. setTimeout(() => {
  285. this.handleGetCallLogs();
  286. }, 2000); // 等待 2 秒,确保通话记录有时间更新
  287. },
  288. getLogDateColor(callType){
  289. return callType==3 ? '#ff0000' : '#000000'
  290. },
  291. filterContacts() {
  292. // 实现模糊查找逻辑
  293. const searchText = this.searchText.toLowerCase();
  294. this.filteredContacts = this.contacts.filter(contact => {
  295. return (contact.name || '').toLowerCase().includes(searchText) ||
  296. contact.phoneNumbers.some(phone => phone.toLowerCase().includes(searchText));
  297. });
  298. }
  299. },
  300. mounted() {
  301. this.getM();
  302. }
  303. }
  304. </script>
  305. <style>
  306. /* 样式定义 */
  307. .null {}
  308. /*顶部标题与屏幕顶部间隔8%*/
  309. .head{
  310. width: 100%;
  311. height: 100px;
  312. display: flex;
  313. justify-content: center;
  314. align-items: center;
  315. flex-direction: column;
  316. background-color: #f0efef;
  317. margin-top: 10%;
  318. margin-bottom: 10rpx;
  319. padding: 8rpx;
  320. font-size: 90rpx;
  321. box-shadow: 0 2rpx 3rpx rgba(143, 143, 143, 0.2);
  322. }
  323. hr{
  324. height: 2px;
  325. width: 90%;
  326. margin-top: 10px;
  327. }
  328. /* 顶部导航栏样式 */
  329. .navbar {
  330. display: flex;
  331. justify-content: space-around;
  332. align-items: center;
  333. width: 100%;
  334. background-color: #F8F8F8;
  335. height: 90px;
  336. position: fixed;
  337. top: 0;
  338. z-index: 999;
  339. }
  340. .icon {
  341. justify-content: space-around;
  342. width: 100rpx;
  343. height: 100rpx;
  344. }
  345. /* 搜索框样式 */
  346. .uni-column {
  347. width: 690rpx;
  348. height: 180rpx;
  349. border: 1px solid black;
  350. margin-top: 18rpx;
  351. border-radius: 20rpx;
  352. font-size: 50rpx;
  353. display: flex;
  354. justify-content: space-between;
  355. }
  356. .search-input {
  357. width: 75%;
  358. display: flex;
  359. align-items: center;
  360. }
  361. .search-img {
  362. width: 25%;
  363. display: flex;
  364. border-left: 1px solid black;
  365. justify-content: center;
  366. align-items: center;
  367. }
  368. .search-input input {
  369. width: 90%;
  370. height: 80%;
  371. font-size: 50rpx;
  372. padding: 10px;
  373. }
  374. .search-img image {
  375. width: 50%;
  376. height: 50%;
  377. font-size: 50rpx;
  378. }
  379. /* 通话记录区域样式 */
  380. .content {
  381. min-height: 100%;
  382. display: flex;
  383. flex-direction: column;
  384. align-items: center;
  385. justify-content: center;
  386. background-color: #f0efef;
  387. }
  388. .con-mes,.con-mes-null {
  389. line-height: 1.1;
  390. font-size: 35px;
  391. font-weight: 700;
  392. display: flex;
  393. align-items: center;
  394. justify-content: center;
  395. text-align: center;
  396. white-space: pre-line; /* 保证换行符有效 */
  397. word-wrap: break-word; /* 确保长单词换行 */
  398. width: 410rpx;
  399. height: 200rpx;
  400. border: none;
  401. background-color: white;
  402. }
  403. .con-mes-null{
  404. border-radius: 20rpx 0rpx 0rpx 20rpx;
  405. width: 510rpx;
  406. height: 200rpx;
  407. }
  408. .name-btn {
  409. display: flex;
  410. align-items: center;
  411. justify-content: space-between;
  412. width: 690rpx;
  413. height: 200rpx;
  414. border: 1px solid black;
  415. margin-top: 18rpx;
  416. border-radius: 20rpx;
  417. font-size: 50rpx;
  418. }
  419. .text-area {
  420. height: 70%;
  421. display: flex;
  422. flex-direction: column;
  423. align-items: center;
  424. }
  425. .loud {
  426. display: flex;
  427. justify-content: center;
  428. align-items: center;
  429. width: 100rpx;
  430. height: 200rpx;
  431. border-radius: 20rpx 0rpx 0rpx 20rpx;
  432. background-color: white;
  433. }
  434. .btn {
  435. display: flex;
  436. justify-content: center;
  437. align-items: center;
  438. width: 180rpx;
  439. height: 200rpx;
  440. background-color: #ecfff3;
  441. border-radius: 0rpx 20rpx 20rpx 0rpx;
  442. }
  443. .Call {
  444. width: 100rpx;
  445. height: 100rpx;
  446. object-fit: contain;
  447. }
  448. .notFind {
  449. height: 100%;
  450. font-size: 50rpx;
  451. display: flex;
  452. justify-content: center;
  453. align-items: center;
  454. color: black;
  455. margin-top: 50rpx;
  456. }
  457. </style>