index.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365
  1. <style>
  2. #import-header {
  3. position: relative;
  4. background-color: #3ed798;
  5. color: white;
  6. height: 80px;
  7. padding: 0 30px;
  8. line-height: 80px;
  9. }
  10. #import-header>img {
  11. height: 34px;
  12. margin-right: 10px;
  13. position: relative;
  14. top: 10px;
  15. }
  16. #import-header #exit-btn {
  17. position: absolute;
  18. top: 0;
  19. right: 0;
  20. border: none;
  21. width: 60px;
  22. height: 36px;
  23. background: url('../assets/images/btn_closed.png') no-repeat center;
  24. background-size: cover;
  25. outline: none;
  26. }
  27. #import-header #exit-btn:hover {
  28. background: url('../assets/images/btn_closed_hover.png') no-repeat center;
  29. }
  30. #import-body {
  31. position: absolute;
  32. top: 80px;
  33. left: 0;
  34. right: 0;
  35. bottom: 0;
  36. display: flex;
  37. flex-direction: column;
  38. }
  39. #import-body #import-div {
  40. text-align: center;
  41. padding: 20px 0;
  42. }
  43. #import-body #import-div #import-btn {
  44. width: 316px;
  45. height: 70px;
  46. border-radius: 35px;
  47. border: none;
  48. font-size: 30px;
  49. }
  50. #import-body #progress-div {
  51. flex-grow: 1;
  52. display: flex;
  53. flex-direction: column;
  54. padding: 30px 50px;
  55. }
  56. #import-body #progress-div #console-panel {
  57. flex-grow: 1;
  58. background-color: #e3e3e3;
  59. overflow: auto;
  60. }
  61. #import-body #progress-div #console-panel .console-line {
  62. font-size: 14px;
  63. text-align: center;
  64. display: flex;
  65. justify-content: center;
  66. align-items: center;
  67. }
  68. #import-body #progress-div #console-panel .console-line>span {
  69. margin: 0 10px;
  70. }
  71. #import-body #progress-div #console-panel .console-line>span.red {
  72. color: #fc7156;
  73. }
  74. #import-body #progress-div #console-panel .console-line>span.green {
  75. color: #3ed798;
  76. }
  77. .file {
  78. position: relative;
  79. display: inline-block;
  80. border-radius: 4px;
  81. padding: 4px 12px;
  82. overflow: hidden;
  83. color: white;
  84. text-decoration: none;
  85. text-indent: 0;
  86. line-height: 20px;
  87. }
  88. .file input {
  89. position: absolute;
  90. font-size: 100px;
  91. right: 0;
  92. top: 0;
  93. opacity: 0;
  94. }
  95. .file:hover {
  96. color: #004974;
  97. text-decoration: none;
  98. }
  99. </style>
  100. <template>
  101. <div style='height: 100%;'>
  102. <div id='import-header'>
  103. <img src='../assets/images/nav_images.png' alt=''>照片批量上传工具
  104. <button id='exit-btn' @click='logout'></button>
  105. </div>
  106. <div id='import-body'>
  107. <div id='import-div'>
  108. <a href='javascript:;' class='file'>
  109. <!--webkitdirectory-->
  110. <input id='photos' :disabled='uploadDisabled' multiple="true" title=' ' name='files' type='file' v-on:change='importPhotos'/>
  111. <button id='import-btn'>
  112. {{btnText}}
  113. </button>
  114. </a>
  115. <button class='form-control' style='width: 40%;margin:auto;font-size:20px;'>
  116. <span>成功:{{successNum}}/{{allNum}}</span>
  117. <span>失败:{{errorNum}}/{{allNum}}</span>
  118. <!--<span>处理进度:{{completeProportion}}</span>-->
  119. </button>
  120. </div>
  121. <div id='progress-div'>
  122. <div>
  123. </div>
  124. <div id='console-panel'>
  125. <div v-for='msgInfo in returnMsgList'>
  126. <p class='console-line' v-if='msgInfo.success'>
  127. <span><img src='../assets/images/icon_sucess.png'></span>
  128. <span>{{msgInfo.fileName}}:{{msgInfo.msg}}</span>
  129. <span>处理成功</span>
  130. </p>
  131. <p class='console-line' v-if='!msgInfo.success'>
  132. <span><img src='../assets/images/icon_failed.png'></span>
  133. <span>{{msgInfo.fileName}}:{{msgInfo.msg}}</span>
  134. <span class='red'>处理失败</span>
  135. </p>
  136. </div>
  137. </div>
  138. </div>
  139. </div>
  140. </div>
  141. </template>
  142. <script>
  143. const CryptoJS = require('crypto-js');
  144. const Base64 = require('js-base64').Base64;
  145. const fs = nodeRequire("fs");
  146. export default {
  147. data() {
  148. return {
  149. btnText:'上传照片',
  150. uploadDisabled:false,
  151. subTextShow: false,
  152. totalCount:0,
  153. photoList:[],//总的照片
  154. returnMsgList:[],
  155. successNum:0,
  156. errorNum:0,
  157. allNum:0,
  158. completeProportion:0
  159. }
  160. },
  161. methods: {
  162. logout() {
  163. localStorage.removeItem('rootOrgId');
  164. localStorage.removeItem('userName');
  165. localStorage.removeItem('user_token');
  166. this.$router.push({path: '/login'});
  167. },
  168. importPhotos() {
  169. var photos = document.getElementById('photos');
  170. this.photoList = photos.files;
  171. if (this.photoList.length>0) {
  172. this.returnMsgList = [];
  173. this.successNum = 0;
  174. this.errorNum = 0;
  175. this.allNum = 0;
  176. /*this.uploadDisabled = true;
  177. this.btnText = '处理中';*/
  178. this.processQueue();
  179. }
  180. },
  181. isImageFile(file) {
  182. let lowercaseName = file.toLowerCase();
  183. if (lowercaseName.endsWith('.jpg') || lowercaseName.endsWith('.jpeg') || lowercaseName.endsWith('.png')) {
  184. return true;
  185. } else {
  186. return false;
  187. }
  188. },
  189. processQueue(){
  190. this.allNum = this.photoList.length;
  191. for(var i = 0;i<this.allNum;i++){
  192. this.processStudentPhoto(this.photoList[i]);
  193. }
  194. },
  195. processStudentPhoto(photo) {
  196. let arr = photo.name.split('.');
  197. let fileName = arr[0];//文件名就是身份证号码
  198. let fileSuffix = arr[1];//文件后缀
  199. let md5Hash = CryptoJS.MD5(Base64.encode(photo.name)).toString();
  200. let photoMD5 = md5Hash+'.'+fileSuffix;
  201. let rootOrgId = localStorage.getItem('rootOrgId');
  202. //根据身份证号码查询学生信息
  203. this.$http.get('/api/ecs_core/studentFaceInfo/identityNumber?orgId='+rootOrgId+'&identityNumber='+fileName, {
  204. }).then((response) => {
  205. var studentFaceInfoJSON = response.body;
  206. if (studentFaceInfoJSON.student && studentFaceInfoJSON.student.id) {
  207. let md5 = studentFaceInfoJSON.photoMD5;
  208. let studentId = studentFaceInfoJSON.student.id;
  209. //分析人脸
  210. this.detectFace(photo).then((res)=>{
  211. if(res.body.faces.length<1){
  212. this.finishOneBack(false,fileName,'没有检测到人脸',photo);
  213. }else{
  214. let face_token = res.body.faces[0].face_token;
  215. this.getFaceSetToken().then((res)=>{
  216. var face_set_token = res.bodyText;
  217. let photoMD5 = md5Hash+'.'+fileSuffix;
  218. var savePhotoInfo = {
  219. 'studentFaceInfoId':studentFaceInfoJSON.id,
  220. 'face_set_token':face_set_token,
  221. 'face_token':face_token,
  222. 'studentId':studentId,
  223. 'userName':localStorage.getItem('userName'),
  224. 'fileName':fileName,
  225. 'file':photo,
  226. 'identityNumber':fileName,
  227. 'fileSuffix':fileSuffix,
  228. 'photoMD5':photoMD5
  229. };
  230. this.addFaceToSet(face_set_token, face_token).then((res)=>{
  231. this.saveImageToUpyun(savePhotoInfo);
  232. },(err)=>{
  233. this.finishOneBack(false,fileName,'addFaceToSet失败',photo);
  234. });
  235. },(error)=>{
  236. this.finishOneBack(false,fileName,'getFaceSetToken失败',photo);
  237. }
  238. );
  239. }
  240. },(err)=>{
  241. this.finishOneBack(false,fileName,'调用face++检测人脸失败',photo);
  242. });
  243. }else{
  244. this.finishOneBack(false,fileName,'查询身份证不存在',photo);
  245. }
  246. },(error)=>{
  247. this.finishOneBack(false,fileName,'根据身份证号码查询失败',photo);
  248. });
  249. },
  250. saveImageToUpyun(savePhotoInfo){
  251. this._saveImageToUpyun(savePhotoInfo).then((res)=>{
  252. //调用core后台接口,对数据进行保存
  253. let photoMD5 = savePhotoInfo.md5Hash+'.'+savePhotoInfo.fileSuffix;
  254. if(!savePhotoInfo.studentFaceInfoId){
  255. this.saveStudentFaceInfoByPost(savePhotoInfo);
  256. }else{
  257. this.saveStudentFaceInfoByPut(savePhotoInfo);
  258. }
  259. },(err)=>{
  260. this.finishOneBack(false,savePhotoInfo.fileName,'saveImageToUpyun失败',savePhotoInfo.file);
  261. });
  262. },
  263. _saveImageToUpyun(savePhotoInfo){
  264. var url = process.env.VUE_APP_UPYUN_BUCKETURL+'/student_base_photo/'
  265. +savePhotoInfo.identityNumber+'/'
  266. +savePhotoInfo.photoMD5;
  267. var authorization = 'Basic '+Base64.encode(process.env.VUE_APP_UPYUN_OPERATOR+':'+process.env.VUE_APP_UPYUN_PASSWORD);
  268. var headers = {headers:{'Authorization':authorization,'Content-Type': 'image/jpeg'}};
  269. return this.$http.put(url,savePhotoInfo.file,headers);
  270. },
  271. getFaceSetToken(){
  272. return this.$http.get('/api/ecs_core/faceSet/enableFaceSet');
  273. },
  274. addFaceToSet(faceset_token,face_token){
  275. let formData_addface = new FormData();
  276. formData_addface.append('api_key', process.env.VUE_APP_FACEPP_API_KEY);
  277. formData_addface.append('api_secret',process.env.VUE_APP_FACEPP_API_SECRET);
  278. formData_addface.append('faceset_token', faceset_token);
  279. formData_addface.append('face_tokens', face_token);
  280. return this.$http.post('/facepp/v3/faceset/addface', formData_addface);
  281. },
  282. detectFace(file){
  283. let fileBlob = new Blob([file]);
  284. let formData_face_token = new FormData();
  285. formData_face_token.append('api_key',process.env.VUE_APP_FACEPP_API_KEY);
  286. formData_face_token.append('api_secret',process.env.VUE_APP_FACEPP_API_SECRET);
  287. formData_face_token.append('image_file', fileBlob);
  288. return this.$http.post('/facepp/v3/detect', formData_face_token);
  289. },
  290. saveStudentFaceInfoByPost(savePhotoInfo){
  291. this.$http.post('/api/ecs_core/studentFaceInfo', {
  292. 'faceSetToken': savePhotoInfo.face_set_token,
  293. 'faceToken': savePhotoInfo.face_token,
  294. 'photoMD5': savePhotoInfo.photoMD5,
  295. 'student': {
  296. 'id': savePhotoInfo.studentId
  297. },
  298. 'createUser':savePhotoInfo.userName,
  299. 'updateUser':savePhotoInfo.userName,
  300. 'createType':'IMPORT_TOOL'
  301. }).then((res)=>{
  302. this.finishOneBack(true,savePhotoInfo.fileName,'处理成功');
  303. },(err)=>{
  304. this.finishOneBack(false,savePhotoInfo.fileName,'saveStudentFaceInfoByPost失败',savePhotoInfo.file);
  305. });
  306. },
  307. saveStudentFaceInfoByPut(savePhotoInfo){
  308. this.$http.put('/api/ecs_core/studentFaceInfo', {
  309. 'faceSetToken': savePhotoInfo.face_set_token,
  310. 'faceToken': savePhotoInfo.face_token,
  311. 'photoMD5': savePhotoInfo.photoMD5,
  312. 'student': {
  313. 'id': savePhotoInfo.studentId
  314. },
  315. 'createUser':savePhotoInfo.userName,
  316. 'updateUser':savePhotoInfo.userName,
  317. 'createType':'IMPORT_TOOL'
  318. }).then((res)=>{
  319. this.finishOneBack(true,savePhotoInfo.fileName,'处理成功');
  320. },(err)=>{
  321. this.finishOneBack(false,savePhotoInfo.fileName,'saveStudentFaceInfoByPut失败',savePhotoInfo.file);
  322. });
  323. },
  324. finishOneBack(isSuccess,fileName,msg,file){
  325. this.returnMsgList.push({'success':isSuccess,'fileName':fileName,'msg':msg});
  326. if(isSuccess){
  327. this.successNum++;
  328. }else{
  329. this.errorNum++;
  330. if(fs){
  331. var errorfilePath = file.path.substring(0,file.path.lastIndexOf("\\"))+"\\errorfiles";
  332. if (!fs.existsSync(errorfilePath)) {
  333. fs.mkdirSync(errorfilePath);
  334. }
  335. var readStream = fs.createReadStream(file.path);
  336. var writeStream = fs.createWriteStream(errorfilePath+"\\"+file.name);
  337. readStream.pipe(writeStream);
  338. }
  339. }
  340. if(this.errorNum+this.successNum == this.allNum){
  341. this.btnText = '上传照片';
  342. this.uploadDisabled = false;
  343. var photos = document.getElementById('photos');
  344. photos.files = null;
  345. }
  346. }
  347. },
  348. created() {
  349. }
  350. }
  351. </script>