guoziyun hace 1 año
padre
commit
1677d2b9c4
Se han modificado 40 ficheros con 71 adiciones y 5601 borrados
  1. 2 0
      app.js
  2. 1 0
      libs/utils/index.js
  3. 41 0
      libs/utils/thumb.js
  4. 9 0
      models/schema-art.js
  5. 1 1
      routes/napi/web/art.js
  6. 0 1
      zorro/src/app/app-routing.module.ts
  7. 0 2
      zorro/src/app/pages/art/art-routing.module.ts
  8. 0 2
      zorro/src/app/pages/art/art.module.ts
  9. 0 114
      zorro/src/app/pages/epg/column-add.component.ts
  10. 0 78
      zorro/src/app/pages/epg/epg-add.component.ts
  11. 0 132
      zorro/src/app/pages/epg/epg-art-card.component.ts
  12. 0 85
      zorro/src/app/pages/epg/epg-clone.component.ts
  13. 0 497
      zorro/src/app/pages/epg/epg-content-onshelf.component.ts
  14. 0 480
      zorro/src/app/pages/epg/epg-content-pager.component.ts
  15. 0 44
      zorro/src/app/pages/epg/epg-content-status.component.ts
  16. 0 400
      zorro/src/app/pages/epg/epg-content-unshelf.component.ts
  17. 0 349
      zorro/src/app/pages/epg/epg-content.component.ts
  18. 0 150
      zorro/src/app/pages/epg/epg-info.component.ts
  19. 0 468
      zorro/src/app/pages/epg/epg-list.component.ts
  20. 0 209
      zorro/src/app/pages/epg/epg-preview.component.ts
  21. 0 86
      zorro/src/app/pages/epg/epg-publish-confirm.component.ts
  22. 0 21
      zorro/src/app/pages/epg/epg-routing.module.ts
  23. 0 120
      zorro/src/app/pages/epg/epg.module.ts
  24. 0 82
      zorro/src/app/pages/epg/epg.service.ts
  25. 0 72
      zorro/src/app/pages/epg/order-edit.component.ts
  26. 0 117
      zorro/src/app/pages/page/art-row.component.ts
  27. 17 240
      zorro/src/app/pages/page/detail.component.html
  28. 0 74
      zorro/src/app/pages/page/detail.component.ts
  29. 0 69
      zorro/src/app/pages/page/drag-drop.component.ts
  30. 0 6
      zorro/src/app/pages/page/list.component.ts
  31. 0 8
      zorro/src/app/pages/page/page-routing.module.ts
  32. 0 20
      zorro/src/app/pages/page/page.module.ts
  33. 0 128
      zorro/src/app/pages/page/publish-choose.component.ts
  34. 0 157
      zorro/src/app/pages/page/publish-confirm.component.ts
  35. 0 562
      zorro/src/app/pages/page/publish-detail.component.ts
  36. 0 90
      zorro/src/app/pages/page/publish-schedule-add.component.ts
  37. 0 110
      zorro/src/app/pages/page/publish-schedule-cell.component.ts
  38. 0 336
      zorro/src/app/pages/page/publish-schedule-edit.component.ts
  39. 0 265
      zorro/src/app/pages/page/publish-schedules.component.ts
  40. 0 26
      zorro/src/app/pages/page/test.component.ts

+ 2 - 0
app.js

