Explorar el Código

图片和来电音频

seimin hace 11 meses
padre
commit
546536baf9

+ 185 - 0
components/IncidentAttachment.vue

@@ -0,0 +1,185 @@
1
+<template>
2
+  <view class="container" @touchstart.stop>
3
+    <view class="container_head">
4
+      {{incidentData.reqAttachment ? '报修图片' : ''}}{{incidentData.reqAttachment && incidentData.callID ? '及' : ''}}{{incidentData.callID ? '录音' : ''}}
5
+    </view>
6
+    <view class="container_form">
7
+      <view class="repairImgList" v-if="incidentData.reqAttachment">
8
+        <image class="repairImg" :src="img.previewUrl" mode="aspectFill" v-for="(img, i) in pageData.repairImgList" :key="i" @click="previewImg(i)"></image>
9
+      </view>
10
+      <view class="audio" v-if="incidentData.callID">
11
+        <!-- <sy-audio isCountDown src='https://m801.music.126.net/20240418145806/654da83af722660e0fd769b4751127f0/jdyyaac/obj/w5rDlsOJwrLDjj7CmsOj/22271469539/935c/9115/d211/9dba43d41e350fbea95efe39cf5d7e07.m4a' audioTitle="" subheading=""></sy-audio> -->
12
+        <sy-audio ref="audio" isCountDown :src='pageData.audioSrc' audioTitle="" subheading=""></sy-audio>
13
+      </view>
14
+    </view>
15
+    <view class="container_foot">
16
+      <view class="foot_btns">
17
+        <view class="know" @click="know">知道了</view>
18
+      </view>
19
+    </view>
20
+  </view>
21
+  <view class="mask" @touchstart.stop></view>
22
+</template>
23
+
24
+<script setup>
25
+  import { defineEmits, ref, reactive, defineProps } from 'vue'
26
+  import { onLoad } from '@dcloudio/uni-app'
27
+  import { useLoginUserStore } from '@/stores/loginUser'
28
+  import { api_wechatRequesterIncident, api_callrecord } from "@/http/api.js"
29
+  
30
+  const emit = defineEmits(['cancelEmit', 'knowEmit']);
31
+  const { incidentData } = defineProps({
32
+    incidentData: Object,
33
+  });
34
+  const loginUserStore = useLoginUserStore();
35
+  
36
+  // 页面数据
37
+  const pageData = reactive({
38
+    repairImgList: [],//报修图片
39
+    audioSrc: '',//音频
40
+  });
41
+  
42
+  const audio = ref(null);
43
+  
44
+  // 取消
45
+  function cancel(){
46
+    emit('cancelEmit')
47
+  }
48
+  
49
+  // 确认
50
+  function know(){
51
+    audio.value.audioDestroy()
52
+    emit('knowEmit');
53
+  }
54
+  
55
+  // 预览图片
56
+  function previewImg(index){
57
+    uni.previewImage({
58
+      current: index,
59
+      urls: pageData.repairImgList.map(v => v.previewUrl),
60
+      longPressActions: {
61
+        itemList: ['发送给朋友', '保存图片', '收藏'],
62
+        success: function(data) {
63
+          console.log('选中了第' + (data.tapIndex + 1) + '个按钮,第' + (data.index + 1) + '张图片');
64
+        },
65
+        fail: function(err) {
66
+          console.log(err.errMsg);
67
+        }
68
+      }
69
+    });
70
+  }
71
+  
72
+  // 获取报修图片
73
+  function getRepairImgs() {
74
+    uni.showLoading({
75
+      title: "加载中",
76
+      mask: true,
77
+    });
78
+    api_wechatRequesterIncident(incidentData.id).then((res) => {
79
+        uni.hideLoading();
80
+        res.data = res.data || [];
81
+        res.data.forEach(v => {
82
+          v.previewUrl = location.origin + "/file" + v.relativeFilePath;
83
+        })
84
+        pageData.repairImgList = res.data;
85
+      });
86
+  }
87
+  
88
+  // 获取通话音频
89
+  function getCallrecord() {
90
+    uni.showLoading({
91
+      title: "加载中",
92
+      mask: true,
93
+    });
94
+    let postData = {
95
+      idx: 0,
96
+      sum: 1,
97
+      callrecord: {callAccept: incidentData.callID},
98
+    };
99
+    api_callrecord(postData).then((res) => {
100
+        uni.hideLoading();
101
+        if(res.status == 200){
102
+          res.list = res.list || [];
103
+          if(res.list.length){
104
+            pageData.audioSrc = location.protocol + "//" + location.hostname + res.list[0].recordingFileName;
105
+          }else{
106
+            pageData.audioSrc = '';
107
+          }
108
+          pageData.audioSrc = 'https://webfs.tx.kugou.com/202404181537/6389dd96e215021020c845b8cbe26e24/v2/eb95a90ba7c513810d694d548b924a29/G347/M01/D0/C3/O5UEAGUDY0aAC4eQADXnW-VR_t4017.mp3'
109
+        }else{
110
+          uni.showToast({
111
+            icon: 'none',
112
+            title: res.msg || '请求数据失败!'
113
+          });
114
+        }
115
+      });
116
+  }
117
+  
118
+  onLoad((option) => {
119
+    incidentData.reqAttachment && getRepairImgs();
120
+    incidentData.callID && getCallrecord();
121
+  })
122
+</script>
123
+
124
+<style lang="scss" scoped>
125
+.mask{
126
+  position: fixed;
127
+  left: 0;
128
+  top: 0;
129
+  right: 0;
130
+  bottom: 0;
131
+  background-color: rgba(0, 0, 0, 0.4);
132
+  z-index: 999;
133
+}
134
+.container{
135
+  position: fixed;
136
+  left: 50%;
137
+  top: 50%;
138
+  transform: translate(-50%, -50%);
139
+  z-index: 9999;
140
+  width: 690rpx;
141
+  background-color: #fff;
142
+  border-radius: 10rpx;
143
+  
144
+  .container_head{
145
+    padding: 55rpx;
146
+    font-size: 32rpx;
147
+    color: #333;
148
+    text-align: center;
149
+  }
150
+  
151
+  .container_form{
152
+    padding: 0 55rpx;
153
+    .repairImgList{
154
+      margin-bottom: 55rpx;
155
+      display: flex;
156
+      justify-content: space-between;
157
+      align-items: center;
158
+      gap: 15rpx;
159
+      .repairImg{
160
+        width: 184rpx;
161
+        height: 186rpx;
162
+      }
163
+    }
164
+    
165
+    .audio{
166
+      margin-bottom: 45rpx;
167
+    }
168
+  }
169
+  
170
+  .container_foot{
171
+    .foot_btns{
172
+      display: flex;
173
+      border-top: 1rpx solid #DDDDDD;
174
+      .know{
175
+        flex: 1;
176
+        font-size: 32rpx;
177
+        padding: 30rpx;
178
+        display: flex;
179
+        justify-content: center;
180
+        color: $uni-primary;
181
+      }
182
+    }
183
+  }
184
+}
185
+</style>

