Открыть боковую панель
Teknokomo
universo-monorepo
Коммиты
594083d6
Коммит
594083d6
создал
Авг 05, 2023
по автору
Georgiy Medvedev
Просмотр файлов
Merge branch universo-monorepo:main into main
владельцы
e3722ab1
b8becc66
Изменения
5
Скрыть пробелы
Построчно
Рядом
universo-frontend/src/layouts/MainLayout.vue
Просмотр файла @
594083d6
...
@@ -63,13 +63,13 @@ const linksList = [
...
@@ -63,13 +63,13 @@ const linksList = [
title
:
'
Страница авторизации
'
,
title
:
'
Страница авторизации
'
,
caption
:
'
Страница
'
,
caption
:
'
Страница
'
,
icon
:
'
school
'
,
icon
:
'
school
'
,
link
:
''
link
:
'
http://localhost:9000/register
'
},
},
{
{
title
:
'
Страница регистрации
'
,
title
:
'
Страница регистрации
'
,
caption
:
'
Страница
'
,
caption
:
'
Страница
'
,
icon
:
'
school
'
,
icon
:
'
school
'
,
link
:
''
link
:
'
http://localhost:9000/zareg
'
},
},
{
{
title
:
'
Страница с политикой в отношении обработки персональных данных
'
,
title
:
'
Страница с политикой в отношении обработки персональных данных
'
,
...
...
universo-frontend/src/pages/PhaserTest.vue
Просмотр файла @
594083d6
<
template
>
<
template
>
<q-layout
view=
"hHh lpR fFf"
>
<q-layout
view=
"hHh lpR fFf"
>
<!-- Шапка страницы -->
<q-header
elevated
>
<q-header
elevated
>
<q-toolbar>
<q-toolbar>
<!-- Кнопка для открытия/закрытия бокового меню -->
<q-btn
<q-btn
dense
dense
flat
flat
...
@@ -15,19 +17,45 @@
...
@@ -15,19 +17,45 @@
</q-toolbar>
</q-toolbar>
</q-header>
</q-header>
<!-- Боковое меню -->
<q-drawer
<q-drawer
v-model=
"drawer"
v-model=
"drawer"
show-if-above
show-if-above
>
>
<!-- Кнопка для добавления новой доски -->
<q-btn
<q-btn
color=
"primary"
color=
"Standard"
@
click=
"addRectangle"
text-color=
"#00000000"
@
click=
"addBoard"
label=
"Добавить доску"
label=
"Добавить доску"
/>
/>
</q-drawer>
</q-drawer>
<!-- Контейнер для основного содержимого страницы -->
<q-page-container
class=
"grid-center"
>
<q-page-container
class=
"grid-center"
>
<!-- Контейнер для игры Phaser -->
<div
ref=
"gameRef"
></div>
<div
ref=
"gameRef"
></div>
<!-- Скрытое поле ввода для редактирования заголовков досок -->
<input
ref=
"inputRef"
v-model=
"editingText"
@
input=
"updateTitle"
@
blur=
"stopEditing"
style=
"position: absolute; display: none"
/>
<!-- Цикл для создания элементов для каждой доски -->
<div
v-for=
"(board, index) in boards"
:key=
"index"
class=
"board-inputs"
>
<input
v-model=
"board.userTitle"
placeholder=
"Board title"
:class=
"
{ 'highlighted': board.isHighlighted }"
@click="toggleHighlight(board)"
/>
<div
v-for=
"(column, columnIndex) in board.userColumns"
:key=
"columnIndex"
class=
"column-inputs"
>
<input
v-model=
"column.title"
placeholder=
"Column title"
/>
<textarea
v-model=
"column.text"
placeholder=
"Column text"
></textarea>
</div>
</div>
</q-page-container>
</q-page-container>
</q-layout>
</q-layout>
</
template
>
</
template
>
...
@@ -37,59 +65,283 @@ import { ref, onMounted } from 'vue';
...
@@ -37,59 +65,283 @@ import { ref, onMounted } from 'vue';
import
*
as
Phaser
from
'
phaser
'
;
import
*
as
Phaser
from
'
phaser
'
;
import
{
defineComponent
}
from
'
vue
'
;
import
{
defineComponent
}
from
'
vue
'
;
class
Board
{
public
board
:
Phaser
.
GameObjects
.
Rectangle
;
public
draggableArea
:
Phaser
.
GameObjects
.
Rectangle
;
public
title
:
Phaser
.
GameObjects
.
Text
;
private
columns
:
Phaser
.
GameObjects
.
Rectangle
[]
=
[];
private
subColumns
:
Phaser
.
GameObjects
.
Rectangle
[][]
=
[];
private
columnTitles
:
Phaser
.
GameObjects
.
Text
[]
=
[];
public
cards
:
Phaser
.
GameObjects
.
Rectangle
[]
=
[];
public
selectedCard
:
Phaser
.
GameObjects
.
Rectangle
|
null
=
null
;
public
cardOffsetX
=
0
;
public
cardOffsetY
=
0
;
static
boards
:
Board
[]
=
[];
public
offsetX
=
0
;
public
offsetY
=
0
;
public
targetCardPosition
:
{
x
:
number
,
y
:
number
}
|
null
=
null
;
private
lines
:
Phaser
.
GameObjects
.
Rectangle
[]
=
[];
public
highlightedSubColumn
:
Phaser
.
GameObjects
.
Rectangle
|
null
=
null
;
userTitle
=
''
;
userColumns
:
{
title
:
string
,
text
:
string
}[]
=
[
{
title
:
''
,
text
:
''
},
{
title
:
''
,
text
:
''
},
{
title
:
''
,
text
:
''
},
];
isHighlighted
=
false
;
constructor
(
private
scene
:
Phaser
.
Scene
,
x
:
number
,
y
:
number
,
title
:
string
)
{
this
.
board
=
scene
.
add
.
rectangle
(
x
,
y
+
200
,
600
,
400
,
0xBDBDBD
).
setStrokeStyle
(
1
,
0x000000
);
this
.
draggableArea
=
scene
.
add
.
rectangle
(
x
,
y
+
25
,
600
,
50
,
0xff0000
,
0
).
setInteractive
().
setStrokeStyle
(
1
,
0x000000
);
this
.
title
=
scene
.
add
.
text
(
x
-
282
,
y
+
15
,
title
,
{
color
:
'
#000000
'
,
fontSize
:
'
20px
'
}).
setInteractive
();
this
.
title
.
on
(
'
pointerdown
'
,
()
=>
{
this
.
isHighlighted
=
!
this
.
isHighlighted
;
this
.
title
.
setColor
(
this
.
isHighlighted
?
'
#ffff00
'
:
'
#000000
'
);
});
for
(
let
i
=
0
;
i
<
3
;
i
++
)
{
const
column
=
scene
.
add
.
rectangle
(
x
-
200
+
i
*
200
,
y
+
140
,
170
,
90
,
0x000000
).
setInteractive
();
const
columnTitle
=
scene
.
add
.
text
(
column
.
x
-
82
,
column
.
y
-
70
,
`Column
${
i
+
1
}
`
,
{
color
:
'
#000000
'
});
this
.
columns
.
push
(
column
);
this
.
columnTitles
.
push
(
columnTitle
);
this
.
subColumns
[
i
]
=
[];
for
(
let
j
=
0
;
j
<
2
;
j
++
)
{
const
subColumn
=
scene
.
add
.
rectangle
(
x
-
200
+
i
*
200
,
y
+
240
+
j
*
100
,
170
,
90
,
0x000000
).
setInteractive
();
this
.
subColumns
[
i
].
unshift
(
subColumn
);
}
if
(
i
===
0
||
i
===
2
)
{
const
line
=
scene
.
add
.
rectangle
((
i
===
0
?
column
.
x
+
100
:
column
.
x
-
100
),
y
+
238
,
2
,
300
,
0x000000
);
this
.
lines
.
push
(
line
);
}
}
this
.
scene
.
input
.
on
(
'
pointermove
'
,
(
pointer
:
Phaser
.
Input
.
Pointer
)
=>
{
if
(
this
.
selectedCard
)
{
this
.
selectedCard
.
setPosition
(
pointer
.
x
-
this
.
cardOffsetX
,
pointer
.
y
-
this
.
cardOffsetY
);
this
.
columns
.
forEach
((
column
)
=>
{
if
(
column
&&
this
.
selectedCard
&&
Phaser
.
Geom
.
Intersects
.
RectangleToRectangle
(
this
.
selectedCard
.
getBounds
(),
column
.
getBounds
()))
{
this
.
targetCardPosition
=
{
x
:
column
.
x
,
y
:
column
.
y
};
}
});
// Reset the stroke style of the previously highlighted sub-column
if
(
this
.
highlightedSubColumn
)
{
this
.
highlightedSubColumn
.
setStrokeStyle
(
1
,
0x000000
);
this
.
highlightedSubColumn
=
null
;
}
this
.
subColumns
.
forEach
((
subColumns
)
=>
{
subColumns
.
forEach
((
subColumn
)
=>
{
if
(
subColumn
&&
this
.
selectedCard
&&
Phaser
.
Geom
.
Intersects
.
RectangleToRectangle
(
this
.
selectedCard
.
getBounds
(),
subColumn
.
getBounds
()))
{
this
.
targetCardPosition
=
{
x
:
subColumn
.
x
,
y
:
subColumn
.
y
};
// Set the stroke style of the highlighted sub-column
subColumn
.
setStrokeStyle
(
2
,
0xff0000
);
this
.
highlightedSubColumn
=
subColumn
;
}
});
});
Board
.
boards
.
forEach
((
board
)
=>
{
if
(
board
!==
this
&&
board
.
contains
(
pointer
.
x
,
pointer
.
y
))
{
this
.
cards
=
this
.
cards
.
filter
(
card
=>
card
!==
this
.
selectedCard
);
if
(
this
.
selectedCard
)
{
board
.
cards
.
push
(
this
.
selectedCard
);
}
}
});
}
});
}
addCard
(
columnIndex
:
number
,
subColumnIndex
?:
number
)
{
if
(
this
.
columns
.
length
>
columnIndex
)
{
let
targetColumn
;
if
(
subColumnIndex
!==
undefined
&&
this
.
subColumns
[
columnIndex
].
length
>
subColumnIndex
)
{
targetColumn
=
this
.
subColumns
[
columnIndex
][
subColumnIndex
];
}
else
{
targetColumn
=
this
.
columns
[
columnIndex
];
}
const
card
=
this
.
scene
.
add
.
rectangle
(
targetColumn
.
x
,
targetColumn
.
y
,
targetColumn
.
width
,
targetColumn
.
height
,
0xFFFFFF
).
setInteractive
().
setStrokeStyle
(
1
,
0x000000
);
this
.
cards
.
push
(
card
);
card
.
on
(
'
pointerdown
'
,
(
pointer
:
Phaser
.
Input
.
Pointer
)
=>
{
this
.
selectedCard
=
card
;
this
.
cardOffsetX
=
pointer
.
x
-
card
.
x
;
this
.
cardOffsetY
=
pointer
.
y
-
card
.
y
;
});
card
.
on
(
'
pointerup
'
,
()
=>
{
if
(
this
.
targetCardPosition
)
{
this
.
selectedCard
.
setPosition
(
this
.
targetCardPosition
.
x
,
this
.
targetCardPosition
.
y
);
this
.
targetCardPosition
=
null
;
}
if
(
this
.
highlightedSubColumn
)
{
this
.
highlightedSubColumn
.
setStrokeStyle
(
1
,
0x000000
);
this
.
highlightedSubColumn
=
null
;
}
this
.
selectedCard
=
null
;
});
}
}
contains
(
x
:
number
,
y
:
number
)
{
return
this
.
board
.
getBounds
().
contains
(
x
,
y
);
}
setPosition
(
x
:
number
,
y
:
number
)
{
const
dx
=
x
-
this
.
board
.
x
;
const
dy
=
y
-
this
.
board
.
y
;
this
.
board
.
setPosition
(
x
,
y
);
this
.
title
.
setPosition
(
this
.
title
.
x
+
dx
,
this
.
title
.
y
+
dy
);
this
.
draggableArea
.
setPosition
(
this
.
draggableArea
.
x
+
dx
,
this
.
draggableArea
.
y
+
dy
);
this
.
columns
.
forEach
((
column
,
i
)
=>
{
column
.
setPosition
(
column
.
x
+
dx
,
column
.
y
+
dy
);
if
(
i
<
this
.
columnTitles
.
length
)
{
this
.
columnTitles
[
i
].
setPosition
(
this
.
columnTitles
[
i
].
x
+
dx
,
this
.
columnTitles
[
i
].
y
+
dy
);
}
});
this
.
subColumns
.
forEach
((
subColumns
)
=>
{
subColumns
.
forEach
((
subColumn
)
=>
{
subColumn
.
setPosition
(
subColumn
.
x
+
dx
,
subColumn
.
y
+
dy
);
});
});
this
.
lines
.
forEach
((
line
)
=>
{
line
.
setPosition
(
line
.
x
+
dx
,
line
.
y
+
dy
);
});
this
.
cards
.
forEach
((
card
)
=>
{
if
(
card
===
this
.
selectedCard
)
{
card
.
setPosition
(
x
-
this
.
cardOffsetX
,
y
-
this
.
cardOffsetY
);
}
else
{
card
.
setPosition
(
card
.
x
+
dx
,
card
.
y
+
dy
);
}
});
}
}
export
default
defineComponent
({
export
default
defineComponent
({
setup
()
{
setup
()
{
const
gameRef
=
ref
(
null
);
const
gameRef
=
ref
<
HTMLDivElement
|
null
>
(
null
);
const
drawer
=
ref
(
false
);
const
drawer
=
ref
(
false
);
let
game
:
Phaser
.
Game
;
let
game
:
Phaser
.
Game
|
null
=
null
;
let
rectangles
:
Phaser
.
GameObjects
.
Rectangle
[]
=
[];
let
selectedBoard
:
Board
|
null
=
null
;
let
selectedRectangle
:
Phaser
.
GameObjects
.
Rectangle
|
null
=
null
;
const
inputRef
=
ref
<
HTMLInputElement
|
null
>
(
null
);
const
editingText
=
ref
(
''
);
let
editingBoard
:
Board
|
null
=
null
;
const
updateTitle
=
()
=>
{
if
(
editingBoard
)
{
editingBoard
.
userTitle
=
editingText
.
value
;
editingBoard
.
title
.
setText
(
editingText
.
value
);
}
};
const
stopEditing
=
()
=>
{
if
(
inputRef
.
value
)
{
inputRef
.
value
.
style
.
display
=
'
none
'
;
}
if
(
editingBoard
)
{
editingBoard
.
isHighlighted
=
false
;
editingBoard
.
title
.
setColor
(
'
#000000
'
);
editingBoard
=
null
;
}
};
const
addRectangle
=
()
=>
{
const
addBoard
=
()
=>
{
const
rect
=
game
.
scene
.
scenes
[
0
].
add
.
rectangle
(
200
,
200
,
100
,
100
,
0xff0000
).
setInteractive
();
if
(
game
&&
game
.
scene
.
scenes
[
0
])
{
rectangles
.
push
(
rect
);
const
board
=
new
Board
(
game
.
scene
.
scenes
[
0
],
200
,
200
,
'
Board Title
'
);
board
.
addCard
(
0
);
board
.
addCard
(
0
,
0
);
board
.
addCard
(
0
,
1
);
board
.
addCard
(
1
);
board
.
addCard
(
1
,
1
);
Board
.
boards
.
push
(
board
);
}
};
const
toggleHighlight
=
(
board
:
Board
)
=>
{
board
.
isHighlighted
=
!
board
.
isHighlighted
;
};
};
onMounted
(()
=>
{
onMounted
(()
=>
{
const
config
:
Phaser
.
Types
.
Core
.
GameConfig
=
{
if
(
gameRef
.
value
)
{
type
:
Phaser
.
AUTO
,
const
config
:
Phaser
.
Types
.
Core
.
GameConfig
=
{
backgroundColor
:
'
#ffffff
'
,
type
:
Phaser
.
AUTO
,
parent
:
gameRef
.
value
,
backgroundColor
:
'
#ffffff
'
,
scene
:
{
parent
:
gameRef
.
value
,
create
:
function
()
{
scene
:
{
this
.
input
.
on
(
'
pointerdown
'
,
function
(
pointer
)
{
create
:
function
()
{
rectangles
.
forEach
(
rect
=>
{
this
.
input
.
on
(
'
pointerdown
'
,
(
pointer
:
Phaser
.
Input
.
Pointer
)
=>
{
if
(
rect
.
getBounds
().
contains
(
pointer
.
x
,
pointer
.
y
))
{
Board
.
boards
.
forEach
(
board
=>
{
selectedRectangle
=
rect
;
if
(
board
.
draggableArea
.
getBounds
().
contains
(
pointer
.
x
,
pointer
.
y
))
{
selectedBoard
=
board
;
board
.
offsetX
=
pointer
.
x
-
board
.
board
.
x
;
board
.
offsetY
=
pointer
.
y
-
board
.
board
.
y
;
this
.
events
.
on
(
'
editTitle
'
,
(
board
:
Board
)
=>
{
if
(
inputRef
.
value
)
{
editingBoard
=
board
;
editingText
.
value
=
board
.
userTitle
;
const
rect
=
board
.
title
.
getBounds
();
inputRef
.
value
.
style
.
left
=
`
${
rect
.
x
}
px`
;
inputRef
.
value
.
style
.
top
=
`
${
rect
.
y
}
px`
;
inputRef
.
value
.
style
.
width
=
`
${
rect
.
width
}
px`
;
inputRef
.
value
.
style
.
display
=
'
block
'
;
inputRef
.
value
.
focus
();
}
});
}
});
});
this
.
input
.
on
(
'
pointerup
'
,
()
=>
{
selectedBoard
=
null
;
});
this
.
input
.
on
(
'
pointermove
'
,
(
pointer
:
Phaser
.
Input
.
Pointer
)
=>
{
if
(
selectedBoard
)
{
selectedBoard
.
setPosition
(
pointer
.
x
-
selectedBoard
.
offsetX
,
pointer
.
y
-
selectedBoard
.
offsetY
);
if
(
selectedBoard
.
selectedCard
)
{
selectedBoard
.
selectedCard
.
setPosition
(
pointer
.
x
-
selectedBoard
.
cardOffsetX
,
pointer
.
y
-
selectedBoard
.
cardOffsetY
);
}
}
}
});
});
});
}
this
.
input
.
on
(
'
pointerup
'
,
function
()
{
selectedRectangle
=
null
;
});
this
.
input
.
on
(
'
pointermove
'
,
function
(
pointer
)
{
if
(
selectedRectangle
)
{
selectedRectangle
.
x
=
pointer
.
x
;
selectedRectangle
.
y
=
pointer
.
y
;
}
});
}
}
}
};
};
game
=
new
Phaser
.
Game
(
config
);
game
=
new
Phaser
.
Game
(
config
);
}
});
});
return
{
return
{
gameRef
,
gameRef
,
drawer
,
drawer
,
addRectangle
addBoard
,
};
toggleHighlight
,
inputRef
,
editingText
,
updateTitle
,
stopEditing
,
boards
:
Board
.
boards
}
}
}
});
});
</
script
>
</
script
>
<
style
scoped
>
.highlighted
{
background-color
:
yellow
;
/* Измените на нужный цвет выделения */
}
</
style
>
universo-frontend/src/pages/RegistrStr.vue
0 → 100644
Просмотр файла @
594083d6
<
template
>
<q-page
class=
"flex bg-image flex-center"
>
<div
class=
"q-pa-md row justify-center items-center"
>
<q-avatar
size=
"300px"
class=
"bg-white text-purple"
>
<img
src=
"https://img.freepik.com/free-photo/flags-russia_1232-3062.jpg?t=st=1690649797~exp=1690650397~hmac=f8f0497df1d20075c4c58ebd9af6a6b4a99b511a82bc2d15c2b742cbb5adae99"
class=
"absolute-center shadow-10"
>
</q-avatar>
</div
>
<q-card-section>
<div
class=
"text-center q-pt-lg"
>
<div
class=
"col text-h6 ellipsis"
>
<q-form
@
submit=
"submitForm"
>
<q-input
outlined
v-model=
"email"
label=
"Email"
type=
"email"
color=
"pink"
/>
<q-input
outlined
v-model=
"phone"
label=
"Phone +7"
type=
"tel"
color=
"pink"
/>
<q-input
outlined
v-model=
"surname"
label=
"Surname"
color=
"pink"
/>
<q-input
outlined
v-model=
"name"
label=
"Name"
color=
"pink"
/>
<q-input
outlined
v-model=
"password"
label=
"Password"
type=
"password"
color=
"pink"
/>
<q-input
outlined
v-model=
"confirmPassword"
label=
"Confirm Password"
type=
"password"
color=
"pink"
/>
<q-btn
type=
"submit"
label=
"Register"
class=
"q-mt-md"
color=
"pink"
/>
</q-form>
</div>
</div>
</q-card-section>
</q-page>
</
template
>
<
script
lang=
"ts"
>
import
{
defineComponent
}
from
'
vue
'
export
default
defineComponent
({
name
:
'
RegistrationPage
'
,
data
()
{
return
{
email
:
''
,
phone
:
''
,
surname
:
''
,
name
:
''
,
password
:
''
,
confirmPassword
:
''
}
},
methods
:
{
submitForm
()
{
if
(
this
.
password
!==
this
.
confirmPassword
)
{
alert
(
"
Passwords don't match
"
);
return
;
}
},
},
})
</
script
>
<
style
scoped
>
.bg-image
{
background-image
:
linear-gradient
(
135deg
,
#7028e4
0%
,
#5a0c30
100%
);
}
</
style
>
universo-frontend/src/pages/RegistrationPage.vue
0 → 100644
Просмотр файла @
594083d6
import { LocalStorage } from 'quasar'
<
template
>
<q-layout>
<q-page-container>
<q-page
class=
"flex bg-image flex-center"
>
<q-card
v-bind:style=
"$q.screen.lt.sm?
{'width': '80%'}:{'width':'30%'}">
<q-card-section>
<q-avatar
size=
"103px"
class=
"absolute-center shadow-10"
>
<img
src=
"https://img.freepik.com/free-photo/glowing-spaceship-orbits-planet-in-starry-galaxy-generated-by-ai_188544-9655.jpg?w=740&t=st=1690464256~exp=1690464856~hmac=f54129b3d446065615f6e8602c35056df42ad5eeb0f3c7a858da1d51d9673b52"
>
</q-avatar>
</q-card-section>
<q-card-section>
<div
class=
"text-center q-pt-lg"
>
<div
class=
"col text-h6 ellipsis"
>
Log in
</div>
</div>
</q-card-section>
<q-card-section>
<q-form
class=
"q-gutter-md"
>
<q-input
filled
v-model=
"username"
label=
"Username"
lazy-rules
/>
<q-input
type=
"password"
filled
v-model=
"password"
label=
"Password"
lazy-rules
/>
<div>
<q-btn
label=
"Login"
to=
"/"
type=
"button"
color=
"primary"
/>
</div>
</q-form>
</q-card-section>
</q-card>
</q-page>
</q-page-container>
</q-layout>
</
template
>
<
script
lang=
"ts"
>
import
{
defineComponent
,
ref
}
from
'
vue
'
;
export
default
defineComponent
({
setup
()
{
const
username
=
ref
(
''
);
const
password
=
ref
(
''
);
return
{
username
,
password
,
};
},
});
</
script
>
<
style
scoped
>
.bg-image
{
background-image
:
linear-gradient
(
135deg
,
#7028e4
0%
,
#e5b2ca
100%
);
}
</
style
>
universo-frontend/src/router/routes.ts
Просмотр файла @
594083d6
...
@@ -8,6 +8,8 @@ const routes: RouteRecordRaw[] = [
...
@@ -8,6 +8,8 @@ const routes: RouteRecordRaw[] = [
{
path
:
''
,
component
:
()
=>
import
(
'
pages/IndexPage.vue
'
)
},
{
path
:
''
,
component
:
()
=>
import
(
'
pages/IndexPage.vue
'
)
},
{
path
:
'
/user
'
,
component
:
()
=>
import
(
'
pages/UserProfile.vue
'
)},
{
path
:
'
/user
'
,
component
:
()
=>
import
(
'
pages/UserProfile.vue
'
)},
{
path
:
'
/phaser
'
,
component
:
()
=>
import
(
'
pages/PhaserTest.vue
'
)},
{
path
:
'
/phaser
'
,
component
:
()
=>
import
(
'
pages/PhaserTest.vue
'
)},
{
path
:
'
/register
'
,
component
:
()
=>
import
(
'
pages/RegistrationPage.vue
'
),
},
{
path
:
'
/zareg
'
,
component
:
()
=>
import
(
'
pages/RegistrStr.vue
'
),
},
],
],
},
},
...
...
Редактирование
Предварительный просмотр
Поддерживает Markdown
0%
Попробовать снова
или
прикрепить новый файл
.
Отмена
You are about to add
0
people
to the discussion. Proceed with caution.
Сначала завершите редактирование этого сообщения!
Отмена
Пожалуйста,
зарегистрируйтесь
или
войдите
чтобы прокомментировать