detail.component.ts 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408
  1. import { HttpErrorResponse } from '@angular/common/http';
  2. import { Component, OnInit, TemplateRef, ViewContainerRef } from '@angular/core';
  3. import { ActivatedRoute, Router } from '@angular/router';
  4. import { NzMessageService } from 'ng-zorro-antd/message';
  5. import { NzModalRef, NzModalService } from 'ng-zorro-antd/modal';
  6. import { NzUploadFile } from 'ng-zorro-antd/upload';
  7. import { delay, firstValueFrom, Observable, of } from 'rxjs';
  8. import { ArtStatus } from './art-status.component';
  9. import { ArtService } from './art.service';
  10. import { ArtRefuseReasonComponent } from './art-refuse-reason.component';
  11. import { NzImageService } from 'ng-zorro-antd/image';
  12. import { ScoreDetailComponent } from '../score/score-detail.component';
  13. @Component({
  14. selector: 'art-detail',
  15. templateUrl: './detail.component.html',
  16. styles: [
  17. `
  18. .thumb{
  19. border:1px solid #999;
  20. }
  21. .clickable{
  22. cursor: pointer;
  23. }
  24. div.thumb-item{
  25. width:100%;
  26. aspect-ratio: 1 / 1;
  27. overflow:hidden;
  28. background-color:#fefefe;
  29. }
  30. .trash{
  31. background: transparent;
  32. position: absolute;
  33. left: 50%;
  34. top: 50%;
  35. transform: translate(-50%);
  36. }
  37. img.thumb{
  38. height:100%;
  39. }
  40. :host ::ng-deep .thumb-uploader > .ant-upload {
  41. width: 100%;
  42. height:100%;
  43. aspect-ratio: 1 / 1;
  44. }
  45. `
  46. ]
  47. })
  48. export class DetailComponent implements OnInit {
  49. id: string;
  50. art: any;
  51. aiPrompt: string;
  52. headers: any;
  53. headerHash: any;
  54. constructor(
  55. public artService: ArtService,
  56. private activatedRoute: ActivatedRoute,
  57. public message: NzMessageService,
  58. public modal: NzModalService,
  59. private nzImageService: NzImageService,
  60. private router: Router,
  61. private viewContainerRef: ViewContainerRef,
  62. ) {
  63. this.id = this.activatedRoute.snapshot.params['id'];
  64. this.loadData();
  65. this.artService.getHeaders()
  66. .subscribe(resp => {
  67. this.headers = resp;
  68. this.headerHash = this.headers.reduce((hash, cur) => {
  69. hash[cur.data] = cur;
  70. return hash;
  71. }, {});
  72. })
  73. }
  74. error: HttpErrorResponse;
  75. isLoading: boolean = false;
  76. loadData() {
  77. this.isLoading = true;
  78. this.artService.get(this.id).subscribe({
  79. next: (resp: any) => { this.art = resp; this.aiPrompt = this.art.aiPrompt; this.error = null; },
  80. error: (err) => { this.error = err; this.isLoading = false; },
  81. complete: () => { this.isLoading = false }
  82. })
  83. }
  84. ngOnInit(): void {
  85. this.activatedRoute.params.subscribe(params => {
  86. let id = params['id'];
  87. if (this.id != id) {
  88. this.id = id;
  89. this.loadData();
  90. }
  91. })
  92. }
  93. title(key: string): any {
  94. return this.headerHash && this.headerHash[key] ? this.headerHash[key]?.title || key : key;
  95. }
  96. drop() { return this.artService.drop(this.id, 'xx') }
  97. recover() { return this.artService.recover(this.id) }
  98. deleteSpecial() { return this.artService.deleteSpecial(this.id) }
  99. lock() { return this.artService.lock(this.id, true) }
  100. unlock() { return this.artService.lock(this.id, false) }
  101. selftest() { return this.artService.selftest(this.id); }
  102. refuse() {
  103. const modalRef = this.modal.create({
  104. nzTitle: $localize`确认拒稿么?`,
  105. nzFooter: null,
  106. nzContent: ArtRefuseReasonComponent,
  107. });
  108. modalRef.getContentComponent().onSuccess.subscribe(reason => {
  109. modalRef.close();
  110. firstValueFrom(this.artService.refuse(this.id, reason))
  111. .then((resp: any) => {
  112. this.loadData();
  113. }).catch(err => {
  114. this.message.error(err.error?.message || err.message);
  115. });
  116. })
  117. }
  118. artStatus = ArtStatus;
  119. getStatusOperation(status: number) {
  120. return () => {
  121. return this.artService.status(this.id, status);
  122. }
  123. }
  124. // selftest() {
  125. // return () => {
  126. // return this.artService.selftest(this.id);
  127. // }
  128. // }
  129. // selftestModal: NzModalRef;
  130. // onSelfTestDoing(isloading) {
  131. // if (isloading) {
  132. // this.selftestModal = this.modal.info({
  133. // nzTitle: $localize`提交自测`,
  134. // nzContent: $localize`正在生成测试数据,请稍候...`,
  135. // nzOkText: null,
  136. // });
  137. // }
  138. // }
  139. // onSelfTestSuccess() {
  140. // if (this.selftestModal) {
  141. // this.selftestModal.close();
  142. // }
  143. // this.modal.success({
  144. // nzTitle: $localize`提交自测`,
  145. // nzContent: $localize`生成测试数据成功,可以在APP进行测试`,
  146. // });
  147. // }
  148. // onSelfTestFailed() {
  149. // if (this.selftestModal) {
  150. // this.selftestModal.close();
  151. // }
  152. // this.modal.error({
  153. // nzTitle: $localize`提交自测`,
  154. // nzContent: $localize`生成测试数据失败,请检查`,
  155. // });
  156. // }
  157. onMarkPreview() {
  158. const images = [{ src: `/napi/web/art/${this.art.id}/bin/markImg` }];
  159. this.nzImageService.preview(images);
  160. }
  161. modalTitle(key: string) {
  162. let title = '';
  163. switch (key) {
  164. case 'meta':
  165. title = $localize`作品信息`;
  166. break;
  167. default:
  168. break;
  169. }
  170. return title;
  171. }
  172. openModal(content: TemplateRef<any>, title: string) {
  173. this.modal.create({
  174. nzContent: content,
  175. nzTitle: title,
  176. nzFooter: null,
  177. });
  178. }
  179. canView(bin): boolean {
  180. if (!this.art) return false;
  181. if (this.art.auth.isPageAdmin) return true;
  182. else if (['page', 'raw', 'special', 'work', 'rawColored', 'svgv1', 'svgall', 'svgv2'].includes(bin.type)) return true;
  183. else return false;
  184. }
  185. getOperation(): Observable<any> {
  186. return of(1).pipe(delay(2000));
  187. //return new Observable(sub => {
  188. // setTimeout(() => {
  189. // sub.error(new Error('test error'))
  190. // }, 2000);
  191. //})
  192. }
  193. onClickTag(tag) {
  194. let filters = JSON.stringify({ tags: [tag] });
  195. this.router.navigateByUrl(`/pages/all?filters=${filters}`)
  196. }
  197. onClickEpg(epg) {
  198. let filters = JSON.stringify({ epgs: [epg] });
  199. this.router.navigateByUrl(`/pages/all?filters=${filters}`)
  200. }
  201. onClickUser(user) {
  202. let filters = JSON.stringify({ user: [user] });
  203. this.router.navigateByUrl(`/pages/all?filters=${filters}`)
  204. }
  205. // 点击查看评分详情
  206. onClickScoreInfo(art: any) {
  207. const modal = this.modal.create({
  208. // nzTitle: $localize`评分详情`,
  209. nzCentered: true,
  210. nzWidth: 800,
  211. nzFooter: null,
  212. nzContent: ScoreDetailComponent,
  213. nzViewContainerRef: this.viewContainerRef,
  214. nzComponentParams: {
  215. art,
  216. },
  217. });
  218. modal.getContentComponent().onScoreSuccess.subscribe(e => {
  219. this.loadData();
  220. })
  221. }
  222. //////////////////////// special thumb upload ////////////////////////
  223. uploading: boolean = false;
  224. beforeSpecialThumbUpload = (file: NzUploadFile): boolean => {
  225. const isJpgOrPng = file.type === 'image/jpeg' || file.type === 'image/png';
  226. if (!isJpgOrPng) {
  227. this.message.error($localize`不支持的图片格式`);
  228. return false;
  229. }
  230. const isLt2M = file.size! / 1024 / 1024 < 2;
  231. if (!isLt2M) {
  232. this.message.error($localize`图片大小超过限制`);
  233. return false;
  234. }
  235. this.handleSpecailThumbUpload(file);
  236. return false;
  237. };
  238. handleSpecailThumbUpload(file: NzUploadFile): void {
  239. console.log(file);
  240. const formData = new FormData();
  241. formData.set("specialThumb", file as unknown as File);
  242. // formData.append('files[]', file);
  243. formData.set("workId", this.art._id);
  244. this.uploading = true;
  245. firstValueFrom(this.artService.uploadSpecialThumb(formData))
  246. .then((resp: any) => {
  247. this.uploading = false;
  248. this.loadData();
  249. }).catch(err => {
  250. this.uploading = false;
  251. this.message.error(err.error?.message || err.message);
  252. });
  253. }
  254. // 删除上传缩略图
  255. removeSpecialThumb() {
  256. firstValueFrom(this.artService.deleteSpecialThumb(this.art._id))
  257. .then((resp: any) => {
  258. this.loadData();
  259. }).catch(err => {
  260. this.message.error(err.error?.message || err.message);
  261. });
  262. }
  263. // 切换special缩略图
  264. onSpecialThumbChange(value: number) {
  265. firstValueFrom(this.artService.useSpecialThumb(this.art._id, value))
  266. .then((resp: any) => {
  267. this.loadData();
  268. }).catch(err => {
  269. this.message.error(err.error?.message || err.message);
  270. });
  271. }
  272. // 设置神秘图开关
  273. onMysteryChange(value: boolean) {
  274. firstValueFrom(this.artService.setMystery(this.art._id, value))
  275. .then((resp: any) => {
  276. this.loadData();
  277. }).catch(err => {
  278. this.message.error(err.error?.message || err.message);
  279. });
  280. }
  281. // 设置AI参考图开关
  282. onAiChange(value: boolean) {
  283. firstValueFrom(this.artService.setAi(this.art._id, value))
  284. .then((resp: any) => {
  285. this.loadData();
  286. }).catch(err => {
  287. this.message.error(err.error?.message || err.message);
  288. });
  289. }
  290. // 删除AI参考图
  291. removeAiImage() {
  292. firstValueFrom(this.artService.deleteAiImage(this.art._id))
  293. .then((resp: any) => {
  294. this.loadData();
  295. }).catch(err => {
  296. this.message.error(err.error?.message || err.message);
  297. });
  298. }
  299. // 更新AI关键字
  300. updateAiPrompt() {
  301. if (this.aiPrompt === undefined || this.aiPrompt == this.art.aiPrompt) return; // 没有变更
  302. firstValueFrom(this.artService.setAiPrompt(this.art._id, this.aiPrompt))
  303. .then(resp => this.loadData())
  304. .catch(err => this.message.error(err.error?.message || err.message))
  305. }
  306. cancelAiPrompt() {
  307. this.aiPrompt = this.art.aiPrompt;
  308. }
  309. // 设置AI参考图开关
  310. onUseChange(value: string) {
  311. firstValueFrom(this.artService.setUse(this.art._id, value))
  312. .then((resp: any) => {
  313. this.loadData();
  314. }).catch(err => {
  315. this.message.error(err.error?.message || err.message);
  316. });
  317. }
  318. //////////////////////// ai image upload ////////////////////////
  319. beforeAiUpload = (file: NzUploadFile): boolean => {
  320. const isJpgOrPng = file.type === 'image/jpeg' || file.type === 'image/png';
  321. if (!isJpgOrPng) {
  322. this.message.error($localize`不支持的图片格式`);
  323. return false;
  324. }
  325. const isLt5M = file.size! / 1024 / 1024 < 5;
  326. if (!isLt5M) {
  327. this.message.error($localize`图片大小超过限制`);
  328. return false;
  329. }
  330. this.handleAiUpload(file);
  331. return false;
  332. };
  333. handleAiUpload(file: NzUploadFile): void {
  334. console.log(file);
  335. const formData = new FormData();
  336. formData.set("aiImage", file as unknown as File);
  337. // formData.append('files[]', file);
  338. formData.set("workId", this.art._id);
  339. this.uploading = true;
  340. firstValueFrom(this.artService.uploadAiImage(formData))
  341. .then((resp: any) => {
  342. this.uploading = false;
  343. this.loadData();
  344. }).catch(err => {
  345. this.uploading = false;
  346. this.message.error(err.error?.message || err.message);
  347. });
  348. }
  349. }