repairRecord.vue 13 KB

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