diff --git a/form_angular/src/app/form/form.component.css b/form/src/app/form/form.component.css similarity index 52% rename from form_angular/src/app/form/form.component.css rename to form/src/app/form/form.component.css index 17e4315a93ac2f8407a3905cd567995c0a357f5d..5d78cd06c5f047021baddbe66edf8dae0954fb5d 100644 --- a/form_angular/src/app/form/form.component.css +++ b/form/src/app/form/form.component.css @@ -33,30 +33,3 @@ padding: 10px; margin: 10px; } - -.button-right { - border-style: dotted; - border-color: rgb(42, 142, 72); - width: 100px; - height: 50px; - color: rgb(42, 142, 72); - background-color: white; -} - -.button-wrong { - border-style: dotted; - border-color: rgb(180, 0, 0); - width: 100px; - height: 50px; - color: rgb(180, 0, 0); - background-color: white; -} - -.button-disable { - border-style: dotted; - border-color: black; - width: 100px; - height: 50px; - color: black; - background-color: white; -} \ No newline at end of file diff --git a/form/src/app/form/form.component.html b/form/src/app/form/form.component.html new file mode 100644 index 0000000000000000000000000000000000000000..4855aad24209bf9518225bb508d981fb13b5a4fe --- /dev/null +++ b/form/src/app/form/form.component.html @@ -0,0 +1,145 @@ +<div style="margin: 30px;"> + <!-- å•è¯è¡¨å•æ•°æ® --> + <div style="border: 2px #ccc solid; border-radius: 15px; width: 1000px; margin: 30px"> + <div style="padding: 20px; margin-bottom: 20px;"> + <h2>å•è¯è¡¨ç¼–辑</h2> + + <button nz-button nzType="primary" (click)="addNewWord()" style="margin-bottom: 20px;"> + <i nz-icon nzType="plus"></i> æ·»åŠ æ–°å•è¯ + </button> + + <div *ngFor="let word of item.data; let i = index" + style="border: 1px #ccc solid; border-radius: 15px; padding: 20px; margin-bottom: 15px; background-color: rgb(255, 250, 243);"> + + <div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 15px;"> + <h3>å•è¯ #{{i+1}}</h3> + <button nz-button nzType="danger" (click)="deleteWord(i)"> + <i nz-icon nzType="delete"></i> åˆ é™¤ + </button> + </div> + + <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 15px;"> + <!-- 左侧列 --> + <div> + <!-- å•è¯ --> + <div style="margin-bottom: 10px; display: flex; align-items: center;"> + <span style="width: 120px; text-align: right;">å•è¯ï¼š</span> + <input type="text" nz-input [(ngModel)]="word.word" (blur)="save()" style="width: 250px;"> + </div> + + <!-- è¯æ€§ --> + <div style="margin-bottom: 10px; display: flex; align-items: center;"> + <span style="width: 120px; text-align: right;">è¯æ€§ï¼š</span> + <div style="width: 250px;"> + <a nz-dropdown [nzDropdownMenu]="menu"> + {{word.partOfSpeech || 'æ— '}} + <i nz-icon nzType="down"></i> + </a> + <button style="margin-left: 10px;" nz-button (click)="word.partOfSpeech = ''; save()"> + x + </button> + + <nz-dropdown-menu #menu="nzDropdownMenu"> + <ul nz-menu nzSelectable> + <div nz-menu-item *ngFor="let pos of posArr;" align="center" (click)="word.partOfSpeech = pos; save()"> + {{pos}} + </div> + </ul> + </nz-dropdown-menu> + </div> + </div> + + <!-- å•è¯éŸ³é¢‘ --> + <div style="margin-bottom: 10px; display: flex; align-items: center;"> + <span style="width: 120px; text-align: right;">å•è¯éŸ³é¢‘:</span> + <app-audio-recorder [audioUrl]="word.wordAudioUrl" + (audioUploaded)="onItemAudioUploadSuccess($event, word, 'wordAudioUrl')"> + </app-audio-recorder> + </div> + + <!-- 翻译 --> + <div style="margin-bottom: 10px; display: flex; align-items: center;"> + <span style="width: 120px; text-align: right;">å•è¯ç¿»è¯‘:</span> + <input type="text" nz-input [(ngModel)]="word.wordTranslation" (blur)="save()" style="width: 250px;"> + </div> + + + <div style="margin-top: 20px; border-top: 1px dashed #eee; padding-top: 15px;"> + <div style="margin-bottom: 10px; display: flex; align-items: center;"> + <span style="width: 120px; text-align: right;">显示类型:</span> + <div style="width: auto; display: flex; align-items: center;"> + <label style="margin-right: 10px; font-size: 14px;"> + <input type="radio" [(ngModel)]="word.displayType" [value]="1" (change)="save()"> 高亮 + </label> + <label style="margin-right: 10px; font-size: 14px;"> + <input type="radio" [(ngModel)]="word.displayType" [value]="2" (change)="save()"> å•è¯å¡ + </label> + <label style="font-size: 14px;"> + <input type="radio" [(ngModel)]="word.displayType" [value]="3" (change)="save()" checked> 高亮&å•è¯å¡ + </label> + </div> + </div> + </div> + </div> + + <!-- å³ä¾§åˆ— --> + <div> + <!-- ä¾‹å¥ --> + <div style="margin-bottom: 10px; display: flex; align-items: center;"> + <span style="width: 120px; text-align: right;">例å¥ï¼š</span> + <input type="text" nz-input [(ngModel)]="word.exampleSentence" (blur)="save()" style="width: 250px;"> + </div> + + <!-- 例å¥éŸ³é¢‘ --> + <div style="margin-bottom: 10px; display: flex; align-items: center;"> + <span style="width: 120px; text-align: right;">例å¥éŸ³é¢‘:</span> + <app-audio-recorder [audioUrl]="word.exampleSentenceAudioUrl" + (audioUploaded)="onItemAudioUploadSuccess($event, word, 'exampleSentenceAudioUrl')"> + </app-audio-recorder> + </div> + + <!-- 图片 --> + <div style="margin-bottom: 10px; display: flex;"> + <span style="width: 120px; text-align: right;">图片:</span> + <div style="width: 250px;"> + <app-upload-image-with-preview [picUrl]="word.imageUrl" + (imageUploaded)="onItemImgUploadSuccess($event, word, 'imageUrl')"> + </app-upload-image-with-preview> + </div> + <button *ngIf="word.imageUrl" style="margin-left: 10px;" nz-button (click)="onDeletaImg(word, 'imageUrl')"> + x + </button> + </div> + + <!-- å…¶ä»–å¯é€‰å—段 --> + <div style="margin-top: 20px; border-top: 1px dashed #eee; padding-top: 15px;"> + <!-- 英å¼éŸ³æ ‡ --> + <div style="margin-bottom: 10px; display: flex; align-items: center;"> + <span style="width: 120px; text-align: right;">英å¼éŸ³æ ‡ï¼š</span> + <input type="text" nz-input [(ngModel)]="word.britishPhonetic" (blur)="save()" style="width: 250px;"> + </div> + + <!-- 美å¼éŸ³æ ‡ --> + <div style="margin-bottom: 10px; display: flex; align-items: center;"> + <span style="width: 120px; text-align: right;">美å¼éŸ³æ ‡ï¼š</span> + <input type="text" nz-input [(ngModel)]="word.americanPhonetic" (blur)="save()" style="width: 250px;"> + </div> + + <!-- 英文定义 --> + <div style="margin-bottom: 10px; display: flex; align-items: center;"> + <span style="width: 120px; text-align: right;">英文定义:</span> + <input type="text" nz-input [(ngModel)]="word.englishDefinition" (blur)="save()" style="width: 250px;"> + </div> + + <!-- 例å¥ç¿»è¯‘ --> + <div style="margin-bottom: 10px; display: flex; align-items: center;"> + <span style="width: 120px; text-align: right;">例å¥ç¿»è¯‘:</span> + <input type="text" nz-input [(ngModel)]="word.exampleSentenceTranslation" (blur)="save()" style="width: 250px;"> + </div> + </div> + </div> + </div> + </div> + </div> + </div> +</div> \ No newline at end of file diff --git a/form/src/app/form/form.component.ts b/form/src/app/form/form.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..24e87374a9f2d37ef8b7c77cce6df5c2651dcb42 --- /dev/null +++ b/form/src/app/form/form.component.ts @@ -0,0 +1,208 @@ +import { Component, EventEmitter, Input, OnDestroy, OnChanges, OnInit, Output, ApplicationRef, ChangeDetectorRef } from '@angular/core'; +import { JsonPipe } from '@angular/common'; +import { NzMessageService } from 'ng-zorro-antd/message'; + + +@Component({ + selector: 'app-form', + templateUrl: './form.component.html', + styleUrls: ['./form.component.css'] +}) +export class FormComponent implements OnInit, OnChanges, OnDestroy { + + // å‚¨å˜æ•°æ®ç”¨ + saveKey = "learn_word"; + + // 储å˜å¯¹è±¡ + item; + + posArr = [ + 'n.', + 'adj.', + 'v.', + 'pron.', + 'adv.', + 'prep.', + 'conj.', + 'interj.', + 'art.', + 'num.', + 'abbr.', + 'verb phrase', + 'noun phrase', + 'prep phrase', + 'ordinal number', + ]; + + + constructor(private appRef: ApplicationRef, private changeDetectorRef: ChangeDetectorRef, + private message: NzMessageService) { + + } + + + ngOnInit() { + this.item = {}; + + // 获å–å˜å‚¨çš„æ•°æ® + (<any>window).courseware.getData((data) => { + if (data) { + this.item = data; + + // åˆ é™¤å¯èƒ½å˜åœ¨çš„quesArr属性 + if (this.item.quesArr) { + delete this.item.quesArr; + } + } + + this.initItem(); + console.log("this.item: ", JSON.stringify(this.item)); + + this.init(); + this.changeDetectorRef.markForCheck(); + this.changeDetectorRef.detectChanges(); + this.refresh(); + + }, this.saveKey); + } + + initItem() { + // ç¡®ä¿item有æ£ç¡®çš„æ•°æ®ç»“æž„ + if (!this.item.data) { + this.item.data = []; + } + + } + + init() { + // 如果没有任何数æ®ï¼Œå¯ä»¥æ·»åŠ ç¤ºä¾‹æ•°æ® + if (this.item.data.length === 0) { + this.addSampleData(); + } + } + + // æ·»åŠ ç¤ºä¾‹æ•°æ® + addSampleData() { + + this.item.data.push({ + id:'', + pictureBookId: '', + word: '', + displayType: '', + partOfSpeech: '', + exampleSentence: '', + wordAudioUrl: '', + exampleSentenceAudioUrl: '', + wordTranslation: '', + exampleSentenceTranslation: '', + imageUrl: '', + britishPhonetic: '', + americanPhonetic: '', + englishDefinition: '', + backContent: {}, + createdTime: new Date().toISOString() + }); + } + + ngOnChanges() { + } + + ngOnDestroy() { + } + + onItemImgUploadSuccess(e, item, key='imageUrl') { + item[key] = e.url; + this.save(); + } + + onItemAudioUploadSuccess(e, item, key='wordAudioUrl') { + item[key] = e.url; + this.save(); + } + + onDeletaImg(item, key) { + item[key] = ''; + this.save(); + } + + getNewSortItem() { + // 深拷è´item对象 + const newItem = JSON.parse(JSON.stringify(this.item)); + + // åˆ é™¤å¯èƒ½å˜åœ¨çš„quesArr属性 + if (newItem.quesArr) { + delete newItem.quesArr; + } + + return newItem; + } + + /** + * å‚¨å˜æ•°æ® + */ + save() { + // æ›´æ–°dataä¸çš„æ—¶é—´æˆ³ + // if (this.item.data && this.item.data.length > 0) { + // const now = new Date().toISOString(); + // this.item.data.forEach(item => { + // if (!item.createdTime) { + // item.createdTime = now; + // } + // }); + // } + + // åˆ é™¤itemä¸å¯èƒ½å˜åœ¨çš„quesArr属性 + // if (this.item.quesArr) { + // delete this.item.quesArr; + // } + + const newItem = this.getNewSortItem(); + + + (<any>window).courseware.setData(newItem, null, this.saveKey); + + this.refresh(); + console.log('this.item = ' + JSON.stringify(this.item)); + + this.message.create('success', "ä¿å˜æˆåŠŸ"); + } + + /** + * 刷新 æ¸²æŸ“é¡µé¢ + */ + refresh() { + setTimeout(() => { + this.appRef.tick(); + }, 1); + } + + // æ·»åŠ æ–°å•è¯åˆ°dataæ•°ç»„ä¸ + addNewWord() { + this.item.data.push({ + id: '', + pictureBookId: '', + word: '', + displayType: '', + partOfSpeech: '', + exampleSentence: '', + wordAudioUrl: '', + exampleSentenceAudioUrl: '', + wordTranslation: '', + exampleSentenceTranslation: '', + imageUrl: '', + britishPhonetic: '', + americanPhonetic: '', + englishDefinition: '', + backContent: {}, + createdTime: new Date().toISOString() + }); + this.save(); + } + + // åˆ é™¤data数组ä¸çš„å•è¯ + deleteWord(index) { + this.item.data.splice(index, 1); + this.save(); + } + +} \ No newline at end of file