qr-code generator
parent
be0d57a6b9
commit
f588583f73
|
|
@ -0,0 +1,24 @@
|
|||
import {Injectable} from '@angular/core';
|
||||
import {HttpClient} from '@angular/common/http';
|
||||
import {environment} from '../../environments/environment';
|
||||
import {Observable} from "rxjs";
|
||||
|
||||
@Injectable({providedIn: 'root'})
|
||||
export class AssetsService {
|
||||
constructor(private http: HttpClient) {
|
||||
}
|
||||
|
||||
upload(file: any, params?: any): Observable<any> {
|
||||
let formData = new FormData();
|
||||
formData.append('file', file);
|
||||
for (let param in params) {
|
||||
if (params.hasOwnProperty(param)) formData.append(param, params[param]);
|
||||
}
|
||||
return this.http.post<any>(`${environment.apiUrl}/api/assets`, formData, {reportProgress: true, observe: 'events'});
|
||||
}
|
||||
|
||||
bookmark(id: string, registry: string, params?: any): Observable<any> {
|
||||
return this.http.patch(`${environment.apiUrl}/api/assets/bookmark/${id}`, {registry: registry}, {params: params});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -12,6 +12,7 @@
|
|||
<a href="/development/">Разработка</a>
|
||||
<a href="/projects/">Проекты</a>
|
||||
<a href="/contacts/">Контакты</a>
|
||||
<a href routerLink="qr-code">Создай свой Qr-code</a>
|
||||
<a href="tel:+74994900465" class="right tel">
|
||||
<svg>
|
||||
<use href="assets/ico/call_24.svg#ico"></use>
|
||||
|
|
@ -65,7 +66,8 @@
|
|||
<a href routerLink="sites">Сайты</a>
|
||||
<a href routerLink="development">Разработка</a>
|
||||
<a href routerLink="projects">Проекты</a>
|
||||
<a href routerLink="contacts">Контакты</a>
|
||||
<a href routerLink="contacts">Контакты</a>
|
||||
<a href routerLink="qr-code">Создай свой Qr-code</a>
|
||||
</div>
|
||||
<div class="footer-info">
|
||||
<h4>Контакты</h4>
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import { Tk023Component } from './projects/tk023/tk023.component';
|
|||
import { Tk465Component } from './projects/tk465/tk465.component';
|
||||
import { ScvgComponent } from './projects/scvg/scvg.component';
|
||||
import { pipe_prodComponent } from './projects/pipe_prod/pipe_prod.component';
|
||||
import { QrCodeComponent } from './qr-code/qr-code.component';
|
||||
import { NotFoundComponentComponent } from './not-found-component/not-found-component.component';
|
||||
|
||||
|
||||
|
|
@ -29,5 +30,6 @@ export const routes: Routes = [
|
|||
{path:'projects/tk465', component: Tk465Component},
|
||||
{path:'projects/gazprom', component: ScvgComponent},
|
||||
{path:'projects/pipe_prod', component: pipe_prodComponent},
|
||||
{path: 'qr-code', component: QrCodeComponent},
|
||||
{path: '404', component: NotFoundComponentComponent},
|
||||
];
|
||||
|
|
|
|||
|
|
@ -0,0 +1,200 @@
|
|||
<div class="block">
|
||||
<div class="container-fluid">
|
||||
<div class="row inner">
|
||||
<div class="col-md-7 col-lg-8 block-options">
|
||||
<div class="accordion" [formGroup]="QrCodeForm">
|
||||
<div class="pane" ng-class="{'active':editView==='content'}" style>
|
||||
<div class="pane-header" ng-click="setEditView('content')">
|
||||
<div class="icon"></div>
|
||||
<h3 class="title">Введите текст или загрузите изображение</h3>
|
||||
<div class="plus">
|
||||
<i class="fa" aria-hidden="true"></i>
|
||||
</div>
|
||||
<div class="minus">
|
||||
<i class="fa" aria-hidden="true"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pane-content">
|
||||
<div class="tab-content">
|
||||
<div class="">
|
||||
<div class="form-group">
|
||||
<label for="qrcodeUrl">Ваш текст или картинка</label>
|
||||
<textarea name="text" rows="3" class="form-control" formControlName="text" #text></textarea>
|
||||
<small class="form-text text-mutted">Line breaks are allowed</small>
|
||||
</div>
|
||||
|
||||
<div class="area" [class.hover]="dragOver">
|
||||
<input #fileInput type="file" [id]="field.name" (change)="onFileInput($event)" />
|
||||
<p>Перетащите сюда или <span (click)="fileInput.click()">выберите файл</span> в формате JPEG или PNG общим объемом не более 10 Мбайт.</p>
|
||||
<div class="indicator" *ngIf="upload.file">
|
||||
<div class="label">Загружается файл {{upload.file?.name}}</div>
|
||||
<div class="progress"><div class="fill" [style.width]="upload.progress + '%'"></div></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="values" *ngIf="asset">
|
||||
<div class="item">
|
||||
<div class="preview"><img [src]="asset.links?.thumb" alt="" /></div>
|
||||
<button type="button" class="btn icon remove" (click)="clear()"></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="values" *ngIf="asset">
|
||||
<div class="item">
|
||||
<div class="preview"><img [src]="asset.links?.thumb" alt="" /></div>
|
||||
<button type="button" class="btn icon remove" (click)="clear()"></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pane" ng-class="{'active':editView==='colors'}" style>
|
||||
<div class="pane-header" ng-click="setEditView('colors')">
|
||||
<div class="icon"></div>
|
||||
<h3 class="title">Выберите цвет</h3>
|
||||
<div class="plus">
|
||||
<i class="fa" aria-hidden="true"></i>
|
||||
</div>
|
||||
<div class="minus">
|
||||
<i class="fa" aria-hidden="true"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pane-content overflow-visible color-panel">
|
||||
<label>Основной цвет</label>
|
||||
<div class="color-group color-group-body">
|
||||
<div class="form-check form-check-inline">
|
||||
<label class="form-check-label">
|
||||
<input class="form-check-input ng-valid ng-not-empty ng dirty ng-valid-parse ng-touched" type="radio" name="color-type" value="solid" formControlName="color-type" checked>
|
||||
Сплошной
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-check form-check-inline">
|
||||
<label class="form-check-label">
|
||||
<input class="form-check-input ng-valid ng-not-empty ng dirty ng-valid-parse ng-touched" type="radio" name="color-type" value="gradient" formControlName="color-type">
|
||||
Градиент
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-check form-check-inline">
|
||||
<label class="form-check-label">
|
||||
<input class="form-check-input ng-valid ng-not-empty ng dirty ng-valid-parse ng-touched" type="checkbox" name="eyeMode" value=true style formControlName="custom-eye-color" [defaultValue]=false>
|
||||
Цвет глаз
|
||||
</label>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-6 col-lg-4 form-group">
|
||||
<input type="color" formControlName="foreground-color">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-6 col-lg-4 form-group">
|
||||
<input type="color" formControlName="gradient-start">
|
||||
</div>
|
||||
<div class="col-sm-6 col-lg-4 form-group">
|
||||
<input type="color" formControlName="gradient-end">
|
||||
</div>
|
||||
<div class="col-sm-6 col-lg-4 form-group">
|
||||
<div class="input-group">
|
||||
<span>
|
||||
<button>Цвета местами</button>
|
||||
</span>
|
||||
<select formControlName="gradient-type" id="gradient-type">
|
||||
<option value="vertical">Вертикальный</option>
|
||||
<option value="horizontal">Горизонтальный</option>
|
||||
<option value="diagonal">Диагональ сверху вниз</option>
|
||||
<option value="inverse_diagonal">Диагональ снизу вверх</option>
|
||||
<option value="radial">Крулый</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label>Цвет глаз</label>
|
||||
<div class="row">
|
||||
<div class="col-sm-6 col-lg-4 form-group">
|
||||
<input type="color" formControlName="inner-eye-color">
|
||||
</div>
|
||||
<div class="col-sm-6 col-lg-4 form-group">
|
||||
<input type="color" formControlName="outside-eye-color">
|
||||
</div>
|
||||
<div class="col-sm-6 col-lg-4 form-group">
|
||||
<span>
|
||||
<button>Цвета местами</button>
|
||||
</span>
|
||||
<span>
|
||||
<button>Копировать основной цвет</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-6 col-lg-4">
|
||||
<label>Цвет фона</label>
|
||||
<div class="form-group color-group">
|
||||
<input type="color" formControlName="background-color">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pane">
|
||||
<div class="pane-header">
|
||||
<div class="icon"></div>
|
||||
<h3 class="title">Добавить изображение</h3>
|
||||
<div class="plus">
|
||||
<i class="fa" aria-hidden="true"></i>
|
||||
</div>
|
||||
<div class="minus">
|
||||
<i class="fa" aria-hidden="true"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pane-content">
|
||||
|
||||
<div class="area" [class.hover]="dragOver">
|
||||
<input #fileInput type="file" [id]="field.name" (change)="onFileInput($event)" />
|
||||
<p>Перетащите сюда или <span (click)="fileInput.click()">выберите файл</span> в формате JPEG или PNG общим объемом не более 10 Мбайт.</p>
|
||||
<div class="indicator" *ngIf="upload.file">
|
||||
<div class="label">Загружается файл {{upload.file?.name}}</div>
|
||||
<div class="progress"><div class="fill" [style.width]="upload.progress + '%'"></div></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="values" *ngIf="asset">
|
||||
<div class="item">
|
||||
<div class="preview"><img [src]="asset.links?.thumb" alt="" /></div>
|
||||
<button type="button" class="btn icon remove" (click)="clear()"></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pane">
|
||||
<div class="pane-header">
|
||||
<div class="icon"></div>
|
||||
<h3 class="title">Ручной дизайн</h3>
|
||||
<div class="plus">
|
||||
<i class="fa" aria-hidden="true"></i>
|
||||
</div>
|
||||
<div class="minus">
|
||||
<i class="fa" aria-hidden="true"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pane-content">
|
||||
<input type="radio"/>Обычный
|
||||
<input type="radio"/>Скругленные края
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-5 col-lg-4 block-download">
|
||||
<div class="preview">
|
||||
<p>Созданный QR-CODE</p>
|
||||
</div>
|
||||
<button class="btn primary submit" (click)="onSubmit()" [disabled]="QrCodeForm.invalid" >
|
||||
отправить
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,194 @@
|
|||
*,::after,::before {
|
||||
box-sizing: border-box
|
||||
}
|
||||
|
||||
textarea {
|
||||
overflow: auto;
|
||||
resize: vertical
|
||||
}
|
||||
|
||||
.block {
|
||||
box-shadow: 0 0 20px rgba(0,0,0,.25);
|
||||
position: relative;
|
||||
margin: 0 auto;
|
||||
border-radius: 32px;
|
||||
}
|
||||
|
||||
.container-fluid {
|
||||
width: 100%;
|
||||
padding-right: 15px;
|
||||
padding-left: 15px;
|
||||
margin-right: auto;
|
||||
margin-left: auto
|
||||
}
|
||||
|
||||
.row {
|
||||
display: -webkit-box;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-ms-flex-wrap: wrap;
|
||||
flex-wrap: wrap;
|
||||
margin-right: -15px;
|
||||
margin-left: -15px
|
||||
}
|
||||
|
||||
.block .block-options {
|
||||
padding: 32px
|
||||
}
|
||||
|
||||
.block .accordion .pane {
|
||||
margin-bottom: 4px
|
||||
}
|
||||
|
||||
.block .accordion .pane .pane-header {
|
||||
cursor: pointer;
|
||||
-webkit-transition: all .3s;
|
||||
transition: all .3s;
|
||||
background: #fff;
|
||||
font-size: 1.1rem;
|
||||
position: relative;
|
||||
font-weight: 500;
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
height: 48px;
|
||||
box-shadow: 0 0 6px rgba(0,0,0,.05)
|
||||
}
|
||||
|
||||
.block .accordion .pane .pane-header .title {
|
||||
padding: 0 16px;
|
||||
text-transform: uppercase;
|
||||
font-size: 1rem;
|
||||
margin: 0;
|
||||
-webkit-flex-shrink: 1;
|
||||
-webkit-flex-grow: 1;
|
||||
flex-grow: 1;
|
||||
flex-shrink: 1;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
line-height: 48px;
|
||||
text-align: initial
|
||||
}
|
||||
|
||||
.block .accordion .pane .pane-header .icon {
|
||||
width: 48px;
|
||||
text-align: center;
|
||||
-webkit-flex-shrink: 0;
|
||||
-webkit-flex-grow: 0;
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
background: #f2f8fc;
|
||||
line-height: 48px
|
||||
}
|
||||
|
||||
.block .accordion .pane .pane-header .minus, .block .accordion .pane .pane-header .plus {
|
||||
padding: 0 16px;
|
||||
-webkit-flex-shrink: 0;
|
||||
-webkit-flex-grow: 0;
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
line-height: 48px;
|
||||
color: rgba(111,120,127,.5);
|
||||
font-size: .9rem
|
||||
}
|
||||
|
||||
.block .accordion .pane .pane-header .minus {
|
||||
display: none
|
||||
}
|
||||
|
||||
.block .accordion .pane .pane-header:hover {
|
||||
background-color: #f2f8fc;
|
||||
box-shadow: none
|
||||
}
|
||||
|
||||
.block.accordion .pane.active .pane-content {
|
||||
padding: 1rem 0;
|
||||
display: block;
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
max-height: 1400px;
|
||||
overflow: visible;
|
||||
-webkit-transition: all .5s ease-in-out 0s,opacity .25s ease-in-out .2s;
|
||||
transition: all .5s ease-in-out 0s,opacity .25s ease-in-out .2s
|
||||
}
|
||||
|
||||
.form-control {
|
||||
display: block;
|
||||
width: 100%;
|
||||
padding: .375rem .75rem;
|
||||
font-size: 1rem;
|
||||
line-height: 1.5;
|
||||
color: #495057;
|
||||
background-color: #fff;
|
||||
background-clip: padding-box;
|
||||
border: 1px solid #ced4da;
|
||||
border-radius: .25rem;
|
||||
transition: border-color .15s ease-in-out,box-shadow .15s ease-in-out
|
||||
}
|
||||
|
||||
.block .accordion .pane.active .pane-content {
|
||||
padding: 1rem 0;
|
||||
display: block;
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
max-height: 1400px;
|
||||
overflow: visible;
|
||||
-webkit-transition: all .5s ease-in-out 0s,opacity .25s ease-in-out .2s;
|
||||
transition: all .5s ease-in-out 0s,opacity .25s ease-in-out .2s
|
||||
}
|
||||
|
||||
.body .accordion .pane .pane-content .color-group label {
|
||||
font-weight: 400
|
||||
}
|
||||
|
||||
.block .accordion .pane .pane-content .color-group-body {
|
||||
padding-bottom: 0
|
||||
}
|
||||
|
||||
.form-check {
|
||||
position: relative;
|
||||
display: block;
|
||||
padding-left: 1.25rem;
|
||||
}
|
||||
|
||||
.form-check-inline {
|
||||
display: -ms-inline-flexbox;
|
||||
display: inline-flex;
|
||||
-webkit-box-align: center;
|
||||
-ms-flex-align: center;
|
||||
align-items: center;
|
||||
padding-left: 0;
|
||||
margin-right: .75rem;
|
||||
}
|
||||
|
||||
.form-check-label {
|
||||
cursor: pointer;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.form-check-input {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.block .block-download {
|
||||
background: #fff;
|
||||
padding: 16px;
|
||||
border-left: 1px solid rgba(0, 0, 0, .1);
|
||||
border-radius: 0 5px 5px 0;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.block .inner {
|
||||
display:flex;
|
||||
align-items: stretch
|
||||
}
|
||||
}
|
||||
|
||||
.fa {
|
||||
display: inline-block;
|
||||
font: normal normal normal 14px/1 FontAwesome;
|
||||
font-size: inherit;
|
||||
text-rendering: auto;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { QrCodeComponent } from './qr-code.component';
|
||||
|
||||
describe('QrCodeComponent', () => {
|
||||
let component: QrCodeComponent;
|
||||
let fixture: ComponentFixture<QrCodeComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [QrCodeComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(QrCodeComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,109 @@
|
|||
import {Component, Input, OnInit} from '@angular/core';
|
||||
import {Router} from "@angular/router";
|
||||
import {FormBuilder, FormGroup, FormsModule, ReactiveFormsModule} from "@angular/forms";
|
||||
import {NgIf} from "@angular/common";
|
||||
import {FormsService} from "../_services/forms.service";
|
||||
import {HttpEventType, HttpResponse} from "@angular/common/http";
|
||||
import {AssetsService} from "../_services/assets.service";
|
||||
|
||||
@Component({
|
||||
selector: 'qr-code',
|
||||
standalone: true,
|
||||
imports: [
|
||||
FormsModule,
|
||||
NgIf,
|
||||
ReactiveFormsModule
|
||||
],
|
||||
templateUrl: './qr-code.component.html',
|
||||
styleUrl: './qr-code.component.scss'
|
||||
})
|
||||
export class QrCodeComponent implements OnInit{
|
||||
@Input() field!: any;
|
||||
public asset: any;
|
||||
public QrCodeForm: FormGroup;
|
||||
public dragOver = false;
|
||||
|
||||
public upload: any = {file: null, progress: 0};
|
||||
constructor(private formBuilder:FormBuilder, private formsService: FormsService, private assetsService: AssetsService){}
|
||||
|
||||
ngOnInit() {
|
||||
this.QrCodeForm = this.formBuilder.group({
|
||||
'text' : [''],
|
||||
'format' : ['svg'],
|
||||
'size' : [350],
|
||||
'foreground-color' : ['#000000'],
|
||||
'background-color' : ['#ffffff'],
|
||||
'gradient-start' : ['#ffffff'],
|
||||
'gradient-end' : ['#00A0FF'],
|
||||
'gradient-type' : ['vertical'],
|
||||
'color-type' : ['solid'],
|
||||
'custom-eye-color' : [false],
|
||||
'outside-eye-color' : ['#000000'],
|
||||
'inner-eye-color' : ['#000000'],
|
||||
'image' : []
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
onSubmit(){
|
||||
this.QrCodeForm.markAllAsTouched();
|
||||
if (this.QrCodeForm.valid)
|
||||
this.formsService.save('model', 'QrCode', null, this.QrCodeForm.value).subscribe(res => {
|
||||
}, error => {
|
||||
console.log(error);
|
||||
});
|
||||
}
|
||||
|
||||
get initialValue() {
|
||||
return this.field.value?.data[0];
|
||||
}
|
||||
get control() {
|
||||
return this.QrCodeForm.controls[this.field.name];
|
||||
}
|
||||
get value() {
|
||||
return this.control?.value || [];
|
||||
}
|
||||
set value(asset: any) {
|
||||
this.asset = asset;
|
||||
this.control?.setValue(asset?.id);
|
||||
}
|
||||
|
||||
|
||||
setValue(value: any) {
|
||||
this.control.setValue(value);
|
||||
this.control.markAsTouched();
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.control.markAsTouched();
|
||||
this.value = null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
onFileInput(event: any) {
|
||||
this.uploadFiles(event.target.files);
|
||||
event.target.value = null;
|
||||
}
|
||||
|
||||
uploadFiles(files: any) {
|
||||
for (let file of files) {
|
||||
this.uploadFile(file);
|
||||
}
|
||||
}
|
||||
uploadFile(file: File) {
|
||||
this.upload.file = file;
|
||||
this.assetsService.upload(file).subscribe((result) => {
|
||||
if (result.type === HttpEventType.UploadProgress) {
|
||||
this.upload.progress = Math.round(100 * result.loaded / result.total);
|
||||
} else if (result instanceof HttpResponse) {
|
||||
this.upload = {file: null, progress: 0};
|
||||
this.value = result.body.data;
|
||||
this.control.markAsTouched();
|
||||
}
|
||||
}, error => {
|
||||
this.upload = {file: null, progress: 0};
|
||||
alert(error.error.message);
|
||||
});
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue