conductInspections.vue 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230
  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. async voicePlay(item) {
  482. const innerAudioContext = uni.createInnerAudioContext();
  483. // innerAudioContext.autoplay = true;
  484. innerAudioContext.src = uni.getStorageSync('fileBrowseEnvironmentS') + '/' + item;
  485. innerAudioContext.onPlay(() => {
  486. // console.log('开始播放');
  487. uni.showToast({
  488. title: '开始播放',
  489. icon: "none",
  490. mask: true,
  491. });
  492. });
  493. innerAudioContext.onError((res) => {
  494. // console.log('播放失败');
  495. uni.showToast({
  496. title: '播放失败',
  497. icon: "none",
  498. mask: true,
  499. });
  500. });
  501. },
  502. //语音备忘删除
  503. async voiceDele(item, voiceIndex) {
  504. let self = this;
  505. uni.showModal({
  506. title: '',
  507. cancelColor: '#999999',
  508. confirmColor: '#FF6E6E',
  509. content: '确定删除此语音吗?',
  510. success(res) {
  511. if (res.confirm) {
  512. console.log('用户点击确定')
  513. self.form.voiceList.splice(voiceIndex, 1);
  514. self.$forceUpdate();
  515. } else if (res.cancel) {
  516. console.log('用户点击取消')
  517. }
  518. }
  519. })
  520. },
  521. //录制
  522. recordButton(item, e) {
  523. console.log("按下")
  524. let self = this;
  525. if (this.form.voiceList.length >= 5) {
  526. uni.showToast({
  527. title: '语音备忘最多只能上传5条',
  528. icon: "none",
  529. mask: true,
  530. duration: 2000
  531. });
  532. return
  533. }
  534. this.liveType = true;
  535. console.log('录制', e)
  536. this.startPoint = e.touches[0]; //记录长按时开始点信息,后面用于计算上划取消时手指滑动的距离。
  537. const options = {
  538. duration: 60000,
  539. sampleRate: 16000,
  540. numberOfChannels: 1,
  541. encodeBitRate: 48000,
  542. format: 'mp3',
  543. frameSize: 50
  544. }
  545. this.recorderManager.start(options); //开始录音
  546. this.recorderManager.onStart(() => {
  547. console.log('recorder start')
  548. })
  549. this.recorderManager.onError((res) => {
  550. console.log(res);
  551. })
  552. uni.showToast({
  553. title: "正在录音,上划取消发送",
  554. icon: "none",
  555. duration: 60000 //先定义个60秒,后面可以手动调用wx.hideToast()隐藏
  556. });
  557. this.sendLock = false; //长按时是不上锁的。
  558. },
  559. //取消
  560. cancelButton(e) {
  561. console.log("移动")
  562. let self = this;
  563. this.liveType = false;
  564. console.log('取消', e)
  565. let moveLenght = e.touches[e.touches.length - 1].clientY - this.startPoint.clientY; //移动距离
  566. if (Math.abs(moveLenght) > 50) {
  567. uni.showToast({
  568. title: "松开手指,取消发送",
  569. icon: "none",
  570. duration: 60000
  571. });
  572. this.sendLock = true; //触发了上滑取消发送,上锁
  573. } else {
  574. uni.showToast({
  575. title: "正在录音,上划取消发送",
  576. icon: "none",
  577. duration: 60000
  578. });
  579. this.sendLock = false; //上划距离不足,依然可以发送,不上锁
  580. }
  581. },
  582. //发送
  583. sendButton(item, e) {
  584. console.log("松开")
  585. let self = this;
  586. this.liveType = false;
  587. console.log('发送', e)
  588. uni.hideToast(); //结束录音、隐藏Toast提示框
  589. this.recorderManager.stop(); //结束录音
  590. this.recorderManager.onStop((res) => {
  591. if (!this.sendLock) {
  592. this.uploadVoice(item, res.duration, res.tempFilePath);
  593. }
  594. console.log('停止录音', res.tempFilePath)
  595. console.log("sendLock", this.sendLock);
  596. })
  597. },
  598. //上传MP3
  599. async uploadVoice(item, times, tempFilePaths) {
  600. var self = this;
  601. uni.uploadFile({
  602. url: config.base_url + '/system/file/upload', //仅为示例,非真实的接口地址
  603. header: {
  604. 'Authorization': uni.getStorageSync('token')
  605. },
  606. filePath: tempFilePaths,
  607. name: 'file',
  608. formData: {
  609. 'user': 'test'
  610. },
  611. success: (uploadFileRes) => {
  612. let res = JSON.parse(uploadFileRes.data);
  613. if (res.code == 200) {
  614. console.log("上传成功", res)
  615. console.log(item)
  616. let seconds = Math.round((parseInt(times) % (1000 * 60)) / 1000);
  617. this.form.voiceList.push({
  618. 'fileLength': seconds,
  619. 'fileUrl': res.data.url,
  620. 'fileName': res.data.name
  621. })
  622. console.log(JSON.stringify(self.voice))
  623. //self.textParseUrlIps(res.data.url);
  624. // self.newData.imgList.push(res.data.url);
  625. } else {
  626. uni.showToast({
  627. title: res.msg,
  628. icon: "none",
  629. mask: true,
  630. duration: 2000
  631. });
  632. }
  633. },
  634. fail: err => {
  635. uni.hideLoading()
  636. },
  637. complete: () => {}
  638. });
  639. },// #ifdef WEB
  640. /* H5 */
  641. async initRecorder() {
  642. try {
  643. // 获取麦克风权限
  644. await navigator.mediaDevices.getUserMedia({ audio: true });
  645. this.recorder = new Recorder({
  646. type: "mp3", // 输出格式
  647. bitRate: 64, // 降低比特率提升兼容性
  648. // 不指定 sampleRate,让浏览器使用系统默认值(iOS 兼容性要求)
  649. });
  650. this.recorder.open(() => {
  651. // 初始化成功,继续 startRecord 流程
  652. this.startRecord();
  653. }, (error) => {
  654. console.error("录音器初始化失败:", error);
  655. uni.showToast({
  656. title: '麦克风权限获取失败,请允许麦克风权限',
  657. icon: "none",
  658. mask: true,
  659. duration: 2000
  660. });
  661. });
  662. } catch (err) {
  663. console.error("获取麦克风失败:", err);
  664. uni.showToast({
  665. title: '请允许麦克风权限',
  666. icon: "none",
  667. mask: true,
  668. duration: 2000
  669. });
  670. }
  671. },
  672. // 开始录音
  673. async startRecord() {
  674. let self = this;
  675. if (this.form.voiceList.length >= 5) {
  676. uni.showToast({
  677. title: '语音备忘最多只能上传5条',
  678. icon: "none",
  679. mask: true,
  680. duration: 2000
  681. });
  682. return
  683. }
  684. if (!this.recorder) {
  685. await this.initRecorder();
  686. // this.startRecord()
  687. return;
  688. }
  689. this.recorder.start();
  690. this.recording = true;
  691. this.liveType = true;
  692. // uni.showToast({ title: "录音开始", icon: "none" });
  693. uni.showToast({
  694. title: "正在录音,上划取消发送",
  695. icon: "none",
  696. duration: 60000 //先定义个60秒,后面可以手动调用wx.hideToast()隐藏
  697. });
  698. },
  699. // 停止录音
  700. stopRecord() {
  701. if (!this.recorder || !this.recording) return;
  702. this.recorder.stop((blob, duration) => {
  703. this.recording = false;
  704. this.liveType = false;
  705. this.audioBlob = blob;
  706. // 生成临时文件路径
  707. this.audioPath = URL.createObjectURL(blob);
  708. this.uploadAudio(duration);
  709. }, (error) => {
  710. console.error("录音失败:", error);
  711. uni.showToast({ title: "录音失败", icon: "none" });
  712. });
  713. },
  714. delRecord(){
  715. this.recorder.stop((blob, duration) => {
  716. this.recording = false;
  717. this.liveType = false;
  718. this.audioBlob = null;
  719. this.audioPath = null;
  720. })
  721. },
  722. // 上传录音文件
  723. uploadAudio(times) {
  724. let self = this;
  725. if (!this.audioBlob) return;
  726. // 1. 通过fetch获取Blob数据
  727. fetch(this.audioPath)
  728. .then(response => response.blob())
  729. .then(blob => {
  730. // 2. 将Blob转换为File对象
  731. const file = new File([blob], 'audio.mp3', { type: 'audio/mp3' });
  732. // console.log('file',file);
  733. // 3. 使用uni.uploadFile上传
  734. uni.uploadFile({
  735. url: config.base_url + '/system/file/upload',
  736. header: {
  737. 'Authorization': uni.getStorageSync('token')
  738. },
  739. file: file,
  740. name: 'file',
  741. formData: {
  742. 'user': 'test'
  743. },
  744. success: (uploadFileRes) => {
  745. let res = JSON.parse(uploadFileRes.data);
  746. if (res.code == 200) {
  747. console.log("上传成功", res)
  748. let seconds = Math.round((parseInt(times) % (1000 * 60)) / 1000);
  749. this.form.voiceList.push({
  750. 'fileLength': seconds,
  751. 'fileUrl': res.data.url,
  752. 'fileName': res.data.name
  753. })
  754. // console.log(JSON.stringify(self.voice))
  755. //self.textParseUrlIps(res.data.url);
  756. // self.newData.imgList.push(res.data.url);
  757. } else {
  758. uni.showToast({
  759. title: res.msg,
  760. icon: "none",
  761. mask: true,
  762. duration: 2000
  763. });
  764. }
  765. },
  766. fail: (err) => {
  767. console.error('上传失败', err);
  768. uni.showToast({
  769. title: res.msg,
  770. icon: "none",
  771. mask: true,
  772. duration: 2000
  773. });
  774. }
  775. });
  776. })
  777. .catch(error => {
  778. console.error('获取Blob数据失败', error);
  779. });
  780. },
  781. //H5 拖拽
  782. // 触摸开始
  783. handleTouchStart(event) {
  784. // 彻底阻止所有默认行为
  785. event.preventDefault();
  786. event.stopPropagation();
  787. this.touchStartY = event.touches[0].clientY;
  788. this.hasMoved = false;
  789. this.moveDirection = null;
  790. this.hasTriggeredMethod3 = false;
  791. // 设置长按定时器
  792. this.longPressTimer = setTimeout(() => {
  793. this.isLongPress = true;
  794. this.startRecord(); // 执行方法1
  795. }, 300); // 300ms触发长按
  796. },
  797. // 触摸移动
  798. handleTouchMove(event) {
  799. console.log("按下")
  800. let self = this;
  801. // 彻底阻止所有默认行为
  802. event.preventDefault();
  803. event.stopPropagation();
  804. if (!this.isLongPress) return;
  805. const currentY = event.touches[0].clientY;
  806. const deltaY = currentY - this.touchStartY;
  807. // 检测滑动方向(向上滑动为负值)
  808. if (Math.abs(deltaY) > 75) { // 增加阈值避免误触
  809. uni.showToast({
  810. title: "松开手指,取消发送",
  811. icon: "none",
  812. duration: 60000
  813. });
  814. this.hasMoved = true;
  815. this.moveDirection = deltaY < 0 ? 'up' : 'down';
  816. // 向上滑动执行方法3(确保只触发一次)
  817. if (this.moveDirection === 'up' && !this.hasTriggeredMethod3) {
  818. this.hasTriggeredMethod3 = true;
  819. this.delRecord();
  820. }
  821. }
  822. },
  823. // 触摸结束
  824. handleTouchEnd() {
  825. // 清除长按定时器
  826. clearTimeout(this.longPressTimer);
  827. uni.hideToast(); //结束录音、隐藏Toast提示框
  828. if (this.isLongPress) {
  829. this.stopRecord(); // 执行方法2
  830. }
  831. // 重置状态
  832. this.isLongPress = false;
  833. this.hasMoved = false;
  834. this.moveDirection = null;
  835. },
  836. // 关键:禁用上下文菜单(长按菜单)
  837. handleContextMenu(e) {
  838. e.preventDefault();
  839. return false;
  840. },
  841. // #endif
  842. }
  843. }
  844. </script>
  845. <style lang="stylus" scoped>
  846. @import '@/api/request/imagesUrl.styl';
  847. .inspectAdd {
  848. height: 100%;
  849. display flex;
  850. box-sizing: border-box;
  851. padding-bottom: 230rpx;
  852. box-sizing: border-box;
  853. .tip {
  854. width: 750rpx;
  855. height: 80rpx;
  856. background: rgba(1, 131, 250, 0.2);
  857. font-weight: normal;
  858. font-size: 28rpx;
  859. color: #0183FA;
  860. line-height: 80rpx;
  861. text-align: center;
  862. }
  863. .list {
  864. width: 750rpx;
  865. margin: 20rpx 0rpx 0;
  866. border-radius: 20rpx 20rpx 20rpx 20rpx;
  867. overflow: hidden;
  868. .list-li {
  869. display: flex;
  870. justify-content: space-between;
  871. align-items: center;
  872. padding: 0 30rpx;
  873. box-sizing: border-box;
  874. border-bottom: 1rpx solid #E0E0E0;
  875. background: #fff;
  876. >view:nth-of-type(1) {
  877. font-size: 30rpx;
  878. color: #333333;
  879. line-height: 100rpx;
  880. text-align: left;
  881. overflow: hidden;
  882. text-overflow: ellipsis;
  883. white-space: nowrap;
  884. }
  885. >view:nth-of-type(2) {
  886. font-size: 28rpx;
  887. color: #666666;
  888. line-height: 100rpx;
  889. text-align: left;
  890. display: flex;
  891. justify-content: space-between;
  892. align-items: center;
  893. >img {
  894. width: 30rpx;
  895. height: 30rpx;
  896. margin-left: 10rpx;
  897. }
  898. }
  899. }
  900. .hidden-content {
  901. width: 630rpx;
  902. min-height: 200rpx;
  903. background: #F5F5F5;
  904. border-radius: 20rpx 20rpx 20rpx 20rpx;
  905. margin: 20rpx 30rpx 0;
  906. padding: 20rpx;
  907. box-sizing: border-box;
  908. font-size: 28rpx;
  909. color: #666666;
  910. line-height: 39rpx;
  911. text-align: left;
  912. }
  913. .photo {
  914. display: flex;
  915. justify-content: flex-start;
  916. flex-wrap: wrap;
  917. margin-top: 20rpx;
  918. padding: 0 30rpx;
  919. box-sizing: border-box;
  920. >img {
  921. width: 150rpx;
  922. height: 150rpx;
  923. border-radius: 10rpx 10rpx 10rpx 10rpx;
  924. margin: 0 10rpx 10rpx 0;
  925. }
  926. >img:nth-of-type(4) {
  927. margin-right: 0;
  928. }
  929. }
  930. .describe {
  931. width: 750rpx;
  932. background: #fff;
  933. overflow: hidden;
  934. .describe-n {
  935. width: 690rpx;
  936. min-height: 180rpx;
  937. border-radius: 10rpx 10rpx 10rpx 10rpx;
  938. border: 1rpx solid #E0E0E0;
  939. margin: 40rpx;
  940. padding: 22rpx;
  941. box-sizing: border-box;
  942. background: #F5F5F5;
  943. }
  944. }
  945. .check-for-img-max-box {
  946. padding: 20px 28rpx;
  947. box-sizing: border-box;
  948. background: #fff;
  949. border-bottom: 2rpx solid #E0E0E0;
  950. .left-title-p {
  951. width: 100%;
  952. text-align: left;
  953. font-size: 30rpx;
  954. font-family: PingFang SC-Medium, PingFang SC;
  955. font-weight: 400;
  956. color: #333333;
  957. line-height: 80rpx;
  958. }
  959. .right-img-box {
  960. .img-box {
  961. display inline-block;
  962. height: 150rpx;
  963. width: 150rpx;
  964. position relative;
  965. margin: 0 10rpx 20rpx 0;
  966. border-radius 10rpx;
  967. overflow hidden;
  968. .img-data {
  969. height: 150rpx;
  970. width: 150rpx;
  971. }
  972. .position-img {
  973. position absolute;
  974. right: 0;
  975. top: 0;
  976. width: 36rpx;
  977. height: 36rpx;
  978. }
  979. }
  980. .img-box:nth-of-type(4n+4) {
  981. margin-right: 0rpx;
  982. }
  983. .add-button {
  984. margin: 0 0rpx 20rpx 0;
  985. border-radius 10rpx;
  986. overflow hidden;
  987. display inline-block;
  988. height: 150rpx;
  989. width: 150rpx;
  990. }
  991. }
  992. }
  993. .list-li:last-of-type {
  994. border: none;
  995. }
  996. .hazardCheckNum{
  997. border-bottom: 2rpx solid #E0E0E0;
  998. padding: 30rpx 40rpx 34rpx 40rpx;
  999. box-sizing: border-box;
  1000. background: #fff;
  1001. display: flex;
  1002. justify-content: flex-end;
  1003. align-items: center;
  1004. .hazardCheckNum-l{
  1005. width: 30rpx;
  1006. height: 30rpx;
  1007. margin-right: 18rpx;
  1008. }
  1009. .hazardCheckNum-r{
  1010. font-size: 28rpx;
  1011. color: #FF5900;
  1012. line-height: 40rpx;
  1013. text-align: left;
  1014. }
  1015. }
  1016. }
  1017. .srearch {
  1018. display: flex;
  1019. justify-content: space-between;
  1020. align-items: center;
  1021. .srearch-l {
  1022. font-size: 30rpx;
  1023. color: #333333;
  1024. line-height: 100rpx;
  1025. text-align: left;
  1026. }
  1027. .srearch-r {
  1028. width: 30rpx;
  1029. height: 30rpx;
  1030. margin-right: 0rpx;
  1031. }
  1032. }
  1033. /* 语音备注 */
  1034. .voice {
  1035. width: 750rpx;
  1036. min-height: 100rpx;
  1037. background: #FFFFFF;
  1038. border-radius: 20rpx 20rpx 20rpx 20rpx;
  1039. overflow: hidden;
  1040. .voice_t {
  1041. height: 100rpx;
  1042. display: flex;
  1043. justify-content: space-between;
  1044. align-items: center;
  1045. padding: 0 30rpx;
  1046. box-sizing: border-box;
  1047. border-top: 2rpx solid #E0E0E0;
  1048. .voice_t_t {
  1049. width: 100%;
  1050. display: flex;
  1051. justify-content: space-between;
  1052. >text:nth-of-type(1) {
  1053. font-size: 30rpx;
  1054. color: #333333;
  1055. line-height: 100rpx;
  1056. }
  1057. >text:nth-of-type(2) {
  1058. font-size: 28rpx;
  1059. color: #999999;
  1060. line-height: 100rpx;
  1061. }
  1062. }
  1063. .voice_t_b {
  1064. width: 100%;
  1065. font-size: 30rpx;
  1066. font-family: PingFang SC-Medium, PingFang SC;
  1067. font-weight: 400;
  1068. color: #333333;
  1069. line-height: 100rpx;
  1070. text-align: left;
  1071. }
  1072. }
  1073. .voice_b {
  1074. display flex;
  1075. justify-content flex-start;
  1076. align-items center;
  1077. margin-bottom: 20rpx;
  1078. padding: 0 30rpx;
  1079. box-sizing: border-box;
  1080. .voice_b_l {
  1081. width: 400rpx;
  1082. height: 40rpx;
  1083. background: #0183FA;
  1084. border-radius: 10rpx;
  1085. display flex;
  1086. justify-content flex-start;
  1087. align-items center;
  1088. font-size: 24rpx;
  1089. color: #FFFFFF;
  1090. line-height: 30rpx;
  1091. text-align: left;
  1092. >img {
  1093. width: 20rpx;
  1094. height: 20rpx;
  1095. margin-left: 16rpx;
  1096. margin-right: 20rpx;
  1097. }
  1098. }
  1099. .voice_b_r {
  1100. width: 28rpx;
  1101. height: 28rpx;
  1102. margin-left: 20rpx;
  1103. }
  1104. }
  1105. .voice-btn {
  1106. width: 150rpx;
  1107. height: 150rpx;
  1108. position: fixed;
  1109. right: 24rpx;
  1110. bottom: 176rpx;
  1111. >img {
  1112. width: 150rpx;
  1113. height: 150rpx;
  1114. position: absolute;
  1115. }
  1116. >text {
  1117. font-size: 24rpx;
  1118. color: #999999;
  1119. line-height: 30rpx;
  1120. text-align: center;
  1121. position: absolute;
  1122. top: 84rpx;
  1123. left: 26rpx;
  1124. }
  1125. }
  1126. }
  1127. }
  1128. .sub-btn {
  1129. display: flex;
  1130. justify-content: space-between;
  1131. position: fixed;
  1132. bottom: 30rpx;
  1133. width: 750rpx;
  1134. padding: 0 46rpx;
  1135. box-sizing: border-box;
  1136. >img {
  1137. width: 160rpx;
  1138. height: 160rpx;
  1139. }
  1140. .broadcast_m{
  1141. width: 160rpx;
  1142. height: 160rpx;
  1143. background-size:160rpx 160rpx !important;
  1144. background: url($imagesUrl+'safetyCheck/icon_aqjc_luyin.png');
  1145. }
  1146. }
  1147. .inspectRecord{
  1148. position: fixed;
  1149. top: 118rpx;
  1150. right: 0;
  1151. z-index: 200;
  1152. width: 160rpx;
  1153. height: 60rpx;
  1154. background: #0183FA;
  1155. border-radius: 50rpx 0rpx 0rpx 50rpx;
  1156. font-size: 30rpx;
  1157. color: #FFFFFF;
  1158. line-height: 60rpx;
  1159. text-align: center;
  1160. }
  1161. </style>