diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 7d45083fa9f56ca0b7c36c61b76da8b6e143eac4..efe0b91ed11986671d62c62ab3d505eb5fd9d226 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -21,6 +21,9 @@ import { registerLocaleData } from '@angular/common'; import zh from '@angular/common/locales/zh'; import {UploadImageWithPreviewComponent} from "./common/upload-image-with-preview/upload-image-with-preview.component"; import {BackgroundImagePipe} from "./pipes/background-image.pipe"; +import {UploadVideoComponent} from "./common/upload-video/upload-video.component"; +import {ResourcePipe} from "./pipes/resource.pipe"; +import {TimePipe} from "./pipes/time.pipe"; registerLocaleData(zh); @NgModule({ @@ -32,6 +35,9 @@ registerLocaleData(zh); AudioRecorderComponent, UploadImageWithPreviewComponent, BackgroundImagePipe, + ResourcePipe, + TimePipe, + UploadVideoComponent, PlayerContentWrapperComponent ], diff --git a/src/app/common/upload-video/upload-video.component.html b/src/app/common/upload-video/upload-video.component.html new file mode 100644 index 0000000000000000000000000000000000000000..e39116aaf958867cde66a3076e2b11538a86ab77 --- /dev/null +++ b/src/app/common/upload-video/upload-video.component.html @@ -0,0 +1,95 @@ + +<div class="up-video" style="display: flex;"> + <!--<nz-upload class="" [nzDisabled]="!showUploadBtn"--> + <!--[nzShowUploadList]="false"--> + <!--[nzBeforeUpload]="beforeUpload"--> + <!--nzAccept = ".mp4"--> + <!--style="margin-right: 1rem">--> + + <nz-upload class="p-image-uploader" [nzDisabled]="uploading" + [nzShowUploadList]="false" + nzAccept = ".mp4" + [nzAction]="uploadUrl" + [nzData]="uploadData" + (nzChange)="handleChange($event)" + style="margin-right: 1rem"> + + <button type="button" nz-button nzType="default" *ngIf="showUploadBtn" [disabled]="uploading" + [nzLoading]="uploading" > + <i nz-icon type="plus" theme="outline"></i> + + <span>{{ uploading ? 'Uploading' : 'Select Video' }}</span> + <!--<span>Select Video</span>--> + </button> + + + + + </nz-upload> + <!--<button nz-button nzType="primary" *ngIf="showUploadBtn"--> + <!--style="margin-right: 1rem"--> + <!--type="button"--> + <!--[nzLoading]="uploading"--> + <!--(click)="handleUpload()"--> + <!--[disabled]="!videoFile || uploading">--> + <!--<i nz-icon type="upload" theme="outline"></i>--> + <!--<span>{{ uploading ? 'Uploading' : 'Start Upload' }}</span>--> + <!--</button>--> + + <!-- + <button type="button" + (click)="extraCover()" + nz-button nzType="default" + [disabled]=" !(videoUrl && videoUrl.constructor.name === 'String')">设置å°é¢</button> + --> + <!-- + <nz-divider></nz-divider> + + <div [style.display]="!uploading?'none':''" style="position:relative"> + <div style="width: calc( 100% - 20px);"> + <nz-progress [nzPercent]="progress"> + </nz-progress> + </div> + <i (click)="cancelUpload()" class="anticon anticon-close-circle" style="position: absolute;top: 50%;right: 0;transform: translateY(-50%);cursor: pointer"></i> + </div> + --> + + +</div> +<div class="p-box d-flex align-items-center p-video-uploader" style="top: 20px;"> + <div class="p-upload-icon" *ngIf="!showUploadBtn && !videoUrl && !uploading"> + <i nz-icon type="upload" theme="outline"></i> + <div class="m-3"></div> + <span>Click here to upload video</span> + <div class="mt-5 p-progress-bar" *ngIf="uploading"> + <div class="p-progress-bg" [style.width]="progress*0.2+'rem'"></div> + <div class="p-progress-value">{{progress}}%</div> + </div> + </div> + <div class="p-upload-progress-bg dddd " *ngIf="uploading" + [ngClass]="{'smart-bar': showUploadBtn}" > + <div class="i-bg" [style.width]="progress+'%'"></div> + <div class="i-text"> + <fa name="cloud-upload"></fa> + Uploading... + </div> + </div> + <div class="p-upload-check-bg" *ngIf="checking"> + <div class="i-bg" [style.width]="progress+'%'"></div> + <div class="i-text"> + <fa name="cloud-upload"></fa> + <i nz-icon type="loading" theme="outline"></i>Checking... + </div> + </div> + <div class="p-preview" *ngIf="!showUploadBtn && !uploading && videoUrl " > + <!--<video crossorigin="anonymous" [src]="videoUrl" controls #videoNode></video>--> + <video [src]="safeVideoUrl(videoUrl)" controls #videoNode (loadedmetadata)="videoLoadedMetaData()"></video> + </div> +</div> +<div [style.display]="!checkVideoExists?'none':''"> + <span><i nz-icon type="loading" theme="outline"></i> checking file to upload</span> +</div> + + + + diff --git a/src/app/common/upload-video/upload-video.component.scss b/src/app/common/upload-video/upload-video.component.scss new file mode 100644 index 0000000000000000000000000000000000000000..b6664069c2a89349f8338503aaeefde1aa7e4c0e --- /dev/null +++ b/src/app/common/upload-video/upload-video.component.scss @@ -0,0 +1,130 @@ +@import '../../style/common_mixin'; + +.p-video-uploader { + position: relative; + display: block; + width: 100%; + height: 0; + padding-bottom: 56.25%; + .p-box { + position: absolute; + left: 0; + top: 0; + right: 0; + bottom: 0; + border: 2px dashed #ddd; + //border-radius: 0.5rem; + background-color: #fafafa; + text-align: center; + color: #aaa; + .p-upload-icon { + text-align: center; + margin: auto; + .anticon-upload { + color: #888; + font-size: 5rem; + } + .p-progress-bar { + position: relative; + width: 20rem; + height: 1.5rem; + border: 1px solid #ccc; + border-radius: 1rem; + .p-progress-bg { + background-color: #1890ff; + border-radius: 1rem; + height: 100%; + } + .p-progress-value { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + text-shadow: 0 0 4px #000; + color: white; + text-align: center; + font-size: 0.9rem; + line-height: 1.5rem; + } + } + } + .p-preview { + width: 100%; + height: 100%; + //background-image: url("https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png"); + video{ + max-height: 100%; + max-width: 100%; + } + } + } +} + +.p-btn-delete { + position: absolute; + right: -0.5rem; + top: -0.5rem; + width: 2rem; + height: 2rem; + border: 0.2rem solid white; + border-radius: 50%; + font-size: 1.2rem; + background-color: #fb781a; + color: white; + text-align: center; +} + +.p-upload-progress-bg, .p-upload-check-bg { + position: absolute; + width: 100%; + height: 100%; + display: flex; + align-items: center; + justify-content: center; + & .i-text { + position: absolute; + text-align: center; + color: white; + text-shadow: 0 0 2px rgba(0, 0, 0, .85); + } + & .i-bg { + position: absolute; + left: 0; + top: 0; + height: 100%; + background-color: #1890ff; + //border-radius: 0.5rem 0 0 0.5rem; + } +} +.p-upload-progress-bg.smart-bar{ + height: 50px!important; + margin-top: 20px!important; +} +.p-upload-check-bg { + position: absolute; + width: 100%; + height: 100%; + display: flex; + align-items: center; + justify-content: center; + & .i-text { + position: absolute; + text-align: center; + color: white; + text-shadow: 0 0 2px rgba(0, 0, 0, .85); + } + & .i-bg { + position: absolute; + left: 0; + top: 0; + height: 100%; + background-color: #17a2b8; + //border-radius: 0.5rem 0 0 0.5rem; + } +} + + +//:host ::ng-deep .ant-upload { +// display: block; +//} diff --git a/src/app/common/upload-video/upload-video.component.ts b/src/app/common/upload-video/upload-video.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..b947e15cf3ca045f10becd3be01a73e36d8f6c93 --- /dev/null +++ b/src/app/common/upload-video/upload-video.component.ts @@ -0,0 +1,201 @@ +import {Component, ElementRef, EventEmitter, Input, OnChanges, OnDestroy, Output, SecurityContext, ViewChild} from '@angular/core'; +import {NzMessageService, UploadFile} from 'ng-zorro-antd'; +import {DomSanitizer, SafeResourceUrl} from '@angular/platform-browser'; + + +@Component({ + selector: 'app-upload-video', + templateUrl: './upload-video.component.html', + styleUrls: ['./upload-video.component.scss'] +}) +export class UploadVideoComponent implements OnChanges, OnDestroy { + uploading = false; + checking = false; + checkVideoExists = false; + uploadClicked = false; + + @Input() + videoFile = null; + uploaderInst = null; + + progress = 0; + // @Input() + // setCovering = false; + + @Input() + videoUrl = ''; + @ViewChild('videoNode') + videoNode: ElementRef; + + + + + // @Input() + // extractCoverFunc = null; + + @Output() + videoUploaded = new EventEmitter(); + @Output() + videoUploadFailure = new EventEmitter(); + @Output() + extractVideoCover = new EventEmitter(); + + + @Input() + showUploadBtn = true; + @Input() + item: any; + // videoItem = null; + + uploadUrl = (<any> window).courseware.uploadUrl(); + uploadData = (<any> window).courseware.uploadData(); + + constructor(private nzMessageService: NzMessageService, + private sanitization: DomSanitizer + // private cwService: _coursewareService, + // private resService: ResourceService + ) { + this.uploading = false; + this.videoFile = null; + + } + ngOnChanges() { + // if (!this.videoFile || this.showUploadBtn) { + // return; + // } + // this.beforeUpload(this.videoFile); + // this.handleUpload(); + } + ngOnDestroy(): void { + URL.revokeObjectURL(this.videoUrl); + } + + safeVideoUrl(url) { + console.log(url) + return this.sanitization.bypassSecurityTrustResourceUrl(url); // `${url}`; + } + videoLoadedMetaData() { + + } + + + handleChange(info: { type: string, file: UploadFile, event: any }): void { + + console.log('info:' , info); + + switch (info.type) { + case 'start': + + // this.beforeUpload(info.file); + + + if (!this.checkSelectFile(info.file)) { + return; + } + + + this.uploading = true; + this.progress = 0; + + break; + + case 'success': + + + this.uploadSuccess(info.file); + + // this.beforeUpload(info.file); + // this.uploadSuccess(info.file); + + break; + case 'progress': + this.progress = parseInt(info.event.percent, 10); + this.doProgress(this.progress); + break; + } + } + + + checkSelectFile(file) { + if (!file.lastModified) { + return false; + } + const delta = 500; + const isOverSize = (file.size / 1024 / 1024) < delta; + if (!isOverSize) { + this.nzMessageService.error(`video must smaller than ${delta}MB!`); + return false; + } + return true; + } + checkHashFinish = (hash) => { + this.checking = false; + this.uploading = true; + } + + uploadSuccess = (file) => { + + this.nzMessageService.info('Upload Success'); + // this.updateStatus(false); + this.uploading = false; + this.videoFile = null; + // this.updateSource(url); + this.videoUrl = file.response.url; + // console.log(this.picUrl) + // this.uploadFinished(url); + // if (!inOSS) { + const vid = document.createElement('video'); + vid.addEventListener('loadedmetadata', () => { + const height = vid.videoHeight; + const width = vid.videoWidth; + let duration = vid.duration; + if (duration) { + duration = duration * 1000; + } + file['height'] = height; + file['width'] = width; + file['duration'] = duration; + vid.preload = 'none'; + vid.src = ''; + vid.remove(); + + this.videoUploaded.emit(file.response); + + // this.resService.updateVideo(id, {width, height, duration}).then( () => { + // this.videoUploaded.emit({res_id: id, id, name, hash, url}); + // }); + + }); + vid.src = file.response.url; + // } else { + // this.videoUploaded.emit(file.response); + // } + + + + } + + + + uploadFailure = (err, file) => { + this.uploading = false; + if (err.name && err.name === 'cancel') { + return; + } + console.log(err); + this.nzMessageService.error('Upload Error ' + err.message); + this.videoUploadFailure.emit(file); + } + + doProgress = (p) => { + if (p > 1) { + p = 1; + } + if (p < 0) { + p = 0; + } + // console.log(Math.floor(p * 100)); + this.progress = Math.floor(p * 100); + } +} + diff --git a/src/app/pipes/resource.pipe.ts b/src/app/pipes/resource.pipe.ts new file mode 100644 index 0000000000000000000000000000000000000000..d7f5f8df579c3d7981a2556ef149990a9d6ba50b --- /dev/null +++ b/src/app/pipes/resource.pipe.ts @@ -0,0 +1,28 @@ +import {Pipe, PipeTransform} from '@angular/core'; +// import {ElectronUtil} from '../util/ElectronUtil'; + +declare const APP_PATH; +// const isInElectron = localStorage.getItem('electron'); + +@Pipe({ + name: 'resource' +}) +export class ResourcePipe implements PipeTransform { + + transform(pic_url: any, coursewareSid?: string): string { + // return pic_url; + if (pic_url && typeof pic_url === 'object') { + return pic_url; + } + // console.log('resource', pic_url) + return `${pic_url}?t=${Math.random()}`; + + // let ret = value ? `/api/resource/${value}/file` : ''; + // let ret = res_id ? `/resource/audio/${res_id}` : ''; + // if (isInElectron && ElectronUtil.hasCached(coursewareSid, res_id)) { + // ret = `file://${APP_PATH}/courseware_cache/${coursewareSid}/${res_id}`; + // } + // return ret; + } + +} diff --git a/src/app/pipes/time.pipe.ts b/src/app/pipes/time.pipe.ts new file mode 100644 index 0000000000000000000000000000000000000000..af84a0c697319c7f0c1f41115a113a6f4540d423 --- /dev/null +++ b/src/app/pipes/time.pipe.ts @@ -0,0 +1,19 @@ +import {Pipe, PipeTransform} from '@angular/core'; +import * as _ from 'lodash'; + +@Pipe({ + name: 'time' +}) +export class TimePipe implements PipeTransform { + + transform(value: any, args?: any): any { + let ret = '00:00'; + if (_.isNumber(value) && value > 0) { + const minutes = Math.floor(value / 60); + const seconds = Math.floor(value % 60); + ret = _.padStart(minutes, 2, '0') + ':' + _.padStart(seconds, 2, '0'); + } + return ret; + } + +} diff --git a/src/assets/default/video-not-upload.jpg b/src/assets/default/video-not-upload.jpg new file mode 100755 index 0000000000000000000000000000000000000000..31fbbcc7703958c0153d883aeded25e2a53e0542 Binary files /dev/null and b/src/assets/default/video-not-upload.jpg differ diff --git a/src/index.html b/src/index.html index df57d466a8da6efea2687c67f79ca8c22bc08f51..b77274e92ddffbd8103ae2b3f181ef27f6e95ba9 100644 --- a/src/index.html +++ b/src/index.html @@ -8,81 +8,81 @@ <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="icon" type="image/x-icon" href="favicon.ico"> - <script> + <script> - window.courseware = (function(){ - if(window.parent.net){ - window.net = window.parent.net; - var id = window.frameElement.getAttribute("dataid"); - return { - getData: function(callback){ - net.getData("getCoursewareData",id,function(res){ - if(res){ - res = JSON.parse(res); - if(res.msg==="success"){ - let callData = null; - if(res.data&&res.data!='null'){ - callData = JSON.parse(res.data); - } - callback&&callback(callData); - }else{ - alert('æ•°æ®åŠ è½½å¤±è´¥ï¼'); + window.courseware = (function(){ + if(window.parent.net){ + window.net = window.parent.net; + var id = window.frameElement.getAttribute("dataid"); + return { + getData: function(callback, key=''){ + net.getData("getCoursewareData",id,function(res){ + if(res){ + res = JSON.parse(res); + if(res.msg==="success"){ + let callData = null; + if(res.data&&res.data!='null'){ + callData = JSON.parse(res.data); } + callback&&callback(callData); }else{ alert('æ•°æ®åŠ è½½å¤±è´¥ï¼'); } - }); - }, - setData: function(data,callback){ - let str = JSON.stringify(data); - net.getData("setCoursewareData",{id: id,data: str},function(res){ - if(res){ - res = JSON.parse(res); - if(res.msg==="success"){ - callback&&callback(); - }else{ - alert('æ•°æ®ä¿å˜å¤±è´¥ï¼'); - } + }else{ + alert('æ•°æ®åŠ è½½å¤±è´¥ï¼'); + } + }); + }, + setData: function(data,callback, key=''){ + let str = JSON.stringify(data); + net.getData("setCoursewareData",{id: id,data: str},function(res){ + if(res){ + res = JSON.parse(res); + if(res.msg==="success"){ + callback&&callback(); }else{ alert('æ•°æ®ä¿å˜å¤±è´¥ï¼'); } - }); - }, - uploadUrl: function(){ - return net.getUploadFileURL(); - }, - uploadData: function(){ - return net.getAjaxData("uploadFile",""); - } - } - } else { - return { - getData: function(callback){ - let data = localStorage.getItem("courseware_data"); - if(data){ - data = JSON.parse(data); + }else{ + alert('æ•°æ®ä¿å˜å¤±è´¥ï¼'); } - callback&&callback(data); - }, - setData: function(data,callback){ - console.log("******local********"); - localStorage.setItem("courseware_data",JSON.stringify(data)); - callback&&callback(); - }, - uploadUrl: function(){ - return 'http://localhost:3000/api/test'; - }, - uploadData: function(){ - return {}; + }); + }, + uploadUrl: function(){ + return net.getUploadFileURL(); + }, + uploadData: function(){ + return net.getAjaxData("uploadFile",""); + } + } + } else { + return { + getData: function(callback, key=''){ + let data = localStorage.getItem("courseware_data_" + key); + if(data){ + data = JSON.parse(data); } + callback&&callback(data); + }, + setData: function(data,callback, key=''){ + console.log("******local********"); + localStorage.setItem("courseware_data_" + key,JSON.stringify(data)); + callback&&callback(); + }, + uploadUrl: function(){ + return 'http://localhost:3000/api/test'; + }, + uploadData: function(){ + return {}; } } - })(); - - - </script> + } + })(); + + + </script> </head> <body> - <app-root></app-root> -</body> +<app-root></app-root> +</body> </html> diff --git a/tslint.json b/tslint.json index f29eef2d3fc70cfde3cd319bc502e678604f4095..4fcb4b1d413f773db7ea534e2c9d6d29436a819a 100644 --- a/tslint.json +++ b/tslint.json @@ -89,7 +89,10 @@ "use-lifecycle-interface": true, "use-pipe-transform-interface": true, "no-trailing-whitespace": false, - "variable-name": false + "variable-name": false, + + "no-unused-expression": false, + "align": false }, "rulesDirectory": [ "codelyzer"