mirror of
https://github.com/1721518185/WeCloud.git
synced 2026-07-28 01:25:14 +00:00
feat: integrate blob storage and blob manage
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import {Component, Input, OnInit} from '@angular/core';
|
||||
import { NzMessageService } from 'ng-zorro-antd/message';
|
||||
import { NzUploadChangeParam, NzUploadFile } from 'ng-zorro-antd/upload';
|
||||
import { BlobServiceClient, ContainerClient } from '@azure/storage-blob';
|
||||
@@ -10,10 +10,11 @@ import { NzModalService } from 'ng-zorro-antd/modal';
|
||||
styleUrls: ['./file-upload.component.scss']
|
||||
})
|
||||
export class FileUploadComponent implements OnInit {
|
||||
@Input() uniqueGameName: string | '' | undefined;
|
||||
uploading = false;
|
||||
fileList: NzUploadFile[] = [];
|
||||
uploadTitle = '';
|
||||
sasUrl = 'https://wecloudblob.blob.core.windows.net/?sv=2022-11-02&ss=bfqt&srt=sco&sp=rwdlacupiytfx&se=2023-12-18T18:13:02Z&st=2023-12-18T10:13:02Z&spr=https,http&sig=pMEPhuudBIztt%2FjWBY%2BWIeGqWHN6BdYMAzUmGSy7PUU%3D';
|
||||
sasUrl = 'https://wecloudblob.blob.core.windows.net/games?si=all&sv=2022-11-02&sr=c&sig=YU%2BMkvJqCF8%2FKQkAH2orl8%2FVzHWhUpZydluUfHfFZK0%3D';
|
||||
containerName = 'games';
|
||||
blobServiceClient: BlobServiceClient;
|
||||
containerClient: ContainerClient;
|
||||
@@ -34,8 +35,13 @@ export class FileUploadComponent implements OnInit {
|
||||
};
|
||||
|
||||
handleUpload(): void {
|
||||
if (!this.uniqueGameName || this.uniqueGameName.trim() === '') {
|
||||
this.$message.warning('Please select the media name before uploading.');
|
||||
return;
|
||||
}
|
||||
|
||||
this.modalService.confirm({
|
||||
nzTitle: 'Confirm Upload',
|
||||
nzTitle: 'Please Confirm Your Upload',
|
||||
nzContent: `Title: ${this.uploadTitle}<br/>Number of files: ${this.fileList.length}`,
|
||||
nzOnOk: () => this.onConfirmUpload()
|
||||
});
|
||||
@@ -43,13 +49,11 @@ export class FileUploadComponent implements OnInit {
|
||||
|
||||
async onConfirmUpload() {
|
||||
this.uploading = true;
|
||||
this.uploadTitle = ''; // Reset the title for the next upload
|
||||
|
||||
for (const file of this.fileList) {
|
||||
const blob = new Blob([file as any], { type: file.type });
|
||||
await this.uploadFileToAzure(this.uploadTitle, file.name, blob);
|
||||
}
|
||||
|
||||
this.uploadTitle = '';
|
||||
this.fileList = [];
|
||||
this.uploading = false;
|
||||
}
|
||||
@@ -66,7 +70,8 @@ export class FileUploadComponent implements OnInit {
|
||||
|
||||
async uploadFileToAzure(uploadTitle: string, fileName: string, blob: Blob) {
|
||||
try {
|
||||
const newFileName = `${uploadTitle}_${fileName}`;
|
||||
const username = localStorage.getItem('username') as any;
|
||||
const newFileName = `${uploadTitle}_${username}_${this.uniqueGameName}_${fileName}`;
|
||||
const blockBlobClient = this.containerClient.getBlockBlobClient(newFileName);
|
||||
await blockBlobClient.uploadData(blob);
|
||||
this.$message.success(`${fileName} uploaded successfully.`);
|
||||
|
||||
@@ -146,7 +146,7 @@
|
||||
|
||||
<!-- Insert the FileUploadComponent -->
|
||||
<div nz-col [nzSpan]="24">
|
||||
<app-file-upload></app-file-upload>
|
||||
<app-file-upload [uniqueGameName]="uniqueGameName"></app-file-upload>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@@ -16,6 +16,8 @@ export class ListComponent implements OnInit {
|
||||
loading: boolean = true;
|
||||
isVisible: boolean = false;
|
||||
addForm!: FormGroup;
|
||||
// the game name for upload media
|
||||
uniqueGameName: string | '' | undefined;
|
||||
addField: any = [
|
||||
{
|
||||
label: 'Name',
|
||||
@@ -108,6 +110,10 @@ export class ListComponent implements OnInit {
|
||||
Distributer: [null],
|
||||
Developer: [null],
|
||||
});
|
||||
// @ts-ignore
|
||||
this.addForm.get('Name').valueChanges.subscribe((value) => {
|
||||
this.uniqueGameName = value;
|
||||
});
|
||||
this.getWegameList(true);
|
||||
}
|
||||
|
||||
@@ -115,24 +121,7 @@ export class ListComponent implements OnInit {
|
||||
return JSON.parse(localStorage.getItem('names') || '[]');
|
||||
}
|
||||
submitForm(): void {
|
||||
if (this.addForm.valid) {
|
||||
let params = { ...this.addForm.value };
|
||||
this.apiService.post('new_game', params).subscribe(
|
||||
(res: any) => {
|
||||
this.isVisible = false;
|
||||
this.getWegameList();
|
||||
this.$message.success(`add success!`);
|
||||
},
|
||||
() => {}
|
||||
);
|
||||
} else {
|
||||
Object.values(this.addForm.controls).forEach((control: any) => {
|
||||
if (control.invalid) {
|
||||
control.markAsDirty();
|
||||
control.updateValueAndValidity({ onlySelf: true });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
onSearch() {
|
||||
@@ -179,7 +168,6 @@ export class ListComponent implements OnInit {
|
||||
)[1];
|
||||
});
|
||||
this.total = total;
|
||||
console.log('this.wegameList', this.wegameList);
|
||||
if (names) {
|
||||
localStorage.setItem('names', JSON.stringify(names));
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ export class MyUploadComponent implements OnInit {
|
||||
}[] = [];
|
||||
// Azure Storage SAS URL
|
||||
sasUrl = 'https://wecloudblob.blob.core.windows.net/?sv=2022-11-02&ss=bfqt&srt=sco&sp=rwdlacupiytfx&se=2023-12-18T18:13:02Z&st=2023-12-18T10:13:02Z&spr=https,http&sig=pMEPhuudBIztt%2FjWBY%2BWIeGqWHN6BdYMAzUmGSy7PUU%3D';
|
||||
// sasToken = 'si=asset&sv=2022-11-02&sr=c&sig=OA8DpIpkl1ak4ZG%2BA7BzMtIRbI6carOTxLtzAcHa9pg%3D'
|
||||
containerName = 'games';
|
||||
constructor(
|
||||
private apiService: ApiService,
|
||||
private $message: NzMessageService,
|
||||
@@ -38,14 +38,6 @@ export class MyUploadComponent implements OnInit {
|
||||
|
||||
ngOnInit() {
|
||||
this.username = localStorage.getItem('username') as any;
|
||||
this.loadImagesFromAzure();
|
||||
|
||||
}
|
||||
|
||||
private extractSasToken(): string {
|
||||
// Extract the SAS token from your sasUrl
|
||||
const sasToken = this.sasUrl.split('?')[1];
|
||||
return sasToken ? `?${sasToken}` : '';
|
||||
}
|
||||
|
||||
previewImage(image: { url: string, type: string }) {
|
||||
@@ -73,56 +65,20 @@ export class MyUploadComponent implements OnInit {
|
||||
|
||||
async deleteImage(index: number) {
|
||||
const image = this.images[index];
|
||||
const blobUrl = image.blobUrl; // Assuming this is the URL of the blob
|
||||
const blobUrl = image.blobUrl;
|
||||
const xmsVersion = '2022-11-02';
|
||||
|
||||
const headers = new HttpHeaders({
|
||||
'x-ms-date': new Date().toUTCString(),
|
||||
'x-ms-version': '2022-11-02' // Use the version of Azure Storage Service you are using
|
||||
'x-ms-version': xmsVersion
|
||||
});
|
||||
|
||||
try {
|
||||
await this.http.delete(blobUrl, { headers }).toPromise();
|
||||
// After the image is successfully deleted from Azure, remove it from the images array
|
||||
//refresh the page
|
||||
this.images.splice(index, 1);
|
||||
} catch (error) {
|
||||
console.error('Failed to delete blob:', error);
|
||||
}
|
||||
}
|
||||
async loadImagesFromAzure() {
|
||||
const containerUrl = 'https://medias.blob.core.windows.net/games';
|
||||
const query = '?restype=container&comp=list'; // SAS token
|
||||
const fullUrl = `${containerUrl}${query}`;
|
||||
|
||||
try {
|
||||
const response = await lastValueFrom(this.http.get(fullUrl, { responseType: 'text' }));
|
||||
const parser = new DOMParser();
|
||||
const xmlDoc = parser.parseFromString(response, 'text/xml');
|
||||
const blobs = xmlDoc.getElementsByTagName('Blob');
|
||||
|
||||
this.images = [];
|
||||
for (let i = 0; i < blobs.length; i++) {
|
||||
const blob = blobs[i];
|
||||
const name = blob.getElementsByTagName('Name')[0].textContent || '';
|
||||
const title = JSON.parse(localStorage.getItem('gameInfo') as any).Title;
|
||||
const lastModified = blob.getElementsByTagName('Last-Modified')[0].textContent || '';
|
||||
if (!name || name.includes(this.username)) {
|
||||
// const blobUrl = `${containerUrl}/${name}${this.extractSasToken()}}`;
|
||||
const url = blob.getElementsByTagName('Url')[0].textContent || '';
|
||||
const blobUrl = `${containerUrl}/${name}${this.extractSasToken()}`;
|
||||
const picTitle = name.split('_')[0].replace('assets/', '');
|
||||
// Assuming you want to display images
|
||||
this.images.push({
|
||||
url: url,
|
||||
type: 'image/jpeg',
|
||||
uploadTime: lastModified ,
|
||||
blobUrl: blobUrl,
|
||||
picTitle: picTitle
|
||||
}); // Modify 'type' as needed
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error fetching blobs:', error);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,16 +1,2 @@
|
||||
|
||||
<h2>Upload Count({{images.length}})</h2>
|
||||
<nz-list [nzDataSource]="images" nzBordered>
|
||||
<nz-list-item *ngFor="let image of images; let i = index">
|
||||
<nz-list-item-meta
|
||||
[nzAvatar]="image.url"
|
||||
[nzTitle]="'Upload by ' + username"
|
||||
[nzDescription]="image.uploadTime">
|
||||
<p>{{ image.picTitle }}</p>
|
||||
<a [href]="image.url" target="_blank">{{ image.url }}</a>
|
||||
</nz-list-item-meta>
|
||||
<button nz-button nzType="primary" [ngStyle]="{ marginRight: '10px' }" (click)="previewImage(image)">Preview</button>
|
||||
<button nz-button nzType="primary" [ngStyle]="{ marginRight: '10px' }" (click)="downloadImage(image.url)">Download</button>
|
||||
<button nz-button nzType="primary" nzDanger (click)="deleteImage(i)">Delete</button>
|
||||
</nz-list-item>
|
||||
</nz-list>
|
||||
<app-my-upload [filterCriteria]="username"></app-my-upload>
|
||||
|
||||
@@ -3,12 +3,14 @@ import { CommonModule } from '@angular/common';
|
||||
import { RouterModule } from '@angular/router';
|
||||
import { SharedModule } from '../../shared/shared.module';
|
||||
import { MyUploadComponent } from './myUpload.component';
|
||||
import {UploadList} from "../uploadList/uploadList.module";
|
||||
// import {FormatTimePipe} from "../../../pipe/formatTime.pipe";
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
SharedModule,
|
||||
UploadList,
|
||||
RouterModule.forChild([
|
||||
{
|
||||
path: '',
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
|
||||
|
||||
<nz-row nzGutter="16">
|
||||
<nz-col nzSpan="8" *ngFor="let item of images;let index = index">
|
||||
<nz-card [nzHoverable]="true">
|
||||
<nz-card-meta [title]="item.picTitle"></nz-card-meta>
|
||||
<img *ngIf="item.type.startsWith('image')" [src]="item.url" alt="Uploaded Image" class="media"/>
|
||||
<video *ngIf="item.type.startsWith('video')" [src]="item.url" controls class="media"></video>
|
||||
<audio *ngIf="item.type.startsWith('audio')" [src]="item.url" controls class="media"></audio>
|
||||
<nz-card-meta [nzTitle]="'Upload by ' + username" [nzDescription]="item.uploadTime| date:'medium'"></nz-card-meta>
|
||||
|
||||
</nz-card>
|
||||
<button nz-button nzType="primary" [ngStyle]="{ marginRight: '8px' }" (click)="previewImage(item)">Preview</button>
|
||||
<button nz-button nzType="primary" [ngStyle]="{ marginRight: '8px' }" (click)="downloadImage(item.url)">Download</button>
|
||||
<button nz-button nzType="primary" nzDanger (click)="deleteImage(index)">Delete</button>
|
||||
</nz-col>
|
||||
</nz-row>
|
||||
@@ -0,0 +1,17 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { RouterModule } from '@angular/router';
|
||||
import { SharedModule } from '../../shared/shared.module';
|
||||
import { MyUploadComponent} from "./uploadList";
|
||||
|
||||
// import {FormatTimePipe} from "../../../pipe/formatTime.pipe";
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
SharedModule,
|
||||
],
|
||||
declarations: [MyUploadComponent],
|
||||
exports: [MyUploadComponent],
|
||||
})
|
||||
export class UploadList {}
|
||||
@@ -0,0 +1,17 @@
|
||||
.music-search {
|
||||
width: 500px;
|
||||
margin: 0 auto;
|
||||
display: block;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.nz-avatar {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
}
|
||||
|
||||
.media {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
object-fit: cover;
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
import { Component, OnInit, Input } from '@angular/core';
|
||||
import { NzMessageService } from 'ng-zorro-antd/message';
|
||||
import { ApiService } from 'src/app/services/api.service';
|
||||
import { HttpClient, HttpHeaders } from '@angular/common/http';
|
||||
import { lastValueFrom } from 'rxjs';
|
||||
import { NzModalService } from 'ng-zorro-antd/modal';
|
||||
|
||||
@Component({
|
||||
selector: 'app-my-upload',
|
||||
templateUrl: './uploadList.html',
|
||||
styleUrls: ['./uploadList.scss']
|
||||
})
|
||||
export class MyUploadComponent implements OnInit {
|
||||
@Input() filterCriteria: string | '' | undefined;
|
||||
|
||||
favoriteList: any = [];
|
||||
loading: boolean = true;
|
||||
username: any;
|
||||
images: {
|
||||
url: any,
|
||||
type: any,
|
||||
uploadTime: any,
|
||||
blobUrl: any,
|
||||
picTitle: any
|
||||
}[] = [];
|
||||
sasUrl = 'https://wecloudblob.blob.core.windows.net/games?si=all&sv=2022-11-02&sr=c&sig=YU%2BMkvJqCF8%2FKQkAH2orl8%2FVzHWhUpZydluUfHfFZK0%3D'
|
||||
containerName = 'games';
|
||||
|
||||
constructor(
|
||||
private apiService: ApiService,
|
||||
private $message: NzMessageService,
|
||||
private http: HttpClient,
|
||||
private modalService: NzModalService
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
console.log('filterCriteria!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!');
|
||||
console.log(this.filterCriteria);
|
||||
this.username = localStorage.getItem('username') as any;
|
||||
this.loadImagesFromAzure();
|
||||
}
|
||||
|
||||
private extractSasToken(): string {
|
||||
const sasToken = this.sasUrl.split('?')[1];
|
||||
return sasToken ? `?${sasToken}` : '';
|
||||
}
|
||||
|
||||
previewImage(image: { url: string, type: string }) {
|
||||
let content = '';
|
||||
if (image.type.startsWith('image')) {
|
||||
content = `<img src="${image.url}" alt="Image" style="width: 100%; max-height: 600px;">`;
|
||||
} else if (image.type.startsWith('video')) {
|
||||
content = `<video src="${image.url}" controls style="width: 100%; max-height: 600px;"></video>`;
|
||||
} else if (image.type.startsWith('audio')) {
|
||||
content = `<audio src="${image.url}" controls style="width: 100%; max-height: 600px;"></audio>`;
|
||||
} else {
|
||||
// Handle other types as needed
|
||||
}
|
||||
this.modalService.create({
|
||||
nzTitle: 'Preview',
|
||||
nzContent: content,
|
||||
nzFooter: null,
|
||||
nzWidth: '80%'
|
||||
});
|
||||
}
|
||||
|
||||
downloadImage(url: string) {
|
||||
window.location.href = url;
|
||||
}
|
||||
|
||||
async deleteImage(index: number) {
|
||||
const image = this.images[index];
|
||||
const blobUrl = image.blobUrl;
|
||||
|
||||
const headers = new HttpHeaders({
|
||||
'x-ms-date': new Date().toUTCString(),
|
||||
'x-ms-version': '2022-11-02'
|
||||
});
|
||||
|
||||
try {
|
||||
await this.http.delete(blobUrl, { headers }).toPromise();
|
||||
this.images.splice(index, 1);
|
||||
} catch (error) {
|
||||
console.error('Failed to delete blob:', error);
|
||||
}
|
||||
}
|
||||
|
||||
async loadImagesFromAzure() {
|
||||
const containerUrl = 'https://wecloudblob.blob.core.windows.net/games';
|
||||
const query = '?restype=container&comp=list';
|
||||
const fullUrl = `${containerUrl}${query}`;
|
||||
let filter = this.filterCriteria
|
||||
if (this.filterCriteria==='username'){
|
||||
filter = this.username;
|
||||
}
|
||||
try {
|
||||
const response = await lastValueFrom(this.http.get(fullUrl, { responseType: 'text' }));
|
||||
const parser = new DOMParser();
|
||||
const xmlDoc = parser.parseFromString(response, 'text/xml');
|
||||
const blobs = xmlDoc.getElementsByTagName('Blob');
|
||||
|
||||
this.images = [];
|
||||
for (let i = 0; i < blobs.length; i++) {
|
||||
const blob = blobs[i];
|
||||
const name = blob.getElementsByTagName('Name')[0].textContent || '';
|
||||
const lastModified = blob.getElementsByTagName('Last-Modified')[0].textContent || '';
|
||||
|
||||
if (!name || (filter && !name.includes(filter))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const url = blob.getElementsByTagName('Url')[0].textContent || '';
|
||||
const blobUrl = `${containerUrl}/${name}${this.extractSasToken()}`;
|
||||
const picTitle = name.split('_')[0].replace(`${this.containerName}/`, '');
|
||||
|
||||
this.images.push({
|
||||
url: url,
|
||||
type: 'image/jpeg',
|
||||
uploadTime: lastModified,
|
||||
blobUrl: blobUrl,
|
||||
picTitle: picTitle
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error fetching blobs:', error);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,22 +21,6 @@
|
||||
border-bottom: 1px dashed var(--ant-primary-color);
|
||||
"
|
||||
></h2>
|
||||
<button
|
||||
nz-button
|
||||
class="login-form-button login-form-margin"
|
||||
[nzType]="'primary'"
|
||||
(click)="onEdit()"
|
||||
>
|
||||
Edit Game Detail
|
||||
</button>
|
||||
<button
|
||||
nz-button
|
||||
nzType="default"
|
||||
style="margin: 20px 0 20px 10px"
|
||||
(click)="onDelete()"
|
||||
>
|
||||
Delete game
|
||||
</button>
|
||||
<button
|
||||
nz-button
|
||||
nzType="dashed"
|
||||
@@ -78,38 +62,8 @@
|
||||
<ng-template #elseBlock>
|
||||
<nz-empty nzNotFoundImage="simple"></nz-empty>
|
||||
</ng-template>
|
||||
<nz-modal
|
||||
[(nzVisible)]="isVisible"
|
||||
nzTitle="Edit Wegame"
|
||||
nzOkText="Ok"
|
||||
nzCancelText="Cancel"
|
||||
(nzOnOk)="submitForm()"
|
||||
(nzOnCancel)="isVisible = false"
|
||||
>
|
||||
<ng-container *nzModalContent>
|
||||
<form nz-form [formGroup]="editForm">
|
||||
<div nz-row [nzGutter]="24">
|
||||
<div nz-col [nzSpan]="24" *ngFor="let field of editField">
|
||||
<nz-form-item>
|
||||
<nz-form-label [nzSm]="6" [nzXs]="24" nzRequired nzFor="name"
|
||||
>{{ field.label }}
|
||||
</nz-form-label>
|
||||
<nz-form-control
|
||||
[nzSm]="14"
|
||||
[nzXs]="24"
|
||||
[nzErrorTip]="'Please input ' + field.label!"
|
||||
>
|
||||
<input
|
||||
nz-input
|
||||
[formControlName]="field.value"
|
||||
id="name"
|
||||
[placeholder]="field.label"
|
||||
/>
|
||||
</nz-form-control>
|
||||
</nz-form-item>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</ng-container>
|
||||
</nz-modal>
|
||||
<h2 style="margin-top: 40px; border-bottom: 1px solid #eee">Shared medias</h2>
|
||||
<app-my-upload [filterCriteria]="movieInfo.Name"></app-my-upload>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ export class MovieDetailsComponent implements OnInit {
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.movieInfo = JSON.parse(localStorage.getItem('movieInfo') as any);
|
||||
this.movieInfo = JSON.parse(localStorage.getItem('wegameInfo') as any);
|
||||
this.editForm = this.fb.group({
|
||||
Name: [null, [Validators.required]],
|
||||
Distributor: [null, [Validators.required]],
|
||||
@@ -69,19 +69,6 @@ export class MovieDetailsComponent implements OnInit {
|
||||
});
|
||||
}
|
||||
|
||||
onDelete() {
|
||||
this.apiService.post('del_game', { _id: this.movieInfo._id }).subscribe(
|
||||
(res: any) => {
|
||||
this.$message.success(`Delete ${this.movieInfo.Name} Success!`);
|
||||
this.router.navigate(['/layout/list'], {});
|
||||
},
|
||||
() => {}
|
||||
);
|
||||
}
|
||||
onEdit() {
|
||||
this.isVisible = true;
|
||||
this.editForm.patchValue(this.movieInfo);
|
||||
}
|
||||
submitForm(): void {
|
||||
if (this.editForm.valid) {
|
||||
let params = { ...this.editForm.value, _id: this.movieInfo._id };
|
||||
|
||||
@@ -3,12 +3,13 @@ import { CommonModule } from '@angular/common';
|
||||
import { RouterModule } from '@angular/router';
|
||||
import { SharedModule } from '../../shared/shared.module';
|
||||
import { MovieDetailsComponent } from './wegame-details.component';
|
||||
// import {FormatTimePipe} from "../../../pipe/formatTime.pipe";
|
||||
import {UploadList} from "../uploadList/uploadList.module";
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
SharedModule,
|
||||
UploadList,
|
||||
RouterModule.forChild([
|
||||
{
|
||||
path: '',
|
||||
|
||||
Reference in New Issue
Block a user