change-face.vue 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444
  1. <template>
  2. <!-- 换脸弹窗 -->
  3. <a-modal
  4. v-if="faceShowStatus"
  5. title="换脸操作"
  6. :visible="faceShowStatus"
  7. :confirmLoading="confirmLoading"
  8. dialog-class="change-face-modal"
  9. @ok="handlePolicyOperationSure"
  10. @cancel="handlePolicyOperationCancel"
  11. >
  12. <div class="face-right-div-p">
  13. <a-radio-group v-model="faceType" button-style="solid" @change="handleFaceTypeChange">
  14. <a-radio-button value="a">人脸合成</a-radio-button>
  15. <a-radio-button value="b">合成列表</a-radio-button>
  16. </a-radio-group>
  17. <span v-if="faceType === 'b'" class="add-acount-policy-btn">
  18. <a-button type="primary" @click="handleRefresh">刷新</a-button>
  19. </span>
  20. </div>
  21. <a-form-model
  22. v-if="faceType === 'a'"
  23. class="policy-operation-form"
  24. ref="policyOperationForm"
  25. :model="policyOperationForm"
  26. :label-col="labelCol"
  27. :wrapper-col="wrapperCol"
  28. >
  29. <a-form-model-item label="图片上传">
  30. <a-upload
  31. name="avatar"
  32. list-type="picture-card"
  33. class="avatar-uploader"
  34. :show-upload-list="false"
  35. :before-upload="beforeUpload"
  36. >
  37. <div v-if="imageUrl" class="upload-img-class">
  38. <div class="img-flex-url">
  39. <img :src="imageUrl" alt="avatar"/>
  40. </div>
  41. </div>
  42. <div v-else>
  43. <a-icon type="plus" style="margin-top: 10px;"/>
  44. <div class="ant-upload-text">Upload</div>
  45. </div>
  46. </a-upload>
  47. <p style="margin: 0;">1、请保证脸部无遮挡、光线充足</p>
  48. <p style="margin: 0;">2、视频、照片中只能出现一张人脸</p>
  49. <p style="margin: 0;">3、图片尺寸不能小于500*500</p>
  50. </a-form-model-item>
  51. </a-form-model>
  52. <div v-else-if="faceType === 'b'" class="face-right-div">
  53. <a-table
  54. class="face-right-table"
  55. ref="table"
  56. size="middle"
  57. bordered
  58. :columns="settlementColumns"
  59. :dataSource="dataSource"
  60. :pagination="false"
  61. >
  62. <span slot="input_video_url" slot-scope="text, record">
  63. <div style="width:100px;height:177px;margin-left:25px;">
  64. <video v-if="record.input_video_url" :src="record.input_video_url" style="width:100px" controls></video>
  65. <img v-else style="width:100px" src="@/assets/noImg.png">
  66. </div>
  67. </span>
  68. <span slot="input_image_url" slot-scope="text, record">
  69. <img v-if="text" :src="text" style="width:60px">
  70. <img v-if="!text" src="@/assets/noImg.png" style="width:60px">
  71. </span>
  72. <span slot="actions" slot-scope="text, record">
  73. <a :disabled="!faceShowList.projectId || record.status !== 'finished'" @click="handlePushData(record)">推送</a>
  74. <a-divider type="vertical"/>
  75. <a @click="handleDownLoadData(record)" :disabled="record.status !== 'finished'">下载</a>
  76. </span>
  77. </a-table>
  78. <a-pagination
  79. class="pagin-table-class"
  80. :total="totalAll"
  81. :show-total="total => `共 ${totalAll} 条`"
  82. size="small"
  83. :current="defaultCurrent"
  84. :defaultPageSize="2"
  85. @change="onShowSizeChange"
  86. @showSizeChange="onShowSizeChange"
  87. />
  88. </div>
  89. </a-modal>
  90. </template>
  91. <script>
  92. import {mapGetters} from 'vuex';
  93. import {fileInsertV2} from '@/api/actor';
  94. import {getAction, postAction} from '@/api/manage';
  95. import BMF from 'browser-md5-file';
  96. let COS = require('cos-js-sdk-v5');
  97. let cos = new COS({
  98. SecretId: 'AKIDE6IpMi8fJQRCg1iuWzFajjRs43kbbets',
  99. SecretKey: 'tXzuwMfplTTK3c9GFUyETilasvQfePu9'
  100. });
  101. export default {
  102. name: 'change-face',
  103. props: {
  104. status: {
  105. type: Boolean,
  106. default: false
  107. },
  108. defaultList: {
  109. type: Object,
  110. default: () => {}
  111. }
  112. },
  113. data() {
  114. return {
  115. confirmLoading: false,
  116. departValueInfoModality: [],
  117. departValueInfoMod: [],
  118. departValueInfoSence: [],
  119. getProjectId: '',
  120. defaultCurrent: 1,
  121. dataAllList: [],
  122. totalAll: 0,
  123. uploadType: 'face',
  124. dataSource: [],
  125. settlementColumns: [
  126. {
  127. title: '视频',
  128. dataIndex: 'input_video_url',
  129. align: 'center',
  130. width: 150,
  131. scopedSlots: {customRender: 'input_video_url'}
  132. },
  133. {
  134. title: '人脸',
  135. dataIndex: 'input_image_url',
  136. align: 'center',
  137. width: 150,
  138. scopedSlots: {customRender: 'input_image_url'}
  139. },
  140. {
  141. title: '状态',
  142. align: 'center',
  143. dataIndex: 'status',
  144. width: 100
  145. },
  146. {
  147. title: '操作',
  148. align: 'center',
  149. dataIndex: 'actions',
  150. width: 130,
  151. scopedSlots: {customRender: 'actions'}
  152. }
  153. ],
  154. labelCol: {span: 4},
  155. wrapperCol: {span: 16},
  156. imageUrl: '',
  157. faceShowStatus: false,
  158. faceType: 'a',
  159. policyOperationForm: {},
  160. urlResult: '',
  161. md5Result: ''
  162. };
  163. },
  164. watch: {
  165. status(newVal) {
  166. this.faceShowStatus = newVal;
  167. },
  168. defaultList(newVal) {
  169. if (newVal) {
  170. this.faceShowList = newVal;
  171. }
  172. }
  173. },
  174. methods: {
  175. ...mapGetters(['userInfo']),
  176. // 分页操作按钮
  177. onShowSizeChange(current, pageSize) {
  178. this.dataSource = [];
  179. this.defaultCurrent = current;
  180. const defaultData = [...this.dataAllList];
  181. this.dataSource = [...defaultData.slice(2 * current - 2, 2 * current)];
  182. },
  183. handleFaceTypeChange(e) {
  184. if (e.target.value === 'b') {
  185. this.handleInitTable();
  186. this.handleGetMaterialTagInfo();
  187. }
  188. },
  189. handleInitTable() {
  190. const paramsData = {
  191. videoMd5: this.faceShowList.projectId ? this.faceShowList.code : this.faceShowList.videoCode
  192. };
  193. this.dataSource = [];
  194. // http://192.168.1.204:8000
  195. postAction('http://faceswap.bjhcstszkj.cn/jeecg-boot/task/query', paramsData).then(result => {
  196. if (result.code === 0) {
  197. this.dataAllList = result.data;
  198. this.dataSource = result.data.slice(0, 2);
  199. this.totalAll = result.data.length ? result.data.length : 0;
  200. }
  201. else {
  202. this.dataAllList = [];
  203. this.dataSource = [];
  204. this.totalAll = 0;
  205. }
  206. }).catch(error => {
  207. console.log(error, 'eeee');
  208. });
  209. },
  210. handleGetDetailList() {
  211. getAction('/ctop/materialInfo/queryById', {
  212. id: this.faceShowList.projectId ? this.faceShowList.code : this.faceShowList.videoCode
  213. }).then(res => {
  214. if (res.success) {
  215. this.getProjectId = res.result.projectId;
  216. }
  217. });
  218. },
  219. handleGetMaterialTagInfo() {
  220. getAction('/ctop/materialTagInfo/detail', {
  221. code: this.faceShowList.projectId ? this.faceShowList.code : this.faceShowList.videoCode
  222. }).then(res => {
  223. if (res.success) {
  224. this.departValueInfoModality = res.modalityTagList.map(item => item.tagId);
  225. this.departValueInfoMod = res.modTagList.map(item => item.tagId);
  226. this.departValueInfoSence = res.senceTagList.map(item => item.tagId);
  227. }
  228. });
  229. },
  230. // TODO 推送操作
  231. handlePushData(list) {
  232. const paramsData = {
  233. projectId: this.faceShowList.projectId, // 项目选择
  234. ascription: {
  235. planId: 'e9ca23d68d884d4ebb19d07889727dae',
  236. shotId: 'e9ca23d68d884d4ebb19d07889727dae',
  237. clipId: 'e9ca23d68d884d4ebb19d07889727dae'
  238. },
  239. status: '1',
  240. code: list.output_video_md5,
  241. url: list.output_video_url && list.output_video_url.split(':')[1],
  242. type: 'VIDEO',
  243. userId: this.userInfo().id,
  244. modalityTagList: this.departValueInfoModality, // 形式标签
  245. senceTagList: this.departValueInfoSence, // 场景标签
  246. modTagList: this.departValueInfoMod // 基调标签
  247. };
  248. const that = this;
  249. this.$confirm({
  250. title: '提示',
  251. content: '是否进行推送操作',
  252. onOk() {
  253. fileInsertV2(paramsData).then(res => {
  254. if (res.code === 0) {
  255. that.handlePolicyOperationCancel();
  256. that.$emit('sureface');
  257. that.$message.success('素材已经同步到未审核列表,请进入查看');
  258. }
  259. else {
  260. that.$message.error(res.message);
  261. }
  262. });
  263. }
  264. });
  265. },
  266. // 下载操作
  267. handleDownLoadData(list) {
  268. const randomTime = new Date().getTime() + '_check.mp4';
  269. fetch(list.output_video_url)
  270. .then(res => res.blob())
  271. .then(blob => {
  272. const a = document.createElement('a');
  273. document.body.appendChild(a);
  274. a.style.display = 'none';
  275. // 使用获取到的blob对象创建的url
  276. const url = window.URL.createObjectURL(blob);
  277. a.href = url;
  278. // 指定下载的文件名
  279. a.download = randomTime;
  280. a.click()
  281. document.body.removeChild(a);
  282. // 移除blob对象的url
  283. window.URL.revokeObjectURL(url);
  284. });
  285. },
  286. // 刷新按钮
  287. handleRefresh() {
  288. this.defaultCurrent = 1;
  289. this.handleInitTable();
  290. },
  291. checkTv(data) {
  292. const index = data.lastIndexOf(".");
  293. let tv_id = '';
  294. tv_id = data.substring(index);
  295. if(tv_id !== ".mp4" && tv_id !== ".rmvb" && tv_id !== ".avi" && tv_id !== ".ts") {
  296. return data + '.mp4';
  297. }
  298. else {
  299. return data;
  300. }
  301. },
  302. // 确定按钮
  303. handlePolicyOperationSure() {
  304. if (this.faceType === 'a') {
  305. this.confirmLoading = true;
  306. if (!this.md5Result) {
  307. this.$message.error('请上传图片');
  308. return;
  309. }
  310. const paramsData = {
  311. inputVideoUrl: this.checkTv(this.faceShowList.url),
  312. inputImageUrl: this.urlResult,
  313. videoMd5: this.faceShowList.projectId ? this.faceShowList.code : this.faceShowList.videoCode,
  314. imageMd5: this.md5Result,
  315. createBy: this.userInfo().id
  316. };
  317. postAction('http://faceswap.bjhcstszkj.cn/jeecg-boot/task/single', paramsData).then(result => {
  318. if (result.code === 0) {
  319. this.handlePolicyOperationCancel();
  320. this.$emit('sureface');
  321. this.$message.success('图片上传成功');l
  322. this.confirmLoading = false;
  323. }
  324. else {
  325. this.$message.success('目标图片已存在,请下载视频');
  326. this.faceType = 'b';
  327. this.handleInitTable();
  328. this.handleGetMaterialTagInfo();
  329. this.confirmLoading = false;
  330. }
  331. }).catch(error => {
  332. this.confirmLoading = false;
  333. });
  334. }
  335. else {
  336. this.handlePolicyOperationCancel();
  337. }
  338. },
  339. // 取消按钮
  340. handlePolicyOperationCancel() {
  341. this.faceType = 'a';
  342. this.imageUrl = '';
  343. this.urlResult = '';
  344. this.md5Result = '';
  345. this.defaultCurrent = 1;
  346. this.$emit('closeface');
  347. },
  348. getBase64(img, callback) {
  349. const reader = new FileReader();
  350. reader.addEventListener('load', () => callback(reader.result));
  351. reader.readAsDataURL(img);
  352. },
  353. beforeUpload(info) {
  354. const isZip = info.type.includes('png') || info.type.includes('jpeg');
  355. if (!isZip) {
  356. this.$message.error('请上传正确类型的图片');
  357. this.imageUrl = '';
  358. this.urlResult = '';
  359. this.md5Result = '';
  360. return;
  361. }
  362. new Promise(function (resolve, reject) {
  363. let width = 500;
  364. let height = 500;
  365. let _URL = window.URL || window.webkitURL;
  366. let img = new Image();
  367. img.onload = function () {
  368. let valid = img.width >= width && img.height >= height;
  369. valid ? resolve() : reject();
  370. };
  371. img.src = _URL.createObjectURL(info);
  372. }).then(() => {
  373. this.handleMd5(info);
  374. this.cosUpload(info);
  375. this.getBase64(info, imageUrl => {
  376. this.imageUrl = imageUrl;
  377. });
  378. }, () => {
  379. this.$message.error('图片尺寸不能小于500*500');
  380. this.imageUrl = '';
  381. this.urlResult = '';
  382. this.md5Result = '';
  383. return Promise.reject();
  384. });
  385. },
  386. handleMd5(file) {
  387. let bmf = new BMF();
  388. let that = this;
  389. return new Promise(function (resolve, reject) {
  390. bmf.md5(file, (err, md5) => {
  391. that.md5Result = md5;
  392. });
  393. });
  394. },
  395. cosUpload(file) {
  396. let that = this;
  397. let date = new Date();
  398. let y = date.getFullYear();
  399. let MM = date.getMonth() + 1;
  400. MM = MM < 10 ? '0' + MM : MM;
  401. let d = date.getDate();
  402. d = d < 10 ? '0' + d : d;
  403. let timeElse = new Date().getTime();
  404. let arr = file.name.split('.');
  405. let str = '';
  406. for (let i = 0; i < arr.length; i++) {
  407. if (i === arr.length - 1) {
  408. }
  409. else {
  410. if (i === arr.length - 2) {
  411. str += arr[i];
  412. }
  413. else {
  414. str += arr[i] + '.';
  415. }
  416. }
  417. }
  418. cos.putObject(
  419. {
  420. Bucket: 'media-1301855440',
  421. Region: 'ap-chongqing',
  422. // 存储桶所在地域,必须字段
  423. Key: that.uploadType + '/' + y + '-' + MM + '-' + d + '/' + str + '-' + timeElse + '.' + arr[arr.length - 1],
  424. StorageClass: 'STANDARD',
  425. Body: file // 上传文件对象
  426. },
  427. function (err, data) {
  428. if (err) {
  429. that.$message.error('上传失败!!!' + err);
  430. return;
  431. }
  432. that.loadingElse = false;
  433. that.urlResult = 'http://' + data.Location;
  434. }
  435. );
  436. }
  437. }
  438. }
  439. </script>
  440. <style lang="less">
  441. @import "change-face";
  442. </style>