refator: move api operation to group

This commit is contained in:
秦圆圆 2022-01-25 14:57:48 +08:00
parent 6a22e79508
commit 4eb639d392
12 changed files with 388 additions and 338 deletions

View File

@ -2,11 +2,7 @@
<nz-sider nzTheme="light" nzWidth="250">
<nz-content>
<div class="inner-content">
<eo-api-group-tree
[treeNodes]="treeNodes"
(groupTreeEvent)="groupTreeEvent($event)"
(updateGroupTreeEvent)="updateGroupTreeEvent($event)"
></eo-api-group-tree>
<eo-api-group-tree></eo-api-group-tree>
</div>
</nz-content>
</nz-sider>
@ -14,7 +10,7 @@
<nz-content>
<div class="inner-content">
<div class="tabs-bar f_row">
<eo-api-tab class="fg1" [apiDataItems]="apiDataItems"></eo-api-tab>
<eo-api-tab class="fg1"></eo-api-tab>
<div class="env">
<eo-env></eo-env>
</div>

View File

@ -1,48 +1,13 @@
import { Component, OnDestroy, OnInit } from '@angular/core';
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { NzFormatEmitEvent, NzTreeNode } from 'ng-zorro-antd/tree';
import { Message, MessageService } from '../../shared/services/message';
import { GroupService } from '../../shared/services/group/group.service';
import { NzModalService } from 'ng-zorro-antd/modal';
import { ApiDataService } from '../../shared/services/api-data/api-data.service';
import { listToTree } from '../../utils/tree';
import { GroupApiDataModel, GroupTreeItem } from '../../shared/models';
import { ApiData } from '../../shared/services/api-data/api-data.model';
import { Group } from '../../shared/services/group/group.model';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { ApiTabService } from './tab/api-tab.service';
@Component({
selector: 'eo-api',
templateUrl: './api.component.html',
styleUrls: ['./api.component.scss'],
})
export class ApiComponent implements OnInit, OnDestroy {
/**
* Default projectID.
*/
projectID = 1;
/**
* All group items.
*/
groupByID: { [key: number | string]: Group };
/**
* All api data items.
*/
apiDataItems: { [key: number | string]: ApiData };
/**
* All Tree items.
*/
treeItems: Array<GroupTreeItem>;
/**
* Level Tree nodes.
*/
treeNodes: Array<GroupTreeItem>;
export class ApiComponent implements OnInit {
/**
* API uuid
*/
@ -62,68 +27,16 @@ export class ApiComponent implements OnInit, OnDestroy {
title: '测试',
},
];
private destroy$: Subject<void> = new Subject<void>();
constructor(
private route: ActivatedRoute,
private groupService: GroupService,
private apiDataService: ApiDataService,
private messageService: MessageService,
private modalService: NzModalService,
private tabSerive: ApiTabService
) {}
ngOnInit(): void {
this.watchChangeRouter();
this.buildGroupTreeData();
this.watchGroupTreeData();
}
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
/**
* Load all group and apiData items.
*/
buildGroupTreeData(): void {
this.groupByID = {};
this.treeItems = [];
this.getGroups();
}
getGroups() {
this.groupService.loadAllByProjectID(this.projectID).subscribe((items: Array<Group>) => {
items.forEach((item) => {
delete item.updatedAt;
this.groupByID[item.uuid] = item;
this.treeItems.push({
title: item.name,
key: item.uuid,
weight: item.weight || 0,
parentID: item.parentID || 0,
isLeaf: false,
});
});
this.getApis();
});
}
getApis() {
this.apiDataService.loadAllByProjectID(this.projectID).subscribe((items: Array<ApiData>) => {
let apiItems = {};
items.forEach((item) => {
delete item.updatedAt;
apiItems[item.uuid] = item;
this.treeItems.push({
title: item.name,
key: item.uuid,
weight: item.weight || 0,
parentID: item.groupID || 0,
method: item.method,
isLeaf: true,
});
});
this.apiDataItems = apiItems;
this.generateGroupTreeData();
});
}
/**
* Get current API ID to show content tab
*/
@ -136,173 +49,4 @@ export class ApiComponent implements OnInit, OnDestroy {
clickContentMenu(data) {
this.tabSerive.apiEvent$.next({ action: 'beforeChangeRouter', data: data });
}
/**
* Event emit from group tree component.
*
* @param event NzFormatEmitEvent
*/
groupTreeEvent(event: NzFormatEmitEvent | any): void {
switch (event.eventName) {
case 'deleteGroup':
this.deleteGroupTreeItems(event.node);
break;
case 'loadAllGroup':
this.buildGroupTreeData();
break;
case 'copyApi':
this.copyApi(event.node);
break;
case 'deleteApi':
this.deleteApi(event.node);
break;
default: {
this.tabSerive.apiEvent$.next({ action: event.eventName, data: event.node });
break;
}
}
}
/**
* Watch group and apiData change event.
*/
watchGroupTreeData(): void {
this.messageService
.get()
.pipe(takeUntil(this.destroy$))
.subscribe((data: Message) => {
switch (data.type) {
case 'addApi':
case 'editApi':
{
this.tabSerive.apiEvent$.next({ action: `${data.type}Finish`,data:data.data});
this.buildGroupTreeData();
break;
}
case 'groupAdd':
case 'groupEdit':
case 'groupDelete':
this.buildGroupTreeData();
break;
case 'apiDelete':
let tmpApi = data.data;
this.tabSerive.apiEvent$.next({ action: 'removeApiDataTabs', data: [tmpApi.uuid] });
this.buildGroupTreeData();
break;
case 'apiBatchDelete':
this.tabSerive.apiEvent$.next({ action: 'removeApiDataTabs', data: data.data.uuids });
break;
}
});
}
/**
* Generate group tree nodes.
*/
generateGroupTreeData(): void {
this.treeItems.sort((a, b) => a.weight - b.weight);
this.treeNodes = [];
listToTree(this.treeItems, this.treeNodes, 0);
}
/**
* Copy api data.
*
* @param node NzTreeNode
*/
copyApi(node: NzTreeNode): void {
const data = this.apiDataItems[node.key];
delete data.uuid;
delete data.createdAt;
data.name += ' Copy';
window.sessionStorage.setItem('apiDataWillbeSave', JSON.stringify(data));
this.tabSerive.apiEvent$.next({
action: 'newApi',
});
}
/**
* Delete api data.
*
* @param node NzTreeNode
*/
deleteApi(node: NzTreeNode): void {
this.modalService.confirm({
nzTitle: '删除确认?',
nzContent: `确认要删除数据<strong>${node.title}</strong>吗?删除后不可恢复!`,
nzOnOk: () => {
this.apiDataService.remove(node.key).subscribe((result: boolean) => {
this.messageService.send({ type: 'apiDelete', data: { uuid: node.key } });
});
},
});
}
/**
* Get all child items belong to parentID
*
* @param list
* @param tree
* @param parentID
*/
getChildrenFromTree(list: Array<GroupTreeItem>, tree: GroupApiDataModel, parentID: number | string): void {
list.forEach((data) => {
if (data.parentID === parentID) {
if (!data.isLeaf) {
tree.group.push(data.key);
this.getChildrenFromTree(list, tree, data.key);
} else {
tree.api.push(data.key);
}
}
});
}
/**
* Delete all tree items of parent node.
*
* @param data GroupApiDataModel
*/
deleteGroupTreeItems(group: Group) {
const data: GroupApiDataModel = { group: [], api: [] };
this.getChildrenFromTree(this.treeItems, data, group.uuid);
if (data.group.length > 0 && data.api.length > 0) {
this.groupService.bulkRemove(data.group).subscribe((result) => {
this.apiDataService.bulkRemove(data.api).subscribe((result) => {
this.buildGroupTreeData();
this.messageService.send({ type: 'apiBatchDelete', data: { uuids: data.api } });
});
});
} else if (data.group.length > 0) {
this.groupService.bulkRemove(data.group).subscribe((result) => {
this.buildGroupTreeData();
});
} else if (data.api.length > 0) {
this.apiDataService.bulkRemove(data.api).subscribe((result) => {
this.buildGroupTreeData();
this.messageService.send({ type: 'apiBatchDelete', data: { uuids: data.api } });
});
} else {
this.buildGroupTreeData();
}
}
/**
* Update tree items after drag.
*
* @param data GroupApiDataModel
*/
updateGroupTreeEvent(data: GroupApiDataModel) {
if (data.group.length > 0 && data.api.length > 0) {
this.groupService.bulkUpdate(data.group).subscribe((result) => {
console.log(result);
this.apiDataService.bulkUpdate(data.api).subscribe((result) => {
console.log(result);
});
});
} else if (data.group.length > 0) {
this.groupService.bulkUpdate(data.group).subscribe((result) => {
console.log(result);
});
} else if (data.api.length > 0) {
this.apiDataService.bulkUpdate(data.api).subscribe((result) => {
console.log(result);
});
}
}
}

View File

@ -28,6 +28,7 @@ import { ApiTabService } from './tab/api-tab.service';
import { MessageService } from '../../shared/services/message';
import { ApiGroupTreeComponent } from './group/tree/api-group-tree.component';
import { ApiTabComponent } from './tab/api-tab.component';
import { ApiService } from './api.service';
const COMPONENTS = [ApiComponent, ApiGroupEditComponent, ApiGroupTreeComponent];
@NgModule({
@ -54,6 +55,6 @@ const COMPONENTS = [ApiComponent, ApiGroupEditComponent, ApiGroupTreeComponent];
],
declarations: [...COMPONENTS, ApiTabComponent],
exports: [],
providers: [ApiDataService, GroupService, MessageService, ApiTabService],
providers: [ApiDataService, GroupService, MessageService, ApiTabService,ApiService],
})
export class ApiModule {}

