Add add endpoint button in replication rule

Modify stop job button privilege

Modify dispay issue about replication rule
This commit is contained in:
pfh 2018-01-17 11:28:28 +08:00
parent 208cb02d5c
commit 757a99bc6e
19 changed files with 93 additions and 48 deletions

View File

@ -38,7 +38,7 @@ export const CONFIRMATION_DIALOG_TEMPLATE: string = `
</ng-template>
<ng-template [ngSwitchCase]="4">
<button type="button" class="btn btn-outline" (click)="cancel()" [hidden]="isDelete">{{'BUTTON.CANCEL' | translate}}</button>
<button type="button" class="btn btn-danger" (click)="operate()" [hidden]="isDelete">{{'BUTTON.REPLICATE' | translate}}</button>
<button type="button" class="btn btn-primary" (click)="operate()" [hidden]="isDelete">{{'BUTTON.REPLICATE' | translate}}</button>
<button type="button" class="btn btn-primary" (click)="cancel()" [disabled]="!batchOverStatus" [hidden]="!isDelete">{{'BUTTON.CLOSE' | translate}}</button>
</ng-template>
</div>

View File

@ -42,7 +42,7 @@ import { Subscription } from 'rxjs/Subscription';
const FAKE_PASSWORD = 'rjGcfuRu';
@Component({
selector: 'create-edit-endpoint',
selector: 'hbr-create-edit-endpoint',
template: CREATE_EDIT_ENDPOINT_TEMPLATE,
styles: [CREATE_EDIT_ENDPOINT_STYLE]
})

View File

@ -25,7 +25,7 @@ export const ENDPOINT_TEMPLATE: string = `
<clr-dg-column [clrDgField]="'insecure'">{{'CONFIG.VERIFY_REMOTE_CERT' | translate }}</clr-dg-column>
<clr-dg-column [clrDgSortBy]="creationTimeComparator">{{'DESTINATION.CREATION_TIME' | translate}}</clr-dg-column>
<clr-dg-placeholder>{{'DESTINATION.PLACEHOLDER' | translate }}</clr-dg-placeholder>
<clr-dg-row *ngFor="let t of targets" [clrDgItem]='t'>
<clr-dg-row *clrDgItems="let t of targets" [clrDgItem]='t'>
<clr-dg-cell>{{t.name}}</clr-dg-cell>
<clr-dg-cell>{{t.endpoint}}</clr-dg-cell>
<clr-dg-cell>
@ -42,6 +42,6 @@ export const ENDPOINT_TEMPLATE: string = `
</div>
</div>
<confirmation-dialog #confirmationDialog [batchInfors]="batchDelectionInfos" (confirmAction)="confirmDeletion($event)"></confirmation-dialog>
<create-edit-endpoint (reload)="reload($event)"></create-edit-endpoint>
<hbr-create-edit-endpoint (reload)="reload($event)"></hbr-create-edit-endpoint>
</div>
`;

View File

@ -7,6 +7,7 @@ export * from './log/index';
export * from './filter/index';
export * from './endpoint/index';
export * from './repository/index';
export * from './create-edit-endpoint/index';
export * from './repository-stackview/index';
export * from './tag/index';
export * from './list-replication-rule/index';

View File

