Commit 659aef97 authored by 李维's avatar 李维

optimize code

parent b4f85ada
...@@ -13,19 +13,19 @@ import { FormComponent } from './form/form.component'; ...@@ -13,19 +13,19 @@ import { FormComponent } from './form/form.component';
import { PlayComponent } from "./play/play.component"; import { PlayComponent } from "./play/play.component";
// import { ColorPickerModule } from 'ngx-color-picker'; // import { ColorPickerModule } from 'ngx-color-picker';
import { LessonTitleConfigComponent } from './common/lesson-title-config/lesson-title-config.component'; // import { LessonTitleConfigComponent } from './common/lesson-title-config/lesson-title-config.component';
import { AudioRecorderComponent } from './common/audio-recorder/audio-recorder.component'; import { AudioRecorderComponent } from './common/audio-recorder/audio-recorder.component';
import { PlayerContentWrapperComponent } from './common/player-content-wrapper/player-content-wrapper.component'; // import { PlayerContentWrapperComponent } from './common/player-content-wrapper/player-content-wrapper.component';
/** 配置 angular i18n **/ /** 配置 angular i18n **/
import { registerLocaleData } from '@angular/common'; import { registerLocaleData } from '@angular/common';
import zh from '@angular/common/locales/zh'; import zh from '@angular/common/locales/zh';
import {UploadImageWithPreviewComponent} from "./common/upload-image-with-preview/upload-image-with-preview.component"; import {UploadImageWithPreviewComponent} from "./common/upload-image-with-preview/upload-image-with-preview.component";
import {BackgroundImagePipe} from "./pipes/background-image.pipe"; import {BackgroundImagePipe} from "./pipes/background-image.pipe";
import {UploadVideoComponent} from "./common/upload-video/upload-video.component"; // import {UploadVideoComponent} from "./common/upload-video/upload-video.component";
import {ResourcePipe} from "./pipes/resource.pipe"; import {ResourcePipe} from "./pipes/resource.pipe";
import {TimePipe} from "./pipes/time.pipe"; import {TimePipe} from "./pipes/time.pipe";
import {CustomHotZoneComponent} from "./common/custom-hot-zone/custom-hot-zone.component"; // import {CustomHotZoneComponent} from "./common/custom-hot-zone/custom-hot-zone.component";
registerLocaleData(zh); registerLocaleData(zh);
@NgModule({ @NgModule({
...@@ -33,15 +33,11 @@ registerLocaleData(zh); ...@@ -33,15 +33,11 @@ registerLocaleData(zh);
AppComponent, AppComponent,
FormComponent, FormComponent,
PlayComponent, PlayComponent,
LessonTitleConfigComponent,
AudioRecorderComponent, AudioRecorderComponent,
UploadImageWithPreviewComponent,
BackgroundImagePipe, BackgroundImagePipe,
ResourcePipe, ResourcePipe,
TimePipe, TimePipe,
UploadVideoComponent, UploadImageWithPreviewComponent,
CustomHotZoneComponent,
PlayerContentWrapperComponent
], ],
imports: [ imports: [
FormsModule, FormsModule,
......
This diff is collapsed.
<div class="p-image-children-editor">
<div style="float: left; width: 25%;border-right: 2px solid #ddd; border-bottom: 2px solid #ddd;">
<h5 style="margin-left: 2.5%;">预览:</h5>
<div id="canvas-container" style="margin:5px;">
<div class="preview-box" #wrap style="margin-bottom: 20px">
<canvas id="canvas" #canvas></canvas>
</div>
</div>
<div style="text-align: center">
<button nz-button nzType="primary" [nzSize]="'small'" nzShape="round" (click)="saveClick()" [disabled]="!hotZoneChanged" >
<i nz-icon type="save"></i>
Save
</button>
</div>
<h5 style="border-top: 2px solid #ddd;">背景图:</h5>
<div style="width: 200px;margin: auto;">
<div class="bg-box">
<app-upload-image-with-preview
[picUrl]="bgItem.url"
(imageUploaded)="onBackgroundUploadSuccess($event)"
>
</app-upload-image-with-preview>
</div>
</div>
</div>
<div style="float: left; width: 75%">
<div nzAlign="middle">
<div class="img-box clearfix" *ngFor="let it of hotZoneArr; let i = index" style="border-bottom: 1px solid #DDD; padding-bottom: 10px;" >
<div style="float: left; height: 115px; position: relative; width: 250px; ">
<h3> 第-{{ i + 1 }}-题 </h3>
<div style="position: absolute; bottom: 0;">
<h5> 题目音频 </h5>
<app-audio-recorder [audioUrl]="it.audio_url ? it.audio_url : null" (audioUploaded)="onAudioUploadSuccess($event, it, false)" ></app-audio-recorder>
</div>
</div>
<div style="float: left; height: 115px; position: relative; width: 250px; ">
<div style="position: absolute; bottom: 0;">
<h5> 卡片音频 </h5>
<app-audio-recorder [audioUrl]="it.card_audio_url ? it.card_audio_url : null" (audioUploaded)="onAudioUploadSuccess($event, it, true)" ></app-audio-recorder>
</div>
</div>
<div style="float: left;">
<div style="width:200px">
<app-upload-image-with-preview [picUrl]="it.image_url" (imageUploaded)="onImgUploadSuccessByImg($event, it)" ></app-upload-image-with-preview>
</div>
</div>
<div style="float: left; display: flex; flex-direction: column; padding-left: 10px;">
<button style="flex:1; margin-bottom: 5px;" nz-button nzType="dashed" (click)="handleMoveItemUp($event, i)" [disabled]="i==0">
<i nz-icon nzType="up" nzTheme="outline"></i>
上移
</button>
<button style="flex:1; margin-bottom: 5px;" nz-button nzType="dashed" (click)="handleMoveItemDown($event, i)" [disabled]="i==hotZoneArr.length-1">
<i nz-icon nzType="down" nzTheme="outline"></i>
下移
</button>
<button style="flex:1; margin-bottom: 5px;" nz-button nzType="danger" (click)="deleteItem($event, i)">
<i nz-icon nzType="delete" nzTheme="outline"></i>
删除
</button>
</div>
</div>
<div nz-col nzSpan="5" nzOffset="1">
<div class="bg-box">
<button
nz-button
nzType="dashed"
(click)="addBtnClick()"
class="add-btn"
>
<i nz-icon nzType="plus-circle" nzTheme="outline"></i>
<!--Add Image-->
Add hot zone
</button>
</div>
</div>
</div>
</div>
<nz-divider></nz-divider>
<div class="save-box">
</div>
</div>
.p-image-children-editor {
width: 100%;
height: 100%;
border-radius: 0.5rem;
min-width: 1200px;
border: 2px solid #ddd;
.preview-box {
margin: auto;
width: 100%;
// height: 35vw;
border: 2px dashed #ddd;
border-radius: 0.5rem;
background-color: #fafafa;
text-align: center;
color: #aaa;
.preview-img {
height: 100%;
width: auto;
}
}
.bg-box {
margin-bottom: 1rem;
}
.clearfix:after {
content: ".";
display: block;
height: 0;
clear: both;
visibility: hidden;
}
.img-box {
margin: 1rem;
}
.img-box-upload {
width: 120px;
height: 80px;
}
.add-btn {
margin-top: 1rem;
width: 200px;
height: 90px;
display: flex;
align-items: center;
justify-content: center;
}
}
.save-box {
width: 100%;
display: flex;
align-items: center;
justify-content: center;
.save-btn {
width: 160px;
height: 40px;
//margin-top: 20px;
margin-bottom: 20px;
display: flex;
align-items: center;
justify-content: center;
}
}
h5 {
margin-top: 1rem;
}
//@import '../../../style/common_mixin';
//
//.p-image-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");
// @include k-img-bg();
// }
// }
//}
//
//.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 {
// 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: #27b43f;
// border-radius: 0.5rem;
// }
//}
//
//
//:host ::ng-deep .ant-upload {
// display: block;
//}
<div class="title-config">
<div class="title-wrap">
<div class="row" style="margin: 5px;">
<div class="p-content" style="width:100%">
<div class="p-tool-box d-flex" style="background: #fff;">
<nz-select class="ml-1" style="width: 120px;" [(ngModel)]="__fontFamily"
(ngModelChange)="onChangeFontFamily($event)"
nzPlaceHolder="Font Family"
[nzDropdownMatchSelectWidth]="false"
>
<nz-option [nzValue]="font" [nzLabel]="font" *ngFor="let font of fontFamilyList"></nz-option>
</nz-select>
<nz-select class="ml-1" style="width: 110px;" [(ngModel)]="__fontSize"
(ngModelChange)="onChangeFontSize()"
nzPlaceHolder="Font Size">
<nz-option [nzValue]="i" [nzLabel]="'Size - ' + i" *ngFor="let i of fontSizeRange"></nz-option>
</nz-select>
<div class="p-divider"></div>
<div class="i-tool-font-btn d-flex mr-2">
<div class="position-relative fa-icon" (click)="onChangeBold()">
<div class="fa fa-bold"></div>
</div>
</div>
<div class="i-tool-font-btn d-flex mr-2">
<div class="position-relative fa-icon" (click)="onChangeItalic()">
<div class="fa fa-italic"></div>
</div>
</div>
<div class="i-tool-font-btn d-flex mr-2">
<div class="position-relative fa-icon" (click)="onChangeUnderline()">
<div class="fa fa-underline"></div>
</div>
</div>
<div class="i-tool-font-btn d-flex">
<div class="position-relative fa-icon" (click)="onChangeStrikethrough()">
<div class="fa fa-strikethrough"></div>
</div>
</div>
<div class="p-divider"></div>
<div class="i-tool-font-color d-flex">
<div class="position-relative i-left flex-fill" (click)="onChangeFontColor($event)">
<div class="fa fa-font"></div>
<div class="i-color" [style.background-color]="__fontColor"></div>
</div>
<div class="i-dropdown-menu" nzPlacement="bottom"
nz-popover [(nzVisible)]="isShowFontColorPane" nzTrigger="click"
[nzContent]="colorPane">
<i nz-icon type="down" theme="outline"></i>
</div>
</div>
<div class="p-divider"></div>
<div style="background: #fff;display: block;">
<div class="position-relative" (click)="onChangeStrikethrough()">
<app-audio-recorder [audioUrl]="titleObj && titleObj.audio_url" (audioUploaded)="titleAudioUploaded($event)"></app-audio-recorder>
</div>
</div>
</div>
<div class="width-100 d-flex">
<iframe #titleEl frameborder="0" style="overflow: hidden;width: 100%; height:48px; margin: 0; padding: 0;"></iframe>
</div>
</div>
<ng-template #colorPane>
<div class="p-color-pane" nz-row nzGutter="16" nzType="flex">
<div nz-col class="p-color-item" *ngFor="let color of colorList"
[style.background-color]="color"
(click)="onSelectColor(color)"
>
</div>
</div>
</ng-template>
</div>
<ng-container *ngIf="withIcon">
<div class="row type-row">
课程类型:
<nz-radio-group [(ngModel)]="titleObj && titleObj.type" (ngModelChange)="typeChange($event)">
<label nz-radio nzValue="a">单数课</label>
<label nz-radio nzValue="b">双数课</label>
<label nz-radio nzValue="c">复习课</label>
</nz-radio-group>
</div>
</ng-container>
</div>
<ng-container *ngIf="withIcon">
<div class="title-icons">
<div class="icons-list">
<nz-checkbox-wrapper style="width: 100%;clear:both" (nzOnChange)="iconsChanges($event)">
<div [class]="'icon-item icon-'+i" *ngFor="let i of groupIconsCount[titleObj.type];">
<div class="img-box">
<nz-badge class="icon-badge" [nzCount]="titleObj && titleObj.icons && titleObj.icons.indexOf(i) + 1">
<img [src]="'assets/title-icons/'+titleObj.type+'/icon-'+i+'.png'" alt="">
</nz-badge>
</div>
<label nz-checkbox [nzValue]="i" [ngModel]="titleObj && titleObj.icons && titleObj.icons.indexOf(i) > -1"></label>
</div>
</nz-checkbox-wrapper>
</div>
</div>
</ng-container>
</div>
@import '../../style/common_mixin';
.title-config {
.letter-wrap{
width: 3rem;
flex: 0 0 3rem;
}
.str-wrap{
margin-left: 1rem;flex: 1 1;
}
.type-row{
margin: 0;padding-top: 1rem;
}
.icon-item{
margin-right: 16px;
float: left;
width: 45px;
height: 75px;
display: flex;
justify-content: center;
align-items: center;
position: relative;
.icon-badge{
position: absolute;
top: 0;
right: 0;
}
.img-box{
top: 0;
position: absolute;
width: 45px;
height: 50px;
display: flex;
justify-content: center;
align-items: center;
img{
max-width: 100%;
}
}
label{
position: absolute;
bottom: 0;
}
}
}
@mixin tool-btn {
border: 1px solid #ddd;
display: flex;
justify-content: center;
align-items: center;
height: 100%;
outline: none;
border-radius: 6px;
color: #555;
}
.p-title-box {
.p-title {
font-size: 20px;
}
input {
width: 300px;
margin-left: 10px;
}
}
.p-content {
border: 1px solid #ddd;
box-shadow: 0 0 8px #eee;
margin-top: 10px;
}
.p-divider {
border-left: 1px solid #ccc;
margin: 5px 8px;
height: 85%;
}
.p-tool-box {
background-color: #efefef;
padding: 2px;
height: 37px;
display: flex;
align-items: center;
border-bottom: 1px solid #ddd;
// save
.i-tool-save {
@include tool-btn();
color: white;
}
.i-tool-save:disabled {
color: #555;
}
// font-size
.i-tool-font-size {
@include tool-btn();
width: 37px;
& > span {
position: absolute;
top: -5px;
right: 5px;
}
}
.i-tool-font-size:hover {
color: black;
border-color: #bbb;
}
// font-color
.i-tool-font-color, .i-tool-font-btn {
border: 1px solid #ddd;
//padding: 3px 7px;
border-radius: 6px;
width: 45px;
height: 31px;
background-color: white;
color: #555;
::ng-deep > span {
display: flex;
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
padding: 3px 7px;
}
.i-left {
.fa-font,.fa-bold,.fa-italic,.fa-strikethrough, .fa-underline {
font-size: 10px;
position: absolute;
color: #555;
left: 8px;
top: 7px;
}
.i-color {
width: 68%;
height: 5px;
background-color: black;
position: absolute;
top: 21px;
left: 5px;
}
}
.i-dropdown-menu {
width: 15px;
font-size: 10px;
border-left: 1px solid #ddd;
display: flex;
align-items: center;
.anticon-down {
transform: scale(0.6);
}
}
}
.i-tool-font-btn{
width: 31px;
}
.fa-icon{
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
}
// bg-color
.i-tool-bg-color {
@include tool-btn();
padding: 0 9px;
::ng-deep > span {
display: flex;
align-items: center;
}
.i-color {
display: block;
width: 16px;
height: 16px;
background-color: white;
margin-left: 10px;
}
}
// horizontal-center
.i-tool-horizontal-center {
@include tool-btn();
width: 37px;
}
}
.p-box {
width: 1280px;
height: 720px;
left: 0;
top: 0;
transform-origin: top left;
overflow: hidden;
}
.p-sentence {
@include k-no-select();
}
.p-animation-index-box {
.i-animation-index {
position: absolute;
font-size: 34px;
font-family: Arial;
border: 1px solid #ddd;
border-radius: 4px;
min-width: 50px;
min-height: 50px;
display: flex;
align-items: center;
justify-content: center;
background-color: darkorange;
color: white;
}
}
.p-edit {
background-color: white;
border: 1px solid black;
padding: 2px 15px;
}
.p-selected {
border: 1px solid darkorange;
box-shadow: 0 0 8px #ddd;
}
.p-tool-item-disable {
color: rgba(0, 0, 0, .25) !important;
background-color: transparent !important;
pointer-events: none;
}
// -----------
.p-color-pane {
width: 80px;
.p-color-item {
width: 17px;
height: 17px;
cursor: pointer;
margin: 4px;
border: 1px solid #bbb;
}
.p-color-item-active {
border: 1px solid white;
transform: scale(1.1);
}
}
.p-user-guide {
font-size: 40px;
display: flex;
justify-content: center;
align-items: center;
}
.p-animation-box {
width: 400px;
background-color: #efefef;
border-left: 1px solid #ddd;
padding: 10px;
text-align: center;
.p-animation-label {
z-index: 1;
position: relative;
}
::ng-deep .ant-radio-button-wrapper {
padding: 0 10px;
@include k-no-select();
}
.i-toolbox {
& > div {
font-size: 20px;
cursor: pointer;
color: #555;
padding: 0 3px;
margin: 0 5px;
}
& > div:hover {
color: #1585ff;
transform: scale(1.1);
}
}
.p-animation-list {
background-color: white;
border-radius: 10px;
height: calc(100% - 86px);
overflow-y: auto;
.p-animation-item {
padding: 0 20px;
background-color: aliceblue;
margin-top: 6px;
border-top: 1px solid #ddd;
border-bottom: 1px solid #ddd;
cursor: pointer;
text-align: left;
display: flex;
@include k-no-select();
}
.i-active {
background-color: antiquewhite;
}
}
}
import {
Component,
ElementRef,
EventEmitter,
Input,
OnChanges,
OnDestroy,
OnInit,
Output,
ViewChild
} from '@angular/core';
@Component({
selector: 'app-lesson-title-config',
templateUrl: './lesson-title-config.component.html',
styleUrls: ['./lesson-title-config.component.scss']
})
export class LessonTitleConfigComponent implements OnInit, OnChanges, OnDestroy, OnChanges {
fontFamilyList = [
'Arial',
'ARBLI'
];
colorList = [
'#111111',
'#ffffff',
'#595959',
'#0075c2',
'#c61c1e',
'#9cbc3a'
];
MIN_FONT_SIZE = 1;
MAX_FONT_SIZE = 7;
isShowFontColorPane = false;
isShowBGColorPane = false;
fontSizeRange: number[];
editorContent = '';
__fontFamily = 'Arial';
__fontColor = '';
__fontSize = 3;
loopCnt = 0;
maxLoops = 20;
groupIconsCount = {
a: Array.from(Array(11).keys()),
b: Array.from(Array(8).keys()),
c: Array.from(Array(8).keys()),
};
prevIcons = [];
prevType = '';
@ViewChild('titleEl') titleEl: ElementRef;
titleEW = null;
@Input()
titleObj = {
type: 'a',
content: '',
icons: [],
audio_url: ''
};
@Input()
withIcon = true;
@Output()
titleUpdated = new EventEmitter();
constructor() {
this.fontSizeRange = [];
for (let i = this.MIN_FONT_SIZE; i <= this.MAX_FONT_SIZE; ++i) {
this.fontSizeRange.push(i);
}
this.__fontSize = 3;
this.__fontColor = this.colorList[0];
}
ngOnChanges(vars) {
if (!vars.titleObj.previousValue) {// 初始化,内容是空
return;
}
let defObj = this.titleObj;
if (!vars.titleObj.currentValue) {
defObj = {
type: 'a',
content: '',
icons: [],
audio_url: ''
};
} else {
defObj = vars.titleObj.currentValue;
}
this.titleObj.icons = defObj.icons || [];
this.titleObj.type = defObj.type || 'a';
this.titleObj.content = defObj.content || '';
this.titleObj.audio_url = defObj.audio_url || '';
this.titleEW.document.body.innerHTML = this.titleObj.content;
}
ngOnInit() {
if (!this.titleObj) {
this.titleObj = {
type: 'a',
content: '',
icons: [],
audio_url: ''
};
}
this.titleObj.icons = this.titleObj.icons || [];
this.titleObj.type = this.titleObj.type || 'a';
this.titleObj.content = this.titleObj.content || '';
this.titleObj.audio_url = this.titleObj.audio_url || '';
this.editorContent = `<html lang="en"><head><meta charset="utf-8">
<meta name="viewport"
content="width=device-width,initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no"/>
</head>
<body style="height:48px;overflow: hidden;margin: 0;padding: 0 .5rem;background: #FFF;line-height: 48px;">
${this.titleObj.content}
</body>
</html>`;
this.titleEW = this.titleEl.nativeElement.contentWindow;
const tdoc = this.titleEW.document;
tdoc.designMode = "on";
tdoc.open('text/html', 'replace');
tdoc.write(this.editorContent);
tdoc.close();
tdoc.addEventListener("keypress", this.keyPress, true);
tdoc.addEventListener("blur", () => {
if (this.titleObj.content === this.titleEW.document.body.innerHTML.trim()) {
return;
}
this.shouldSave();
}, true);
}
htmlEncode(text) {
if (!text) {
return '';
}
return text.replace(/\&/ig, '&amp;')
.replace(/\</ig, '&lt;')
.replace(/\>/ig, '&gt;')
.replace(/\"/ig, '&quot;');
}
htmlDecode(text) {
if (!text) {
return '';
}
return text.replace(/\&amp\;/ig, '&')
.replace(/\&lt\;/ig, '<')
.replace(/\&gt\;/ig, '>')
.replace(/\&quot\;/ig, '"');
}
ngOnDestroy(): void {
}
iconsChanges(val) {
let a = this.titleObj.icons;
let b = val;
if (a.length > b.length) {
const diff = a.filter(x => !b.includes(x));
const ti = [...this.titleObj.icons];
for (let i = 0; i < diff.length; i++) {
const d = diff[i];
const idx = ti.indexOf(d);
ti.splice(idx, 1);
}
this.titleObj.icons = ti;
} else {
const diff = b.filter(x => !a.includes(x));
this.titleObj.icons = [...this.titleObj.icons, ...diff];
}
this.shouldSave();
}
typeChange(val) {
this.titleObj.icons = [];
this.shouldSave();
}
keyPress(evt) {
try {
if (evt.charCode === 13) {
evt.preventDefault();
evt.stopPropagation();
return;
}
if (evt.ctrlKey) {
const key = String.fromCharCode(evt.charCode).toLowerCase();
let cmd = '';
switch (key) {
case 'b': cmd = "bold"; break;
case 'i': cmd = "italic"; break;
case 'u': cmd = "underline"; break;
}
if (cmd) {
this.execEditorCommand(cmd);
// stop the event bubble
evt.preventDefault();
evt.stopPropagation();
}
}
} catch (e) {
console.log(1, e);
alert(e);
}
}
execEditorCommand(command, option?: any) {
try {
this.titleEW.focus();
this.titleEW.document.execCommand(command, false, option);
this.loopCnt = 0;
return false;
} catch (e) {
alert(e);
if (this.loopCnt < this.maxLoops) {
setTimeout(() => {
this.execEditorCommand(command, option);
}, 100);
this.loopCnt += 1;
} else {
alert("Error executing command.");
}
}
}
onSelectColor(color) {
this.execEditorCommand('forecolor', color);
this.__fontColor = color;
}
onChangeFontColor(val) {
this.execEditorCommand('forecolor', this.__fontColor);
}
onChangeFontFamily(font) {
this.execEditorCommand('fontname', font);
}
onChangeFontSize(size?: any) {
if (size) {
size += this.__fontSize;
} else {
size = this.__fontSize;
}
size = Math.max(this.MIN_FONT_SIZE, size);
size = Math.min(this.MAX_FONT_SIZE, size);
this.execEditorCommand('fontsize', size);
}
onChangeBold() {
this.execEditorCommand('bold');
}
onChangeItalic() {
this.execEditorCommand('italic');
}
onChangeUnderline() {
this.execEditorCommand('underline');
}
onChangeStrikethrough() {
this.execEditorCommand('strikethrough');
}
titleAudioUploaded(res) {
this.titleObj.audio_url = res.url;
this.titleUpdated.emit(this.titleObj);
}
shouldSave = () => {
console.log('title shouldSave');
this.titleObj.content = this.titleEW.document.body.innerHTML.trim();
this.titleUpdated.emit(this.titleObj);
}
}
<div class="cmp-player-content-wrapper" #wrapperEl>
<div class="cmp-cnt-box">
<img [src]="'assets/'+ratio+'.png'" alt="">
<div class="cmp-cnt-main">
<ng-content></ng-content>
</div>
</div>
</div>
\ No newline at end of file
@import '../../style/common_mixin';
.cmp-player-content-wrapper{
max-height: 100%;
display: block;
position: relative;
height: 100%;
.cmp-cnt-box{
display: inline-block;
max-width: 100%;
max-height: 100%;
width: auto;
height: 100%;
position: relative;
img{
height: 100%;
width: auto;
}
}
.cmp-cnt-main{
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
display: flex;
flex-direction: column;
}
}
[style*="--aspect-ratio"] > :first-child {
width: 100%;
}
[style*="--aspect-ratio"] > img {
height: auto;
}
@supports (--custom:property) {
[style*="--aspect-ratio"] {
position: relative;
}
[style*="--aspect-ratio"]::before {
content: "";
display: block;
padding-bottom: calc(100% / (var(--aspect-ratio)));
}
[style*="--aspect-ratio"] > :first-child {
position: absolute;
top: 0;
left: 0;
height: 100%;
}
}
import {
AfterViewInit,
Component,
ElementRef,
Input,
OnChanges,
OnDestroy,
OnInit,
ViewChild
} from '@angular/core';
@Component({
selector: 'app-player-content-wrapper',
templateUrl: './player-content-wrapper.component.html',
styleUrls: ['./player-content-wrapper.component.scss']
})
export class PlayerContentWrapperComponent implements OnInit, OnChanges, OnDestroy, AfterViewInit {
@ViewChild('wrapperEl') wrapperEl: ElementRef;
// // aspect ratio?
@Input() ratio;
_w: string;
_h: string;
constructor() {
if (window.innerHeight < window.innerWidth) {
this._h = '100%';
this._w = 'auto';
} else {
this._w = '100%';
this._h = 'auto';
}
}
ngOnInit() {
if (!this.ratio) {
this.ratio = '20-9';
}
}
ngOnChanges() {
}
ngOnDestroy(): void {
}
ngAfterViewInit() {
}
}
<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>
@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;
//}
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);
}
}
...@@ -31,67 +31,9 @@ ...@@ -31,67 +31,9 @@
} }
@font-face
{
font-family: 'BRLNSDB';
src: url("../../assets/font/BRLNSDB.TTF") ;
}
@font-face
{
font-family: 'RoundedBold';
src: url("../../assets/font/ArialRoundedBold.otf") ;
}
@font-face{
font-family: 'BRLNSB_1';
src: url("../../assets/font/BerlinSansFB/BRLNSB_1.TTF") ;
}
@font-face{ @font-face{
font-family: 'BerlinSansFBDemi-Bold'; font-family: 'BerlinSansFBDemi-Bold';
src: url("../../assets/font/BerlinSansFB/BRLNSDB_1.TTF") ; src: url("../../assets/font/BerlinSansFB/BRLNSDB_1.TTF") ;
} }
@font-face{
font-family: 'BRLNSR_1';
src: url("../../assets/font/BerlinSansFB/BRLNSR_1.TTF") ;
}
@font-face{
font-family: 'GOTHIC_1';
src: url("../../assets/font/CenturyGothic/GOTHIC_1.TTF") ;
}
@font-face{
font-family: 'GOTHICB_1';
src: url("../../assets/font/CenturyGothic/GOTHICB_1.TTF") ;
}
@font-face{
font-family: 'GOTHICBI_1';
src: url("../../assets/font/CenturyGothic/GOTHICBI_1.TTF") ;
}
@font-face{
font-family: 'GOTHICI_1';
src: url("../../assets/font/CenturyGothic/GOTHICI_1.TTF") ;
}
@font-face{
font-family: 'MMTextBook';
src: url("../../assets/font/MMTextBook/MMTextBook.otf") ;
}
@font-face{
font-family: 'MMTextBook-Bold';
src: url("../../assets/font/MMTextBook/MMTextBook-Bold.otf") ;
}
@font-face{
font-family: 'MMTextBook-BoldItalic';
src: url("../../assets/font/MMTextBook/MMTextBook-BoldItalic.otf") ;
}
@font-face{
font-family: 'MMTextBook-Italic';
src: url("../../assets/font/MMTextBook/MMTextBook-Italic.otf") ;
}
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