conductInspections.vue 33 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231
  1. <!-- 开展检查 -->
  2. <template>
  3. <view class="inspectAdd">
  4. <scroll-view scroll-y @scrolltolower="scrollGet" class="info-max-box">
  5. <view class="list">
  6. <view class="list-li srearch">
  7. <view class="srearch-l">{{newData.subName?newData.subName:'实验室名称'}}</view>
  8. </view>
  9. <view class="list-li" style="margin-bottom: 20rpx;border:none;">
  10. <view>
  11. {{newData.roomNum?newData.roomNum:''}}房间&{{newData.buildName?newData.buildName:'楼栋'}}&{{newData.deptName?newData.deptName:'学院单位'}}
  12. </view>
  13. </view>
  14. <view class="list-li">
  15. <view>现场照片</view>
  16. </view>
  17. <view v-if="form.photoList[0]" class="check-for-img-max-box">
  18. <view class="left-title-p"></view>
  19. <view class="right-img-box">
  20. <view class="img-box" v-for="(imgUrl,imgIndex) in form.photoList" :key="imgIndex">
  21. <img class="img-data" :src="baseUrl+imgUrl.fileUrl">
  22. <img class="position-img" :src="imagesUrl('commonality/icon_ssp_closure.png')"
  23. @click="delImg(imgIndex)">
  24. </view>
  25. <!-- <img class="add-button" :src="imagesUrl('safetyCheck/img_ssp_pz@1x.png')"
  26. @click="selectImage()" v-if="form.photoList.length<6"> -->
  27. </view>
  28. </view>
  29. <view class="list-li" @click="checkItemModuleButton('open')" style="border-bottom: none;">
  30. <view> {{newData.hazardCheckCode?newData.hazardCheckCode:''}}
  31. {{newData.hazardCheckName?newData.hazardCheckName:'检查项'}}
  32. </view>
  33. <view style="color: #0183FA;">
  34. <img :src="imagesUrl('commonality/icon_wd_gd@1x.png')">
  35. </view>
  36. </view>
  37. <view class="hazardCheckNum" @click="hiddenDangerClick()">
  38. <img class="hazardCheckNum-l" :src="imagesUrl('safetyCheck/icon_aqjc_yinhuan.png')">
  39. <view class="hazardCheckNum-r">{{'已连续出现'+hazardCheckNum+'次隐患'}} </view>
  40. </view>
  41. <view class="list-li" v-if="!form.checkFlag">
  42. <view>隐患描述</view>
  43. <view></view>
  44. </view>
  45. <view class="describe">
  46. <textarea v-if="!form.checkFlag && !checkItemModuleType" cursor-spacing="40" class="describe-n" type="text"
  47. v-model="form.hazardDescribe" maxlength="200" placeholder="请填写隐患描述"
  48. placeholder-style="font-size:24rpx;color:#999;"></textarea>
  49. </view>
  50. </view>
  51. <view class="voice">
  52. <view class="voice_t">
  53. <view class="voice_t_t" v-if="!form.voiceList[0]"><text>语音备注:</text><text>按住话筒录音</text></view>
  54. <view class="voice_t_b" v-if="form.voiceList[0]">语音备注:({{form.voiceList.length}})</view>
  55. </view>
  56. <view class="voice_b" v-if="form.voiceList[0]" v-for="(voiceItem,voiceIndex) in form.voiceList"
  57. :key="voiceIndex">
  58. <view class="voice_b_l" @tap="voicePlay(voiceItem.fileUrl)">
  59. <img class="add-button" :src="imagesUrl('safetyCheck/icon_yybz_yy.png')" />
  60. {{voiceItem.fileLength}}″
  61. </view>
  62. <img class="voice_b_r" :src="imagesUrl('safetyCheck/icon_yybz_sc.png')"
  63. @tap="voiceDele('',voiceIndex)" />
  64. </view>
  65. <!-- <view class="voice-btn">
  66. <img class="voice_t_r" :src="imagesUrl('commonality/icon_sskz_azsh.png')"
  67. @longpress.stop="recordButton(item,$event)" @touchmove.stop="cancelButton"
  68. @touchend.stop="sendButton(item,$event)" />
  69. <text>按住说话</text>
  70. </view> -->
  71. </view>
  72. </scroll-view>
  73. <checkItemModule :propsData="propsData" v-if="checkItemModuleType"></checkItemModule>
  74. <!-- <view class="sub-btn">
  75. <view @click="submitForm(1)">保存草稿</view>
  76. <view @click="submitForm(2)">提交</view>
  77. </view> -->
  78. <view class="sub-btn" v-if="!checkItemModuleType">
  79. <!-- #ifdef MP-WEIXIN -->
  80. <view
  81. class="broadcast_m no-long-press"
  82. @longpress.stop="recordButton(item,$event)"
  83. @touchmove.stop="cancelButton"
  84. @touchend.stop="sendButton(item,$event)"
  85. ></view>
  86. <!-- <img
  87. @longpress.stop="recordButton(item,$event)"
  88. @touchmove.stop="cancelButton"
  89. @touchend.stop="sendButton(item,$event)"
  90. :src="imagesUrl('safetyCheck/icon_aqjc_luyin.png')" /> -->
  91. <!-- #endif -->
  92. <!-- #ifdef WEB -->
  93. <view
  94. class="broadcast_m no-long-press"
  95. @touchstart="handleTouchStart"
  96. @touchmove="handleTouchMove"
  97. @touchend="handleTouchEnd"
  98. @touchcancel="handleTouchEnd"
  99. @contextmenu.prevent="handleContextMenu"
  100. ></view>
  101. <!-- <img
  102. @touchstart="handleTouchStart"
  103. @touchmove="handleTouchMove"
  104. @touchend="handleTouchEnd"
  105. @touchcancel="handleTouchEnd"
  106. @contextmenu.prevent="handleContextMenu"
  107. :src="imagesUrl('safetyCheck/icon_aqjc_luyin.png')" /> -->
  108. <!-- #endif -->
  109. <img @click="selectImage()" :src="imagesUrl('safetyCheck/icon_aqjc_paizhao.png')" />
  110. <img @click="submitForm(2)" :src="imagesUrl('safetyCheck/icon_aqjc_tijiao.png')" />
  111. </view>
  112. <view v-if="!checkItemModuleType" class="inspectRecord" @click="inspectRecordClick()">检查记录</view>
  113. </view>
  114. </template>
  115. <script>
  116. // #ifdef WEB
  117. import Recorder from 'recorder-core';
  118. import 'recorder-core/src/engine/mp3';
  119. import 'recorder-core/src/engine/mp3-engine';
  120. // #endif
  121. import {
  122. config
  123. } from '@/api/request/config.js'
  124. import {
  125. securityAppCheckPlanCheckInfo,
  126. securityAppCheckSetOptionCheckCommit,
  127. securityAppCheckPhotoGetCheckNumBySub,
  128. } from '@/pages_safetyCheck/api/index.js'
  129. import checkItemModule from '@/pages_safetyCheck/component/checkItemModule.vue'
  130. export default {
  131. name: "inspectAdd",
  132. components: {
  133. checkItemModule
  134. },
  135. data() {
  136. return {
  137. baseUrl: config.base_url,
  138. form: {
  139. checkFlag: false,
  140. photoList: [],
  141. voiceList: [],
  142. },
  143. newData: {},
  144. radioList: [{
  145. id: 1,
  146. name: '符合',
  147. checked: false,
  148. },
  149. {
  150. id: 0,
  151. name: '不符合',
  152. checked: true,
  153. },
  154. ],
  155. pageType: '',
  156. sendLock: true, //发送锁,当为true时上锁,false时解锁发送
  157. recorderManager: wx.getRecorderManager(),
  158. optionData: null,
  159. //检查项组件数据
  160. checkItemModuleType: false,
  161. propsData: {},
  162. hazardCheckNum:0,
  163. //广播相关
  164. liveType: false,
  165. sendLock: true, //发送锁,当为true时上锁,false时解锁发送
  166. //H5
  167. recording: false,
  168. recorder: null,
  169. audioBlob: null,
  170. audioPath: null,
  171. }
  172. },
  173. onLoad(option) {
  174. let optionData = JSON.parse(decodeURIComponent(option.infoData));
  175. uni.setNavigationBarTitle({
  176. title: optionData.subName + '(' + (optionData.pageType == 0 ? (optionData.roomNum ? optionData
  177. .roomNum : '-') : (optionData
  178. .subRoom ? optionData
  179. .subRoom : '-')) + ')'
  180. })
  181. this.$set(this, 'pageType', optionData.pageType);
  182. this.$set(this, 'optionData', optionData);
  183. },
  184. onShow() {
  185. // #ifdef MP-WEIXIN
  186. if (this.recorderManager) {
  187. this.recorderManager.stop();
  188. }
  189. // #endif
  190. },
  191. mounted() {
  192. if (this.pageType == 0) {
  193. this.securityAppCheckPlanCheckInfo();
  194. this.securityAppCheckPhotoGetCheckNumBySub();
  195. }
  196. },
  197. methods: {
  198. //折叠展开
  199. inspectDot() {
  200. this.newData.inspectDot = !this.newData.inspectDot
  201. },
  202. //检查记录
  203. inspectRecordClick(){
  204. let infoData = this.optionData;
  205. infoData.curTabTow=1;//已检查
  206. uni.navigateTo({
  207. url: '/pages_safetyCheck/views/itemsManage/hiddenDangerItems?infoData=' +
  208. encodeURIComponent(JSON
  209. .stringify(infoData))
  210. });
  211. },
  212. //获取提交详情
  213. async securityAppCheckPlanCheckInfo() {
  214. let obj = {
  215. setOptionId: this.optionData.setOptionId,
  216. manageId: this.optionData.manageId,
  217. }
  218. const {
  219. data
  220. } = await securityAppCheckPlanCheckInfo(obj);
  221. if (data.code == 200) {
  222. data.data.inspectDot = true;
  223. this.$set(this, 'newData', data.data);
  224. //编辑的时候
  225. this.$set(this.form, 'checkFlag', data.data.checkFlag ? data.data.checkFlag : false);
  226. if(data.data.hazardDescribe){
  227. this.$set(this.form, 'hazardDescribe', data.data.hazardDescribe);
  228. }else{
  229. this.$set(this.newData,'hazardCheckPro',this.optionData.hazardCheckPro);
  230. this.$set(this.newData,'hazardCheckCode',this.optionData.hazardCheckCode);
  231. this.$set(this.newData,'hazardCheckName',this.optionData.hazardCheckName);
  232. this.$set(this.newData,'hazardCheckPro1',this.optionData.hazardCheckPro1);
  233. this.$set(this.newData,'hazardCheckCode1',this.optionData.hazardCheckCode1);
  234. this.$set(this.newData,'hazardCheckName1',this.optionData.hazardCheckName1);
  235. this.$set(this.newData,'hazardCheckPro2',this.optionData.hazardCheckPro2);
  236. this.$set(this.newData,'hazardCheckCode2',this.optionData.hazardCheckCode2);
  237. this.$set(this.newData,'hazardCheckName2',this.optionData.hazardCheckName2);
  238. this.$set(this.newData,'hazardCheckPoint',this.optionData.hazardCheckPoint);
  239. this.$set(this.form,'hazardDescribe',this.optionData.hazardCheckPoint);
  240. // let list=[];
  241. // list=data.data.hazardCheckPoint.split('#');
  242. // this.$set(this.form, 'hazardDescribe', list[0]);
  243. }
  244. this.$set(this.form, 'photoList', data.data.photoList ? data.data.photoList : []);
  245. this.$set(this.form, 'voiceList', data.data.voiceList ? data.data.voiceList : []);
  246. }
  247. },
  248. hiddenDangerClick(row) {
  249. if(!this.hiddenDangerRecordStatus){
  250. let infoData = this.optionData;
  251. infoData.subName=this.newData.subName;
  252. infoData.roomNum=this.newData.roomNum;
  253. infoData.hiddenDangerRecordStatus = true;//判断是否跳转隐患列表
  254. uni.navigateTo({
  255. url: '/pages_safetyCheck/views/inspectManage/hiddenDangerRecord?infoData=' +
  256. encodeURIComponent(JSON.stringify(infoData))
  257. });
  258. }
  259. },
  260. checkItemModuleButton(type, item,keyPoint) {
  261. if (type == 'open') {
  262. let obj = {
  263. infoType: this.pageType,
  264. checkType: 0,
  265. editStatus:this.optionData.editStatus,
  266. editItem:{
  267. hazardCheckName:this.newData.hazardCheckName,
  268. hazardCheckName1:this.newData.hazardCheckName1,
  269. hazardCheckName2:this.newData.hazardCheckName2,
  270. hazardCheckCode:this.newData.hazardCheckCode,
  271. hazardCheckCode1:this.newData.hazardCheckCode1,
  272. hazardCheckCode2:this.newData.hazardCheckCode2,
  273. hazardCheckPoint:this.newData.hazardCheckPoint,
  274. hazardCheckPro:this.newData.hazardCheckPro,
  275. hazardCheckPro1:this.newData.hazardCheckPro1,
  276. hazardCheckPro2:this.newData.hazardCheckPro2,
  277. setOptionId:this.newData.setOptionId,
  278. }
  279. }
  280. if (this.pageType == 0) {
  281. //检查
  282. obj.manageId = this.optionData.manageId
  283. obj.planSetId = this.optionData.planSetId
  284. }
  285. this.$set(this, 'propsData', obj);
  286. this.$set(this, 'checkItemModuleType', true);
  287. } else if (type == 'out') {
  288. this.$set(this, 'checkItemModuleType', false);
  289. this.$set(this, 'propsData', {});
  290. } else if (type == 'submit') {
  291. this.$set(this.newData, 'hazardCheckPro', item.hazardCheckPro);
  292. this.$set(this.newData, 'hazardCheckCode', item.hazardCheckCode);
  293. this.$set(this.newData, 'hazardCheckName', item.hazardCheckName);
  294. this.$set(this.newData, 'hazardCheckPro1', item.hazardCheckPro1);
  295. this.$set(this.newData, 'hazardCheckCode1', item.hazardCheckCode1);
  296. this.$set(this.newData, 'hazardCheckName1', item.hazardCheckName1);
  297. this.$set(this.newData, 'hazardCheckPro2', item.hazardCheckPro2);
  298. this.$set(this.newData, 'hazardCheckCode2', item.hazardCheckCode2);
  299. this.$set(this.newData, 'hazardCheckName2', item.hazardCheckName2);
  300. this.$set(this.newData, 'hazardCheckPoint', item.hazardCheckPoint);
  301. this.$set(this.form, 'hazardDescribe', keyPoint);
  302. // this.$set(this.optionData, 'setOptionId', item.setOptionId);
  303. this.$set(this.optionData, 'hazardCheckPro', item.hazardCheckPro);
  304. this.$set(this, 'checkItemModuleType', false);
  305. this.securityAppCheckPhotoGetCheckNumBySub();
  306. //编辑的时候
  307. if(this.optionData.editStatus){
  308. if(this.optionData.setOptionId!=this.newData.setOptionId){
  309. this.$set(this.form, 'photoList', []);
  310. this.$set(this.form, 'voiceList', []);
  311. }
  312. }
  313. }
  314. },
  315. //提交
  316. async submitForm(status) {
  317. if (!this.form.checkFlag) {
  318. if (!this.form.hazardDescribe) {
  319. uni.showToast({
  320. title: '请填写隐患描述!',
  321. icon: "none",
  322. mask: true,
  323. duration: 2000
  324. });
  325. return
  326. }
  327. if (!this.form.photoList[0]) {
  328. uni.showToast({
  329. title: '请选择现场照片!',
  330. icon: "none",
  331. mask: true,
  332. duration: 2000
  333. });
  334. return
  335. }
  336. }
  337. let obj = {
  338. checkFlag: this.form.checkFlag,
  339. checkStatus: 1,
  340. hazardDescribe: this.form.hazardDescribe,
  341. photoList: this.form.photoList,
  342. voiceList: this.form.voiceList,
  343. manageId: this.optionData.manageId,
  344. hazardCheckPro: this.newData.hazardCheckPro,
  345. hazardCheckCode: this.newData.hazardCheckCode,
  346. hazardCheckName: this.newData.hazardCheckName,
  347. hazardCheckPro2: this.newData.hazardCheckPro2,
  348. hazardCheckCode2: this.newData.hazardCheckCode2,
  349. hazardCheckName2: this.newData.hazardCheckName2,
  350. hazardCheckPro1: this.newData.hazardCheckPro1,
  351. hazardCheckCode1: this.newData.hazardCheckCode1,
  352. hazardCheckName1: this.newData.hazardCheckName1,
  353. hazardCheckPoint: this.form.hazardDescribe,
  354. }
  355. if(this.optionData.setOptionId){
  356. obj.setOptionId = this.optionData.setOptionId;
  357. }
  358. const {
  359. data
  360. } = await securityAppCheckSetOptionCheckCommit(obj);
  361. if (data.code == 200) {
  362. uni.$emit('infoPageSubmitButton', { type:1,subId:this.newData.subId });
  363. uni.showToast({
  364. title: '提交成功!',
  365. icon: "none",
  366. mask: true,
  367. duration: 2000
  368. });
  369. let infoData = this.optionData;
  370. infoData.setOptionId = data.data;
  371. uni.redirectTo({
  372. url: '/pages_safetyCheck/views/itemsManage/hiddenDangerItemsDetail?infoData=' +
  373. encodeURIComponent(JSON
  374. .stringify(infoData))
  375. });
  376. }
  377. },
  378. //获取检查项在当前实验室出现的次数
  379. async securityAppCheckPhotoGetCheckNumBySub() {
  380. let obj = {
  381. subId: this.optionData.subId,
  382. hazardCheckPro: this.optionData.hazardCheckPro,
  383. }
  384. const {
  385. data
  386. } = await securityAppCheckPhotoGetCheckNumBySub(obj);
  387. if (data.code == 200) {
  388. this.$set(this, 'hazardCheckNum', data.data);
  389. }
  390. },
  391. //滚动事件
  392. scrollGet() {},
  393. //单选按钮
  394. radioClick(d) {
  395. let self = this;
  396. d.checked = !d.checked
  397. if (d.checked) {
  398. //如果有选中的,循环把其他选中的取消
  399. this.radioList.forEach(function(item) {
  400. if (item.id == d.id) {
  401. self.$set(self.form, 'checkFlag', item.id);
  402. } else {
  403. item.checked = false;
  404. }
  405. })
  406. } else {
  407. }
  408. },
  409. /******图片上传******/
  410. selectImage() {
  411. let self = this;
  412. let photoMaxNun = 0;
  413. if (this.form.photoList.length > 5) {
  414. uni.showToast({
  415. title: '最多上传6张图片',
  416. icon: "none",
  417. mask: true,
  418. duration: 2000
  419. });
  420. return
  421. }
  422. photoMaxNun = 6 - this.form.photoList.length;
  423. uni.chooseImage({
  424. count: photoMaxNun,
  425. sizeType: ["original", "compressed"],
  426. sourceType: ["album", "camera"],
  427. success: function(res) {
  428. if (res.tempFilePaths[0]) {
  429. res.tempFilePaths.forEach(function(item, index) {
  430. self.uploadImg(item);
  431. })
  432. }
  433. }
  434. });
  435. },
  436. async uploadImg(tempFilePaths) {
  437. var self = this;
  438. uni.showLoading({
  439. title: '上传中',
  440. mask: true
  441. });
  442. uni.uploadFile({
  443. url: config.base_url + '/system/file/upload', //仅为示例,非真实的接口地址
  444. header: {
  445. 'Authorization': uni.getStorageSync('token')
  446. },
  447. filePath: tempFilePaths,
  448. name: 'file',
  449. formData: {
  450. 'user': 'test'
  451. },
  452. success: (uploadFileRes) => {
  453. let res = JSON.parse(uploadFileRes.data);
  454. if (res.code == 200) {
  455. this.form.photoList.push({
  456. 'fileUrl': res.data.url,
  457. 'fileName': res.data.name
  458. });
  459. } else {
  460. uni.showToast({
  461. title: res.msg,
  462. icon: "none",
  463. mask: true,
  464. duration: 2000
  465. });
  466. }
  467. },
  468. fail: err => {},
  469. complete: () => {
  470. uni.hideLoading()
  471. }
  472. });
  473. },
  474. //删除图片
  475. delImg(minIndex) {
  476. this.form.photoList.splice(minIndex, 1);
  477. this.$forceUpdate();
  478. },
  479. /******语音备注 *******/
  480. //语音备忘播放
  481. voicePlay(item) {
  482. const url = uni.getStorageSync('fileBrowseEnvironmentS') + '/' + item;
  483. // #ifdef WEB
  484. const audio = new Audio(url);
  485. audio.play().then(() => {
  486. uni.showToast({ title: '开始播放', icon: "none", mask: true });
  487. }).catch(() => {
  488. uni.showToast({ title: '播放失败', icon: "none", mask: true });
  489. });
  490. // #endif
  491. // #ifndef WEB
  492. const innerAudioContext = uni.createInnerAudioContext();
  493. innerAudioContext.src = url;
  494. innerAudioContext.autoplay = true;
  495. innerAudioContext.onPlay(() => {
  496. uni.showToast({ title: '开始播放', icon: "none", mask: true });
  497. });
  498. innerAudioContext.onError(() => {
  499. uni.showToast({ title: '播放失败', icon: "none", mask: true });
  500. });
  501. // #endif
  502. },
  503. //语音备忘删除
  504. async voiceDele(item, voiceIndex) {
  505. let self = this;
  506. uni.showModal({
  507. title: '',
  508. cancelColor: '#999999',
  509. confirmColor: '#FF6E6E',
  510. content: '确定删除此语音吗?',
  511. success(res) {
  512. if (res.confirm) {
  513. console.log('用户点击确定')
  514. self.form.voiceList.splice(voiceIndex, 1);
  515. self.$forceUpdate();
  516. } else if (res.cancel) {
  517. console.log('用户点击取消')
  518. }
  519. }
  520. })
  521. },
  522. //录制
  523. recordButton(item, e) {
  524. console.log("按下")
  525. let self = this;
  526. if (this.form.voiceList.length >= 5) {
  527. uni.showToast({
  528. title: '语音备忘最多只能上传5条',
  529. icon: "none",
  530. mask: true,
  531. duration: 2000
  532. });
  533. return
  534. }
  535. this.liveType = true;
  536. console.log('录制', e)
  537. this.startPoint = e.touches[0]; //记录长按时开始点信息,后面用于计算上划取消时手指滑动的距离。
  538. const options = {
  539. duration: 60000,
  540. sampleRate: 16000,
  541. numberOfChannels: 1,
  542. encodeBitRate: 48000,
  543. format: 'mp3',
  544. frameSize: 50
  545. }
  546. this.recorderManager.start(options); //开始录音
  547. this.recorderManager.onStart(() => {
  548. console.log('recorder start')
  549. })
  550. this.recorderManager.onError((res) => {
  551. console.log(res);
  552. })
  553. uni.showToast({
  554. title: "正在录音,上划取消发送",
  555. icon: "none",
  556. duration: 60000 //先定义个60秒,后面可以手动调用wx.hideToast()隐藏
  557. });
  558. this.sendLock = false; //长按时是不上锁的。
  559. },
  560. //取消
  561. cancelButton(e) {
  562. console.log("移动")
  563. let self = this;
  564. this.liveType = false;
  565. console.log('取消', e)
  566. let moveLenght = e.touches[e.touches.length - 1].clientY - this.startPoint.clientY; //移动距离
  567. if (Math.abs(moveLenght) > 50) {
  568. uni.showToast({
  569. title: "松开手指,取消发送",
  570. icon: "none",
  571. duration: 60000
  572. });
  573. this.sendLock = true; //触发了上滑取消发送,上锁
  574. } else {
  575. uni.showToast({
  576. title: "正在录音,上划取消发送",
  577. icon: "none",
  578. duration: 60000
  579. });
  580. this.sendLock = false; //上划距离不足,依然可以发送,不上锁
  581. }
  582. },
  583. //发送
  584. sendButton(item, e) {
  585. console.log("松开")
  586. let self = this;
  587. this.liveType = false;
  588. console.log('发送', e)
  589. uni.hideToast(); //结束录音、隐藏Toast提示框
  590. this.recorderManager.stop(); //结束录音
  591. this.recorderManager.onStop((res) => {
  592. if (!this.sendLock) {
  593. this.uploadVoice(item, res.duration, res.tempFilePath);
  594. }
  595. console.log('停止录音', res.tempFilePath)
  596. console.log("sendLock", this.sendLock);
  597. })
  598. },
  599. //上传MP3
  600. async uploadVoice(item, times, tempFilePaths) {
  601. var self = this;
  602. uni.uploadFile({
  603. url: config.base_url + '/system/file/upload', //仅为示例,非真实的接口地址
  604. header: {
  605. 'Authorization': uni.getStorageSync('token')
  606. },
  607. filePath: tempFilePaths,
  608. name: 'file',
  609. formData: {
  610. 'user': 'test'
  611. },
  612. success: (uploadFileRes) => {
  613. let res = JSON.parse(uploadFileRes.data);
  614. if (res.code == 200) {
  615. console.log("上传成功", res)
  616. console.log(item)
  617. let seconds = Math.round((parseInt(times) % (1000 * 60)) / 1000);
  618. this.form.voiceList.push({
  619. 'fileLength': seconds,
  620. 'fileUrl': res.data.url,
  621. 'fileName': res.data.name
  622. })
  623. console.log(JSON.stringify(self.voice))
  624. //self.textParseUrlIps(res.data.url);
  625. // self.newData.imgList.push(res.data.url);
  626. } else {
  627. uni.showToast({
  628. title: res.msg,
  629. icon: "none",
  630. mask: true,
  631. duration: 2000
  632. });
  633. }
  634. },
  635. fail: err => {
  636. uni.hideLoading()
  637. },
  638. complete: () => {}
  639. });
  640. },// #ifdef WEB
  641. /* H5 */
  642. async initRecorder() {
  643. try {
  644. // 获取麦克风权限
  645. await navigator.mediaDevices.getUserMedia({ audio: true });
  646. this.recorder = new Recorder({
  647. type: "mp3", // 输出格式
  648. bitRate: 64, // 降低比特率提升兼容性
  649. // 不指定 sampleRate,让浏览器使用系统默认值(iOS 兼容性要求)
  650. });
  651. this.recorder.open(() => {
  652. // 初始化成功,继续 startRecord 流程
  653. this.startRecord();
  654. }, (error) => {
  655. console.error("录音器初始化失败:", error);
  656. uni.showToast({
  657. title: '麦克风权限获取失败,请允许麦克风权限',
  658. icon: "none",
  659. mask: true,
  660. duration: 2000
  661. });
  662. });
  663. } catch (err) {
  664. console.error("获取麦克风失败:", err);
  665. uni.showToast({
  666. title: '请允许麦克风权限',
  667. icon: "none",
  668. mask: true,
  669. duration: 2000
  670. });
  671. }
  672. },
  673. // 开始录音
  674. async startRecord() {
  675. let self = this;
  676. if (this.form.voiceList.length >= 5) {
  677. uni.showToast({
  678. title: '语音备忘最多只能上传5条',
  679. icon: "none",
  680. mask: true,
  681. duration: 2000
  682. });
  683. return
  684. }
  685. if (!this.recorder) {
  686. await this.initRecorder();
  687. // this.startRecord()
  688. return;
  689. }
  690. this.recorder.start();
  691. this.recording = true;
  692. this.liveType = true;
  693. // uni.showToast({ title: "录音开始", icon: "none" });
  694. uni.showToast({
  695. title: "正在录音,上划取消发送",
  696. icon: "none",
  697. duration: 60000 //先定义个60秒,后面可以手动调用wx.hideToast()隐藏
  698. });
  699. },
  700. // 停止录音
  701. stopRecord() {
  702. if (!this.recorder || !this.recording) return;
  703. this.recorder.stop((blob, duration) => {
  704. this.recording = false;
  705. this.liveType = false;
  706. this.audioBlob = blob;
  707. // 生成临时文件路径
  708. this.audioPath = URL.createObjectURL(blob);
  709. this.uploadAudio(duration);
  710. }, (error) => {
  711. console.error("录音失败:", error);
  712. uni.showToast({ title: "录音失败", icon: "none" });
  713. });
  714. },
  715. delRecord(){
  716. this.recorder.stop((blob, duration) => {
  717. this.recording = false;
  718. this.liveType = false;
  719. this.audioBlob = null;
  720. this.audioPath = null;
  721. })
  722. },
  723. // 上传录音文件
  724. uploadAudio(times) {
  725. let self = this;
  726. if (!this.audioBlob) return;
  727. // 1. 通过fetch获取Blob数据
  728. fetch(this.audioPath)
  729. .then(response => response.blob())
  730. .then(blob => {
  731. // 2. 将Blob转换为File对象
  732. const file = new File([blob], 'audio.mp3', { type: 'audio/mp3' });
  733. // console.log('file',file);
  734. // 3. 使用uni.uploadFile上传
  735. uni.uploadFile({
  736. url: config.base_url + '/system/file/upload',
  737. header: {
  738. 'Authorization': uni.getStorageSync('token')
  739. },
  740. file: file,
  741. name: 'file',
  742. formData: {
  743. 'user': 'test'
  744. },
  745. success: (uploadFileRes) => {
  746. let res = JSON.parse(uploadFileRes.data);
  747. if (res.code == 200) {
  748. console.log("上传成功", res)
  749. let seconds = Math.round((parseInt(times) % (1000 * 60)) / 1000);
  750. this.form.voiceList.push({
  751. 'fileLength': seconds,
  752. 'fileUrl': res.data.url,
  753. 'fileName': res.data.name
  754. })
  755. // console.log(JSON.stringify(self.voice))
  756. //self.textParseUrlIps(res.data.url);
  757. // self.newData.imgList.push(res.data.url);
  758. } else {
  759. uni.showToast({
  760. title: res.msg,
  761. icon: "none",
  762. mask: true,
  763. duration: 2000
  764. });
  765. }
  766. },
  767. fail: (err) => {
  768. console.error('上传失败', err);
  769. uni.showToast({
  770. title: res.msg,
  771. icon: "none",
  772. mask: true,
  773. duration: 2000
  774. });
  775. }
  776. });
  777. })
  778. .catch(error => {
  779. console.error('获取Blob数据失败', error);
  780. });
  781. },
  782. //H5 拖拽
  783. // 触摸开始
  784. handleTouchStart(event) {
  785. // 彻底阻止所有默认行为
  786. event.preventDefault();
  787. event.stopPropagation();
  788. this.touchStartY = event.touches[0].clientY;
  789. this.hasMoved = false;
  790. this.moveDirection = null;
  791. this.hasTriggeredMethod3 = false;
  792. // 设置长按定时器
  793. this.longPressTimer = setTimeout(() => {
  794. this.isLongPress = true;
  795. this.startRecord(); // 执行方法1
  796. }, 300); // 300ms触发长按
  797. },
  798. // 触摸移动
  799. handleTouchMove(event) {
  800. console.log("按下")
  801. let self = this;
  802. // 彻底阻止所有默认行为
  803. event.preventDefault();
  804. event.stopPropagation();
  805. if (!this.isLongPress) return;
  806. const currentY = event.touches[0].clientY;
  807. const deltaY = currentY - this.touchStartY;
  808. // 检测滑动方向(向上滑动为负值)
  809. if (Math.abs(deltaY) > 75) { // 增加阈值避免误触
  810. uni.showToast({
  811. title: "松开手指,取消发送",
  812. icon: "none",
  813. duration: 60000
  814. });
  815. this.hasMoved = true;
  816. this.moveDirection = deltaY < 0 ? 'up' : 'down';
  817. // 向上滑动执行方法3(确保只触发一次)
  818. if (this.moveDirection === 'up' && !this.hasTriggeredMethod3) {
  819. this.hasTriggeredMethod3 = true;
  820. this.delRecord();
  821. }
  822. }
  823. },
  824. // 触摸结束
  825. handleTouchEnd() {
  826. // 清除长按定时器
  827. clearTimeout(this.longPressTimer);
  828. uni.hideToast(); //结束录音、隐藏Toast提示框
  829. if (this.isLongPress) {
  830. this.stopRecord(); // 执行方法2
  831. }
  832. // 重置状态
  833. this.isLongPress = false;
  834. this.hasMoved = false;
  835. this.moveDirection = null;
  836. },
  837. // 关键:禁用上下文菜单(长按菜单)
  838. handleContextMenu(e) {
  839. e.preventDefault();
  840. return false;
  841. },
  842. // #endif
  843. }
  844. }
  845. </script>
  846. <style lang="stylus" scoped>
  847. @import '@/api/request/imagesUrl.styl';
  848. .inspectAdd {
  849. height: 100%;
  850. display flex;
  851. box-sizing: border-box;
  852. padding-bottom: 230rpx;
  853. box-sizing: border-box;
  854. .tip {
  855. width: 750rpx;
  856. height: 80rpx;
  857. background: rgba(1, 131, 250, 0.2);
  858. font-weight: normal;
  859. font-size: 28rpx;
  860. color: #0183FA;
  861. line-height: 80rpx;
  862. text-align: center;
  863. }
  864. .list {
  865. width: 750rpx;
  866. margin: 20rpx 0rpx 0;
  867. border-radius: 20rpx 20rpx 20rpx 20rpx;
  868. overflow: hidden;
  869. .list-li {
  870. display: flex;
  871. justify-content: space-between;
  872. align-items: center;
  873. padding: 0 30rpx;
  874. box-sizing: border-box;
  875. border-bottom: 1rpx solid #E0E0E0;
  876. background: #fff;
  877. >view:nth-of-type(1) {
  878. font-size: 30rpx;
  879. color: #333333;
  880. line-height: 100rpx;
  881. text-align: left;
  882. overflow: hidden;
  883. text-overflow: ellipsis;
  884. white-space: nowrap;
  885. }
  886. >view:nth-of-type(2) {
  887. font-size: 28rpx;
  888. color: #666666;
  889. line-height: 100rpx;
  890. text-align: left;
  891. display: flex;
  892. justify-content: space-between;
  893. align-items: center;
  894. >img {
  895. width: 30rpx;
  896. height: 30rpx;
  897. margin-left: 10rpx;
  898. }
  899. }
  900. }
  901. .hidden-content {
  902. width: 630rpx;
  903. min-height: 200rpx;
  904. background: #F5F5F5;
  905. border-radius: 20rpx 20rpx 20rpx 20rpx;
  906. margin: 20rpx 30rpx 0;
  907. padding: 20rpx;
  908. box-sizing: border-box;
  909. font-size: 28rpx;
  910. color: #666666;
  911. line-height: 39rpx;
  912. text-align: left;
  913. }
  914. .photo {
  915. display: flex;
  916. justify-content: flex-start;
  917. flex-wrap: wrap;
  918. margin-top: 20rpx;
  919. padding: 0 30rpx;
  920. box-sizing: border-box;
  921. >img {
  922. width: 150rpx;
  923. height: 150rpx;
  924. border-radius: 10rpx 10rpx 10rpx 10rpx;
  925. margin: 0 10rpx 10rpx 0;
  926. }
  927. >img:nth-of-type(4) {
  928. margin-right: 0;
  929. }
  930. }
  931. .describe {
  932. width: 750rpx;
  933. background: #fff;
  934. overflow: hidden;
  935. .describe-n {
  936. width: 690rpx;
  937. min-height: 180rpx;
  938. border-radius: 10rpx 10rpx 10rpx 10rpx;
  939. border: 1rpx solid #E0E0E0;
  940. margin: 40rpx;
  941. padding: 22rpx;
  942. box-sizing: border-box;
  943. background: #F5F5F5;
  944. }
  945. }
  946. .check-for-img-max-box {
  947. padding: 20px 28rpx;
  948. box-sizing: border-box;
  949. background: #fff;
  950. border-bottom: 2rpx solid #E0E0E0;
  951. .left-title-p {
  952. width: 100%;
  953. text-align: left;
  954. font-size: 30rpx;
  955. font-family: PingFang SC-Medium, PingFang SC;
  956. font-weight: 400;
  957. color: #333333;
  958. line-height: 80rpx;
  959. }
  960. .right-img-box {
  961. .img-box {
  962. display inline-block;
  963. height: 150rpx;
  964. width: 150rpx;
  965. position relative;
  966. margin: 0 10rpx 20rpx 0;
  967. border-radius 10rpx;
  968. overflow hidden;
  969. .img-data {
  970. height: 150rpx;
  971. width: 150rpx;
  972. }
  973. .position-img {
  974. position absolute;
  975. right: 0;
  976. top: 0;
  977. width: 36rpx;
  978. height: 36rpx;
  979. }
  980. }
  981. .img-box:nth-of-type(4n+4) {
  982. margin-right: 0rpx;
  983. }
  984. .add-button {
  985. margin: 0 0rpx 20rpx 0;
  986. border-radius 10rpx;
  987. overflow hidden;
  988. display inline-block;
  989. height: 150rpx;
  990. width: 150rpx;
  991. }
  992. }
  993. }
  994. .list-li:last-of-type {
  995. border: none;
  996. }
  997. .hazardCheckNum{
  998. border-bottom: 2rpx solid #E0E0E0;
  999. padding: 30rpx 40rpx 34rpx 40rpx;
  1000. box-sizing: border-box;
  1001. background: #fff;
  1002. display: flex;
  1003. justify-content: flex-end;
  1004. align-items: center;
  1005. .hazardCheckNum-l{
  1006. width: 30rpx;
  1007. height: 30rpx;
  1008. margin-right: 18rpx;
  1009. }
  1010. .hazardCheckNum-r{
  1011. font-size: 28rpx;
  1012. color: #FF5900;
  1013. line-height: 40rpx;
  1014. text-align: left;
  1015. }
  1016. }
  1017. }
  1018. .srearch {
  1019. display: flex;
  1020. justify-content: space-between;
  1021. align-items: center;
  1022. .srearch-l {
  1023. font-size: 30rpx;
  1024. color: #333333;
  1025. line-height: 100rpx;
  1026. text-align: left;
  1027. }
  1028. .srearch-r {
  1029. width: 30rpx;
  1030. height: 30rpx;
  1031. margin-right: 0rpx;
  1032. }
  1033. }
  1034. /* 语音备注 */
  1035. .voice {
  1036. width: 750rpx;
  1037. min-height: 100rpx;
  1038. background: #FFFFFF;
  1039. border-radius: 20rpx 20rpx 20rpx 20rpx;
  1040. overflow: hidden;
  1041. .voice_t {
  1042. height: 100rpx;
  1043. display: flex;
  1044. justify-content: space-between;
  1045. align-items: center;
  1046. padding: 0 30rpx;
  1047. box-sizing: border-box;
  1048. border-top: 2rpx solid #E0E0E0;
  1049. .voice_t_t {
  1050. width: 100%;
  1051. display: flex;
  1052. justify-content: space-between;
  1053. >text:nth-of-type(1) {
  1054. font-size: 30rpx;
  1055. color: #333333;
  1056. line-height: 100rpx;
  1057. }
  1058. >text:nth-of-type(2) {
  1059. font-size: 28rpx;
  1060. color: #999999;
  1061. line-height: 100rpx;
  1062. }
  1063. }
  1064. .voice_t_b {
  1065. width: 100%;
  1066. font-size: 30rpx;
  1067. font-family: PingFang SC-Medium, PingFang SC;
  1068. font-weight: 400;
  1069. color: #333333;
  1070. line-height: 100rpx;
  1071. text-align: left;
  1072. }
  1073. }
  1074. .voice_b {
  1075. display flex;
  1076. justify-content flex-start;
  1077. align-items center;
  1078. margin-bottom: 20rpx;
  1079. padding: 0 30rpx;
  1080. box-sizing: border-box;
  1081. .voice_b_l {
  1082. width: 400rpx;
  1083. height: 40rpx;
  1084. background: #0183FA;
  1085. border-radius: 10rpx;
  1086. display flex;
  1087. justify-content flex-start;
  1088. align-items center;
  1089. font-size: 24rpx;
  1090. color: #FFFFFF;
  1091. line-height: 30rpx;
  1092. text-align: left;
  1093. >img {
  1094. width: 20rpx;
  1095. height: 20rpx;
  1096. margin-left: 16rpx;
  1097. margin-right: 20rpx;
  1098. }
  1099. }
  1100. .voice_b_r {
  1101. width: 28rpx;
  1102. height: 28rpx;
  1103. margin-left: 20rpx;
  1104. }
  1105. }
  1106. .voice-btn {
  1107. width: 150rpx;
  1108. height: 150rpx;
  1109. position: fixed;
  1110. right: 24rpx;
  1111. bottom: 176rpx;
  1112. >img {
  1113. width: 150rpx;
  1114. height: 150rpx;
  1115. position: absolute;
  1116. }
  1117. >text {
  1118. font-size: 24rpx;
  1119. color: #999999;
  1120. line-height: 30rpx;
  1121. text-align: center;
  1122. position: absolute;
  1123. top: 84rpx;
  1124. left: 26rpx;
  1125. }
  1126. }
  1127. }
  1128. }
  1129. .sub-btn {
  1130. display: flex;
  1131. justify-content: space-between;
  1132. position: fixed;
  1133. bottom: 30rpx;
  1134. width: 750rpx;
  1135. padding: 0 46rpx;
  1136. box-sizing: border-box;
  1137. >img {
  1138. width: 160rpx;
  1139. height: 160rpx;
  1140. }
  1141. .broadcast_m{
  1142. width: 160rpx;
  1143. height: 160rpx;
  1144. background-size:160rpx 160rpx !important;
  1145. background: url($imagesUrl+'safetyCheck/icon_aqjc_luyin.png');
  1146. }
  1147. }
  1148. .inspectRecord{
  1149. position: fixed;
  1150. top: 118rpx;
  1151. right: 0;
  1152. z-index: 200;
  1153. width: 160rpx;
  1154. height: 60rpx;
  1155. background: #0183FA;
  1156. border-radius: 50rpx 0rpx 0rpx 50rpx;
  1157. font-size: 30rpx;
  1158. color: #FFFFFF;
  1159. line-height: 60rpx;
  1160. text-align: center;
  1161. }
  1162. </style>