Commit edac7bc6 authored by kingweight's avatar kingweight

更新视频上传组件

parent a97b98b7
...@@ -3858,6 +3858,11 @@ ...@@ -3858,6 +3858,11 @@
"randomfill": "^1.0.4" "randomfill": "^1.0.4"
} }
}, },
"crypto-js": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz",
"integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q=="
},
"css": { "css": {
"version": "2.2.4", "version": "2.2.4",
"resolved": "https://registry.npmjs.org/css/-/css-2.2.4.tgz", "resolved": "https://registry.npmjs.org/css/-/css-2.2.4.tgz",
......
...@@ -36,7 +36,8 @@ ...@@ -36,7 +36,8 @@
"spark-md5": "^3.0.0", "spark-md5": "^3.0.0",
"tslib": "^1.10.0", "tslib": "^1.10.0",
"zone.js": "~0.10.2", "zone.js": "~0.10.2",
"querystring": "^0.2.0" "querystring": "^0.2.0",
"crypto-js": "^4.1.1"
}, },
"devDependencies": { "devDependencies": {
"@angular-devkit/build-angular": "~0.900.3", "@angular-devkit/build-angular": "~0.900.3",
......
...@@ -25,7 +25,7 @@ export class UploadImageWithPreviewComponent implements OnDestroy, OnChanges { ...@@ -25,7 +25,7 @@ export class UploadImageWithPreviewComponent implements OnDestroy, OnChanges {
@Input() @Input()
iconSize = 2; iconSize = 2;
@Input() @Input()
TIP = '点击上传图片'; TIP = '点击上传视频封面';
@Input() @Input()
disableUpload = false; disableUpload = false;
......
<div class="p-video-box"> <div class="p-video-box">
<div class="up-video" style="display: flex;"> <div class="up-video" style="display: flex;">
<!--<nz-upload class="" [nzDisabled]="!showUploadBtn"--> <nz-upload class="p-image-uploader" [nzDisabled]="uploading" [nzShowUploadList]="false" nzAccept=".mp4"
<!--[nzShowUploadList]="false"--> (nzChange)="handleChange($event)" [nzCustomRequest]="customReq"
<!--[nzBeforeUpload]="beforeUpload"--> style="margin-right: 1rem">
<!--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" <button type="button" nz-button nzType="default" *ngIf="showUploadBtn" [disabled]="uploading"
[nzLoading]="uploading" > [nzLoading]="uploading">
<i nz-icon nzType="plus" nzTheme="outline"></i> <i nz-icon nzType="plus" nzTheme="outline"></i>
<span>{{ uploading ? 'Uploading' : 'Select Video' }}</span> <span>{{ uploading ? 'Uploading' : 'Select Video' }}</span>
<!--<span>Select Video</span>-->
</button> </button>
</nz-upload> </nz-upload>
<!--<button nz-button nzType="primary" *ngIf="showUploadBtn"--> <button type="button" nz-button nzType="default" *ngIf="showCapture && !uploading" [disabled]="uploading"
<!--style="margin-right: 1rem"--> (click)="captureVideo()"
<!--type="button"--> [nzLoading]="uploading">
<!--[nzLoading]="uploading"--> <i nz-icon nzType="camera" nzTheme="outline"></i>
<!--(click)="handleUpload()"--> <span>截图</span>
<!--[disabled]="!videoFile || uploading">--> </button>
<!--<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">
<div class="p-upload-icon" *ngIf="!showUploadBtn && !videoUrl && !uploading">
<i nz-icon nzType="upload" nzTheme="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>
<div class="p-upload-progress-bg dddd " *ngIf="uploading" <div class="p-box d-flex align-items-center p-video-uploader">
[ngClass]="{'smart-bar': showUploadBtn}" > <div class="p-upload-icon" *ngIf="!showUploadBtn && !videoUrl && !uploading">
<div class="i-bg" [style.width]="progress+'%'"></div> <i nz-icon nzType="upload" nzTheme="outline"></i>
<div class="i-text"> <div class="m-3"></div>
<fa-icon icon="cloud-upload-alt"></fa-icon> <span>Click here to upload video</span>
Uploading... <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>
</div> <div class="p-upload-progress-bg" *ngIf="uploading" [ngClass]="{'smart-bar': showUploadBtn}">
<div class="p-upload-check-bg" *ngIf="checking"> <div class="i-bg" [style.width]="progress+'%'"></div>
<div class="i-bg" [style.width]="progress+'%'"></div> <div class="i-text">
<div class="i-text"> <fa-icon icon="cloud-upload-alt"></fa-icon>
<fa-icon icon="cloud-upload-alt"></fa-icon> Uploading...
<i nz-icon nzType="loading" nzTheme="outline"></i>Checking... </div>
</div>
<div class="p-upload-check-bg" *ngIf="checking">
<div class="i-bg" [style.width]="progress+'%'"></div>
<div class="i-text">
<fa-icon icon="cloud-upload-alt"></fa-icon>
<i nz-icon nzType="loading" nzTheme="outline"></i>Checking...
</div>
</div>
<div class="p-preview" *ngIf="!uploading&&playVedioUrl">
<video crossorigin="anonymous" [src]="playVedioUrl" controls #videoNode></video>
<!-- <video crossorigin="anonymous" [src]="safeVideoUrl()" controls #videoNode></video> -->
</div> </div>
</div> </div>
<div class="p-preview" *ngIf="!uploading && videoUrl " > <div [style.display]="!checkVideoExists?'none':''">
<!--<video crossorigin="anonymous" [src]="videoUrl" controls #videoNode></video>--> <span><i nz-icon nzType="loading" nzTheme="outline"></i> checking file to upload</span>
<video [src]="safeVideoUrl(videoUrl)" controls #videoNode></video>
</div> </div>
</div> </div>
<div [style.display]="!checkVideoExists?'none':''">
<span><i nz-icon nzType="loading" nzTheme="outline"></i> checking file to upload</span>
</div>
</div>
import { ApplicationRef, ChangeDetectorRef, ElementRef, ViewChild } from "@angular/core";
export class ComponentBase {
// 储存数据用
saveKey = "";
// 储存对象
item: any = {};
ngOnChanges() { }
ngOnDestroy() { }
constructor(public appRef: ApplicationRef, public changeDetectorRef: ChangeDetectorRef) { }
ngOnInit() {
// 获取存储的数据
(<any>window).courseware.getData((data) => {
if (data) {
this.item = data;
this.itemStr = JSON.stringify(this.item, null, 4).trim();
}
this.init();
this.changeDetectorRef.markForCheck();
this.changeDetectorRef.detectChanges();
this.refresh();
}, this.saveKey);
}
@ViewChild("itemTextarea", { static: true }) itemTextarea: ElementRef;
copyData() {
this.itemTextarea.nativeElement.select();
document.execCommand("copy");
}
/**
* 储存图片数据
* @param e
*/
// onAssetUploadSuccess(e: any, ...key: Array<string>) {
// let item = this.item;
// for (let i = 0; i < key.length; i++) {
// if (i + 1 == key.length) {
// if (key[i] == "audio") {
// let sp = e.url.split(".mp3");
// let u = sp[0] + "_l.mp3";
// item[key[i]] = u;
// item["audioName"] = e.name || "";
// } else {
// item[key[i]] = e.url;
// }
// this.save();
// return;
// }
// item = item[key[i]];
// }
// }
/**
* 储存图片数据
* @param e
*/
onImageUploadSuccess(e, key, it = this.item) {
it[key] = e.url;
this.save();
}
/**
* 储存音频数据
* @param e
*/
onAudioUploadSuccess(e, key, it = this.item) {
let url = e.url;
let sp = url.split(".mp3");
let u = sp[0] + "_l.mp3";
it[key] = u;
it["audioName"] = e.name || "";
this.save();
}
save() {
(<any>window).courseware.setData(this.item, null, this.saveKey);
this.itemStr = JSON.stringify(this.item, null, 4).trim();
this.refresh();
console.log('this.item = ' + JSON.stringify(this.item));
}
itemStr = "";
load() {
this.itemStr = this.itemTextarea.nativeElement.value;
if (this.isJSON(this.itemStr)) {
this.item = JSON.parse(this.itemStr);
this.init();
this.changeDetectorRef.markForCheck();
this.changeDetectorRef.detectChanges();
this.refresh();
}
}
isJSON(str) {
if (typeof str == 'string') {
try {
var obj = JSON.parse(str);
if (typeof obj == 'object' && obj) {
return true;
}
return false;
} catch (e) {
return false;
}
}
return false;
}
/**
* 刷新 渲染页面
*/
refresh() {
setTimeout(() => {
this.appRef.tick();
}, 1);
}
init() { }
}
...@@ -4,5 +4,74 @@ ...@@ -4,5 +4,74 @@
.model-content { .model-content {
width: 100%; width: 100%;
height: 100%; height: 100%;
}
.radioPaire {
float: left;
margin: 3px;
border-style: dashed;
border-color: #000;
border-width: 1px;
}
.border {
border-radius: 20px;
border-style: dashed;
padding: 20px;
margin: 20px;
/*width: 500px; */
/*//border-radius: 20px;*/
/*//border-width: 2px;*/
/*//border-color: #000000;*/
}
.border-lite {
border: 2px dashed #ddd;
border-radius: 0.5rem;
padding: 10px;
margin: 10px;
}
.button-right {
border-style: dotted;
border-color: rgb(42, 142, 72);
width: 100px;
height: 50px;
color: rgb(42, 142, 72);
background-color: white;
}
.button-wrong {
border-style: dotted;
border-color: rgb(180, 0, 0);
width: 100px;
height: 50px;
color: rgb(180, 0, 0);
background-color: white;
}
.button-disable {
border-style: dotted;
border-color: black;
width: 100px;
height: 50px;
color: black;
background-color: white;
}
#drop-area {
border: 2px dashed #ccc;
padding: 20px;
text-align: center;
font-family: Arial, sans-serif;
font-size: 16px;
cursor: pointer;
}
#drop-area:hover {
background-color: #f0f0f0;
}
#drop-area:active {
background-color: #e0e0e0;
} }
...@@ -10,39 +10,35 @@ ...@@ -10,39 +10,35 @@
<textarea nz-input [(ngModel)]="item.video_title" (blur)="save()" style=" width: 350px; min-height: 60px;"></textarea> <textarea nz-input [(ngModel)]="item.video_title" (blur)="save()" style=" width: 350px; min-height: 60px;"></textarea>
</div> </div>
<div style="margin: 30px; display: flex; align-items: center; "> <div class="model-content">
<h3>上传视频封面:</h3>
<div style="margin-top: 10px; display: flex; "> <div
<div style="width: 300px;"> id="drop-area"
<app-upload-image-with-preview [picUrl]="item.video_cover_pic" (dragover)="onDragOver($event)"
(imageUploaded)="onItemImgUploadSuccess($event, item, 'video_cover_pic')"> (drop)="onDrop($event)"
</app-upload-image-with-preview> >
</div> 拖拽文件到此区域进行批量上传
<button *ngIf="item.video_cover_pic" style="float: right;" nz-button nzType="" (click)="onDeletaImg(item, 'video_cover_pic')">
x
</button>
</div> </div>
</div> <div style="width: 100%; display: inline-flex;flex-direction: row;flex-wrap: wrap;">
<div style="width: 30% ; margin-left: 2.5%; min-width: 300px;margin-top: 20px;border: 2px dashed;padding: 20px 10px 5px 10px;"
<div style="margin: 30px; display: flex; align-items: center; "> align='center' *ngFor="let data of item.dataArr;index as i">
<h3 style="">上传视频:</h3> <div style="width: 100px; background-color: white; transform: translateY(-30px);" > 视频 {{i+1}}</div>
<div style="display: flex; margin: 30px; width: 1000px"> <div style="width: 80%;" align='center'>
<input type="text" nz-input [(ngModel)]="data.title" (blur)="save()">
<div>
<div style="width: 300px; border: 1px #ccc solid; border-radius: 15px; padding: 15px; background-color: rgb(255, 250, 243);">
<app-upload-video
[videoUrl]="item.video_url"
(videoUploaded)="onVideoUploaded($event)"
>
</app-upload-video>
</div> </div>
<br>
<app-upload-image-with-preview style="width: 80%;" [picUrl]="data.cover" (imageUploaded)="onImageUploadSuccess_new($event,i)">
</app-upload-image-with-preview>
<app-upload-video [videoUrl]="data.video_url.replace('.mp4', '_h.mp4')" [showUploadBtn]="true" (videoUploaded)="onVideoUploaded_new($event,i)" (onCapture)="handleOnCapture($event,i)" (parentRef)="onParentRef($event,i)"></app-upload-video>
<div style="padding: 5px 0;" align='center'>
<input type="text" nz-input [(ngModel)]="data.video_url" (blur)="save()">
</div>
<button style="margin-top: 10px; " (click)="onRemoveData(i)">删除</button>
</div> </div>
<button style="width: 30% ;min-width: 300px;height: 170px; margin-top: 20px; margin-left:2.5%;"
(click)="onAddData()">添加视频</button>
</div> </div>
</div> </div>
...@@ -83,6 +79,11 @@ ...@@ -83,6 +79,11 @@
(总计: {{ques.time | time}}) (总计: {{ques.time | time}})
</span> </span>
</div> </div>
<div style="display: flex; align-items: center; gap: 5px;">
<button style="float: right; margin-right: -140px;" nz-button (click)="setFragment(ques,i)">
去设置
</button>
</div>
</div> </div>
<button style="float: right; margin-right: -140px;" nz-button nzType="danger" (click)="deletePageBtnClick(i)"> <button style="float: right; margin-right: -140px;" nz-button nzType="danger" (click)="deletePageBtnClick(i)">
删除节点 删除节点
...@@ -306,6 +307,84 @@ ...@@ -306,6 +307,84 @@
</div> </div>
<!-- 视频片段选择弹窗 -->
<nz-modal
[(nzVisible)]="isFragmentModalVisible"
nzTitle="选择视频时间节点"
(nzOnCancel)="fragmentModalCancel()"
[nzFooter]="null"
[nzWidth]="900">
<!-- 视频播放器 -->
<div style="padding: 20px;">
<div style="margin-bottom: 20px; text-align: center;">
<video
id="fragment-video"
[src]="fragmentVideoUrl"
controls
style="width: 100%; max-width: 800px;"
(loadedmetadata)="onFragmentVideoLoadedMetadata($event)"
(timeupdate)="onFragmentVideoTimeUpdate($event)">
您的浏览器不支持视频播放
</video>
</div>
<!-- 视频帧轨道 -->
<div style="margin-bottom: 20px;">
<div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px;">
<span>当前时间: {{formatTime(fragmentSelectedTime)}}</span>
<span>总时长: {{formatTime(fragmentVideoDuration)}}</span>
</div>
<!-- 视频帧缩略图轨道 -->
<div *ngIf="videoThumbnails.length > 0" style="margin-bottom: 15px;">
<div style="display: flex; overflow-x: auto; padding: 10px 0; border: 1px solid #e8e8e8; border-radius: 4px; background-color: #fafafa;">
<div
*ngFor="let thumbnail of videoThumbnails; let i = index"
(click)="onThumbnailClick(getThumbnailTime(i))"
[style.border]="isThumbnailActive(i) ? '2px solid #1890ff' : '2px solid transparent'"
style="flex-shrink: 0; margin: 0 2px; cursor: pointer; border-radius: 2px; transition: border 0.2s;">
<img
[src]="thumbnail"
[alt]="'帧 ' + (i + 1)"
style="width: 80px; height: 45px; object-fit: cover; display: block; border-radius: 2px;">
<div style="font-size: 10px; text-align: center; color: #666; margin-top: 2px;">
{{formatTime(getThumbnailTime(i))}}
</div>
</div>
</div>
</div>
<!-- 生成缩略图提示 -->
<div *ngIf="isGeneratingThumbnails" style="text-align: center; padding: 10px; color: #999;">
<i nz-icon nzType="loading" nzTheme="outline"></i>
正在生成视频帧缩略图...
</div>
<!-- 时间滑块 -->
<nz-slider
[nzMin]="0"
[nzMax]="fragmentVideoDuration"
[nzStep]="0.1"
[ngModel]="fragmentSelectedTime"
(ngModelChange)="onFragmentTrackChange($event)"
(nzOnBeforeChange)="onFragmentTrackStart()"
(nzOnAfterChange)="onFragmentTrackEnd()"
[nzTooltipVisible]="'always'">
</nz-slider>
</div>
<!-- 确认和取消按钮 -->
<div style="text-align: right; margin-top: 30px;">
<button nz-button nzType="default" (click)="fragmentModalCancel()" style="margin-right: 10px;">
取消
</button>
<button nz-button nzType="primary" (click)="fragmentModalOk()">
确认节点
</button>
</div>
</div>
</nz-modal>
......
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment