This commit is contained in:
Chris Chen 2025-11-04 07:42:24 -08:00
parent ed3c116d13
commit b8b35645ac
5 changed files with 274 additions and 98 deletions

View File

@ -29,6 +29,7 @@ export interface MD2MobInfo {
export interface MD2MobLevelInfo {
id: string;
level: number;
mobInfoId: string;
rewardTokens: number;
fixedRareTreasure: number;

View File

@ -23,20 +23,129 @@
<div class="levels-section">
<h4>Mob Levels</h4>
<div class="levels-list">
<div
*ngFor="let levelInfo of mobLevelInfos; let i = index"
class="level-item"
[class.selected]="selectedLevelInfo?.id === levelInfo.id"
(click)="selectLevelInfo(levelInfo)">
<strong>Level {{ i + 1 }}</strong>
<div class="level-details">
<span>HP: {{ levelInfo.fixedHp }}</span>
<span>Actions: {{ levelInfo.actions }}</span>
<span>Reward Tokens: {{ levelInfo.rewardTokens }}</span>
</div>
</div>
<div class="levels-toolbar">
<button kendoButton (click)="addLevelHandler()" [primary]="true">
<span class="k-icon k-i-plus"></span> Add Level
</button>
</div>
<kendo-grid #levelsGrid [data]="mobLevelInfos" [loading]="isLoading" [pageSize]="levelsState.take"
[skip]="levelsState.skip" [sortable]="true" [filterable]="true" [pageable]="true" [height]="300"
kendoGridTemplateEditing (edit)="editLevelHandler($event)" (cancel)="cancelLevelHandler($event)"
(save)="saveLevelHandler($event)" (remove)="removeLevelHandler($event)" (dataStateChange)="levelsState = $event">
<kendo-grid-column field="level" title="Level" [width]="50">
<ng-template kendoGridCellTemplate let-dataItem>
{{ dataItem.level }}
</ng-template>
<ng-template kendoGridEditTemplate let-dataItem="dataItem" let-rowIndex="rowIndex">
<kendo-numerictextbox [(ngModel)]="dataItem.level" [name]="'level_' + rowIndex" [min]="1" [decimals]="0"
[format]="'n0'">
</kendo-numerictextbox>
</ng-template>
</kendo-grid-column>
<!-- <kendo-grid-column field="fixedHp" title="HP" [width]="80">
<ng-template kendoGridCellTemplate let-dataItem>
{{ dataItem.fixedHp }}
</ng-template>
<ng-template kendoGridEditTemplate let-dataItem="dataItem" let-rowIndex="rowIndex">
<kendo-numerictextbox [(ngModel)]="dataItem.fixedHp" [name]="'fixedHp_' + rowIndex" [min]="0" [decimals]="0"
[format]="'n0'">
</kendo-numerictextbox>
</ng-template>
</kendo-grid-column> -->
<kendo-grid-column field="hpPerHero" title="HP/Hero" [width]="80">
<ng-template kendoGridCellTemplate let-dataItem>
{{ dataItem.hpPerHero }}
</ng-template>
<ng-template kendoGridEditTemplate let-dataItem="dataItem" let-rowIndex="rowIndex">
<kendo-numerictextbox [(ngModel)]="dataItem.hpPerHero" [name]="'hpPerHero_' + rowIndex" [min]="0"
[decimals]="0" [format]="'n0'">
</kendo-numerictextbox>
</ng-template>
</kendo-grid-column>
<!-- <kendo-grid-column field="actions" title="Actions" [width]="100">
<ng-template kendoGridCellTemplate let-dataItem>
{{ dataItem.actions }}
</ng-template>
<ng-template kendoGridEditTemplate let-dataItem="dataItem" let-rowIndex="rowIndex">
<kendo-numerictextbox [(ngModel)]="dataItem.actions" [name]="'actions_' + rowIndex" [min]="0" [decimals]="0"
[format]="'n0'">
</kendo-numerictextbox>
</ng-template>
</kendo-grid-column> -->
<kendo-grid-column field="rewardTokens" title="Reward Tokens" [width]="80">
<ng-template kendoGridCellTemplate let-dataItem>
{{ dataItem.rewardTokens }}
</ng-template>
<ng-template kendoGridEditTemplate let-dataItem="dataItem" let-rowIndex="rowIndex">
<kendo-numerictextbox [(ngModel)]="dataItem.rewardTokens" [name]="'rewardTokens_' + rowIndex" [min]="0"
[decimals]="0" [format]="'n0'">
</kendo-numerictextbox>
</ng-template>
</kendo-grid-column>
<kendo-grid-column field="fixedRareTreasure" title="Rare Treasure" [width]="80">
<ng-template kendoGridCellTemplate let-dataItem>
{{ dataItem.fixedRareTreasure }}
</ng-template>
<ng-template kendoGridEditTemplate let-dataItem="dataItem" let-rowIndex="rowIndex">
<kendo-numerictextbox [(ngModel)]="dataItem.fixedRareTreasure" [name]="'fixedRareTreasure_' + rowIndex"
[min]="0" [decimals]="0" [format]="'n0'">
</kendo-numerictextbox>
</ng-template>
</kendo-grid-column>
<kendo-grid-column field="fixedEpicTreasure" title="Epic Treasure" [width]="80">
<ng-template kendoGridCellTemplate let-dataItem>
{{ dataItem.fixedEpicTreasure }}
</ng-template>
<ng-template kendoGridEditTemplate let-dataItem="dataItem" let-rowIndex="rowIndex">
<kendo-numerictextbox [(ngModel)]="dataItem.fixedEpicTreasure" [name]="'fixedEpicTreasure_' + rowIndex"
[min]="0" [decimals]="0" [format]="'n0'">
</kendo-numerictextbox>
</ng-template>
</kendo-grid-column>
<kendo-grid-column field="fixedLegendTreasure" title="Legend Treasure" [width]="60">
<ng-template kendoGridCellTemplate let-dataItem>
{{ dataItem.fixedLegendTreasure }}
</ng-template>
<ng-template kendoGridEditTemplate let-dataItem="dataItem" let-rowIndex="rowIndex">
<kendo-numerictextbox [(ngModel)]="dataItem.fixedLegendTreasure" [name]="'fixedLegendTreasure_' + rowIndex"
[min]="0" [decimals]="0" [format]="'n0'">
</kendo-numerictextbox>
</ng-template>
</kendo-grid-column>
<!-- result.defenceInfo.blue
result.defenceInfo.green -->
<kendo-grid-column field="defenceInfo.blue" title="Blue Dice" [width]="60">
<ng-template kendoGridCellTemplate let-dataItem>
{{ dataItem.defenceInfo.blue }}
</ng-template>
<ng-template kendoGridEditTemplate let-dataItem="dataItem" let-rowIndex="rowIndex">
<kendo-numerictextbox [(ngModel)]="dataItem.defenceInfo.blue" [name]="'defenceInfo.blue_' + rowIndex"
[min]="0" [decimals]="0" [format]="'n0'">
</kendo-numerictextbox>
</ng-template>
</kendo-grid-column>
<kendo-grid-command-column title="Actions" [width]="250">
<ng-template kendoGridCellTemplate let-isNew="isNew" let-dataItem="dataItem" let-rowIndex="rowIndex">
<button kendoGridEditCommand [primary]="true">Edit</button>
<button kendoGridRemoveCommand>Remove</button>
<button kendoButton (click)="selectLevelInfo(dataItem)" [look]="'flat'">
View Skills
</button>
<button kendoGridSaveCommand>Save</button>
<button kendoGridCancelCommand>Cancel</button>
</ng-template>
</kendo-grid-command-column>
</kendo-grid>
</div>
<div class="skills-section" *ngIf="selectedLevelInfo">
@ -47,88 +156,62 @@
</button>
</div>
<kendo-grid
#skillsGrid
[data]="skillsData"
[loading]="isLoading"
[pageSize]="skillsState.take"
[skip]="skillsState.skip"
[sortable]="true"
[filterable]="true"
[pageable]="true"
[height]="400"
kendoGridTemplateEditing
(save)="saveSkillHandler($event)"
(remove)="removeSkillHandler($event)"
<kendo-grid #skillsGrid [data]="skillsData" [loading]="isLoading" [pageSize]="skillsState.take"
[skip]="skillsState.skip" [sortable]="true" [filterable]="true" [pageable]="true" [height]="400"
kendoGridTemplateEditing (save)="saveSkillHandler($event)" (remove)="removeSkillHandler($event)"
(dataStateChange)="skillsState = $event">
<kendo-grid-column field="name" title="Name" width="150">
<kendo-grid-column field="name" title="Name" [width]="150">
<ng-template kendoGridEditTemplate let-dataItem="dataItem">
<input kendoTextBox [(ngModel)]="dataItem.name" name="name" />
<input kendoTextBox [(ngModel)]="dataItem.name" name="name" placeholder="Enter skill name" />
</ng-template>
</kendo-grid-column>
<kendo-grid-column field="type" title="Type" width="120">
<kendo-grid-column field="type" title="Type" [width]="120">
<ng-template kendoGridCellTemplate let-dataItem>
{{ getSkillTypeName(dataItem.type) }}
</ng-template>
<ng-template kendoGridEditTemplate let-dataItem="dataItem">
<kendo-dropdownlist
[(ngModel)]="dataItem.type"
name="type"
[data]="skillTypes"
[valueField]="'value'"
<kendo-dropdownlist [(ngModel)]="dataItem.type" name="type" [data]="skillTypes" [valueField]="'value'"
[textField]="'text'">
</kendo-dropdownlist>
</ng-template>
</kendo-grid-column>
<kendo-grid-column field="skillTarget" title="Target" width="150">
<kendo-grid-column field="skillTarget" title="Target" [width]="150">
<ng-template kendoGridCellTemplate let-dataItem>
{{ getSkillTargetName(dataItem.skillTarget) }}
</ng-template>
<ng-template kendoGridEditTemplate let-dataItem="dataItem">
<kendo-dropdownlist
[(ngModel)]="dataItem.skillTarget"
name="skillTarget"
[data]="skillTargets"
[valueField]="'value'"
[textField]="'text'">
<kendo-dropdownlist [(ngModel)]="dataItem.skillTarget" name="skillTarget" [data]="skillTargets"
[valueField]="'value'" [textField]="'text'">
</kendo-dropdownlist>
</ng-template>
</kendo-grid-column>
<kendo-grid-column field="clawRoll" title="Claw Roll" width="100">
<kendo-grid-column field="clawRoll" title="Claw Roll" [width]="100">
<ng-template kendoGridEditTemplate let-dataItem="dataItem">
<kendo-numerictextbox
[(ngModel)]="dataItem.clawRoll"
name="clawRoll"
[min]="0">
<kendo-numerictextbox [(ngModel)]="dataItem.clawRoll" name="clawRoll" [min]="0">
</kendo-numerictextbox>
</ng-template>
</kendo-grid-column>
<kendo-grid-column field="skillRoll" title="Skill Roll" width="100">
<kendo-grid-column field="skillRoll" title="Skill Roll" [width]="100">
<ng-template kendoGridEditTemplate let-dataItem="dataItem">
<kendo-numerictextbox
[(ngModel)]="dataItem.skillRoll"
name="skillRoll"
[min]="0">
<kendo-numerictextbox [(ngModel)]="dataItem.skillRoll" name="skillRoll" [min]="0">
</kendo-numerictextbox>
</ng-template>
</kendo-grid-column>
<kendo-grid-column field="description" title="Description" width="300">
<kendo-grid-column field="description" title="Description" [width]="300">
<ng-template kendoGridEditTemplate let-dataItem="dataItem">
<textarea kendoTextArea
[(ngModel)]="dataItem.description"
name="description"
rows="3">
<textarea kendoTextArea [(ngModel)]="dataItem.description" name="description"
placeholder="Enter skill description" rows="3">
</textarea>
</ng-template>
</kendo-grid-column>
<kendo-grid-command-column title="Actions" width="200">
<kendo-grid-command-column title="Actions" [width]="200">
<ng-template kendoGridCellTemplate let-isNew="isNew" let-dataItem="dataItem" let-rowIndex="rowIndex">
<button kendoGridEditCommand [primary]="true">Edit</button>
<button kendoGridRemoveCommand>Remove</button>
@ -145,4 +228,3 @@
<kendo-dialog-actions>
<button kendoButton [primary]="true" (click)="close()">Close</button>
</kendo-dialog-actions>

View File

@ -41,40 +41,8 @@
}
}
.levels-list {
display: flex;
flex-direction: column;
gap: 10px;
max-height: 150px;
overflow-y: auto;
padding: 10px;
border: 1px solid #e0e0e0;
border-radius: 4px;
}
.level-item {
padding: 10px;
border: 1px solid #e0e0e0;
border-radius: 4px;
cursor: pointer;
transition: background-color 0.2s;
&:hover {
background-color: #f5f5f5;
}
&.selected {
background-color: #e3f2fd;
border-color: #2196f3;
}
}
.level-details {
display: flex;
gap: 15px;
margin-top: 5px;
font-size: 12px;
color: #666;
.levels-toolbar {
margin-bottom: 10px;
}
.skills-section {

View File

@ -16,9 +16,20 @@ import { MsgBoxService } from '../../../../services/msg-box.service';
})
export class MD2MobInfoDetailComponent extends DialogContentBase implements OnInit {
@Input() public mobInfo: MD2MobInfo;
@ViewChild('levelsGrid') levelsGrid: GridComponent;
@ViewChild('skillsGrid') skillsGrid: GridComponent;
public mobLevelInfos: MD2MobLevelInfo[] = [];
public levelsData: GridDataResult = { data: [], total: 0 };
public levelsState: State = {
skip: 0,
take: 10,
sort: [],
filter: {
logic: 'and',
filters: []
}
};
public selectedLevelInfo: MD2MobLevelInfo | null = null;
public skillsData: GridDataResult = { data: [], total: 0 };
public skillsState: State = {
@ -74,10 +85,120 @@ export class MD2MobInfoDetailComponent extends DialogContentBase implements OnIn
}
public selectLevelInfo(levelInfo: MD2MobLevelInfo): void {
if (levelInfo.defenceInfo == null) {
levelInfo.defenceInfo = { blue: 0, green: 0, yellow: 0, orange: 0, red: 0, black: 0 };
}
if (levelInfo.attackInfo == null) {
levelInfo.attackInfo = { blue: 0, green: 0, yellow: 0, orange: 0, red: 0, black: 0 };
}
this.selectedLevelInfo = levelInfo;
this.loadSkills(levelInfo);
}
public editLevelHandler({ sender, rowIndex, dataItem }: any): void {
// The template-driven editing directive handles edit mode automatically
// This handler is here to ensure the grid enters edit mode
// The grid should already be in edit mode from the directive, but we ensure it
}
public cancelLevelHandler({ sender, rowIndex }: any): void {
// Cancel editing - template-driven editing handles this automatically
sender.closeRow(rowIndex);
}
public addLevelHandler(): void {
if (!this.mobInfo || !this.levelsGrid) return;
const newLevel: MD2MobLevelInfo = {
id: this.generateLevelId(),
level: this.mobLevelInfos.length + 1,
mobInfoId: this.mobInfo.id,
rewardTokens: 0,
fixedRareTreasure: 0,
fixedEpicTreasure: 0,
fixedLegendTreasure: 0,
fixedHp: 1,
hpPerHero: 0,
actions: 1,
attackInfo: { yellow: null, orange: null, red: null, blue: null, green: null, black: null },
defenceInfo: { yellow: null, orange: null, red: null, blue: null, green: null, black: null },
skills: []
};
// Use grid's addRow method to properly add a new row in edit mode
this.levelsGrid.addRow(newLevel);
}
public saveLevelHandler({ dataItem, isNew }: any): void {
if (isNew) {
dataItem.id = this.generateLevelId();
dataItem.mobInfoId = this.mobInfo?.id;
}
// Ensure required objects exist
if (!dataItem.attackInfo) {
dataItem.attackInfo = { yellow: null, orange: null, red: null, blue: null, green: null, black: null };
}
if (!dataItem.defenceInfo) {
dataItem.defenceInfo = { yellow: null, orange: null, red: null, blue: null, green: null, black: null };
}
if (!dataItem.skills) {
dataItem.skills = [];
}
this.isLoading = true;
this.mobLevelInfoService.createOrUpdate(dataItem).pipe(first()).subscribe(result => {
this.isLoading = false;
if (isNew) {
// For new items, add to the array
result.attackInfo.black = 0;
this.mobLevelInfos.push(result);
} else {
// For existing items, update in the array
const index = this.mobLevelInfos.findIndex(l => l.id === result.id);
if (index !== -1) {
this.mobLevelInfos[index] = result;
// Update selected level if it's the one being edited
if (this.selectedLevelInfo?.id === result.id) {
this.selectedLevelInfo = result;
this.loadSkills(result);
}
}
}
// Close the edit row after saving
if (this.levelsGrid) {
const rowIndex = this.mobLevelInfos.findIndex(l => l.id === result.id);
if (rowIndex !== -1) {
this.levelsGrid.closeRow(rowIndex);
}
}
}, error => {
this.isLoading = false;
console.error('Error saving level info:', error);
});
}
public removeLevelHandler({ dataItem }: { dataItem: MD2MobLevelInfo }): void {
this.msgBoxService.showConfirmDeleteBox().pipe(first()).subscribe(answer => {
if (answer === true) {
this.isLoading = true;
this.mobLevelInfoService.delete(dataItem.id).pipe(first()).subscribe(result => {
const index = this.mobLevelInfos.findIndex(l => l.id === dataItem.id);
if (index !== -1) {
this.mobLevelInfos.splice(index, 1);
// Clear selection if deleted level was selected
if (this.selectedLevelInfo?.id === dataItem.id) {
this.selectedLevelInfo = null;
this.skillsData = { data: [], total: 0 };
}
}
this.isLoading = false;
});
}
});
}
public loadSkills(levelInfo: MD2MobLevelInfo): void {
if (levelInfo && levelInfo.skills) {
this.skillsData = {
@ -169,6 +290,10 @@ export class MD2MobInfoDetailComponent extends DialogContentBase implements OnIn
return 'skill_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
}
private generateLevelId(): string {
return 'level_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
}
public getSkillTypeName(type: MobSkillType): string {
return MobSkillType[type] || '';
}

View File

@ -57,7 +57,7 @@ export class MD2MobInfoMaintenanceComponent implements OnInit {
const dialogRef = this.dialogService.open({
title: 'Add New Mob Info',
content: MD2MobInfoEditorComponent,
width: 800,
width: '90vw',
height: 600
});
@ -81,7 +81,7 @@ export class MD2MobInfoMaintenanceComponent implements OnInit {
const dialogRef = this.dialogService.open({
title: 'Edit Mob Info',
content: MD2MobInfoEditorComponent,
width: 800,
width: '90vw',
height: 600
});
@ -112,7 +112,7 @@ export class MD2MobInfoMaintenanceComponent implements OnInit {
const dialogRef = this.dialogService.open({
title: `Mob Info: ${mobInfo.name}`,
content: MD2MobInfoDetailComponent,
width: 1200,
width: '90vw',
height: 800
});