change-face.vue 16 KB

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