repairRecord.vue 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633
  1. <template>
  2. <view class="container">
  3. <view class="title">维修日期</view>
  4. <uni-datetime-picker disabled placeholder="请选择日期" type="date" v-model="repairDate" />
  5. <view class="title">维修内容</view>
  6. <view class="textarea">
  7. <textarea placeholder-style="color:#CCCCCC" placeholder="请输入维修内容" v-model="repairContent"></textarea>
  8. </view>
  9. <view class="title">维修结果</view>
  10. <!-- 录音区域 -->
  11. <view class="voice">
  12. <view class="voice_box" v-if="!recordingPath" @click="handleRecording">
  13. <img src="../../static/images/repairsImg/voice.png" />
  14. </view>
  15. <view v-if="!recordingPath" @click="handleRecording">点击录音</view>
  16. <view class="item_recording" v-if="recordingPath" @click="handlePlayRecording">
  17. <img :src="recordingImg" />
  18. {{ recordingTime }}″
  19. </view>
  20. <view class="recording_icon" v-if="recordingPath" @click="handleDeleteRecording">×</view>
  21. </view>
  22. <!-- 录音弹窗区域 -->
  23. <uni-popup :safe-area="true" background-color="#fff" ref="popup_recording">
  24. <view class="popup_recording">
  25. <recording @getTempFilePath="getTempFilePath" />
  26. </view>
  27. </uni-popup>
  28. <view class="title">关联耗材</view>
  29. <!-- 每一个耗材盒子区域 -->
  30. <view class="detail_box" v-for="(item, index) in goodList" :key="index">
  31. <view class="detail_box_item">
  32. <view class="item_key">耗材名称</view>
  33. <view class="item_value">{{ item.consumeName }}</view>
  34. </view>
  35. <view class="detail_box_item">
  36. <view class="item_key">耗材数量</view>
  37. <view class="item_value">
  38. <uni-number-box v-model="item.number" :min="0" @change="bindChange($event, index)"></uni-number-box>
  39. </view>
  40. </view>
  41. <view class="detail_box_item">
  42. <view class="item_key">耗材单价</view>
  43. <view class="item_value">
  44. <input type="number" placeholder="请输入耗材单价" v-model="item.price" @blur="handleChangePrice($event, item)" />
  45. </view>
  46. </view>
  47. </view>
  48. <!-- 添加耗材区域 -->
  49. <view class="add" @click="handleAdd">
  50. <text>+</text>
  51. 添加耗材
  52. </view>
  53. <!-- 合计费用区域 -->
  54. <view class="total">
  55. <view>合计费用</view>
  56. <view>{{ countMoney }}元</view>
  57. </view>
  58. <view class="title">维修师傅</view>
  59. <view class="box">
  60. <img src="../../static/images/repairsImg/people.png" />
  61. {{ info.maintenancerName }}
  62. </view>
  63. <view class="title">手机</view>
  64. <view class="box">
  65. <img src="../../static/images/repairsImg/phone2.png" />
  66. {{ info.maintenancerPhone }}
  67. </view>
  68. <view class="title">现场拍照</view>
  69. <uni-file-picker
  70. ref="filePicker"
  71. limit="3"
  72. v-model="imgList"
  73. fileMediatype="image"
  74. :image-styles="imageStyles"
  75. mode="grid"
  76. @select="select"
  77. @delete="handleDelete"
  78. ></uni-file-picker>
  79. <view class="btn" @click="handleSub">确认提交</view>
  80. <!-- 用于图片压缩的canvas画布 -->
  81. <canvas
  82. :style="{
  83. width: cw + 'px',
  84. height: cw + 'px',
  85. position: 'absolute',
  86. zIndex: -1,
  87. left: '-10000rpx',
  88. top: '-10000rpx'
  89. }"
  90. canvas-id="zipCanvas"
  91. ></canvas>
  92. <!--画布结束-->
  93. </view>
  94. </template>
  95. <script>
  96. import recording from '../components/recording.vue'
  97. // 图片压缩方法
  98. import getLessLimitSizeImage from '../util/imageCompress.js'
  99. const innerAudioContext = uni.createInnerAudioContext()
  100. export default {
  101. components: {
  102. recording
  103. },
  104. data() {
  105. return {
  106. // 维修日期
  107. repairDate: '',
  108. // 维修内容
  109. repairContent: '',
  110. // 耗材列表
  111. goodList: [],
  112. // 显示的图片数据
  113. imgList: [],
  114. // 上传的图片数据
  115. subImgList: [],
  116. // 图片上传框的样式
  117. imageStyles: {
  118. width: 60,
  119. height: 60,
  120. border: {
  121. color: '#ccc',
  122. width: 1,
  123. style: 'dashed',
  124. radius: '9px'
  125. }
  126. },
  127. //画板边长默认是屏幕宽度,正方形画布
  128. cw: uni.getSystemInfoSync().windowWidth,
  129. // 录音文件路径
  130. recordingPath: '',
  131. // 录音图片地址
  132. recordingImg: '../../static/images/repairsImg/recording.jpg',
  133. // 录音时长
  134. recordingTime: 0,
  135. // 播放状态
  136. playStatus: false,
  137. // 定时器标识
  138. timer: null,
  139. // 订单详情信息
  140. info: {}
  141. }
  142. },
  143. computed: {
  144. // 合计费用
  145. countMoney() {
  146. let countMoney = 0
  147. this.goodList.forEach((ele) => {
  148. countMoney += Number(ele.number) * Number(ele.price)
  149. })
  150. if (countMoney) {
  151. return countMoney
  152. } else {
  153. return 0
  154. }
  155. }
  156. },
  157. onLoad(options) {
  158. this.info = JSON.parse(options.info)
  159. this.getTodayTime()
  160. },
  161. mounted() {
  162. //在ios下静音时播放没有声音,默认为true,改为false就好了。
  163. uni.setInnerAudioOption({
  164. obeyMuteSwitch: false
  165. })
  166. uni.$on('addConsumable', this.addConsumable)
  167. },
  168. methods: {
  169. // 获取今天日期 YY-MM-DD
  170. getTodayTime() {
  171. let today = new Date()
  172. this.repairDate = `${today.getFullYear()}-${(today.getMonth() + 1).toString().padStart(2, 0)}-${today.getDate().toString().padStart(2, 0)}`
  173. },
  174. // 单价输入框输入回调
  175. handleChangePrice(e, item) {
  176. // 验证输入单价格式 只能是数字,小数点最多两位
  177. const reg = /(^[1-9]\d*(\.\d{1,2})?$)|(^0(\.\d{1,2})?$)/
  178. if (!reg.test(e.detail.value)) {
  179. uni.showToast({
  180. title: '请确定单价为数字,并且只能保留两位小数点',
  181. icon: 'none'
  182. })
  183. item.price = 0
  184. }
  185. },
  186. // 全局自定义事件
  187. addConsumable(e) {
  188. // console.log(e)
  189. // 判断是否存在相同的耗材
  190. let flag = this.goodList.some((ele) => {
  191. return ele.consumeName === e.data.name
  192. })
  193. if (!flag) {
  194. this.$set(e.data, 'number', 1)
  195. this.$set(e.data, 'consumeName', e.data.name)
  196. this.$set(e.data, 'consumeId', e.data.id)
  197. this.$set(e.data, 'articleId', e.articleId)
  198. // this.$delete(e.data, 'name')
  199. // this.$delete(e.data, 'id')
  200. this.goodList.push(e.data)
  201. }
  202. },
  203. // 确认提交按钮回调
  204. handleSub() {
  205. // if (!this.goodList.length) {
  206. // uni.showToast({
  207. // title: '请添加耗材',
  208. // icon: 'none'
  209. // })
  210. // return
  211. // }
  212. // if (this.countMoney <= 0) {
  213. // uni.showToast({
  214. // title: '合计费用不能小于0元',
  215. // icon: 'none'
  216. // })
  217. // return
  218. // }
  219. if (this.subImgList.length === 0) {
  220. uni.showToast({
  221. title: '请上传现场照片',
  222. icon: 'none'
  223. })
  224. return
  225. }
  226. uni.showModal({
  227. title: '提示',
  228. content: '确认提交吗?',
  229. success: async (res) => {
  230. if (res.confirm) {
  231. // 维修内容
  232. console.log(this.repairContent)
  233. // 录音文件路径
  234. console.log(this.recordingPath)
  235. // 录音时长
  236. console.log(this.recordingTime)
  237. // 耗材集合
  238. console.log(this.goodList)
  239. // 总费用
  240. console.log(this.countMoney)
  241. // 现场拍照集合
  242. console.log(this.subImgList)
  243. const res = await this.$myRequest_repairs({
  244. url: '/repairRecord/finishOrder',
  245. method: 'post',
  246. data: {
  247. recordId: this.info.id,
  248. images: this.subImgList,
  249. content: this.repairContent,
  250. voice: this.recordingPath,
  251. voiceLength: this.recordingTime,
  252. consumes: this.goodList
  253. }
  254. })
  255. // console.log(res)
  256. if (res.code === '200') {
  257. uni.showToast({
  258. title: '提交成功',
  259. icon: 'success'
  260. })
  261. setTimeout(() => {
  262. uni.reLaunch({
  263. url: '/pagesRepairs/box/box'
  264. })
  265. }, 1500)
  266. }
  267. }
  268. }
  269. })
  270. },
  271. // 耗材数量计数器改变回调
  272. bindChange(e, index) {
  273. // console.log(e)
  274. // console.log(index)
  275. if (e === 0) {
  276. this.goodList.splice(index, 1)
  277. }
  278. },
  279. // 添加耗材按钮回调
  280. handleAdd() {
  281. uni.navigateTo({
  282. url: '/pagesRepairs/addGoods/addGoods'
  283. })
  284. },
  285. // 选择图片回调
  286. select(e) {
  287. // console.log(e)
  288. e.tempFiles.forEach((item) => {
  289. //这里的id和页面中写的html代码的canvas的id要一致
  290. let canvasId = 'zipCanvas'
  291. //原图的路径
  292. let imagePath = item.path
  293. //大小限制1024kb
  294. let limitSize = 1024
  295. //初始绘画区域是画布自身的宽度也就是屏幕宽度
  296. let drawWidth = uni.getSystemInfoSync().windowWidth
  297. getLessLimitSizeImage(canvasId, imagePath, limitSize, drawWidth, (resPath) => {
  298. uni.showLoading({
  299. title: '上传中'
  300. })
  301. uni.uploadFile({
  302. url: `https://chtech.ncjti.edu.cn/campusMaintenance/repair-api/repair/api/repairRecord/uploadFile`,
  303. filePath: resPath,
  304. name: 'file',
  305. header: {
  306. token: uni.getStorageSync('repairsUserInfo').token,
  307. user_head: uni.getStorageSync('repairsUserInfo').userhead
  308. },
  309. success: (uploadFileRes) => {
  310. // console.log(JSON.parse(uploadFileRes.data))
  311. this.subImgList.push(JSON.parse(uploadFileRes.data).data.resultUrl)
  312. this.imgList.push({
  313. url: item.path,
  314. name: ''
  315. })
  316. uni.hideLoading()
  317. },
  318. fail: () => {
  319. uni.showToast({
  320. title: '上传失败',
  321. icon: 'error'
  322. })
  323. }
  324. })
  325. })
  326. })
  327. },
  328. // 删除图片回调
  329. handleDelete(e) {
  330. // console.log(e);
  331. const num = this.imgList.findIndex((v) => v.url === e.tempFilePath)
  332. this.subImgList.splice(num, 1)
  333. this.imgList.splice(num, 1)
  334. },
  335. // 点击录音按钮回调
  336. handleRecording() {
  337. uni.getSetting({
  338. success: (res) => {
  339. if (!res.authSetting['scope.record']) {
  340. uni.authorize({
  341. scope: 'scope.record',
  342. success(res) {
  343. // 授权成功
  344. uni.showToast({
  345. title: '授权成功',
  346. icon: 'none'
  347. })
  348. },
  349. fail() {
  350. uni.showModal({
  351. content: '检测到您没打开麦克风权限,是否去设置打开?',
  352. confirmText: '确认',
  353. cancelText: '取消',
  354. success: (res) => {
  355. if (res.confirm) {
  356. uni.openSetting({
  357. success: (res) => {}
  358. })
  359. } else {
  360. uni.showToast({
  361. title: '获取麦克风权限失败',
  362. icon: 'none'
  363. })
  364. }
  365. }
  366. })
  367. }
  368. })
  369. } else {
  370. this.$refs.popup_recording.open('bottom')
  371. }
  372. },
  373. fail() {
  374. uni.showToast({
  375. title: '获取麦克风权限失败',
  376. icon: 'none'
  377. })
  378. }
  379. })
  380. },
  381. // 点击录音播放回调
  382. handlePlayRecording() {
  383. innerAudioContext.src = this.recordingPath
  384. if (!this.playStatus) {
  385. this.playStatus = true
  386. innerAudioContext.play()
  387. this.timer = setInterval(() => {
  388. if (this.recordingImg == '../../static/images/repairsImg/recording.jpg') {
  389. this.recordingImg = '../../static/images/repairsImg/recording2.jpg'
  390. } else if (this.recordingImg == '../../static/images/repairsImg/recording2.jpg') {
  391. this.recordingImg = '../../static/images/repairsImg/recording3.jpg'
  392. } else if (this.recordingImg == '../../static/images/repairsImg/recording3.jpg') {
  393. this.recordingImg = '../../static/images/repairsImg/recording.jpg'
  394. }
  395. }, 300)
  396. //播放结束
  397. innerAudioContext.onEnded(() => {
  398. clearInterval(this.timer)
  399. this.timer = null
  400. this.recordingImg = '../../static/images/repairsImg/recording.jpg'
  401. this.playStatus = false
  402. })
  403. } else {
  404. clearInterval(this.timer)
  405. this.recordingImg = '../../static/images/repairsImg/recording.jpg'
  406. this.playStatus = false
  407. innerAudioContext.stop()
  408. }
  409. },
  410. // 删除录音回调
  411. handleDeleteRecording() {
  412. if (this.playStatus) {
  413. uni.showToast({
  414. title: '播放中不能删除',
  415. icon: 'none',
  416. mask: true
  417. })
  418. } else {
  419. uni.showModal({
  420. title: '提示',
  421. content: '确定删除录音吗?',
  422. success: (res) => {
  423. if (res.confirm) {
  424. this.recordingPath = ''
  425. this.recordingTime = 0
  426. }
  427. }
  428. })
  429. }
  430. },
  431. // 自定义事件回调,获取录音文件路径
  432. getTempFilePath(path, time) {
  433. this.recordingPath = path
  434. this.recordingTime = time
  435. this.$refs.popup_recording.close()
  436. }
  437. }
  438. }
  439. </script>
  440. <style lang="scss" scoped>
  441. .container {
  442. box-sizing: border-box;
  443. padding: 0 30rpx;
  444. width: 100%;
  445. height: 100vh;
  446. overflow-y: auto;
  447. .title {
  448. display: flex;
  449. align-items: center;
  450. height: 107rpx;
  451. font-size: 36rpx;
  452. font-weight: bold;
  453. }
  454. .textarea {
  455. height: 310rpx;
  456. border-radius: 10rpx;
  457. border: 1rpx solid #cccccc;
  458. textarea {
  459. box-sizing: border-box;
  460. padding: 25rpx 35rpx;
  461. width: 100%;
  462. font-size: 32rpx;
  463. }
  464. }
  465. .voice {
  466. display: flex;
  467. align-items: center;
  468. height: 94rpx;
  469. font-size: 32rpx;
  470. color: #cccccc;
  471. border-radius: 10rpx;
  472. border: 1rpx solid #cccccc;
  473. .voice_box {
  474. display: flex;
  475. justify-content: center;
  476. align-items: center;
  477. margin: 0 38rpx 0 33rpx;
  478. width: 101rpx;
  479. height: 47rpx;
  480. border-radius: 33rpx;
  481. box-shadow: 0px 0px 4rpx rgba(0, 0, 0, 0.25);
  482. img {
  483. width: 33rpx;
  484. height: 33rpx;
  485. }
  486. }
  487. .item_recording {
  488. margin-left: 35rpx;
  489. display: flex;
  490. align-items: center;
  491. width: 230rpx;
  492. height: 65rpx;
  493. color: #000;
  494. border-radius: 100rpx;
  495. border: 1rpx solid #cccccc;
  496. img {
  497. margin: 0 12rpx;
  498. width: 40rpx;
  499. height: 40rpx;
  500. }
  501. }
  502. .recording_icon {
  503. margin-left: auto;
  504. margin-right: 35rpx;
  505. font-size: 40rpx;
  506. }
  507. }
  508. .popup_recording {
  509. width: 100%;
  510. height: 460rpx;
  511. background-color: #fff;
  512. }
  513. .detail_box {
  514. box-sizing: border-box;
  515. margin-bottom: 46rpx;
  516. padding: 5rpx 30rpx 0;
  517. width: 690rpx;
  518. height: 284rpx;
  519. border-radius: 9rpx;
  520. box-shadow: 0px 0px 4px rgba(0, 0, 0, 0.25);
  521. .detail_box_item {
  522. display: flex;
  523. justify-content: space-between;
  524. align-items: center;
  525. height: 92rpx;
  526. font-size: 32rpx;
  527. border-bottom: 1rpx solid #e5e5e5;
  528. .item_key {
  529. width: 150rpx;
  530. color: #808080;
  531. }
  532. .item_value {
  533. font-weight: bold;
  534. input {
  535. text-align: right;
  536. }
  537. }
  538. }
  539. }
  540. .add {
  541. display: flex;
  542. justify-content: center;
  543. align-items: center;
  544. width: 690rpx;
  545. height: 90rpx;
  546. color: #6fb6b8;
  547. font-size: 32rpx;
  548. border-radius: 9rpx;
  549. background-color: #ebf2f2;
  550. text {
  551. margin-right: 10rpx;
  552. font-size: 48rpx;
  553. }
  554. }
  555. .total {
  556. display: flex;
  557. justify-content: space-between;
  558. align-items: center;
  559. margin: auto;
  560. width: 622rpx;
  561. height: 100rpx;
  562. font-size: 32rpx;
  563. font-weight: bold;
  564. border-bottom: 1rpx solid #e5e5e5;
  565. }
  566. .box {
  567. display: flex;
  568. align-items: center;
  569. height: 94rpx;
  570. font-size: 32rpx;
  571. border-radius: 10rpx;
  572. border: 1rpx solid #cccccc;
  573. img {
  574. margin: 0 14rpx 0 30rpx;
  575. width: 40rpx;
  576. height: 40rpx;
  577. }
  578. }
  579. .btn {
  580. display: flex;
  581. justify-content: center;
  582. align-items: center;
  583. margin: 65rpx 0 60rpx;
  584. height: 100rpx;
  585. color: #fff;
  586. font-size: 32rpx;
  587. border-radius: 12rpx;
  588. background-color: #6fb6b8;
  589. }
  590. }
  591. </style>