removing friendly pages
This commit is contained in:
parent
797863008d
commit
444ee0481d
|
|
@ -0,0 +1 @@
|
||||||
|
python3 compress-with-structure.py
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
import os
|
||||||
|
from PIL import Image, UnidentifiedImageError
|
||||||
|
from shutil import copy2
|
||||||
|
|
||||||
|
# Ścieżki katalogów
|
||||||
|
input_folder = "src/main/resources/static/zaklik-public-images" # Folder źródłowy
|
||||||
|
output_folder = "src/main/resources/static/zaklik-public-images" # Folder docelowy
|
||||||
|
|
||||||
|
# Funkcja kompresująca obrazy
|
||||||
|
def compress_image(input_path, output_path):
|
||||||
|
try:
|
||||||
|
with Image.open(input_path) as img:
|
||||||
|
img_format = img.format # Zachowaj format pliku
|
||||||
|
if img_format in ["JPEG", "PNG"]:
|
||||||
|
img.save(output_path, optimize=True, quality=15)
|
||||||
|
else:
|
||||||
|
copy2(input_path, output_path) # Kopiuj bez zmian, jeśli format nie jest obsługiwany
|
||||||
|
except UnidentifiedImageError:
|
||||||
|
print(f"Nie rozpoznano formatu obrazu: {input_path}")
|
||||||
|
|
||||||
|
# Funkcja do przetwarzania plików w folderze
|
||||||
|
def process_folder(input_folder, output_folder):
|
||||||
|
for root, _, files in os.walk(input_folder):
|
||||||
|
for file in files:
|
||||||
|
# Ścieżka do pliku wejściowego
|
||||||
|
input_path = os.path.join(root, file)
|
||||||
|
|
||||||
|
# Tworzenie ścieżki docelowej z odpowiednią strukturą katalogów
|
||||||
|
relative_path = os.path.relpath(root, input_folder)
|
||||||
|
target_dir = os.path.join(output_folder, relative_path)
|
||||||
|
os.makedirs(target_dir, exist_ok=True)
|
||||||
|
|
||||||
|
# Obsługa plików już skompresowanych
|
||||||
|
if "(compressed)" in file:
|
||||||
|
continue
|
||||||
|
if "properties" in file:
|
||||||
|
continue
|
||||||
|
# Generowanie nazwy pliku wyjściowego
|
||||||
|
name, ext = os.path.splitext(file)
|
||||||
|
output_file = f"{name}(compressed){ext}"
|
||||||
|
output_path = os.path.join(target_dir, output_file)
|
||||||
|
# Sprawdzenie, czy skompresowany plik już istnieje
|
||||||
|
if os.path.exists(output_path):
|
||||||
|
print(f"Pominięto, plik już istnieje: {output_path}")
|
||||||
|
continue
|
||||||
|
# Kompresja pliku
|
||||||
|
print(f"Kompresowanie: {input_path} -> {output_path}")
|
||||||
|
compress_image(input_path, output_path)
|
||||||
|
|
||||||
|
# Wykonanie skryptu
|
||||||
|
process_folder(input_folder, output_folder)
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
INPUT_DIR="./src/main/resources/static/zaklik-public-images" # Folder z oryginalnymi obrazami
|
||||||
|
OUTPUT_DIR="./src/main/resources/static/zaklik-public-images-generated" # Główny folder do zapisu obrazów w nowych formatach
|
||||||
|
|
||||||
|
# Iteracja po wszystkich plikach w katalogu wejściowym i jego podkatalogach
|
||||||
|
find "$INPUT_DIR" -type f | while read -r file; do
|
||||||
|
# Relatywna ścieżka pliku w katalogu wejściowym
|
||||||
|
relative_path="${file#$INPUT_DIR/}"
|
||||||
|
|
||||||
|
# Ścieżka do katalogu docelowego
|
||||||
|
target_dir="$OUTPUT_DIR/$(dirname "$relative_path")"
|
||||||
|
|
||||||
|
# Nazwa pliku bez rozszerzenia
|
||||||
|
filename=$(basename "$file" | cut -f 1 -d '.')
|
||||||
|
|
||||||
|
# Ścieżki docelowych plików w formatach WebP i AVIF
|
||||||
|
webp_file="$target_dir/$filename.webp"
|
||||||
|
avif_file="$target_dir/$filename.avif"
|
||||||
|
|
||||||
|
# Sprawdzenie, czy oba pliki już istnieją
|
||||||
|
if [ -f "$webp_file" ] && [ -f "$avif_file" ]; then
|
||||||
|
echo "Pomijam plik: $file (już skompresowany)"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Tworzenie podkatalogu w katalogu docelowym
|
||||||
|
mkdir -p "$target_dir"
|
||||||
|
|
||||||
|
# Konwersja do WebP (jeśli plik nie istnieje)
|
||||||
|
if [ ! -f "$webp_file" ]; then
|
||||||
|
convert "$file" -quality 80 "$webp_file"
|
||||||
|
echo "Skompresowano do WebP: $webp_file"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Konwersja do AVIF (jeśli plik nie istnieje)
|
||||||
|
if [ ! -f "$avif_file" ]; then
|
||||||
|
convert "$file" -quality 80 "$avif_file"
|
||||||
|
echo "Skompresowano do AVIF: $avif_file"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "Konwersja zakończona."
|
||||||
|
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
const sharp = require('sharp');
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
const inputDir = './src/main/resources/static/zaklik-public-images'; // Folder z oryginalnymi obrazami
|
||||||
|
const outputDir = './src/main/resources/static/main-images/optimized-by-sharp'; // Folder do zapisu obrazów w nowych formatach
|
||||||
|
|
||||||
|
const formats = ['webp', 'avif'];
|
||||||
|
|
||||||
|
fs.readdir(inputDir, (err, files) => {
|
||||||
|
if (err) {
|
||||||
|
console.error('Error reading input directory:', err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
files.forEach(file => {
|
||||||
|
const inputFile = path.join(inputDir, file);
|
||||||
|
const fileName = path.parse(file).name;
|
||||||
|
|
||||||
|
formats.forEach(format => {
|
||||||
|
const outputFile = path.join(outputDir, `${fileName}.${format}`);
|
||||||
|
sharp(inputFile)
|
||||||
|
.toFormat(format, { quality: 80 })
|
||||||
|
.toFile(outputFile, (err, info) => {
|
||||||
|
if (err) {
|
||||||
|
console.error(`Error converting ${file} to ${format}:`, err);
|
||||||
|
} else {
|
||||||
|
console.log(`Converted ${file} to ${format} format.`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -21,6 +21,7 @@
|
||||||
"@ngx-translate/core": "^16.0.3",
|
"@ngx-translate/core": "^16.0.3",
|
||||||
"@ngx-translate/http-loader": "^16.0.0",
|
"@ngx-translate/http-loader": "^16.0.0",
|
||||||
"bootstrap": "^5.3.3",
|
"bootstrap": "^5.3.3",
|
||||||
|
"date-fns": "^4.1.0",
|
||||||
"rxjs": "~7.8.0",
|
"rxjs": "~7.8.0",
|
||||||
"tslib": "^2.3.0",
|
"tslib": "^2.3.0",
|
||||||
"zone.js": "~0.14.10"
|
"zone.js": "~0.14.10"
|
||||||
|
|
@ -9423,6 +9424,15 @@
|
||||||
"integrity": "sha512-GAj5FOq0Hd+RsCGVJxZuKaIDXDf3h6GQoNEjFgbLLI/trgtavwUbSnZ5pVfg27DVCaWjIohryS0JFwIJyT2cMg==",
|
"integrity": "sha512-GAj5FOq0Hd+RsCGVJxZuKaIDXDf3h6GQoNEjFgbLLI/trgtavwUbSnZ5pVfg27DVCaWjIohryS0JFwIJyT2cMg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/date-fns": {
|
||||||
|
"version": "4.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz",
|
||||||
|
"integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==",
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/kossnocorp"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/date-format": {
|
"node_modules/date-format": {
|
||||||
"version": "4.0.14",
|
"version": "4.0.14",
|
||||||
"resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.14.tgz",
|
"resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.14.tgz",
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@
|
||||||
"@ngx-translate/core": "^16.0.3",
|
"@ngx-translate/core": "^16.0.3",
|
||||||
"@ngx-translate/http-loader": "^16.0.0",
|
"@ngx-translate/http-loader": "^16.0.0",
|
||||||
"bootstrap": "^5.3.3",
|
"bootstrap": "^5.3.3",
|
||||||
|
"date-fns": "^4.1.0",
|
||||||
"rxjs": "~7.8.0",
|
"rxjs": "~7.8.0",
|
||||||
"tslib": "^2.3.0",
|
"tslib": "^2.3.0",
|
||||||
"zone.js": "~0.14.10"
|
"zone.js": "~0.14.10"
|
||||||
|
|
|
||||||
|
|
@ -8,19 +8,19 @@
|
||||||
<h5 class="card-title">Wrocław</h5>
|
<h5 class="card-title">Wrocław</h5>
|
||||||
<div class="col-auto">
|
<div class="col-auto">
|
||||||
<a href="mailto:kusartur@gmail.com">
|
<a href="mailto:kusartur@gmail.com">
|
||||||
<img width="30" height="20" src="icons/envelope.svg" />
|
<img width="30" height="20" src="icons/envelope.svg" alt="envelope" >
|
||||||
kusartur@gmail.com
|
kusartur@gmail.com
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-auto">
|
<div class="col-auto">
|
||||||
<a href="tel:+48512558222"> <img width="30" height="20"
|
<a href="tel:+48511116016"> <img width="30" height="20"
|
||||||
src="icons/telephone.svg" /> +48 511 116 016</a>
|
src="icons/telephone.svg" alt="telephone" > +48 511 116 016</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-auto">
|
<div class="col-auto">
|
||||||
<a href="https://www.linkedin.com/in/artur-ku%C5%9B-99a7b02/"> <img
|
<a href="https://www.linkedin.com/in/artur-ku%C5%9B-99a7b02/"> <img
|
||||||
width="30" height="20" src="icons/linkedin.svg" />Linkedin
|
width="30" height="20" src="icons/linkedin.svg" alt="linkedin" >Linkedin
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,6 @@
|
||||||
<a class="navbar-brand" [routerLink]="'/about-me'">{{ 'topBar.aboutMe' | translate }}</a>
|
<a class="navbar-brand" [routerLink]="'/about-me'">{{ 'topBar.aboutMe' | translate }}</a>
|
||||||
<a class="navbar-brand" [routerLink]="'/asset-calculator'">{{ 'topBar.depreciationCalculator' | translate }}</a>
|
<a class="navbar-brand" [routerLink]="'/asset-calculator'">{{ 'topBar.depreciationCalculator' | translate }}</a>
|
||||||
<a class="navbar-brand" [routerLink]="'/quotes'">{{ 'topBar.courses' | translate }}</a>
|
<a class="navbar-brand" [routerLink]="'/quotes'">{{ 'topBar.courses' | translate }}</a>
|
||||||
<a class="navbar-brand" [routerLink]="'/friendly-pages'">{{ 'topBar.friendlyPages' | translate }}</a>
|
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -36,11 +36,6 @@ export const routes: Routes = [
|
||||||
path: "fixed-asset",
|
path: "fixed-asset",
|
||||||
title:"Środki trwałe",
|
title:"Środki trwałe",
|
||||||
component: FixedAssetComponent
|
component: FixedAssetComponent
|
||||||
},
|
|
||||||
{
|
|
||||||
path: "friendly-pages",
|
|
||||||
title:"Zaprzyjaznione strony",
|
|
||||||
component: FriendlyPagesComponent
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
.month-picker .mat-calendar-body-cell-content {
|
||||||
|
display: none; /* Ukrywa dni */
|
||||||
|
}
|
||||||
|
.month-picker .mat-calendar-body-cell-content:not(.mat-calendar-body-disabled) {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
@ -44,12 +44,18 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group row align-items-center ">
|
<div class="form-group row align-items-center ">
|
||||||
|
|
||||||
<div class="col-5 col-lg-6 text-start ">
|
<div class="col-5 col-lg-6 text-start ">
|
||||||
<label class="form-label" for="startDepreciation" >{{ 'asset-calculator.startOfDepreciation' | translate }} </label>
|
<label class="form-label" for="startDepreciation" >{{ 'asset-calculator.startOfDepreciation' | translate }} </label>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-7 col-lg-6">
|
|
||||||
<input type="month" lang="pl" class="form-control" [(ngModel)]="startDepreciation" id="startDepreciation" placeholder="2024" />
|
|
||||||
|
|
||||||
|
<div class="col-7 col-lg-6">
|
||||||
|
<mat-form-field appearance="outline" class="w-100">
|
||||||
|
<mat-label>{{ 'asset-calculator.startOfDepreciation' | translate }}</mat-label>
|
||||||
|
<input matInput [(ngModel)]="startDepreciation" [matDatepicker]="picker" readonly>
|
||||||
|
<mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
|
||||||
|
<mat-datepicker #picker startView="multi-year" (monthSelected)="onMonthSelected($event, picker)" panelClass="month-picker"></mat-datepicker>
|
||||||
|
</mat-form-field>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,16 @@ import { Asset, AssetPlanPosition, TypeDepreciation, YearMonth, AssetLifeChange,
|
||||||
import { AssetService } from '../assets/service/asset.service';
|
import { AssetService } from '../assets/service/asset.service';
|
||||||
import { TranslateModule, TranslateService } from "@ngx-translate/core";
|
import { TranslateModule, TranslateService } from "@ngx-translate/core";
|
||||||
import { FormsModule } from '@angular/forms';
|
import { FormsModule } from '@angular/forms';
|
||||||
|
import { MatDatepicker, MatDatepickerModule } from '@angular/material/datepicker';
|
||||||
|
import { MatNativeDateModule } from '@angular/material/core';
|
||||||
|
import { MatInputModule } from '@angular/material/input';
|
||||||
|
import { format, parse, addMonths, isAfter } from 'date-fns';
|
||||||
|
|
||||||
|
|
||||||
interface FormValues {
|
interface FormValues {
|
||||||
initialValueAsset:number;
|
initialValueAsset:number;
|
||||||
rate: number;
|
rate: number;
|
||||||
startDepreciation: string;
|
startDepreciation: Date;
|
||||||
typeDepreciation: TypeDepreciation;
|
typeDepreciation: TypeDepreciation;
|
||||||
factor: number;
|
factor: number;
|
||||||
}
|
}
|
||||||
|
|
@ -17,7 +22,6 @@ class AssetLifeChangeWrapper{
|
||||||
when = signal<string> ( YearMonth.todayTxt());
|
when = signal<string> ( YearMonth.todayTxt());
|
||||||
initial = signal<number>( 0 );
|
initial = signal<number>( 0 );
|
||||||
|
|
||||||
|
|
||||||
constructor( assetLifeChange : AssetLifeChange ){
|
constructor( assetLifeChange : AssetLifeChange ){
|
||||||
this.set(assetLifeChange);
|
this.set(assetLifeChange);
|
||||||
}
|
}
|
||||||
|
|
@ -38,7 +42,11 @@ const NAME_IN_STORAGE = 'assetForCalculator';
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-asset-calculator',
|
selector: 'app-asset-calculator',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [DecimalPipe, TranslateModule, FormsModule],
|
imports: [DecimalPipe, TranslateModule, FormsModule, MatDatepickerModule, MatNativeDateModule, MatInputModule],
|
||||||
|
providers: [
|
||||||
|
MatDatepickerModule,
|
||||||
|
MatNativeDateModule
|
||||||
|
],
|
||||||
templateUrl: "asset-calculator.component.html",
|
templateUrl: "asset-calculator.component.html",
|
||||||
styleUrls: ['asset-calculator.component.css']
|
styleUrls: ['asset-calculator.component.css']
|
||||||
})
|
})
|
||||||
|
|
@ -50,7 +58,7 @@ export class AssetCalculatorComponent {
|
||||||
|
|
||||||
initialValueAsset = signal <number> ( 6000 );
|
initialValueAsset = signal <number> ( 6000 );
|
||||||
rate = signal <number> ( 20 );
|
rate = signal <number> ( 20 );
|
||||||
startDepreciation = signal <string> ( YearMonth.today().toString() );
|
startDepreciation = signal <Date> ( new Date() );
|
||||||
typeDepreciation = signal <TypeDepreciation>( TypeDepreciation.linear );
|
typeDepreciation = signal <TypeDepreciation>( TypeDepreciation.linear );
|
||||||
factor = signal <number>( 2 );
|
factor = signal <number>( 2 );
|
||||||
|
|
||||||
|
|
@ -74,6 +82,8 @@ export class AssetCalculatorComponent {
|
||||||
effect( () => this.reCalculate( ) )
|
effect( () => this.reCalculate( ) )
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private storage = {
|
private storage = {
|
||||||
load: (key: string) => {
|
load: (key: string) => {
|
||||||
try {
|
try {
|
||||||
|
|
@ -97,7 +107,6 @@ export class AssetCalculatorComponent {
|
||||||
this.restoreState();
|
this.restoreState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
ngOnDestroy(): void {
|
ngOnDestroy(): void {
|
||||||
const asset = this.controlsToAsset( );
|
const asset = this.controlsToAsset( );
|
||||||
if( null != asset){
|
if( null != asset){
|
||||||
|
|
@ -114,7 +123,6 @@ export class AssetCalculatorComponent {
|
||||||
this.storage.save(NAME_IN_STORAGE, asset);
|
this.storage.save(NAME_IN_STORAGE, asset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private assetToControls( asset : Asset ) {
|
private assetToControls( asset : Asset ) {
|
||||||
if( asset.life.length > 0 ){
|
if( asset.life.length > 0 ){
|
||||||
this.updateBatch(asset);
|
this.updateBatch(asset);
|
||||||
|
|
@ -126,7 +134,7 @@ private updateBatch(asset: Asset) {
|
||||||
const method0 = asset.depreciationMethods[0];
|
const method0 = asset.depreciationMethods[0];
|
||||||
|
|
||||||
this.initialValueAsset.set(firstLife.initial);
|
this.initialValueAsset.set(firstLife.initial);
|
||||||
this.startDepreciation.set(YearMonth.toTxt(firstLife.when));
|
this.startDepreciation.set(YearMonth.toDate(firstLife.when));
|
||||||
this.rate.set(method0.rate);
|
this.rate.set(method0.rate);
|
||||||
this.typeDepreciation.set(method0.type);
|
this.typeDepreciation.set(method0.type);
|
||||||
this.factor.set(method0.factor);
|
this.factor.set(method0.factor);
|
||||||
|
|
@ -137,13 +145,12 @@ private updateBatch(asset: Asset) {
|
||||||
this.lifeChangesSignal.set(lifeChanges);
|
this.lifeChangesSignal.set(lifeChanges);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private controlsToAsset(): Asset {
|
private controlsToAsset(): Asset {
|
||||||
|
|
||||||
const formValues = this.formValues();
|
const formValues = this.formValues();
|
||||||
const lifeChanges = this.lifeChangesSignal();
|
const lifeChanges = this.lifeChangesSignal();
|
||||||
|
|
||||||
const when = YearMonth.from( formValues.startDepreciation );
|
const when = YearMonth.fromDate( formValues.startDepreciation );
|
||||||
const asset = new Asset( when );
|
const asset = new Asset( when );
|
||||||
|
|
||||||
const method = new AssetDepreciationMethod( when.year, formValues.rate, formValues.typeDepreciation, formValues.factor );
|
const method = new AssetDepreciationMethod( when.year, formValues.rate, formValues.typeDepreciation, formValues.factor );
|
||||||
|
|
@ -200,7 +207,7 @@ private controlsToAsset(): Asset {
|
||||||
// Method to add a life change
|
// Method to add a life change
|
||||||
addLifeChange( ) {
|
addLifeChange( ) {
|
||||||
const assetLifeChange2 : AssetLifeChange
|
const assetLifeChange2 : AssetLifeChange
|
||||||
= new AssetLifeChange( YearMonth.from( this.formValues().startDepreciation ), 1000, 0, 0 );
|
= new AssetLifeChange( YearMonth.fromDate( this.formValues().startDepreciation ), 1000, 0, 0 );
|
||||||
return this.addLifeChange2( assetLifeChange2 );
|
return this.addLifeChange2( assetLifeChange2 );
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -218,6 +225,15 @@ private controlsToAsset(): Asset {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateDateFromInput(inputValue: string) {
|
||||||
|
// Konwersja stringa 'YYYY-MM' na Date
|
||||||
|
const parsedDate = parse(inputValue, 'yyyy-MM', new Date());
|
||||||
|
this.startDepreciation.set(parsedDate);
|
||||||
|
}
|
||||||
|
onMonthSelected(event: Date, datepicker: MatDatepicker<Date>) {
|
||||||
|
this.startDepreciation.set(event); // Aktualizacja pola w formularzu
|
||||||
|
datepicker.close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,11 @@ export class YearMonth{
|
||||||
return new YearMonth( year, month );
|
return new YearMonth( year, month );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static fromDate( date: Date) {
|
||||||
|
|
||||||
|
return new YearMonth( date.getFullYear(), date.getMonth()+1 );
|
||||||
|
}
|
||||||
|
|
||||||
readonly year : number ;
|
readonly year : number ;
|
||||||
readonly month : number ;
|
readonly month : number ;
|
||||||
|
|
||||||
|
|
@ -18,6 +23,9 @@ export class YearMonth{
|
||||||
this.month = month;
|
this.month = month;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toDate(){
|
||||||
|
return new Date(this.year, this.month-1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
static today( ):YearMonth{
|
static today( ):YearMonth{
|
||||||
const today = new Date();
|
const today = new Date();
|
||||||
|
|
@ -31,6 +39,9 @@ export class YearMonth{
|
||||||
static toTxt( ym : YearMonth ):string{
|
static toTxt( ym : YearMonth ):string{
|
||||||
return ym.year + '-' + String(ym.month).padStart(2, '0');;
|
return ym.year + '-' + String(ym.month).padStart(2, '0');;
|
||||||
}
|
}
|
||||||
|
static toDate( ym : YearMonth) : Date{
|
||||||
|
return new Date(ym.year, ym.month-1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
toJSON() {
|
toJSON() {
|
||||||
return { year: this.year, month: this.month };
|
return { year: this.year, month: this.month };
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue