voice.vue 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560
  1. <template>
  2. <view class="voice-play-page" v-if="isAuth">
  3. <view class="top-filter-box">
  4. <image src="@/static/question/select.png" mode="aspectFill" @click="showFilter = true" />
  5. <text @click="showFilter = true">筛选</text>
  6. </view>
  7. <view class="empty-box" v-if="list.length==0&&finished">
  8. <image
  9. :src="globalImgUrls.activityNoAuth"
  10. mode="widthFix"
  11. />
  12. <view>暂无数据</view>
  13. </view>
  14. <view class="list-wrap" :class="showAudioPop?showAudioBigPop?'list-bot3':'list-bot2':'list-bot1'" v-else>
  15. <view class="item" v-for="item in list" :key="item.BroadcastId" @click="handleGoDetail(item)">
  16. <view class="title">{{item.BroadcastName}}</view>
  17. <view class="time">发布时间:{{item.CreateTime|formatTime}}</view>
  18. <view class="flex audio-box" @click.stop="handlePlay(item)">
  19. <image
  20. :src="item.BroadcastId==curVoiceId&&!curAudioPaused?require('@/static/voice/playing.png'):require('@/static/voice/pause.png')"
  21. mode="widthFix"
  22. />
  23. <text>{{item.VoicePlaySeconds|formatVoiceTime}}</text>
  24. </view>
  25. <button class="publish-btn" @click.stop="handleSendMsgItem(item)" v-if="item.CouldSendMsg">
  26. <image class="publish-img" src="@/static/voice/publish.png" mode="widthFix" />
  27. </button>
  28. <button class="del-btn" @click.stop="handleDelItem(item)">
  29. <image class="del-img" src="@/static/voice/del.png" mode="widthFix" v-if="item.IsAuthor" />
  30. </button>
  31. <button
  32. class="share-btn"
  33. open-type="share"
  34. :data-item="item"
  35. @click.stop=""
  36. >
  37. <image class="share-img" src="@/static/share-icon.png" mode="aspectFill"/>
  38. </button>
  39. </view>
  40. </view>
  41. <navigator url="/pages-voice/addVoice">
  42. <view :class="['add-btn',showAudioPop?showAudioBigPop?'add-btn-bot3':'add-btn-bot2':'add-btn-bot1']" v-if="IsVoiceAdmin">新建语音</view>
  43. </navigator>
  44. <!-- 筛选弹窗 -->
  45. <van-popup
  46. :show="showFilter"
  47. position="bottom"
  48. :close-on-click-overlay="true"
  49. @close="showFilter = false"
  50. round
  51. >
  52. <view class="fliter-wrap-list">
  53. <view class="flex top">
  54. <text style="color:#000">全部筛选</text>
  55. <text style="color:#E3B377" @click="showFilter=false">取消</text>
  56. </view>
  57. <van-tree-select
  58. :items="options"
  59. :main-active-index="mainActiveIndex"
  60. :active-id="activeId"
  61. @click-nav="onClickNav"
  62. @click-item="onClickItem"
  63. main-active-class="main-active-class"
  64. content-active-class="content-active-class"
  65. />
  66. </view>
  67. </van-popup>
  68. <!-- 音频悬浮 -->
  69. <view v-if="showPage">
  70. <audioBox v-if="showAudioPop"/>
  71. </view>
  72. <van-dialog id="van-dialog" />
  73. </view>
  74. <noAuth :info="noAuthData" v-else/>
  75. </template>
  76. <script>
  77. import {apiVoiceList,apiVoiceSectionList,apiVoicePlayRecord,apiVoiceDel,apiVoiceSendMsg} from '@/api/voice'
  78. import {apiGetSceneToParams} from '@/api/common'
  79. import noAuth from './components/noAuth.vue'
  80. import audioBox from '@/components/audioBox/audioBox.vue'
  81. const moment=require('@/utils/moment-with-locales.min')
  82. export default {
  83. components:{
  84. noAuth,
  85. audioBox
  86. },
  87. filters:{
  88. formatTime(e){
  89. return moment(e).format('YYYY-MM-DD HH:mm:ss')
  90. },
  91. formatVoiceTime(e){
  92. let m=parseInt(e/60)
  93. let s=parseInt(e%60)
  94. return `${m>9?m:'0'+m}:${s>9?s:'0'+s}`
  95. }
  96. },
  97. computed:{
  98. showAudioPop(){//是否显示音频弹窗
  99. return this.$store.state.audio.show
  100. },
  101. showAudioBigPop(){
  102. return this.$store.state.audio.showBig
  103. },
  104. curVoiceId(){//当前正在播放的音频id
  105. return this.$store.state.audio.voiceId
  106. },
  107. curAudioPaused(){//当前音频是否暂停状态
  108. return this.$store.state.audio.paused
  109. },
  110. },
  111. data() {
  112. return {
  113. list:[],
  114. page:1,
  115. pageSize:20,
  116. finished:false,
  117. voiceId:0,//分享时进入的音频id
  118. IsVoiceAdmin:false,//是否是语音管理员
  119. isAuth:true,
  120. noAuthData:null,
  121. showFilter:false,
  122. options:[],
  123. mainActiveIndex:0,
  124. activeId:0,//选择的板块id
  125. showPage:false,
  126. }
  127. },
  128. onLoad(options){
  129. this.init(options)
  130. this.getOptionsList()
  131. this.addListenVoiceSuccess()
  132. },
  133. onShow(){
  134. //无权限时刷新列表
  135. if(!this.isAuth){
  136. this.getVoiceList()
  137. }
  138. this.showPage=true
  139. },
  140. onHide(){
  141. this.showPage=false
  142. },
  143. onUnload(){
  144. uni.$off('addVoiceSuccess')
  145. },
  146. onShareAppMessage({from,target}) {
  147. console.log(from,target);
  148. let path='/pages/voice/voice'
  149. let title='语音播报'
  150. let imageUrl=''
  151. if(from=='button'){
  152. title=`${target.dataset.item.SectionName}:${target.dataset.item.BroadcastName}`
  153. path=`/pages-voice/voiceDetail?voiceId=${target.dataset.item.BroadcastId}`
  154. imageUrl=target.dataset.item.ImgUrl
  155. }
  156. return {
  157. title:title,
  158. path:path,
  159. imageUrl:imageUrl
  160. }
  161. },
  162. onPullDownRefresh(){
  163. this.voiceId=0
  164. this.page=1
  165. this.list=[]
  166. this.finished=false
  167. this.getVoiceList()
  168. this.getOptionsList()
  169. setTimeout(() => {
  170. uni.stopPullDownRefresh()
  171. }, 1500)
  172. },
  173. onReachBottom() {
  174. if(this.finished) return
  175. this.page++
  176. this.getVoiceList()
  177. },
  178. methods: {
  179. handleGoDetail(item){
  180. uni.navigateTo({
  181. url: '/pages-voice/voiceDetail?voiceId='+item.BroadcastId,
  182. });
  183. },
  184. // 监听添加音频成功刷新列表
  185. addListenVoiceSuccess(){
  186. uni.$on('addVoiceSuccess',()=>{
  187. this.voiceId=0
  188. this.page=1
  189. this.list=[]
  190. this.finished=false
  191. this.getVoiceList()
  192. this.getOptionsList()
  193. })
  194. },
  195. async init(options){
  196. if(options.scene){
  197. const res=await apiGetSceneToParams({scene_key:options.scene})
  198. if(res.code===200){
  199. const obj=JSON.parse(res.data)
  200. this.voiceId=obj.voiceId
  201. }
  202. }else{
  203. this.voiceId=options.voiceId||0
  204. }
  205. this.getVoiceList()
  206. },
  207. // 获取音频列表
  208. async getVoiceList(){
  209. const res=await apiVoiceList({
  210. page_index:this.page,
  211. page_size:this.pageSize,
  212. broadcast_id:Number(this.voiceId),
  213. section_id:Number(this.activeId)
  214. })
  215. if(res.code===200){
  216. this.IsVoiceAdmin=res.data.IsVoiceAdmin
  217. let arr=res.data.List||[]
  218. this.list=[...this.list,...arr]
  219. if(arr.length===0){
  220. this.finished=true
  221. }
  222. // 如果有voiceId 则说明是分享进入的 如果没有数据则提示
  223. if(this.voiceId!=0&&arr.length===0){
  224. uni.showToast({
  225. title:'该语音播报不存在',
  226. icon:'none'
  227. })
  228. setTimeout(() => {
  229. this.voiceId=0
  230. this.page=1
  231. this.list=[]
  232. this.finished=false
  233. this.getVoiceList()
  234. this.getOptionsList()
  235. }, 1500);
  236. }
  237. }else if(res.code===403){
  238. //无权限用户
  239. this.isAuth=false
  240. this.noAuthData=res.data
  241. }
  242. },
  243. //获取筛选数据
  244. async getOptionsList(){
  245. const res=await apiVoiceSectionList()
  246. if(res.code!==200) return
  247. const arr=res.data||[]
  248. this.options=arr.map(item=>{
  249. let obj={
  250. text:'',
  251. children:[]
  252. }
  253. obj.text=item.VarietyName
  254. obj.children=item.Children.map(_item=>{
  255. return {
  256. text:_item.SectionName,
  257. id:_item.SectionId
  258. }
  259. })
  260. return obj
  261. })
  262. },
  263. onClickNav({detail}){
  264. console.log(detail);
  265. this.mainActiveIndex=detail.index
  266. },
  267. onClickItem({detail}){
  268. console.log(detail);
  269. if(this.activeId==detail.id){
  270. this.activeId=0
  271. }else{
  272. this.activeId=detail.id
  273. }
  274. this.voiceId=0
  275. this.page=1
  276. this.list=[]
  277. this.finished=false
  278. this.getVoiceList()
  279. this.showFilter=false
  280. },
  281. //推送消息
  282. handleSendMsgItem(item){
  283. this.$dialog.confirm({
  284. title:'',
  285. message: '该操作将推送模板消息和客群,确认推送吗?',
  286. confirmButtonText:'确认'
  287. }).then(()=>{
  288. apiVoiceSendMsg({broadcast_id:item.BroadcastId}).then(res=>{
  289. if(res.code===200){
  290. uni.showToast({
  291. title:"推送成功",
  292. icon:'success'
  293. })
  294. item.CouldSendMsg=false
  295. }
  296. })
  297. }).catch(()=>{})
  298. },
  299. //删除音频
  300. handleDelItem(item){
  301. this.$dialog.confirm({
  302. title:'',
  303. message: '确定要删除该语音播报吗?',
  304. confirmButtonText:'确定'
  305. }).then(()=>{
  306. if(this.temAudio.item&&this.temAudio.item.id==item.BroadcastId){
  307. //删除的音频正好在播放则暂停
  308. this.globalBgMusic.stop()
  309. }
  310. apiVoiceDel({broadcast_id:Number(item.BroadcastId)}).then(res=>{
  311. if(res.code===200){
  312. uni.showToast({
  313. title:'操作成功',
  314. icon:'none'
  315. })
  316. this.page=1
  317. this.list=[]
  318. this.finished=false
  319. this.getVoiceList()
  320. }
  321. })
  322. }).catch(()=>{})
  323. },
  324. //点击音频 播放或者暂停
  325. handlePlay(item){
  326. if(this.$store.state.audio.voiceId==item.BroadcastId){
  327. if(this.globalBgMusic.paused){
  328. this.globalBgMusic.play()
  329. }else{
  330. this.globalBgMusic.pause()
  331. }
  332. }else{
  333. const list=[{url:item.VoiceUrl,time:item.VoicePlaySeconds,title:item.BroadcastName,}]
  334. this.$store.commit('audio/addAudio',{
  335. list:list,
  336. voiceId:item.BroadcastId
  337. })
  338. this.handleVoicePlayRecord(item)
  339. }
  340. },
  341. //上报音频播放记录
  342. async handleVoicePlayRecord(item){
  343. const res=await apiVoicePlayRecord({
  344. broadcast_id:item.BroadcastId
  345. })
  346. if(res.code===200){
  347. console.log('上报音频播放记录');
  348. }
  349. }
  350. },
  351. }
  352. </script>
  353. <style lang="scss">
  354. .voice-play-page{
  355. .fliter-wrap-list{
  356. background-color: #fff;
  357. padding-top: 53rpx;
  358. padding-bottom: 100rpx;
  359. .top{
  360. font-size: 32rpx;
  361. justify-content: space-between;
  362. margin-bottom: 40rpx;
  363. padding: 0 34rpx;
  364. }
  365. .van-sidebar{
  366. flex-shrink: 0;
  367. }
  368. .van-tree-select__content{
  369. overflow-x: hidden;
  370. }
  371. .main-active-class{
  372. border-color: #E3B377;
  373. }
  374. .content-active-class{
  375. color: #E3B377;
  376. }
  377. }
  378. }
  379. </style>
  380. <style lang="scss" scoped>
  381. .voice-play-page{
  382. .top-filter-box{
  383. display: flex;
  384. flex: auto;
  385. align-items: center;
  386. height: 100rpx;
  387. background-color: white;
  388. position: sticky;
  389. top: 0;
  390. left: 0;
  391. padding-left: 34rpx;
  392. z-index: 99;
  393. image{
  394. width: 34rpx;
  395. height: 34rpx;
  396. }
  397. color: #E3B377;
  398. font-size: 28rpx;
  399. }
  400. }
  401. .empty-box{
  402. text-align: center;
  403. font-size: 32rpx;
  404. color: #999;
  405. padding-top: 150rpx;
  406. image{
  407. width: 80vw;
  408. margin-bottom: 57rpx;
  409. }
  410. }
  411. .list-wrap{
  412. padding: 0 34rpx 34rpx 34rpx;
  413. .item{
  414. border-bottom: 1px solid #CDCDCD;
  415. padding: 30rpx 0;
  416. position: relative;
  417. .publish-btn{
  418. position: absolute;
  419. right: 192rpx;
  420. bottom: 30rpx;
  421. width: 36rpx;
  422. height: 36rpx;
  423. background-color: transparent;
  424. line-height: 1;
  425. padding: 0;
  426. &::after{
  427. border: none;
  428. }
  429. }
  430. .publish-img{
  431. width: 34rpx;
  432. height: 34rpx;
  433. }
  434. .del-btn{
  435. position: absolute;
  436. right: 96rpx;
  437. bottom: 30rpx;
  438. width: 36rpx;
  439. height: 36rpx;
  440. background-color: transparent;
  441. line-height: 1;
  442. padding: 0;
  443. &::after{
  444. border: none;
  445. }
  446. }
  447. .del-img{
  448. width: 34rpx;
  449. height: 34rpx;
  450. }
  451. .share-btn{
  452. position: absolute;
  453. bottom: 30rpx;
  454. right: 0rpx;
  455. background-color: transparent;
  456. width: 36rpx;
  457. height: 36rpx;
  458. line-height: 1;
  459. padding: 0;
  460. &::after{
  461. border: none;
  462. }
  463. }
  464. .share-img{
  465. width: 32.5rpx;
  466. height: 32rpx;
  467. }
  468. .title{
  469. font-size: 32rpx;
  470. }
  471. .time{
  472. font-size: 28rpx;
  473. color: #666;
  474. margin-top: 20rpx;
  475. margin-bottom: 30rpx;
  476. }
  477. .audio-box{
  478. width: 185rpx;
  479. height: 56rpx;
  480. align-items: center;
  481. justify-content: center;
  482. border-radius: 28rpx;
  483. background-color: #F4E1C9;
  484. color: #E3B377;
  485. image{
  486. width: 23rpx;
  487. height: 28rpx;
  488. margin-right: 20rpx;
  489. }
  490. }
  491. }
  492. }
  493. .list-bot1{
  494. padding-bottom: 200rpx;
  495. }
  496. .list-bot2{
  497. padding-bottom: 340rpx;
  498. }
  499. .list-bot3{
  500. padding-bottom: 440rpx;
  501. }
  502. .add-btn{
  503. position: fixed;
  504. width: 514rpx;
  505. height: 80rpx;
  506. text-align: center;
  507. line-height: 80rpx;
  508. color: #E3B377;
  509. font-size: 32rpx;
  510. left: 50%;
  511. bottom: calc(150rpx + constant(safe-area-inset-bottom));
  512. bottom: calc(150rpx + env(safe-area-inset-bottom));
  513. transform: translateX(-50%);
  514. background: #333333;
  515. box-shadow: 0px 4rpx 20rpx rgba(160, 126, 84, 0.25);
  516. border-radius: 40rpx;
  517. }
  518. .add-btn-bot1{
  519. bottom: calc(120rpx + constant(safe-area-inset-bottom));
  520. bottom: calc(120rpx + env(safe-area-inset-bottom));
  521. }
  522. .add-btn-bot2{
  523. bottom: calc(260rpx + constant(safe-area-inset-bottom));
  524. bottom: calc(260rpx + env(safe-area-inset-bottom));
  525. }
  526. .add-btn-bot3{
  527. bottom: calc(355rpx + constant(safe-area-inset-bottom));
  528. bottom: calc(355rpx + env(safe-area-inset-bottom));
  529. }
  530. </style>