uploadVNormal.vue 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599
  1. <template>
  2. <div class="questionNamep">
  3. <!-- 0=企业微信,1=H5相机 -->
  4. <!-- 企业微信拍照 -->
  5. <div class="cameraDiv" @click="uploadImg" v-if="userInfo.photoMethod == '0'">
  6. <van-icon class="photo photos" name="photograph" size="22px" color="#969696" />
  7. </div>
  8. <!-- 原生自带拍照 -->
  9. <H5Camera
  10. @getImg="getImg"
  11. ref="H5Camera"
  12. :objectType="objectType"
  13. :capture="pictureSource == '1' ? '' : 'camera'"
  14. v-else />
  15. <div id="allmap"></div>
  16. <div class="mask" v-if="progressFlag">
  17. <el-progress
  18. type="circle"
  19. :percentage="percentage"
  20. :show-text="true"
  21. :width="110"
  22. :format="format"></el-progress>
  23. <div class="progressClose" @click="progressClose">取消</div>
  24. </div>
  25. <imageAIVerifyErr
  26. v-if="imageAIVerifyFlag"
  27. :imageAIVerifyFlag="imageAIVerifyFlag"
  28. :imageAIVerifyData="imageAIVerifyData"
  29. @confirmUpload="confirmUpload"
  30. @uploadImgFun="uploadImgFun"
  31. :source="'visit'"
  32. @normalFlow="normalFlow"
  33. @close="close"></imageAIVerifyErr>
  34. <!-- 图像识别白名单弹框提示 -->
  35. <imageWhiteStore
  36. v-if="imageWhiteStoreFlag"
  37. :imageWhiteStoreFlag="imageWhiteStoreFlag"
  38. :imageWhiteStoreData="imageWhiteStoreData"
  39. @normalFlow="normalFlow"
  40. @close="close">
  41. </imageWhiteStore>
  42. </div>
  43. </template>
  44. <script>
  45. import { addstorePhoto, addVisitsPosition, addPhotoToDB } from '@/api/index';
  46. import imageAIVerifyErr from './imageAIVerifyErr';
  47. import imageWhiteStore from './imageWhiteStore';
  48. import H5Camera from '@/components/H5Camera';
  49. import axios from 'axios';
  50. import uploadAliOss from '@/utils/uploadAliOss';
  51. import { addH5Photo } from '@/api/H5Camera';
  52. import { mapState } from 'vuex';
  53. export default {
  54. name: 'uploadImg',
  55. components: { imageAIVerifyErr, H5Camera, imageWhiteStore },
  56. props: {
  57. uploadid: {
  58. type: String,
  59. default: '',
  60. },
  61. storeGroupId: {
  62. type: String,
  63. default: '',
  64. },
  65. parentCollectionId: {
  66. type: String,
  67. default: '',
  68. },
  69. secondCollectionId: {
  70. type: [String, Number],
  71. default: '',
  72. },
  73. firstCollectionId: {
  74. type: String,
  75. default: '',
  76. },
  77. fourthCollectionId: {
  78. type: String,
  79. default: '',
  80. },
  81. thirdCollectionId: {
  82. type: String,
  83. default: '',
  84. },
  85. visitsId: {
  86. type: String,
  87. default: '',
  88. },
  89. taskId: {
  90. type: String,
  91. default: '',
  92. },
  93. collectionId: {
  94. type: String,
  95. default: '',
  96. },
  97. objectType: {
  98. type: String,
  99. default: '',
  100. },
  101. type: {
  102. type: Number,
  103. default: 1,
  104. },
  105. imgArr: {
  106. type: Array,
  107. default() {
  108. return [];
  109. },
  110. },
  111. visitModel: {
  112. type: String,
  113. default: '1',
  114. },
  115. deviceCode: {
  116. type: String,
  117. default: '',
  118. },
  119. putInCode: {
  120. type: String,
  121. default: '',
  122. },
  123. pictureSource: {
  124. // 是否允许从相册选择图片 1:允许;0:不允许
  125. type: String,
  126. default: '0',
  127. },
  128. photoIdentifyType: {
  129. // 图匠识别目的(1:店招内容识别(不能连拍和多选),3:调色机识别(不能连拍和多选),6:陈列SKU图片识别(不需要图匠实时识别))
  130. type: String,
  131. default: '',
  132. },
  133. continuousShoot: {
  134. // 是否允许连拍/相册多选 1:允许;0:不允许
  135. type: String,
  136. default: '0',
  137. },
  138. },
  139. computed: {
  140. ...mapState({
  141. userInfo: (state) => state.user.userInfo,
  142. }),
  143. },
  144. data() {
  145. return {
  146. shows: false,
  147. url: '',
  148. progressFlag: false,
  149. percentage: 0,
  150. timeFlag: null,
  151. imageAIVerifyFlag: false,
  152. imageAIVerifyData: null, //图匠校验返回的数据
  153. mediaId: '', //当前上传图片id
  154. addressesRemark: '', //当前位置信息
  155. controller: null, //取消请求状态
  156. fileUrl: '',
  157. imageWhiteStoreData: null,
  158. imageWhiteStoreFlag: false,
  159. localIdsArr: [],
  160. };
  161. },
  162. methods: {
  163. // 原生H5拍照图片
  164. // url: base64
  165. getImg(base64) {
  166. // 图片名称:用户名-时间戳
  167. let username = localStorage.getItem('loginName');
  168. let imgName = username + '-' + new Date().getTime();
  169. uploadAliOss(base64, imgName)
  170. .then((res) => {
  171. if (res.url && res.url.indexOf('http') != -1) {
  172. this.fileUrl = res.url;
  173. this.uploadImagev();
  174. }
  175. })
  176. .catch((err) => {
  177. console.log('err:' + err);
  178. });
  179. },
  180. uploadImgFun() {
  181. // 0=企业微信,1=H5相机
  182. if (this.userInfo.photoMethod == '0') {
  183. this.uploadImg();
  184. } else {
  185. this.$refs.H5Camera.camera();
  186. }
  187. },
  188. uploadImg() {
  189. var map = new TMap.Map('allmap', {
  190. zoom: 14,
  191. center: new TMap.LatLng(39.986785, 116.301012),
  192. });
  193. var geocoder = new TMap.service.Geocoder(); // 新建一个正逆地址解析类
  194. var markers = new TMap.MultiMarker({
  195. map: map,
  196. geometries: [],
  197. });
  198. markers.setGeometries([]);
  199. if (this.objectType == '' || this.objectType == null) {
  200. this.$toast('请选择类型!');
  201. return;
  202. }
  203. let url = window.location.href;
  204. let that = this;
  205. let wx = this.wx;
  206. let qiyeData;
  207. this.addressesRemark = '';
  208. const instance = axios.create();
  209. instance.defaults.headers.common['userId'] = localStorage.getItem('loginName');
  210. instance
  211. .get(process.env.VUE_APP_BASE_API + 'mobile/wx/ticket', {
  212. params: {
  213. url: url,
  214. },
  215. })
  216. .then((response) => {
  217. if (response.status == 200) {
  218. qiyeData = response.data.data;
  219. wx.config({
  220. beta: true, // 必须这么写,否则wx.invoke调用形式的jsapi会有问题
  221. debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
  222. appId: qiyeData.appId, // 必填,企业微信的corpID
  223. timestamp: qiyeData.timestamp, // 必填,生成签名的时间戳
  224. nonceStr: qiyeData.nonceStr, // 必填,生成签名的随机串
  225. signature: qiyeData.signature, // 必填,签名,见 附录-JS-SDK使用权限签名算法
  226. jsApiList: ['ready', 'chooseImage', 'uploadImage', 'getLocation'], // 必填,需要使用的JS接口列表,凡是要调用的接口都需要传进来
  227. });
  228. wx.ready(function () {
  229. wx.getLocation({
  230. type: 'gcj02',
  231. success: function (res) {
  232. var location = new TMap.LatLng(res.latitude, res.longitude);
  233. map.setCenter(location);
  234. markers.updateGeometries([
  235. {
  236. id: 'main', // 点标注数据数组
  237. position: location,
  238. },
  239. ]);
  240. geocoder.getAddress({ location: location }).then(
  241. function (result) {
  242. var addresses = result.result.formatted_addresses;
  243. that.addressesRemark = addresses.recommend;
  244. },
  245. function (err) {
  246. that.addressesRemark = '';
  247. }
  248. );
  249. },
  250. fail: function () {
  251. that.$dialog.alert({
  252. message: 'GPS未开启',
  253. });
  254. },
  255. });
  256. console.log(that.pictureSource);
  257. let sourceType = that.pictureSource == '1' ? ['album', 'camera'] : ['camera'];
  258. let count = 1;
  259. // 1:店招内容识别(不能连拍和多选),3:调色机识别(不能连拍和多选) 需要实时识别的不支持连拍和多选
  260. if (that.photoIdentifyType != 1 && that.photoIdentifyType != 3) {
  261. count = that.continuousShoot == '1' ? 10 : 1; //是否允许连拍/相册多选 最多5张
  262. }
  263. wx.chooseImage({
  264. count: count,
  265. sizeType: ['original'], // 可以指定是原图还是压缩图,默认二者都有
  266. sourceType: sourceType, // 可以指定来源是相册还是相机,默认二者都有
  267. defaultCameraMode: count == 1 ? 'normal' : 'batch', //表示进入拍照界面的默认模式,目前有normal与batch两种选择,normal表示普通单拍模式,batch表示连拍模式,不传该参数则为normal模式。从3.0.26版本开始支持front和batch_front两种值,其中front表示默认为前置摄像头单拍模式,batch_front表示默认为前置摄像头连拍模式。(注:用户进入拍照界面仍然可自由切换两种模式)
  268. // defaultCameraMode: 'normal', //表示进入拍照界面的默认模式,目前有normal与batch两种选择,normal表示普通单拍模式,batch表示连拍模式,不传该参数则为normal模式。从3.0.26版本开始支持front和batch_front两种值,其中front表示默认为前置摄像头单拍模式,batch_front表示默认为前置摄像头连拍模式。(注:用户进入拍照界面仍然可自由切换两种模式)
  269. isSaveToAlbum: 0, //整型值,0表示拍照时不保存到系统相册,1表示自动保存,默认值是1
  270. success: function (res) {
  271. let localIds = res.localIds;
  272. that.localIdsArr = [];
  273. that.syncUpload(localIds);
  274. // var localIds = '';
  275. // if (res.localIds != undefined) {
  276. // localIds = res.localIds[0];
  277. // } else {
  278. // localIds = res.localId;
  279. // }
  280. // wx.uploadImage({
  281. // localId: localIds, // 需要上传的图片的本地ID,由chooseImage接口获得
  282. // isShowProgressTips: 0, // 默认为1,显示进度提示
  283. // success: function (res) {
  284. // that.mediaId = res.serverId;
  285. // that.uploadImagev(addressesRemark);
  286. // },
  287. // fail: (err) => {
  288. // that.$toast(err.errMsg);
  289. // that.$toast(err.errCode);
  290. // },
  291. // });
  292. },
  293. });
  294. });
  295. }
  296. });
  297. },
  298. syncUpload(localIds) {
  299. if (!localIds.length) {
  300. this.uploadImagev();
  301. } else {
  302. var localId = localIds.pop();
  303. wx.uploadImage({
  304. localId: localId,
  305. isShowProgressTips: 1, // 默认为1,显示进度提示
  306. success: (res) => {
  307. this.localIdsArr.push(res.serverId);
  308. this.syncUpload(localIds);
  309. },
  310. });
  311. }
  312. },
  313. uploadImagev() {
  314. // 初始化重置 图匠校验
  315. this.resetProgress();
  316. this.close();
  317. var that = this;
  318. var parentCollectionId = null;
  319. if (that.parentCollectionId != null && that.parentCollectionId != 'null') {
  320. parentCollectionId = that.parentCollectionId;
  321. }
  322. var secondCollectionId = null;
  323. if (that.secondCollectionId != null && that.secondCollectionId != 'null') {
  324. secondCollectionId = that.secondCollectionId;
  325. }
  326. var firstCollectionId = null;
  327. if (that.firstCollectionId != null && that.firstCollectionId != 'null') {
  328. firstCollectionId = that.firstCollectionId;
  329. }
  330. var fourthCollectionId = null;
  331. if (that.fourthCollectionId != null && that.fourthCollectionId != 'null') {
  332. fourthCollectionId = that.fourthCollectionId;
  333. }
  334. var thirdCollectionId = null;
  335. if (that.thirdCollectionId != null && that.thirdCollectionId != 'null') {
  336. thirdCollectionId = that.thirdCollectionId;
  337. }
  338. var form = {
  339. mediaIds: [],
  340. fileUrl: '',
  341. collectionItemId: that.collectionId,
  342. objectType: that.objectType,
  343. storeGroupId: that.storeGroupId,
  344. taskId: that.taskId,
  345. visitsId: localStorage.getItem('visitId'),
  346. visitModel: that.visitModel,
  347. visitSource: '1',
  348. locationRemark: that.addressesRemark,
  349. parentCollectionId: parentCollectionId,
  350. secondCollectionId: secondCollectionId,
  351. firstCollectionId: firstCollectionId,
  352. fourthCollectionId: fourthCollectionId,
  353. thirdCollectionId: thirdCollectionId,
  354. deviceCode: that.deviceCode, //设备编号
  355. putInCode: that.putInCode, //投放编号
  356. };
  357. // 0=企业微信,1=H5相机
  358. if (this.userInfo.photoMethod == '0') {
  359. form.mediaIds = this.localIdsArr; // string 图片素材id
  360. } else {
  361. form.fileUrl = this.fileUrl; // string 图片素材id
  362. }
  363. this.controller = null;
  364. // 需要图匠校验的添加参数和loading
  365. if (
  366. this.photoIdentifyType &&
  367. (this.photoIdentifyType == '1' || this.photoIdentifyType == '3')
  368. ) {
  369. form.photoIdentifyType = this.photoIdentifyType;
  370. this.progress();
  371. this.controller = new AbortController(); //取消请求
  372. } else {
  373. this.toastLoading(0, '上传中...', true);
  374. }
  375. addstorePhoto(form, this.controller ? this.controller.signal : null)
  376. .then((res) => {
  377. this.requestThen(res);
  378. })
  379. .catch((error) => {
  380. this.requestCatch(error);
  381. });
  382. },
  383. // 公用请求then
  384. requestThen(res) {
  385. this.toastLoading().clear();
  386. if (res.code == -1) {
  387. // 图匠图片校验接口超时
  388. this.requestTimeOut(res);
  389. } else if (res.code == 200) {
  390. // 图匠校验结果返回
  391. if (
  392. this.photoIdentifyType &&
  393. (this.photoIdentifyType == '1' || this.photoIdentifyType == '3')
  394. ) {
  395. // 重置loaidng状态
  396. this.resetProgress();
  397. this.imageAIVerifyFlag = true;
  398. this.imageAIVerifyData = res.data[0];
  399. } else {
  400. // 正常流程
  401. // 图像识别白名单用户弹出框提示
  402. if (res.data[0].whiteStore) {
  403. this.imageWhiteStoreFlag = true;
  404. this.imageWhiteStoreData = res.data[0];
  405. } else {
  406. this.normalFlow(res);
  407. }
  408. }
  409. } else {
  410. this.resetProgress();
  411. that.$toast('上传失败!');
  412. }
  413. },
  414. // 公用请求catch
  415. requestCatch(error) {
  416. if (error.message === 'canceled') {
  417. this.$toast('取消上传');
  418. console.log('请求被取消:', error.message);
  419. }
  420. this.resetProgress();
  421. },
  422. // 正常流程
  423. normalFlow(res) {
  424. this.$toast('上传成功!');
  425. let fileInfoList = [];
  426. res.data.forEach((val) => {
  427. fileInfoList.push({
  428. fileUrl: val.url,
  429. id: val.fileId,
  430. type: 2,
  431. });
  432. });
  433. this.$emit('newimgarr', {
  434. fileInfoList: fileInfoList,
  435. // fileUrl: res.data.url,
  436. // id: res.data.fileId,
  437. // type: 2,
  438. photoIdentifyType: this.photoIdentifyType,
  439. });
  440. },
  441. progress() {
  442. // 后端接口20000ms后失效,每1000m progress加10,到90停止;
  443. this.progressFlag = true;
  444. this.percentage = 10;
  445. this.timeFlag = setInterval(() => {
  446. this.percentage = this.percentage + 10;
  447. if (this.percentage == 90) clearInterval(this.timeFlag);
  448. }, 1000);
  449. },
  450. format(percentage) {
  451. return `${percentage} %\n图像识别中`;
  452. },
  453. // 重置loaidng状态
  454. resetProgress() {
  455. this.percentage = 100;
  456. clearInterval(this.timeFlag);
  457. this.progressFlag = false;
  458. this.percentage = 0;
  459. },
  460. // 照片是否入库,1.照片识别三次不通过仍要上传,2.照片识别通过
  461. // isUpdate:是否更新店招照片,只有门店店招需要更新
  462. confirmUpload(res) {
  463. if (this.photoIdentifyType && this.photoIdentifyType != '6') {
  464. var form = {
  465. mediaIds: [],
  466. fileUrl: '',
  467. visitSource: '1', // Long 拜访模式
  468. storeGroupId: this.storeGroupId, // string 门店任务组,多个用逗号隔开
  469. visitsId: localStorage.getItem('visitId'), // string 拜访id
  470. taskId: this.taskId, // string 任务id
  471. objectType: this.objectType, // string 照片类型,取任务上的照片类型,如果没有则取手动选择的照片类型
  472. locationRemark: this.addressesRemark, // String 当前地址信息
  473. firstCollectionId: this.firstCollectionId, // Long 第一级采集项id,取当前采集项的字段就行
  474. secondCollectionId: this.secondCollectionId, // Long 第二级采集项id,取当前采集项的字段就行
  475. putInCode: this.putInCode, // String 当前任务对应的投放编号
  476. deviceCode: this.deviceCode, // String 当前任务对应的设备编号
  477. collectionItemId: this.collectionId,
  478. url: res.data.url, // String 当前拍摄图片的url
  479. businessId: res.data.businessId, // 当前拍摄图片id
  480. feedbackMessage: res.feedbackMessage,
  481. };
  482. // 0=企业微信,1=H5相机
  483. if (this.userInfo.photoMethod == '0') {
  484. form.mediaIds = this.localIdsArr; // string 图片素材id
  485. } else {
  486. form.fileUrl = this.fileUrl; // string 图片素材id
  487. }
  488. if (res.isUpdate) {
  489. form.isUpdate = 'true';
  490. }
  491. addPhotoToDB(form).then((resData) => {
  492. if (resData.code == 200) {
  493. console.log(resData);
  494. res.data.fileId = resData.data[0].fileId;
  495. res.data = [res.data];
  496. this.normalFlow(res);
  497. }
  498. });
  499. }
  500. },
  501. close() {
  502. this.imageAIVerifyFlag = false;
  503. this.imageWhiteStoreFlag = false;
  504. },
  505. requestTimeOut(res) {
  506. this.resetProgress();
  507. this.close();
  508. this.$dialog
  509. .confirm({
  510. title: '系统提示',
  511. message: res.msg,
  512. showCancelButton: false,
  513. })
  514. .then(() => {
  515. this.confirmUpload(res);
  516. });
  517. },
  518. // 取消图片上传
  519. progressClose() {
  520. this.controller.abort();
  521. },
  522. },
  523. };
  524. </script>
  525. <style lang="scss" scoped>
  526. .questionNamep {
  527. font-size: 16px;
  528. color: #484848;
  529. line-height: 40px;
  530. padding: 0 15px;
  531. box-sizing: border-box;
  532. position: relative;
  533. .cameraDivp {
  534. flex: 1;
  535. display: flex;
  536. align-items: center;
  537. justify-content: center;
  538. .photo {
  539. /*margin-top: 9px;*/
  540. float: right;
  541. }
  542. .camera {
  543. width: 60px;
  544. height: 100%;
  545. position: absolute;
  546. right: 0;
  547. top: 0;
  548. opacity: 0;
  549. z-index: 89;
  550. }
  551. }
  552. .mask {
  553. position: fixed;
  554. top: 0;
  555. left: 0;
  556. right: 0;
  557. bottom: 0;
  558. width: 100%;
  559. height: 100%;
  560. background: rgba(255, 255, 255, 1);
  561. display: flex;
  562. justify-content: center;
  563. align-items: center;
  564. z-index: 99999999;
  565. display: flex;
  566. flex-direction: column;
  567. .progressClose {
  568. width: 70px;
  569. text-align: center;
  570. background: #67c23a;
  571. color: #fff;
  572. height: 30px;
  573. line-height: 30px;
  574. border-radius: 5px;
  575. margin-top: 5px;
  576. font-size: 12px;
  577. }
  578. }
  579. }
  580. #allmap {
  581. width: 10px;
  582. height: 10px;
  583. left: -1000px;
  584. position: relative;
  585. }
  586. </style>
  587. <style lang="scss">
  588. .mask {
  589. .el-progress__text {
  590. white-space: pre-wrap;
  591. }
  592. }
  593. </style>