View File

@ -0,0 +1,45 @@
import { Injectable } from '@angular/core';
import { NzModalService } from 'ng-zorro-antd/modal';
import { ApiDataService } from '../../shared/services/api-data/api-data.service';
import { MessageService } from '../../shared/services/message';
@Injectable()
export class ApiService {
constructor(
private apiDataService: ApiDataService,
private modalService: NzModalService,
private messageService: MessageService
) {}
/**
* Copy api data.
*
* @param node NzTreeNode
*/
copy(apiData): void {
delete apiData.uuid;
delete apiData.createdAt;
apiData.name += ' Copy';
window.sessionStorage.setItem('apiDataWillbeSave', JSON.stringify(apiData));
this.messageService.send({ type: 'copyApi', data: apiData });
}
/**
* Delete api data.
*
* @param node NzTreeNode
*/
delete(apiData): void {
this.modalService.confirm({
nzTitle: '删除确认?',
nzContent: `确认要删除数据 <strong title="${apiData.title}">${
apiData.title.length > 50 ? apiData.title.slice(0, 50) + '...' : apiData.title
}</strong> `,
nzOnOk: () => {
this.apiDataService.remove(apiData.key).subscribe((result: boolean) => {
this.messageService.send({ type: 'deleteApi', data: { uuid: apiData.key } });
});
},
});
}
}