@ -8,7 +8,6 @@ export const INLINE_ALERT_STYLE: string = `
min-width: 30px !important;
}
.alert-item {
display: inline-block;
text-align: center;
}
:host >>> .alert-icon-wrapper{

View File

@ -13,9 +13,9 @@ export const LIST_REPLICATION_RULE_TEMPLATE: string = `
<clr-dg-column [clrDgField]="'projects'" *ngIf="!projectScope">{{'REPLICATION.PROJECT' | translate}}</clr-dg-column>
<clr-dg-column [clrDgField]="'description'">{{'REPLICATION.DESCRIPTION' | translate}}</clr-dg-column>
<clr-dg-column [clrDgField]="'targets'">{{'REPLICATION.DESTINATION_NAME' | translate}}</clr-dg-column>
<clr-dg-column [clrDgField]="'trigger'">{{'REPLICATION.SCHEDULE' | translate}}</clr-dg-column>
<clr-dg-column [clrDgField]="'trigger'">{{'REPLICATION.MODE' | translate}}</clr-dg-column>
<clr-dg-placeholder>{{'REPLICATION.PLACEHOLDER' | translate }}</clr-dg-placeholder>
<clr-dg-row *ngFor="let p of changedRules" [clrDgItem]="p" [style.backgroundColor]="(projectScope && withReplicationJob && selectedId === p.id) ? '#eee' : ''">
<clr-dg-row *clrDgItems="let p of changedRules" [clrDgItem]="p" [style.backgroundColor]="(projectScope && withReplicationJob && selectedId === p.id) ? '#eee' : ''">
<clr-dg-cell (click)="selectRule(p)">{{p.name}}</clr-dg-cell>
<clr-dg-cell *ngIf="!projectScope" (click)="selectRule(p)">
<a href="javascript:void(0)" (click)="redirectTo(p)">{{p.projects?.length>0 ? p.projects[0].name : ''}}</a>

View File

@ -194,6 +194,7 @@ export class ListReplicationRuleComponent implements OnInit, OnChanges {
}
selectRule(rule: ReplicationRule): void {
this.selectedId = rule.id || '';
this.selectedRow = [];
this.selectOne.emit(rule);
this.hasJobs.emit(true);

View File

@ -13,7 +13,7 @@ export const REPLICATION_TEMPLATE: string = `
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
<hbr-list-replication-rule #listReplicationRule [readonly]="readonly" [projectId]="projectId" [isSystemAdmin]="isSystemAdmin" (replicateManual)=replicateManualRule($event) (hasJobs)="hasJobList($event)" (selectOne)="selectOneRule($event)" (openNewRule)="openModal()" (editOne)="openEditRule($event)" (reload)="reloadRules($event)" [loading]="loading" [withReplicationJob]="withReplicationJob" (redirect)="customRedirect($event)"></hbr-list-replication-rule>
</div>
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12" [hidden]="!hasJobs">
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12" [hidden]="!hasJobs" style="padding-left:0px;">
<div *ngIf="withReplicationJob" class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
<div class="row flex-items-xs-between" style="height:60px;">
<h5 class="flex-items-xs-bottom option-left-down" style="margin-left: 14px;">{{'REPLICATION.REPLICATION_JOBS' | translate}}</h5>
@ -40,7 +40,7 @@ export const REPLICATION_TEMPLATE: string = `
<div *ngIf="withReplicationJob" class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
<clr-datagrid [clrDgLoading]="jobsLoading" (clrDgRefresh)="clrLoadJobs($event)"><clr-dg-action-bar>
<div class="btn-group">
<button type="button" class="btn btn-sm btn-secondary" [disabled]="!(jobs && jobs.length>0)" (click)="stopJobs()">{{'REPLICATION.STOPJOB' | translate}}</button>
<button type="button" class="btn btn-sm btn-secondary" [hidden]="!(jobs && jobs.length>0 && isSystemAdmin)" (click)="stopJobs()">{{'REPLICATION.STOPJOB' | translate}}</button>
</div>
</clr-dg-action-bar>
<clr-dg-column [clrDgField]="'repository'">{{'REPLICATION.NAME' | translate}}</clr-dg-column>

View File

@ -318,14 +318,20 @@ export class ReplicationComponent implements OnInit, OnDestroy {
.subscribe(res => findedList = BathInfoChanges(findedList, res));
})
.catch(error => {
this.translateService.get('BATCH.REPLICATE_FAILURE').subscribe(res => {
findedList = BathInfoChanges(findedList, res, false, true);
});
if (error && error.status === 412) {
Observable.forkJoin(this.translateService.get('BATCH.REPLICATE_FAILURE'),
this.translateService.get('REPLICATION.REPLICATE_SUMMARY_FAILURE'))
.subscribe(function (res) {
findedList = BathInfoChanges(findedList, res[0], false, true, res[1]);
});
} else {
this.translateService.get('BATCH.REPLICATE_FAILURE').subscribe(res => {
findedList = BathInfoChanges(findedList, res, false, true);
});
}
});
}
customRedirect(rule: ReplicationRule) {
this.redirect.emit(rule);
}

