live.vue 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408
  1. <template>
  2. <view class="live">
  3. <view class="top">
  4. </view>
  5. <view class="head">
  6. <span class="cuIcon-back" @click="back"></span>
  7. <view class="case">
  8. 实况记录
  9. </view>
  10. </view>
  11. <view class="list">
  12. <view class="car_data">
  13. <view class="fat" v-for="item in form" :key="item.id">
  14. <view class="fat_title">{{item.name}}</view>
  15. <view class="sun" v-for="row in item.categoryList" :key="row.id">
  16. {{row.name}}
  17. <span>{{row.names}}</span>
  18. </view>
  19. </view>
  20. </view>
  21. <!-- 备注信息 -->
  22. <view class="car_data">
  23. <view class="fat">
  24. <view class="fat_title">备注信息</view>
  25. <view class="message" v-for="(item,index) in historyList" :key="item.id">
  26. <view class="top">
  27. <view class="left">
  28. <view class="img">
  29. <image :src="item.logo" mode="" v-if="item.logo"></image>
  30. <image :src="item.photo" mode="" v-if="item.photo"></image>
  31. </view>
  32. <view class="name">
  33. {{item.merchantName}}<br />
  34. <span>{{item.createTime}}</span>
  35. </view>
  36. </view>
  37. <view class="play" v-if="item.recordUrl">
  38. <!-- <image src="/static/pause.png" mode="" @click="exchangeStatus(item),play(item)" v-if="item.isPlay">
  39. </image>
  40. <image src="/static/play.png" mode="" @click="exchangeStatus(item),play(item)" v-else></image> -->
  41. <image :src="item.isPlay ? '/static/pause.png' : '/static/play.png'" mode="aspectFit"
  42. @click="handleAudioPlay(item)" class="play-icon" />
  43. </view>
  44. </view>
  45. <view class="content">
  46. {{item.msgContent}}
  47. </view>
  48. </view>
  49. </view>
  50. </view>
  51. </view>
  52. </view>
  53. </template>
  54. <script>
  55. import configService from '../../../common/service/config.service'
  56. export default {
  57. data() {
  58. return {
  59. taskId: null,
  60. form: [],
  61. historyList: [],
  62. audioContext: null,
  63. currentAudioId: null, // 当前播放的音频ID
  64. isAudioPlaying: false, // 音频是否正在播放
  65. checkType:''
  66. }
  67. },
  68. methods: {
  69. //返回
  70. back() {
  71. uni.navigateBack({
  72. delta: 1
  73. });
  74. },
  75. getLive() {
  76. this.$http.get("/merchant/localTsCustomerServiceDict/list?taskId=" + this.taskId + '&source=' + this.checkType).then(
  77. res => {
  78. if (res.data.code == 200) {
  79. this.form = res.data.result
  80. let list = []
  81. this.form.forEach(item => {
  82. let temp = []
  83. item.categoryList.forEach(row => {
  84. if (row.idList.length > 0) {
  85. const idSet = new Set(row.idList);
  86. // Find the names that match the ids in idList
  87. const matchedNames = row.itemList
  88. .filter(item => idSet.has(item.id)) // Filter items where the id matches
  89. .map(item => item.name) // Extract the name from the matching items
  90. .join(','); // Join names with commas
  91. row.names = matchedNames;
  92. temp.push(row)
  93. }
  94. })
  95. item.categoryList = temp
  96. if (item.categoryList.length > 0) {
  97. list.push(item)
  98. }
  99. })
  100. this.form = list
  101. }
  102. })
  103. },
  104. // 处理音频播放/暂停
  105. handleAudioPlay(item) {
  106. // 如果是当前正在播放的音频
  107. if (this.currentAudioId === item.id) {
  108. if (this.isAudioPlaying) {
  109. this.pauseAudio();
  110. } else {
  111. this.resumeAudio();
  112. }
  113. }
  114. // 如果是新的音频
  115. else {
  116. this.playNewAudio(item);
  117. }
  118. },
  119. // 播放新音频
  120. playNewAudio(item) {
  121. // 先停止当前播放的音频
  122. if (this.audioContext) {
  123. this.stopAudio();
  124. }
  125. // 创建新的音频上下文
  126. this.audioContext = uni.createInnerAudioContext();
  127. this.audioContext.src = `${configService.apiUrl}/${item.recordUrl}`;
  128. // 设置事件监听
  129. this.setupAudioListeners();
  130. // 更新状态
  131. this.currentAudioId = item.id;
  132. this.isAudioPlaying = true;
  133. // 开始播放
  134. this.audioContext.play();
  135. // 更新UI状态
  136. this.updateAudioStatus(item.id, true);
  137. },
  138. // 暂停音频
  139. pauseAudio() {
  140. if (this.audioContext) {
  141. this.audioContext.pause();
  142. this.isAudioPlaying = false;
  143. this.updateAudioStatus(this.currentAudioId, false);
  144. }
  145. },
  146. // 继续播放
  147. resumeAudio() {
  148. if (this.audioContext) {
  149. this.audioContext.play();
  150. this.isAudioPlaying = true;
  151. this.updateAudioStatus(this.currentAudioId, true);
  152. }
  153. },
  154. // 停止音频
  155. stopAudio() {
  156. if (this.audioContext) {
  157. this.audioContext.stop();
  158. this.updateAudioStatus(this.currentAudioId, false);
  159. this.cleanupAudio();
  160. }
  161. },
  162. // 设置音频事件监听
  163. setupAudioListeners() {
  164. if (!this.audioContext) return;
  165. this.audioContext.onPlay(() => {
  166. this.isAudioPlaying = true;
  167. this.updateAudioStatus(this.currentAudioId, true);
  168. });
  169. this.audioContext.onPause(() => {
  170. this.isAudioPlaying = false;
  171. this.updateAudioStatus(this.currentAudioId, false);
  172. });
  173. this.audioContext.onStop(() => {
  174. this.isAudioPlaying = false;
  175. this.updateAudioStatus(this.currentAudioId, false);
  176. });
  177. this.audioContext.onEnded(() => {
  178. this.isAudioPlaying = false;
  179. this.updateAudioStatus(this.currentAudioId, false);
  180. this.cleanupAudio();
  181. });
  182. this.audioContext.onError((err) => {
  183. console.error('播放错误:', err);
  184. this.isAudioPlaying = false;
  185. this.updateAudioStatus(this.currentAudioId, false);
  186. this.cleanupAudio();
  187. uni.showToast({
  188. title: '音频播放失败',
  189. icon: 'none'
  190. });
  191. });
  192. },
  193. // 更新音频播放状态
  194. updateAudioStatus(audioId, isPlaying) {
  195. this.historyList = this.historyList.map(item => {
  196. if (item.id === audioId) {
  197. item.isPlay = isPlaying;
  198. } else if (item.isPlay) {
  199. item.isPlay = false; // 确保其他音频状态被重置
  200. }
  201. return item;
  202. });
  203. },
  204. // 清理音频资源
  205. cleanupAudio() {
  206. if (this.audioContext) {
  207. this.audioContext.destroy();
  208. this.audioContext = null;
  209. }
  210. this.currentAudioId = null;
  211. this.isAudioPlaying = false;
  212. },
  213. getHistroyList() {
  214. let data = {
  215. taskId: this.taskId
  216. }
  217. this.$http.post('/merchant/localTsCustomerServiceTaskMsg/queryList', data).then((res) => {
  218. this.historyList = res.data.result
  219. this.historyList.forEach(item => {
  220. if (item.recordUrl) {
  221. item.isPlay = false
  222. } else {
  223. item.isPlay = null
  224. }
  225. })
  226. })
  227. },
  228. },
  229. beforeDestroy() {
  230. if (this.audioContext) {
  231. this.audioContext.stop();
  232. this.audioContext.destroy();
  233. this.audioContext = null;
  234. }
  235. },
  236. mounted() {
  237. },
  238. onLoad(options) {
  239. this.taskId = options.taskId
  240. this.checkType = options.checkType
  241. this.getLive()
  242. this.getHistroyList()
  243. }
  244. }
  245. </script>
  246. <style lang="scss" scoped>
  247. .live {
  248. width: 100vw;
  249. height: 100vh;
  250. background: #F8F8FA;
  251. display: flex;
  252. flex-direction: column;
  253. // overflow-y: auto;
  254. /*#ifdef APP-PLUS*/
  255. .top {
  256. width: 100%;
  257. height: var(--status-bar-height);
  258. background: #FFFFFF;
  259. }
  260. /*#endif*/
  261. .head {
  262. width: 100%;
  263. height: 112rpx;
  264. padding: 20rpx;
  265. display: flex;
  266. align-items: center;
  267. background: #FFFFFF;
  268. position: relative;
  269. .cuIcon-back {
  270. font-size: 40rpx;
  271. margin-right: 40rpx;
  272. }
  273. .case {
  274. position: absolute;
  275. left: 50%;
  276. transform: translateX(-50%);
  277. font-size: 36rpx;
  278. font-weight: 600;
  279. }
  280. }
  281. .list {
  282. flex: 1;
  283. overflow-y: auto;
  284. background: #F7F7F7;
  285. padding: 24rpx 30rpx;
  286. .car_data {
  287. width: 100%;
  288. .fat {
  289. background: #FFFFFF;
  290. padding: 20rpx;
  291. margin-bottom: 20rpx;
  292. border-radius: 10rpx;
  293. .fat_title {
  294. font-weight: 600;
  295. font-size: 30rpx;
  296. color: #333333;
  297. margin-bottom: 20rpx;
  298. }
  299. .sun {
  300. width: 100%;
  301. height: 73rpx;
  302. border-bottom: 1px solid #eee;
  303. display: flex;
  304. align-items: center;
  305. justify-content: space-between;
  306. font-size: 28rpx;
  307. color: #666666;
  308. span {
  309. color: #181818;
  310. }
  311. }
  312. .message {
  313. border-bottom: 1px solid #eee;
  314. padding-bottom: 16rpx;
  315. margin-bottom: 16rpx;
  316. .top {
  317. width: 100%;
  318. display: flex;
  319. align-items: center;
  320. justify-content: space-between;
  321. margin-bottom: 12rpx;
  322. .left {
  323. display: flex;
  324. align-items: center;
  325. .img {
  326. width: 68rpx;
  327. height: 68rpx;
  328. border-radius: 8rpx;
  329. overflow: hidden;
  330. margin-right: 12rpx;
  331. image {
  332. width: 100%;
  333. height: 100%;
  334. }
  335. }
  336. .name {
  337. font-size: 28rpx;
  338. color: #000000;
  339. span {
  340. font-size: 24rpx;
  341. color: #999999
  342. }
  343. }
  344. }
  345. .play {
  346. font-size: 24rpx;
  347. color: #000000;
  348. text-align: center;
  349. image {
  350. width: 40rpx;
  351. height: 40rpx;
  352. }
  353. }
  354. }
  355. .content {
  356. font-size: 26rpx;
  357. }
  358. }
  359. }
  360. }
  361. }
  362. }
  363. </style>