View File

@ -2,9 +2,9 @@
<div class="t-1">
<nz-tag [nzColor]="'#108ee9'">{{ apiData.protocol | uppercase }}</nz-tag>
<nz-tag [nzColor]="'#0aa07a'">{{ apiData.method | uppercase }}</nz-tag>
<span>{{ apiData.uri }}</span>
<span class="mt10 dp_b text_omit">{{ apiData.uri }}</span>
</div>
<p class="api_name">{{ apiData.name }}</p>
<p class="api_name text_omit">{{ apiData.name }}</p>
<div *ngIf="apiData.requestHeaders && apiData.requestHeaders.length">
<p class="api_line">请求头部</p>
<eo-api-detail-header [model]="apiData.requestHeaders"></eo-api-detail-header>

View File

@ -29,7 +29,7 @@ export class ApiDetailComponent implements OnInit {
BODY_TYPE: reverseObj(ApiBodyType),
JSON_ROOT_TYPE: reverseObj(JsonRootType)
};
constructor(private apiService: ApiDataService, private route: ActivatedRoute) {
constructor(private apiDataService: ApiDataService, private route: ActivatedRoute) {
}
ngOnInit(): void {
this.route.queryParams.subscribe((params) => {
@ -41,7 +41,7 @@ export class ApiDetailComponent implements OnInit {
});
}
getApiByUuid(id: number) {
this.apiService.load(id).subscribe((result: ApiData) => {
this.apiDataService.load(id).subscribe((result: ApiData) => {
['requestBody', 'responseBody'].forEach((tableName) => {
if (['xml', 'json'].includes(result[`${tableName}Type`])) {
result[tableName] = treeToListHasLevel(result[tableName]);

View File

@ -7,11 +7,14 @@
</nz-form-control>
</nz-form-item>
</form>
<div *ngIf="isDelete">
删除<strong>{{ group.name }}</strong>后,该分组下的数据都会删除。该操作无法撤销,确认删除吗?
</div>
<p *ngIf="isDelete">
删除 <strong title="{{ group.name }}">{{
group.name.length > 50 ? group.name.slice(0, 50) + '...' : group.name
}}</strong
> 后,该分组下的数据都会删除。该操作无法撤销,确认删除吗?
</p>
</div>
<footer class="group_edit_footer">
<button nz-button nzType="primary" (click)='submit()' [nzLoading]="showLoading">确定</button>
<button nz-button nzType="primary" (click)="submit()" [nzLoading]="showLoading">确定</button>
<button nz-button (click)="close()">取消</button>
</footer>

View File

@ -36,7 +36,7 @@
[nzSearchValue]="searchValue"
[nzExpandedKeys]="expandedKeys"
(nzClick)="treeItemClick($event)"
(nzExpandChange)="nzEvent($event)"
(nzExpandChange)="groupTreeEvent($event)"
nzDraggable
nzBlockNode
(nzOnDrop)="treeItemDrop($event)"

View File

@ -1,35 +1,185 @@
import { Component, Input, OnInit, Output, EventEmitter } from '@angular/core';
import { Component, OnInit, OnDestroy } from '@angular/core';
import { GroupTreeItem, GroupApiDataModel } from '../../../../shared/models';
import { Group } from '../../../../shared/services/group/group.model';
import { Message } from '../../../../shared/services/message/message.model';
import { ApiData } from '../../../../shared/services/api-data/api-data.model';
import { NzModalRef, NzModalService } from 'ng-zorro-antd/modal';
import { NzFormatEmitEvent, NzTreeNode } from 'ng-zorro-antd/tree';
import { ApiGroupEditComponent } from '../edit/api-group-edit.component';
import { Message } from '../../../../shared/services/message/message.model';
import { GroupService } from '../../../../shared/services/group/group.service';
import { ApiService } from '../../api.service';
import { ApiTabService } from '../../tab/api-tab.service';
import { ApiDataService } from '../../../../shared/services/api-data/api-data.service';
import { MessageService } from '../../../../shared/services/message';
import { Subject, takeUntil } from 'rxjs';
import { listToTree } from '../../../../utils/tree';
@Component({
selector: 'eo-api-group-tree',
templateUrl: './api-group-tree.component.html',
styleUrls: ['./api-group-tree.component.scss'],
})
export class ApiGroupTreeComponent implements OnInit {
@Input() treeNodes: Array<GroupTreeItem> | any;
@Output() groupTreeEvent = new EventEmitter();
@Output() updateGroupTreeEvent = new EventEmitter();
export class ApiGroupTreeComponent implements OnInit, OnDestroy {
/**
* Expanded keys of tree.
*/
expandedKeys: Array<string | number>;
searchValue = '';
constructor(private modalService: NzModalService) {}
/**
* Default projectID.
*/
projectID = 1;
/**
* All group items.
*/
groupByID: { [key: number | string]: Group };
/**
* All api data items.
*/
apiDataItems: { [key: number | string]: ApiData };
ngOnInit(): void {}
nzEvent(event: NzFormatEmitEvent): void {
console.log(event);
this.groupTreeEvent.emit(event);
/**
* All Tree items.
*/
treeItems: Array<GroupTreeItem>;
/**
* Level Tree nodes.
*/
treeNodes: Array<GroupTreeItem>;
private destroy$: Subject<void> = new Subject<void>();
constructor(
private modalService: NzModalService,
private groupService: GroupService,
private apiDataService: ApiDataService,
private apiService: ApiService,
private tabSerive: ApiTabService,
private messageService: MessageService
) {}
ngOnInit(): void {
this.buildGroupTreeData();
this.watchGroupTreeData();
}
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
/**
* Watch group and apiData change event.
*/
watchGroupTreeData(): void {
this.messageService
.get()
.pipe(takeUntil(this.destroy$))
.subscribe((data: Message) => {
switch (data.type) {
case 'addApi':
case 'editApi': {
this.tabSerive.apiEvent$.next({ action: `${data.type}Finish`, data: data.data });
this.buildGroupTreeData();
break;
}
case 'copyApi': {
this.tabSerive.apiEvent$.next({
action: 'copyApi',
});
break;
}
case 'groupAdd':
case 'groupEdit':
case 'groupDelete':
this.buildGroupTreeData();
break;
case 'deleteApi':
let tmpApi = data.data;
this.tabSerive.apiEvent$.next({ action: 'removeApiDataTabs', data: [tmpApi.uuid] });
this.buildGroupTreeData();
break;
case 'apiBatchDelete':
this.tabSerive.apiEvent$.next({ action: 'removeApiDataTabs', data: data.data.uuids });
break;
}
});
}
/**
* Generate group tree nodes.
*/
generateGroupTreeData(): void {
this.treeItems.sort((a, b) => a.weight - b.weight);
this.treeNodes = [];
listToTree(this.treeItems, this.treeNodes, 0);
}
/**
* Load all group and apiData items.
*/
buildGroupTreeData(): void {
this.groupByID = {};
this.treeItems = [];
this.getGroups();
}
getGroups() {
this.groupService.loadAllByProjectID(this.projectID).subscribe((items: Array<Group>) => {
items.forEach((item) => {
delete item.updatedAt;
this.groupByID[item.uuid] = item;
this.treeItems.push({
title: item.name,
key: item.uuid,
weight: item.weight || 0,
parentID: item.parentID || 0,
isLeaf: false,
});
});
this.getApis();
});
}
getApis() {
this.apiDataService.loadAllByProjectID(this.projectID).subscribe((items: Array<ApiData>) => {
let apiItems = {};
items.forEach((item) => {
delete item.updatedAt;
apiItems[item.uuid] = item;
this.treeItems.push({
title: item.name,
key: item.uuid,
weight: item.weight || 0,
parentID: item.groupID || 0,
method: item.method,
isLeaf: true,
});
});
this.apiDataItems = apiItems;
this.tabSerive.apiEvent$.next({ action: 'afterLoadApi', data: this.apiDataItems });
this.generateGroupTreeData();
});
}
/**
* Event emit from group tree component.
*
* @param event NzFormatEmitEvent
*/
groupTreeEvent(event: NzFormatEmitEvent | any): void {
switch (event.eventName) {
case 'deleteGroup':
this.deleteGroupTreeItems(event.node);
break;
case 'loadAllGroup':
this.buildGroupTreeData();
break;
case 'copyApi':
this.apiService.copy(this.apiDataItems[event.node.key]);
break;
case 'deleteApi':
this.apiService.delete(this.apiDataItems[event.node.key]);
break;
default: {
this.tabSerive.apiEvent$.next({ action: event.eventName, data: event.node });
break;
}
}
}
/**
@ -42,7 +192,7 @@ export class ApiGroupTreeComponent implements OnInit {
event.node.isExpanded = !event.node.isExpanded;
} else {
event.eventName = 'detailApi';
this.groupTreeEvent.emit(event);
this.groupTreeEvent(event);
}
}
@ -112,12 +262,12 @@ export class ApiGroupTreeComponent implements OnInit {
if (res) {
if ('deleteGroup' === res.type) {
const group: Group = res.data.group;
this.groupTreeEvent.emit({
this.groupTreeEvent({
eventName: 'deleteGroup',
node: group,
});
} else {
this.groupTreeEvent.emit({
this.groupTreeEvent({
eventName: 'loadAllGroup',
});
}
@ -148,7 +298,23 @@ export class ApiGroupTreeComponent implements OnInit {
groupApiData.group.push({ uuid: dragNode.key, weight: 0, parentID: 0 });
}
}
this.updateGroupTreeEvent.emit(groupApiData);
this.updateGroupTreeEvent(groupApiData);
}
/**
* Update tree items after drag.
*
* @param data GroupApiDataModel
*/
updateGroupTreeEvent(data: GroupApiDataModel) {
if (data.group.length > 0 && data.api.length > 0) {
this.groupService.bulkUpdate(data.group).subscribe((result) => {
this.apiDataService.bulkUpdate(data.api).subscribe((result) => {});
});
} else if (data.group.length > 0) {
this.groupService.bulkUpdate(data.group).subscribe((result) => {});
} else if (data.api.length > 0) {
this.apiDataService.bulkUpdate(data.api).subscribe((result) => {});
}
}
private nodeToGroup(node: NzTreeNode): Group {
return {
@ -159,8 +325,56 @@ export class ApiGroupTreeComponent implements OnInit {
weight: node.origin.weight,
};
}
/**
* Get all child items belong to parentID
*
* @param list
* @param tree
* @param parentID
*/
getChildrenFromTree(list: Array<GroupTreeItem>, tree: GroupApiDataModel, parentID: number | string): void {
list.forEach((data) => {
if (data.parentID === parentID) {
if (!data.isLeaf) {
tree.group.push(data.key);
this.getChildrenFromTree(list, tree, data.key);
} else {
tree.api.push(data.key);
}
}
});
}
/**
* Delete all tree items of parent node.
*
* @param data GroupApiDataModel
*/
deleteGroupTreeItems(group: Group) {
const data: GroupApiDataModel = { group: [], api: [] };
this.getChildrenFromTree(this.treeItems, data, group.uuid);
if (data.group.length > 0 && data.api.length > 0) {
this.groupService.bulkRemove(data.group).subscribe((result) => {
this.apiDataService.bulkRemove(data.api).subscribe((result) => {
this.buildGroupTreeData();
this.messageService.send({ type: 'apiBatchDelete', data: { uuids: data.api } });
});
});
} else if (data.group.length > 0) {
this.groupService.bulkRemove(data.group).subscribe((result) => {
this.buildGroupTreeData();
});
} else if (data.api.length > 0) {
this.apiDataService.bulkRemove(data.api).subscribe((result) => {
this.buildGroupTreeData();
this.messageService.send({ type: 'apiBatchDelete', data: { uuids: data.api } });
});
} else {
this.buildGroupTreeData();
}
}
onClick(event: NzFormatEmitEvent) {
event.event.stopPropagation();
this.groupTreeEvent.emit(event);
this.groupTreeEvent(event);
}
}

View File

@ -1,29 +1,32 @@
<nz-tabset
[(nzSelectedIndex)]="selectedIndex"
nzType="editable-card"
(nzAdd)="newTab()"
nzHideAdd="true"
(nzClose)="closeTab($event)"
(nzSelectChange)="switchTab()"
(nzSelectChange)="pickTab()"
[nzTabBarExtraContent]="extraTemplate"
>
<nz-tab *ngFor="let tab of tabs; let i = index" nzClosable [nzTitle]="titleTemplate">
<ng-template #titleTemplate>
<span class="mr5 method_text_{{ tab.method }}" *ngIf="tab.method">{{ tab.method.slice(0, 4) }}</span>
{{ tab.title }}
<span class="text_omit tab_text"> {{ tab.title }}</span>
</ng-template>
</nz-tab>
</nz-tabset>
<ng-template #extraTemplate>
<button (click)="newTab()" class="ant-tabs-nav-add {{ tabs.length >= MAX_TAB_LIMIT ? 'hidden' : '' }}">
<i nz-icon nzType="plus"></i>
</button>
<a nz-dropdown nzTrigger="click" [nzDropdownMenu]="menu">
<button class="ant-tabs-nav-more"><i nz-icon nzType="ellipsis" nzTheme="outline"></i></button>
</a>
<nz-dropdown-menu #menu="nzDropdownMenu">
<ul nz-menu>
<li nz-menu-item>关闭其它标签页</li>
<li nz-menu-item>关闭所有标签</li>
<li nz-menu-item (click)="operateTab('closeOther')">关闭所有标签(当前标签除外)</li>
<li nz-menu-item (click)="operateTab('closeAll')">关闭所有标签</li>
<!-- <li nz-menu-item>关闭已保存</li> -->
<li nz-menu-item>关闭左侧标签页</li>
<li nz-menu-item>关闭右侧标签页</li>
<li nz-menu-item (click)="operateTab('closeLeft')">关闭左侧标签页</li>
<li nz-menu-item (click)="operateTab('closeRight')">关闭右侧标签页</li>
</ul>
</nz-dropdown-menu>
</ng-template>

View File

@ -1,3 +1,6 @@
.tab_text{
max-width: 100px;
}
::ng-deep {
eo-api-tab {
width: calc(100% - 160px);
@ -18,7 +21,7 @@
}
.ant-tabs-tab-btn {
font-size: 12px;
font-weight: bold;
display: flex;
}
}
.ant-tabs-nav-wrap {

View File

@ -1,18 +1,20 @@
import { Component, OnInit, Input, OnChanges, SimpleChanges, OnDestroy } from '@angular/core';
import { Component, OnInit, OnDestroy } from '@angular/core';
import { ActivatedRoute, NavigationEnd, Router } from '@angular/router';
import { ActivatedRoute, Router } from '@angular/router';
import { TabItem } from './tab.model';
import { ApiData } from '../../../shared/services/api-data/api-data.model';
import { ApiTabService } from './api-tab.service';
import { filter, Subject, takeUntil } from 'rxjs';
import { min, Subject, takeUntil } from 'rxjs';
@Component({
selector: 'eo-api-tab',
templateUrl: './api-tab.component.html',
styleUrls: ['./api-tab.component.scss'],
})
export class ApiTabComponent implements OnInit, OnChanges, OnDestroy {
@Input() apiDataItems;
id: number;
export class ApiTabComponent implements OnInit, OnDestroy {
apiDataItems: { [key: number | string]: ApiData };
/**
* Tab items.
*/
@ -30,7 +32,7 @@ export class ApiTabComponent implements OnInit, OnChanges, OnDestroy {
test: { path: '/home/api/test', title: '新 API' },
detail: { path: '/home/api/detail', title: 'API 详情' },
};
MAX_LIMIT = 15;
MAX_TAB_LIMIT = 15;
private destroy$: Subject<void> = new Subject<void>();
constructor(private router: Router, private route: ActivatedRoute, private tabSerive: ApiTabService) {}
@ -38,11 +40,6 @@ export class ApiTabComponent implements OnInit, OnChanges, OnDestroy {
ngOnInit(): void {
this.watchApiAction();
}
ngOnChanges(changes: SimpleChanges): void {
if (changes.apiDataItems && !changes.apiDataItems.previousValue && changes.apiDataItems.currentValue) {
this.initTab();
}
}
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
@ -58,12 +55,13 @@ export class ApiTabComponent implements OnInit, OnChanges, OnDestroy {
*/
initTab() {
let apiID = Number(this.route.snapshot.queryParams.uuid);
let hasApiExist = this.apiDataItems[apiID];
//delete api
if (!hasApiExist) {
this.closeTab({ index: this.selectedIndex });
return;
}
if (apiID) {
let hasApiExist = this.apiDataItems[apiID];
if (!hasApiExist) {
this.closeTab({ index: this.selectedIndex });
return;
}
const tab = this.getTabInfo({
id: apiID,
});
@ -81,7 +79,7 @@ export class ApiTabComponent implements OnInit, OnChanges, OnDestroy {
* @param tab TabItem
*/
appendTab(which = 'test', apiData: any = {}): void {
if (this.tabs.length >= this.MAX_LIMIT) return;
if (this.tabs.length >= this.MAX_TAB_LIMIT) return;
let tab: TabItem = Object.assign(
{
uuid: new Date().getTime(),
@ -89,20 +87,20 @@ export class ApiTabComponent implements OnInit, OnChanges, OnDestroy {
which === 'unset' ? {} : this.defaultTabs[which],
apiData
);
let existTabIndex = this.tabs.findIndex((val) => val.key === tab.key);
if (tab.key && existTabIndex !== -1) {
this.selectedIndex = existTabIndex;
if (this.tabs[existTabIndex].path !== tab.path) {
//exist api(same tab) change page
this.tabs[existTabIndex].path = tab.path;
this.switchTab();
let existApiIndex = this.tabs.findIndex((val) => val.key === tab.key);
if (tab.key && existApiIndex !== -1) {
this.selectedIndex = existApiIndex;
if (this.tabs[existApiIndex].path !== tab.path) {
//* exist api in same tab change route,such as edit page to detail
this.tabs[existApiIndex].path = tab.path;
this.pickTab();
}
return;
}
this.tabs.push(tab);
// if index no change,manual change reflesh content
if (this.selectedIndex === this.tabs.length - 1) {
this.switchTab();
this.pickTab();
return;
}
this.selectedIndex = this.tabs.length - 1;
@ -123,32 +121,68 @@ export class ApiTabComponent implements OnInit, OnChanges, OnDestroy {
this.closeTab(item);
});
}
removeTabCache(index) {
if (!this.tabs[index]) return;
this.tabSerive.removeData(this.tabs[index].uuid);
}
/**
* Close Tab and keep tab status
*
* @param index number
*/
closeTab({ index }: { index: number }): void {
if (this.tabs[index]) {
this.tabSerive.removeData(this.tabs[index].uuid);
}
this.tabs.splice(index, 1);
//no tab left
let selectIndex = this.selectedIndex <= index ? index - 1 : this.selectedIndex;
this.batchCloseTab([index], selectIndex);
}
batchCloseTab(closeTabs, selectIndex?) {
closeTabs.forEach((index) => {
this.removeTabCache(index);
});
this.tabs = this.tabs.filter((val, index) => !closeTabs.includes(index));
if (0 === this.tabs.length) {
this.newTab();
return;
}
//selectedIndex no change
if (this.selectedIndex < this.tabs.length) {
this.switchTab();
if (selectIndex !== this.selectedIndex) {
this.selectedIndex = selectIndex;
}
}
/**
* router change after switch the tab or tab content
* Tab Close operate
* @param action closeOther|closeAll|closeLeft|closeRight
*/
oeprateCloseTab(action) {
let closeTabs = [...new Array(this.tabs.length).keys()],
tmpSelectIndex = 0;
switch (action) {
case 'closeOther':
closeTabs.splice(this.selectedIndex, 1);
break;
case 'closeLeft':
closeTabs = closeTabs.slice(0, this.selectedIndex);
break;
case 'closeRight':
closeTabs = closeTabs.slice(this.selectedIndex + 1);
tmpSelectIndex = this.selectedIndex;
break;
}
this.batchCloseTab(closeTabs, tmpSelectIndex);
}
/**
* Tab operate
* @param action closeOther|closeAll|closeLeft|closeRight
*/
operateTab(action) {
if (action.includes('close')) {
this.oeprateCloseTab(action);
}
}
/**
* Pick tab after switch the tab or tab content
* @param {TabItem} inArg.tab
* @param inArg.index
*/
switchTab() {
pickTab() {
let tab = this.tabs[this.selectedIndex];
this.tabSerive.tabChange$.next(tab);
this.activeRoute(tab);
@ -170,6 +204,7 @@ export class ApiTabComponent implements OnInit, OnChanges, OnDestroy {
case 'editApi':
this.appendTab('edit', inArg.data.origin);
break;
case 'copyApi':
case 'newApi':
this.appendTab('edit', inArg.data ? { groupID: inArg.data.key } : {});
break;
@ -180,6 +215,7 @@ export class ApiTabComponent implements OnInit, OnChanges, OnDestroy {
apiData: inArg.data,
})
);
this.pickTab();
break;
}
case 'addApiFinish':
@ -190,13 +226,18 @@ export class ApiTabComponent implements OnInit, OnChanges, OnDestroy {
apiData: inArg.data,
})
);
this.switchTab();
this.pickTab();
break;
}
case 'removeApiDataTabs': {
this.removeApiDataTabs(inArg.data);
break;
}
case 'afterLoadApi': {
this.apiDataItems = inArg.data;
this.initTab();
break;
}
case 'beforeChangeRouter': {
this.changeCurrentTab(
this.getTabInfo({