change-face.vue 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445
  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. // http://faceswap.tjyourong.com.cn
  196. postAction('http://faceswap.tjyourong.com.cn/jeecg-boot/task/query', paramsData).then(result => {
  197. if (result.code === 0) {
  198. this.dataAllList = result.data;
  199. this.dataSource = result.data.slice(0, 2);
  200. this.totalAll = result.data.length ? result.data.length : 0;
  201. }
  202. else {
  203. this.dataAllList = [];
  204. this.dataSource = [];
  205. this.totalAll = 0;
  206. }
  207. }).catch(error => {
  208. console.log(error, 'eeee');
  209. });
  210. },
  211. handleGetDetailList() {
  212. getAction('/ctop/materialInfo/queryById', {
  213. id: this.faceShowList.projectId ? this.faceShowList.code : this.faceShowList.videoCode
  214. }).then(res => {
  215. if (res.success) {
  216. this.getProjectId = res.result.projectId;
  217. }
  218. });
  219. },
  220. handleGetMaterialTagInfo() {
  221. getAction('/ctop/materialTagInfo/detail', {
  222. code: this.faceShowList.projectId ? this.faceShowList.code : this.faceShowList.videoCode
  223. }).then(res => {
  224. if (res.success) {
  225. this.departValueInfoModality = res.modalityTagList.map(item => item.tagId);
  226. this.departValueInfoMod = res.modTagList.map(item => item.tagId);
  227. this.departValueInfoSence = res.senceTagList.map(item => item.tagId);
  228. }
  229. });
  230. },
  231. // TODO 推送操作
  232. handlePushData(list) {
  233. const paramsData = {
  234. projectId: this.faceShowList.projectId, // 项目选择
  235. ascription: {
  236. planId: 'e9ca23d68d884d4ebb19d07889727dae',
  237. shotId: 'e9ca23d68d884d4ebb19d07889727dae',
  238. clipId: 'e9ca23d68d884d4ebb19d07889727dae'
  239. },
  240. status: '1',
  241. code: list.output_video_md5,
  242. url: list.output_video_url && list.output_video_url.split(':')[1],
  243. type: 'VIDEO',
  244. userId: this.userInfo().id,
  245. modalityTagList: this.departValueInfoModality, // 形式标签
  246. senceTagList: this.departValueInfoSence, // 场景标签
  247. modTagList: this.departValueInfoMod // 基调标签
  248. };
  249. const that = this;
  250. this.$confirm({
  251. title: '提示',
  252. content: '是否进行推送操作',
  253. onOk() {
  254. fileInsertV2(paramsData).then(res => {
  255. if (res.code === 0) {
  256. that.handlePolicyOperationCancel();
  257. that.$emit('sureface');
  258. that.$message.success('素材已经同步到未审核列表,请进入查看');
  259. }
  260. else {
  261. that.$message.error(res.message);
  262. }
  263. });
  264. }
  265. });
  266. },
  267. // 下载操作
  268. handleDownLoadData(list) {
  269. const randomTime = new Date().getTime() + '_check.mp4';
  270. fetch(list.output_video_url)
  271. .then(res => res.blob())
  272. .then(blob => {
  273. const a = document.createElement('a');
  274. document.body.appendChild(a);
  275. a.style.display = 'none';
  276. // 使用获取到的blob对象创建的url
  277. const url = window.URL.createObjectURL(blob);
  278. a.href = url;
  279. // 指定下载的文件名
  280. a.download = randomTime;
  281. a.click()
  282. document.body.removeChild(a);
  283. // 移除blob对象的url
  284. window.URL.revokeObjectURL(url);
  285. });
  286. },
  287. // 刷新按钮
  288. handleRefresh() {
  289. this.defaultCurrent = 1;
  290. this.handleInitTable();
  291. },
  292. checkTv(data) {
  293. const index = data.lastIndexOf(".");
  294. let tv_id = '';
  295. tv_id = data.substring(index);
  296. if(tv_id !== ".mp4" && tv_id !== ".rmvb" && tv_id !== ".avi" && tv_id !== ".ts") {
  297. return data + '.mp4';
  298. }
  299. else {
  300. return data;
  301. }
  302. },
  303. // 确定按钮
  304. handlePolicyOperationSure() {
  305. if (this.faceType === 'a') {
  306. this.confirmLoading = true;
  307. if (!this.md5Result) {
  308. this.$message.error('请上传图片');
  309. return;
  310. }
  311. const paramsData = {
  312. inputVideoUrl: this.checkTv(this.faceShowList.url),
  313. inputImageUrl: this.urlResult,
  314. videoMd5: this.faceShowList.projectId ? this.faceShowList.code : this.faceShowList.videoCode,
  315. imageMd5: this.md5Result,
  316. createBy: this.userInfo().id
  317. };
  318. postAction('http://faceswap.tjyourong.com.cn/jeecg-boot/task/single', paramsData).then(result => {
  319. if (result.code === 0) {
  320. this.handlePolicyOperationCancel();
  321. this.$emit('sureface');
  322. this.$message.success('图片上传成功');l
  323. this.confirmLoading = false;
  324. }
  325. else {
  326. this.$message.success('目标图片已存在,请下载视频');
  327. this.faceType = 'b';
  328. this.handleInitTable();
  329. this.handleGetMaterialTagInfo();
  330. this.confirmLoading = false;
  331. }
  332. }).catch(error => {
  333. this.confirmLoading = false;
  334. });
  335. }
  336. else {
  337. this.handlePolicyOperationCancel();
  338. }
  339. },
  340. // 取消按钮
  341. handlePolicyOperationCancel() {
  342. this.faceType = 'a';
  343. this.imageUrl = '';
  344. this.urlResult = '';
  345. this.md5Result = '';
  346. this.defaultCurrent = 1;
  347. this.$emit('closeface');
  348. },
  349. getBase64(img, callback) {
  350. const reader = new FileReader();
  351. reader.addEventListener('load', () => callback(reader.result));
  352. reader.readAsDataURL(img);
  353. },
  354. beforeUpload(info) {
  355. const isZip = info.type.includes('png') || info.type.includes('jpeg');
  356. if (!isZip) {
  357. this.$message.error('请上传正确类型的图片');
  358. this.imageUrl = '';
  359. this.urlResult = '';
  360. this.md5Result = '';
  361. return;
  362. }
  363. new Promise(function (resolve, reject) {
  364. let width = 500;
  365. let height = 500;
  366. let _URL = window.URL || window.webkitURL;
  367. let img = new Image();
  368. img.onload = function () {
  369. let valid = img.width >= width && img.height >= height;
  370. valid ? resolve() : reject();
  371. };
  372. img.src = _URL.createObjectURL(info);
  373. }).then(() => {
  374. this.handleMd5(info);
  375. this.cosUpload(info);
  376. this.getBase64(info, imageUrl => {
  377. this.imageUrl = imageUrl;
  378. });
  379. }, () => {
  380. this.$message.error('图片尺寸不能小于500*500');
  381. this.imageUrl = '';
  382. this.urlResult = '';
  383. this.md5Result = '';
  384. return Promise.reject();
  385. });
  386. },
  387. handleMd5(file) {
  388. let bmf = new BMF();
  389. let that = this;
  390. return new Promise(function (resolve, reject) {
  391. bmf.md5(file, (err, md5) => {
  392. that.md5Result = md5;
  393. });
  394. });
  395. },
  396. cosUpload(file) {
  397. let that = this;
  398. let date = new Date();
  399. let y = date.getFullYear();
  400. let MM = date.getMonth() + 1;
  401. MM = MM < 10 ? '0' + MM : MM;
  402. let d = date.getDate();
  403. d = d < 10 ? '0' + d : d;
  404. let timeElse = new Date().getTime();
  405. let arr = file.name.split('.');
  406. let str = '';
  407. for (let i = 0; i < arr.length; i++) {
  408. if (i === arr.length - 1) {
  409. }
  410. else {
  411. if (i === arr.length - 2) {
  412. str += arr[i];
  413. }
  414. else {
  415. str += arr[i] + '.';
  416. }
  417. }
  418. }
  419. cos.putObject(
  420. {
  421. Bucket: 'media-1301855440',
  422. Region: 'ap-chongqing',
  423. // 存储桶所在地域,必须字段
  424. Key: that.uploadType + '/' + y + '-' + MM + '-' + d + '/' + str + '-' + timeElse + '.' + arr[arr.length - 1],
  425. StorageClass: 'STANDARD',
  426. Body: file // 上传文件对象
  427. },
  428. function (err, data) {
  429. if (err) {
  430. that.$message.error('上传失败!!!' + err);
  431. return;
  432. }
  433. that.loadingElse = false;
  434. that.urlResult = 'http://' + data.Location;
  435. }
  436. );
  437. }
  438. }
  439. }
  440. </script>
  441. <style lang="less">
  442. @import "change-face";
  443. </style>