+ 1 - 1
components/IncidentListFilter.vue

@@ -225,7 +225,7 @@
225 225
     }
226 226
   }
227 227
   
228
-  onLoad(async (option) => {
228
+  onLoad((option) => {
229 229
     searchData.hospital = evt.hospital;
230 230
     searchData.selected = evt.selected;
231 231
     searchData.area = evt.area;

+ 15 - 1
http/api.js

@@ -24,7 +24,7 @@ export function api_bindAccount(data){
24 24
 /**
25 25
  * 获取系统名称
26 26
  */
27
-export function api_getSysNameAndLogo(data){
27
+export function api_getSysNameAndLogo(){
28 28
   return get("/auth/getSysNameAndLogo");
29 29
 }
30 30
 
@@ -61,4 +61,18 @@ export function api_area(data){
61 61
  */
62 62
 export function api_incidentcategory(data){
63 63
   return post("/bpm/data/fetchDataList/incidentcategory", data);
64
+}
65
+
66
+/**
67
+ * 获取报修图片列表
68
+ */
69
+export function api_wechatRequesterIncident(data){
70
+  return get("/common/common/listAttachment/wechatRequesterIncident/" + data);
71
+}
72
+
73
+/**
74
+ * 获取通话音频
75
+ */
76
+export function api_callrecord(data){
77
+  return post("/bpm/data/fetchDataList/callrecord", data);
64 78
 }

+ 39 - 30
pages/incidentList/incidentList.vue

@@ -25,18 +25,12 @@
25 25
           <view class="body_item_content_p" v-if="data.currentLog && data.currentLog.extra1DTO && data.currentLog.extra2 && data.currentLog.startTime">
26 26
             <text class="name ellipsis">延期处理:{{currentLogOverTime(data.currentLog)}}</text>
27 27
           </view>
28
-          <view class="body_item_content_p" v-if="data.assigneeName">
29
-            <text class="name">处理人:{{data.assigneeName}}</text>
30
-            <view class="icon_all">
31
-              <uni-icons type="mic-filled" class="mic-filled" :size="22" color="#949494"></uni-icons>
32
-              <uni-icons type="image-filled" class="image-filled" :size="22" color="#949494"></uni-icons>
33
-            </view>
34
-          </view>
35
-          <view class="body_item_content_p" v-if="data.candidateGroupsName">
36
-            <text class="name">处理组:{{data.candidateGroupsName}}</text>
37
-            <view class="icon_all">
38
-              <uni-icons type="mic-filled" class="mic-filled" :size="22" color="#949494"></uni-icons>
39
-              <uni-icons type="image-filled" class="image-filled" :size="22" color="#949494"></uni-icons>
28
+          <view class="body_item_content_p">
29
+            <text class="name"><template v-if="data.assigneeName">处理人:{{data.assigneeName}}</template></text>
30
+            <text class="name"><template v-if="data.candidateGroupsName">处理组:{{data.candidateGroupsName}}</template></text>
31
+            <view class="icon_all" @click="attachmentClick(data)">
32
+              <uni-icons type="mic-filled" class="mic-filled" :size="22" color="#949494" v-if="data.callID"></uni-icons>
33
+              <uni-icons type="image-filled" class="image-filled" :size="22" color="#949494" v-if="data.reqAttachment"></uni-icons>
40 34
             </view>
41 35
           </view>
42 36
         </view>
@@ -58,11 +52,13 @@
58 52
       <text class="newicon newicon-zanwu"></text>
59 53
     </view>
60 54
   </view>
61
-  <incidentListFilter v-if="dataInfo.isFilter" @cancelEmit="cancelFilter" @confirmEmit="conformFilter" :evt="dataInfo.evt"></incidentListFilter>
55
+  <incidentListFilter v-if="dataInfo.isFilter" @cancelEmit="cancelFilter" @confirmEmit="conformFilter" :evt="dataInfo.evtFilter"></incidentListFilter>
56
+  <IncidentAttachment v-if="dataInfo.isAttachment" @knowEmit="knowAttachment" :incidentData="dataInfo.incidentData"></IncidentAttachment>
62 57
 </template>
63 58
 
64 59
 <script setup>
65 60
   import IncidentListFilter from '@/components/IncidentListFilter.vue';
61
+  import IncidentAttachment from '@/components/IncidentAttachment.vue';
66 62
   import { ref, reactive } from 'vue'
67 63
   import { onLoad, onPullDownRefresh, onReachBottom } from '@dcloudio/uni-app'
68 64
   import { api_getDictionary, api_incident, api_incident_count } from "@/http/api.js"
@@ -94,7 +90,9 @@
94 90
     idx: 0,//页码
95 91
     hasMore: true,//是否有更多数据
96 92
     isFilter: false,//筛选框开关
97
-    evt: {
93
+    isAttachment: false,//图片和录音开关
94
+    incidentId: undefined,
95
+    evtFilter: {
98 96
       hospital: {},
99 97
       selected: 'todoingAll',
100 98
       area: {id: 0, area: '全部'},
@@ -135,9 +133,8 @@
135 133
   }
136 134
   
137 135
   // 确认筛选
138
-  function conformFilter(evt){
139
-    console.log(evt)
140
-    dataInfo.evt = evt;
136
+  function conformFilter(evtFilter){
137
+    dataInfo.evtFilter = evtFilter;
141 138
     dataInfo.isFilter = false;
142 139
     getList(0);
143 140
   }
@@ -147,6 +144,17 @@
147 144
     dataInfo.isFilter = false;
148 145
   }
149 146
   
147
+  // 点击图片和录音
148
+  function attachmentClick(incidentData){
149
+    dataInfo.incidentData = incidentData;
150
+    dataInfo.isAttachment = true;
151
+  }
152
+  
153
+  // 知道了图片和录音
154
+  function knowAttachment(){
155
+    dataInfo.isAttachment = false;
156
+  }
157
+  
150 158
   // 处理按钮
151 159
   function handler(type){
152 160
     uni.navigateTo({
@@ -156,19 +164,19 @@
156 164
   
157 165
   // 获取列表信息
158 166
   function getList(idx){
159
-    dataInfo.idx = idx === undefined ? dataInfo.idx : idx;
160
-    if(dataInfo.idx === 0){
161
-      dataInfo.list = [];
162
-    }
163 167
     uni.showLoading({
164 168
       title: "加载中",
165 169
       mask: true,
166 170
     });
171
+    dataInfo.idx = idx === undefined ? dataInfo.idx : idx;
172
+    if(dataInfo.idx === 0){
173
+      dataInfo.list = [];
174
+    }
167 175
     let postData = {
168 176
         "idx": dataInfo.idx,
169 177
         "sum": 10,
170 178
         "incident": {
171
-            "queryTask": dataInfo.evt.selected || undefined,
179
+            "queryTask": dataInfo.evtFilter.selected || undefined,
172 180
             "assignee": loginUserStore.loginUser.user.id,
173 181
             "statusId": dataInfo.tabActiveId || undefined,
174 182
         }
@@ -182,11 +190,11 @@
182 190
       postData.incident.branch = loginUserStore.loginUser.user.branch.id;
183 191
     }
184 192
     
185
-    if(dataInfo.evt && dataInfo.evt.category && dataInfo.evt.category.id){
186
-      postData.incident.levelCategory = dataInfo.evt.category;
193
+    if(dataInfo.evtFilter && dataInfo.evtFilter.category && dataInfo.evtFilter.category.id){
194
+      postData.incident.levelCategory = dataInfo.evtFilter.category;
187 195
     }
188
-    if(dataInfo.evt && dataInfo.evt.area && dataInfo.evt.area.id){
189
-      postData.incident.area = dataInfo.evt.area;
196
+    if(dataInfo.evtFilter && dataInfo.evtFilter.area && dataInfo.evtFilter.area.id){
197
+      postData.incident.area = dataInfo.evtFilter.area;
190 198
     }
191 199
     
192 200
     api_incident(postData).then(res => {
@@ -194,6 +202,7 @@
194 202
       uni.stopPullDownRefresh();
195 203
       if(res.status == 200){
196 204
         let list = res.list || [];
205
+        list = list.map(v => ({...v, ...{reqAttachment: true, callID:123}}))//模拟
197 206
         if(list.length){
198 207
           dataInfo.hasMore = true;
199 208
           dataInfo.list = dataInfo.idx === 0 ? list : dataInfo.list.concat(list);
@@ -248,13 +257,13 @@
248 257
             delete incident.candidateGroups;
249 258
         }
250 259
         
251
-        if(dataInfo.evt && dataInfo.evt.category && dataInfo.evt.category.id){
252
-          incident.levelCategory = dataInfo.evt.category;
260
+        if(dataInfo.evtFilter && dataInfo.evtFilter.category && dataInfo.evtFilter.category.id){
261
+          incident.levelCategory = dataInfo.evtFilter.category;
253 262
         }
254 263
         
255
-        if(dataInfo.evt && dataInfo.evt.area && dataInfo.evt.area.id){
264
+        if(dataInfo.evtFilter && dataInfo.evtFilter.area && dataInfo.evtFilter.area.id){
256 265
           incident.place = {
257
-            area: dataInfo.evt.area
266
+            area: dataInfo.evtFilter.area
258 267
           }
259 268
         }
260 269
     })

+ 3 - 2
static/scss/common.scss

@@ -5,8 +5,9 @@ page{
5 5
   height: calc(100vh - 50px);
6 6
 }
7 7
 
8
-uni-toast{
9
-  z-index: 99999;
8
+uni-toast,
9
+[longpressactions]{
10
+  z-index: 99999!important;
10 11
 }
11 12
 
12 13
 .page_padding{

+ 7 - 0
uni_modules/sy-audio/changelog.md

@@ -0,0 +1,7 @@
1
+## 1.0.6(2024-04-03)
2
+修复音频文件为服务器地址时不播放的问题
3
+## 1.0.5(2023-04-27)
4
+1.修复VUE3已知的问题
5
+2.新增change事件
6
+## 1.0.4(2023-04-10)
7
+修复vue3时已知的问题

+ 311 - 0
uni_modules/sy-audio/components/sy-audio/sy-audio.vue

@@ -0,0 +1,311 @@
1
+<template>
2
+	<view class="audio_center">
3
+		<view class="audio_center_cover">
4
+			<image v-if="audioCover" class="audio_center_cover_img" mode="aspectFill" :src="audioCover"></image>
5
+			<view class="iconfont" @tap="clickAudio" :class="audio_status ?'icon-play-stop':'icon-play-cell'"></view>
6
+		</view>
7
+		<view class="audio_center_right">
8
+			<view v-if="stringObject(src) == 'string'" class="single">
9
+				<view class="single_title">
10
+					<view class="single_title_info" :style="{color:audioTitleColor}">{{audioTitle}}</view>
11
+					<view class="tips">{{timeTxt}}</view>
12
+				</view>
13
+				<view class="tips">{{subheading}}</view>
14
+				<slider :backgroundColor='backgroundColor' :activeColor='activeColor' @change="sliderChange"
15
+					:value="sliderIndex" :max="maxSliderIndex" :block-size='0' />
16
+			</view>
17
+			<view v-else>相关功能正在开发中~</view>
18
+		</view>
19
+	</view>
20
+</template>
21
+<script>
22
+	let timer;
23
+	export default {
24
+		name: 'syAudio',
25
+		emits: ['audioPlay', 'audioPause', 'audioEnd', 'audioCanplay', 'change'],
26
+		data() {
27
+			return {
28
+				audio_status: false,
29
+
30
+				timeTxt: '00 : 00',
31
+				timeIndex: 0,
32
+
33
+				sliderIndex: 0,
34
+				maxSliderIndex: 100,
35
+
36
+				stringObject: (data) => {
37
+					return typeof(data)
38
+				},
39
+				innerAudioContext: uni.createInnerAudioContext()
40
+			}
41
+		},
42
+		props: {
43
+			//是否自动播放(只支持微信内置浏览器,小程序,app)
44
+			autoplay: {
45
+				type: Boolean,
46
+				default: false
47
+			},
48
+			//音频地址
49
+			src: {
50
+				type: [String, Array],
51
+				default: ''
52
+			},
53
+			//是否倒计时
54
+			isCountDown: {
55
+				type: Boolean,
56
+				default: false
57
+			},
58
+			//音乐封面
59
+			audioCover: {
60
+				type: String,
61
+				default: ''
62
+			},
63
+			//标题
64
+			audioTitle: {
65
+				type: String,
66
+				default: 'new Audio Title'
67
+			},
68
+			//标题颜色
69
+			audioTitleColor: {
70
+				type: String,
71
+				default: '#333'
72
+			},
73
+			//副标题
74
+			subheading: {
75
+				type: String,
76
+				default: 'new Audio Subheading'
77
+			},
78
+			//滑块左侧已选择部分的线条颜色
79
+			activeColor: {
80
+				type: String,
81
+				default: '#bf41a2'
82
+			},
83
+			//滑块右侧背景条的颜色
84
+			backgroundColor: {
85
+				type: String,
86
+				default: '#f1c38b'
87
+			}
88
+		},
89
+		watch: {
90
+			src: {
91
+				handler: function(newVal, oldVal) {
92
+					this.innerAudioContext.src = typeof(newVal) == 'string' ? newVal : newVal;
93
+				},
94
+				deep: true
95
+			}
96
+		},
97
+		async mounted() {
98
+			this.innerAudioContext.src = typeof(this.src) == 'string' ? this.src : this.src[0];
99
+      this.countDown();
100
+
101
+			if (this.autoplay) {
102
+				if (!this.src) return console.error('src cannot be empty,The target value is string or array')
103
+
104
+				// #ifdef H5
105
+				var ua = window.navigator.userAgent.toLowerCase();
106
+				if (ua.match(/MicroMessenger/i) == 'micromessenger') {
107
+					const jweixin = require('../../utils/jweixin');
108
+
109
+					jweixin.config({});
110
+					jweixin.ready(() => {
111
+						WeixinJSBridge.invoke('getNetworkType', {}, (e) => {
112
+							this.innerAudioContext.play();
113
+							this.countDown()
114
+						})
115
+					})
116
+				}
117
+				// #endif
118
+
119
+				// #ifndef H5
120
+				this.innerAudioContext.autoplay = true;
121
+				// #endif
122
+			}
123
+
124
+			this.innerAudioContext.onPlay(() => {
125
+				this.audio_status = true;
126
+				this.$emit('audioPlay')
127
+				setTimeout(() => {
128
+					this.maxSliderIndex = parseFloat(this.innerAudioContext.duration).toFixed(2);
129
+					this.countDown();
130
+				}, 100)
131
+			});
132
+
133
+			this.innerAudioContext.onPause(() => {
134
+				this.$emit('audioPause');
135
+			});
136
+
137
+			this.innerAudioContext.onEnded(() => {
138
+				this.audio_status = !this.audio_status;
139
+				this.$emit('audioEnd');
140
+			});
141
+
142
+			this.innerAudioContext.onCanplay((event) => {
143
+				this.$emit('audioCanplay');
144
+				// #ifdef MP
145
+				this.maxSliderIndex = parseFloat(this.innerAudioContext.duration).toFixed(2);
146
+				// #endif
147
+			});
148
+
149
+			this.innerAudioContext.onPlay(() => {
150
+				this.$emit('change', {
151
+					state: true
152
+				});
153
+			});
154
+
155
+			this.innerAudioContext.onPause(() => {
156
+				this.$emit('change', {
157
+					state: false
158
+				});
159
+			});
160
+		},
161
+		methods: {
162
+			//销毁innerAudioContext()实例
163
+			audioDestroy() {
164
+				if (this.innerAudioContext) {
165
+					this.innerAudioContext.destroy();
166
+					this.audio_status = false;
167
+				}
168
+			},
169
+			//跳转到指定位置
170
+			audioSeek(value) {
171
+				this.sliderChange(value)
172
+			},
173
+			//控制音乐播放/暂停
174
+			audioPause() {
175
+				this.clickAudio()
176
+			},
177
+			countDown() {
178
+				timer = setInterval(() => {
179
+					this.sliderIndex = parseFloat(this.innerAudioContext.currentTime).toFixed(2);
180
+
181
+					this.timeTxt = this.getTime(this.isCountDown ? this.innerAudioContext.duration - this
182
+						.innerAudioContext
183
+						.currentTime : this.innerAudioContext.currentTime);
184
+					this.timeTxt = this.isCountDown ? '- ' + this.timeTxt : this.timeTxt;
185
+
186
+					if (this.innerAudioContext.currentTime >= this.innerAudioContext.duration) {
187
+						clearInterval(timer);
188
+					}
189
+				}, 100)
190
+			},
191
+			clickAudio() {
192
+
193
+				if (this.audio_status && !this.innerAudioContext.paused) {
194
+					this.innerAudioContext.pause();
195
+					clearInterval(timer);
196
+				} else {
197
+					this.innerAudioContext.play();
198
+				}
199
+
200
+				this.audio_status = !this.audio_status;
201
+			},
202
+			getTime(time) {
203
+				let m = parseInt(time / 60 % 60)
204
+				m = m < 10 ? '0' + m : m
205
+				let s = parseInt(time % 60)
206
+				s = s < 10 ? '0' + s : s
207
+				return m + ' : ' + s
208
+			},
209
+			sliderChange(e) {
210
+				this.innerAudioContext.seek(e.detail ? e.detail.value : e);
211
+				this.sliderIndex = e.detail ? e.detail.value : e;
212
+			}
213
+		},
214
+		onUnload() {
215
+			this.audioDestroy()
216
+		},
217
+		onHide() {
218
+			this.audioDestroy()
219
+		},
220
+		beforeDestroy() {
221
+			this.audioDestroy()
222
+		}
223
+	}
224
+</script>
225
+
226
+<style lang="scss" scoped>
227
+	@import url('../../static/font/iconfont.css');
228
+
229
+	::v-deep uni-slider,
230
+	::v-deep slider {
231
+		margin: 0;
232
+		overflow: hidden;
233
+		padding: 0;
234
+	}
235
+
236
+	.audio_center {
237
+		display: flex;
238
+		box-shadow: 0 0 10rpx #c3c3c3;
239
+		border-radius: 10rpx;
240
+		overflow: hidden;
241
+		align-items: stretch;
242
+
243
+		.single {
244
+			flex: 1;
245
+			display: flex;
246
+			flex-direction: column;
247
+			justify-content: space-between;
248
+			padding: 16rpx 24rpx 10rpx 0;
249
+
250
+			.tips {
251
+				font-size: 22rpx;
252
+				color: #919191;
253
+				flex-shrink: 0;
254
+			}
255
+
256
+			&_title {
257
+				&_info {
258
+					font-size: 28rpx;
259
+					overflow: hidden;
260
+					display: -webkit-box;
261
+					-webkit-line-clamp: 1;
262
+					-webkit-box-orient: vertical;
263
+					margin-right: 20rpx;
264
+				}
265
+
266
+				display: flex;
267
+				justify-content: space-between;
268
+				align-items: flex-end;
269
+			}
270
+		}
271
+
272
+		&_cover {
273
+			flex-shrink: 0;
274
+			background: #f5f5f5;
275
+			position: relative;
276
+			display: flex;
277
+			align-items: stretch;
278
+			width: 127rpx;
279
+
280
+			.iconfont {
281
+				position: absolute;
282
+				top: 0;
283
+				left: 0;
284
+				right: 0;
285
+				bottom: 0;
286
+				margin: auto;
287
+				z-index: 9;
288
+				display: flex;
289
+				justify-content: center;
290
+				align-items: center;
291
+				font-size: 64rpx;
292
+				background: rgba(0, 0, 0, .25);
293
+				color: #fff;
294
+			}
295
+
296
+			&_img {
297
+				width: 127rpx;
298
+				height: 100%;
299
+				border-radius: 10rpx 0 0 10rpx;
300
+			}
301
+
302
+			margin-right:30rpx;
303
+		}
304
+
305
+		&_right {
306
+			display: flex;
307
+			align-items: stretch;
308
+			flex: 1;
309
+		}
310
+	}
311
+</style>

+ 85 - 0
uni_modules/sy-audio/package.json

@@ -0,0 +1,85 @@
1
+{
2
+  "id": "sy-audio",
3
+  "displayName": "sy-audio",
4
+  "version": "1.0.6",
5
+  "description": "sy-audio基于uni.createInnerAudioContext()方法搭建,并支持全端环境;",
6
+  "keywords": [
7
+    "audio",
8
+    "播放器",
9
+    "createInnerAudio",
10
+    "音乐播放器",
11
+    "音频倍速播放"
12
+],
13
+  "repository": "",
14
+  "engines": {
15
+    "HBuilderX": "^3.6.13"
16
+  },
17
+  "dcloudext": {
18
+    "type": "component-vue",
19
+    "sale": {
20
+      "regular": {
21
+        "price": "0.00"
22
+      },
23
+      "sourcecode": {
24
+        "price": "0.00"
25
+      }
26
+    },
27
+    "contact": {
28
+      "qq": ""
29
+    },
30
+    "declaration": {
31
+      "ads": "无",
32
+      "data": "无",
33
+      "permissions": "无"
34
+    },
35
+    "npmurl": ""
36
+  },
37
+  "uni_modules": {
38
+    "dependencies": [],
39
+    "encrypt": [],
40
+    "platforms": {
41
+      "cloud": {
42
+        "tcb": "y",
43
+        "aliyun": "y"
44
+      },
45
+      "client": {
46
+        "Vue": {
47
+          "vue2": "y",
48
+          "vue3": "y"
49
+        },
50
+        "App": {
51
+          "app-vue": "y",
52
+          "app-nvue": "y"
53
+        },
54
+        "H5-mobile": {
55
+          "Safari": "y",
56
+          "Android Browser": "y",
57
+          "微信浏览器(Android)": "y",
58
+          "QQ浏览器(Android)": "y"
59
+        },
60
+        "H5-pc": {
61
+          "Chrome": "y",
62
+          "IE": "y",
63
+          "Edge": "y",
64
+          "Firefox": "y",
65
+          "Safari": "y"
66
+        },
67
+        "小程序": {
68
+          "微信": "y",
69
+          "阿里": "y",
70
+          "百度": "y",
71
+          "字节跳动": "y",
72
+          "QQ": "y",
73
+          "钉钉": "y",
74
+          "快手": "y",
75
+          "飞书": "y",
76
+          "京东": "y"
77
+        },
78
+        "快应用": {
79
+          "华为": "y",
80
+          "联盟": "y"
81
+        }
82
+      }
83
+    }
84
+  }
85
+}

+ 62 - 0
uni_modules/sy-audio/readme.md

@@ -0,0 +1,62 @@
1
+
2
+
3
+欢迎使用 sy-audio 在线音乐播放器
4
+
5
+## props (组件属性)
6
+
7
+  属性  | 类型 | 默认值 | 备注
8
+------------- | ------------- | ---------
9
+autoplay  |Boolean| false | 是否自动播放(只支持微信内置浏览器,小程序,app)
10
+src  |String| '' | 音频地址
11
+isCountDown  |Boolean| false |是否倒计时
12
+audioCover  |String| '' | 音乐封面
13
+audioTitle  |String| new Audio Title | 标题
14
+audioTitleColor  |String| #333 | 标题颜色
15
+subheading  |String| new Audio Subheading | 副标题
16
+activeColor  |String| #bf41a2 | 滑块左侧已选择部分的线条颜色
17
+backgroundColor  |String| #f1c38b | 滑块右侧背景条的颜色
18
+
19
+
20
+## 组件事件
21
+
22
+  事件  | 事件内容
23
+------------- | -------------
24
+@audioPlay | 音频播放事件
25
+@audioPause | 音频暂停事件
26
+@audioEnd | 音频自然播放结束事件
27
+@change | 音频播放状态监听(返回true/false)
28
+@audioCanplay | 音频进入可以播放状态,但不保证后面可以流畅播放
29
+
30
+## ref 事件
31
+  事件  | 事件内容
32
+------------- | -------------
33
+audioPause() | 控制音乐播放/暂停
34
+audioSeek() | 跳转到指定位置
35
+audioDestroy() | 销毁innerAudioContext()实例
36
+
37
+```html
38
+<sy-audio isCountDown ref="audio" src='' audioCover='' subheading='' audioTitle=''></sy-audio>
39
+
40
+//项目为vue2时 调用跳转到音乐15s位置方法,如下:
41
+this.$refs.audio.audioSeek(15)
42
+
43
+//项目为vue3时 请通过value形式调用子组件方法
44
+```
45
+
46
+#### View代码
47
+```html
48
+<sy-audio isCountDown ref="audio" src='' audioCover='' subheading='' audioTitle=''></sy-audio>
49
+
50
+//项目为vue2时 src为本地路径时,使用require方法,如下:
51
+<sy-audio :src="require('@/static/audio.mp3')"></sy-audio>
52
+
53
+//项目为vue3时 src为本地路径时,使用自定义方法,如下:
54
+<sy-audio :src="toUrl('../../static/audio.mp3')"></sy-audio>
55
+methods: {
56
+	toUrl(src){
57
+		return new URL(src, import.meta.url).href
58
+	}
59
+}
60
+```
61
+
62
+> sy-audio将会持续更新,欢迎大家踊跃提出宝贵建议;

+ 37 - 0
uni_modules/sy-audio/static/font/iconfont.css

@@ -0,0 +1,37 @@
1
+@font-face {
2
+  font-family: "iconfont"; /* Project id 3988464 */
3
+  src: url('../../static/font/iconfont.ttf') format('truetype');
4
+}
5
+
6
+.iconfont {
7
+  font-family: "iconfont" !important;
8
+  font-size: 16px;
9
+  font-style: normal;
10
+  -webkit-font-smoothing: antialiased;
11
+  -moz-osx-font-smoothing: grayscale;
12
+}
13
+
14
+.icon-play-stop:before {
15
+  content: "\e662";
16
+}
17
+
18
+.icon-play-stop-new:before {
19
+  content: "\e605";
20
+}
21
+
22
+.icon-play-cell-new:before {
23
+  content: "\ea83";
24
+}
25
+
26
+.icon-play-nav:before {
27
+  content: "\e63c";
28
+}
29
+
30
+.icon-play-next:before {
31
+  content: "\e63e";
32
+}
33
+
34
+.icon-play-cell:before {
35
+  content: "\ea82";
36
+}
37
+

BIN
uni_modules/sy-audio/static/font/iconfont.ttf


La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 1 - 0
uni_modules/sy-audio/utils/jweixin.js