@@ -43,6 +43,8 @@ app.use(bodyParser.urlencoded({
 app.use('/napi/web/auth', require('./routes/napi/web/auth'));
 app.use('/napi/web/menu', authChecker.checkLogin, require('./routes/napi/web/menu'));
 app.use('/napi/web/art', authChecker.checkLogin, require('./routes/napi/web/art'));
+app.use('/napi/web/user', authChecker.checkLogin, require('./routes/napi/web/user'));
+app.use('/napi/web/role', authChecker.checkLogin, require('./routes/napi/web/role'));
 
 
 app.use('/thumbs/v1', require('./routes/res/thumbs'));

+ 1 - 0
libs/utils/index.js

@@ -3,4 +3,5 @@ module.exports = {
   pager: require('./pager'),
   session: require('./session'),
   lang: require('./lang'),
+  thumb: require('./thumb'),
 };

+ 41 - 0
libs/utils/thumb.js

@@ -0,0 +1,41 @@
+function pageThumb(id, size, lastMod) {
+  size = size || 320
+  let thumb = `/thumbs/v2/page/${size}/${id}.png`
+  if(lastMod) {
+    if (lastMod) thumb = `${thumb}?t=` + lastMod.getTime();
+  }
+  return thumb;
+}
+
+function workThumb(id, size, lastMod) {
+  size = size || 320
+  let thumb = `/thumbs/v2/work/${size}/${id}.png`
+  if (lastMod) thumb = `${thumb}?t=` + lastMod.getTime();
+  return thumb;
+}
+
+function specialThumb(id, size, version) {
+  size = size || 320
+  let thumb = `/thumbs/v2/special/${size}/${id}.png`
+  if (version) thumb = `${thumb}?v=` + version;
+  return thumb;
+}
+
+function specialOutlineThumb(id, size, version) {
+  size = size || 320
+  let thumb = `/thumbs/v2/special_outline/${size}/${id}.png`
+  if (version) thumb = `${thumb}?v=` + version;
+  return thumb;
+}
+
+function zipPath(workId) {
+  return  `/zips/v2/number/1500/${workId}.zip`
+}
+
+module.exports = {
+  pageThumb,
+  workThumb,
+  specialThumb, 
+  specialOutlineThumb,
+  zipPath,
+}

+ 9 - 0
models/schema-art.js

@@ -129,6 +129,15 @@ function artTransform(doc, ret) {
   } else if (doc.work) {
     ret.thumb = `${config.resHost}/thumbs/v2/work/320/${doc._id}.png?t=${doc.lastMod.getTime()}`
   }
+
+  ret.binHash = {
+    "page": `${config.resHost}/thumbs/v2/page/640/${doc._id}.png?t=${doc.lastMod.getTime()}`,
+    "work": doc.work ? `${config.resHost}/thumbs/v2/work/640/${doc._id}.png?t=${doc.lastMod.getTime()}` : undefined,
+    "special": doc.special ? `${config.resHost}/thumbs/v2/special/640/${doc._id}.png?t=${doc.lastMod.getTime()}` : undefined,
+    "specialThumb": doc.specialThumb ? `${config.resHost}/thumbs/v2/special_thumb/640/${doc._id}.png?t=${doc.lastMod.getTime()}` : undefined,
+    "specialOutline": doc.specialOutline ? `${config.resHost}/thumbs/v2/special_outline/640/${doc._id}.png?t=${doc.lastMod.getTime()}` : undefined,
+    "aiImage": doc.aiImage ? `${config.resHost}/thumbs/v2/ai_image/640/${doc._id}.png?t=${doc.lastMod.getTime()}` : undefined,
+  }
 }
 
 artSchema.index({

+ 1 - 1
routes/napi/web/art.js

@@ -95,7 +95,7 @@ router.get('/:id', auth.need('read:own', 'art'), function (req, res, next) {
     utils.validators.validateId(id);
     /**@type {Query} */
     let query = models.Art.findOne({ $or: [{ _id: id }, { pageId: id }] });
-    query.populate({ path: 'bins', select: '-data' });
+    // query.populate({ path: 'bins', select: '-data' });
     artUserPopulates.forEach(p => {
       query.populate(p);
     })

+ 0 - 1
zorro/src/app/app-routing.module.ts

@@ -19,7 +19,6 @@ const routes: Routes = [
       { path: 'sys', loadChildren: () => import('./pages/sys/sys.module').then(m => m.SysModule) },
       { path: 'system', loadChildren: () => import('./pages/sys/sys.module').then(m => m.SysModule) },
       { path: 'content', loadChildren: () => import('./pages/content/content.module').then(m => m.ContentModule) },
-      { path: 'epg', loadChildren: () => import('./pages/epg/epg.module').then(m => m.EpgModule) },
       { path: 'coloring', loadChildren: () => import('./projs/coloring/coloring.module').then(m => m.ColoringModule) },
     ],
   }

+ 0 - 2
zorro/src/app/pages/art/art-routing.module.ts

@@ -5,7 +5,6 @@ import { CanDeactivateGuard, PlayComponent } from './play.component';
 import { CheckerComponent } from './checker.component';
 import { PotraceComponent } from './potrace.component';
 import { ColorMergeComponent } from './color-merge.component';
-import { TestComponent } from './test.component';
 import { SvgComponent } from './svg.component';
 import { PrecheckerComponent } from './prechecker.component';
 import { SegmentComponent } from './segment.component';
@@ -20,7 +19,6 @@ const routes: Routes = [{
     { path: 'svg/:id', component: SvgComponent, canDeactivate: [] },
     { path: 'svg2/:id', component: Svg2Component, canDeactivate: [] },
     { path: 'svg3/:id', component: Svg3Component, canDeactivate: [] },
-    { path: 'test', component: TestComponent, canDeactivate: [] },
     { path: 'color-merge/:id', component: ColorMergeComponent, canDeactivate: [] },
     { path: 'potrace', component: PotraceComponent, canDeactivate: [] },
     { path: 'checker', component: CheckerComponent, canDeactivate: [] },

+ 0 - 2
zorro/src/app/pages/art/art.module.ts

@@ -23,7 +23,6 @@ import { ColorPickerComponent } from './color-picker.component';
 
 import { DragDropModule } from '@angular/cdk/drag-drop';
 import { NzIconModule } from 'ng-zorro-antd/icon';
-import { TestComponent } from './test.component';
 import { NzButtonModule } from 'ng-zorro-antd/button';
 import { NzSwitchModule } from 'ng-zorro-antd/switch';
 import { NzCheckboxModule } from 'ng-zorro-antd/checkbox';
@@ -52,7 +51,6 @@ import { Svg3Component } from './svg3.component';
     PlayUpdateComponent,
     ColorMergeComponent,
     ColorPickerComponent,
-    TestComponent,
     SvgComponent,
     PrecheckerComponent,
     PrecheckerUploadComponent,

+ 0 - 114
zorro/src/app/pages/epg/column-add.component.ts

@@ -1,114 +0,0 @@
-import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
-import { FormBuilder, FormGroup, Validators } from '@angular/forms';
-import { NzMessageService } from 'ng-zorro-antd/message';
-import { firstValueFrom } from 'rxjs';
-import { FormUtils } from 'src/app/lib/utils/form-utils';
-import { ContentService } from '../content/content.service';
-import { EpgService } from './epg.service';
-
-@Component({
-  selector: 'app-column-add',
-  template: `
-
-<form [formGroup]="form" autocomplete="off" (ngSubmit)="onSubmit()" novalidate class="form-horizontal ">
-  <nz-form-item>
-    <nz-form-control i18n-nzErrorTip nzErrorTip="请输入栏目id">
-      <nz-input-group nzPrefixIcon="user">
-        <input nz-input formControlName="id" i18n-placeholder placeholder="栏目id">
-      </nz-input-group>
-    </nz-form-control>
-  </nz-form-item>
-
-  <nz-form-item>
-  <nz-form-control i18n-nzErrorTip nzErrorTip="请选择多语翻译">
-    <nz-input-group nzPrefixIcon="global">
-      <nz-select [(ngModel)]="translate" formControlName="translate" i18n-placeholder placeholder="请选择多语翻译">
-        <nz-option *ngFor="let item of translateOptions; let i=index" [nzValue]="item.value" [nzLabel]="item.label"></nz-option>
-      </nz-select>
-    </nz-input-group>
-    </nz-form-control>
-  </nz-form-item>
-
-
-  <!-- <nz-form-item>
-    <nz-form-control i18n-nzErrorTip nzErrorTip="请输入栏目英文名">
-      <nz-input-group nzPrefixIcon="user">
-        <input nz-input formControlName="en" placeholder="英文名">
-      </nz-input-group>
-    </nz-form-control>
-  </nz-form-item>
-
-  <nz-form-item>
-    <nz-form-control i18n-nzErrorTip nzErrorTip="请输入栏目中文名">
-      <nz-input-group nzPrefixIcon="user">
-        <input nz-input formControlName="zh" placeholder="中文名">
-      </nz-input-group>
-    </nz-form-control>
-  </nz-form-item> -->
-
-  <div class="d-flex flex-row-reverse">
-    <button nz-button nzType="primary" nzBlock type="submit" [disabled]="isLoading" i18n>添加</button>
-  </div>
-</form>
-
-  `,
-  styles: [
-  ]
-})
-export class ColumnAddComponent implements OnInit {
-
-  @Input() epgId: string;
-
-  @Output() onSuccess = new EventEmitter<any>();
-  @Output() onError = new EventEmitter<any>();
-
-  form: FormGroup;
-  isLoading = false;
-
-  translateOptions: any[] = [];
-
-  translate: string; // 多语翻译ID
-
-  constructor(
-    private epgService: EpgService,
-    private contentService: ContentService,
-    private fb: FormBuilder,
-    private message: NzMessageService,
-  ) { 
-    firstValueFrom(this.contentService.translateOptions()).then((resp: any[]) => {
-      this.translateOptions = resp;
-    }) 
-  }
-
-  ngOnInit(): void {
-    this.form = this.fb.group({
-      id: [null, [Validators.required]],
-      visible: [false, [Validators.required]],
-      translate: [null, [Validators.required]],
-      // en: [null, [Validators.required]],
-      // zh: [null, [Validators.required]],
-    })
-  }
-
-  
-  onSubmit() {
-    if (this.form.invalid) {
-      FormUtils.markAsDirty(this.form);
-      return;
-    }
-    this.isLoading = true;
-    Object.assign(this.form.value, { _id: this.epgId });
-    firstValueFrom(this.epgService.columnAdd(this.form.value))
-      .then(res => {
-        this.onSuccess.emit(this.form.value);
-        this.message.success($localize`添加成功`);
-      })
-      .catch(err => {
-        this.message.error(err.error?.message || err.message);
-        this.onError.emit(err);
-      }).then(() => {
-        this.isLoading = false;
-      })
-  }
-
-}

+ 0 - 78
zorro/src/app/pages/epg/epg-add.component.ts

@@ -1,78 +0,0 @@
-import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
-import { FormBuilder, FormGroup, Validators } from '@angular/forms';
-import { NzMessageService } from 'ng-zorro-antd/message';
-import { firstValueFrom } from 'rxjs';
-import { FormUtils } from 'src/app/lib/utils/form-utils';
-import { EpgService } from './epg.service';
-
-@Component({
-  selector: 'app-epg-add',
-  template: `
-
-<form [formGroup]="form" autocomplete="off" (ngSubmit)="onSubmit()" novalidate class="form-horizontal ">
-  <nz-form-item>
-    <nz-form-control i18n-nzErrorTip nzErrorTip="请输入epgId">
-      <nz-input-group nzPrefixIcon="user">
-        <input nz-input formControlName="epgId" placeholder="EPG id">
-      </nz-input-group>
-    </nz-form-control>
-  </nz-form-item>
-
-  <nz-form-item>
-    <nz-form-control i18n-nzErrorTip nzErrorTip="请输入描述信息">
-      <nz-input-group nzPrefixIcon="user">
-        <input nz-input formControlName="desc" i18n-placeholder placeholder="描述">
-      </nz-input-group>
-    </nz-form-control>
-  </nz-form-item>
-
-  <div class="d-flex flex-row-reverse">
-    <button nz-button nzType="primary" nzBlock type="submit" [disabled]="isLoading" i18n>添加</button>
-  </div>
-</form>
-
-  `,
-  styles: [
-  ]
-})
-export class EpgAddComponent implements OnInit {
-  @Output() onSuccess = new EventEmitter<any>();
-  @Output() onError = new EventEmitter<any>();
-
-  form: FormGroup;
-  isLoading = false;
-
-  constructor(
-    private epgService: EpgService,
-    private fb: FormBuilder,
-    private message: NzMessageService,
-  ) { }
-
-  ngOnInit(): void {
-    this.form = this.fb.group({
-      epgId: [null, [Validators.required]],
-      desc: [null],
-    })
-  }
-
-  
-  onSubmit() {
-    if (this.form.invalid) {
-      FormUtils.markAsDirty(this.form);
-      return;
-    }
-    this.isLoading = true;
-    firstValueFrom(this.epgService.epgAdd(this.form.value))
-      .then(res => {
-        this.onSuccess.emit(this.form.value);
-        this.message.success($localize`添加成功, 请继续完善栏目信息`);
-      })
-      .catch(err => {
-        this.message.error(err.error?.message || err.message);
-        this.onError.emit(err);
-      }).then(() => {
-        this.isLoading = false;
-      })
-  }
-
-}

+ 0 - 132
zorro/src/app/pages/epg/epg-art-card.component.ts

@@ -1,132 +0,0 @@
-import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
-
-@Component({
-  selector: 'app-epg-art-card',
-  template: `
-  <div class="art-card rounded d-flex flex-column text-secondary " [ngClass]="{'select': select}" >
-    <div class="img d-flex  flex-column align-items-center position-relative" (click)="onClick()">
-      <img [src]="item?.thumb" loading="lazy" />
-    </div>
-
-    <div class="p-1">
-  
-      <div class="d-flex flex-row flex-nowrap">
-        <art-status [showText]="false" [data]="item"></art-status>
-        <span>{{item.name}}</span>
-      </div>
-
-      <div class="d-flex flex-row justify-content-between flex-nowrap">
-        <div><ng-container i18n>色块</ng-container>: {{item.coloredAreaCount}}/<span style="font-weight: bold; font-size: 1em;">{{item.areaCount}}</span></div>
-        <nz-tag *ngIf="item.use == 'normal'" nzColor="blue" class="clickable" (click)="useClick.emit(item.use)" i18n>日常</nz-tag>
-        <nz-tag *ngIf="item.use == 'daily'" nzColor="purple" class="clickable" (click)="useClick.emit(item.use)" i18n>每日</nz-tag>
-        <nz-tag *ngIf="item.use == 'album'" nzColor="volcano" class="clickable" (click)="useClick.emit(item.use)" i18n>专辑</nz-tag>
-        <nz-tag *ngIf="item.use == 'collection'" nzColor="orange" class="clickable" (click)="useClick.emit(item.use)" i18n>合集</nz-tag>
-        <nz-tag *ngIf="item.use != 'normal' && item.use != 'daily' && item.use != 'album' && item.use != 'collection'" nzColor="default" class="clickable" (click)="useClick.emit(item.use)">{{item.use}}</nz-tag>
-      </div>
-
-      <div class="d-flex flex-row align-items-center">
-        <nz-tag *ngIf="!hideAuthor" (click)="authorClick.emit(item.user._id)" class="clickable" >
-          {{item.user?.username}} 
-        </nz-tag>
-        <span i18n>于:</span>
-        <span>
-          {{item.date  | dateFormat}}
-        </span>
-      </div>
-
-      <div *ngIf="item.status == 9000">
-        <ng-container i18n>上线时间</ng-container>: {{item.publishTime | dateFormat  }}
-      </div>
-
-      <div>
-        <nz-tag *ngIf="item?.lock" nzColor="magenta"> <i class="fal fa-lock"></i> </nz-tag>
-        <ng-container *ngIf="item.tags">
-          <nz-tag *ngFor="let tag of item.tags" class="clickable" nzColor="default" (click)="tagClick.emit(tag)" [innerHTML]="tag"></nz-tag>
-        </ng-container>
-      </div>
-    </div>
-
-  </div>
-
-  `,
-  styles: [
-
-    `
-
-:host {
-  background-color:#fff;
-  overflow-x:hidden;
-}
-
-.clickable {
-  cursor:pointer;
-}
-
-.art-card{
-  position:relative;
-}
-.art-card:hover{
-  /*box-shadow: 0 .125rem .25rem rgba(0,0,0,.075)!important*/
-  box-shadow:0  0 .7rem rgba(0,0,0,.15)!important
-}
-.select {
-  background: #dcdcdc;
-  border-style: dashed;
-  border-width: 2px;
-  border-color: blue;
-}
-.status{
-  position:absolute;
-  left:-1px;
-  top:-1px;
-}
-.size{
-  position:absolute;
-  top:-1px;
-  right:-1px;
-  margin:0;
-}
-
-div.img{
-  width:100%;
-  aspect-ratio: 1 / 1;
-  overflow:hidden;
-  background-color:#fefefe;
-}
-
-img{
-  height:100%;
-  cursor : pointer;
-}
-
-    `
-  ]
-})
-
-
-export class EpgArtCardComponent implements OnInit {
-
-  @Input('data') item: any;
-  @Input() hideAuthor: boolean = false;
-  @Output() itemClick = new EventEmitter<any>();
-  @Output() tagClick = new EventEmitter<string>();
-  @Output() useClick = new EventEmitter<string>();
-  @Output() authorClick = new EventEmitter<string>();
-
-  select: boolean = false;
-
-  constructor() { }
-
-  ngOnInit(): void {
-  }
-
-  onClick() {
-    this.select = !this.select;
-    this.itemClick.emit([this.item, this.select]);
-  }
-
-}
-
-export interface Art {
-  thumb: string,
-}

+ 0 - 85
zorro/src/app/pages/epg/epg-clone.component.ts

@@ -1,85 +0,0 @@
-import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
-import { FormBuilder, FormGroup, Validators } from '@angular/forms';
-import { NzMessageService } from 'ng-zorro-antd/message';
-import { firstValueFrom } from 'rxjs';
-import { FormUtils } from 'src/app/lib/utils/form-utils';
-import { EpgService } from './epg.service';
-
-@Component({
-  selector: 'app-epg-copy',
-  template: `
-
-<div style="color: orange; margin-bottom: 10px">
-  <i nz-icon nzType="info-circle" nzTheme="outline"></i>
-  <ng-container i18n>克隆源: {{srcEpg.epgId}},  请输入新的EPG信息: </ng-container>
-</div>
-
-<form [formGroup]="form" autocomplete="off" (ngSubmit)="onSubmit()" novalidate class="form-horizontal ">
-  <nz-form-item>
-    <nz-form-control i18n-nzErrorTip nzErrorTip="请输入新epgId">
-      <nz-input-group nzPrefixIcon="user">
-        <input nz-input formControlName="epgId" i18n-placeholder placeholder="新 EPG id">
-      </nz-input-group>
-    </nz-form-control>
-  </nz-form-item>
-
-  <nz-form-item>
-    <nz-form-control i18n-nzErrorTip nzErrorTip="请输入描述信息">
-      <nz-input-group nzPrefixIcon="user">
-        <input nz-input formControlName="desc" i18n-placeholder placeholder="描述">
-      </nz-input-group>
-    </nz-form-control>
-  </nz-form-item>
-
-  <div class="d-flex flex-row-reverse">
-    <button nz-button nzType="primary" nzBlock type="submit" [disabled]="isLoading" i18n>拷贝</button>
-  </div>
-</form>
-
-  `,
-  styles: [
-  ]
-})
-export class EpgCloneComponent implements OnInit {
-  @Input() srcEpg: any;
-  @Output() onSuccess = new EventEmitter<any>();
-  @Output() onError = new EventEmitter<any>();
-
-  form: FormGroup;
-  isLoading = false;
-
-  constructor(
-    private epgService: EpgService,
-    private fb: FormBuilder,
-    private message: NzMessageService,
-  ) { }
-
-  ngOnInit(): void {
-    this.form = this.fb.group({
-      epgId: [null, [Validators.required]],
-      desc: [null],
-    })
-  }
-
-  
-  onSubmit() {
-    if (this.form.invalid) {
-      FormUtils.markAsDirty(this.form);
-      return;
-    }
-    this.isLoading = true;
-    let data = Object.assign({}, this.form.value, { columns: this.srcEpg.columns, status: this.srcEpg.status });
-    firstValueFrom(this.epgService.epgAdd(data))
-      .then(res => {
-        this.onSuccess.emit(data);
-        this.message.success($localize`添加成功, 请继续完善栏目信息`);
-      })
-      .catch(err => {
-        this.message.error(err.error?.message || err.message);
-        this.onError.emit(err);
-      }).then(() => {
-        this.isLoading = false;
-      })
-  }
-
-}

+ 0 - 497
zorro/src/app/pages/epg/epg-content-onshelf.component.ts

@@ -1,497 +0,0 @@
-import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core';
-import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop';
-import { NzMessageService } from 'ng-zorro-antd/message';
-import { firstValueFrom } from 'rxjs';
-import { FilterItem, FilterType } from 'src/app/lib/pager/pager-filter.component';
-import { EpgService } from './epg.service';
-import { Router } from '@angular/router';
-import { startOfDay } from 'date-fns';
-
-@Component({
-  selector: 'epg-content-onshelf',
-  template: `
-
-<ng-container *ngIf="remoteUrl">
-  <cg-pager 
-      [remoteUrl]="remoteUrl"
-      remoteHeaderUrl="/napi/web/epgcontent/headers"
-      [defaultOrder]="'desc'"
-      [defaultOrderBy]="'order'"
-      [listTemplate]="listTemplate"
-      [pageSizeOptions]="[30, 60, 90, 120, 150, 180, 480]"
-      [topPagination]="true"
-      [bottomPagination]="true"
-      [showOrder]="true"
-      [showSearch]="true"
-      [debug]="false"
-      [refresh]="refreshTrigger"
-      [filterConfig]="filterConfig"
-      [inputFilters]="inputFilters"
-      [inputSort]="inputSort"
-      [orderColumns]="orderColumns"
-    ></cg-pager>
-</ng-container>
-
-  <ng-template #listTemplate  let-list="list">
-    <nz-table #basicTable nzSize="small" [nzFrontPagination]="false" [nzShowPagination]="false" [nzData]="list?.data" class="table table-hover table-sm  table-edit">
-      <thead>
-        <tr>
-          <th [nzChecked]="checked" [nzIndeterminate]="indeterminate" (nzCheckedChange)="onAllChecked(list.data, $event)"></th>
-          <th class="text-center" i18n>作品名称</th>
-          <th class="text-center" i18n>效果图</th>
-          <!-- <th class="text-center" i18n>作品状态</th> -->
-          <th class="text-center" i18n>作者</th>
-          <th class="text-center" i18n>标签</th>
-          <th class="text-center" i18n>状态</th>
-          <th [nzWidth]="'100px'"class="text-center" i18n>上架栏目</th>
-          <th [nzWidth]="'200px'" class="text-center" i18n>上架时间/计划时间</th>
-          <th [nzWidth]="'80px'" class="text-center" i18n>操作员</th>
-          <th [nzWidth]="'100px'" class="text-center" i18n>排序号</th>
-          <th *can="['update', 'any', 'epgcontent']" [nzWidth]="'130px'" class="text-center" i18n>操作</th>
-        </tr>
-      </thead>
-      <tbody *ngIf="basicTable.data && basicTable.data.length > 0" cdkDropList [cdkDropListDisabled]="isSpecialColumn(colId)" (cdkDropListDropped)="dragdrop($any($event), basicTable.data)">
-        <tr *ngFor="let data of basicTable.data; let i = index" cdkDrag class="editable-row text-center" [ngClass]="{'settop': data.order > 100000, 'schedule': data.status == 500}">
-          <td [nzChecked]="checkIdSet.has(data._id)" (nzCheckedChange)="onItemChecked(list.data, data._id, $event)"></td>
-          <td>
-            <a style="text-decoration:underline" (click)='onDetail(data.art._id)'>{{data.art.name}}</a>
-          </td>
-          <td>
-            <img nz-image width="100px" height="100px" nzSrc={{data.art.thumb}} loading="lazy" />
-          </td>
-          <!-- <td>
-            <art-status [showText]="true" [data]="data.art"></art-status>
-          </td> -->
-          <td>
-            <nz-tag (click)="filter('art.user.id', [data.art.user._id])" class="clickable" >
-                {{data.art.user?.username}} 
-            </nz-tag>
-          </td>
-          <td>
-            <nz-tag *ngIf="data.lock" nzColor="magenta"> <i class="fal fa-lock"></i> </nz-tag>
-            <ng-container *ngIf="data.art.tags">
-              <nz-tag *ngFor="let tag of data.art.tags" class="clickable" nzColor="default" (click)="filter('art.tags', [tag])" [innerHTML]="tag"></nz-tag>
-            </ng-container>
-          </td>
-          <td>
-            <app-epg-content-status [showText]="true" [data]="data"></app-epg-content-status>
-          </td>
-          <td>
-            <nz-tag *ngIf="data.column && data.column.length > 0" nzColor="#ffa500">{{data.column}}</nz-tag>
-            <nz-tag *ngIf="!data.column" nzColor="#ff4500">others</nz-tag>
-          </td>
-          <td>
-            {{data.onshelfTime | dateFormat: 'yyyy/M/d H:m:s' }}
-          </td>
-          <td>
-              {{data.operator?.name || data.operator?.username}}
-          </td>
-          <td class="text-center">
-            <order-edit [data]="data" [isUpdate]="isUpdate(data)" [canEdit]="!isSpecialColumn(colId)" (onChange)="updateOrder($event)"></order-edit>
-          </td>
-          <td *can="['update', 'any', 'epgcontent']" class="text-center">
-            <div>
-              <a *ngIf="!data.lock" nz-popconfirm i18n-nzPopconfirmTitle nzPopconfirmTitle="确定加锁吗?" (nzOnConfirm)="lock(data, true)" i18n>加锁</a>
-              <a *ngIf="data.lock" nz-popconfirm i18n-nzPopconfirmTitle nzPopconfirmTitle="确定解锁吗?" (nzOnConfirm)="lock(data, false)" i18n>解锁</a>
-              <span style="color: #dcdcdc"> | </span>
-              <a nz-popconfirm i18n-nzPopconfirmTitle nzPopconfirmTitle="确定下架吗?" (nzOnConfirm)="unpublish([data._id])" i18n>下架</a>
-              <span style="color: #dcdcdc"> | </span>
-              <a *ngIf="!isSpecialColumn(colId)" (click)="setTop(data)" i18n>置顶</a>
-            </div>
-            <!-- <div>
-              <a *ngIf="!isSpecialColumn(colId)" (click)="exchangeOrder(basicTable.data[i], basicTable.data[i-1])" i18n>上移</a>
-              <nz-divider  *ngIf="!isSpecialColumn(colId)" nzType="vertical"></nz-divider>
-              <a *ngIf="!isSpecialColumn(colId)" (click)="exchangeOrder(basicTable.data[i], basicTable.data[i+1])" i18n>下移</a>
-            </div> -->
-          </td>
-        </tr>
-      </tbody>
-    </nz-table>
-  </ng-template>
-
-  `,
-  styles: [`
-
-.table th {
-  font-weight: bold;
-}
-
-.table td a {
-  color: dodgerblue;
-}
-
-.table td a:hover {
-  color: forestgreen;
-}
-
-.strikethrough {
-  background: repeating-linear-gradient(
-    180deg,
-    black 0%,
-    black 100%
-  );
-  background-size: 100% 1px;
-  background-position: center;
-  background-repeat: no-repeat;
-}
-
-.settop {
-  background: blanchedalmond;
-}
-
-.schedule {
-  background: #87cefa;
-}
-
-::ng-deep .cdk-drag-preview {
-  display: table;
-}
-
-::ng-deep .cdk-drag-placeholder {
-  opacity: 0;
-}
-
-.cdk-drag-preview {
-  box-sizing: border-box;
-  border-radius: 4px;
-  box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2),
-              0 8px 10px 1px rgba(0, 0, 0, 0.14),
-              0 3px 14px 2px rgba(0, 0, 0, 0.12);
-}
-
-.cdk-drag-placeholder {
-  opacity: 0;
-}
-
-.cdk-drag-animating {
-  transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);
-}
-
-    `
-  ]
-})
-
-
-
-export class EpgContentOnshelfComponent implements OnInit {
-
-  private _epgId: string = null; // 当前选中的epgId
-  public get epgId(): string { return this._epgId };
-  @Input() public set epgId(value: string) {
-    this._epgId = value;
-    this.cleanAllChecked();
-  }
-
-  private _colId: string = null; // 当前选中的栏目id
-  public get colId(): string { return this._colId };
-  @Input() public set colId(value: string) {
-    this._colId = value;
-    if (this.isSpecialColumn(this._colId)) {
-      this.inputSort = ['onshelfTime', 'desc'];
-      this.orderColumns = ['onshelfTime'];
-    } else {
-      this.inputSort = ['order', 'desc'];
-      this.orderColumns = ['order'];
-    }
-    if (this.epgId) {
-      this.remoteUrl = value 
-        ? `/napi/web/epgcontent/${this.epgId}/onshelves?column=${value}` 
-        : `/napi/web/epgcontent/${this.epgId}/onshelves`;
-    }
-    this.cleanAllChecked();
-  }
-
-  _remoteUrl: string = null;
-  get remoteUrl(): string { return this._remoteUrl;}
-  set remoteUrl(value: string) {
-    this._remoteUrl = value;
-    this.refreshTrigger++;
-  }
-
-  @Output() onUnPubSuccess = new EventEmitter<any>();
-
-  constructor(
-    private router: Router,
-    private epgService: EpgService,
-    private message: NzMessageService,
-  ) { }
-
-  ngOnInit(): void {
-  }
-
-  inputFilters: any;
-
-  refreshTrigger: number = 0;
-
-  inputSort: any = ['order', 'desc'];
-  orderColumns: any = [ 'order' ];
-
-  updateSet: Set<string> = new Set();
-  
-  isUpdate(data: any) {
-    return this.updateSet.has(`${this.epgId}-${this.colId}-${data._id}`);
-  }
-
-
-  onDetail(artId: string) {
-    this.router.navigate(['/pages/detail', artId]);
-  }
-
-
-  /**
-   * 更新order
-   * @param data : 形如:[{_id: xxx, order:xxx}] 
-   */
-  updateOrder(data) {
-    console.log(data);
-    firstValueFrom(this.epgService.epgContentUpdate(this.epgId, data))
-      .then(res => {
-        for (let i = 0; i < data.length; i++) {
-          this.updateSet.add(`${this.epgId}-${this.colId}-${data[i]._id}`);
-        }
-        this.refreshTrigger++;
-      })
-      .catch(err => {
-        this.message.error(err.error?.message || err.message);
-        console.log(err);
-      }).then(() => {
-      })
-  }
-
-
-  // 判断某栏目是否特殊栏目(系统保留虚拟栏目) 
-  isSpecialColumn(col: string) {
-    return [undefined, null, '', 'all', 'others'].includes(col?.toLowerCase());
-  }
-
-  filter(data, value) {
-    console.log('filter', data, value);
-    let filter = {};
-    filter[data] = value;
-    this.inputFilters = filter;
-  }
-
-  filterSchedule(date: Date) {
-    let filter = {
-      status: 500,
-      onshelfTime: [startOfDay(date), startOfDay(date)]
-    };
-    this.inputFilters = filter;
-  }
-
-  // 置顶
-  setTop(data) {
-    firstValueFrom(this.epgService.epgContentSetTop(this.epgId, [data._id]))
-    .then(res => {
-      for (let i = 0; i < data.length; i++) {
-        this.updateSet.add(`${this.epgId}-${this.colId}-${data[i]._id}`);
-      }
-      this.refreshTrigger++;
-    })
-    .catch(err => {
-      this.message.error(err.error?.message || err.message);
-      console.log(err);
-    }).then(() => {
-    })
-  }
-
-  // 交换order, 用于上移/下移的操作
-  exchangeOrder(data1, data2) {
-    if (!data1 || !data2) return;
-
-    let data = [
-      { _id: data1._id, order: data2.order },
-      { _id: data2._id, order: data1.order },
-    ]
-    
-    this.updateOrder(data);
-  }
-
-  /**
-   * 下架
-   */
-  unpublish(idList: string[]) {
-    let data = idList.map(e => { return { epg: this.epgId, id: e } });
-    firstValueFrom(this.epgService.epgContentOffshelf(data))
-    .then(res => {
-      this.refreshTrigger++;
-      this.message.success($localize`下架成功`); 
-      this.cleanAllChecked();
-      this.onUnPubSuccess.emit(); // 通知外部
-    })
-    .catch(err => {
-      this.message.error(err.error?.message || err.message);
-      console.log(err);
-    }).then(() => {
-    })
-  }
-
-  lock(data: any, lock: boolean) {
-    firstValueFrom(this.epgService.epgContentUpdate(this.epgId, [{ _id: data._id, lock }]))
-    .then(res => {
-      data.lock = lock;
-    })
-  }
-
-  // 拖拽排序
-  dragdrop(event: CdkDragDrop<string[]>, list): void {
-    if (this.isSpecialColumn(this.colId)) {
-      this.message.error($localize`特殊栏目不支持排序`); 
-      return;
-    }
-
-    let data = [];
-    let from = event.previousIndex, to = event.currentIndex;
-    if (from == to) return;
-    const target = list[from];
-    const delta = to < from ? - 1: 1;
-    for (let i = from; i != to; i += delta) {
-      data.push({ _id: list[i + delta]._id, order: list[i].order });
-      list[i] = list[i + delta];
-    }
-    data.push({ _id: target._id, order: list[to].order });
-    list[to] = target;
-
-    this.updateOrder(data);
-
-    // moveItemInArray(list, event.previousIndex, event.currentIndex);
-  }
-
-
-  checkIdSet = new Set<string>();
-  checked = false;
-  indeterminate = false;
-
-  onItemChecked(list: any[], id: string, checked: boolean): void {
-    this.updateCheckedSet(id, checked);
-    this.refreshCheckedStatus(list);
-  }
-
-  updateCheckedSet(id: string, checked: boolean): void {
-    if (checked) {
-      this.checkIdSet.add(id);
-    } else {
-      this.checkIdSet.delete(id);
-    }
-  }
-
-  refreshCheckedStatus(list: any[]): void {
-    this.checked = list.every(item => this.checkIdSet.has(item._id));
-    this.indeterminate = list.some(item => this.checkIdSet.has(item._id)) && !this.checked;
-   }
-
-  onAllChecked(list: any[], checked: boolean): void {
-    list.forEach(item => this.updateCheckedSet(item._id, checked));
-    this.refreshCheckedStatus(list);
-  }
-
-  cleanAllChecked() {
-    this.checkIdSet.clear();
-    this.checked = false;
-    this.indeterminate = false;
-  }
-
-  //////////////////////////////////////////////////
-  filterConfig: FilterItem[] = [
-    // {
-    //   data: 'art.status',
-    //   title: $localize`作品状态`,
-    //   filterType: FilterType.radio,
-    //   options: [
-    //     // { label: '草稿', value: 1000 },
-    //     { label: $localize`需要修改`, value: 2000 },
-    //     { label: $localize`测试中`, value: 3000 },
-    //     { label: $localize`测试通过`, value: 7000 },
-    //     { label: $localize`已发布`, value: 9000 },
-    //     { label: $localize`下线`, value: 8000 },
-    //   ],
-    //   span: 4,
-    // },
-    {
-      data: 'status',
-      title: $localize`状态`,
-      filterType: FilterType.radio,
-      options: [
-        { label: $localize`已上架`, value: 1000 },
-        { label: $localize`计划上架`, value: 500 },
-      ],
-      span: 2,
-    },
-    {
-      data: 'onshelfTime',
-      title: $localize`上架时间`,
-      filterType: FilterType.dateRange,
-      span: 2,
-    },
-    {
-      data: 'art.tags',
-      title: $localize`标签`,
-      filterType: FilterType.select,
-      mode : 'tags',
-      span: 2,
-      fullWidth: true,
-      optionsUrl : '/napi/web/art/agg/tags',
-    },
-    {
-      data: 'art.user.id',
-      title: $localize`作者`,
-      filterType: FilterType.select,
-      mode : 'multiple',
-      span: 2,
-      fullWidth: true,
-      optionsUrl : '/napi/web/user/select/options',
-    },
-    {
-      data: 'art.hasSpecial',
-      title: $localize`彩绘`,
-      filterType : FilterType.select,
-      options: [
-        { label: $localize`是`, value: true },
-        { label: $localize`否`, value: false },
-      ],
-      span: 1,
-    },
-    {
-      data: 'art.mystery',
-      title: $localize`神秘图`,
-      filterType : FilterType.select,
-      options: [
-        { label: $localize`是`, value: true },
-        { label: $localize`否`, value: false },
-      ],
-      span: 1,
-    },
-    {
-      data: 'art.ai',
-      title: $localize`AI`,
-      filterType : FilterType.select,
-      options: [
-        { label: $localize`是`, value: true },
-        { label: $localize`否`, value: false },
-      ],
-      span: 1,
-    },
-    {
-      data: 'art.width',
-      title: $localize`尺寸`,
-      filterType : FilterType.select,
-      options: [
-        { label: '2000', value: 2000 },
-        { label: '1500', value: 1500 },
-      ],
-      span: 1,
-    },
-    {
-      data: 'art.lock',
-      title: $localize`加锁`,
-      filterType : FilterType.select,
-      options: [
-        { label: $localize`是`, value: true },
-        { label: $localize`否`, value: false },
-      ],
-      span: 1,
-    },
-
-  ];
-
-}

+ 0 - 480
zorro/src/app/pages/epg/epg-content-pager.component.ts

@@ -1,480 +0,0 @@
-/**
- * 基于pager.component, 去掉route navigate
- */
-import { HttpClient } from '@angular/common/http';
-import { Component, Input, OnInit, TemplateRef } from '@angular/core';
-import { CacheService } from 'src/app/lib/pager/cache.service';
-import { FilterItem } from 'src/app/lib/pager/pager-filter.component';
-import { CellRender } from 'src/app/lib/pager/pager-table.component';
-import { CommonUtils } from 'src/app/lib/utils/common-utils';
-
-@Component({
-  selector: 'epg-content-pager',
-  template: `
-    
-<nz-result *ngIf="error" nzStatus="404" nzTitle="{{error.status}}" nzSubTitle="{{error?.error?.message}}"> </nz-result>
-<ng-container *ngIf="!error">
-  <!--filters-->
-  <cg-pager-filter [filters]="filters" [inputFilters]="inputFilters" [filterConfig]="filterConfig" (filterChange)="onFilterChange($event); delayUpdateQueryString('filters')"></cg-pager-filter>
-
-  <div *ngIf="debug">
-    search: {{search}} <br/>
-    orderBy : {{orderBy}} <br/>
-    order : {{order}} <br/>
-    filters : {{filters | json}} <br/>
-    params: {{params | json}} <br/>
-    resp.meta : {{respMeta | json}} <br/>
-  </div>
-
-
-  <nz-spin [nzDelay]="500" [nzSpinning]="isLoading">
-  
-  <div class="d-flex flex-row  justify-content-between align-items-center" >
-
-    <!--top pagination -->
-    <div *ngIf="topPagination && list" class="d-flex  m-2" >
-      <nz-pagination *ngIf="list" 
-        [(nzPageIndex)]="page" 
-        [nzShowTotal]="rangeTemplate" 
-        nzShowSizeChanger 
-        nzShowQuickJumper 
-        [nzPageSizeOptions]="pageSizeOptions"  
-        [(nzPageSize)]="length" 
-        nzSize="small"
-        (nzPageIndexChange)="delayUpdateQueryString('pageIndex')"
-        (nzPageSizeChange)="delayUpdateQueryString('pageSize')"
-        [nzTotal]="total">
-      </nz-pagination>
-    </div>
-
-
-    <!--sort-->
-    <div *ngIf="orderableHeaders.length > 0 && showOrder">
-      <span class="me-2"><i class="fal fa-sort-alpha-down"></i></span>
-      <nz-button-group>
-        <button nz-button  pagerSort 
-          *ngFor="let header of orderableHeaders" 
-          [order]="header.order"
-          [nzType]="orderBy == header.data ? 'primary' : 'default' " 
-          (onSortChange)="inputSort=[header.data, $event]">
-          <i nz-icon *ngIf="orderBy == header.data" [nzType]="order == 'desc' ? 'sort-descending'  : 'sort-ascending'" nzTheme="outline"></i>{{header.title}}
-        </button>
-      </nz-button-group>
-    </div>
-
-    <!--搜索-->
-    <nz-input-group *ngIf="showSearch" nzPrefixIcon="search" [nzSuffix]="clearIcon"  class="search" >
-      <input nz-input [(ngModel)]="search" (change)="delayUpdateQueryString('search')"  i18n-placeholder placeholder="搜索">
-      <ng-template #clearIcon>
-        <i nz-icon nzType="close-circle" *ngIf="search" nzTheme="fill" class="clearIcon" (click)="search=undefined; delayUpdateQueryString('search clear')"  ></i>
-      </ng-template>
-    </nz-input-group>
-
-  </div>
-
-
-  <!--content list-->
-  <ng-container *ngIf="listTemplate">
-    <ng-container *ngTemplateOutlet="listTemplate; context : {list, headers }" >
-    </ng-container>
-    <nz-empty *ngIf="list && list?.data.length <= 0" i18n-nzNotFoundContent nzNotFoundContent="没有数据"	 ></nz-empty>
-  </ng-container>
-
-  <!--default content list-->
-  <cg-pager-table *ngIf="!listTemplate" [cellRenders]="cellRenders" [headers]="headers" [list]="list?.data" (onSort)="inputSort=$event" > </cg-pager-table> 
-
-
-  <!--bottom pagination-->
-  <div *ngIf="bottomPagination && list" class="d-flex flex-row m-2 "  >
-      <nz-pagination *ngIf="list" 
-        [(nzPageIndex)]="page" 
-        [nzShowTotal]="rangeTemplate" 
-        nzShowSizeChanger 
-        nzShowQuickJumper 
-        [nzPageSizeOptions]="pageSizeOptions"  
-        [(nzPageSize)]="length" 
-        nzSize="small"
-        (nzPageIndexChange)="delayUpdateQueryString('pageIndex')"
-        (nzPageSizeChange)="delayUpdateQueryString('pageSize')"
-        [nzTotal]="total">
-      </nz-pagination>
-  </div>
-
-  </nz-spin>
-
-  <ng-template #rangeTemplate let-range="range" let-total>
-        {{ range[0] }}-{{ range[1] }} <ng-container i18n>共</ng-container> {{ total }} <ng-container i18n>项</ng-container>
-  </ng-template>
-
-</ng-container>
-
-  `,
-  styles: [
-    `
-    :host {
-    }
-
-    select.length{
-      width: auto;
-    }
-
-    .search{
-      width: 236px;
-    }
-
-    .search .clearIcon{
-      color: #ccc;
-      cursor: pointer;
-    }
-    .search .clearIcon:hover{
-      color: #999;
-    }
-
-    `
-  ]
-})
-export class EpgContentPagerComponent implements OnInit {
-  @Input() defaultOrderBy: string;
-  @Input() defaultOrder: orderType;
-  @Input() listTemplate: TemplateRef<any>; //列表渲染模板
-  @Input() pageSizeOptions: number[] = [10, 20, 30, 60, 120, 180];
-  @Input() showOrder: boolean = true;
-  @Input() showSearch: boolean = true;
-  @Input() highlightSearch: boolean = true;
-  @Input() orderColumns: string[];
-  @Input() topPagination: boolean = true;
-  @Input() bottomPagination: boolean = true;
-  @Input() debug: boolean = false;
-  @Input() replaceUrl: boolean = true;
-  @Input() inputFilters: any;
-  @Input() filterConfig: FilterItem[];
-  @Input() cellRenders: CellRender;
-
-  @Input()set refresh(val) { //刷新数据
-    this.invalidate();
-  } 
-
-  @Input() set inputSort(val: [string, orderType]) {
-    console.log('val: ', val);
-    if (val) {
-      let [orderBy, order] = val;
-      this.orderBy = orderBy;
-      this.order = order;
-      this.delayUpdateQueryString('sort');
-    }
-  }
-
-
-
-  //dateRange: any;
-
-  public get total(): number {
-    if (this.list) return this.list.recordsFiltered;
-    else return 0;
-  }
-
-  filters: any = {};
-
-
-  /**
-   * 远端表头接口
-   */
-  private _remoteHeaderUrl: string = null;
-  public get remoteHeaderUrl(): string {
-    return this._remoteHeaderUrl;
-  }
-  @Input() public set remoteHeaderUrl(value: string) {
-    this._remoteHeaderUrl = value;
-    this.loadHeader();
-  }
-
-
-  /**
-   * 远端数据接口
-   */
-  private _remoteUrl: string = null;
-  public get remoteUrl(): string { return this._remoteUrl; }
-  @Input() public set remoteUrl(value: string) {
-    this._remoteUrl = value;
-  }
-
-
-  private _page: number = 1;
-  public get page(): number { return this._page; }
-  public set page(value: number) {
-    this._page = value;
-  }
-
-  private _length: number = 0;
-  public get length(): number { return this._length || this.pageSizeOptions[0] || 10; }
-  public set length(value: number) {
-    this._length = value;
-  }
-
-
-  private _orderBy: string;
-  public get orderBy(): string { return this._orderBy; }
-  @Input()
-  public set orderBy(value: string) {
-    if (this._orderBy && this._orderBy != value) {
-      this.page = 1; //set page to the 1st page.
-    }
-    this._orderBy = value;
-  }
-
-  private _order: orderType;
-  public get order(): orderType { return this._order; }
-  @Input()
-  public set order(value: orderType) {
-    this._order = value;
-  }
-
-
-  search: string;
-
-
-  headers: Header[];
-  list: PagerResponse<any>;
-
-  private isPendingLoad: boolean = false;
-  isLoading: boolean = false;
-
-
-  error: any;
-
-  constructor(
-    private http: HttpClient,
-    public cache: CacheService,
-  ) {
-  }
-
-  get orderableHeaders(): Header[] {
-    if (!this.headers) return [];
-
-    let orderable = this.headers.filter(h => h.orderable)
-    if (!this.orderColumns)
-      return orderable;
-
-    return this.orderColumns.map(key => {
-      let header: Header = orderable.find(h => h.data == key);
-      return header
-    }).filter(h => h);
-  }
-
-  get params(): any {
-    let params = {
-      page: this.page,
-      length: this.length,
-      order: this.order,
-      orderBy: this.orderBy,
-      search: this.search,
-    };
-
-    this.cleanUp(this.filters);
-    params['filters'] = JSON.stringify(this.filters);
-    this.cleanUp(params);
-    return params;
-  }
-
-  private loadHeader() {
-    if (!this.remoteHeaderUrl) return;
-    this.cache.get(this.remoteHeaderUrl).subscribe({
-      next: (resp: Header[]) => {
-        this.headers = resp;
-      },
-      error: err => console.warn(err),
-      complete: () => this.updateHeader(),
-    })
-  }
-
-
-
-  invalidate() {
-    if (this.isPendingLoad) return;
-    this.isPendingLoad = true;
-    requestAnimationFrame(() => {
-      this.loadData();
-    })
-  }
-
-  get respMeta(): any {
-    let resp = { ...this.list };
-    delete resp['data'];
-    return resp;
-  }
-
-
-  private async loadData() {
-    this.isPendingLoad = false;
-    this.isLoading = true;
-    if (!this.remoteUrl) throw new Error('no remote url.');
-    this.cache.get(this.remoteUrl, this.params).subscribe({
-      next: resp => {
-        console.log('got resp:', resp);
-        this.list = resp as PagerResponse<any>;
-        this.doHighlight(this.list, this.headers);
-        this.error = null;
-      },
-      error: (err) => {
-        console.error(err)
-        this.isLoading = false;
-        this.error = err;
-      },
-      complete: () => { this.isLoading = false; this.updateHeader() }
-    })
-  }
-
-  doHighlight(list: PagerResponse<any>, headers: Header[]) {
-    if (!this.highlightSearch || !list.data || !this.search || !headers) return;
-    let search = this.search.trim();
-    if (!search) return;
-    let regex = new RegExp(CommonUtils.escapeRegExp(search), 'gi');
-    this.headers.filter(h => h.searchable)
-      .forEach(header => {
-        this.list.data.forEach(item => {
-          let value = item[header.data];
-          if (value && value.replace && value.match) { //字符串类型
-            let match = value.match(regex)
-            if (match) {
-              item[header.data] = value.replace(regex, `<span class='highlight'>${match[0]}</span>`)
-            }
-          } else if (Array.isArray(value) && value.length > 0 && value[0].replace) { //字符串数字类型,如tags
-            item[header.data] = value.map(v => {
-              let match = v.match(regex)
-              if (match) {
-                return v.replace(regex, `<span class='highlight'>${match[0]}</span>`)
-              } else {
-                return v;
-              }
-            })
-          }
-        })
-      })
-  }
-
-
-  updateHeader() {
-    if (!this.headers) return;
-    this.headers.forEach(h => {
-      if (h.data == this.orderBy) {
-        h.order = this.order
-      } else {
-        h.order = null;
-      }
-    })
-  }
-
-
-  // 点击清空筛选按钮,至少要保留原来的inputFilters
-  onFilterChange(data) {
-    if (JSON.stringify(data) === '{}') {
-      this.filters = Object.assign({}, this.inputFilters);
-    } else {
-      this.filters = data;
-    }
-  }
-
-  //当前是否有等待更新queryString的请求
-  private isPendingRequest: boolean = false;
-
-  /**
-   * 延期更新QueryString 
-   * 当同一时间多次出发更新时, 合并操作.
-   * @returns 
-   */
-  delayUpdateQueryString(from: string) {
-    console.log('delayUpdateQueryString: ', from, this.isPendingRequest);
-    if (this.isPendingRequest) return;
-    this.isPendingRequest = true;
-    requestAnimationFrame(() => {
-      this.updateQueryString();
-    })
-  }
-
-  /**
-   * 更新QueryString, 出发数据重新加载.
-   */
-  updateQueryString() {
-    this.isPendingRequest = false;
-
-    let params: any = {
-      page: this.page,
-      length: this.length,
-    };
-
-    if (this.order) {
-      params.order = this.order;
-      params.orderBy = this.orderBy;
-    };
-
-    if (this.search) {
-      params.search = this.search;
-    }
-
-    this.cleanUp(this.filters);
-    if(Object.keys(this.filters).length > 0) {
-      params.filters = JSON.stringify(this.filters);
-    }
-
-    let obj = Object.assign({}, params);
-    this.cleanUp(obj);
-
-    this.invalidate();
-
-    console.log('updating qeurystring:', obj);
-  }
-
-
-  cleanUp(obj: any) {
-    for (var attrKey in obj) {
-      var attrValue = obj[attrKey];
-      if (attrValue === null || attrValue === undefined || attrValue === "" || attrValue !== attrValue) {
-        delete obj[attrKey];
-      } else if (Object.prototype.toString.call(attrValue) === "[object Object]") {
-        if (Object.keys(attrValue).length === 0) delete obj[attrKey];
-      } else if (Array.isArray(attrValue)) {
-        if (attrValue.length === 0) delete obj[attrKey];
-      }
-    }
-  }
-
-
-  ngOnInit() {
-
-    this.order = this.defaultOrder;
-    this.orderBy = this.defaultOrderBy;
-    if (this.orderBy && !this.order) this.order = 'desc';
-
-  }
-
-
-
-
-
-}
-
-
-
-export interface PagerResponse<Type> {
-  page: number,
-  length: number,
-  draw: number,
-  recordsTotal: number,
-  recordsFiltered: number,
-  data?: Type[],
-}
-
-
-export interface Header {
-  data: string,
-  title: string,
-  orderable: boolean,
-  searchable: boolean,
-  type: string,
-  order?: orderType,
-}
-
-
-export type orderType = 'asc' | 'desc' | null;
-
-

+ 0 - 44
zorro/src/app/pages/epg/epg-content-status.component.ts

@@ -1,44 +0,0 @@
-import { Component, Input, OnInit } from '@angular/core';
-
-@Component({
-  selector: 'app-epg-content-status',
-  template: `
-
-<nz-tag nzColor="#d3d3d3" *ngIf="item?.status == status.NULL"><span *ngIf="showText" i18n>未上架</span></nz-tag>
-<nz-tag nzColor="#696969" *ngIf="item?.status == status.OFFSHELF"><i class="fal fa-level-down"></i><span *ngIf="showText" i18n>已下架</span></nz-tag>
-<nz-tag nzColor="#007bff" *ngIf="item?.status == status.SCHEDULE"> <i class="fal fa-clock"></i> <span *ngIf="showText" i18n>计划上架</span> </nz-tag>
-<nz-tag nzColor="#28a745" *ngIf="item?.status == status.ONSHELF"><i class="fal fa-eye"></i><span *ngIf="showText" i18n>已上架</span></nz-tag>
-  `,
-  styles: [
-    `
-  i+span{
-    padding-left:5px;
-  }
-  i.fas{
-    font-size:10px;
-    display:none;
-  }
-  `
-  ]
-})
-
-export class EpgContentStatusComponent implements OnInit {
-
-  @Input('data') item: any;
-  @Input() showText: boolean = true;
-  
-  status = EpgContentStatus;
-
-  constructor() { }
-
-  ngOnInit(): void {
-  }
-
-}
-
-export enum EpgContentStatus {
-  NULL = 0,
-  OFFSHELF = 100,
-  SCHEDULE = 500,
-  ONSHELF = 1000,
-}

+ 0 - 400
zorro/src/app/pages/epg/epg-content-unshelf.component.ts

@@ -1,400 +0,0 @@
-import { Component, Input, OnInit, Output, EventEmitter, ViewContainerRef } from '@angular/core';
-import { NzMessageService } from 'ng-zorro-antd/message';
-import { NzModalService } from 'ng-zorro-antd/modal';
-import { firstValueFrom } from 'rxjs';
-import { FilterItem, FilterType } from 'src/app/lib/pager/pager-filter.component';
-import { EpgPublishConfirmComponent } from './epg-publish-confirm.component';
-import { EpgService } from './epg.service';
-import { addHours, startOfDay } from 'date-fns';
-
-@Component({
-  selector: 'epg-content-unshelf',
-  template: `
-<div style="margin-bottom: 10px">
-  <i nz-icon nzType="info-circle" nzTheme="outline"></i>
-  <span style="color: orange">{{tip}}</span>
-  <span class="number" (click)="this.selectList.length > 0 && showPublishConfirm(this.selectList, false)">【{{this.selectList.length}}】</span>
-  &nbsp;&nbsp;&nbsp;&nbsp;
-  <span i18n>定时上架: </span> <nz-switch nzSize="small" [(ngModel)]="isSchedule"></nz-switch>
-  &nbsp;&nbsp;
-  <span *ngIf="isSchedule"><nz-date-picker nzShowTime nzFormat="yyyy-MM-dd HH:mm:ss" [(ngModel)]="scheduleTime"></nz-date-picker></span> 
-</div>
-
-<ng-container *ngIf="remoteUrl">
-  <epg-content-pager
-      [remoteUrl]="remoteUrl"
-      remoteHeaderUrl="/napi/web/art/pager/headers"
-      [defaultOrder]="'desc'"
-      [defaultOrderBy]="'date'"
-      [listTemplate]="listTemplate"
-      [pageSizeOptions]="[30, 60, 90, 120, 150, 180, 480]"
-      [topPagination]="true"
-      [bottomPagination]="true"
-      [showOrder]="true"
-      [showSearch]="true"
-      [debug]="false"
-      [refresh]="refreshTrigger"
-      [filterConfig]="filterConfig"
-      [inputFilters]="inputFilters"
-      [orderColumns]="['areaCount', 'publishTime', 'date','lastMod' ]"
-    ></epg-content-pager>
-</ng-container>
-
-<ng-template #listTemplate  let-list="list" >
-    <div nz-row [nzGutter]="[20, 20]" class="pt-4 pb-4 scroll ">
-      <app-epg-art-card   *ngFor="let item of list?.data" 
-        nz-col  [nzSm]="8" [nzMd]="6" [nzLg]="6" [nzXl]="4" [nzXXl]="3"	
-        [data]="item"  
-        [hideAuthor]="false"
-        (itemClick)="onItemClick($event)"
-        (useClick)="filter('use', $event)"
-        (tagClick)="filter('tags', [$event])"
-        (authorClick)="filter('user', [$event])"
-         >
-      </app-epg-art-card>
-    </div>
-  </ng-template>
-
-`,
-  styles: [
-    `
-.scroll {
-  height: 50vh;
-  overflow-y: auto;
-}
-
-.number {
-  font-weight: bold;
-  color: green;
-}
-
-.number:hover{
-  cursor : pointer;
-  color: #ff00ff;
-}
-
-.table th {
-  font-weight: bold;
-}
-
-.table td a {
-  color: dodgerblue;
-}
-
-.table td a:hover {
-  color: forestgreen;
-}
-
-    `
-  ]
-})
-export class EpgContentUnshelfComponent implements OnInit {
-
-  private _epg: any;
-  public get epg(): any { return this._epg; }
-  @Input() public set epg(value: any) {
-    this._epg = value;
-  }
-
-  private _colId: string = null; // 当前选中的栏目id
-  public get colId(): string { return this._colId };
-  @Input() public set colId(value: string) {
-    this._colId = value;
-    if (this.epg) {
-      this.remoteUrl = value 
-        ? `/napi/web/epgcontent/${this.epg.epgId}/unshelves?column=${value}` 
-        : `/napi/web/epgcontent/${this.epg.epgId}/unshelves`;
-    }
-  }
-
-  private _remoteUrl: string = null;
-  public get remoteUrl(): string { return this._remoteUrl;}
-  @Input() public set remoteUrl(value: string) {
-    this._remoteUrl = value;
-    this.refreshTrigger++;
-  }
-
-  get tip(): string {
-    let tip;
-    if (!this.colId) {
-      tip = $localize`已选择EPG: ${this.epg.epgId}, 未选择任何栏目,将自动匹配, 请选择要上架的内容:`;
-    } else if(this.isSpecialColumn(this.colId)) {
-      tip = $localize`${this.getPath(this.colId)} 是虚拟栏目,将自动匹配, 请选择要上架的内容:`;
-    } else {
-      tip = $localize`发布路径: ${this.getPath(this.colId)}, 请选择要上架的内容:`;
-    }
-    return tip
-  }
-
-  @Output() onPubSuccess = new EventEmitter<any>();
-
-  // 选中要发布的内容集合
-  selectSet: Set<any> = new Set();
-  get selectList(): any[] {
-    return Array.from(this.selectSet);
-  }
-
-  // 定时发布相关
-  DEFAULT_TIME = 16; //16:00
-  _isSchedule: boolean = false;
-  get isSchedule() {
-    return this._isSchedule;
-  }
-  set isSchedule(value: boolean) {
-    this._isSchedule = value;
-    if (value == false) {
-      this.inputFilters = { status: 9000 };
-    } else {
-      this.inputFilters = { status: [9000, 7000] }; // 如果勾选了定时发布, 那么ready状态的作品也筛选出来
-    }
-  }
-  _scheduleTime: Date;  // 计划上架日期
-  get scheduleTime() {
-    if (!this._scheduleTime) { 
-      this._scheduleTime = addHours(startOfDay(new Date()), this.DEFAULT_TIME);
-    }
-    return this._scheduleTime;
-  }
-  set scheduleTime(time: Date) {
-    this._scheduleTime = time;
-  }
-
-  constructor(
-    private epgService: EpgService,
-    private message: NzMessageService,
-    private modal: NzModalService,
-    private viewContainerRef: ViewContainerRef,
-  ) { }
-
-  ngOnInit(): void {
-  }
-
-  inputFilters: any = { status: 9000, use: 'normal' };
-
-  refreshTrigger: number = 0;
-
-
-  // 判断某栏目是否特殊栏目(系统保留虚拟栏目) 
-  isSpecialColumn(col: string) {
-    return [undefined, null, '', 'all', 'others'].includes(col?.toLowerCase());
-  }
-
-  onItemClick(data) {
-    let art = data[0];
-    let status = data[1];
-    if (status == true) {
-      this.selectSet.add(art);
-    } else {
-      this.selectSet.delete(art);
-    }
-  }
-
-  filter(data, value) {
-    console.log('filter', data, value);
-    let filter = {};
-    filter[data] = value;
-    this.inputFilters = filter;
-  }
-
-  // 显示发布详情对话框
-  showPublishConfirm(dataList: any[], ret: boolean) {
-    if (!dataList || dataList.length <= 0) {
-      this.message.error($localize`未选择任何数据`);
-      return;
-    }
-
-    let pubList = this.generatePublishList(dataList);
-
-    const modal = this.modal.create({
-      nzTitle: $localize`发布上架确认`,
-      nzCentered: true,
-      nzWidth: 800,
-      nzContent: EpgPublishConfirmComponent,
-      nzViewContainerRef: this.viewContainerRef,
-      nzComponentParams: {
-        publishList: pubList,
-        ret,
-        tip: this.isSpecialColumn(this.colId) ? $localize`未选择有效栏目, 将自动匹配, 请确认上架: ` : $localize`请确认上架:`,
-      },
-      nzOnOk: this.doPublish.bind(this),
-
-    })
-  }
-
-  // 执行发布动作
-  doPublish(instance: EpgPublishConfirmComponent) {
-    let dataList = instance.publishList.map(e => {
-      return {epg: this.epg.epgId, column: e.column, art: e.art, schedule: this.isSchedule? this.scheduleTime: undefined}
-    });
-
-    firstValueFrom(this.epgService.epgContentOnshelf(dataList))
-    .then(res => {
-      this.refreshTrigger++;
-      this.selectSet.clear();
-      this.message.success($localize`上架成功`);
-      this.onPubSuccess.emit(instance.ret); // 通知外部
-    })
-    .catch(err => {
-      this.message.error(err.error?.message || err.message);
-      console.log(err);
-    }).then(() => {
-    })
-
-    
-  }
-
-
-  /**
-   * 生成发布列表
-   * 如果没有指定有效栏目, 将根据作品的tag属性和column id对应发布
-   * @param dataList 
-   * @returns 
-   */
-  generatePublishList(dataList: any[]): any[] {
-    let pubList = [];
-    if (this.isSpecialColumn(this.colId)) { // 未指定栏目或选择的是不允许发布内容的特殊栏目
-      dataList.forEach(data => {
-        let tags = data.tags;
-        tags.push('latest');  // 增加默认“latest”标签,大多数都需要同步发布到latest下面
-        tags = tags.filter((item, index, arr) => arr.indexOf(item, 0) == index);  // 去重
-        let count = 0;
-        if (tags && tags.length && tags.length > 0) {
-          tags.forEach(tag => {   // 根据作品的tag决定发布到哪个栏目下
-            if (this.epg.columns.findIndex(item => item.id == tag) >= 0) { // 找到与tag匹配的栏目
-              let node = { data, column: tag, art: data._id, path: this.getPath(tag)};
-              pubList.push(node);
-              count++;
-            }
-          })
-        }
-        if (count == 0) {  // 未找到合适的发布点, 则column置为'', 相当于发布到"其他"栏目下
-          let node = { data, column: '', art: data._id, path: `${this.epg.epgId} -> 其他(others)` };
-          pubList.push(node);
-        }
-      })
-    } else {
-      dataList.forEach(data => {
-        let node = { data, column: this.colId, art: data._id, path: this.getPath(this.colId)};
-        pubList.push(node);
-      })
-    }
-
-    return pubList;
-  }
-
-  // 获取完整发布路径, 形如: "enc-数字填色 -> 动物(animal)" 的文字信息
-  getPath(col: string) {
-    let colInfo = this.epg.columns.find(item => item.id == col);
-    let path = `${this.epg.epgId} -> ${colInfo.zh}(${colInfo.id})`;
-    return path;
-  }
-
-
-
-  
-  //////////////////////////////////////////////////
-  filterConfig: FilterItem[] = [
-    {
-      data: 'date',
-      title: $localize`上传`,
-      filterType: FilterType.dateRange,
-      span: 2,
-    },
-    {
-      data: 'publishTime',
-      title: $localize`发布`,
-      filterType: FilterType.dateRange,
-      span: 2,
-    },
-    {
-      data: 'epgs',
-      title: 'EPG',
-      filterType: FilterType.select,
-      mode : 'default',
-      span: 1,
-      fullWidth: true,
-      optionsUrl : '/napi/web/epg/options',
-    },
-    {
-      data: 'use',
-      title: $localize`用途`,
-      filterType: FilterType.radio,
-      options: [
-        { label: $localize`日常`, value: 'normal' },
-        { label: $localize`每日`, value: 'daily' },
-        { label: $localize`专辑`, value: 'album' },
-        { label: $localize`收集`, value: 'collection' },
-      ],
-      span: 3,
-    },
-    {
-      data: 'tags',
-      title: $localize`标签`,
-      filterType: FilterType.select,
-      mode : 'tags',
-      span: 2,
-      fullWidth: true,
-      optionsUrl : '/napi/web/art/agg/tags',
-    },
-    {
-      data: 'user',
-      title: $localize`作者`,
-      filterType: FilterType.select,
-      mode : 'multiple',
-      span: 2,
-      fullWidth: true,
-      optionsUrl : '/napi/web/user/select/options',
-    },
-    {
-      data: 'hasSpecial',
-      title: $localize`彩绘`,
-      filterType : FilterType.select,
-      options: [
-        { label: $localize`是`, value: true },
-        { label: $localize`否`, value: false },
-      ],
-      span: 1,
-    },
-    {
-      data: 'mystery',
-      title: $localize`神秘图`,
-      filterType : FilterType.select,
-      options: [
-        { label: $localize`是`, value: true },
-        { label: $localize`否`, value: false },
-      ],
-      span: 1,
-    },
-    {
-      data: 'ai',
-      title: $localize`AI`,
-      filterType : FilterType.select,
-      options: [
-        { label: $localize`是`, value: true },
-        { label: $localize`否`, value: false },
-      ],
-      span: 1,
-    },
-    {
-      data: 'lock',
-      title: $localize`加锁`,
-      filterType : FilterType.select,
-      options: [
-        { label: $localize`是`, value: true },
-        { label: $localize`否`, value: false },
-      ],
-      span: 1,
-    },
-    {
-      data: 'random',
-      title: $localize`随机`,
-      filterType : FilterType.select,
-      options: [
-        { label: $localize`是`, value: true },
-        { label: $localize`否`, value: false },
-      ],
-      span: 1,
-    },
-
-  ];
-}

+ 0 - 349
zorro/src/app/pages/epg/epg-content.component.ts

@@ -1,349 +0,0 @@
-
-import { Component, OnDestroy, OnInit, TemplateRef, ViewChild } from '@angular/core';
-import { ActivatedRoute, NavigationEnd, Router } from '@angular/router';
-import { addDays, format, startOfDay } from 'date-fns';
-import { NzI18nService } from 'ng-zorro-antd/i18n';
-import { NzMessageService } from 'ng-zorro-antd/message';
-import { NzModalService } from 'ng-zorro-antd/modal';
-import { firstValueFrom } from 'rxjs';
-import { filter } from 'rxjs/operators';
-import { EpgContentOnshelfComponent } from './epg-content-onshelf.component';
-import { EpgContentUnshelfComponent } from './epg-content-unshelf.component';
-import { EpgService } from './epg.service';
-
-@Component({
-  selector: 'app-epg-content',
-  template: `
-
-<nz-page-header >
-  <nz-page-header-title>{{title}}</nz-page-header-title>
-  <nz-page-header-subtitle>
-    <ng-container *ngIf="epgs && epgs.length > 0">
-      <nz-select [(ngModel)]="epgId" (ngModelChange)="onEpgChange($event)">
-      <nz-option *ngFor="let item of epgOptions; let i=index" [nzValue]="item.value" [nzLabel]="item.label"></nz-option>
-      </nz-select>
-    </ng-container>
-  </nz-page-header-subtitle>
-  <nz-page-header-extra>
-    <nz-date-picker i18n-nzPlaceHolder nzPlaceHolder="上架计划" nzFormat="yyyy-MM-dd" [nzDateRender]="tplRender" [(ngModel)]="scheduleTime"></nz-date-picker>
-    <button nz-button nzType="primary" *can="['update', 'any', 'epg']" 
-      (click)="showUnpublishConfirm()" [disabled]="!onshelfComponent || onshelfComponent.checkIdSet.size <= 0">
-      <i nz-icon nzType="vertical-align-bottom" nzTheme="outline"></i>
-      <ng-container i18n>下架</ng-container>
-    </button>  
-    <button nz-button nzType="primary" *can="['update', 'any', 'epg']"
-      (click)="openPublishModal(unshelfTemplate)">
-      <i nz-icon nzType="vertical-align-top" nzTheme="outline"></i>
-      <ng-container i18n>上架</ng-container>
-    </button>
-    <button nz-button nzType="primary" *can="['read', 'any', 'epg']" 
-      (click)="showPreview(previewTemplate)">
-      <i nz-icon nzType="eye" nzTheme="twotone" [nzTwotoneColor]="'#52c41a'"></i>
-      <ng-container i18n>预览</ng-container>
-    </button>
-  </nz-page-header-extra>
-</nz-page-header>
-
-<!-- <div nz-row nzJustify="start" style="margin-bottom: 20px;">
-  <div *ngFor="let col of curColumns; let i=index" nz-col nzSpan="1" (click)="onColumnClick(col.id)"
-    class="column select" [ngClass]="{'special': isSpecialColumn(col.id), 'column-select': colId==col.id}">
-  {{col.zh}}
-  </div>
-</div> -->
-
-<nz-tabset [nzSize]="'small'" [nzSelectedIndex]="curColIdx" >
-  <nz-tab *ngFor="let col of curColumns; let i=index" >
-    <a class="tab" [ngClass]="{'tab-special': isSpecialColumn(col.id), 'tab-select': colId==col.id}" 
-      *nzTabLink nz-tab-link [routerLink]="[getLink(col.id)]" [queryParams]="getQueryParams()" queryParamsHandling="merge">
-      {{i18n.getLocaleId() == 'en' ? col.en : col.zh}}
-    </a> 
-  </nz-tab>
-</nz-tabset>
-
-<epg-content-onshelf #onshelf [epgId]="epgId" [colId]="colId">
-</epg-content-onshelf>
-
-<ng-template #unshelfTemplate let-modal="modalRef">
-  <epg-content-unshelf #unshelf [epg]="curEpg" [colId]="colId" 
-    (onPubSuccess)="onPubSuccess(); $event && modal.close()">
-  </epg-content-unshelf>
-</ng-template>
-
-<ng-template #previewTemplate let-modal="modalRef">
-  <app-epg-preview [epgInfo]="curEpg" [colId]="colId||'all'">
-  </app-epg-preview>
-</ng-template>
-
-
-<ng-template #tplRender let-current>
-  <div class="ant-picker-cell-inner" [ngClass]="{'picker-cell': getScheduleCount(current) > 0}">
-    <nz-badge nzSize="small" [nzOffset]="[10, -5]" [nzCount]="getScheduleCount(current)">
-      <span>{{ current.getDate() }}</span>
-    </nz-badge>
-  </div>
-</ng-template>
-
-  `,
-  styles: [
-    `
-    :host {
-      text-align: center;
-    }
-    
-    .special {
-      color: #bc8f8f;
-    }
-
-    .column {
-      cursor: pointer;
-    }
-
-    .column-select {
-      color: #ff8c00;
-      /* background: #dcdcdc; */
-      border-style: solid;
-      border-width: 1px;
-    }
-
-    .column:hover {
-      color: #ff8c00;
-      background: #F2F2F2;
-    }
-
-    .tab {
-      color: black;
-    }
-
-    .tab-special {
-      color: #bdb76b;
-    }
-
-    .tab-select {
-      color: #ff4500;
-    }
-
-    .tab:hover {
-      color: orange;
-    }
-
-
-    .picker-cell {
-      background: #00bfff;
-      /* border-radius: 50%; */
-    }
-
-    `
-  ]
-})
-export class EpgContentComponent implements OnInit, OnDestroy {
-  @ViewChild('onshelf') onshelfComponent: EpgContentOnshelfComponent;
-  @ViewChild('unshelf') unshelfComponent: EpgContentUnshelfComponent
-
-
-  title: string = $localize`EPG内容`;
-
-  epgs: any[];  // 所有epg列表
-  epgOptions: any[] = [];
-
-  _epgId: string;  // 当前选中的epg
-  get epgId() {
-    return this._epgId;
-  }
-  set epgId(id: string) {
-    this._epgId = id;
-    this.colId = this.curColumns && this.curColumns.length > 0 ? this.curColumns[0].id : null;
-    this.loadSchedule();
-  }
-
-  _colId: string;  // 当前选中的栏目
-  get colId() {
-    return this._colId;
-  }
-  set colId(id: string) {
-    this._colId = id;
-  }
-
-  get curEpg(): any {
-    return this.epgs?.find(e => e.epgId == this.epgId);
-  }
-
-  get curColumns() {
-    let columns = this.epgs?.find(e => e.epgId == this.epgId)?.columns || [];
-    return columns;
-  }
-
-
-  get curColIdx(): number {
-    let colIdx = this.curColumns?.findIndex(e => e.id == this.colId);
-    if (colIdx < 0) colIdx = 0;
-    return colIdx;
-  }
-
-  currentTab: number = 1; // 当前标签页
-
-  ////////////////////////// 上架计划schedule相关 /////////////////////
-  schedule: any; // 上架计划
-  _scheduleTime: Date;  // 计划上架日期
-  get scheduleTime() {
-    return this._scheduleTime;
-  }
-  set scheduleTime(time: Date) {
-    this._scheduleTime = time;
-    if (this.getScheduleCount(time) > 0) {// 选择查看某天的上架计划
-      this.onshelfComponent.filterSchedule(time);
-    }
-  }
-
-  getScheduleCount(current: Date) {
-    for (let dateStr of Object.keys(this.schedule)) {
-      let date = new Date(dateStr);
-      if (format(date, 'yyyyMMdd') == format(current, 'yyyyMMdd')) {
-        return this.schedule[dateStr];
-      }
-    }
-  }
-  loadSchedule() {
-    if (this.epgId) {
-      firstValueFrom(this.epgService.epgContentSchedule(this.epgId))
-        .then((resp: any) => {
-          this.schedule = resp;
-        })
-    }
-  }
-  ///////////////////////////////////////////////////////////
-
-  constructor(
-    private modal: NzModalService,
-    private message: NzMessageService,
-    private epgService: EpgService,
-    private router: Router,
-    private activatedRoute: ActivatedRoute,
-    public i18n: NzI18nService,
-  ) {
-    this.epgId = this.activatedRoute.snapshot.params['epgId'];
-    this.colId = this.activatedRoute.snapshot.params['colId'];
-    this.loadData();
-  }
-
-  subscription: any;
-  ngOnInit(): void {
-    // 监听路由变化,页面相应变更
-    this.subscription = this.router.events.pipe(
-      filter(event => event instanceof NavigationEnd),
-    ).subscribe((event) => {
-      this.epgId = this.activatedRoute.snapshot.params['epgId'] || this.epgId;
-      this.colId = this.activatedRoute.snapshot.params['colId'] || this.colId;
-    })
-  }
-
-  ngOnDestroy(): void {
-    // 清理subscribe,避免内存泄漏,否则已经跳到其他页面了subscribe还一直触发
-    this.subscription.unsubscribe();
-  }
-
-  loadData() {
-    firstValueFrom(this.epgService.epgAll())
-      .then((resp: any) => {
-        this.epgs = resp.data || [];
-        // add default all column
-        this.epgs.forEach(e => {
-          e.columns.push({
-            id: 'all',
-            valid: true,
-            visible: true,
-            en: 'All',
-            zh: '全部',
-          })
-        })
-        console.log(this.epgs);
-
-        this.epgOptions = this.epgs.map(epg => {
-          return { label: epg.desc, value: epg.epgId };
-        });
-        if (!this.epgId) {
-          this.epgId = this.epgs[0]?.epgId;  // todo:get from router or set first one as default 
-        }
-        if (!this.colId) {
-          this.colId = this.curColumns ? this.curColumns[0].id : null;
-          if (this.colId) {
-            this.onColumnClick(this.colId);
-          }
-        }
-      })
-
-  }
-
-  // 判断某栏目是否特殊栏目(系统保留虚拟栏目) 
-  isSpecialColumn(col: string) {
-    return [undefined, null, '', 'all', 'others'].includes(col?.toLowerCase());
-  }
-
-  onEpgChange(value: string) {
-    let url = this.colId ? `/epg/content/${this.epgId}/${this.colId}` : `/epg/content/${this.epgId}`
-    this.router.navigateByUrl(url);
-  }
-
-  onColumnClick(id: string) {
-    this.colId = id;
-    this.router.navigateByUrl(`/epg/content/${this.epgId}/${this.colId}`);
-  }
-
-  getLink(colId: string) {
-    return `/epg/content/${this.epgId}/${colId}`;
-  }
-  getQueryParams() {
-    return { orderBy: this.onshelfComponent.inputSort[0] };
-  }
-
-  // 打开上架发布界面 
-  openPublishModal(content: TemplateRef<any>) {
-    this.modal.create({
-      nzContent: content,
-      nzTitle: $localize`发布上架选择`,
-      nzCentered: true,
-      nzWidth: '90vw',
-      nzOnOk: this.showPublishConfirm.bind(this)
-    })
-  }
-
-
-  // 显示下架确认
-  showUnpublishConfirm(): void {
-    if (this.onshelfComponent.checkIdSet.size <= 0) {
-      this.message.error($localize`未选中任何记录`);
-      return;
-    }
-
-    let idList = Array.from(this.onshelfComponent.checkIdSet);
-    this.modal.confirm({
-      nzTitle: $localize`确认下架吗?`,
-      nzOkText: $localize`确认`,
-      nzCancelText: $localize`取消`,
-      nzAutofocus: 'cancel',
-      nzOkType: 'primary',
-      nzOkDanger: true,
-      nzOnOk: () => this.onshelfComponent.unpublish(idList),
-      nzOnCancel: () => console.log('Cancel')
-    });
-  }
-
-  showPublishConfirm(): boolean {
-    this.unshelfComponent.showPublishConfirm(this.unshelfComponent.selectList, true);
-    return false;
-  }
-
-  onPubSuccess() { // 发布成功, 已上架列表也要刷新下
-    this.onshelfComponent.refreshTrigger++;
-    this.loadSchedule();
-  }
-
-  showPreview(content: TemplateRef<any>): void {
-    this.modal.create({
-      nzContent: content,
-      nzCentered: true,
-      nzWidth: '50vh',
-      nzFooter: null,
-      nzBodyStyle: { 'background-color': 'rgba(0,0,0,0.5)' },
-    })
-  }
-}

+ 0 - 150
zorro/src/app/pages/epg/epg-info.component.ts

@@ -1,150 +0,0 @@
-import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
-import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms';
-import { NzMessageService } from 'ng-zorro-antd/message';
-import { firstValueFrom } from 'rxjs';
-import { FormUtils } from 'src/app/lib/utils/form-utils';
-import { EpgService } from './epg.service';
-
-@Component({
-  selector: 'app-epg-info',
-  template: `
-
-<form [formGroup]="form" autocomplete="off" (ngSubmit)="onSubmit()" novalidate class="form-horizontal ">
-  <nz-form-item>
-    <nz-form-label [nzSpan]="5" nzFor="epgId">EPG ID</nz-form-label>
-    <nz-form-control>
-      <nz-input-group>
-        <input nz-input formControlName="epgId" placeholder="epg id">
-      </nz-input-group>
-    </nz-form-control>
-  </nz-form-item>
-
-  <nz-form-item>
-    <nz-form-label [nzSpan]="5" nzFor="desc" i18n>描述</nz-form-label>
-    <nz-form-control>
-      <nz-input-group nzPrefixIcon="edit">
-        <input nz-input formControlName="desc" i18n-placeholder placeholder="描述">
-      </nz-input-group>
-    </nz-form-control>
-  </nz-form-item>
-
-  <nz-form-item>
-    <nz-form-label [nzSpan]="5" nzFor="createBy" i18n>创建者</nz-form-label>
-    <nz-form-control>
-      <nz-input-group>
-        <input nz-input formControlName="createBy" i18n-placeholder placeholder="创建者">
-      </nz-input-group>
-    </nz-form-control>
-  </nz-form-item>
-
-  <nz-form-item>
-    <nz-form-label [nzSpan]="5" nzFor="createTime" i18n>创建时间</nz-form-label>
-    <nz-form-control>
-      <nz-input-group>
-        <input nz-input formControlName="createTime">
-      </nz-input-group>
-    </nz-form-control>
-  </nz-form-item>
-
-  <nz-form-item>
-    <nz-form-label [nzSpan]="5" nzFor="lastModTime" i18n>上次修改时间</nz-form-label>
-    <nz-form-control>
-      <nz-input-group>
-        <input nz-input formControlName="lastModTime">
-      </nz-input-group>
-    </nz-form-control>
-  </nz-form-item>
-
-  <nz-form-item>
-    <nz-form-label [nzSpan]="5" nzFor="status" i18n>状态</nz-form-label>
-    <nz-form-control>
-      <nz-input-group>
-        <input nz-input formControlName="status">
-      </nz-input-group>
-    </nz-form-control>
-  </nz-form-item>
-
-
-  <div class="d-flex flex-row-reverse" >
-    <button nz-button nzType="primary" type="submit" [disabled]="isLoading" i18n>确认</button>
-  </div>
-
-</form>
-
-  `,
-  styles: [
-  ]
-})
-export class EpgInfoComponent implements OnInit {
-  @Output() onSuccess = new EventEmitter<boolean>();
-  @Output() onError = new EventEmitter<any>();
-  
-  @Input() epg: any;
-
-  form: FormGroup;
-  isLoading = false;
-
-  constructor(
-    private epgService: EpgService,
-    private fb: FormBuilder,
-    private message: NzMessageService,
-  ) { }
-
-  ngOnInit(): void {
-    this.form = this.fb.group({
-      epgId: new FormControl({value: this.epg.epgId, disabled: true}),
-      desc: new FormControl(this.epg.desc),
-      createBy: new FormControl({value: this.epg.createBy.username, disabled: true}),
-      createTime: new FormControl({value: this.epg.createTime, disabled: true}),
-      lastModTime: new FormControl({value: this.epg.lastModTime, disabled: true}),
-      status: new FormControl({value: this.getStatusTitle(this.epg.status), disabled: true}),
-    })
-  }
-
-  
-  onSubmit() {
-    if (this.form.invalid) {
-      FormUtils.markAsDirty(this.form);
-      return;
-    }
-
-    if (!this.form.dirty) {
-      this.onSuccess.emit(false);
-      return;
-    }
-
-    this.isLoading = true;
-    let data = [Object.assign(this.form.value, { _id: this.epg._id })];
-    firstValueFrom(this.epgService.epgUpdate(data))
-      .then(res => {
-        this.onSuccess.emit(true);
-        this.message.success($localize`修改成功`);
-      })
-      .catch(err => {
-        this.message.error(err.error?.message || err.message);
-        this.onError.emit(err);
-      }).then(() => {
-        this.isLoading = false;
-      })
-  }
-
-  getStatusTitle(status: number) {
-    let title: string;
-    switch(status) {
-    case 100:
-      title = $localize`不可用`;
-      break;
-    case 500:
-      title = $localize`可用`;
-      break;
-    case 1000:
-      title: $localize`已删除`;
-      break;
-    default:
-      title = $localize`未知`;
-      break;
-    }
-    return title;
-  }
-
-}

+ 0 - 468
zorro/src/app/pages/epg/epg-list.component.ts

@@ -1,468 +0,0 @@
-import { Component, OnInit, TemplateRef } from '@angular/core';
-import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop';
-import { EpgService } from './epg.service';
-import { FormBuilder, FormGroup, FormArray, Validators } from '@angular/forms';
-import { firstValueFrom } from 'rxjs';
-import { NzMessageService } from 'ng-zorro-antd/message';
-import { NzModalService } from 'ng-zorro-antd/modal';
-import { FormUtils } from 'src/app/lib/utils/form-utils';
-
-
-@Component({
-  selector: 'app-epg-list',
-  template: `
-
-<!-- <nz-spin [nzDelay]="500" [nzSpinning]="isLoading"> -->
-
-<nz-page-header nzTitle="{{title}}">
-  <nz-page-header-extra>
-    <button class="btn " *can="['create', 'any', 'epg']" (click)="openModal(epgAdd, modalTitle('epgAdd'))" i18n>新建EPG</button>
-    <button class="btn " *can="['update', 'any', 'epg']" (click)="openModal(columnAdd, modalTitle('columnAdd'))" i18n>添加栏目</button>
-    <button class="btn" (click)="onReset()" i18n>重置</button>
-    <nz-badge nzDot [nzShowDot]="dirty && dirty.length > 0">
-      <button class="btn" *can="['update', 'any', 'epg']" (click)="onSave()" i18n>保存</button>
-    </nz-badge>
-    <button class="btn" *can="['update', 'any', 'epg']" (click)="onPublish()" i18n>发布</button>
-  </nz-page-header-extra>
-</nz-page-header>
-
-<ng-template #epgAdd let-modal="modalRef">
-  <app-epg-add (onSuccess)="loadData(); modal.close();"></app-epg-add>
-</ng-template>
-
-<ng-template #columnAdd let-modal="modalRef">
-  <app-column-add [epgId]="table.epgs[curEpgIdx]._id" (onSuccess)="loadData(); modal.close();"></app-column-add>
-</ng-template>
-
-<div>
-      <div nz-row *ngIf="table" [nzGutter]="[20, 20]" [nzJustify]="'start'">
-        <div nz-col [nzSpan]="3"  *ngFor="let epg of table.epgs; let i = index">
-            <nz-card class="card-config" [ngClass]="{'card-select': curEpgIdx == i}" 
-              [nzHoverable]="true" [nzSize]="'small'" [nzBodyStyle]="{'padding': '0'}" 
-              [nzActions]="[actionEdit, actionCopy, actionDelete]" (click)="onSelect(i)">
-              <p class="title">{{epg.epgId}}</p>
-              <p class="desc">{{epg.desc}}</p>
-            </nz-card>
-            <ng-template #epgInfo let-modal="modalRef">
-              <app-epg-info [epg]="epg" (onSuccess)="$event? loadData(): null; modal.close();"></app-epg-info>
-            </ng-template>
-            <ng-template #epgCopy let-modal="modalRef">
-              <app-epg-copy [srcEpg]="epg" (onSuccess)="$event? loadData(): null; modal.close();"></app-epg-copy>
-            </ng-template>
-            <ng-template #actionEdit>
-              <i nz-icon nzType="edit" *can="['update', 'any', 'epg']" (click)="openModal(epgInfo, modalTitle('epgInfo'))"></i>
-            </ng-template>
-            <ng-template #actionDelete>
-              <i nz-icon nzType="delete" *can="['delete', 'any', 'epg']" (click)="showDeleteConfirm(epg)"></i>
-            </ng-template>
-            <ng-template #actionCopy>
-              <i nz-icon nzType="copy" *can="['create', 'any', 'epg']" (click)="openModal(epgCopy, modalTitle('epgCopy'))"></i>
-            </ng-template>
-        </div>
-      </div>
-</div>
-
-<form *ngIf="table && table.epgs && table.epgs.length > 0" [formGroup]="form" class="table-scroll">
-  <nz-table nzSize="small" [nzTemplateMode]="true" class="table table-hover table-sm  table-edit" [nzScroll]="{ x: '135%' }" >
-    <thead>
-      <tr>
-        <th [ngStyle]="{'font-weight':'bold'}" i18n>栏目ID</th>
-        <th [ngStyle]="{'font-weight':'bold'}" i18n>是否可见</th>
-        <ng-container *ngFor="let lang of table.langs; let i=index">
-          <th [ngStyle]="{'font-weight': lang=='zh-CN' || lang== 'en' ? 'bold' : 'normal'}">{{table.langHeaders[i]?.label}}</th>
-        </ng-container>
-        <ng-container *can="['update', 'any', 'epg']">
-          <th [nzWidth]="'60px'" nzRight i18n>操作</th>
-        </ng-container>
-      </tr>
-    </thead>
-    <tbody cdkDropList (cdkDropListDropped)="dragdrop($any($event))">
-      <ng-container [formGroup]="currentEpg">
-      <ng-container formArrayName="columns" *ngFor="let group of currentColumns?.controls; let i = index">
-        <tr [formGroupName]="i" cdkDrag>
-          <td class="form-group align-middle">
-            <input nz-input type="text" formControlName="id" placeholder="">
-          </td>
-
-          <td class="form-group align-middle">
-            <span class="toggle-checkbox checkbox-inline toggle-sm mrg-top-10">
-              <input type="checkbox" formControlName="visible" id="check_{{i}}">
-              <label for="check_{{i}}"></label>
-            </span>
-          </td>
-
-          <ng-container *ngFor="let lang of table.langs">
-            <td class="form-group align-middle">
-              {{group.get('translate').value[lang]}}
-              <!-- {{table.epgs[curEpgIdx].columns[i].translate[lang]}} -->
-                <!-- {{group.get(lang).value}} -->
-              <!-- <input nz-input type="text"
-                [ngClass]="{ 'is-invalid':  group.get(lang).invalid, 'is-valid' : group.get(lang).valid }"
-                [formControlName]="lang" placeholder=""> -->
-            </td>
-          </ng-container>
-
-          <ng-container *can="['update', 'any', 'epg']">
-            <td class="form-group align-middle" nzRight>
-              <a nz-popconfirm i18n-nzPopconfirmTitle nzPopconfirmTitle="确定删除吗?" (nzOnConfirm)="onDeleteCol(group.get('id').value)">
-                <i nz-icon nzType="delete" nzTheme="fill"></i>
-              </a>
-            </td>
-          </ng-container>
-
-        </tr>
-      </ng-container>
-      </ng-container>
-    </tbody>
-  </nz-table>
-</form>
-
-<!-- </nz-spin> -->
-
-  `,
-  styles: [`
-
-
-.card-config {
-  height: 142px;
-  width: 147px;
-  text-align:center;
-  border-radius: 4px;
-}
-
-.card-select {
-  background: dodgerblue;
-  color: white;
-}
-
-.title {
-  font-size: 30px;
-  font-weight: 600;
-  margin: 8px 0px 6px 0px;
-}
-
-.desc {
-  font-size: 12px;
-}
-
-
-.is-changed input{
-  color: darkcyan;
-}
-
-.table th,
-.table td{
-  background: white;
-}
-
-.table td{
-  width:150px;
-}
-
-.table td a {
-  color: dodgerblue;
-}
-
-.table td a:hover {
-  color: red;
-}
-
-
-.table-scroll {
-  position: relative;
-  width:100%;
-  z-index: 1;
-  margin: auto;
-  overflow: auto;
-}
-.table-scroll table {
-  width: 100%;
-  min-width: 1280px;
-  margin: auto;
-  border-collapse: collapse;
-  border-spacing: 0;
-}
-.table-wrap {
-  position: relative;
-}
-.table-scroll th,
-.table-scroll td {
-  vertical-align: center;
-}
-.table-scroll thead th {
-  position: -webkit-sticky;
-  position: sticky;
-  top: 0;
-}
-/* safari and ios need the tfoot itself to be position:sticky also */
-.table-scroll tfoot,
-.table-scroll tfoot th,
-.table-scroll tfoot td {
-  position: -webkit-sticky;
-  position: sticky;
-  bottom: 0;
-  z-index:4;
-}
-
-a:focus {
-  background: red;
-} /* testing links*/
-
-th,
-th:first-child {
-  position: -webkit-sticky;
-  position: sticky;
-  left: 0;
-  z-index: 2;
-}
-th,
-thead th:first-child,
-tfoot th:first-child {
-  z-index: 5;
-}
-
-input{
-  min-width: 100px;
-}
-
-::ng-deep .cdk-drag-preview {
-  display: table;
-}
-
-::ng-deep .cdk-drag-placeholder {
-  opacity: 0;
-}
-
-    `]
-})
-
-
-export class EpgListComponent implements OnInit {
-  title: string = $localize`EPG管理`;
-  table: any;
-  form: FormGroup;
-  curEpgIdx: number = 0;
-  lastEpgIdx: number = 0;
-  isLoading: boolean = false;
-
-  constructor(
-    private epgService: EpgService,
-    private fb: FormBuilder,
-    private message: NzMessageService,
-    private modal: NzModalService,
-  ) { }
-
-  ngOnInit(): void {
-    this.loadData();
-  }
-
-  loadData() {
-    this.isLoading = true;
-    const controls = this.fb.array([]);
-    firstValueFrom(this.epgService.epgTable()).then((table: any) => {
-      this.table = table;
-      this.table.epgs.forEach(epg => {
-        controls.push(this.createGroup(table.langs, epg));
-      })
-      this.isLoading = false;
-
-      this.form = this.fb.group({
-        epgs: controls,
-      });
-
-    })
-
-  }
-
-  createGroup(langs: any, epg: any) {
-    // let useEpg = Object.assign({}, epg);
-    // useEpg.columns.forEach(col => {
-    //   col.translate = col.translate._id;
-    // })
-    let group = this.fb.group({ columns: this.fb.array([]) });
-    group.addControl('_id', this.fb.control(null, Validators.required));
-    group.addControl('epgId', this.fb.control(null, Validators.required));
-    group.addControl('desc', this.fb.control(null, Validators.required));
-    group.addControl('createTime', this.fb.control(null, Validators.required));
-    epg.columns.forEach(col => {
-      let subgroup = this.fb.group({});
-      subgroup.addControl('id', this.fb.control(null, Validators.required));
-      subgroup.addControl('visible', this.fb.control(null, Validators.required));
-      subgroup.addControl('translate', this.fb.control(null, Validators.required));
-      // langs.forEach(lang => {
-      //   subgroup.addControl(lang, this.fb.control(null, Validators.required))
-      // })
-      subgroup.reset(col);
-      (group.get('columns') as FormArray).push(subgroup);
-    })
-    group.reset(epg);
-    return group;
-  }
-
-  get getFormData(): FormArray {
-    return <FormArray>this.form.get('epgs');
-  }
-
-  // 当前选中的EPG
-  get currentEpg(): FormGroup {
-    let controls = <FormArray>this.form.get('epgs');
-    let group: FormGroup = <FormGroup>controls.at(this.curEpgIdx);
-    return group;
-  }
-
-  // 当前选中的EPG的栏目列表
-  get currentColumns(): FormArray {
-    let controls = <FormArray>this.form.get('epgs');
-    let group = controls.controls[this.curEpgIdx];
-    let subcontrols = group?.get('columns') as FormArray;
-    return subcontrols;
-  }
-
-  get dirty() {
-    let controls: FormArray = <FormArray>this.form?.get('epgs');
-    let dirty = [];
-    if (!controls) return dirty;
-    
-    for (var i = 0; i < controls.length; i++) {
-      let group: FormGroup = <FormGroup>controls.at(i);
-      if (group.dirty) {
-        let dirtyValues: any = {};
-        dirtyValues._id = group.value._id;
-        Object.keys(group.controls).forEach(key => {
-          let control = group.controls[key];
-          if (control.dirty) {
-            dirtyValues[key] = control.value;
-          }
-        })
-        dirty.push(dirtyValues);
-      }
-    }
-    return dirty;
-  }
-
-  onSelect(index) {
-    if (this.curEpgIdx != index) {
-      this.lastEpgIdx = this.curEpgIdx;
-      this.curEpgIdx = index;
-    }
-  }
-
-  onReset() {
-    let groups = <FormArray>this.form.get('epgs');
-    for (var i = 0; i < groups.controls.length; i++) {
-      groups.controls[i].reset(this.table.epgs[i]);
-    }
-  }
-
-  onPublish() {
-    let data = { id: this.table.epgs[this.curEpgIdx]._id };
-    firstValueFrom(this.epgService.epgPublish(data))
-    .then(res => {
-      this.message.success($localize`发布成功!`);
-    }).catch(err => {
-      this.message.error(err.error?.message || err.message);
-    });
-  }
-
-  onSave() {
-    let data = this.dirty;
-    data.forEach(epg => {
-      epg.columns.forEach(col => {
-        col.translate = col.translate._id;
-      })
-    })
-    if (!data || data.length == 0) {
-      this.message.info($localize`没有更新`);
-      return;
-    }
-    firstValueFrom(this.epgService.epgUpdate(data))
-      .then(res => {
-        this.loadData();
-        this.message.success($localize`保存成功!`);
-      }).catch(err => {
-        this.message.error(err.error?.message || err.message);
-      });
-  }
-
-  // 删除一行栏目
-  onDeleteCol(colId: string) {
-    let data = {
-      _id: this.table.epgs[this.curEpgIdx]._id,
-      id: colId,
-    }
-    firstValueFrom(this.epgService.columnRemove(data))
-      .then(res => {
-        this.loadData();
-        this.message.success($localize`删除成功!`);
-      }).catch(err => {
-        this.message.error(err.error?.message || err.message);
-      });
-  }
-
-  // 删除epg
-  onDeleteEpg(id: string) {
-    this.curEpgIdx = this.lastEpgIdx;
-    firstValueFrom(this.epgService.epgDelete(id))
-      .then(res => {
-        this.loadData();
-        this.message.success($localize`删除成功!`);
-        console.log("=================curEpgIdx:" + this.curEpgIdx);
-      }).catch(err => {
-        this.message.error(err.error?.message || err.message);
-      });
-  }
-
-  modalTitle(key: string) {
-    let title = '';
-    switch(key) {
-    case 'epgAdd':
-      title = $localize`新建EPG`;
-      break;
-    case 'columnAdd':
-      title = $localize`添加栏目`;
-      break;
-    case 'epgInfo':
-      title = $localize`EPG 信息`;
-      break;
-    case 'epgCopy':
-      title = $localize`EPG克隆`;
-      break;
-    default:
-      break;
-    }
-    return title;
-  }
-  openModal(content: TemplateRef<any>, title: string, width: number = 520) {
-    this.modal.create({
-      nzContent: content,
-      nzTitle: title,
-      nzFooter: null,
-      nzWidth: width,
-    })
-  }
-
-  showDeleteConfirm(epg: any): void {
-    this.modal.confirm({
-      nzTitle: $localize`确认删除 ${epg.epgId} EPG吗?`,
-      nzContent: $localize`<b style="color: red;">删除EPG同时栏目信息, 内容发布信息将一并清除, 请谨慎操作!</b>`,
-      nzOkText: $localize`确认`,
-      nzCancelText: $localize`取消`,
-      nzAutofocus: 'cancel',
-      nzOkType: 'primary',
-      nzOkDanger: true,
-      nzOnOk: () => this.onDeleteEpg(epg._id),
-      nzOnCancel: () => console.log('Cancel')
-    });
-  }
-
-  // 栏目拖拽排序
-  dragdrop(event: CdkDragDrop<string[]>): void {
-
-    moveItemInArray(this.currentColumns.controls, event.previousIndex, event.currentIndex);
-    moveItemInArray(this.currentColumns.value, event.previousIndex, event.currentIndex);
-    
-    this.currentColumns.markAsDirty();
-
-  }
-
-}

+ 0 - 209
zorro/src/app/pages/epg/epg-preview.component.ts

@@ -1,209 +0,0 @@
-import { ThisReceiver } from '@angular/compiler';
-import { Component, ElementRef, Input, OnInit, ViewChild } from '@angular/core';
-import { NzI18nService } from 'ng-zorro-antd/i18n';
-import { NzMessageService } from 'ng-zorro-antd/message';
-import { firstValueFrom } from 'rxjs';
-import { EpgService } from './epg.service';
-
-@Component({
-  selector: 'app-epg-preview',
-  template: `
-    <div class="background">
-        <div class="header">
-          <div class="title" i18n>画廊</div>
-          <div #columnsDiv class="columns">
-            <span class="column select" *ngFor="let col of columns; let i=index" (click)="onColumnClick(col.id)"
-              [ngClass]="{'column-select': colId == col.id}">
-              {{i18n.getLocaleId() == 'en' ? col.en : col.zh}}
-            <!-- {{col.zh}} -->
-            </span>
-          </div>
-        </div>
-        <div class="content">
-          <div nz-row [nzGutter]="[8, 8]" class="pt-2 pb-2 scroll ">
-            <div nz-col [nzSpan]="12"
-              *ngFor="let item of dataList; let i=index" class="rounded d-flex flex-column text-secondary ">
-              <div class="img d-flex  flex-column align-items-center position-relative">
-                <img [src]="item?.thumb" loading="lazy"/>
-                <nz-tag  *ngIf="item?.shelfStatus == 500" nzColor="green" class="schedule" i18n>即将发布</nz-tag>
-                <!-- <span *ngIf="item?.shelfStatus == 500" class="schedule" i18n>即将发布</span> -->
-              </div>
-            </div>
-          </div>
-        </div>
-    </div>
-  `,
-  styles: [`
-    .background {
-      height: 90vh;
-      width: 100%;
-      padding: 50px 15px 10px 15px;
-      background-image: url('../../../assets/images/phone.png');
-      background-repeat: no-repeat;
-      background-size:100% 100%;
-    }
-
-    .header {
-      background: #fff;
-      line-height: 20px;
-    }
-
-    .content {
-      background: #f5f5f5;
-      margin-top: 6px;
-      height: 85%;
-      overflow: auto;
-      overflow-x: hidden;
-    }
-    .content::-webkit-scrollbar { 
-      display: none;
-    }
-
-    .title {
-      margin-left: 10px;
-      font-size: 18px; 
-      font-weight: bold;
-      line-height: 20px;
-    }
-
-    .columns {
-      margin-left: -6px;
-      text-align:left;
-      white-space: nowrap;  /*控制不要换行*/
-      overflow: auto;
-      margin-top: 15px;
-      font-size: 13px;
-      line-height: 13px;
-      font-weight: 600;
-      /* Firefox hide scrollbar*/
-      scrollbar-width: none; 
-    }
-    /*webkit browser hide scrollbar*/
-    .columns::-webkit-scrollbar { 
-      display: none;
-    }
-
-    .column {
-      cursor: pointer;
-      display:inline-block;
-      position: relative;
-      margin-left: 6px;
-      padding: 3px 6px;
-      background: transparent;
-    }
-
-    .column-select {
-      color: #ff8c00;
-      background: #ddd;
-      border-style: solid;
-      border-width: 1px;
-      border-color: #fff8dc;
-    }
-
-    .column:hover {
-      color: #ff8c00;
-      background: #F2F2F2;
-    }
-
-    div.img{
-      width:100%;
-      aspect-ratio: 1 / 1;
-      overflow:hidden;
-      background-color:#fefefe;
-    }
-
-    img{
-      height:100%;
-      cursor : pointer;
-    }
-
-    .schedule {
-      top:0px;
-      right:0px;
-      font-size: 10px !important;
-      position: absolute;
-      text-align: center;
-      line-height: 12px;
-      width:40px;
-      height:12px;
-      // border-radius: 50%;
-      // background: #999;
-      // color:green;
-      // text-shadow: none;
-      // cursor: pointer;
-    }
-
-  `
-  ]
-})
-export class EpgPreviewComponent implements OnInit {
-  @ViewChild('columnsDiv') columnsDiv: ElementRef;
-
-  dataHash: any = {};
-
-  @Input() epgInfo: any;
-
-  _colId: string;  // 当前选中的栏目
-  @Input() set colId(value: string) {
-    this._colId = value;
-  }
-  get colId() {
-    return this._colId;
-  }
-
-  get columns() {
-    return this.epgInfo.columns || [];
-  }
-
-  get curColIdx(): number {
-    let colIdx = this.columns?.findIndex(e => e.id == this.colId);
-    if (colIdx < 0) colIdx = 0;
-    return colIdx;
-  }
-
-  get dataList(): any[] {
-    let list = this.dataHash[this.colId];
-    if (!list) {
-      this.dataHash[this.colId] = [];
-      list = [];
-      this.loadData(this.colId);
-    }
-    return list;
-  }
-
-  constructor(
-    private message: NzMessageService,
-    private epgService: EpgService,
-    public i18n: NzI18nService,
-  ) { }
-
-  ngOnInit(): void {
-  }
-
-  ngAfterViewInit(): void {
-    this.adjustScroll();
-  }
-
-  loadData(colId: string) {
-    firstValueFrom(this.epgService.epgContentList(this.epgInfo.epgId, colId))
-      .then((resp: any) => {
-        this.dataHash[colId] = resp.data || [];
-      });
-  }
-
-  onColumnClick(colId: string) {
-    this.colId = colId;
-    this.adjustScroll();
-  }
-
-  // 调整栏目进度条
-  adjustScroll() {
-    let divElem = this.columnsDiv.nativeElement as HTMLDivElement;
-    let viewWidth = divElem.clientWidth;  //视窗宽度
-    let count = viewWidth / (divElem.scrollWidth / this.columns.length); //视窗内显示了多少个元素
-    let width = viewWidth / count; //每个元素占用的宽度
-    let scrollPos = (divElem.scrollWidth / this.columns.length) * this.curColIdx - count * width / 2 + width / 2;
-    divElem.scroll({ left: scrollPos, top: 0, behavior: 'smooth' });
-  }
-
-}

+ 0 - 86
zorro/src/app/pages/epg/epg-publish-confirm.component.ts

@@ -1,86 +0,0 @@
-import { ChangeDetectorRef, Component, Input, OnInit } from '@angular/core';
-
-@Component({
-  selector: 'epg-publish-confirm',
-  template: `
-<div style="color: orange; margin-bottom: 10px">
-  <i nz-icon nzType="info-circle" nzTheme="outline"></i>
-  {{tip}}
-</div>
-<nz-table #basicTable  nzSize="small" [nzFrontPagination]="false" [nzShowPagination]="false" [nzData]="publishList"
-  [nzScroll]="{ y: '500px' }" [nzTemplateMode]="true" class="table table-hover table-sm  table-edit">
-      <thead>
-        <tr>
-          <th i18n>作品</th>
-          <th i18n>效果图</th>
-          <!-- <th i18n>作品状态</th> -->
-          <th i18n>标签</th>
-          <th i18n>拟上架到</th>
-          <th i18n>操作</th>
-        </tr>
-      </thead>
-      <tbody *ngIf="basicTable.data && basicTable.data.length > 0">
-        <tr *ngFor="let data of basicTable.data; let i = index" class="editable-row">
-          <td>
-            {{data.data.name}}
-          </td>
-          <td>
-            <img nz-image width="50px" height="50px" nzSrc={{data.data.thumb}} loading="lazy" />
-          </td>
-          <!-- <td>
-            <art-status [showText]="true" [data]="data.data"></art-status>
-          </td> -->
-          <td>
-            <nz-tag *ngIf="data.data.lock" nzColor="magenta"> <i class="fal fa-lock"></i> </nz-tag>
-            <ng-container *ngIf="data.data.tags">
-              <nz-tag *ngFor="let tag of data.data.tags" class="clickable" nzColor="default" [innerHTML]="tag"></nz-tag>
-            </ng-container>
-          </td>
-          <td>
-            {{data.path}}
-          <td>
-            <a (click)="remove(i)" i18n>
-              撤销
-            </a>
-          </td>
-        </tr>
-      </tbody>
-    </nz-table>
-  `,
-  styles: [
-    `
-.table th {
-  font-weight: bold;
-}
-
-.table td a {
-  color: dodgerblue;
-}
-
-.table td a:hover {
-  color: forestgreen;
-}
-    `
-  ]
-})
-export class EpgPublishConfirmComponent implements OnInit {
-  @Input() publishList: any[];
-
-  @Input() tip: string;
-
-  @Input() ret: boolean;
-
-  constructor(private cdref: ChangeDetectorRef) { }  // 不这么做会抛ExpressionChangedAfterItHasBeenCheckedError异常, 搞不懂,先略过
-
-  ngOnInit(): void {
-  }
-
-  ngAfterContentChecked() {
-    this.cdref.detectChanges();
-  }
-
-  remove(index: number) {
-    this.publishList = this.publishList.filter((d, idx) => idx !== index);
-  }
-
-}

+ 0 - 21
zorro/src/app/pages/epg/epg-routing.module.ts

@@ -1,21 +0,0 @@
-import { NgModule } from '@angular/core';
-import { RouterModule, Routes } from '@angular/router';
-import { EpgContentComponent } from './epg-content.component';
-import { EpgListComponent } from './epg-list.component';
-
-const routes: Routes = [{
-  path: '',
-  children: [
-    { path: 'list', component: EpgListComponent, },
-    { path: 'content', component: EpgContentComponent, },
-    { path: 'content/:epgId', component: EpgContentComponent, },
-    { path: 'content/:epgId/:colId', component: EpgContentComponent, },
-    { path: '**', redirectTo: 'list' }
-  ]
-}];
-
-@NgModule({
-  imports: [RouterModule.forChild(routes)],
-  exports: [RouterModule]
-})
-export class EpgRoutingModule { }

+ 0 - 120
zorro/src/app/pages/epg/epg.module.ts

@@ -1,120 +0,0 @@
-import { NgModule } from '@angular/core';
-import { CommonModule } from '@angular/common';
-
-import { EpgRoutingModule } from './epg-routing.module';
-import { EpgListComponent } from './epg-list.component';
-import { AuthModule } from 'src/app/auth/auth.module';
-import { NzPageHeaderModule } from 'ng-zorro-antd/page-header';
-import { NzButtonModule } from 'ng-zorro-antd/button';
-import { FormsModule, ReactiveFormsModule } from '@angular/forms';
-import { NzFormModule } from 'ng-zorro-antd/form';
-// import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
-import { HttpClientModule } from '@angular/common/http';
-import { NzDescriptionsModule } from 'ng-zorro-antd/descriptions';
-import { NzDividerModule } from 'ng-zorro-antd/divider';
-import { NzDropDownModule } from 'ng-zorro-antd/dropdown';
-import { NzGridModule } from 'ng-zorro-antd/grid';
-import { NzIconModule } from 'ng-zorro-antd/icon';
-import { NzInputModule } from 'ng-zorro-antd/input';
-import { NzMessageModule } from 'ng-zorro-antd/message';
-import { NzModalModule } from 'ng-zorro-antd/modal';
-import { NzPopconfirmModule } from 'ng-zorro-antd/popconfirm';
-import { NzResultModule } from 'ng-zorro-antd/result';
-import { NzSelectModule } from 'ng-zorro-antd/select';
-import { NzSpinModule } from 'ng-zorro-antd/spin';
-import { NzTagModule } from 'ng-zorro-antd/tag';
-import { ToolboxModule } from '../toolbox/toolbox.module';
-import { NzCardModule } from 'ng-zorro-antd/card';
-import { NzTableComponent, NzTableModule } from 'ng-zorro-antd/table';
-import { EpgAddComponent } from './epg-add.component';
-import { ColumnAddComponent } from './column-add.component';
-import { EpgInfoComponent } from './epg-info.component';
-import { UtilsModule } from 'src/app/lib/utils/utils.module';
-import { DragDropModule } from '@angular/cdk/drag-drop';
-import { NzLayoutModule } from 'ng-zorro-antd/layout';
-import { NzTreeModule } from 'ng-zorro-antd/tree';
-import { NzTabsModule } from 'ng-zorro-antd/tabs';
-import { PagerModule } from 'src/app/lib/pager/pager.module';
-import { NzPaginationModule } from 'ng-zorro-antd/pagination';
-import { EpgContentOnshelfComponent } from './epg-content-onshelf.component';
-import { PageModule } from '../page/page.module';
-import { NzImageModule } from 'ng-zorro-antd/image';
-import { NzToolTipModule } from 'ng-zorro-antd/tooltip';
-import { OrderEditComponent } from './order-edit.component';
-import { EpgContentPagerComponent } from './epg-content-pager.component';
-import { NzEmptyModule } from 'ng-zorro-antd/empty';
-import { EpgPublishConfirmComponent } from './epg-publish-confirm.component';
-import { EpgCloneComponent } from './epg-clone.component';
-import { NzBadgeModule } from 'ng-zorro-antd/badge';
-import { EpgContentComponent } from './epg-content.component';
-import { EpgContentUnshelfComponent } from './epg-content-unshelf.component';
-import { EpgArtCardComponent } from './epg-art-card.component';
-import { EpgPreviewComponent } from './epg-preview.component';
-import { NzDatePickerModule } from 'ng-zorro-antd/date-picker';
-import { NzSwitchModule } from 'ng-zorro-antd/switch';
-import { EpgContentStatusComponent } from './epg-content-status.component';
-
-
-@NgModule({
-  declarations: [
-    EpgListComponent,
-    EpgAddComponent,
-    EpgInfoComponent,
-    ColumnAddComponent,
-    EpgContentOnshelfComponent,
-    OrderEditComponent,
-    EpgContentPagerComponent,
-    EpgPublishConfirmComponent,
-    EpgCloneComponent,
-    EpgContentComponent,
-    EpgContentUnshelfComponent,
-    EpgArtCardComponent,
-    EpgPreviewComponent,
-    EpgContentStatusComponent
-  ],
-  imports: [
-    CommonModule,
-    UtilsModule,
-    EpgRoutingModule,
-    FormsModule,
-    AuthModule,
-    PagerModule,
-    NzPageHeaderModule,
-    NzFormModule,
-    ReactiveFormsModule,
-    // NgbModule,
-    HttpClientModule,
-    ToolboxModule,
-    NzButtonModule,
-    NzDividerModule,
-    NzDropDownModule,
-    NzPageHeaderModule,
-    NzGridModule,
-    NzTagModule,
-    NzDescriptionsModule,
-    NzPopconfirmModule,
-    NzMessageModule,
-    NzSelectModule,
-    NzFormModule,
-    NzInputModule,
-    NzModalModule,
-    NzIconModule,
-    NzResultModule,
-    NzSpinModule,
-    NzTableModule,
-    NzCardModule,
-    DragDropModule,
-    NzLayoutModule,
-    NzTreeModule,
-    NzTabsModule,
-    NzPaginationModule,
-    PageModule,
-    NzImageModule,
-    NzToolTipModule,
-    NzEmptyModule,
-    NzBadgeModule,
-    NzDatePickerModule,
-    NzSwitchModule,
-  ]
-})
-export class EpgModule { }

+ 0 - 82
zorro/src/app/pages/epg/epg.service.ts

@@ -1,82 +0,0 @@
-import { Injectable } from '@angular/core';
-import { HttpClient } from '@angular/common/http';
-
-@Injectable({
-  providedIn: 'root'
-})
-export class EpgService {
-
-  constructor(private http: HttpClient) { }
-
-  epgTable() {
-    return this.http.get('/napi/web/epg/table');
-  }
-
-  epgAll() {
-    return this.http.get('/napi/web/epg/all');
-  }
-
-  epgOption() {
-    return this.http.get('/napi/web/epg/option');
-  }
-
-  epgUpdate(data: any) {
-    return this.http.patch('/napi/web/epg/update', data);
-  }
-
-  epgPublish(data: any) {
-    return this.http.patch('/napi/web/epg/publish', data)
-  }
-
-  epgAdd(data: string) {
-    return this.http.post('/napi/web/epg/add', data);
-  }
-
-  epgDelete(id: string) {
-    return this.http.delete('/napi/web/epg/' + id);
-  }
-
-  columnAdd(data: any) {
-    return this.http.post('/napi/web/epg/column/add', data);
-  }
-
-  columnRemove(data: any) {
-    return this.http.post('/napi/web/epg/column/remove', data);
-  }
-
-  // 更新,一般用于调整order
-  epgContentUpdate(epgId: string, data: any) {
-    return this.http.patch(`/napi/web/epgcontent/${epgId}/update`, data);
-  }
-
-  // 置顶
-  epgContentSetTop(epgId: string, data: any) {
-    return this.http.post(`/napi/web/epgcontent/${epgId}/settop`, data);
-  }
-
-  // 上架内容
-  epgContentOnshelf(data: any) {
-    return this.http.post(`/napi/web/epgcontent/onshelf`, data);
-  }
-
-  // 下架内容
-  epgContentOffshelf(data: any) {
-    return this.http.post(`/napi/web/epgcontent/offshelf`, data);
-  }
-
-  // v8接口 客户端获取栏目内容
-  epgContentList(epgId: string, colId: string) {
-    return this.http.get(`/napi/number/v8/list/${epgId}/preview?column=${colId}`);
-  }
-
-  // 获取某epg的上架计划
-  epgContentSchedule(epgId: string) {
-    return this.http.get(`/napi/web/epgcontent/${epgId}/schedule`);
-  }
-
-  // 作品加锁解锁
-  lock(artId: string, lock: boolean) {
-    return this.http.patch(`/napi/web/art/${artId}/lock`, { lock })
-  }
-
-}

+ 0 - 72
zorro/src/app/pages/epg/order-edit.component.ts

@@ -1,72 +0,0 @@
-import { Component, ElementRef, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core';
-
-@Component({
-  selector: 'order-edit',
-  template: `
-
-<div class="editable-cell" [hidden]="editId === data._id"
-  [ngStyle]="{'color': isUpdate? 'green' : 'black', 'font-weight' : isUpdate? 'bold' : 'normal'}"
-  (click)="startEdit(data._id, data.order)">
-  <!-- <p >{{data.order}}</p> -->
-  {{data.order}}
-</div>
-<input #inputElement nz-input [hidden]="editId !== data._id" type="number" 
-  required pattern="^(0|[1-9][0-9]*|-[1-9][0-9]*)$"
-  [(ngModel)]="data.order" #order="ngModel" 
-  (blur)="stopEdit(data, order)"
-  (keydown.enter)="stopEdit(data, order)"
-/>
-<div class="text-danger" *ngIf="order.invalid && order.dirty" i18n>输入正确整数</div>
-
-  `,
-
-  styles: [
-    `
-/* .editable-cell {
-  position: relative;
-  padding: 5px 12px;
-  cursor: pointer;
-}
-
-.editable-row:hover .editable-cell {
-  border: 1px solid #d9d9d9;
-  border-radius: 4px;
-  padding: 4px 11px;
-} */
-    `
-  ]
-})
-export class OrderEditComponent implements OnInit {
-  
-  @Input() data: any;
-  @Input() isUpdate: boolean = false;
-  @Input() canEdit: boolean = false;
-
-  @Output() onChange = new EventEmitter<any>(); 
-
-  constructor() { }
-
-  ngOnInit(): void {
-  }
-
-  editId: string | null = null;
-  editOriginValue: number;
-
-  startEdit(id: string, value: number): void {
-    if (!this.canEdit) return;
-    this.editId = id;
-    this.editOriginValue = value;
-  }
-
-  stopEdit(data: any, control: any): void {
-    if (control.invalid && control.dirty) {
-      data.order = this.editOriginValue;
-    } else if (data.order != this.editOriginValue) {
-      this.onChange.emit([{ _id: data._id, order: data.order }]);
-    }
-    
-    this.editId = null;
-    
-  }
-
-}

+ 0 - 117
zorro/src/app/pages/page/art-row.component.ts

@@ -1,117 +0,0 @@
-import { Component, EventEmitter, Input, OnInit, Output, ViewContainerRef } from '@angular/core';
-import { NzModalService } from 'ng-zorro-antd/modal';
-import { ArtService } from './art.service';
-import { PublishDetailComponent } from './publish-detail.component';
-
-@Component({
-  selector: 'art-row',
-  template: `
-  <div class="row p-2">
-    <div class="col">
-      <div class="image-box">
-        <img class="image" (click)="itemClick.emit(data)" [src]="data?.thumb" loading="lazy" />
-      </div>
-    </div>
-    <div class="col">
-      <div>
-        <i class="fal fa-lock-alt" *ngIf="data?.lock" [ngClass]="{'text-primary' : data?.lock}" ></i>
-        <i class="fal fa-lock-open-alt" *ngIf="!data?.lock" [ngClass]="{'text-primary' : data?.lock}"></i>
-      </div>
-    </div>
-    <div class="col">
-      <div>
-          <nz-tag *ngIf="data?.special" nzColor="purple" i18n>炫彩</nz-tag>
-          <nz-tag *ngIf="!data?.special" i18n>普通</nz-tag>
-      </div>
-    </div>
-    <div class="col">{{data?.areaCount}}</div>
-    <div class="col">{{data?.tags[0]}}</div>
-    <div class="col col-5">
-      <div>
-        <button *ngIf="!hideRemove" class="btn btn-sm me-2" (click)="onRemove.emit(data?._id)"><i class="fal fa-trash-alt"></i><ng-container i18n>移除</ng-container></button>
-        <button *ngIf="!data?.lock" cgOpButton class="btn btn-sm me-2" i18n-confirmContent confirmContent="确定要加锁?"
-            (onSuccess)="data=$event" [operation]="artService.lockOperation(data?._id, true)">
-            <i class="fal fa-lock"></i> <ng-container i18n>加锁</ng-container>
-        </button>
-        <button *ngIf=" data.lock" cgOpButton class="btn btn-sm me-2" i18n-confirmContent confirmContent="确定取消加锁?"
-          (onSuccess)="data=$event" [operation]="artService.lockOperation(data?._id, false)">
-          <i class="fal fa-lock-open"></i> <ng-container i18n>解锁</ng-container>
-        </button>
-        <button class="btn btn-sm" (click)="onClickShelfInfo(data)"><i class="fal fa-clock"></i><ng-container i18n>拟上架</ng-container></button>
-      </div>
-    </div>
-
-  </div>
-  `,
-  styles: [
-    `
-    :host{
-      height:80px;
-      display:block;
-      margin:2px;
-      background-color:#efefef;
-    }
-
-    .row{
-      height:100%;
-    }
-
-    .col{
-      height:100%;
-      vertical-align:middle;
-      display:flex;
-      flex-direction:column;
-      justify-content:center;
-    }
-
-    .image-box{
-      height:100%;
-    }
-
-    .image{
-      height:100%;
-      cursor: pointer;
-    }
-  
-    `
-  ]
-})
-export class ArtRowComponent implements OnInit {
-
-  @Input() data: any;
-  @Input() hideRemove: boolean = false;
-
-  @Input() hideAuthor: boolean = false;
-
-  @Output() onRemove = new EventEmitter<any>();
-  @Output() itemClick = new EventEmitter<string>();
-  @Output() itemUpdate = new EventEmitter<string>();
-
-
-  constructor( 
-    public artService : ArtService,
-    public modal: NzModalService,
-    private viewContainerRef: ViewContainerRef
-  ) { }
-
-  ngOnInit(): void {
-  }
-
-  // 点击查看上架详情
-  onClickShelfInfo(art: any) {
-    const modal = this.modal.create({
-      nzTitle: $localize`上架详情`,
-      nzCentered: true,
-      nzWidth: 750,
-      nzFooter: null,
-      nzContent: PublishDetailComponent,
-      nzViewContainerRef: this.viewContainerRef,
-      nzComponentParams: {
-        art,
-        onshelfNow: false,
-      },
-
-    })
-  }
-
-}

+ 17 - 240
zorro/src/app/pages/page/detail.component.html

@@ -12,93 +12,6 @@
         <nz-tag nzColor="#dc3545" *ngIf="art?.drop" i18n>已删除</nz-tag>
       </nz-page-header-subtitle>
       <nz-page-header-extra *ngIf="art">
-
-        <ng-container *ngIf="!art.drop">
-
-          <a *ngIf="art.auth?.canEdit" class="btn  " [routerLink]="['/art/play', art._id]">
-            <i class="fal fa-palette"></i> <ng-container i18n>上色/更新</ng-container>
-          </a>
-
-          <a *ngIf="!art.auth?.canEdit" class="btn  " [routerLink]="['/art/play', art._id]">
-            <i class="fal fa-palette"></i> <ng-container i18n>查看上色</ng-container>
-          </a>
-
-
-          <button *ngIf="art.auth.canLock && !art.lock" cgOpButton class="btn  " i18n-confirmContent confirmContent="确定要加锁?"
-            (onSuccess)="loadData()" [operation]="lock.bind(this)">
-            <i class="fal fa-lock"></i> <ng-container i18n>加锁</ng-container>
-          </button>
-
-          <button *ngIf="art.auth.canLock && art.lock" cgOpButton class="btn  " i18n-confirmContent confirmContent="确定取消加锁?"
-            (onSuccess)="loadData()" [operation]="unlock.bind(this)">
-            <i class="fal fa-lock-open"></i> <ng-container i18n>解锁</ng-container>
-          </button>
-
-          <button *ngIf="art.auth.canEdit" cgOpButton class="btn  " i18n-confirmContent confirmContent="确定要提交自测?"
-            (onSuccess)="loadData()" [operation]="selftest.bind(this)" i18n-successText successText="提交自测成功,计算可能需要几分钟,请稍候刷新APP">
-            <i class="fas fa-user-check"></i> <ng-container i18n>自测</ng-container>
-          </button>
-<!-- 
-          <button *ngIf="art.auth.canEdit" cgOpButton class="btn  " i18n-confirmContent confirmContent="确定要提交自测?" (onSuccess)="onSelfTestSuccess(); loadData()"
-            [operation]="selftest()" (onLoadingChange)="onSelfTestDoing($event)" (onFail)="onSelfTestFailed()">
-            <i class="fas fa-user-check"></i> <ng-container i18n>自测</ng-container>
-          </button> -->
-
-          <button *ngIf="art.auth.canEdit" cgOpButton class="btn  " i18n-confirmContent confirmContent="确定要提交测试?" (onSuccess)="loadData()"
-            [operation]="getStatusOperation(artStatus.TESTING)">
-            <i class="fal fa-arrow-circle-right"></i> <ng-container i18n>提交测试</ng-container>
-          </button>
-
-          <button *ngIf="!art.auth.canReject && art.auth.statusCanGoModify" cgOpButton class="btn  "
-            i18n-confirmContent confirmContent="确定要取消测试?" (onSuccess)="loadData()" [operation]="getStatusOperation(artStatus.REJECTED)">
-            <i class="fal fa-undo"></i> <ng-container i18n>返回修改</ng-container>
-          </button>
-
-          <button *ngIf="art.auth.canConfirmReady" cgOpButton class="btn  " i18n-confirmContent confirmContent="确定测试通过?"
-            (onSuccess)="loadData()" [operation]="getStatusOperation(artStatus.TESTED)">
-             <i class="fal fa-thumbs-up"></i> <ng-container i18n>测试通过</ng-container>
-          </button>
-
-          <!-- <button *ngIf="art.auth.canConfirmReady" class="btn  " (click)="showTestPassModal(art)">
-            <i class="fal fa-thumbs-up"></i> 测试通过
-          </button> -->
-
-          <button *ngIf="art.auth.canReject" cgOpButton class="btn  " i18n-confirmContent confirmContent="确定打回?" (onSuccess)="loadData()"
-            [operation]="getStatusOperation(artStatus.REJECTED)">
-            <i class="fal fa-thumbs-down"></i> <ng-container i18n>打回修改</ng-container>
-          </button>
-
-
-          <button *ngIf="art.auth.canPublish" cgOpButton class="btn  " i18n-confirmContent confirmContent="确定要发布?" (onSuccess)="loadData(); shouldOnshelfToEpg()"
-            [operation]="getStatusOperation(artStatus.ONLINE)">
-            <i class="fal fa-cloud-upload"></i> <ng-container i18n>发布</ng-container>
-          </button>
-
-          <button *ngIf="art.auth.canUnPublish" cgOpButton class="btn  " i18n-confirmContent confirmContent="确定要取消发布?"
-            (onSuccess)="loadData()" [operation]="getStatusOperation(artStatus.OFFLINE)">
-            <i class="fal fa-cloud-download"></i> <ng-container i18n>取消发布</ng-container>
-          </button>
-
-          <button *ngIf="art.auth.canEdit && art.hasSpecial" cgOpButton class="btn btn-outline-danger"
-            i18n-nzPopconfirmTitle nzPopconfirmTitle="确定要删除Special图么?" [operation]="deleteSpecial.bind(this)" (onSuccess)="loadData()">
-            <i class="fal fa-trash-alt"></i> <ng-container i18n>删除炫彩</ng-container>
-          </button>
-
-        </ng-container>
-
-        <button *ngIf="art.auth?.canRefuse" class="btn btn-outline-warning"
-          i18n-confirmContent confirmContent="确定拒稿么?" (click)="refuse()">
-        <i class="fal fa-times-circle"></i><ng-container i18n>拒稿</ng-container></button>
-
-        <button *ngIf="!art.drop && art.auth.canDrop" cgOpButton class="btn  btn-outline-danger"
-          i18n-confirmContent confirmContent="确定要删除作品么?" [operation]="drop.bind(this)" (onSuccess)="loadData()">
-          <i class="fal fa-trash-alt"></i><ng-container i18n>删除</ng-container></button>
-
-        <button *ngIf="art.drop && art.auth.canDrop" cgOpButton class="btn  btn-primary" i18n-confirmContent confirmContent="确定要恢复作品么?"
-          [operation]="recover.bind(this)" (onSuccess)="loadData()">
-          <i class="fal fa-undo-alt"></i><ng-container i18n>恢复</ng-container></button>
-
-
       </nz-page-header-extra>
 
       <nz-page-header-content *ngIf="!art && error">
@@ -114,18 +27,20 @@
           </nz-descriptions-item>
 
           <nz-descriptions-item nzTitle="{{title('name')}}">{{art.name}} &nbsp;&nbsp;
-            <button class="btn   border-0" (click)="openModal(meta, modalTitle('meta'))"> <i class="fal fa-edit m-0"></i> </button>
+            <button class="btn   border-0" (click)="openModal(meta, modalTitle('meta'))"> <i
+                class="fal fa-edit m-0"></i> </button>
           </nz-descriptions-item>
 
           <nz-descriptions-item nzTitle="{{title('desc')}}">{{art.desc}} &nbsp;&nbsp;
-            <button class="btn   border-0" (click)="openModal(meta, modalTitle('meta'))"> <i class="fal fa-edit m-0"></i> </button>
+            <button class="btn   border-0" (click)="openModal(meta, modalTitle('meta'))"> <i
+                class="fal fa-edit m-0"></i> </button>
           </nz-descriptions-item>
 
           <nz-descriptions-item i18n-nzTitle nzTitle="类型" [nzSpan]="1">
             <nz-tag *ngIf="art.special" nzColor="purple" i18n>炫彩</nz-tag>
             <nz-tag *ngIf="!art.special" i18n>普通</nz-tag>
           </nz-descriptions-item>
-
+          <!-- 
           <nz-descriptions-item i18n-nzTitle nzTitle="用途" [nzSpan]="1">
             <nz-select nzSize="small" [ngModel]="art.use" (ngModelChange)="onUseChange($event)">
               <nz-option nzValue="normal" i18n-nzLabel nzLabel="日常"></nz-option>
@@ -133,17 +48,13 @@
               <nz-option nzValue="album" i18n-nzLabel nzLabel="专辑"></nz-option>
               <nz-option nzValue="collection" i18n-nzLabel nzLabel="合集"></nz-option>
             </nz-select>
-          </nz-descriptions-item>
+          </nz-descriptions-item> -->
 
           <nz-descriptions-item i18n-nzTitle nzTitle="神秘图" [nzSpan]="1">
-            <nz-switch nzSize="small" [nzDisabled]="!art.auth.canEdit" [ngModel]="art.mystery" (ngModelChange)="onMysteryChange($event)"></nz-switch>
+            <nz-switch nzSize="small" [nzDisabled]="true" [ngModel]="art.mystery"></nz-switch>
           </nz-descriptions-item>
 
-          <nz-descriptions-item i18n-nzTitle nzTitle="AI图" [nzSpan]="1">
-            <nz-switch nzSize="small" [ngModel]="art.ai" (ngModelChange)="onAiChange($event)"></nz-switch>
-          </nz-descriptions-item>
-
-          <nz-descriptions-item i18n-nzTitle nzTitle="AI Prompt" [nzSpan]="1" *ngIf="art.ai">
+          <!-- <nz-descriptions-item i18n-nzTitle nzTitle="AI Prompt" [nzSpan]="1" *ngIf="art.ai">
             <editable (save)="updateAiPrompt()" (cancel)="cancelAiPrompt()">
               <ng-template viewMode>
                 {{ art.aiPrompt }}
@@ -153,7 +64,7 @@
                 <input nz-input editableFocusable editableOnEnter editableOnEscape [(ngModel)]="aiPrompt" />
               </ng-template>
             </editable>
-          </nz-descriptions-item>
+          </nz-descriptions-item> -->
 
           <nz-descriptions-item nzTitle="{{title('date')}}" [nzSpan]="1">{{art.date | dateFormat : 'yyyy/M/d H:m:s' }}
           </nz-descriptions-item>
@@ -170,7 +81,8 @@
 
 
 
-          <nz-descriptions-item i18n-nzTitle nzTitle="尺寸" [nzSpan]="1">{{art.width}}x{{art.height}}</nz-descriptions-item>
+          <nz-descriptions-item i18n-nzTitle nzTitle="尺寸"
+            [nzSpan]="1">{{art.width}}x{{art.height}}</nz-descriptions-item>
           <nz-descriptions-item nzTitle="{{title('areaCount')}}/{{title('coloredAreaCount')}}" [nzSpan]="1">
             {{art.areaCount}}/{{art.coloredAreaCount}}
             - {{(art.coloredAreaCount / art.areaCount ) | percent}}
@@ -181,49 +93,15 @@
 
           <nz-descriptions-item nzTitle="{{title('tags')}}" [nzSpan]="1">
             <nz-tag *ngFor="let tag of art.tags" class="clickable" (click)="onClickTag(tag)">{{tag}}</nz-tag>
-            <button class="btn   border-0" (click)="openModal(meta, modalTitle('meta'))"> <i class="fal fa-edit m-0"></i> </button>
+            <button class="btn   border-0" (click)="openModal(meta, modalTitle('meta'))"> <i
+                class="fal fa-edit m-0"></i> </button>
           </nz-descriptions-item>
 
-          <nz-descriptions-item nzTitle="{{title('epgs')}}" [nzSpan]="1">
-            <nz-tag *ngFor="let epg of art.epgs" class="clickable" (click)="onClickEpg(epg)">{{epg}}</nz-tag>
-            <button *can="['update', 'any', 'art']" class="btn   border-0" (click)="openModal(meta, modalTitle('meta'))"> 
-              <i class="fal fa-edit m-0"></i> 
-            </button>
-          </nz-descriptions-item>
-
-
-          <nz-descriptions-item nzTitle="{{title('pageVersion')}}">{{art.pageVersion}}</nz-descriptions-item>
-          <nz-descriptions-item nzTitle="{{title('mapVersion')}}">{{art.mapVersion}}</nz-descriptions-item>
-          <nz-descriptions-item nzTitle="{{title('centersVersion')}}">{{art.centersVersion}}</nz-descriptions-item>
-          <nz-descriptions-item nzTitle="{{title('workVersion')}}">{{art.workVersion}}</nz-descriptions-item>
-
-          <nz-descriptions-item nzTitle="{{title('specialVersion')}}">{{art.specialVersion}}</nz-descriptions-item>
-          <nz-descriptions-item *ngIf="art.binHash.special" nzTitle="{{title('useSpecialThumb')}}">
-            <nz-select [ngModel]="art.useSpecialThumb" (ngModelChange)="onSpecialThumbChange($event)">
-              <nz-option [nzValue]='0' i18n-nzLabel nzLabel="切线图"></nz-option>
-              <nz-option [nzValue]='3' i18n-nzLabel nzLabel="渐变切线图"></nz-option>
-              <nz-option [nzValue]='1' i18n-nzLabel nzLabel="灰度图"></nz-option>
-              <nz-option [nzValue]='2' i18n-nzLabel nzLabel="上传图"></nz-option>
-            </nz-select>
-          </nz-descriptions-item>
 
           <nz-descriptions-item i18n-nzTitle nzTitle="综合评分">
             <a style="color: dodgerblue" (click)="onClickScoreInfo(art)" i18n>
               {{art.score}}
             </a>
-            <a *ngIf="!art.score" style="color: dodgerblue" (click)="onClickScoreInfo(art)" i18n>
-              去评分
-            </a>
-          </nz-descriptions-item>
-
-          <nz-descriptions-item i18n-nzTitle nzTitle="上架详情">
-            <a style="color: dodgerblue" (click)="onClickShelfInfo(art)" i18n>
-              查看
-            </a>
-          </nz-descriptions-item>
-
-          <nz-descriptions-item *ngIf="art.zip && art.auth.isPublisher" i18n-nzTitle nzTitle="下载">
-            <a href="{{art.zip}}" i18n>下载zip包</a>
           </nz-descriptions-item>
 
           <nz-descriptions-item i18n-nzTitle nzTitle="拒稿理由" *ngIf="art.status == 500">
@@ -237,138 +115,37 @@
   </div>
 
 
-  <div *can="['create', 'any', 'role']" class="experiment p-3"  >
-    <h5 i18n>实验功能</h5>
-    <div>
-      <span><a routerLink="/art/svg/{{art?._id}}" class="btn">Play svg</a></span>
-      <span><a routerLink="/art/svg2/{{art?._id}}" class="btn">Play svg2-1</a></span>
-      <span><a routerLink="/art/svg3/{{art?._id}}" class="btn">Play svg2-2</a></span>
-    </div>
-  </div>
-
-
   <div *ngIf="art" nz-row [nzGutter]="[20, 20]" class="pt-4 pb-4 text-center">
     <div nz-col [nzSm]="8" [nzMd]="6" [nzLg]="6" [nzXl]="6">
       <p i18n>线稿</p>
       <div class="thumb-item">
-        <img class="thumb" [src]="art.binHash.page.thumb" />
+        <img class="thumb" [src]="art.binHash.page" />
       </div>
     </div>
 
     <div *ngIf="art.binHash.work" nz-col [nzSm]="8" [nzMd]="6" [nzLg]="6" [nzXl]="6">
       <p i18n>上色效果</p>
       <div class="thumb-item">
-        <img class="thumb" [src]="art.binHash.work.thumb" />
+        <img class="thumb" [src]="art.binHash.work" />
       </div>
     </div>
 
     <div *ngIf="art.binHash.special" nz-col [nzSm]="8" [nzMd]="6" [nzLg]="6" [nzXl]="6">
       <p i18n>炫彩稿</p>
       <div class="thumb-item">
-        <img class="thumb" [src]="art.binHash.special.thumb" />
+        <img class="thumb" [src]="art.binHash.special" />
       </div>
     </div>
 
     <div *ngIf="art.ai && art.binHash.aiImage" nz-col [nzSm]="8" [nzMd]="6" [nzLg]="6" [nzXl]="6">
       <p i18n>AI参考图</p>
       <div class="thumb-item">
-        <img class="thumb" [src]="art.binHash.aiImage.thumb" />
-        <button nz-tooltip i18n-nzTooltipTitle nzTooltipTitle="删除" class="trash btn m-0" (click)="removeAiImage()" >
-          <i class="fa fa-trash"></i>
-        </button>
-      </div>
-    </div>
-    <div *ngIf="art.ai && !art.binHash.aiImage" nz-col [nzSm]="8" [nzMd]="6" [nzLg]="6" [nzXl]="6">
-      <p i18n>AI参考图</p>
-      <div class="thumb-item">
-        <nz-upload class="thumb-uploader" nzListType="picture-card" [nzShowUploadList]="false"
-          [nzBeforeUpload]="beforeAiUpload">
-          <ng-container *ngIf="!art.aiImage">
-            <i class="upload-icon" nz-icon [nzType]="uploading ? 'loading' : 'plus'"></i>
-            <div class="ant-upload-text">Upload</div>
-          </ng-container>
-        </nz-upload>>
-      </div>
-    </div>
-
-  </div>
-
-
-  <div *ngIf="art" nz-row [nzGutter]="[20, 20]" class="pt-4 pb-4 text-center">
-    <div *ngIf="art.binHash.specialOutline" nz-col [nzSm]="8" [nzMd]="6" [nzLg]="6" [nzXl]="6">
-      <p i18n>炫彩稿切线缩略图</p>
-      <div class="thumb-item" [ngClass]="{ 'border border-3 border-info': art.useSpecialThumb == 0}">
-        <img class="thumb" [src]="art.binHash.specialOutline.thumb" />
-      </div>
-    </div>
-    <div *ngIf="art.binHash.special" nz-col [nzSm]="8" [nzMd]="6" [nzLg]="6" [nzXl]="6">
-      <p i18n>炫彩稿渐变切线图</p>
-      <div class="thumb-item" [ngClass]="{ 'border border-3 border-info': art.useSpecialThumb == 3}">
-        <img class="thumb" [src]="art.binHash.special.thumb.replace('special', 'special_gradient')" />
-      </div>
-    </div>
-    <div *ngIf="art.binHash.special" nz-col [nzSm]="8" [nzMd]="6" [nzLg]="6" [nzXl]="6">
-      <p i18n>炫彩稿灰度缩略图</p>
-      <div class="thumb-item" [ngClass]="{ 'border border-3 border-info': art.useSpecialThumb == 1}">
-        <img class="thumb" [src]="art.binHash.special.thumb.replace('special', 'special_gray')" />
-      </div>
-    </div>
-    <div *ngIf="art.binHash.specialThumb" nz-col [nzSm]="8" [nzMd]="6" [nzLg]="6" [nzXl]="6">
-      <p i18n>炫彩稿上传缩略图</p>
-      <div class="thumb-item" [ngClass]="{ 'border border-3 border-info': art.useSpecialThumb == 2}">
-        <img class="thumb" [src]="art.binHash.specialThumb.thumb" />
-        <button nz-tooltip i18n-nzTooltipTitle nzTooltipTitle="删除" class="trash btn m-0" (click)="removeSpecialThumb()" >
-          <i class="fa fa-trash"></i>
-        </button>
-      </div>
-    </div>
-    <div *ngIf="art.binHash.special && !art.binHash.specialThumb" nz-col [nzSm]="8" [nzMd]="6" [nzLg]="6" [nzXl]="6">
-      <p i18n>炫彩稿上传缩略图</p>
-      <div class="thumb-item" [ngClass]="{ 'border border-3 border-info': art.useSpecialThumb == 2}">
-        <nz-upload class="thumb-uploader" nzListType="picture-card" [nzShowUploadList]="false"
-          [nzBeforeUpload]="beforeSpecialThumbUpload">
-          <ng-container *ngIf="!art.specialThumb">
-            <i class="upload-icon" nz-icon [nzType]="uploading ? 'loading' : 'plus'"></i>
-            <div class="ant-upload-text">Upload</div>
-          </ng-container>
-        </nz-upload>>
+        <img class="thumb" [src]="art.binHash.aiImage" />
       </div>
     </div>
 
   </div>
 
-
-  <!--bins-->
-
-  <table class="table table-hover" *ngIf="art">
-    <thead>
-      <tr>
-        <th i18n>类型</th>
-        <th i18n>mime</th>
-        <th i18n>大小</th>
-        <th i18n>尺寸</th>
-        <th i18n>创建时间</th>
-        <th i18n>最后修改</th>
-        <th *ngIf="art.auth.isAuthor || art.auth.isPageAdmin " i18n>查看</th>
-      </tr>
-    </thead>
-    <tbody>
-      <tr *ngFor="let bin of art.bins">
-        <td>{{title(bin.type)}}</td>
-        <td>{{bin.mime}}</td>
-        <td>{{bin.size | dataSize}}</td>
-        <td>{{bin.width ? bin.width + ' x ' + bin.height : '' }}</td>
-        <td>{{bin.date | dateFormat : 'yyyy/M/d H:m:s' }}</td>
-        <td>{{bin.dateLastMod | dateFormat : 'yyyy/M/d H:m:s'}}</td>
-        <td *ngIf="art.auth.isAuthor || art.auth.isPageAdmin">
-          <a *ngIf="canView(bin)" href="/napi/web/art/{{art.id}}/bin/{{bin.type}}" i18n>查看</a>
-          <a *ngIf="bin.type == 'special'" href="/thumbs/v2/special_jpeg/60/{{art.id}}.jpeg" i18n>|jpeg图</a>
-        </td>
-      </tr>
-    </tbody>
-  </table>
-
-
 </nz-spin>
 
 

+ 0 - 74
zorro/src/app/pages/page/detail.component.ts

@@ -5,11 +5,8 @@ import { NzMessageService } from 'ng-zorro-antd/message';
 import { NzModalRef, NzModalService } from 'ng-zorro-antd/modal';
 import { NzUploadFile } from 'ng-zorro-antd/upload';
 import { delay, firstValueFrom, Observable, of } from 'rxjs';
-import { EpgService } from '../epg/epg.service';
 import { ArtStatus } from './art-status.component';
 import { ArtService } from './art.service';
-import { PublishConfirmComponent } from './publish-confirm.component';
-import { PublishDetailComponent } from './publish-detail.component';
 import { ArtRefuseReasonComponent } from './art-refuse-reason.component';
 import { NzImageService } from 'ng-zorro-antd/image';
 import { ScoreDetailComponent } from '../score/score-detail.component';
@@ -66,7 +63,6 @@ export class DetailComponent implements OnInit {
 
   constructor(
     public artService: ArtService,
-    private epgService: EpgService,
     private activatedRoute: ActivatedRoute,
     public message: NzMessageService,
     public modal: NzModalService,
@@ -265,35 +261,6 @@ export class DetailComponent implements OnInit {
     })
   }
 
-  // 点击查看上架详情
-  onClickShelfInfo(art: any) {
-    const modal = this.modal.create({
-      nzTitle: $localize`上架详情`,
-      nzWidth: 750,
-      nzFooter: null,
-      nzContent: PublishDetailComponent,
-      nzViewContainerRef: this.viewContainerRef,
-      nzComponentParams: {
-        art,
-      },
-
-    })
-  }
-
-  // 发布完成后弹出框询问是否上架到epg
-  shouldOnshelfToEpg() {
-    firstValueFrom(this.artService.getShelfInfo(this.art._id))
-      .then((resp: any) => {
-        let onshelfList = resp as any[];
-        if (!onshelfList || onshelfList.length <= 0) {
-          const modal = this.modal.confirm({
-            nzTitle: $localize`确认操作`,
-            nzContent: $localize`该作品未上架到任何EPG,是否现在上架?`,
-            nzOnOk: this.onClickShelfInfo.bind(this, this.art),
-          })
-        }
-      })
-  }
 
   //////////////////////// special thumb upload ////////////////////////
   uploading: boolean = false;
@@ -438,45 +405,4 @@ export class DetailComponent implements OnInit {
   }
 
 
-  //////////////  上架流程变更,以下代码没用了, 准备删掉, 自动上架流程等到发布的时候才进行
-  // 点击测试通过
-  showTestPassModal() {
-    const modal = this.modal.confirm({
-      nzTitle: $localize`确认操作`,
-      nzCentered: true,
-      nzContent: PublishConfirmComponent,
-      nzComponentParams: {
-        art: this.art,
-      },
-      nzOnOk: this.onTestPass.bind(this),
-    })
-  }
-
-  onTestPass(instance: PublishConfirmComponent) {
-    // todo : modify status to TESTED, and publish to epg
-    console.log(this.id);
-    console.log(this.artStatus.TESTED);
-
-    let dataList = Array.from(instance.checkDataSet);
-    let data = dataList.map(e => {
-      return { epg: e.epg, column: e.column, art: this.art._id }
-    });
-
-    firstValueFrom(this.artService.status(this.id, this.artStatus.TESTED))
-      .then(res => {
-        this.loadData();
-        return firstValueFrom(this.epgService.epgContentOnshelf(data));
-      })
-      .then(res => {
-        this.message.success($localize`上架成功`);
-      })
-      .catch(err => {
-        this.message.error(err.error?.message || err.message);
-        console.log(err);
-      }).then(() => {
-      })
-
-  }
-
-
 }

+ 0 - 69
zorro/src/app/pages/page/drag-drop.component.ts

@@ -1,69 +0,0 @@
-import { CdkDragDrop, moveItemInArray, transferArrayItem } from '@angular/cdk/drag-drop';
-import { HttpClient } from '@angular/common/http';
-import { Component, Input, OnInit } from '@angular/core';
-
-@Component({
-  selector: 'app-drag-drop',
-  template: `
-
-  <div id="{{id}}" cdkDropList cdkDropListConnectedTo="{{connect}}" [cdkDropListData]="list" (cdkDropListDropped)="onDrop($event)" >
-    <art-row class="item" *ngFor="let item of list" cdkDrag [data]="item"></art-row>
-  </div>
-
-
-
-    
-  `,
-  styles: [
-    `
-.item{
-  height:40px;
-  background-color:#efefef;
-  border:1px solid #ccc;
-}
-.cdk-drag-placeholder {
-  opacity: 0;
-}
-
-
-  `
-  ]
-})
-export class DragDropComponent implements OnInit {
-  @Input() id: string;
-  @Input() connect: string;
-
-  list: any[] = [];
-
-  onDrop(event: CdkDragDrop<any>) {
-    console.log('onDrop:', event);
-    //moveItemInArray(event.container.data, event.previousIndex, event.currentIndex);
-    if (event.previousContainer === event.container) {
-      moveItemInArray(event.container.data, event.previousIndex, event.currentIndex);
-    } else {
-      transferArrayItem(
-        event.previousContainer.data,
-        event.container.data,
-        event.previousIndex,
-        event.currentIndex,
-      );
-    }
-  }
-
-  constructor(
-    private http: HttpClient,
-  ) {
-
-    let url = '/napi/web/art/pager/user/wangmeng';
-    this.http.get(url).subscribe((resp: any) => {
-      this.list = resp.data as any[];
-    })
-
-
-
-  }
-
-  ngOnInit(): void {
-  }
-
-}

+ 0 - 6
zorro/src/app/pages/page/list.component.ts

@@ -12,12 +12,6 @@ import { SysService } from '../sys/sys.service';
       <nz-page-header *ngIf="!embedMode" [nzGhost]="false">
         <nz-page-header-title i18n>{{data?.title || '作品列表'}}</nz-page-header-title>
         <nz-page-header-extra>
-          <ng-container *ngIf="!hideUpload">
-            <a *can="['create', 'own', 'art']" nz-button nzType="primary" routerLink="/art/play">
-              <i class="fal fa-cloud-upload mr-1"></i>
-              <ng-container i18n>上传作品</ng-container>
-            </a>
-          </ng-container>
         </nz-page-header-extra>
         <nz-page-header-content>
         </nz-page-header-content>

+ 0 - 8
zorro/src/app/pages/page/page-routing.module.ts

@@ -1,11 +1,7 @@
 import { NgModule } from '@angular/core';
 import { RouterModule, Routes } from '@angular/router';
 import { DetailComponent } from './detail.component';
-import { DragDropComponent } from './drag-drop.component';
 import { ListComponent } from './list.component';
-import { PublishScheduleEditComponent } from './publish-schedule-edit.component';
-import { PublishSchedulesComponent } from './publish-schedules.component';
-import { TestComponent } from './test.component';
 
 const routes: Routes = [
   { path: 'my', component: ListComponent, data: { title: $localize`我的作品`, remoteUrl: '/napi/web/art/pager/my', hideAuthor: true } },
@@ -13,10 +9,6 @@ const routes: Routes = [
   { path: 'dropped', component: ListComponent, data: { title: $localize`回收站`, remoteUrl: '/napi/web/art/pager/removed', hideUpload: true } },
   { path: 'my-color-task', component: ListComponent, data: { title: $localize`我的填色任务`, remoteUrl: '/napi/web/art/pager/my-color-task' } },
   { path: 'detail/:id', component: DetailComponent },
-  { path: 'drag-drop', component: DragDropComponent },
-  { path: 'test', component: TestComponent },
-  { path: 'publish-schedules', component: PublishSchedulesComponent },
-  { path: 'publish-schedule/:id', component: PublishScheduleEditComponent },
 ];
 
 @NgModule({

+ 0 - 20
zorro/src/app/pages/page/page.module.ts

@@ -31,21 +31,11 @@ import { NzSpinModule } from 'ng-zorro-antd/spin';
 import { NzCalendarModule } from 'ng-zorro-antd/calendar';
 
 import { AuthModule } from 'src/app/auth/auth.module';
-import { PublishSchedulesComponent } from './publish-schedules.component';
-import { PublishScheduleAddComponent } from './publish-schedule-add.component';
-import { PublishScheduleEditComponent } from './publish-schedule-edit.component';
 import { NzTimePickerModule } from 'ng-zorro-antd/time-picker';
 import { NzDatePickerModule } from 'ng-zorro-antd/date-picker';
 import { DragDropModule } from '@angular/cdk/drag-drop';
-import { ArtRowComponent } from './art-row.component';
-import { DragDropComponent } from './drag-drop.component';
-import { TestComponent } from './test.component';
-import { PublishScheduleCellComponent } from './publish-schedule-cell.component';
 import { NzRadioModule } from 'ng-zorro-antd/radio';
-import { PublishDetailComponent } from './publish-detail.component';
 import { NzTableModule } from 'ng-zorro-antd/table';
-import { PublishConfirmComponent } from './publish-confirm.component';
-import { PublishChooseComponent } from './publish-choose.component';
 import { NzCardModule } from 'ng-zorro-antd/card';
 import { NzTabsModule } from 'ng-zorro-antd/tabs';
 import { NzCheckboxModule } from 'ng-zorro-antd/checkbox';
@@ -66,16 +56,6 @@ import { NzRateModule } from 'ng-zorro-antd/rate';
     DetailComponent,
     ArtStatusComponent,
     MetaComponent,
-    PublishSchedulesComponent,
-    PublishScheduleAddComponent,
-    PublishScheduleEditComponent,
-    ArtRowComponent,
-    DragDropComponent,
-    TestComponent,
-    PublishScheduleCellComponent,
-    PublishDetailComponent,
-    PublishConfirmComponent,
-    PublishChooseComponent,
     ArtRefuseReasonComponent,
   ],
   imports: [

+ 0 - 128
zorro/src/app/pages/page/publish-choose.component.ts

@@ -1,128 +0,0 @@
-import { Component, Input, OnInit } from '@angular/core';
-import { map } from 'rxjs';
-
-@Component({
-  selector: 'app-publish-choose',
-  template: `
-<div nz-row *ngIf="epgs" [nzGutter]="[20, 20]" [nzJustify]="'start'">
-  <div nz-col [nzSpan]="4"  *ngFor="let epg of epgs; let i = index">
-      <nz-card class="card-config" [ngClass]="{'card-select': curEpg == epg.epgId}" 
-        [nzHoverable]="true" [nzSize]="'small'" [nzBodyStyle]="{'padding': '0'}" 
-        (click)="onSelect(epg.epgId)">
-        <h4 class="text-center" [ngStyle]="{'color': curEpg == epg.epgId ? 'white' : ''}">{{epg.epgId}}</h4>
-        <small class="text-center" [ngStyle]="{'color': curEpg == epg.epgId ? 'white' : ''}">{{epg.desc}}</small>
-      </nz-card>
-  </div>
-</div>
-
-<nz-checkbox-wrapper style="width: 100%; margin-top: 20px; margin-bottom: 20px;" (nzOnChange)="onChoose($event)">
-  <div nz-row>
-    <ng-container *ngFor="let col of curColumns; let i=index">
-      <div nz-col *ngIf="!isSpecialColumn(col.id)" nzSpan="3">
-        <label nz-checkbox [nzValue]="col" [ngModel]="isChecked(col.id)">
-          {{col.zh}}
-        </label>
-      </div>
-    </ng-container>
-  </div>
-</nz-checkbox-wrapper>
-
-<nz-table #basicTable *ngIf="dataList && dataList.length > 0 " 
-  nzSize="small" [nzScroll]="{ y: '300px' }" 
-  [nzFrontPagination]="false" [nzShowPagination]="false" [nzData]="dataList">
-  <thead>
-    <tr>
-      <th>EPG</th>
-      <th i18n>栏目</th>
-      <th i18n>发布路径</th>
-    </tr>
-  </thead>
-  <tbody>
-    <tr *ngFor="let data of basicTable.data; let i = index">
-      <td> {{data.epg}} </td>
-      <td> {{data.column}} </td>
-      <td> {{data.path}} </td>
-    </tr>
-  </tbody>
-</nz-table>
-
-  `,
-  styles: [`
-
-.card-config {
-  text-align:center;
-  border-radius: 4px;
-}
-
-.card-select {
-  background: dodgerblue;
-  color: white;
-}
-
-
-  `
-  ]
-})
-export class PublishChooseComponent implements OnInit {
-
-  @Input() art: any;
-
-  @Input() epgs: any;
-
-
-  curEpg: string;   // 当前选中的epg id
-  curCol: string;   // 当前选中的栏目 id
-
-  get curColumns() {
-    let columns = this.epgs?.find(e => e.epgId == this.curEpg)?.columns || [];
-    return columns;
-  }
-
-  get curColIdx(): number {
-    let colIdx = this.curColumns?.findIndex(e => e.id == this.curCol);
-    if (colIdx < 0) colIdx = 0;
-    return colIdx; 
-  }
-
-  selectMap: any = {}; 
-  get dataList(): any[] {
-    let _dataList = [];
-    this.epgs.forEach(epg => {
-      let list = this.selectMap[epg.epgId];
-      if (list && list.length > 0) {
-        _dataList = _dataList.concat(list);
-      }
-    });
-    return _dataList;
-  }
-  
-  constructor() { 
-  }
-
-  ngOnInit(): void {
-    this.curEpg = this.epgs[0]?.epgId; // 默认第一个
-  }
-
-  onSelect(epg: string) {
-    this.curEpg = epg;
-  }
-
-  // 判断某栏目是否特殊栏目(系统保留虚拟栏目) 
-  // 注:这里剔除了others,允许发布到“其他”栏目下,可以讨论
-  isSpecialColumn(col: string) {
-    return [undefined, null, '', 'all'].includes(col?.toLowerCase());
-  }
-
-  onChoose(nodes: any) {
-    let list = nodes.map(col => {
-      return { epg: this.curEpg, column: col.id, path: `${this.curEpg} -> ${col.zh}(${col.id})` }
-    })
-    this.selectMap[this.curEpg] = list;
-  }
-
-  isChecked(colId: string): boolean {
-    let list = this.selectMap[this.curEpg] as any[] || [];
-    return list.find(e => e.column == colId) ? true : false;
-  }
-
-}

+ 0 - 157
zorro/src/app/pages/page/publish-confirm.component.ts

@@ -1,157 +0,0 @@
-import { Component, Input, OnInit } from '@angular/core';
-import { NzMessageService } from 'ng-zorro-antd/message';
-import { firstValueFrom } from 'rxjs';
-import { EpgService } from '../epg/epg.service';
-import { ArtService } from './art.service';
-
-@Component({
-  selector: 'app-publish-confirm',
-  template: `
-
-
-<nz-spin [nzSpinning]="isLoading">
-
-
-<div i18n>确认测试通过?</div>
-
-<ng-container *ngIf="!isLoading && unshelfList && unshelfList.length > 0">
-
-<div style="margin-top: 10px" i18n>提测后是否自动上架到以下epg? 请勾选确认:</div>
-<nz-table #unshelfTable nzSize="small" [nzFrontPagination]="false" [nzShowPagination]="false" [nzData]="unshelfList"
-  [nzTemplateMode]="true" class="table table-hover table-sm  table-edit">
-  <thead>
-    <tr>
-      <th [nzChecked]="checked" [nzIndeterminate]="indeterminate" (nzCheckedChange)="onAllChecked($event)"></th>
-      <th i18n>发布路径</th>
-    </tr>
-  </thead>
-  <tbody *ngIf="unshelfTable.data && unshelfTable.data.length > 0">
-    <tr *ngFor="let data of unshelfTable.data; let i = index" class="editable-row">
-      <td [nzChecked]="checkDataSet.has(data)" (nzCheckedChange)="onItemChecked(data, $event)"></td>
-      <td> {{data.path}} </td>
-    </tr>
-  </tbody>
-</nz-table>
-
-</ng-container>
-
-
-</nz-spin>
-
-
-  `,
-  styles: [
-  ]
-})
-export class PublishConfirmComponent implements OnInit {
-
-  @Input() art: any;
-
-  onshelfList: any[] = []; // 已上架列表
-
-  pubList: any[] = []; // 根据tag与绑定epgs规则生成的可上架的列表
-
-  unshelfList: any[] = []; // pubList 减去 onshelfList 后剩下的最终建议可上架的列表
-
-  epgs: any[] = []; // epg 列表
-
-
-
-
-  constructor(
-    private message: NzMessageService,
-    private artService: ArtService,
-    private epgService: EpgService,
-  ) { 
-    
-  }
-
-  ngOnInit(): void {
-    this.loadData();
-  }
-
-  isLoading: boolean = false;
-
-  loadData() {
-    this.isLoading = true;
-
-    firstValueFrom(this.artService.getShelfInfo(this.art._id)).then((resp: any) => {
-      this.onshelfList = resp as any[];
-      firstValueFrom(this.epgService.epgAll()).then((ret: any) => {
-        this.epgs = ret.data || [];
-        this.generatePubList()
-        this.isLoading = false;
-      })
-    })
-
-
-  }
-
-
-  generatePubList() {
-    let tags = this.art.tags;
-    let epgs = this.art.epgs;
-    let pubList = [];
-    epgs.forEach(epgId => {
-      let epg = this.epgs.find(e => e.epgId == epgId);
-      if (epg) {
-        epg.columns.forEach(col => {
-          if (tags.includes(col.id)) {
-            pubList.push({
-              epg: epg.epgId,
-              column: col.id, 
-              path: `${epg.epgId} -> ${col.zh}(${col.id})`
-            });
-          }
-        })
-      }
-    })
-    
-    this.pubList = pubList;
-    this.unshelfList = this.pubList.filter(item => {
-      if (this.onshelfList.find(e => e.epg == item.epg && e.column == item.column)) return false;  // 找到,说明已经上架过了,忽略
-      else return true;
-    })
-    
-  }
-
-
-
-  
-  //////////////////////////////// table checked ////////////////////////
-
-  checkDataSet = new Set<any>();
-  checked = false;
-  indeterminate = false;
-
-  onItemChecked(data: any, checked: boolean): void {
-    this.updateCheckedSet(data, checked);
-    this.refreshCheckedStatus();
-  }
-
-  updateCheckedSet(data: any, checked: boolean): void {
-    if (checked) {
-      this.checkDataSet.add(data);
-    } else {
-      this.checkDataSet.delete(data);
-    }
-  }
-
-  refreshCheckedStatus(): void {
-    this.checked = this.unshelfList.every(item => this.checkDataSet.has(item));
-    this.indeterminate = this.unshelfList.some(item => this.checkDataSet.has(item)) && !this.checked;
-   }
-
-  onAllChecked(checked: boolean): void {
-    this.unshelfList.forEach(item => this.updateCheckedSet(item, checked));
-    this.refreshCheckedStatus();
-  }
-
-  cleanAllChecked() {
-    this.checkDataSet.clear();
-    this.checked = false;
-    this.indeterminate = false;
-  }
-
-
-}

+ 0 - 562
zorro/src/app/pages/page/publish-detail.component.ts

@@ -1,562 +0,0 @@
-import { Component, Input, OnInit } from '@angular/core';
-import { NzMessageService } from 'ng-zorro-antd/message';
-import { NzModalService } from 'ng-zorro-antd/modal';
-import { firstValueFrom } from 'rxjs';
-import { EpgService } from '../epg/epg.service';
-import { ArtService } from './art.service';
-import { PublishChooseComponent } from './publish-choose.component';
-import { addHours, startOfDay } from 'date-fns';
-
-@Component({
-  selector: 'app-publish-detail',
-  template: `
-
-<nz-spin [nzSpinning]="isLoading">
-
-<div class="frame">
-  <ng-container *ngIf="!isLoading">
-    <div class="header">
-      <div class="title" i18n>
-        已上架到:
-      </div>
-      <div *ngIf="!isLoading && onshelfList && onshelfList.length > 0" >
-          <button nz-button nzType="primary" nzSize="small" type="submit" [disabled]="checkDataSet1.size <= 0" 
-            (click)="unPublish(getListFromSet(checkDataSet1))" i18n>下架</button>
-      </div>
-    </div>
-    <div *ngIf="onshelfList.length <= 0" class="d-flex justify-content-center" i18n>
-      该作品未上架到任何epg
-    </div>
-  </ng-container>
-
-  <nz-table #onshelfTable *ngIf="onshelfList && onshelfList.length > 0" nzSize="small" [nzFrontPagination]="false" [nzShowPagination]="false" [nzData]="onshelfList"
-    [nzTemplateMode]="true" class="table table-hover table-sm  table-edit">
-    <thead>
-      <tr>
-        <th [nzChecked]="checked1" [nzIndeterminate]="indeterminate1" (nzCheckedChange)="onAllChecked1($event)"></th>
-        <th i18n>发布路径</th>
-        <th i18n>发布时间</th>
-        <th i18n>状态</th>
-        <th i18n>加锁</th>
-        <th i18n>操作员</th>
-        <th i18n>操作</th>
-      </tr>
-    </thead>
-    <tbody *ngIf="onshelfTable.data && onshelfTable.data.length > 0">
-      <tr *ngFor="let data of onshelfTable.data; let i = index" class="editable-row">
-        <td [nzChecked]="checkDataSet1.has(data)" (nzCheckedChange)="onItemChecked1(data, $event)"></td>
-        <td> {{data.epg}} -> {{data.title || '其他'}}({{data.column || 'others'}})</td>
-        <td> {{data.onshelfTime | dateFormat }}</td>
-        <td> 
-          <ng-container *ngIf="data.status == 500" i18n>定时上架</ng-container>
-          <ng-container *ngIf="data.status == 1000" i18n>已上架</ng-container>
-        </td>
-        <td><nz-tag *ngIf="data.lock" nzColor="magenta"> <i class="fal fa-lock"></i> </nz-tag></td>
-        <td> {{data.operator.name || data.operator.username}}</td>
-        <td>
-          <div>
-            <a *ngIf="!data.lock" nz-popconfirm i18n-nzPopconfirmTitle nzPopconfirmTitle="确定加锁吗?" (nzOnConfirm)="lock(data, true)" i18n>加锁</a>
-            <a *ngIf="data.lock" nz-popconfirm i18n-nzPopconfirmTitle nzPopconfirmTitle="确定解锁吗?" (nzOnConfirm)="lock(data, false)" i18n>解锁</a>
-            <span style="color: #dcdcdc"> | </span>
-            <a nz-popconfirm i18n-nzPopconfirmTitle nzPopconfirmTitle="确定下架吗?" (nzOnConfirm)="unPublish([data])" i18n>下架</a>
-            <span style="color: #dcdcdc"> | </span>
-          </div>
-           <!-- <a (click)="unPublish([data])" i18n>下架</a> -->
-        </td>
-      </tr>
-    </tbody>
-  </nz-table>
-</div>
-
-
-<div *ngIf="onshelfNow && (art.status == 9000 || art.status == 7000)" class="frame" style="margin-top: 10px">
-
-  <ng-container *ngIf="!isLoading">
-    <div class="header">
-      <div>
-        <span class="title" i18n>现在上架:</span>
-        &nbsp;&nbsp;
-        <span i18n>定时上架: </span><nz-switch nzSize="small" [nzDisabled]="art.status == 7000" [(ngModel)]="isSchedule"></nz-switch>
-        &nbsp;&nbsp;
-        <span *ngIf="isSchedule"><nz-date-picker nzShowTime nzFormat="yyyy-MM-dd HH:mm:ss" [(ngModel)]="scheduleTime"></nz-date-picker></span> 
-      </div>
-      <div *ngIf="!isLoading" >
-        <button *ngIf="finalPubList && finalPubList.length > 0" class="me-2"
-          nz-button nzType="primary" nzSize="small" nzDanger
-          (click)="removeAll()" i18n>清空</button> 
-        <button nz-button nzType="primary" nzSize="small" class="me-2"
-          (click)="openChooseModal(addPubList)" i18n>新增</button>
-        <button *ngIf="finalPubList && finalPubList.length > 0" 
-          nz-button nzType="primary" nzSize="small" [disabled]="checkDataSet2.size <= 0" 
-          (click)="publish(getListFromSet(checkDataSet2))" i18n>上架</button>
-      </div>
-    </div>
-    <div *ngIf="!art.epgs || art.epgs.length <= 0" class="d-flex justify-content-center" i18n>
-      该作品未绑定EPG,无任何上架建议。
-    </div>
-    <div *ngIf="isSuggestAlreadyOnshelf() && finalPubList?.length <= 0" class="d-flex justify-content-center" i18n>
-      无
-    </div>
-  </ng-container>
-
-  <ng-container *ngIf="!isLoading">
-    <nz-table #unshelfTable 
-      *ngIf="finalPubList && finalPubList.length > 0"
-      nzSize="small" [nzFrontPagination]="false" [nzShowPagination]="false" [nzData]="finalPubList"
-      [nzTemplateMode]="true" class="table table-hover table-sm  table-edit">
-      <thead>
-        <tr>
-          <th [nzChecked]="checked2" [nzIndeterminate]="indeterminate2" (nzCheckedChange)="onAllChecked2($event)"></th>
-          <th i18n>发布路径</th>
-          <th i18n>操作</th>
-        </tr>
-      </thead>
-      <tbody *ngIf="unshelfTable.data && unshelfTable.data.length > 0">
-        <tr *ngFor="let data of unshelfTable.data; let i = index" class="editable-row">
-          <td [nzChecked]="checkDataSet2.has(data)" (nzCheckedChange)="onItemChecked2(data, $event)"></td>
-          <td> {{data.path}} </td>
-          <td> 
-            <a (click)="publish([data])" i18n>上架</a>
-            <nz-divider nzType="vertical"></nz-divider>
-            <a (click)="remove(data)" i18n>移除</a>
-          </td>
-        </tr>
-      </tbody>
-    </nz-table>
-  </ng-container>
-
-</div>
-
-
-<div *ngIf="art.publishSchedule && !art.publishSchedule.done && art.status < 9000" class="frame" style="margin-top: 10px">
-
-  <ng-container *ngIf="!isLoading">
-    <div class="header">
-      <div class="title" i18n>
-        计划上架:
-      </div>
-      <div *ngIf="!isLoading" >
-        <button nz-button nzType="primary" nzSize="small"
-          (click)="openChooseModal(addScheduleList)" i18n>新增</button>
-      </div>
-    </div>
-    <div *ngIf="!art.scheduleToEpg || art.scheduleToEpg.length <= 0" class="d-flex justify-content-center" i18n>
-      无
-    </div>
-  </ng-container>
-
-  <ng-container *ngIf="!isLoading">
-  <nz-table #schedualTable 
-    *ngIf="planList && planList.length > 0"
-    nzSize="small" [nzFrontPagination]="false" [nzShowPagination]="false" [nzData]="planList"
-    [nzTemplateMode]="true" class="table table-hover table-sm  table-edit">
-    <tbody *ngIf="schedualTable.data && schedualTable.data.length > 0">
-      <tr *ngFor="let data of schedualTable.data; let i = index" class="editable-row">
-        <td> {{data.path}} </td>
-        <td> 
-          <a (click)="cancel(data)" i18n>撤销</a>
-        </td>
-      </tr>
-    </tbody>
-  </nz-table>
-  </ng-container>
-
-</div>
-
-
-</nz-spin>
-
-  `,
-  styles: [
-    `
-
-.table td a {
-  color: dodgerblue;
-}
-
-.table td a:hover {
-  color: forestgreen;
-}
-
-.frame {
-  border-style: dashed;
-  border-width: 1px;
-  border-color: #d3d3d3;
-  padding: 10px
-}
-
-.title {
-  font-weight: bold;
-}
-
-.header {
-  display: flex;
-  justify-content: space-between;
-}
-
-
-    `
-  ]
-})
-export class PublishDetailComponent implements OnInit {
-  @Input() art: any;
-  @Input() onshelfNow: boolean = true;
-
-  onshelfList: any[] = []; // 已上架列表
-
-  suggestList: any[] = []; // 根据tag与绑定epgs规则生成的建议上架列表
-
-  finalPubList: any[] = []; // suggestList 减去 onShelfList 后剩下的最终上架列表
-
-  epgs: any[] = []; // epg 列表
-
-  _planList: any[];   // 计划发布列表,来自art的schedualToEpg字段
-  get planList() {
-    if (!this._planList && this.art) {
-      this._planList = this.art.scheduleToEpg.map(e => {
-        let [epgId, columnId] = e.split('->');
-        return { epg: epgId, column: columnId, path: `${epgId} -> ${getColumnTitle(epgId, columnId, this.epgs)}(${columnId})` };
-      })
-    }
-    
-    function getColumnTitle(epgId: string, columnId: string, epgs: any[]) {
-      if (columnId == '') return '其他';
-      let title = '';
-      if (epgs) {
-        let epg = epgs.find(e => e.epgId == epgId);
-        if (epg) {
-          let column = epg.columns.find(e => e.id == columnId);
-          if (column) title = column.zh;
-        }
-      }
-      return title;
-    }
-
-    return this._planList;
-  }
-
-  set planList(list: any[]) {
-    let dataList = list.map(e => `${e.epg}->${e.column}`);
-    firstValueFrom(this.artService.epgSchedule(this.art._id, dataList))
-    .then((ret: any) => {
-      this.art.scheduleToEpg = dataList;
-      this._planList = list;
-    });
-
-  }
-
-  // 定时发布相关
-  DEFAULT_TIME = 16; //16:00
-  isSchedule: boolean = false; // 是否定时上架
-  _scheduleTime: Date;  // 计划上架日期
-  get scheduleTime() {
-    if (!this._scheduleTime) { 
-      this._scheduleTime = addHours(startOfDay(new Date()), this.DEFAULT_TIME);
-    }
-    return this._scheduleTime;
-  }
-  set scheduleTime(time: Date) {
-    this._scheduleTime = time;
-  }
-
-
-  constructor(
-    private message: NzMessageService,
-    private modal: NzModalService,
-    private artService: ArtService,
-    private epgService: EpgService,
-  ) { 
-  }
-
-  ngOnInit(): void {
-    this.loadData();
-    this.isSchedule = this.art.status == 7000;
-  }
-
-  isLoading: boolean = false;
-
-  loadData() {
-    this.isLoading = true;
-
-    firstValueFrom(this.artService.getShelfInfo(this.art._id))
-    .then((resp: any) => {
-      this.onshelfList = resp as any[];
-      return firstValueFrom(this.epgService.epgAll());
-    })
-    .then((ret: any) => {
-      this.epgs = ret.data || [];
-      this.generatePubList()
-      this.isLoading = false;
-    })
-
-  }
-
-
-  generatePubList() {
-    let tags = this.art.tags;
-    tags.push('latest');  // 增加默认“latest”标签,大多数都需要同步发布到latest下面
-    tags = tags.filter((item, index, arr) => arr.indexOf(item, 0) == index);  // 去重
-    let epgs = this.art.epgs;
-    let suggestList = [];
-    epgs.forEach(epgId => {
-      let epg = this.epgs.find(e => e.epgId == epgId);
-      if (epg) {
-        epg.columns.forEach(col => {
-          if (tags.includes(col.id)) {
-            suggestList.push({
-              epg: epg.epgId,
-              column: col.id, 
-              path: `${epg.epgId} -> ${col.zh}(${col.id})`
-            });
-          }
-        })
-      }
-    })
-    
-    this.suggestList = suggestList;
-    this.finalPubList = this.suggestList.filter(item => {
-      if (this.onshelfList.find(e => e.epg == item.epg && e.column == item.column)) return false;
-      else return true;
-    })
-    
-  }
-
-  
-  isSuggestAlreadyOnshelf(): boolean {
-    if (this.suggestList.length <= 0) return false;
-
-    let ret = true;
-    for (let i = 0; i < this.suggestList.length; i++) {
-      let item = this.suggestList[i];
-      if (!this.onshelfList.find(e => e.epg == item.epg && e.column == item.column)) {
-        ret = false;
-        break;
-      }
-    }
-    return ret;
-  }
-
-  remove(data) {
-    this.finalPubList = this.finalPubList.filter(e => e != data)
-  }
-
-  removeAll() {
-    this.finalPubList = [];
-  }
-
-  publish(dataList: any[]) {
-    let data = dataList.map(e => {
-      return { epg: e.epg, column: e.column, art: this.art._id, schedule: this.isSchedule? this.scheduleTime: undefined }
-    });
-
-    firstValueFrom(this.epgService.epgContentOnshelf(data))
-    .then(res => {
-      this.message.success($localize`上架成功`); 
-      this.cleanAllChecked2();
-      this.loadData();
-    })
-    .catch(err => {
-      this.message.error(err.error?.message || err.message);
-      console.log(err);
-    }).then(() => {
-    })
-  }
-
-  unPublish(dataList: any[]) {
-    let data = dataList.map(item => {
-      return { epg: item.epg, id: item._id };
-    })
-    firstValueFrom(this.epgService.epgContentOffshelf(data))
-    .then(res => {
-      this.message.success($localize`下架成功`); 
-      this.cleanAllChecked1();
-      this.loadData();
-    })
-    .catch(err => {
-      this.message.error(err.error?.message || err.message);
-      console.log(err);
-    }).then(() => {
-    })
-
-  }
-
-  lock(data: any, lock: boolean) {
-    firstValueFrom(this.epgService.epgContentUpdate(data.epg, [{ _id: data._id, lock }]))
-    .then(res => {
-      data.lock = lock;
-    })
-  }
-
-
-  getListFromSet(set: Set<any>) {
-    return Array.from(set);
-  }
-
-  
-  
-  openChooseModal(onOk: Function) {
-    const modal = this.modal.create({
-      nzTitle: $localize`请选择要发布的epg和栏目`,
-      nzCentered: true,
-      nzWidth: 800,
-      nzContent: PublishChooseComponent,
-      nzComponentParams: {
-        art: this.art,
-        epgs: this.epgs,
-      },
-      nzOnOk: onOk.bind(this),
-    })
-  }
-
-  /**
-   * 新增现在上架列表
-   */
-  addPubList(instance: PublishChooseComponent) {
-    let selectedList = instance.dataList;
-    if (!selectedList) return;
-
-    selectedList.forEach(item => {
-      if (item.column == 'others') {
-        item.column = '';
-      }
-    })
-
-    // 去重
-    let mergeList = this.finalPubList.concat(selectedList);
-    mergeList = mergeList.filter((item, index, arr) => 
-      arr.findIndex(e => e.epg == item.epg && e.column == item.column) == index);  
-
-    // 过滤掉已经发布过的
-    mergeList = mergeList.filter(item => {
-      if (this.onshelfList.find(e => e.epg == item.epg && e.column == item.column)) return false;
-      else return true;
-    })
-
-    // 调整下顺序
-    let finaList = []
-    this.epgs.forEach(epg => {
-      mergeList.forEach(item => {
-        if (item.epg == epg.epgId) {
-          finaList.push(item);
-        }
-      })
-    })
-
-    this.finalPubList = finaList;
-  }
-
-  /**
-   * 新增计划上架列表
-   */
-  addScheduleList(instance: PublishChooseComponent) {
-    let selectedList = instance.dataList;
-    if (!selectedList) return;
-
-    selectedList.forEach(item => {
-      if (item.column == 'others') {
-        item.column = '';
-      }
-    })
-
-    // 去重
-    let mergeList = this.planList.concat(selectedList);
-    mergeList = mergeList.filter((item, index, arr) => 
-      arr.findIndex(e => e.epg == item.epg && e.column == item.column) == index);  
-
-    // 调整下顺序
-    let finaList = []
-    this.epgs.forEach(epg => {
-      mergeList.forEach(item => {
-        if (item.epg == epg.epgId) {
-          finaList.push(item);
-        }
-      })
-    })
-
-    this.planList = finaList;
-  }
-
-  // 取消发布计划
-  cancel(data: any) {
-    let newPlanList = this.planList.filter(e => !(e.epg == data.epg && e.column == data.column));
-    let dataList = newPlanList.map(e => `${e.epg}->${e.column}`);
-    firstValueFrom(this.artService.epgSchedule(this.art._id, dataList))
-    .then((ret: any) => {
-      this.art.scheduleToEpg = dataList;
-      this._planList = newPlanList;
-    });
-  }
-
-
-  //////////////////////////////// table checked ////////////////////////
-
-  ///////////////////// first table //////////////////////
-  checkDataSet1 = new Set<any>();
-  checked1 = false;
-  indeterminate1 = false;
-
-  onItemChecked1(data: any, checked: boolean): void {
-    this.updateCheckedSet1(data, checked);
-    this.refreshCheckedStatus1();
-  }
-
-  updateCheckedSet1(data: any, checked: boolean): void {
-    if (checked) {
-      this.checkDataSet1.add(data);
-    } else {
-      this.checkDataSet1.delete(data);
-    }
-  }
-
-  refreshCheckedStatus1(): void {
-    this.checked1 = this.onshelfList.every(item => this.checkDataSet1.has(item));
-    this.indeterminate1 = this.onshelfList.some(item => this.checkDataSet1.has(item)) && !this.checked1;
-   }
-
-  onAllChecked1(checked: boolean): void {
-    this.onshelfList.forEach(item => this.updateCheckedSet1(item, checked));
-    this.refreshCheckedStatus1();
-  }
-
-  cleanAllChecked1() {
-    this.checkDataSet1.clear();
-    this.checked1 = false;
-    this.indeterminate1 = false;
-  }
-
-
-  ///////////////////// second table //////////////////////
-  checkDataSet2 = new Set<any>();
-  checked2 = false;
-  indeterminate2 = false;
-
-  onItemChecked2(data: any, checked: boolean): void {
-    this.updateCheckedSet2(data, checked);
-    this.refreshCheckedStatus2();
-  }
-
-  updateCheckedSet2(data: any, checked: boolean): void {
-    if (checked) {
-      this.checkDataSet2.add(data);
-    } else {
-      this.checkDataSet2.delete(data);
-    }
-  }
-
-  refreshCheckedStatus2(): void {
-    this.checked2 = this.finalPubList.every(item => this.checkDataSet2.has(item));
-    this.indeterminate2 = this.finalPubList.some(item => this.checkDataSet2.has(item)) && !this.checked2;
-   }
-
-  onAllChecked2(checked: boolean): void {
-    this.finalPubList.forEach(item => this.updateCheckedSet2(item, checked));
-    this.refreshCheckedStatus2();
-  }
-
-  cleanAllChecked2() {
-    this.checkDataSet2.clear();
-    this.checked2 = false;
-    this.indeterminate2 = false;
-  }
-
-}

+ 0 - 90
zorro/src/app/pages/page/publish-schedule-add.component.ts

@@ -1,90 +0,0 @@
-import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
-import { FormBuilder, FormGroup, Validators } from '@angular/forms';
-import { addHours, startOfDay } from 'date-fns';
-import { NzMessageService } from 'ng-zorro-antd/message';
-import { FormUtils } from 'src/app/lib/utils/form-utils';
-import { ArtService } from './art.service';
-
-@Component({
-  selector: 'art-publish-schedule-add',
-  template: `
-
-
-<form *ngIf="form" [formGroup]="form" nz-form (submit)="submit()"  >
-
-  <nz-form-item>
-    <nz-form-label nzFor="" nzSpan="8" nzRequired i18n> 发布时间 </nz-form-label>
-    <nz-form-control >
-        <nz-date-picker formControlName="publishTime" nzShowTime nzFormat="yyyy-MM-dd HH:mm:ss" ></nz-date-picker>
-    </nz-form-control>
-  </nz-form-item>
-
-  <nz-form-item>
-    <nz-form-label nzFor="" nzSpan="8" i18n> 距离现在</nz-form-label>
-    <nz-form-control >
-      <div> {{form.value.publishTime | dateDist }} </div>
-    </nz-form-control>
-  </nz-form-item>
-
-  <button nz-button nzType="primary" type="submit" nzBlock i18n>创建</button>
-</form>
-  `,
-  styles: [
-  ]
-})
-export class PublishScheduleAddComponent implements OnInit {
-
-  @Output() onSuccess = new EventEmitter<any>();
-  @Output() onError = new EventEmitter<any>();
-
-  @Input()
-  public set date(value: Date) {
-    this._date = value;
-  }
-  private _date: Date;
-  public get date(): Date {
-    return this._date;
-  }
-
-  form: FormGroup;
-
-  DEFAULT_TIME = 17; //17:00
-
-  constructor(
-    private fb: FormBuilder,
-    private message: NzMessageService,
-    private artService: ArtService,
-  ) { }
-
-  submit() {
-    if (!this.form.valid) {
-      FormUtils.markAsDirty(this.form);
-      return;
-    }
-    this.artService.createPublishSchedule(this.form.value)
-      .subscribe({
-        next: resp => {
-          this.message.success($localize`创建成功`);
-          this.onSuccess.emit(resp);
-        },
-        error: err => {
-          this.message.error(err.error?.message || err.message);
-          this.onError.emit(err);
-        }
-      })
-  }
-
-  ngOnInit(): void {
-    this.form = this.fb.group({
-      publishTime: [this.getDefault(), [Validators.required]],
-    })
-  }
-
-
-
-  getDefault(): Date {
-    if (this.date) return addHours(this.date, this.DEFAULT_TIME);
-    return addHours(startOfDay(new Date()), this.DEFAULT_TIME);
-  }
-
-}

+ 0 - 110
zorro/src/app/pages/page/publish-schedule-cell.component.ts

@@ -1,110 +0,0 @@
-import { Component, Input, OnInit, TemplateRef } from '@angular/core';
-import { Router } from '@angular/router';
-import { differenceInDays } from 'date-fns';
-import { NzModalService } from 'ng-zorro-antd/modal';
-
-@Component({
-  selector: 'art-publish-schedule-cell',
-  template: `
-  
-  <div class="text-start">
-    {{date | dateFormat : 'M/d'}}
-  </div>
-
-  <div class="schedule"  *ngFor="let schedule of schedules" (click)="jump(schedule)" >
-    <div *ngIf="!schedule.done" class="text-end text-primary"> <i class="fal fa-clock"></i> {{schedule.publishTime | dateFormat : 'H:mm'}}, {{schedule.publishTime | dateDist}}</div>
-    <div *ngIf="schedule.done" class="text-end text-success"> <i class="fal fa-calendar-check"></i> <ng-container i18n>已完成</ng-container>{{schedule.publishTime | dateFormat : 'H:mm'}}</div>
-    <div class="d-flex flex-row flex-wrap"  >
-      <div *ngFor="let art of schedule.arts" class="box"  >
-        <img [src]="art.thumb" loading="lazy"  >
-      </div>
-    </div>
-  </div>
-
-  <div *ngIf="!isPast" class="text-center p-2">
-    <button class="btn btn-sm" (click)="openModal(modalAddByDate, 500, $event)"><i class="fal fa-plus"></i> <ng-container i18n>添加</ng-container></button>
-    <ng-template #modalAddByDate let-modal="modalRef" >
-      <art-publish-schedule-add [date]="date" (onSuccess)="modal.close(); jump($event);" ></art-publish-schedule-add>
-    </ng-template>
-  </div>
-
-
-
-
-  `,
-  styles: [
-    `
-    :host{
-      display:block;
-      height:200px;
-      overflow-y:auto;
-    }
-
-    .schedule{
-      padding:5px 5px;
-      border:2px dashed #ccc;
-      margin-bottom:5px;
-      border-radius:5px;
-    }
-
-    .schedule:hover{
-      border:2px dashed #1890ff;
-    }
-
-    .schedule:hover .text-secondary{
-      color:white !important;
-    }
-
-    .box{
-      box-sizing:border-box;
-      padding:1px;
-      height:40px;
-      width:25%;
-      text-align:center;
-      display :inline-block;
-      display:flex;
-      flex-direction:column;
-      justify-content:center;
-      align-items:center;
-    }
-
-    .box img{
-      height:100%;
-    }
-    `
-  ]
-})
-export class PublishScheduleCellComponent implements OnInit {
-  @Input() schedules: any[] = [];
-  @Input() date: Date;
-
-  constructor(
-    private modal: NzModalService,
-    private router: Router,
-  ) { }
-
-  ngOnInit(): void {
-  }
-
-  jump(resp) {
-    if (resp._id) {
-      this.router.navigateByUrl(`/pages/publish-schedule/${resp._id}`);
-    }
-  }
-
-  get isPast() {
-    return differenceInDays(this.date, new Date()) < 0 ;
-  }
-
-
-  openModal(conent: TemplateRef<any>, width: number = 500, event: Event = null) {
-    if (event) { event.stopPropagation(); }
-    this.modal.create({
-      nzContent: conent,
-      nzTitle: $localize`添加定时发布`,
-      nzWidth: width,
-      nzFooter: null,
-    })
-  }
-
-}

+ 0 - 336
zorro/src/app/pages/page/publish-schedule-edit.component.ts

@@ -1,336 +0,0 @@
-import { CdkDragDrop, moveItemInArray, transferArrayItem } from '@angular/cdk/drag-drop';
-import { Location } from '@angular/common';
-import { Component, OnInit } from '@angular/core';
-import { FormBuilder, FormGroup, Validators } from '@angular/forms';
-import { ActivatedRoute, Router } from '@angular/router';
-import { NzMessageService } from 'ng-zorro-antd/message';
-import { FilterItem, FilterType } from 'src/app/lib/pager/pager-filter.component';
-import { FormUtils } from 'src/app/lib/utils/form-utils';
-import { ArtService } from './art.service';
-
-@Component({
-  selector: 'app-publish-schedule-edit',
-  template: `
-  <nz-page-header >
-    <nz-page-header-title *ngIf="!schedule?.done" i18n>编辑定时发布</nz-page-header-title>
-    <nz-page-header-title *ngIf="schedule?.done"><ng-container i18n>定时发布</ng-container>: <span class="text-success" i18n>已完成</span></nz-page-header-title>
-    <nz-page-header-extra>
-      <button *ngIf="!schedule?.done" class="btn  btn-outline-danger" 
-        cgOpButton [operation]="removeOperation(id)" confirmContent="确定要移除这个定时发布?" (onSuccess)="location.back()" >
-        <i class="fal fa-trash-alt"></i><ng-container i18n>删除</ng-container></button>
-    </nz-page-header-extra>
-  
-    <nz-page-header-content *ngIf="schedule?.done">
-      <ng-container i18n>发布时间</ng-container>: {{schedule?.publishTime | dateFormat : 'yyyy/M/d HH:mm'}}
-    </nz-page-header-content>
-
-    <nz-page-header-content *ngIf="schedule && !schedule.done" >
-      <form  [formGroup]="form" nz-form  nzLayout="inline" (submit)="save()" >
-        <nz-form-item>
-          <nz-form-label nzFor="" nzSpan="8" nzRequired i18n> 发布时间 </nz-form-label>
-          <nz-form-control >
-            <nz-date-picker formControlName="publishTime" nzShowTime nzFormat="yyyy-MM-dd HH:mm:ss"  ></nz-date-picker>
-          </nz-form-control>
-        </nz-form-item>
-        <nz-form-item>
-          <nz-form-control >
-            <div><ng-container i18n>距离现在</ng-container>: {{form.value.publishTime | dateDist }} </div>
-          </nz-form-control>
-        </nz-form-item>
-
-        <button nz-button nzType="primary" [disabled]="!form.dirty" type="submit" i18n>保存</button>
-      </form>
-    </nz-page-header-content>
-  </nz-page-header>
-
-  <div *ngIf="schedule?.done">
-    <art-row *ngFor="let item of list" (itemClick)="goDetail($event)" [data]="item" [hideRemove]="true" > </art-row>
-  </div>
-
-  <div class="row p-2" *ngIf="!schedule?.done">
-
-    <div class="col schedule-col" >
-      <nz-spin nzDelay="500" [nzSpinning]="isLoading">
-        <div  
-          cdkDropList 
-          class="dropList" 
-          id="list-2" 
-          cdkDropListConnectedTo="list-1"
-          [cdkDropListData]="list"
-          (cdkDropListDropped)="drop($event)"
-        >
-          <art-row *ngFor="let item of list" (itemClick)="goDetail($event)" [data]="item"  [hideAuthor]="false" (onRemove)="onRemove($event)" cdkDrag >
-          </art-row>
-        </div>
-      </nz-spin>
-    </div>
-
-    <div class="col">
-      <art-list 
-        remoteUrl="/napi/web/art/pager/pending"
-        [hideAuthor]="false"
-        [hideUpload]="true"
-        [embedMode]="true"
-        [refreshTrigger]="refreshTrigger"
-        [filterConfig]="filterConfig"
-      ></art-list>
-    </div>
-
-  </div>
-
-   
-  `,
-  styles: [
-
-    `
-  .dropList{
-    min-height:1000px;
-    padding:5px;
-  }
-  .schedule-col {
-    border: 2px dashed #ccc; 
-    border-radius: 10px;
-  }
-
-  .cdk-drag-preview {
-  box-sizing: border-box;
-  border-radius: 4px;
-  box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2),
-              0 8px 10px 1px rgba(0, 0, 0, 0.14),
-              0 3px 14px 2px rgba(0, 0, 0, 0.12);
-  }
-
-  .cdk-drag-placeholder {
-    opacity: 0;
-  }
-
-  .row{
-    height:600px;
-  }
-
-  .col{
-    max-height:100%;
-    overflow-y: auto;
-  }
-
-  
-
-  `
-  ]
-})
-export class PublishScheduleEditComponent implements OnInit {
-  list: any[] = [];
-
-  id: string;
-
-  refreshTrigger: boolean = false;
-
-  isLoading: boolean = false;
-  schedule: any;
-  form: FormGroup;
-
-  constructor(
-    private artService: ArtService,
-    private activatedRoute: ActivatedRoute,
-    private message: NzMessageService,
-    public location: Location,
-    private fb: FormBuilder,
-    private router: Router,
-  ) {
-    this.id = this.activatedRoute.snapshot.params['id'];
-    this.loadData();
-
-    this.form = this.fb.group({
-      publishTime: [null, [Validators.required]],
-    })
-
-  }
-
-  goDetail(item: any) {
-    this.router.navigateByUrl(`/pages/detail/${item._id}`)
-  }
-
-  save() {
-    console.log('saveing...');
-    if (!this.form.valid) {
-      FormUtils.markAsDirty(this.form);
-      return;
-    }
-    this.artService.updatePublishSchedule(this.id, this.form.value).subscribe({
-      next: resp => {
-        this.message.success($localize`更新成功`);
-      },
-      error: err => this.message.error(err.error?.message || err.message)
-    })
-  }
-
-
-  loadData() {
-    this.isLoading = true;
-    this.artService.getPublishSchedule(this.id).subscribe({
-      next: (resp: any) => {
-        this.schedule = resp;
-        this.form.reset(this.schedule);
-        this.list = resp.arts;
-        this.isLoading = false;
-      },
-      error: err => {
-        this.message.error(err.error?.message || err.message);
-      }
-    })
-  }
-
-  refresh() {
-    this.refreshTrigger = !this.refreshTrigger;
-  }
-
-  ngOnInit(): void {
-  }
-
-  removeOperation(id) {
-    return (() => {
-      return this.artService.deletePublishSchedule(id);
-    }).bind(this);
-  }
-
-  onRemove(artId: string) {
-    this.isLoading = true;
-    this.artService.publishScheduleRemove(this.id, { artId }).subscribe({
-      next: (resp: any) => {
-        this.isLoading = false;
-        this.schedule = resp;
-        this.list = resp.arts;
-        this.message.success($localize`操作成功`);
-        this.refresh();
-      },
-      error: err => {
-        this.message.error(err.error?.message || err.message);
-      }
-    })
-  }
-
-  drop(event: CdkDragDrop<any>) {
-    let art = event.previousContainer.data[event.previousIndex];
-    let artId = art._id;
-    if (event.previousContainer === event.container) {
-      if (event.previousIndex == event.currentIndex) return;
-      moveItemInArray(event.container.data, event.previousIndex, event.currentIndex);
-      let { previousIndex, currentIndex } = event;
-      this.isLoading = true;
-      this.artService.publishScheduleReorder(this.id, { previousIndex, currentIndex }).subscribe({
-        next: (resp: any) => {
-          this.isLoading = false;
-          this.list = resp.arts;
-          this.message.success($localize`操作成功`);
-        },
-        error: err => {
-          this.message.error(err.error?.message || err.message);
-        }
-      })
-    } else {
-      transferArrayItem(event.previousContainer.data, event.container.data, event.previousIndex, event.currentIndex,);
-      let insertIndex = event.currentIndex;
-      this.isLoading = true;
-      this.artService.publishScheduleInsert(this.id, { artId, insertIndex }).subscribe({
-        next: (resp: any) => {
-          this.isLoading = false;
-          this.list = resp.arts;
-          this.message.success($localize`操作成功`);
-        },
-        error: err => {
-          this.message.error(err.error?.message || err.message);
-        }
-      })
-    }
-  }
-
-
-
-  filterConfig: FilterItem[] = [
-    {
-      data: 'date',
-      title: $localize`上传`,
-      filterType: FilterType.dateRange,
-      span: 4,
-    },
-    {
-      data: 'epgs',
-      title: 'EPG',
-      filterType: FilterType.select,
-      mode : 'default',
-      span: 4,
-      fullWidth: true,
-      optionsUrl : '/napi/web/epg/options',
-    },
-    {
-      data: 'tags',
-      title: $localize`标签`,
-      filterType: FilterType.select,
-      mode: 'tags',
-      span: 4,
-      fullWidth: true,
-      optionsUrl: '/napi/web/art/agg/tags',
-    },
-    {
-      data: 'user',
-      title: $localize`作者`,
-      filterType: FilterType.select,
-      mode: 'multiple',
-      span: 4,
-      fullWidth: true,
-      optionsUrl: '/napi/web/user/select/options',
-    },
-    {
-      data: 'hasSpecial',
-      title: $localize`彩绘`,
-      filterType: FilterType.select,
-      options: [
-        { label: $localize`是`, value: true },
-        { label: $localize`否`, value: false },
-      ],
-      span: 2,
-    },
-    {
-      data: 'mystery',
-      title: $localize`神秘图`,
-      filterType : FilterType.select,
-      options: [
-        { label: $localize`是`, value: true },
-        { label: $localize`否`, value: false },
-      ],
-      span: 2,
-    },
-    {
-      data: 'ai',
-      title: $localize`AI`,
-      filterType : FilterType.select,
-      options: [
-        { label: $localize`是`, value: true },
-        { label: $localize`否`, value: false },
-      ],
-      span: 2,
-    },
-    {
-      data: 'width',
-      title: $localize`尺寸`,
-      filterType: FilterType.select,
-      options: [
-        { label: '2000', value: 2000 },
-        { label: '1500', value: 1500 },
-      ],
-      span: 2,
-    },
-    {
-      data: 'lock',
-      title: $localize`加锁`,
-      filterType: FilterType.select,
-      options: [
-        { label: $localize`是`, value: true },
-        { label: $localize`否`, value: false },
-      ],
-      span: 2,
-    },
-
-  ];
-
-}

+ 0 - 265
zorro/src/app/pages/page/publish-schedules.component.ts

@@ -1,265 +0,0 @@
-import { HttpClient } from '@angular/common/http';
-import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core';
-import { ActivatedRoute, Router } from '@angular/router';
-import { addDays, differenceInDays, endOfDay, endOfMonth, format, isAfter, isBefore, startOfDay, startOfMonth } from 'date-fns';
-import { subDays } from 'date-fns/esm';
-import { NzMessageService } from 'ng-zorro-antd/message';
-import { NzModalService } from 'ng-zorro-antd/modal';
-import { FilterItem, FilterType } from 'src/app/lib/pager/pager-filter.component';
-import { ArtService } from './art.service';
-
-@Component({
-  selector: 'app-publish-schedules',
-  template: `
-
-  <nz-page-header i18n-nzTitle nzTitle="定时发布">
-    <nz-page-header-subtitle>
-      <!--
-      <nz-radio-group [(ngModel)]="displayMode" >
-          <label nz-radio-button nzValue="table" i18n>表格</label>
-          <label nz-radio-button nzValue="calendar" i18n>日历</label>
-      </nz-radio-group>
--->
-    </nz-page-header-subtitle>
-    <nz-page-header-extra *ngIf="displayMode == 'table'">
-
-      <button *can="['create', 'any', 'art-publish']" class="btn" (click)="openModal(modalAdd)" >
-        <i class="fal fa-plus"></i> <ng-container i18n>创建定时发布</ng-container></button>
-
-      <ng-template #modalAdd let-modal="modalRef" >
-        <art-publish-schedule-add  (onSuccess)="modal.close(); jump($event);" ></art-publish-schedule-add>
-      </ng-template>
-
-    </nz-page-header-extra>
-    <nz-page-header-content>
-    </nz-page-header-content>
-  </nz-page-header>
-
-
-
-  <nz-calendar
-    *ngIf="displayMode == 'calendar'"
-    [nzDateFullCell]="dateCellTpl"
-    [nzFullscreen]="true"
-    [(ngModel)]="date"
-    [(nzMode)]="model"
-    [nzDisabledDate]="disabledDate"	
-    (nzPanelChange)="panelChange($event)"
-    (nzSelectChange)="dateChange($event)">
-    <!-- 定义 Cell 的另一种方式 -->
-    <div *dateCell>Foo</div>
-  </nz-calendar>
-
-  <!-- 传入 TemplateRef 的方式 -->
-  <ng-template #dateCellTpl let-date>
-    <art-publish-schedule-cell [date]="date" [schedules]="getByDate(date)"></art-publish-schedule-cell>
-  </ng-template>
-
-
-
-  <cg-pager
-    *ngIf="displayMode== 'table'"
-    remoteHeaderUrl="/napi/web/publish-schedule/pager/headers"
-    remoteUrl="/napi/web/publish-schedule/pager/list"
-    [showOrder]="false" 
-    [refresh]="refreshTrigger"
-    [showSearch]="false"
-    [defaultOrder]="'asc'"
-    [defaultOrderBy]="'publishTime'"
-    [filterConfig]="filterConfigs"
-    [cellRenders]="{tableOperations, createBy, arts}"
-  ></cg-pager>
-
-  <ng-template #arts let-item="item">
-    <div class="d-flex flex-row">
-      <div *ngFor="let art of item?.arts">
-        <img class="image" [src]="art.thumb" loading="lazy" />
-      </div>
-    </div>
-  </ng-template>
-  <ng-template #createBy let-item="item">
-    {{item?.createBy?.name || item?.createBy.username}}
-  </ng-template>
-
-  <ng-template #tableOperations let-item="item" >
-    <button class="btn btn-sm" (click)="jump(item)"><i class="fal fa-edit"></i><ng-container i18n>编辑</ng-container></button>
-    <button class="btn btn-sm" cgOpButton [operation]="removeOperation(item._id)" i18n-confirmContent confirmContent="确定要移除这个定时发布?" (onSuccess)="refresh()" >
-      <i class="fal fa-trash-alt"></i><ng-container i18n>删除</ng-container>
-    </button>
-  </ng-template>
-    
-  `,
-  styles: [
-    `
-    :host{
-      display:block;
-      padding-bottom:200px;
-    }
-    .cell{
-      min-height:200px;
-      overflow-y:scroll;
-    }
-
-    .image{
-      height:80px;
-    }
-
-
-    `
-  ]
-})
-export class PublishSchedulesComponent implements OnInit {
-
-  @ViewChild('modal_add') modalAdd: TemplateRef<any>;
-
-  model: 'month' | 'year' = 'month';
-  date: Date;
-  month: string;
-
-  displayMode: 'table' | 'calendar' = 'calendar';
-
-  refreshTrigger: boolean = false;
-
-  constructor(
-    private modal: NzModalService,
-    private artService: ArtService,
-    private message: NzMessageService,
-    private router: Router,
-    private http: HttpClient,
-    private activatedRoute: ActivatedRoute,
-  ) {
-
-    this.activatedRoute.queryParams.subscribe(params => {
-      this.date = new Date();
-      if (params['date']) {
-        let date = new Date(params['date']);
-        if (date.toString() != 'Invalid Date') {
-          this.date = date;
-        }
-      }
-      this.loadCalendarData();
-    })
-
-  }
-
-  getMonth(date: Date) {
-    return format(date, 'yyyyMM');
-  }
-
-
-  filterConfigs: FilterItem[] = [
-    {
-      data: 'publishTime',
-      filterType: FilterType.dateRange,
-      title: '发布时间',
-      span: 4,
-    }
-  ]
-
-
-  refresh() {
-    this.refreshTrigger = !this.refreshTrigger;
-  }
-
-
-  openModal(conent: TemplateRef<any>, width: number = 500, event: Event = null) {
-    if (event) { event.stopPropagation(); }
-    this.modal.create({
-      nzContent: conent,
-      nzTitle: $localize`创建定时发布`,
-      nzWidth: width,
-      nzFooter: null,
-    })
-  }
-
-
-  addSchedule(date: Date, event: Event) {
-    event.preventDefault();
-    event.stopPropagation();
-
-    this.modal.create({
-      nzContent: this.modalAdd,
-      nzTitle: $localize`新增定时发布`,
-      nzFooter: null,
-    })
-  }
-
-  removeOperation(id) {
-    return (() => {
-      return this.artService.deletePublishSchedule(id);
-    }).bind(this);
-  }
-
-  panelChange(event) {
-    console.log('panelChange:', event);
-  }
-
-
-  dateChange(date: Date) {
-    this.date = date;
-    this.updateParams('date', this.date.toISOString());
-  }
-
-  updateParams(key: string, value: string) {
-    let oldParams = this.activatedRoute.snapshot.queryParams;
-    let params = Object.assign({}, oldParams);
-    params[key] = value;
-    this.router.navigate([], { queryParams: params });
-
-  }
-
-  disabledDate(date) {
-    return differenceInDays(date, new Date()) < 0;
-  }
-
-
-
-
-  ngOnInit(): void {
-  }
-
-  jump(resp) {
-    if (resp._id) {
-      this.router.navigateByUrl(`/pages/publish-schedule/${resp._id}`);
-    }
-  }
-
-
-  monthlyData: any;
-
-  loadCalendarData() {
-    let month = this.getMonth(this.date);
-    if (month == this.month) return;
-    let start = subDays(startOfMonth(this.date), 14);
-    let end = addDays(endOfMonth(this.date), 14);
-    let filters = {
-      publishTime: [start, end],
-    }
-    let params = {
-      filters: JSON.stringify(filters),
-    }
-    this.http.get(`/napi/web/publish-schedule/pager/list`, { params }).subscribe({
-      next: (resp: any) => {
-        this.monthlyData = resp.data;
-        this.month = this.getMonth(this.date);
-      },
-      error: err => {
-        this.message.error(err.error?.message || err.message);
-      }
-    })
-
-  }
-
-
-  getByDate(date: Date) {
-    let start = startOfDay(date);
-    let end = endOfDay(date);
-    //console.log('start:', start, ', end:', end)
-    if (!this.monthlyData) return [];
-    return this.monthlyData.filter(schedule => {
-      let publishTime = new Date(schedule.publishTime);
-      return isAfter(publishTime, start) && isBefore(publishTime, end);
-    })
-  }
-
-}

+ 0 - 26
zorro/src/app/pages/page/test.component.ts

@@ -1,26 +0,0 @@
-import { Component, OnInit } from '@angular/core';
-
-@Component({
-  selector: 'app-test',
-  template: `
-  <div class="row">
-    <div class="col">
-      <app-drag-drop id="list-a" connect="list-b" ></app-drag-drop>
-    </div>
-    <div class="col">
-      <app-drag-drop id="list-b" connect="list-a"></app-drag-drop>
-    </div>
-  </div>
-
-  `,
-  styles: [
-  ]
-})
-export class TestComponent implements OnInit {
-
-  constructor() { }
-
-  ngOnInit(): void {
-  }
-
-}