Commit be906b8d authored by 李维's avatar 李维

temp_save

parent b47070b4
No preview for this file type
...@@ -7,8 +7,8 @@ ...@@ -7,8 +7,8 @@
"premultiplyAlpha": false, "premultiplyAlpha": false,
"genMipmaps": false, "genMipmaps": false,
"packable": true, "packable": true,
"width": 50, "width": 100,
"height": 50, "height": 100,
"platformSettings": {}, "platformSettings": {},
"subMetas": { "subMetas": {
"btn_close": { "btn_close": {
......
...@@ -7,8 +7,8 @@ ...@@ -7,8 +7,8 @@
"premultiplyAlpha": false, "premultiplyAlpha": false,
"genMipmaps": false, "genMipmaps": false,
"packable": true, "packable": true,
"width": 1920, "width": 3840,
"height": 1080, "height": 2160,
"platformSettings": {}, "platformSettings": {},
"subMetas": { "subMetas": {
"mask_bg": { "mask_bg": {
......
...@@ -173,12 +173,13 @@ export default class SceneComponent extends MyCocosSceneComponent { ...@@ -173,12 +173,13 @@ export default class SceneComponent extends MyCocosSceneComponent {
btn.addComponent(cc.Button); btn.addComponent(cc.Button);
btn.on("click", () => { btn.on("click", () => {
this.currentInputTarget = btn; this.currentInputTarget = btn;
this.selectModal.emit("show", [ // this.selectModal.emit("show", [
{index: 0, label: "Option-1", value: 1}, // {index: 0, label: "Option-1", value: 1},
{index: 1, label: "Option-2", value: 2}, // {index: 1, label: "Option-2", value: 2},
{index: 2, label: "Option-3", value: 3}, // {index: 2, label: "Option-3", value: 3},
{index: 3, label: "Option-4", value: 5} // {index: 3, label: "Option-4", value: 5}
]); // ]);
this.keyboard.emit("show");
}) })
}; };
......
...@@ -130,6 +130,6 @@ ...@@ -130,6 +130,6 @@
}, },
"defaultProject": "ng-template-generator", "defaultProject": "ng-template-generator",
"cli": { "cli": {
"analytics": "5f501d82-8f25-4817-a608-9ac70d1f1f70" "analytics": false
} }
} }
\ No newline at end of file
This diff is collapsed.
...@@ -20,18 +20,18 @@ ...@@ -20,18 +20,18 @@
"@angular/platform-browser": "~9.0.2", "@angular/platform-browser": "~9.0.2",
"@angular/platform-browser-dynamic": "~9.0.2", "@angular/platform-browser-dynamic": "~9.0.2",
"@angular/router": "~9.0.2", "@angular/router": "~9.0.2",
"@fortawesome/angular-fontawesome": "^0.6.0", "@fortawesome/angular-fontawesome": "0.6.0",
"@fortawesome/fontawesome-svg-core": "^1.2.27", "@fortawesome/fontawesome-svg-core": "1.2.27",
"@fortawesome/free-regular-svg-icons": "^5.12.1", "@fortawesome/free-regular-svg-icons": "5.12.1",
"@fortawesome/free-solid-svg-icons": "^5.12.1", "@fortawesome/free-solid-svg-icons": "5.12.1",
"@tweenjs/tween.js": "~18.5.0", "@tweenjs/tween.js": "~18.5.0",
"ali-oss": "^6.5.1", "ali-oss": "^6.5.1",
"compressing": "^1.5.0", "compressing": "^1.5.0",
"ng-zorro-antd": "^8.5.2", "ng-zorro-antd": "^8.5.2",
"rxjs": "~6.5.4", "rxjs": "~6.5.4",
"node-sass": "^4.0.0",
"spark-md5": "^3.0.0", "spark-md5": "^3.0.0",
"tslib": "^1.10.0", "tslib": "^1.10.0",
"node-sass": "^4.0.0",
"zone.js": "~0.10.2" "zone.js": "~0.10.2"
}, },
"devDependencies": { "devDependencies": {
......
...@@ -6,7 +6,7 @@ import { Component , OnInit} from '@angular/core'; ...@@ -6,7 +6,7 @@ import { Component , OnInit} from '@angular/core';
styleUrls: ['./app.component.scss'] styleUrls: ['./app.component.scss']
}) })
export class AppComponent implements OnInit { export class AppComponent implements OnInit {
type = 'form'; type = 'play';
constructor() { constructor() {
const tp = this.getQueryString('type'); const tp = this.getQueryString('type');
......
import { BrowserModule } from '@angular/platform-browser'; import { BrowserModule } from '@angular/platform-browser';
import {DragDropModule} from '@angular/cdk/drag-drop';
import { NgModule, ErrorHandler } from '@angular/core'; import { NgModule, ErrorHandler } from '@angular/core';
import {MyErrorHandler} from './MyError'; import {MyErrorHandler} from './MyError';
...@@ -24,7 +25,12 @@ import {AudioRecorderComponent} from './common/audio-recorder/audio-recorder.com ...@@ -24,7 +25,12 @@ import {AudioRecorderComponent} from './common/audio-recorder/audio-recorder.com
import { FontAwesomeModule, FaIconLibrary } from '@fortawesome/angular-fontawesome'; import { FontAwesomeModule, FaIconLibrary } from '@fortawesome/angular-fontawesome';
import { fas } from '@fortawesome/free-solid-svg-icons'; import { fas } from '@fortawesome/free-solid-svg-icons';
import { far } from '@fortawesome/free-regular-svg-icons'; import { far } from '@fortawesome/free-regular-svg-icons';
import { CustomActionComponent } from './common/custom-action/custom-action.component';
import { UploadDragonBoneComponent } from './common/upload-dragon-bone/upload-dragon-bone.component'; import { UploadDragonBoneComponent } from './common/upload-dragon-bone/upload-dragon-bone.component';
import { SubTemplateComponent } from './common/sub-template/sub-template.component';
import {FormulaInputComponent} from './common/formula-input/formula-input.component';
import { MultRectComponent } from './common/mult-rect/mult-rect.component';
registerLocaleData(zh); registerLocaleData(zh);
...@@ -41,9 +47,13 @@ registerLocaleData(zh); ...@@ -41,9 +47,13 @@ registerLocaleData(zh);
TimePipe, TimePipe,
UploadVideoComponent, UploadVideoComponent,
CustomHotZoneComponent, CustomHotZoneComponent,
UploadDragonBoneComponent, SubTemplateComponent,
PlayerContentWrapperComponent
PlayerContentWrapperComponent,
CustomActionComponent,
UploadDragonBoneComponent,
FormulaInputComponent,
MultRectComponent,
], ],
imports: [ imports: [
BrowserModule, BrowserModule,
...@@ -51,7 +61,8 @@ registerLocaleData(zh); ...@@ -51,7 +61,8 @@ registerLocaleData(zh);
FormsModule, FormsModule,
HttpClientModule, HttpClientModule,
BrowserAnimationsModule, BrowserAnimationsModule,
FontAwesomeModule FontAwesomeModule,
DragDropModule
], ],
providers: [ providers: [
{provide: ErrorHandler, useClass: MyErrorHandler}, {provide: ErrorHandler, useClass: MyErrorHandler},
......
<div class="d-flex" (drop)="handle_drop($event)" (dragover)="handle_dragover($event)"> <div class="d-flex">
<div class="p-btn-record d-flex"> <div class="p-btn-record d-flex">
<div class="btn-clear" style="cursor: pointer" (click)="onBtnClearAudio()" *ngIf="withRmBtn && (audioUrl || audioBlob)"> <div class="btn-clear" style="cursor: pointer" (click)="onBtnClearAudio()" *ngIf="withRmBtn && (audioUrl || audioBlob)">
<fa-icon icon="times"></fa-icon> <fa-icon icon="times"></fa-icon>
...@@ -35,7 +35,8 @@ ...@@ -35,7 +35,8 @@
<ng-template #truthyTemplate > <ng-template #truthyTemplate >
<div class="btn-delete" (click)="onBtnDeleteAudio()"> <div class="btn-delete" (click)="onBtnDeleteAudio()">
<fa-icon icon="close"></fa-icon> <!-- <fa-icon icon="close"></fa-icon> -->
<i nz-icon nzType="close" nzTheme="outline"></i>
</div> </div>
</ng-template> </ng-template>
...@@ -45,19 +46,17 @@ ...@@ -45,19 +46,17 @@
<fa-icon icon="cog"></fa-icon> <fa-icon icon="cog"></fa-icon>
</div> </div>
</ng-template> </ng-template>
</div> </div>
<div class="p-progress ml-2" (click)="onBtnPlay()" *ngIf="audioUrl || audioBlob" <div class="p-progress ml-2" (click)="onBtnPlay()" *ngIf="audioUrl || audioBlob">
style="background-color: #70B603; width: 35px; height: 35px; border-radius: 35px; margin-left: 10px;margin-top:-1px"> <nz-progress [nzPercent]="percent" [nzWidth]="30" [nzFormat]="progressText"
<nz-progress [nzPercent]="percent" [nzWidth]="35" [nzFormat]="progressText" nzType="circle"></nz-progress>
nzType="circle"> <div class="p-btn-play" [style.left]="isPlaying?'8px':''">
</nz-progress>
<div class="p-btn-play"
style="color: white;margin-left: 2px;margin-top: 1px;"
[style.left]="isPlaying?'8px':''">
<fa-icon [icon]="playIcon"></fa-icon> <fa-icon [icon]="playIcon"></fa-icon>
</div> </div>
</div> </div>
<div class="audio-name">{{_audioName}}</div>
</div> </div>
...@@ -90,26 +90,17 @@ ...@@ -90,26 +90,17 @@
} }
.p-progress { .p-progress {
margin-top: 3.5px; margin-top: 2px;
position: relative; position: relative;
background-color: #27b43f;
border-radius: 15px;
line-height: 26px; line-height: 26px;
.p-btn-play { .p-btn-play {
position: absolute; position: absolute;
left: 11px; left: 10px;
top: 3px; top: 3px;
color: #ffffff; color: #555;
} }
} }
.audio-name{
margin-top: 3.5px;
position: relative;
margin-left: 7px;;
line-height: 26px;
}
:host ::ng-deep nz-upload { :host ::ng-deep nz-upload {
line-height: 33px; line-height: 33px;
} }
......
import {Component, EventEmitter, Input, OnDestroy, OnInit, Output, NgZone, OnChanges} from '@angular/core'; import {Component, EventEmitter, Input, OnDestroy, OnInit, Output, NgZone, OnChanges} from '@angular/core';
import {NzMessageService, NzNotificationService, UploadFile} from 'ng-zorro-antd'; import {NzMessageService, NzNotificationService, UploadFile} from 'ng-zorro-antd';
import {HttpClient, HttpEvent, HttpEventType, HttpRequest} from '@angular/common/http'; import {HttpClient, HttpEvent, HttpEventType, HttpRequest} from '@angular/common/http';
import {environment} from '../../../environments/environment';
declare var Recorder; declare var Recorder;
...@@ -26,26 +26,17 @@ export class AudioRecorderComponent implements OnInit, OnChanges, OnDestroy { ...@@ -26,26 +26,17 @@ export class AudioRecorderComponent implements OnInit, OnChanges, OnDestroy {
uploadData; uploadData;
@Input() @Input()
needRemove = false; needRemove = true;
@Input() @Input()
audioItem: any = null; audioItem: any = null;
@Input()
_audioName: any = null;
@Input() @Input()
set audioUrl(url) { set audioUrl(url) {
this._audioUrl = url; this._audioUrl = url;
if (url) { if (url) {
this.httpHeadCall(this._audioUrl, flag => { this.audio.src = this._audioUrl;
if (flag) { this.audio.load();
this.audio.src = this._audioUrl;
} else {
this.audio.src = this._audioUrl.replace("_l.", ".");
}
this.audio.load();
});
} }
this.init(); this.init();
} }
...@@ -57,7 +48,6 @@ export class AudioRecorderComponent implements OnInit, OnChanges, OnDestroy { ...@@ -57,7 +48,6 @@ export class AudioRecorderComponent implements OnInit, OnChanges, OnDestroy {
@Output() audioUploaded = new EventEmitter(); @Output() audioUploaded = new EventEmitter();
@Output() audioUploadFailure = new EventEmitter(); @Output() audioUploadFailure = new EventEmitter();
@Output() audioRemoved = new EventEmitter(); @Output() audioRemoved = new EventEmitter();
@Output() audioName = new EventEmitter();
percent = 0; percent = 0;
progress = 0; progress = 0;
recorder: any; recorder: any;
...@@ -72,43 +62,25 @@ export class AudioRecorderComponent implements OnInit, OnChanges, OnDestroy { ...@@ -72,43 +62,25 @@ export class AudioRecorderComponent implements OnInit, OnChanges, OnDestroy {
this.uploadUrl = url; this.uploadUrl = url;
this.uploadData = data; this.uploadData = data;
}; };
this.setUploadUrl();
} }
httpHeadCall(requsetUrl: string, callback) {
let xhr = new XMLHttpRequest(); setUploadUrl() {
console.log("Status: Send Post Request to " + requsetUrl);
try { if (!this.uploadUrl) {
xhr.onreadystatechange = () => {
try { this.uploadUrl = (<any> window).courseware.uploadUrl();
console.log('xhr.readyState: ', xhr.readyState); this.uploadData = (<any> window).courseware.uploadData();
if (xhr.readyState == 4) {
if ((xhr.status >= 200 && xhr.status < 400)) { setTimeout(() => {
callback(true); this.setUploadUrl();
} else { }, 500);
callback(false);
}
}
} catch (e) {
console.log(e)
}
};
xhr.open("HEAD", requsetUrl, true);
xhr.send();
xhr.timeout = 15000;
xhr.onerror = (e) => {
console.log("汪汪汪 posterror", e);
callback(false);
};
xhr.ontimeout = (e) => {
console.log("汪汪汪 ontimeout", e);
callback(false);
};
} catch (e) {
console.log("Send Get Request error: ", e)
} }
} }
init() { init() {
this.playIcon = 'play'; this.playIcon = 'play';
this.isPlaying = false; this.isPlaying = false;
...@@ -202,6 +174,7 @@ export class AudioRecorderComponent implements OnInit, OnChanges, OnDestroy { ...@@ -202,6 +174,7 @@ export class AudioRecorderComponent implements OnInit, OnChanges, OnDestroy {
onBtnDeleteAudio() { onBtnDeleteAudio() {
this.audioUrl = null; this.audioUrl = null;
this.audioUploaded.emit({});
this.audioRemoved.emit(); this.audioRemoved.emit();
} }
...@@ -213,12 +186,8 @@ export class AudioRecorderComponent implements OnInit, OnChanges, OnDestroy { ...@@ -213,12 +186,8 @@ export class AudioRecorderComponent implements OnInit, OnChanges, OnDestroy {
break; break;
case 'success': case 'success':
this.isUploading = false; this.isUploading = false;
let url = info.file.response.url;
url = url.substring(0, url.lastIndexOf(".")) + "_l.mp3";
info.file.response.url = url;
this.uploadSuccess(info.file.response); this.uploadSuccess(info.file.response);
this.audioUploaded.emit(info.file.response); this.audioUploaded.emit(info.file.response);
this.audioName.emit(info.file.name);
break; break;
case 'progress': case 'progress':
this.progress = parseInt(info.event.percent, 10); this.progress = parseInt(info.event.percent, 10);
...@@ -276,39 +245,6 @@ export class AudioRecorderComponent implements OnInit, OnChanges, OnDestroy { ...@@ -276,39 +245,6 @@ export class AudioRecorderComponent implements OnInit, OnChanges, OnDestroy {
this.progress = Math.floor(p * 100); this.progress = Math.floor(p * 100);
} }
linkInputed (url, name) {
url = url.substring(0, url.lastIndexOf(".")) + "_l.mp3";
this.audioUrl = url;
this.audioUploaded.emit({url});
this.audioName.emit(name);
}
handle_dragover(e) {
e.preventDefault();
}
handle_drop(e) {
const dt = e.dataTransfer.getData("text/plain");
console.log("handle_drop===", dt);
if (!dt) {
return;
}
try {
const {url, name} = JSON.parse(dt);
if (url.indexOf("teach")<0 || url.indexOf("cdn")<0) {
return;
}
const white = [".mp3"];
if (!white.includes(url.substr(url.lastIndexOf(".")))) {
return;
}
this.linkInputed(url, name);
} catch (error) {
console.warn("handle_drop拖拽在线音频传递参数不合法,应该是{url:'', name:''}");
}
}
} }
enum Type { enum Type {
......
<div class="position-relative">
<button nz-button (click)="setAnimaBtnClick()" style=" border-radius: 0.5rem; border: 1px solid #ddd;" >
<i nz-icon nzType="tool" nzTheme="outline"></i>
{{btnName}}
</button>
<!--配置龙骨面板-->
<nz-modal [(nzVisible)]="panelVisible" (nzAfterClose)="closePanel()" [nzTitle]="btnName" (nzOnCancel)="panelCancel()" (nzOnOk)="panelOk()" nzOkText="保存">
<div>
<h4>基础内容:</h4>
<div style="margin-bottom: 10px; width: 80%; margin: auto;">
<!-- <nz-radio-group [ngModel]="item.type" (ngModelChange)="radioChange($event)">
<label nz-radio nzValue="pic">图片</label>
<label nz-radio nzValue="text">文本</label>
</nz-radio-group> -->
<div *ngIf="item.type == 'pic'">
<app-upload-image-with-preview
[picUrl]="item?.pic_url"
(imageUploaded)="onItemImgUploadSuccess($event, item)">
</app-upload-image-with-preview>
</div>
<div *ngIf="item.type == 'text'">
<input type="text" nz-input [(ngModel)]="item.text" (blur)="saveText(item)">
</div>
<div *ngIf="item.type == 'anima'" style="margin-left: 100px;">
<app-upload-dragon-bone
[skeJsonData]=item.skeJsonData
[texJsonData]=item.texJsonData
[texPngData]=item.texPngData
(save)="saveAnima($event)"
>
</app-upload-dragon-bone>
</div>
</div>
<nz-divider style="margin-top: 10px;"></nz-divider>
<h4>状态设置:</h4>
<div style="display: flex; justify-content: space-around;">
<div style="width: 30%; display: flex; align-items: center; flex-direction: column;">
<h5> 开始状态 </h5>
<div *ngFor="let op of item.changeOption; let i = index" style="margin-bottom: 5px; ">
<div style="display: flex; align-items: center; justify-content: center;">
<span>{{op[0]}}: </span> <input type="text" nz-input [(ngModel)]="op[1]" (blur)="saveText(op)" style="margin-left: 5px;">
</div>
</div>
</div>
<div style="width: 30%; display: flex; align-items: center; flex-direction: column; ">
<h5> 切换时间 </h5>
<div style="display: flex; width: 100%; align-items: center; justify-content: center;">
<span>time: </span> <input type="text" nz-input [(ngModel)]="item.changeTime" (blur)="saveText(item.changeTime)" style="width: 80px; margin-left: 5px;">
</div>
</div>
<div style="width: 30%; display: flex; align-items: center; flex-direction: column;">
<h5> 结束状态 </h5>
<div *ngFor="let op of item.changeOption; let i = index" style="margin-bottom: 5px; ">
<div style="display: flex; align-items: center; justify-content: center;">
<span>{{op[0]}}: </span> <input [disabled]="op[2] == null" type="text" nz-input [(ngModel)]="op[2]" (blur)="saveText(op)" style="margin-left: 5px;">
</div>
</div>
</div>
</div>
<div style="display: flex; align-items: center; justify-content: center; margin-top: 10px;">
<button style="margin-left: 20px; margin-top: -5px" nz-button (click)="copyChangeData()"> 复制数据 </button>
<div style="margin-left: 20px; margin-top: -5px" >
<span>粘贴数据: </span>
<input style="width: 100px;" type="text" nz-input [(ngModel)]="pasteData" >
<button style="margin-left: 5px;" nz-button [disabled]="pasteData==''" nzType="primary" (click)="importData()">导入</button>
</div>
</div>
<nz-divider style="margin-top: 10px;"></nz-divider>
<h4> 音频设置 </h4>
<app-audio-recorder
style="margin: auto"
[audioUrl]="item?.audio_url"
(audioUploaded)="onItemAudioUploadSuccess($event, item)"
></app-audio-recorder>
</div>
<!--
<div class="anima-upload-btn">
<span style="margin-right: 10px">上传 ske_json 文件: </span>
<nz-upload [nzShowUploadList]="false"
nzAccept="application/json"
[nzAction]="uploadUrl"
[nzData]="uploadData"
(nzChange)="skeJsonHandleChange($event)">
<button nz-button><i nz-icon nzType="upload"></i><span>Upload</span></button>
</nz-upload>
<i *ngIf="isSkeJsonLoading" style="margin-left: 10px;" nz-icon [nzType]="'loading'"></i>
<span *ngIf="skeJsonData && skeJsonData['name']" style="margin-left: 10px"><u> {{skeJsonData['name']}} </u></span>
</div>
<div class="anima-upload-btn">
<span style="margin-right: 10px">上传 tex_json 文件: </span>
<nz-upload [nzShowUploadList]="false"
nzAccept="application/json"
[nzAction]="uploadUrl"
[nzData]="uploadData"
(nzChange)="texJsonHandleChange($event)">
<button nz-button><i nz-icon nzType="upload"></i><span>Upload</span></button>
</nz-upload>
<i *ngIf="isTexJsonLoading" style="margin-left: 10px;" nz-icon [nzType]="'loading'"></i>
<span *ngIf="texJsonData && texJsonData['name']" style="margin-left: 10px"><u> {{texJsonData['name']}} </u></span>
</div>
<div class="anima-upload-btn">
<span style="margin-right: 10px">上传 tex_png 文件: </span>
<nz-upload [nzShowUploadList]="false"
nzAccept = "image/*"
[nzAction]="uploadUrl"
[nzData]="uploadData"
(nzChange)="texPngHandleChange($event)">
<button nz-button><i nz-icon nzType="upload"></i><span>Upload</span></button>
</nz-upload>
<i *ngIf="isTexPngLoading" style="margin-left: 10px;" nz-icon [nzType]="'loading'"></i>
<span *ngIf="texPngData && texPngData['name']" style="margin-left: 10px"><u> {{texPngData['name']}} </u></span>
</div>
<div class="anima-upload-btn" *ngIf="animaNames && animaNames.length > 0">
提示:需包含动画: {{animaNames.toString()}}.
</div> -->
</nz-modal>
</div>
@import '../../style/common_mixin.css';
.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-size: contain;
background-repeat: no-repeat;
background-position: 50% 50%;
//background-image: url("https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png");
}
}
.d-flex{
display: flex;
}
}
.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;
}
}
.anima-upload-btn {
padding: 10px;
}
:host ::ng-deep .ant-upload {
display: block;
}
import {ApplicationRef, Component, EventEmitter, Input, OnChanges, OnDestroy, Output} from '@angular/core';
import { NzMessageService, UploadXHRArgs, UploadFile } from 'ng-zorro-antd';
@Component({
selector: 'app-custom-action',
templateUrl: './custom-action.component.html',
styleUrls: ['./custom-action.component.scss']
})
export class CustomActionComponent implements OnDestroy, OnChanges {
uploading = false;
progress = 0;
@Input()
btnName = '配置变化状态';
@Input()
option = []
@Input()
item;
@Input()
type = 'text'
@Output()
save = new EventEmitter();
@Output()
refreshEmitter = new EventEmitter();
uploadUrl;
uploadData;
panelVisible = false;
pasteData = '';
constructor(private appRef: ApplicationRef, private nzMessageService: NzMessageService) {
window['air'].getUploadCallback = (url, data) => {
this.uploadUrl = url;
this.uploadData = data;
};
const tmpId = setInterval(() => {
console.log(' in setInterval')
this.uploadUrl = (<any> window).courseware.uploadUrl();
this.uploadData = (<any> window).courseware.uploadData();
if (this.uploadUrl) {
clearInterval(tmpId);
}
}, 500)
}
ngOnInit() {
this.initItem();
}
ngOnChanges() {
}
initItem() {
console.log('```this.item: ', this.item);
if (!this.item) {
this.item = {
type: this.type,
skeJsonData: {},
texJsonData: {},
texPngData: {},
changeStart: [
],
changeEnd: [
],
changeTime: 0.3
};
if (this.option) {
this.item.changeOption = JSON.parse(JSON.stringify(this.option));
}
}
console.log('initItem', this.item);
}
setAnimaBtnClick() {
this.panelVisible = true;
this.refresh();
}
panelCancel() {
this.panelVisible = false;
this.refresh();
}
panelOk() {
// this.sendItemDragonBoneData();
this.panelVisible = false;
this.refresh();
this.save.emit(this.item);
}
saveText(t) {
}
radioChange(e) {
this.item.type = e;
console.log('e: ', e);
}
onItemImgUploadSuccess(e, item) {
item.pic_url = e.url;
this.refresh();
}
onItemAudioUploadSuccess(e, item) {
item.audio_url = e.url;
this.refresh();
}
saveAnima(e) {
console.log("~ e: ", e);
}
copyChangeData() {
console.log('this.item: ', this.item);
const jsonData = {
changeOption: this.item.changeOption,
changeTime : this.item.changeTime
// bgItem,
// hotZoneItemArr,
// isHasRect: this.isHasRect,
// isHasPic: this.isHasPic,
// isHasText: this.isHasText,
// isHasAudio: this.isHasAudio,
// isHasAnima: this.isHasAnima,
// hotZoneFontObj: this.hotZoneFontObj,
// defaultItemType: this.defaultItemType,
// hotZoneImgSize: this.hotZoneImgSize,
};
const oInput = document.createElement('input');
oInput.value = JSON.stringify(jsonData);
document.body.appendChild(oInput);
oInput.select(); // 选择对象
document.execCommand('Copy'); // 执行浏览器复制命令
document.body.removeChild(oInput);
this.nzMessageService.success('复制成功');
}
importData() {
if (!this.pasteData) {
return;
}
try {
const data = JSON.parse(this.pasteData);
console.log('data:', data);
const {
changeOption,
changeTime
} = data;
this.item.changeOption = changeOption;
this.item.changeTime = changeTime;
this.pasteData = '';
} catch (e) {
console.log('err: ', e);
this.nzMessageService.error('导入失败');
}
}
/**
* 刷新 渲染页面
*/
refresh() {
// this.refreshEmitter.emit();
setTimeout(() => {
this.appRef.tick();
}, 1);
}
closePanel() {
console.log(' in closePanel ');
this.refresh();
}
ngOnDestroy() {
}
}
...@@ -1961,6 +1961,9 @@ export class HotZoneItem extends MySprite { ...@@ -1961,6 +1961,9 @@ export class HotZoneItem extends MySprite {
isAnimaStyle = false; isAnimaStyle = false;
rectFillColor = '#ffffff';
rectEdgeColor = '#1bfff7';
private _itemType; private _itemType;
private shapeRect: ShapeRect; private shapeRect: ShapeRect;
...@@ -1980,7 +1983,7 @@ export class HotZoneItem extends MySprite { ...@@ -1980,7 +1983,7 @@ export class HotZoneItem extends MySprite {
rect.x = -w / 2; rect.x = -w / 2;
rect.y = -h / 2; rect.y = -h / 2;
rect.setSize(w, h); rect.setSize(w, h);
rect.fillColor = '#ffffff'; rect.fillColor = this.rectFillColor;
rect.alpha = 0.2; rect.alpha = 0.2;
this.addChild(rect); this.addChild(rect);
} }
...@@ -2103,7 +2106,7 @@ export class HotZoneItem extends MySprite { ...@@ -2103,7 +2106,7 @@ export class HotZoneItem extends MySprite {
this.ctx.setLineDash([5, 5]); this.ctx.setLineDash([5, 5]);
this.ctx.lineWidth = 2; this.ctx.lineWidth = 2;
this.ctx.strokeStyle = '#1bfff7'; this.ctx.strokeStyle = this.rectEdgeColor;
// this.ctx.fillStyle = '#ffffff'; // this.ctx.fillStyle = '#ffffff';
this.ctx.beginPath(); this.ctx.beginPath();
...@@ -2138,6 +2141,52 @@ export class HotZoneItem extends MySprite { ...@@ -2138,6 +2141,52 @@ export class HotZoneItem extends MySprite {
} }
} }
export class HotZoneMultRect extends HotZoneItem {
item;
drawLine() {
if (!this.item) {
return;
}
this.ctx.save();
const rect = this.getBoundingBox();
const w = rect.width;
const h = rect.height;
const x = rect.x //+ w / 2;
const y = rect.y //+ h / 2;
this.ctx.setLineDash([4, 4]);
this.ctx.lineWidth = 1;
this.ctx.strokeStyle = '#ffa568';
this.ctx.beginPath();
this.ctx.moveTo( x + w / 2 , y + h / 2 );
this.ctx.lineTo(this.item.x, this.item.y);
// this.ctx.fill();
this.ctx.stroke();
this.ctx.restore();
}
draw() {
super.draw();
if (this.lineDashFlag) {
this.drawLine();
}
}
}
export class HotZoneImg extends MySprite { export class HotZoneImg extends MySprite {
drawFrame() { drawFrame() {
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
<h5 style="margin-left: 2.5%;"> preview: </h5> <h5 style="margin-left: 2.5%;"> preview: </h5>
<div class="preview-box" #wrap> <div class="preview-box" [style.height]="isRotate ? '95vw' : '35vw'" #wrap>
<canvas id="canvas" #canvas></canvas> <canvas id="canvas" #canvas></canvas>
</div> </div>
...@@ -91,19 +91,19 @@ ...@@ -91,19 +91,19 @@
></app-audio-recorder> ></app-audio-recorder>
</div> </div>
<!-- <div *ngIf="customTypeGroupArr[it.gIdx]?.action" style="display: flex;align-items: center; margin-top: 5px">--> <div *ngIf="customTypeGroupArr[it.gIdx]?.action" style="display: flex;align-items: center; margin-top: 5px">
<!-- <app-custom-action--> <app-custom-action
<!-- style="margin: auto"--> style="margin: auto"
<!-- [item]="it ? it['actionData_' + it.gIdx] : {}"--> [item]="it ? it['actionData_' + it.gIdx] : {}"
<!-- [type]="customTypeGroupArr[it.gIdx].action.type"--> [type]="customTypeGroupArr[it.gIdx].action.type"
<!-- [option]="customTypeGroupArr[it.gIdx].action.option"--> [option]="customTypeGroupArr[it.gIdx].action.option"
<!-- (save)="onSaveCustomAction($event, it)">--> (save)="onSaveCustomAction($event, it)">
<!-- ></app-custom-action>--> ></app-custom-action>
<!-- </div>--> </div>
<div *ngIf="customTypeGroupArr[it.gIdx]?.isShowPos" style="display: flex; align-items: center; justify-content: center; margin-top: 5px;"> <div *ngIf="customTypeGroupArr[it.gIdx]?.isShowPos" style="display: flex; align-items: center; justify-content: center; margin-top: 5px;">
x: <input type="text" nz-input [(ngModel)]="it.posX" style="width: 50px; margin-right: 15px;" (blur)="saveItemPos(it)"> x: <input type="text" nz-input [(ngModel)]="it.posX" style="width: 80px; margin-right: 15px;" (blur)="saveItemPos(it)">
y: <input type="text" nz-input [(ngModel)]="it.posY" style="width: 50px" (blur)="saveItemPos(it)"> y: <input type="text" nz-input [(ngModel)]="it.posY" style="width: 80px" (blur)="saveItemPos(it)">
</div> </div>
<div *ngIf="customTypeGroupArr[it.gIdx]?.select" align="center" > <div *ngIf="customTypeGroupArr[it.gIdx]?.select" align="center" >
...@@ -113,28 +113,60 @@ ...@@ -113,28 +113,60 @@
</nz-select> </nz-select>
</div> </div>
<div *ngIf="customTypeGroupArr[it.gIdx]?.checkbox" align="center" style="margin-top: 5px; display: flex; align-items: center; justify-content: center;">
<span style="margin-right: 20px;">{{customTypeGroupArr[it.gIdx].checkbox.title + ':'}}</span>
<div *ngFor="let cItem of it?.checkbox?.list">
<label style="" nz-checkbox [(ngModel)]="cItem.value" (ngModelChange)="checkboxChange(it.checkbox, it)">{{cItem.label}}</label>
</div>
</div>
<div *ngIf="customTypeGroupArr[it.gIdx]?.dropdown" align="center" style="margin-top: 5px; display: flex; align-items: center; justify-content: center;">
<span style="margin-right: 20px;">{{customTypeGroupArr[it.gIdx].dropdown.title + ':'}}</span>
<a nz-dropdown [nzDropdownMenu]="menu">
{{it.dropDownItem ? it.dropDownItem.label : 'select type'}}
<i nz-icon nzType="down"></i>
</a>
<nz-dropdown-menu #menu="nzDropdownMenu">
<ul nz-menu nzSelectable>
<li *ngFor="let cItem of customTypeGroupArr[it.gIdx].dropdown.list" (click)="dropdownClick(cItem, it)" nz-menu-item>
{{cItem.label}}
</li>
</ul>
</nz-dropdown-menu>
</div>
<div *ngIf="customTypeGroupArr[it.gIdx]?.label" align="center" style="margin-top: 5px; display: flex; align-items: center; justify-content: center;"> <div *ngIf="customTypeGroupArr[it.gIdx]?.label" align="center" style="margin-top: 5px; display: flex; align-items: center; justify-content: center;">
<span style="width: 30%;">{{customTypeGroupArr[it.gIdx].label + ':'}}</span> <span style="width: 30%;">{{customTypeGroupArr[it.gIdx].label + ':'}}</span>
<input type="text" nz-input [(ngModel)]="it.labelText" (blur)="saveText(it)"> <input type="text" nz-input [(ngModel)]="it.labelText" (blur)="saveText(it)">
</div> </div>
<!-- <div *ngIf="customTypeGroupArr[it.gIdx]?.mathLabel" align="center" style="margin-top: 5px; display: flex; align-items: center; justify-content: center;">--> <div *ngIf="customTypeGroupArr[it.gIdx]?.mathLabel" align="center" style="margin-top: 5px; display: flex; align-items: center; justify-content: center;">
<!-- <span style="width: 30%;">{{customTypeGroupArr[it.gIdx].mathLabel + ':'}}</span>--> <span style="width: 30%;">{{customTypeGroupArr[it.gIdx].mathLabel + ':'}}</span>
<!-- <app-formula-input [(ngfModel)]="it.mathLabel" ></app-formula-input>--> <app-formula-input [(ngfModel)]="it.mathLabel" ></app-formula-input>
</div>
<!-- </div>--> <div *ngIf="customTypeGroupArr[it.gIdx]?.multRect" align="center" style="margin-top: 5px; display: flex; align-items: center; justify-content: center;">
<app-mult-rect
[rectArr]="it.multRect || []"
(save)="onMultRectSave($event, it)"
>
</app-mult-rect>
</div>
<div *ngIf="customTypeGroupArr[it.gIdx]?.isCopy" align="center" style="margin-top: 5px; display: flex; align-items: center; justify-content: center;"> <div *ngIf="customTypeGroupArr[it.gIdx]?.isCopy" align="center" style="margin-top: 5px; display: flex; align-items: center; justify-content: center;">
<button nz-button (click)="copyItem(it)" > <button nz-button (click)="copyItem(it)" >
<i nz-icon nzType="copy" nzTheme="outline"></i> <i nz-icon nzType="copy" nzTheme="outline"></i>
复制粘贴 复制粘贴
</button> </button>
</div> </div>
</div> </div>
<!-- <div *ngIf="!customTypeGroupArr"> <!-- <div *ngIf="!customTypeGroupArr">
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
border: 2px solid #ddd; border: 2px solid #ddd;
.preview-box { .preview-box {
margin: auto; margin: auto;
......
<nz-input-group [nzSuffix]="suffixTemplate">
<input type="text"
#inputEl
(focus)="showFormulaPad($event)"
nz-input [(ngModel)]="ngfModel"
(ngModelChange)="valueChanged($event)" />
</nz-input-group>
<ng-template #suffixTemplate>
<i nz-icon [nzType]="'percentage'" (click)="showFormulaPad($event)"></i>
</ng-template>
<!-- cdkDrag-->
<!--<div style='position: absolute;visibility: hidden' class="FormulaBoardContainer" #wrapperEl >-->
<!--</div>-->
@import '../../style/common_mixin.css';
:host{
display: inline-block;
}
import {
AfterViewInit,
Component,
ElementRef, EventEmitter,
Input,
OnChanges,
OnDestroy,
OnInit, Output,
ViewChild
} from '@angular/core';
import {fromEvent, Observable, Subscription} from 'rxjs';
declare global {
interface Window {
FBOARD: any;
FormulaBoard: any;
__initfp__: any;
__tmp_fp_input_model__: any;
__FormulaBoard_js_installed__: any;
}
}
@Component({
selector: 'app-formula-input',
templateUrl: './formula-input.component.html',
styleUrls: ['./formula-input.component.scss']
})
export class FormulaInputComponent implements OnInit, OnChanges, OnDestroy, AfterViewInit {
@ViewChild('inputEl', {static: true }) inputEl: ElementRef;
// @ViewChild('wrapperEl', {static: true }) wrapperEl: ElementRef;
private fboard: any;
// [(ngModel)]="item.title" (blur)="save()"
@Input() ngfModel: any;
@Output() ngfModelChange = new EventEmitter();
@Output() ngfBlur = new EventEmitter();
FB_id = 'FormulaBoard';
private esc: Subscription;
constructor() {
if (!window.__FormulaBoard_js_installed__) {
window.__FormulaBoard_js_installed__ = true;
const s = document.createElement('script');
s.src = 'assets/formula-board/formula-board.js';
document.head.append(s);
s.addEventListener('load', () => {
this.FormulaBoardInit();
});
}
}
ngOnInit() {
}
ngOnChanges() {
}
ngOnDestroy(): void {
}
valueChanged(evt) {
console.log('valueChanged', this.ngfModel, evt);
this.ngfModelChange.emit(evt);
}
showFormulaPad(e) {
// console.log('showFormulaPad', e);
window.FBOARD.set(e.target.value);
window.__tmp_fp_input_model__ = this;
const rect = this.inputEl.nativeElement.getBoundingClientRect();
const fb = document.getElementById(this.FB_id);
fb.style.position = 'fixed';
// @ts-ignore
const padWidth = $('#' + this.FB_id).outerWidth() * .8;
// @ts-ignore
const padHeight = $('#' + this.FB_id).outerHeight() * .8;
// @ts-ignore
const ww = $(window).innerWidth();
// @ts-ignore
const hh = $(window).innerHeight();
const left = (ww - padWidth) / 2;
const top = (hh - padHeight) / 2;
console.log('left', left);
fb.style.left = left + 'px';
fb.style.top = top + 'px';
fb.style.visibility = 'visible';
}
updateValue(val) {
this.ngfModel = val;
this.ngfModelChange.emit(val);
}
ngAfterViewInit() {}
FormulaBoardInit() {
// if (window.__initfp__) {
// return;
// }
// window.__initfp__ = true;
window.FBOARD = new window.FormulaBoard({
// container: this.wrapperEl.nativeElement,
id: this.FB_id,
path: 'assets/formula-board/',
options: {
width: 1920,
mode: 'pc',
bottom: false,
}});
document.addEventListener('documentMessage', (e) => {
// @ts-ignore
const {type} = e.detail;
if (type === 'common.setFormula') {
// @ts-ignore
const {formula} = e.detail.data.body;
console.log(formula);
window.FBOARD.clear();
// @ts-ignore
window.__tmp_fp_input_model__.updateValue(formula);
window.FBOARD.editor.style.visibility = 'hidden';
this.ngfBlur.emit();
}
if (type === 'common.closeModal') {
window.FBOARD.editor.style.visibility = 'hidden';
}
});
}
}
<div >
<button nz-button (click)="setRectBtnClick()" style=" border-radius: 0.5rem; border: 1px solid #ddd;">
<i nz-icon nzType="tool" nzTheme="outline"></i>
{{btnName}}
</button>
<!--配置龙骨面板-->
<nz-modal [(nzVisible)]="panelVisible" (nzAfterClose)="panelCancel()" nzTitle="配置多矩形集合"
(nzOnCancel)="panelCancel()" (nzOnOk)="panelOk()" nzOkText="保存">
<div *ngFor="let r of rectArr; let i = index" style="margin-top:5px; width:100%; height:40px; border-radius: 0.5rem; border: solid 2px #ccc;
display: flex; align-items:center; justify-content:space-between">
<div>
<span style="margin-left: 10px; font-weight: 600; font-size: 16px " >
{{'rect-' + (i+1)}}
</span>
</div>
<div>
<nz-radio-group [ngModel]="r.color" (ngModelChange)="radioChange($event, r)" style="display: flex; align-items: center; justify-content: center; flex-wrap: wrap;">
<div *ngFor="let colorData of colorArr; let j = index" style="display: flex; ">
<label nz-radio nzValue="{{colorData.color}}">{{colorData.name}}</label>
</div>
</nz-radio-group>
</div>
<div>
<button nz-button (click)="deleteRect(r)" nzType="danger" style=" margin-right: 5px;">
X
</button>
</div>
</div>
<button nz-button (click)="addRect()" nzType="dashed" style="margin-top: 20px; ">
<i nz-icon nzType="plus-circle" nzTheme="outline"></i>
添加矩形
</button>
<!-- <div class="anima-upload-btn">
<span style="margin-right: 10px">上传 ske_json 文件: </span>
<nz-upload [nzShowUploadList]="false" nzAccept="application/json" [nzAction]="uploadUrl" [nzData]="uploadData"
(nzChange)="skeJsonHandleChange($event)">
<button nz-button><i nz-icon nzType="upload"></i><span>Upload</span></button>
</nz-upload>
<i *ngIf="isSkeJsonLoading" style="margin-left: 10px;" nz-icon [nzType]="'loading'"></i>
<span *ngIf="skeJsonData && skeJsonData['name']" style="margin-left: 10px"><u> {{skeJsonData['name']}} </u></span>
</div>
<div class="anima-upload-btn">
<span style="margin-right: 10px">上传 tex_json 文件: </span>
<nz-upload [nzShowUploadList]="false" nzAccept="application/json" [nzAction]="uploadUrl" [nzData]="uploadData"
(nzChange)="texJsonHandleChange($event)">
<button nz-button><i nz-icon nzType="upload"></i><span>Upload</span></button>
</nz-upload>
<i *ngIf="isTexJsonLoading" style="margin-left: 10px;" nz-icon [nzType]="'loading'"></i>
<span *ngIf="texJsonData && texJsonData['name']" style="margin-left: 10px"><u> {{texJsonData['name']}} </u></span>
</div>
<div class="anima-upload-btn">
<span style="margin-right: 10px">上传 tex_png 文件: </span>
<nz-upload [nzShowUploadList]="false" nzAccept="image/*" [nzAction]="uploadUrl" [nzData]="uploadData"
(nzChange)="texPngHandleChange($event)">
<button nz-button><i nz-icon nzType="upload"></i><span>Upload</span></button>
</nz-upload>
<i *ngIf="isTexPngLoading" style="margin-left: 10px;" nz-icon [nzType]="'loading'"></i>
<span *ngIf="texPngData && texPngData['name']" style="margin-left: 10px"><u> {{texPngData['name']}} </u></span>
</div>
<div class="anima-upload-btn" *ngIf="animaNames && animaNames.length > 0">
提示:需包含动画: {{animaNames.toString()}}.
</div> -->
</nz-modal>
</div>
<div style="justify-content:space-between">
</div>
\ No newline at end of file
import {ApplicationRef, Component, EventEmitter, Input, OnChanges, OnDestroy, Output} from '@angular/core';
import { NzMessageService, UploadXHRArgs, UploadFile } from 'ng-zorro-antd';
@Component({
selector: 'app-mult-rect',
templateUrl: './mult-rect.component.html',
styleUrls: ['./mult-rect.component.scss']
})
export class MultRectComponent implements OnDestroy, OnChanges {
uploading = false;
progress = 0;
@Input()
btnName = '配置多矩形集合';
@Input()
animaNames = [];
@Input()
rectArr = [];
@Output()
save = new EventEmitter();
@Output()
refreshEmitter = new EventEmitter();
uploadUrl;
uploadData;
panelVisible = false;
colorArr = [
{name: 'red', color: '#ff0000'},
{name: 'green', color: '#00ff00'},
{name: 'blue', color: '#0000ff'},
{name: 'black', color: '#000000'},
]
constructor(private appRef: ApplicationRef, private nzMessageService: NzMessageService) {
// this.uploadUrl = (<any> window).courseware.uploadUrl();
// this.uploadData = (<any> window).courseware.uploadData();
// window['air'].getUploadCallback = (url, data) => {
// this.uploadUrl = url;
// this.uploadData = data;
// };
}
ngOnChanges() {
}
radioChange(e, item) {
console.log('e: ', e);
item.color = e;
}
addRect() {
this.rectArr.push(
{
color: this.colorArr[0].color,
width: 100,
height: 100,
x: -1,
y: -1,
}
)
}
deleteRect(item) {
const index = this.rectArr.indexOf(item);
if (index != -1) {
this.rectArr.splice(index, 1);
}
}
/**
* 刷新 渲染页面
*/
refresh() {
// this.refreshEmitter.emit();
setTimeout(() => {
this.appRef.tick();
}, 1);
}
setRectBtnClick() {
this.panelVisible = true;
}
panelOk() {
this.save.emit(this.rectArr)
this.panelVisible = false;
}
panelCancel() {
this.panelVisible = false;
this.refresh();
}
ngOnDestroy() {
}
}
<div class="content">
<div style="display: flex; margin-bottom: 5px;">
<button nz-button nzType="dashed" (click)="selectSubTemplate()" style=" border-radius: 0.5rem; border: 1px solid #ddd;">
<i nz-icon nzType="appstore" nzTheme="outline"></i>
选择子模板类型
</button>
<h2 *ngIf="_item.template" style="margin-left: 10px; margin-top: 1px;">
{{_item.template.description}}
</h2>
<button nz-button nzType="danger" (click)="deleteSubTemplate()" style="position: absolute; right: 30px; border-radius: 0.5rem; border: 1px solid #ddd;">
<i nz-icon nzType="delete" nzTheme="outline"></i>
删除
</button>
</div>
<iframe #iframe id="iframe" [src]="safeUrl" style="width: 98%; height: 70vh; border: 1px solid #ccc; padding: 5px;" frameborder="0">
</iframe>
<nz-modal [(nzVisible)]="isShowTemplate" (nzAfterClose)="closePanel()" nzTitle="选择子模板" [nzFooter]="null"
(nzOnCancel)="panelCancelClicked()" nzWidth="80%" nzHeight="80%">
<div class="template-container">
<div *ngFor="let template of templateArr" >
<div (click)="templateClick(template)" style="width: 150px; height: 100px; border: 1px solid #ccc; margin: 5px; cursor:pointer; user-select: none;">
<div style="width: 100%; height: 70%; border-bottom: 1px solid #ccc; overflow: hidden;">
<img [src]="template.cover" style="width: 100%; height: auto;">
</div>
<div style="width: 100%; height: 30%; display: flex; align-items: center; justify-content: center; ">
<label style="width: 90%; text-align: center; text-overflow: ellipsis; white-space:nowrap; overflow: hidden; ">{{template.description}}</label>
</div>
</div>
</div>
</div>
</nz-modal>
</div>
\ No newline at end of file
@import '../../style/common_mixin.css';
.content {
// width: 100%;
height: 100%;
border: 2px solid #ccc;
padding: 10px;
border-radius: 5px;
}
.template-container{
width: 100%;
display: flex;
// align-items: center;
// justify-content: space-around;
flex-wrap: wrap;
}
\ No newline at end of file
<div class="position-relative" (drop)="handle_drop($event)" (dragover)="handle_dragover($event)"> <div class="position-relative">
<button nz-button (click)="setAnimaBtnClick()" style=" border-radius: 0.5rem; border: 1px solid #ddd;" (dragover)="handle_dragover($event)"> <button nz-button (click)="setAnimaBtnClick()" style=" border-radius: 0.5rem; border: 1px solid #ddd;">
<i nz-icon nzType="tool" nzTheme="outline"></i> <i nz-icon nzType="tool" nzTheme="outline"></i>
{{btnName}} {{btnName}}
</button> </button>
......
...@@ -178,43 +178,4 @@ export class UploadDragonBoneComponent implements OnDestroy, OnChanges { ...@@ -178,43 +178,4 @@ export class UploadDragonBoneComponent implements OnDestroy, OnChanges {
ngOnDestroy() { ngOnDestroy() {
} }
linkInputed (ske, tex, png) {
this.skeJsonData["url"] = ske.url;
this.skeJsonData["name"] = ske.name;
this.texJsonData["url"] = tex.url;
this.texJsonData["name"] = tex.name;
this.texPngData["url"] = png.url;
this.texPngData["name"] = png.name;
this.animaPanelOk();
}
handle_dragover(e) {
e.preventDefault();
}
handle_drop(e) {
e.preventDefault();
const dt = e.dataTransfer.getData("text/plain");
console.log("handle_drop===", dt);
if (!dt) {
return;
}
try {
const {ske, tex, png} = JSON.parse(dt);
if (!ske || !tex || !png ||
ske.url.indexOf("teach")<0 || ske.url.indexOf("cdn")<0 || ske.url.indexOf(".json")<0 ||
tex.url.indexOf("teach")<0 || tex.url.indexOf("cdn")<0 || tex.url.indexOf(".json")<0 ||
png.url.indexOf("teach")<0 || png.url.indexOf("cdn")<0 || png.url.indexOf(".png")<0 ||
!ske.name || !tex.name || !png.name
) {
console.warn("handle_drop拖拽在线骨骼动画传递参数不合法,应该是{ske:{url:'', name:''},tex:{url:'', name:''},png:{url:'', name:''}}");
return;
}
this.linkInputed(ske, tex, png);
} catch (error) {
console.warn("handle_drop拖拽在线骨骼动画传递参数不合法,应该是{ske:{url:'', name:''},tex:{url:'', name:''},png:{url:'', name:''}}");
}
}
} }
<div class="position-relative" (drop)="handle_drop($event)" (dragover)="handle_dragover($event)"> <div class="position-relative">
<nz-upload class="p-image-uploader" [nzDisabled]="disableUpload" <nz-upload class="p-image-uploader" [nzDisabled]="disableUpload"
[nzShowUploadList]="false" [nzShowUploadList]="false"
nzAccept = "image/*" nzAccept = "image/*"
[nzAction]="uploadUrl" [nzAction]="uploadUrl"
[nzData]="uploadData" [nzData]="uploadData"
(nzChange)="handleChange($event)" (nzChange)="handleChange($event)">
>
<!--[nzBeforeUpload]="customUpload">--> <!--[nzBeforeUpload]="customUpload">-->
<div class="p-box d-flex align-items-center"> <div class="p-box d-flex align-items-center">
<div class="p-upload-icon" *ngIf="!picUrl && !uploading"> <div class="p-upload-icon" *ngIf="!picUrl && !uploading">
......
...@@ -42,7 +42,28 @@ export class UploadImageWithPreviewComponent implements OnDestroy, OnChanges { ...@@ -42,7 +42,28 @@ export class UploadImageWithPreviewComponent implements OnDestroy, OnChanges {
this.uploadData = data; this.uploadData = data;
}; };
this.setUploadUrl();
} }
setUploadUrl() {
if (!this.uploadUrl) {
console.log('this.uploadUrl -=- 1 : ', this.uploadUrl)
this.uploadUrl = (<any> window).courseware.uploadUrl();
this.uploadData = (<any> window).courseware.uploadData();
console.log('this.uploadUrl -=- 2 : ', this.uploadUrl)
setTimeout(() => {
this.setUploadUrl();
}, 500);
}
console.log('this.uploadUrl -=- 3 : ', this.uploadUrl)
}
ngOnChanges() { ngOnChanges() {
if (!this.picItem) { if (!this.picItem) {
return; return;
...@@ -155,39 +176,4 @@ export class UploadImageWithPreviewComponent implements OnDestroy, OnChanges { ...@@ -155,39 +176,4 @@ export class UploadImageWithPreviewComponent implements OnDestroy, OnChanges {
ngOnDestroy() { ngOnDestroy() {
} }
linkInputed (url) {
const file = {
response: {url}
};
const img = new Image();
img.addEventListener('load', () => {
const height = img.naturalHeight;
const width = img.naturalWidth;
file['height'] = height;
file['width'] = width;
img.remove();
this.imageUploaded.emit(file.response);
});
img.src = url;
this.picUrl = url;
}
handle_dragover(e) {
e.preventDefault();
}
handle_drop(e) {
const dt = e.dataTransfer.getData("text/plain");
console.log("handle_drop===", dt);
if (!dt || dt.indexOf("teach")<0 || dt.indexOf("cdn")<0) {
return;
}
const white = [".jpg", ".jpeg", ".png", ".bmp", ".JPG", ".JPEG", ".PNG", ".BMP"];
if (!white.includes(dt.substr(dt.lastIndexOf(".")))) {
return;
}
this.linkInputed(dt);
}
} }
<div class="p-video-box" (drop)="handle_drop($event)" (dragover)="handle_dragover($event)"> <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="" [nzDisabled]="!showUploadBtn"-->
<!--[nzShowUploadList]="false"--> <!--[nzShowUploadList]="false"-->
<!--[nzBeforeUpload]="beforeUpload"--> <!--[nzBeforeUpload]="beforeUpload"-->
<!--nzAccept = ".mp4"--> <!--nzAccept = ".mp4"-->
<!--style="margin-right: 1rem">--> <!--style="margin-right: 1rem">-->
<nz-upload class="p-image-uploader" [nzDisabled]="uploading" [nzShowUploadList]="false" nzAccept=".mp4" <nz-upload class="p-image-uploader" [nzDisabled]="uploading"
[nzAction]="uploadUrl" [nzData]="uploadData" (nzChange)="handleChange($event)" style="margin-right: 1rem"> [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>--> <!--<span>Select Video</span>-->
...@@ -20,14 +25,14 @@ ...@@ -20,14 +25,14 @@
</nz-upload> </nz-upload>
<!--<button nz-button nzType="primary" *ngIf="showUploadBtn"--> <!--<button nz-button nzType="primary" *ngIf="showUploadBtn"-->
<!--style="margin-right: 1rem"--> <!--style="margin-right: 1rem"-->
<!--type="button"--> <!--type="button"-->
<!--[nzLoading]="uploading"--> <!--[nzLoading]="uploading"-->
<!--(click)="handleUpload()"--> <!--(click)="handleUpload()"-->
<!--[disabled]="!videoFile || uploading">--> <!--[disabled]="!videoFile || uploading">-->
<!--<i nz-icon type="upload" theme="outline"></i>--> <!--<i nz-icon type="upload" theme="outline"></i>-->
<!--<span>{{ uploading ? 'Uploading' : 'Start Upload' }}</span>--> <!--<span>{{ uploading ? 'Uploading' : 'Start Upload' }}</span>-->
<!--</button>--> <!--</button>-->
<!-- <!--
...@@ -49,37 +54,41 @@ ...@@ -49,37 +54,41 @@
--> -->
</div> </div>
<div class="p-box d-flex align-items-center p-video-uploader"> <div class="p-box d-flex align-items-center p-video-uploader">
<div class="p-upload-icon" *ngIf="!showUploadBtn && !videoUrl && !uploading"> <div class="p-upload-icon" *ngIf="!showUploadBtn && !videoUrl && !uploading">
<i nz-icon nzType="upload" nzTheme="outline"></i> <i nz-icon nzType="upload" nzTheme="outline"></i>
<div class="m-3"></div> <div class="m-3"></div>
<span>Click here to upload video</span> <span>Click here to upload video</span>
<div class="mt-5 p-progress-bar" *ngIf="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-bg" [style.width]="progress*0.2+'rem'"></div>
<div class="p-progress-value">{{progress}}%</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-icon icon="cloud-upload-alt"></fa-icon>
Uploading...
</div>
</div> </div>
<div class="p-upload-check-bg" *ngIf="checking"> </div>
<div class="i-bg" [style.width]="progress+'%'"></div> <div class="p-upload-progress-bg dddd " *ngIf="uploading"
<div class="i-text"> [ngClass]="{'smart-bar': showUploadBtn}" >
<fa-icon icon="cloud-upload-alt"></fa-icon> <div class="i-bg" [style.width]="progress+'%'"></div>
<i nz-icon nzType="loading" nzTheme="outline"></i>Checking... <div class="i-text">
</div> <fa-icon icon="cloud-upload-alt"></fa-icon>
Uploading...
</div> </div>
<div class="p-preview" *ngIf="!uploading && videoUrl "> </div>
<!--<video crossorigin="anonymous" [src]="videoUrl" controls #videoNode></video>--> <div class="p-upload-check-bg" *ngIf="checking">
<video [src]="safeVideoUrl(videoUrl)" controls #videoNode></video> <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> </div>
<div [style.display]="!checkVideoExists?'none':''"> <div class="p-preview" *ngIf="!uploading && videoUrl " >
<span><i nz-icon nzType="loading" nzTheme="outline"></i> checking file to upload</span> <video crossorigin="anonymous" [src]="videoUrl" controls #videoNode></video>
<!-- <video [src]="safeVideoUrl(videoUrl)" controls #videoNode></video> -->
</div> </div>
</div> </div>
\ No newline at end of file <div [style.display]="!checkVideoExists?'none':''">
<span><i nz-icon nzType="loading" nzTheme="outline"></i> checking file to upload</span>
</div>
</div>
...@@ -80,62 +80,43 @@ export class UploadVideoComponent implements OnChanges, OnDestroy { ...@@ -80,62 +80,43 @@ export class UploadVideoComponent implements OnChanges, OnDestroy {
URL.revokeObjectURL(this.videoUrl); URL.revokeObjectURL(this.videoUrl);
} }
httpHeadCall(requsetUrl: string, callback) {
let xhr = new XMLHttpRequest();
console.log("Status: Send Post Request to " + requsetUrl);
try {
xhr.onreadystatechange = () => {
try {
console.log('xhr.readyState: ', xhr.readyState);
if (xhr.readyState == 4) {
if ((xhr.status >= 200 && xhr.status < 400)) {
callback(true);
} else {
callback(false);
}
}
} catch (e) {
console.log(e)
}
};
xhr.open("HEAD", requsetUrl, true);
xhr.send();
xhr.timeout = 15000;
xhr.onerror = (e) => {
callback(false);
};
xhr.ontimeout = (e) => {
callback(false);
};
} catch (e) {
console.log("Send Get Request error: ", e)
}
}
safeVideoUrl(url) { safeVideoUrl(url) {
const _url = url.replace("_l.", "."); console.log(url);
return this.sanitization.bypassSecurityTrustResourceUrl(_url); // `${url}`; return this.sanitization.bypassSecurityTrustResourceUrl(url); // `${url}`;
} }
videoLoadedMetaData() { videoLoadedMetaData() {
} }
handleChange(info: UploadChangeParam): void { handleChange(info: UploadChangeParam/* { type: string, file: UploadFile, event: any }*/): void {
console.log('info:' , info);
switch (info.type) { switch (info.type) {
case 'start': case 'start':
// this.beforeUpload(info.file);
if (!this.checkSelectFile(info.file)) { if (!this.checkSelectFile(info.file)) {
return; return;
} }
this.uploading = true; this.uploading = true;
this.progress = 0; this.progress = 0;
break; break;
case 'success': case 'success':
let url = info.file.response.url;
url = url.substring(0, url.lastIndexOf(".")) + "_l.mp4";
info.file.response.url = url;
this.uploadSuccess(info.file); this.uploadSuccess(info.file);
// this.beforeUpload(info.file);
// this.uploadSuccess(info.file);
break; break;
case 'progress': case 'progress':
this.progress = info.event.percent; this.progress = info.event.percent;
...@@ -144,6 +125,7 @@ export class UploadVideoComponent implements OnChanges, OnDestroy { ...@@ -144,6 +125,7 @@ export class UploadVideoComponent implements OnChanges, OnDestroy {
} }
} }
checkSelectFile(file) { checkSelectFile(file) {
if (!file.lastModified) { if (!file.lastModified) {
return false; return false;
...@@ -225,55 +207,5 @@ export class UploadVideoComponent implements OnChanges, OnDestroy { ...@@ -225,55 +207,5 @@ export class UploadVideoComponent implements OnChanges, OnDestroy {
// console.log(Math.floor(p * 100)); // console.log(Math.floor(p * 100));
this.progress = Math.floor(p * 100); this.progress = Math.floor(p * 100);
} }
linkInputed (url, name) {
const file = {
response: {url}
};
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);
});
vid.src = file.response.url;
this.videoUrl = url;
}
handle_dragover(e) {
e.preventDefault();
}
handle_drop(e) {
const dt = e.dataTransfer.getData("text/plain");
console.log("handle_drop===", dt);
if (!dt) {
return;
}
try {
const {url, name} = JSON.parse(dt);
if (url.indexOf("teach")<0 || url.indexOf("cdn")<0) {
return;
}
const white = [".mp4"];
if (!white.includes(url.substr(url.lastIndexOf(".")))) {
return;
}
this.linkInputed(url, name);
} catch (error) {
console.warn("handle_drop拖拽在线视频传递参数不合法,应该是{url:'', name:''}");
}
}
} }
import { ApplicationRef, ChangeDetectorRef, ElementRef, ViewChild } from "@angular/core";
export class ComponentBase {
// 储存数据用
saveKey = "";
// 储存对象
item: any = {};
ngOnChanges() { }
ngOnDestroy() { }
constructor(private appRef: ApplicationRef, private 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) {
item[key[i]] = e.url;
this.save();
return;
}
item = item[key[i]];
}
}
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() { }
}
@import '../style/common_mixin.css'; @import '../style/common_mixin.css';
.FormulaBoardContainer{
display: inline-block;
}
.model-content { .model-content {
width: 100%; width: 100%;
height: 100%; height: 100%;
...@@ -33,30 +35,3 @@ ...@@ -33,30 +35,3 @@
padding: 10px; padding: 10px;
margin: 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;
}
\ No newline at end of file
<div class="model-content"> <div class="model-content">
<div style="padding: 10px;">
<div class="border" style="width: 1000px;">
<span style="font-size: 20px;">标题: </span>
<input type="text" nz-input [(ngModel)]="item.title" (blur)="save()">
<span style="font-size: 20px;">题目说明: </span>
<input type="text" nz-input [(ngModel)]="item.questionText" (blur)="save()">
<span style="font-size: 20px;">题目说明音频: </span>
<app-audio-recorder
[audioUrl]="item.questionTextAudio"
(audioUploaded)="onAssetUploadSuccess($event, 'questionTextAudio')"
[_audioName]="item.audioFileName"
(audioName)="saveAudioFileName($event)"
></app-audio-recorder>
</div>
<div class="border" style="width: 1000px;"> <!-- <app-formula-input style="position: absolute;right:0;" [(ngfModel)]="item.title" (ngfBlur)="save()"></app-formula-input><br>
<div *ngFor="let question of item.questions; let i = index"> <app-formula-input [(ngfModel)]="item.title" (ngfBlur)="save()"></app-formula-input><br> -->
<div class="border" style="width: 950px;"> <div style="padding: 20px;">
<span style="font-size: 20px;">选项类型: </span> <h2>页面参数</h2>
<div *ngIf="question.type=='img'"> <div style="font-size: 16px; border: 2px solid #ccc; border-radius: 10px; padding: 10px; width: 500px;">
<button class="button-right">图片</button> <div style="display: flex; justify-content: center; align-items: center;">
&nbsp; <span style="width: 120px; margin-right: 20px;" align="right">page_id: </span>
<button class="button-disable" (click)="question.type='txt'; save();">文字</button> <input type="text" nz-input [(ngModel)]="item.page_id" (blur)="save()" style="margin-top: 5px">
</div> </div>
<div *ngIf="question.type=='txt'"> <div style="display: flex; justify-content: center; align-items: center;">
<button class="button-disable" (click)="question.type='img'; save();">图片</button> <span style="width: 120px; margin-right: 20px;" align="right">book_id: </span>
&nbsp; <input type="text" nz-input [(ngModel)]="item.book_id" (blur)="save()" style="margin-top: 5px">
<button class="button-right">文字</button>
</div>
<div *ngIf="question.type=='img'">
图片:
<div style="width: 300px;">
<app-upload-image-with-preview [picUrl]="question.image"
(imageUploaded)="onAssetUploadSuccess($event, 'questions', i, 'image')">
</app-upload-image-with-preview>
</div>
</div>
<span>音频: </span>
<app-audio-recorder [audioUrl]="question.audio"
(audioUploaded)="onAssetUploadSuccess($event, 'questions', i, 'audio')">
</app-audio-recorder>
<div *ngIf="question.type=='txt'">
<span>文本: </span>
<input type="text" nz-input [(ngModel)]="question.text" (blur)="save()">
</div>
<br>
<button style="width: 900px; height: 30px; color: red;" (click)="removeQuestion(i)">删除题目</button>
</div>
</div> </div>
<div class="border" style="width: 950px;"> <div style="display: flex; justify-content: center; align-items: center;">
<button style="width: 900px; height: 100px;" (click)="addQuestion()">增加题目</button> <span style="width: 120px; margin-right: 20px;" align="right">part_name: </span>
<input type="text" nz-input [(ngModel)]="item.part_name" (blur)="save()" style="margin-top: 5px">
</div> </div>
</div> </div>
<h2 style="margin-top: 20px">音频配置:</h2>
<div style="margin: 30px;"></div>
<h2 >热区配置:</h2>
<app-custom-hot-zone
[bgItem]="item.bgItem"
[hotZoneItemArr]="item.hotZoneItemArr"
[customTypeGroupArr]="customTypeGroupArr"
(save)="saveHotZone(item, $event)"
>
</app-custom-hot-zone>
<div style="margin-top: 30px; width: 200px; height: 50px; border: 1px solid #ccc ; border-radius: 5px; display: flex; align-items: center; justify-content: center;">
<label nz-checkbox [(ngModel)]="item.isDebug" (ngModelChange)="save()">调试模式</label>
</div>
</div> </div>
</div> </div>
\ No newline at end of file
import { Component, EventEmitter, Input, OnDestroy, OnChanges, OnInit, Output, ApplicationRef, ChangeDetectorRef } from '@angular/core'; import {
Component,
EventEmitter,
Input,
OnDestroy,
OnChanges,
OnInit,
Output,
ApplicationRef,
ChangeDetectorRef,
AfterViewInit
} from '@angular/core';
import { JsonPipe } from '@angular/common'; import { JsonPipe } from '@angular/common';
import { ComponentBase } from './ComponentBase'; import { removeItemFromArr } from '../play/Unit';
@Component({ @Component({
...@@ -8,51 +19,160 @@ import { ComponentBase } from './ComponentBase'; ...@@ -8,51 +19,160 @@ import { ComponentBase } from './ComponentBase';
templateUrl: './form.component.html', templateUrl: './form.component.html',
styleUrls: ['./form.component.css'] styleUrls: ['./form.component.css']
}) })
export class FormComponent extends ComponentBase implements OnInit, OnChanges, OnDestroy { export class FormComponent implements OnInit, OnChanges, OnDestroy, AfterViewInit {
// 储存数据用 // 储存数据用
saveKey = "card_machine"; saveKey = "OP72";
// 储存对象 // 储存对象
item: any = { item;
title: '',
questionText: '', customTypeGroupArr = [
questionTextAudio: '', {
audioFileName: '', name: '连接点',
questions: [], rect: true,
}; // isShowPos: true,
isCopy: true,
addQuestion() { isShowPos: true,
this.item.questions.push({ centerDot: true,
type: 'img', // label: '答案',
audio: '', },
text: '', {
image: '' name: '书写区',
}); rect: true,
isShowPos: true,
isCopy: true,
anima: true,
},
{
name: '画圈区',
rect: true,
isShowPos: true,
isCopy: true,
// anima: true,
},
{
name: '音频区',
rect: true,
audio: true,
isCopy: true,
},
{
name: '标记区',
rect: true,
isShowPos: true,
isCopy: true,
anima: true,
},
{
name: '涂色区',
anima: true,
// pic: true,
// isShowPos: true,
isCopy: true,
},
{
name: '书写图片',
pic: true,
// isShowPos: true,
isCopy: true,
},
{
name: '装饰图片',
pic: true,
// isShowPos: true,
isCopy: true,
},
];
constructor(private appRef: ApplicationRef, private changeDetectorRef: ChangeDetectorRef) {
}
ngOnInit() {
this.item = {};
// 获取存储的数据
(<any>window).courseware.getData((data) => {
if (data) {
this.item = data;
}
this.changeDetectorRef.markForCheck();
this.changeDetectorRef.detectChanges();
this.refresh();
}, this.saveKey);
}
ngAfterViewInit() {
}
ngOnChanges() {
}
ngOnDestroy() {
}
saveHotZone(group, e) {
console.log('e: ', e);
const {bgItem, hotZoneItemArr} = e;
group.bgItem = bgItem;
group.hotZoneItemArr = hotZoneItemArr;
this.item.itemCombineGroup = [];
this.save(); this.save();
} }
removeQuestion(idx) { /**
this.item.questions.splice(idx, 1); * 储存图片数据
* @param e
*/
onImageUploadSuccess(e, key, item=null) {
if (!item) {
item = this.item;
}
item[key] = e.url;
this.save(); this.save();
} }
saveAudioFileName(name) { /**
this.item.audioFileName=name; * 储存音频数据
* @param e
*/
onAudioUploadSuccess(e, key, item=null) {
if (item == null) {
item = this.item;
}
item[key] = e.url;
this.save(); this.save();
} }
handle_dragover(e) { onVideoUploadSuccess(e, item=null) {
e.preventDefault(); console.log(' in onVideoUploadSuccess')
if (!item) {
item = this.item;
}
item.video_url = e.url;
this.save();
} }
handle_drop(e) { /**
e.preventDefault(); * 储存数据
const dt = e.dataTransfer.getData("text/plain"); */
console.log("handle_drop===", dt); save() {
// 网络文件 dt 的数据遵循如下格式 (<any>window).courseware.setData(this.item, null, this.saveKey);
// 图片 "http://staging-teach.cdn.ireadabc.com/f38f9928-c3bd-47f8-9ed1-43c0e03d2f30.png"; this.refresh();
// 音频(JSON字符串) {url: "http://staging-teach.cdn.ireadabc.com/1c8694612563f4e2da707f1f6a37d066_l.mp3", name: "test.mp3"}; console.log('this.item = ' + JSON.stringify(this.item));
// 视频(JSON字符串) {url: "http://staging-teach.cdn.ireadabc.com/c37a3945121274a1f7d95717327539ec_l.mp4", name: "视频.mp4"};
// 骨骼动画(JSON字符串) {"ske":{"url":"http://staging-teach.cdn.ireadabc.com/3d4a36b83aada60709771fbe15e2b6db.json","name":"飞鸟_ske.json"},"tex":{"url":"http://staging-teach.cdn.ireadabc.com/08a676a5f456634f9c6c90e9efd3c2ea.json","name":"飞鸟_tex.json"},"png":{"url":"http://staging-teach.cdn.ireadabc.com/874393ecf5e0cd42a09f5b34d48854d2.png","name":"飞鸟_tex.png"}};
} }
}
\ No newline at end of file /**
* 刷新 渲染页面
*/
refresh() {
setTimeout(() => {
this.appRef.tick();
}, 1);
}
}
This diff is collapsed.
export async function loadFonts (fontName, fontFile) {
// const font = new FontFace(
// "DroidSansFallback",
// "url(" + "../../assets/play/font/DroidSansFallback.ttf" + ")"
// );
try {
console.log(location.href.split("#")[0] + "assets/play/font/" + fontFile);
const font = new FontFace(
fontName,
"url(" + location.href.split("#")[0] + "../../assets/play/font/" + fontFile + ")"
);
console.log('start: ', new Date().getTime())
await font.load();
console.log('end: ', new Date().getTime())
document.fonts.add(font);
document.body.classList.add("fonts-loaded");
} catch (error) {
console.log(error);
}
}
// --------------- custom class --------------------
import { data } from "./words";
export function checkAnswer(type, string, word) {
console.log('word: ', word);
switch (type) {
case 'device':
case 'side':
case 'work':
case 'adj':
return WordData.getInstance().check(type, string);
case 'name':
case 'city':
return checkCity(string);
case 'time':
return checkTime(string);
case 'date':
return checkDate(string);
case 'word':
return checkWord(string, word);
case 'wordList':
return checkWordInList(string, word);
default:
throw "错误的格式";
}
}
function checkCity(string) {
const bigCase = ('ABCDEFGHIJKLMNOPQRSTUVWXYZ').split('');
const smallCase = ('abcdefghijklmnopqrstuvwxyz').split('');
const right = string.split(' ')
.filter(word => word != '')
.every(word => {
return word.split('')
.every((letter, idx) => {
if (idx == 0) {
return bigCase.includes(letter)
&& !smallCase.includes(letter);
} else {
return !bigCase.includes(letter)
&& smallCase.includes(letter);
}
});
});
return {
right: right,
info: '专有名词首字母应该大写。',
}
}
function checkTime(string) {
const rightResult = {
right: true,
info: '',
}
const wrongResult = {
right: false,
info: 'time格式填写错误。正确格式为“时 分”, 例如:ten thirty',
};
if (string.includes(":")) {
const words = string.split(":");
if (words.length != 2) {
return wrongResult;
}
if (words.some(word => word.length > 2)) {
return wrongResult;
}
const hour = parseInt(words[0]);
const minute = parseInt(words[1]);
if (hour < 0 || 12 < hour) {
return wrongResult;
}
if (minute < 0 || 59 < minute) {
return wrongResult;
}
return rightResult;
}
const hours = [
"one", "two", "three", "four", "five", "six",
"seven", "eight", "nine", "ten", "eleven", "twelve"
];
const minutes = [
"o'clock", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten",
"eleven", "twelve", "thirteen", "fourteen", "fifteen", "a quarter", "sixteen", "seventeen", "eighteen", "nineteen", "twenty",
"twenty-one", "twenty-two", "twenty-three", "twenty-four", "twenty-five", "twenty-six", "twenty-seven", "twenty-eight", "twenty-nine", "thirty", "half",
"thirty-one", "thirty-two", "thirty-three", "thirty-four", "thirty-five", "thirty-six", "thirty-seven", "thirty-eight", "thirty-nine", "forty",
"forty-one", "forty-two", "forty-three", "forty-four", "forty-five", "three quarter", "forty-six", "forty-seven", "forty-eight", "forty-nine", "fifty",
"fifty-one", "fifty-two", "fifty-three", "fifty-four", "fifty-five", "fifty-six", "fifty-seven", "fifty-eight", "fifty-nine",
];
const middles = [
'to',
'past'
];
const middleWord = middles.find(word => string.includes(word));
if (middleWord) {
const strings = string.split(middleWord);
if (minutes.some(word => strings[0].trim().toLowerCase() == word)) {
if (hours.some(word => strings[1].trim().toLowerCase() == word)) {
return rightResult;
}
}
return wrongResult;
}
const strings = string.split(' ');
if (hours.some(word => strings[0].trim().toLowerCase() == word)) {
if (minutes.some(word => strings[1].trim().toLowerCase() == word)) {
return rightResult;
}
}
return wrongResult;
}
function checkDate(string: string) {
const wrongResult = {
right: false,
info: 'date格式填写错误。正确格式为“日 月 年”, 例如:15th August 2008',
};
const rightResult = {
right: true,
info: '',
}
let numberList = [];
if (string.includes('.')) {
numberList = string.split('.').map(string => parseInt(string));
} else {
const days1 = [
'none',
'First', 'second', 'third', 'fourth',
'fifth', 'sixth', 'seventh', 'eighth',
'ninth', 'tenth', 'eleventh', 'twelfth',
'thirteenth', 'fourteenth', 'fifteenth', 'sixteenth',
'seventeenth', 'eighteenth', 'nineteenth', 'Twentieth',
'twenty-first', 'twenty-second', 'Twenty-third', 'twenty-fourth',
'Twenty-fifth', 'Twenty-sixth', 'twenty-seventh', 'Twenty-eighth',
'twenty-ninth', 'Thirtieth', 'Thirty-first'
];
const days2 = [
'none',
'1st', '2nd', '3rd', '4th',
'5th', '6th', '7th', '8th',
'9th', '10th', '11th', '12th',
'13th', '14th', '15th', '16th',
'17th', '18th', '19th', '20th',
'21st', '22nd', '23rd', '24th',
'25th', '26th', '27th', '28th',
'29th', '30th', '31st'
]
const months = [
'none',
'January', 'February', 'March', 'April',
'May', 'June', 'July', 'August',
'September', 'October', 'November', 'December'
];
let yearFlg = false;
let monthFlg = false;
let dayFlg = false;
numberList = string.split(' ').map(string => {
if (!monthFlg) {
const month = months.findIndex(str => str == string);
if (month >= 1) {
monthFlg = true;
return month;
}
}
if (!dayFlg) {
let day = days1.findIndex(str => str == string);
if (day >= 1) {
dayFlg = true;
return day;
}
day = days2.findIndex(str => str == string);
if (day >= 1) {
dayFlg = true;
return day;
}
}
if (!yearFlg) {
const year: number = parseInt(string);
if (!isNaN(year)) {
yearFlg = true;
return year;
}
}
});
}
console.log('numberList = ' + JSON.stringify(numberList));
if (numberList.length < 2 || 3 < numberList.length) {
return wrongResult;
}
if (numberList.some(num => !num)) {
// 有null或者undefind
return wrongResult;
}
if (numberList.some(num => num <= 0)) {
// 负数
return wrongResult;
}
let rightFlg = true;
let yearFlg = false;
let monthFlg = false;
let dayFlg = false;
numberList.forEach((number, idx) => {
if (number > 31) {
if (!yearFlg) {
if (idx == 1) {
rightFlg = false;
} else {
yearFlg = true;
}
} else {
rightFlg = false;
}
} else if (number > 12) {
if (!dayFlg) {
dayFlg = true;
} else {
rightFlg = false;
}
} else {
if (!monthFlg) {
monthFlg = true;
} else if (!dayFlg) {
dayFlg = true;
} else {
rightFlg = false;
}
}
});
if (!monthFlg || !dayFlg) {
rightFlg = false;
}
if (rightFlg) {
return rightResult;
}
return wrongResult;
}
function checkWord(string: string, word: string) {
if (string.trim() != word.replace(/_/g, " ").trim()) {
return { right: false, info: word.replace(/_/g, " ").trim() };
}
return { right: true, info: '' };
}
function checkWordInList(string: any, word: any) {
const wordList: Array<string> = word;
if (wordList.includes(string)) {
return {
right: wordList.includes(string),
info: '错误的单词。'
};
} else {
return {
right: false,
info: '错误的单词。'
}
}
}
export class WordData {
static _instance = null;
static getInstance(): WordData {
if (!WordData._instance) {
WordData._instance = new WordData();
}
return WordData._instance;
}
constructor() {
this.device = data.device;
this.side = data.side;
this.work = data.work;
this.adj = data.adj;
}
device = ['TV', 'heater'];
side = ['north'];
work = ['teacher', 'worker'];
adj = ['big', 'small'];
check(type: any, string: any) {
const wrongString = {
device: '电器名错误。',
side: '方向名词错误。',
work: '工作名错误。',
adj: '形容词错误。',
}
return {
right: this[type].includes(string.trim()),
info: wrongString[type],
};
}
}
...@@ -3,6 +3,8 @@ ...@@ -3,6 +3,8 @@
height: 100%; height: 100%;
background: #ffffff; background: #ffffff;
background-size: cover; background-size: cover;
} }
#canvas { #canvas {
...@@ -10,10 +12,28 @@ ...@@ -10,10 +12,28 @@
} }
@font-face @font-face
{ {
font-family: 'BRLNSDB'; font-family: 'BRLNSDB';
src: url("../../assets/font/BRLNSDB.TTF") ; src: url("../../assets/play/font/BRLNSDB_1.TTF")
}
@font-face
{
font-family: 'Aileron-Black';
src: url("../../assets/play/font/Aileron-Black.ttf")
}
@font-face
{
font-family: 'Aileron-Bold';
src: url("../../assets/play/font/Aileron-Bold.ttf")
} }
@font-face
{
font-family: 'DroidSansFallback';
src: url("../../assets/play/font/DroidSansFallback.ttf");
}
\ No newline at end of file
<div class="game-container" #wrap> <span style="font-family:'DroidSansFallback'" ></span>
<canvas id="canvas" #canvas></canvas> <span style="font-family:'Aileron-Black'" ></span>
<span style="font-family:'Aileron-Bold'" ></span>
<ng-template #tpl>
<video></video>
</ng-template>
<div style="display: none" #videoContainer></div>
<div class="game-container" #wrap >
<canvas id="canvas" style="width: 100%; height: 100%;" #canvas></canvas>
</div> </div>
<div id='div_input'>
</div>
<!-- <div style="position: absolute; top: 0">
<label style="font-family: Aileron-Black;"></label>
<label style="font-family: DroidSansFallback;"></label>
</div> -->
This diff is collapsed.
const res = [ const res = [
// ['bg', "assets/play/bg.jpg"], ['op_item', "assets/play/op_item.png"],
['btn_left', "assets/play/btn_left.png"], ['op_item_wrong', "assets/play/op_item_wrong.png"],
['btn_right', "assets/play/btn_right.png"], ['op_item_right', "assets/play/op_item_right.png"],
// ['text_bg', "assets/play/text_bg.png"], ['op_item_normal', "assets/play/op_item_normal.png"],
['btn_close', "assets/play/btn_close.png"],
['bg', "assets/play/bg.jpg"],
['video_bg', "assets/play/video_bg.jpg"],
['btn_replay', "assets/play/btn_replay.png"],
['icon_right', "assets/play/icon_right.png"],
['icon_wrong', "assets/play/icon_wrong.png"],
['answer_normal', "assets/play/answer_normal.png"],
['answer_right', "assets/play/answer_right.png"],
['answer_wrong', "assets/play/answer_wrong.png"],
['btn_video', "assets/play/btn_video.png"],
['btn_write', "assets/play/btn_write.png"],
]; ];
...@@ -12,7 +26,14 @@ const res = [ ...@@ -12,7 +26,14 @@ const res = [
const resAudio = [ const resAudio = [
['click', "assets/play/music/click.mp3"], ['click', "assets/play/music/click.mp3"],
['submit', "assets/play/music/submit.mp3"],
['more', "assets/play/music/more.mp3"],
]; ];
export {res, resAudio}; export {res, resAudio};
This diff is collapsed.
File mode changed from 100644 to 100755
<?xml version="1.0" encoding="UTF-8"?>
<svg width="31px" height="23px" viewBox="0 0 31 23" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 62 (91390) - https://sketch.com -->
<title>up</title>
<desc>Created with Sketch.</desc>
<g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="1-学科键盘" transform="translate(-1779.000000, -808.000000)">
<g id="输入框">
<g id="keyboard_light_landscape" transform="translate(0.000000, 585.000000)">
<g id="编组" transform="translate(1698.750000, 26.250000)">
<g id="上翻" transform="translate(50.625000, 163.125000)">
<g>
<rect id="key-boarder" fill="#313C42" opacity="0" x="0" y="0" width="90" height="90" rx="3.76"></rect>
<path d="M48.0085956,35.2507117 L59.3110016,50.5843863 C60.4979164,52.1946422 60.1130426,54.4324199 58.4513618,55.5826026 C57.8242714,56.0166623 57.072896,56.25 56.3022624,56.25 L33.6974504,56.25 C31.6554049,56.25 30,54.6458257 30,52.6669764 C30,51.920192 30.2407896,51.1920698 30.6887113,50.5843863 L41.9911172,35.2507117 C43.1780321,33.6404558 45.4872751,33.2674929 47.1489558,34.4176757 C47.4809703,34.6474896 47.7714424,34.9289723 48.0085956,35.2507117 Z" id="矩形" fill="#7F8B8F"></path>
</g>
</g>
</g>
</g>
</g>
</g>
</g>
</svg>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<svg width="31px" height="23px" viewBox="0 0 31 23" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 62 (91390) - https://sketch.com -->
<title>down</title>
<desc>Created with Sketch.</desc>
<g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="1-学科键盘" transform="translate(-1779.000000, -954.000000)">
<g id="输入框">
<g id="keyboard_light_landscape" transform="translate(0.000000, 585.000000)">
<g id="编组" transform="translate(1698.750000, 26.250000)">
<g id="下翻" transform="translate(50.625000, 309.375000)">
<g>
<rect id="key-boarder" fill="#313C42" opacity="0" x="0" y="0" width="90" height="90" rx="3.76"></rect>
<path d="M48.0085956,54.7492883 L59.3110016,39.4156137 C60.4979164,37.8053578 60.1130426,35.5675801 58.4513618,34.4173974 C57.8242714,33.9833377 57.072896,33.75 56.3022624,33.75 L33.6974504,33.75 C31.6554049,33.75 30,35.3541743 30,37.3330236 C30,38.079808 30.2407896,38.8079302 30.6887113,39.4156137 L41.9911172,54.7492883 C43.1780321,56.3595442 45.4872751,56.7325071 47.1489558,55.5823243 C47.4809703,55.3525104 47.7714424,55.0710277 48.0085956,54.7492883 Z" id="矩形" fill="#7F8B8F"></path>
</g>
</g>
</g>
</g>
</g>
</g>
</g>
</svg>
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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