View File

@ -31,7 +31,7 @@
"clarity-icons": "^0.10.17",
"clarity-ui": "^0.10.17",
"core-js": "^2.4.1",
"harbor-ui": "0.6.19",
"harbor-ui": "0.6.21",
"intl": "^1.2.5",
"mutationobserver-shim": "^0.3.2",
"ngx-cookie": "^1.0.0",

View File

@ -13,6 +13,7 @@ import {ConfirmationMessage} from "../../shared/confirmation-dialog/confirmation
import {Subject} from "rxjs/Subject";
import {ListProjectModelComponent} from "./list-project-model/list-project-model.component";
import {toPromise, isEmptyObject, compareValue} from "harbor-ui/src/utils";
import {CreateEditEndpointComponent} from "harbor-ui/src/create-edit-endpoint/create-edit-endpoint.component";
const ONE_HOUR_SECONDS: number = 3600;
const ONE_DAY_SECONDS: number = 24 * ONE_HOUR_SECONDS;
@ -23,7 +24,7 @@ const ONE_DAY_SECONDS: number = 24 * ONE_HOUR_SECONDS;
styleUrls: ['replication-rule.css']
})
export class ReplicationRuleComponent implements OnInit, AfterViewInit, OnDestroy {
export class ReplicationRuleComponent implements OnInit, OnDestroy {
_localTime: Date = new Date();
policyId: number;
projectId: number;
@ -33,7 +34,7 @@ export class ReplicationRuleComponent implements OnInit, AfterViewInit, OnDestro
isScheduleOpt: boolean;
isImmediate: boolean = true;
noProjectInfo: string;
noEndpointInfo: string;
noEndpointInfo: boolean;
filterCount: number = 0;
selectedprojectList: Project[] = [];
triggerNames: string[] = ['Immediate', 'Scheduled', 'Manual'];
@ -56,6 +57,9 @@ export class ReplicationRuleComponent implements OnInit, AfterViewInit, OnDestro
@ViewChild(ListProjectModelComponent)
projectListModel: ListProjectModelComponent;
@ViewChild(CreateEditEndpointComponent)
createEditEndpointComponent: CreateEditEndpointComponent;
baseFilterData(name: string, option: string[], state: boolean) {
return {
name: name,
@ -78,7 +82,7 @@ export class ReplicationRuleComponent implements OnInit, AfterViewInit, OnDestro
Promise.all([this.repService.getEndpoints(), this.repService.listProjects()])
.then(res => {
if (!res[0]) {
this.noEndpointInfo = 'NO_ENDPOINT_INFO';
this.noEndpointInfo = true;
}else {
this.targetList = res[0];
if (!this.policyId) {
@ -86,7 +90,7 @@ export class ReplicationRuleComponent implements OnInit, AfterViewInit, OnDestro
}
}
if (!res[1]) {
this.noProjectInfo = 'NO_PROJECT_INFO';
this.noProjectInfo = 'REPLICATION.NO_PROJECT_INFO';
}else {
if (!this.policyId && !this.projectId) {
this.setProject([res[1][0]]);
@ -95,7 +99,7 @@ export class ReplicationRuleComponent implements OnInit, AfterViewInit, OnDestro
this.setProject( res[1].filter(rule => rule.project_id === this.projectId));
}
}
if (res[0] && res[1] && !this.policyId) {
if (!this.policyId) {
this.copyUpdateForm = Object.assign({}, this.ruleForm.value);
}
});
@ -133,9 +137,6 @@ export class ReplicationRuleComponent implements OnInit, AfterViewInit, OnDestro
});
}
ngAfterViewInit(): void {
}
ngOnDestroy(): void {
if (this.confirmSub) {
this.confirmSub.unsubscribe();
@ -145,6 +146,10 @@ export class ReplicationRuleComponent implements OnInit, AfterViewInit, OnDestro
}
}
get isVaild() {
return !(this.isRuleNameExist || this.noProjectInfo || this.noEndpointInfo);
}
createForm() {
this.ruleForm = this.fb.group({
name: ['', Validators.required],
@ -450,6 +455,20 @@ export class ReplicationRuleComponent implements OnInit, AfterViewInit, OnDestro
this.inProgress = true;
}
openModal() {
this.createEditEndpointComponent.openCreateEditTarget(true);
}
reload($event: boolean) {
if ($event) {
Promise.all([this.repService.getEndpoints()]).then(res => {
this.targetList = res[0];
this.setTarget([this.targetList[this.targetList.length - 1]]);
});
this.noEndpointInfo = false;
}
}
onCancel(): void {
this.router.navigate(['/harbor/replications']);
}

View File

@ -28,10 +28,13 @@ label:first-child {
.filterSelect label{width: 160px;}
.filterSelect label input{width: 100%;}
.cursor{cursor: pointer;}
.pull-left{float: left;}
.padLeft0{padding-left: 0;}
.floatSet {display: inline-block; float: left; width: 120px;margin-right: 10px;}
.floatSet {display: inline-block; width: 120px;margin-right: 10px;}
.form-group{ min-height: 36px;}
.projectInput{float: left;}
.projectInput input{width: 185px;background-color: white;}
.switchIcon{width:20px;height:20px; margin-top: 2px;margin-left: 15px;}
.switchIcon{width:20px;height:20px; margin-top: 2px;margin-left: 5px;}
.addEndpoint{ display: inline-block; line-height: 20px; margin-top: 8px;
vertical-align: bottom; padding-left:10px; cursor: pointer;}

View File

@ -1,6 +1,6 @@
<div>
<a class="cursor" *ngIf="!projectId" (click)="backReplication()">< {{'SIDE_NAV.SYSTEM_MGMT.REPLICATION' | translate}}</a>
<a class="cursor" *ngIf="projectId" (click)="backProjectReplication()"><{{'SIDE_NAV.PROJECTS' | translate}} &nbsp; {{'SIDE_NAV.SYSTEM_MGMT.REPLICATION' | translate}}</a>
<a class="cursor" *ngIf="projectId" (click)="backProjectReplication()"><{{'SIDE_NAV.PROJECTS' | translate}} &nbsp; {{'SIDE_NAV.SYSTEM_MGMT.REPLICATION' | translate | lowercase}}</a>
<h1 class="sub-header-title">{{headerTitle | translate}}</h1>
<form [formGroup]="ruleForm" (ngSubmit)="onSubmit()" novalidate>
<section class="form-block">
@ -57,12 +57,12 @@
<div class="form-group">
<label class="col-md-4 form-group-label-override">{{'DESTINATION.ENDPOINT' | translate}} <span class="colorRed">*</span></label>
<div formArrayName="targets">
<div class="select endpointSelect" *ngFor="let target of targets.controls; let i= index" [formGroupName]="i">
<div class="select endpointSelect pull-left" *ngFor="let target of targets.controls; let i= index" [formGroupName]="i">
<select id="ruleTarget" (change)="targetChange($event)" formControlName="id">
<option *ngFor="let target of targetList" value="{{target.id}}">{{target.name}}: {{target.endpoint}}</option>
</select>
</div>
<label *ngIf="noEndpointInfo" class="colorRed">{{noEndpointInfo | translate}}</label>
<a class="addEndpoint" (click)="openModal()"><clr-icon shape="plus" style="vertical-align: sub;"></clr-icon>{{'REPLICATION.NEW' | translate}}</a>
</div>
</div>
@ -116,9 +116,10 @@
<span class="spinner spinner-inline" [hidden]="inProgress === false"></span>
<br>
<button type="button" id="ruleBtnCancel" class="btn btn-outline" [disabled]="!hasFormChange()" (click)="onCancel()">{{ 'BUTTON.CANCEL' | translate }}</button>
<button type="submit" id="ruleBtnOk" class="btn btn-primary" [disabled]="!ruleForm.valid || isRuleNameExist || !hasFormChange()">{{ 'BUTTON.OK' | translate }}</button>
<button type="submit" id="ruleBtnOk" class="btn btn-primary" [disabled]="!ruleForm.valid || !isVaild || !hasFormChange()">{{ 'BUTTON.OK' | translate }}</button>
</div><!-- [disabled]="!ruleForm.valid"-->
</section>
</form>
<list-project-model (selectedPro)="selectedProject($event)"></list-project-model>
<hbr-create-edit-endpoint (reload)="reload($event)"></hbr-create-edit-endpoint>
</div>

View File

@ -1,4 +1,4 @@
<h2 class="custom-h2">{{'SIDE_NAV.SYSTEM_MGMT.REPLICATION' | translate}}</h2>
<div style="margin-top: 24px;">
<hbr-replication [readonly]="false" [withReplicationJob]='true' (openCreateRule)="openCreatePage()" (openEdit)="openEditPage($event)" (redirect)="customRedirect($event)"></hbr-replication>
<hbr-replication [readonly]="false" [withReplicationJob]='true' [isSystemAdmin]="isSystemAdmin" (openCreateRule)="openCreatePage()" (openEdit)="openEditPage($event)" (redirect)="customRedirect($event)"></hbr-replication>
</div>

View File

@ -15,6 +15,7 @@ import { Component } from '@angular/core';
import {Router,ActivatedRoute} from "@angular/router";
import {ReplicationRule} from "../replication-rule/replication-rule";
import {SessionService} from "../../shared/session.service";
@Component({
selector: 'total-replication',
@ -23,6 +24,7 @@ import {ReplicationRule} from "../replication-rule/replication-rule";
export class TotalReplicationPageComponent {
constructor(private router: Router,
private session: SessionService,
private activeRoute: ActivatedRoute){}
customRedirect(rule: ReplicationRule): void {
if (rule) {
@ -30,6 +32,11 @@ export class TotalReplicationPageComponent {
}
}
public get isSystemAdmin(): boolean {
let account = this.session.getCurrentUser();
return account != null && account.has_admin_role > 0;
}
openEditPage(id: number): void {
this.router.navigate([id, 'rule'], { relativeTo: this.activeRoute });
}

View File

@ -37,7 +37,7 @@
</ng-template>
<ng-template [ngSwitchCase]="4">
<button type="button" class="btn btn-outline" (click)="cancel()" [hidden]="isDelete">{{'BUTTON.CANCEL' | translate}}</button>
<button type="button" class="btn btn-danger" (click)="operate()" [hidden]="isDelete">{{'BUTTON.SWITCH' | translate}}</button>
<button type="button" class="btn btn-primary" (click)="operate()" [hidden]="isDelete">{{'BUTTON.SWITCH' | translate}}</button>
<button type="button" class="btn btn-primary" (click)="cancel()" [disabled]="!batchOverStatus" [hidden]="!isDelete">{{'BUTTON.CLOSE' | translate}}</button>
</ng-template>
</div>

View File

@ -42,8 +42,8 @@
"DELETED_FAILURE": "Deleted failed",
"SWITCH_SUCCESS": "Switch successfully",
"SWITCH_FAILURE": "Switch failed",
"REPLICATE_SUCCESS": "Replicate successfully",
"REPLICATE_FAILURE": "Replicate failed"
"REPLICATE_SUCCESS": "Started successfully",
"REPLICATE_FAILURE": "Started failed"
},
"TOOLTIP": {
"EMAIL": "Email should be a valid email address like name@example.com.",
@ -250,7 +250,8 @@
"REPLICATION_TITLE": "Confirm Rules replication",
"REPLICATION_SUMMARY": "Do you want to replicate the Rules {{param}}?",
"DELETION_TITLE_FAILURE": "failed to delete Rule Deletion",
"DELETION_SUMMARY_FAILURE": "{{param}} have pending/running/retrying status",
"DELETION_SUMMARY_FAILURE": "have pending/running/retrying status",
"REPLICATE_SUMMARY_FAILURE": "have pending/running status",
"FILTER_TARGETS_PLACEHOLDER": "Filter Endpoints",
"DELETION_TITLE_TARGET": "Confirm Endpoint Deletion",
"DELETION_SUMMARY_TARGET": "Do you want to delete the endpoint {{param}}?",
@ -311,14 +312,14 @@
"TOGGLED_SUCCESS": "Toggled replication rule status successfully.",
"CANNOT_EDIT": "Replication rule cannot be changed while it is enabled.",
"POLICY_ALREADY_EXISTS": "Replication rule already exists.",
"FAILED_TO_DELETE_POLICY_ENABLED": "Cannot delete rule: rule has unfinished job(s) or rule is enabled.",
"FAILED_TO_DELETE_POLICY_ENABLED": "Cannot delete rule: rule has unfinished job(s)",
"FOUND_ERROR_IN_JOBS": "Found errors in the replication job(s), please check.",
"INVALID_DATE": "Invalid date.",
"PLACEHOLDER": "We couldn't find any replication rules!",
"JOB_PLACEHOLDER": "We couldn't find any replication jobs!",
"JOB_LOG_VIEWER": "View Replication Job Log",
"NO_ENDPOINT_INFO": "Please go to registries and add an endpoint first",
"NO_PROJECT_INFO": "Please go to projects and add a project first",
"NO_PROJECT_INFO": "Please go to projects and add a project name first",
"FILTER": "Filter",
"SCHEDULE": "Scheduled",
"MANUAL": "Manual",
@ -330,7 +331,8 @@
"SOURCE": "Source",
"REPLICATE": "Replicate",
"DELETE_REMOTE_IMAGES":"Delete remote images when locally deleted",
"REPLICATE_IMMEDIATE":"Replicate existing images immediately"
"REPLICATE_IMMEDIATE":"Replicate existing images immediately",
"NEW": "New"
},
"DESTINATION": {
"NEW_ENDPOINT": "New Endpoint",

View File

@ -42,8 +42,8 @@
"DELETED_FAILURE": "Deleted failed",
"SWITCH_SUCCESS": "Switch successfully",
"SWITCH_FAILURE": "Switch failed",
"REPLICATE_SUCCESS": "Replicate successfully",
"REPLICATE_FAILURE": "Replicate failed"
"REPLICATE_SUCCESS": "Started successfully",
"REPLICATE_FAILURE": "Started failed"
},
"TOOLTIP": {
"EMAIL": "El email debe ser una dirección válida como nombre@ejemplo.com.",
@ -247,7 +247,8 @@
"DELETION_TITLE": "Confirmar Eliminación de Regla",
"DELETION_SUMMARY": "¿Quiere eliminar la regla {{param}}?",
"DELETION_TITLE_FAILURE": "failed to delete Rule Deletion",
"DELETION_SUMMARY_FAILURE": "{{param}} have pending/running/retrying status",
"DELETION_SUMMARY_FAILURE": "have pending/running/retrying status",
"REPLICATE_SUMMARY_FAILURE": "have pending/running status",
"REPLICATION_TITLE": "Confirm Rules replication",
"REPLICATION_SUMMARY": "Do you want to replicate the Rules {{param}}?",
"FILTER_TARGETS_PLACEHOLDER": "Filtrar Endpoints",
@ -317,7 +318,7 @@
"JOB_PLACEHOLDER": "We couldn't find any replication jobs!",
"JOB_LOG_VIEWER": "View Replication Job Log",
"NO_ENDPOINT_INFO": "Please go to registries and add an endpoint first",
"NO_PROJECT_INFO": "Please go to projects and add a project first",
"NO_PROJECT_INFO": "Please go to projects and add a project name first",
"FILTER": "Filter",
"SCHEDULE": "Scheduled",
"MANUAL": "Manual",
@ -327,8 +328,10 @@
"TARGETS":"Target",
"MODE": "Mode",
"SOURCE": "Source",
"REPLICATE": "Replicate",
"DELETE_REMOTE_IMAGES":"Delete remote images when locally deleted",
"REPLICATE_IMMEDIATE":"Replicate existing images immediately"
"REPLICATE_IMMEDIATE":"Replicate existing images immediately",
"NEW": "New"
},
"DESTINATION": {
"NEW_ENDPOINT": "Nuevo Endpoint",

View File

@ -42,8 +42,8 @@
"DELETED_FAILURE": "删除失败",
"SWITCH_SUCCESS": "切换成功",
"SWITCH_FAILURE": "切换失败",
"REPLICATE_SUCCESS": "成功",
"REPLICATE_FAILURE": "复制失败"
"REPLICATE_SUCCESS": "开始成功",
"REPLICATE_FAILURE": "开始失败"
},
"TOOLTIP": {
"EMAIL": "请使用正确的邮箱地址比如name@example.com。",
@ -249,7 +249,8 @@
"DELETION_TITLE_FAILURE": "规则确认删除失败",
"REPLICATION_TITLE": "复制规则确认",
"REPLICATION_SUMMARY": "确认复制规则 {{param}}?",
"DELETION_SUMMARY_FAILURE": "{{param}} 有 pending/running/retrying 状态,不能删除",
"DELETION_SUMMARY_FAILURE": "有 pending/running/retrying 状态,不能删除",
"REPLICATE_SUMMARY_FAILURE": "有 pending/running 状态,不能删除",
"FILTER_TARGETS_PLACEHOLDER": "过滤目标",
"DELETION_TITLE_TARGET": "删除目标确认",
"DELETION_SUMMARY_TARGET": "确认删除目标 {{param}}?",
@ -310,14 +311,14 @@
"TOGGLED_SUCCESS": "切换复制规则状态成功。",
"CANNOT_EDIT": "当复制规则启用时无法修改。",
"POLICY_ALREADY_EXISTS": "规则已存在。",
"FAILED_TO_DELETE_POLICY_ENABLED": "删除复制规则失败: 仍有未完成的任务或规则未停用。",
"FAILED_TO_DELETE_POLICY_ENABLED": "删除复制规则失败: 仍有未完成的任务。",
"FOUND_ERROR_IN_JOBS": "复制任务中包含错误,请检查。",
"INVALID_DATE": "无效日期。",
"PLACEHOLDER": "未发现任何复制规则!",
"JOB_PLACEHOLDER": "未发现任何复制任务!",
"JOB_LOG_VIEWER": "查看复制任务日志",
"NO_ENDPOINT_INFO": "请先添加目标",
"NO_PROJECT_INFO": "请先添加项目",
"NO_PROJECT_INFO": "请先去项目添加一个新的项目名称",
"FILTER": "过滤",
"SCHEDULE": "定时",
"MANUAL": "手动",
@ -327,8 +328,10 @@
"TARGETS":"目标",
"MODE": "模式",
"SOURCE": "资源",
"REPLICATE": "复制",
"DELETE_REMOTE_IMAGES":"删除本地镜像时同时也删除远程的镜像。",
"REPLICATE_IMMEDIATE":"立即复制现有的镜像。"
"REPLICATE_IMMEDIATE":"立即复制现有的镜像。",
"NEW": "新增"
},
"DESTINATION": {
"NEW_ENDPOINT": "新建目标",