add player

This commit is contained in:
Stepan Fedyanin 2024-06-14 17:34:20 +05:00
parent f433380466
commit 9d8d874607
26 changed files with 1229 additions and 907 deletions

126
client/package-lock.json generated
View File

@ -11,7 +11,6 @@
"@formkit/i18n": "^1.6.3",
"@formkit/vue": "^1.6.3",
"@quasar/extras": "^1.16.4",
"@vueuse/components": "^10.10.0",
"axios": "^1.2.1",
"core-js": "^3.6.5",
"esm": "^3.2.25",
@ -22,6 +21,7 @@
"scrolltrigger": "^1.0.1",
"swiper": "^11.1.3",
"vue": "^3.3.13",
"vue-content-loader": "^2.0.1",
"vue-final-modal": "^4.5.4",
"vue-router": "^4.0.0",
"vuex": "^4.0.1",
@ -3990,74 +3990,6 @@
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.27.tgz",
"integrity": "sha512-DL3NmY2OFlqmYYrzp39yi3LDkKxa5vZVwxWdQ3rG0ekuWscHraeIbnI8t+aZK7qhYqEqWKTUdijadunb9pnrgA=="
},
"node_modules/@vueuse/components": {
"version": "10.10.0",
"resolved": "https://registry.npmjs.org/@vueuse/components/-/components-10.10.0.tgz",
"integrity": "sha512-HiA10NQ9HJAGnju+8ZK4TyA8LIc0a6BnJmVWDa/k+TRhaYCVacSDU04k0BQ2otV+gghUDdwu98upf6TDRXpoeg==",
"dependencies": {
"@vueuse/core": "10.10.0",
"@vueuse/shared": "10.10.0",
"vue-demi": ">=0.14.7"
}
},
"node_modules/@vueuse/components/node_modules/@vueuse/core": {
"version": "10.10.0",
"resolved": "https://registry.npmjs.org/@vueuse/core/-/core-10.10.0.tgz",
"integrity": "sha512-vexJ/YXYs2S42B783rI95lMt3GzEwkxzC8Hb0Ndpd8rD+p+Lk/Za4bd797Ym7yq4jXqdSyj3JLChunF/vyYjUw==",
"dependencies": {
"@types/web-bluetooth": "^0.0.20",
"@vueuse/metadata": "10.10.0",
"@vueuse/shared": "10.10.0",
"vue-demi": ">=0.14.7"
},
"funding": {
"url": "https://github.com/sponsors/antfu"
}
},
"node_modules/@vueuse/components/node_modules/@vueuse/metadata": {
"version": "10.10.0",
"resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-10.10.0.tgz",
"integrity": "sha512-UNAo2sTCAW5ge6OErPEHb5z7NEAg3XcO9Cj7OK45aZXfLLH1QkexDcZD77HBi5zvEiLOm1An+p/4b5K3Worpug==",
"funding": {
"url": "https://github.com/sponsors/antfu"
}
},
"node_modules/@vueuse/components/node_modules/@vueuse/shared": {
"version": "10.10.0",
"resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-10.10.0.tgz",
"integrity": "sha512-2aW33Ac0Uk0U+9yo3Ypg9s5KcR42cuehRWl7vnUHadQyFvCktseyxxEPBi1Eiq4D2yBGACOnqLZpx1eMc7g5Og==",
"dependencies": {
"vue-demi": ">=0.14.7"
},
"funding": {
"url": "https://github.com/sponsors/antfu"
}
},
"node_modules/@vueuse/components/node_modules/vue-demi": {
"version": "0.14.8",
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.8.tgz",
"integrity": "sha512-Uuqnk9YE9SsWeReYqK2alDI5YzciATE0r2SkA6iMAtuXvNTMNACJLJEXNXaEy94ECuBe4Sk6RzRU80kjdbIo1Q==",
"hasInstallScript": true,
"bin": {
"vue-demi-fix": "bin/vue-demi-fix.js",
"vue-demi-switch": "bin/vue-demi-switch.js"
},
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/antfu"
},
"peerDependencies": {
"@vue/composition-api": "^1.0.0-rc.1",
"vue": "^3.0.0-0 || ^2.6.0"
},
"peerDependenciesMeta": {
"@vue/composition-api": {
"optional": true
}
}
},
"node_modules/@vueuse/core": {
"version": "10.9.0",
"resolved": "https://registry.npmjs.org/@vueuse/core/-/core-10.9.0.tgz",
@ -15061,6 +14993,14 @@
}
}
},
"node_modules/vue-content-loader": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/vue-content-loader/-/vue-content-loader-2.0.1.tgz",
"integrity": "sha512-pkof4+q2xmzNEdhqelxtJejeP/vQUJtLle4/v2ueG+HURqM9Q/GIGC8GJ2bVVWeLfTDET51jqimwQdmxJTlu0g==",
"peerDependencies": {
"vue": "^3"
}
},
"node_modules/vue-eslint-parser": {
"version": "9.3.2",
"resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-9.3.2.tgz",
@ -18623,48 +18563,6 @@
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.27.tgz",
"integrity": "sha512-DL3NmY2OFlqmYYrzp39yi3LDkKxa5vZVwxWdQ3rG0ekuWscHraeIbnI8t+aZK7qhYqEqWKTUdijadunb9pnrgA=="
},
"@vueuse/components": {
"version": "10.10.0",
"resolved": "https://registry.npmjs.org/@vueuse/components/-/components-10.10.0.tgz",
"integrity": "sha512-HiA10NQ9HJAGnju+8ZK4TyA8LIc0a6BnJmVWDa/k+TRhaYCVacSDU04k0BQ2otV+gghUDdwu98upf6TDRXpoeg==",
"requires": {
"@vueuse/core": "10.10.0",
"@vueuse/shared": "10.10.0",
"vue-demi": ">=0.14.7"
},
"dependencies": {
"@vueuse/core": {
"version": "10.10.0",
"resolved": "https://registry.npmjs.org/@vueuse/core/-/core-10.10.0.tgz",
"integrity": "sha512-vexJ/YXYs2S42B783rI95lMt3GzEwkxzC8Hb0Ndpd8rD+p+Lk/Za4bd797Ym7yq4jXqdSyj3JLChunF/vyYjUw==",
"requires": {
"@types/web-bluetooth": "^0.0.20",
"@vueuse/metadata": "10.10.0",
"@vueuse/shared": "10.10.0",
"vue-demi": ">=0.14.7"
}
},
"@vueuse/metadata": {
"version": "10.10.0",
"resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-10.10.0.tgz",
"integrity": "sha512-UNAo2sTCAW5ge6OErPEHb5z7NEAg3XcO9Cj7OK45aZXfLLH1QkexDcZD77HBi5zvEiLOm1An+p/4b5K3Worpug=="
},
"@vueuse/shared": {
"version": "10.10.0",
"resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-10.10.0.tgz",
"integrity": "sha512-2aW33Ac0Uk0U+9yo3Ypg9s5KcR42cuehRWl7vnUHadQyFvCktseyxxEPBi1Eiq4D2yBGACOnqLZpx1eMc7g5Og==",
"requires": {
"vue-demi": ">=0.14.7"
}
},
"vue-demi": {
"version": "0.14.8",
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.8.tgz",
"integrity": "sha512-Uuqnk9YE9SsWeReYqK2alDI5YzciATE0r2SkA6iMAtuXvNTMNACJLJEXNXaEy94ECuBe4Sk6RzRU80kjdbIo1Q==",
"requires": {}
}
}
},
"@vueuse/core": {
"version": "10.9.0",
"resolved": "https://registry.npmjs.org/@vueuse/core/-/core-10.9.0.tgz",
@ -26407,6 +26305,12 @@
"@vue/shared": "3.4.27"
}
},
"vue-content-loader": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/vue-content-loader/-/vue-content-loader-2.0.1.tgz",
"integrity": "sha512-pkof4+q2xmzNEdhqelxtJejeP/vQUJtLle4/v2ueG+HURqM9Q/GIGC8GJ2bVVWeLfTDET51jqimwQdmxJTlu0g==",
"requires": {}
},
"vue-eslint-parser": {
"version": "9.3.2",
"resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-9.3.2.tgz",

View File

@ -29,6 +29,7 @@
"scrolltrigger": "^1.0.1",
"swiper": "^11.1.3",
"vue": "^3.3.13",
"vue-content-loader": "^2.0.1",
"vue-final-modal": "^4.5.4",
"vue-router": "^4.0.0",
"vuex": "^4.0.1",

View File

@ -62,7 +62,7 @@ a {
}
html{
scrollbar-color: var(--color-black) var(--color-white-opacity);
scrollbar-color: var(--color-black) var(--bg-opacity);
scrollbar-gutter: stable;
scrollbar-width: thin;
}

View File

@ -1,326 +1,336 @@
@define-mixin __p {
position: relative;
z-index: 2;
&:first-child {
margin-top: 0;
}
&:last-child {
margin-bottom: 0;
}
&.m--mb-1 {
margin-bottom: 1rem;
}
&.m--mb-2 {
margin-bottom: 2rem;
}
&.m--mb-3 {
margin-bottom: 3rem;
}
&.m--mb-4 {
margin-bottom: 4rem;
}
&.m--mb-5 {
margin-bottom: 5rem;
}
}
@define-mixin h1 {
@mixin __p;
/*margin-top: 32px;*/
margin-bottom: 1.5rem;
font-size: 100px;
line-height: 1.2;
font-weight: 500;
color: var(--color-white);
padding-bottom: 6px;
@mixin responsive-l {
font-size: 84px;
}
@mixin responsive-m {
font-size: 48px;
}
@mixin responsive-s {
font-size: 36px;
}
}
@define-mixin h2 {
@mixin __p;
margin: 2.5rem 0;
font-size: 3.375rem;
line-height: 1.3;
font-weight: 500;
color: transparent;
padding-bottom: 6px;
background-clip: text;
background-image: var(--linear-gradient);
text-transform: uppercase;
&.m--white {
color: var(--color-white);
margin: 1.5rem 0;
}
&.m--border {
&:after {
margin-top: 10px;
content: '';
display: block;
background: url("./assets/img/icon/borderLine.svg");
max-width: 260px;
width: 100%;
height: 10px;
}
}
a {
text-decoration: none;
font-weight: 500;
color: var(--color-white);
transition: color 0.5s ease;
&:hover {
color: var(--color-white);
}
}
}
@define-mixin h3 {
@mixin __p;
margin-top: 24px;
margin-bottom: 16px;
font-weight: 700;
font-size: 20px;
line-height: 1.2;
}
@define-mixin h4 {
@mixin __p;
margin-top: 16px;
margin-bottom: 8px;
font-weight: 500;
font-size: 18px;
line-height: 1.2;
}
@define-mixin p {
@mixin __p;
margin-top: 8px;
margin-bottom: 8px;
}
@define-mixin ul {
@mixin p;
list-style: none;
padding: 0;
li {
padding-left: 15px;
position: relative;
margin-bottom: 16px;
/* &:before {
content: '';
position: absolute;
left: 0;
width: 15px;
height: 14px;
top: 3px;
background-size: contain;
background-position: center;
background-image: svg-load('assets/img/icons/vector.svg', fill=$color-red);
background-repeat: no-repeat;
}*/
&:before {
content: '';
position: absolute;
left: 0;
width: 5px;
height: 5px;
top: 7px;
background: var(--color-white);
border-radius: 50%;
z-index: 2;
&:first-child {
margin-top: 0;
}
&:last-child {
margin-bottom: 0;
margin-bottom: 0;
}
}
&.m--dots {
&.m--mb-1 {
margin-bottom: 1rem;
}
&.m--mb-2 {
margin-bottom: 2rem;
}
&.m--mb-3 {
margin-bottom: 3rem;
}
&.m--mb-4 {
margin-bottom: 4rem;
}
&.m--mb-5 {
margin-bottom: 5rem;
}
}
@define-mixin h1 {
@mixin __p;
/*margin-top: 32px;*/
margin-bottom: 1.5rem;
font-size: 100px;
line-height: 1.2;
font-weight: 500;
color: var(--color-white);
padding-bottom: 6px;
@mixin responsive-l {
font-size: 84px;
}
@mixin responsive-m {
font-size: 48px;
}
@mixin responsive-s {
font-size: 36px;
}
}
@define-mixin h2 {
@mixin __p;
margin: 2.5rem 0;
font-size: 3.375rem;
line-height: 1.3;
font-weight: 500;
color: transparent;
padding-bottom: 6px;
background-clip: text;
background-image: var(--linear-gradient);
text-transform: uppercase;
@mixin responsive-l {
font-size:40px;
line-height: 1.1;
}
@mixin responsive-m {
font-size:32px;
}
/*@mixin responsive-s {
font-size: 36px;
}*/
&.m--white {
color: var(--color-white);
margin: 1.5rem 0;
}
&.m--border {
&:after {
margin-top: 10px;
content: '';
display: block;
background: url("./assets/img/icon/borderLine.svg");
max-width: 260px;
width: 100%;
height: 10px;
}
}
a {
text-decoration: none;
font-weight: 500;
color: var(--color-white);
transition: color 0.5s ease;
&:hover {
color: var(--color-white);
}
}
}
@define-mixin h3 {
@mixin __p;
margin-top: 24px;
margin-bottom: 16px;
font-weight: 700;
font-size: 20px;
line-height: 1.2;
}
@define-mixin h4 {
@mixin __p;
margin-top: 16px;
margin-bottom: 8px;
font-weight: 500;
font-size: 18px;
line-height: 1.2;
}
@define-mixin p {
@mixin __p;
margin-top: 8px;
margin-bottom: 8px;
}
@define-mixin ul {
@mixin p;
list-style: none;
padding: 0;
li {
padding-left: 1rem;
padding-left: 15px;
position: relative;
margin-bottom: 16px;
&:before {
content: '';
top: 0;
height: auto;
width: auto;
background: none;
}
/* &:before {
content: '';
position: absolute;
left: 0;
width: 15px;
height: 14px;
top: 3px;
background-size: contain;
background-position: center;
background-image: svg-load('assets/img/icons/vector.svg', fill=$color-red);
background-repeat: no-repeat;
}*/
&:before {
content: '';
position: absolute;
left: 0;
width: 5px;
height: 5px;
top: 7px;
background: var(--color-white);
border-radius: 50%;
}
&:last-child {
margin-bottom: 0;
}
}
&.m--dots {
li {
padding-left: 1rem;
&:before {
content: '';
top: 0;
height: auto;
width: auto;
background: none;
}
}
}
}
}
@define-mixin ol {
@mixin p;
@mixin p;
list-style: none;
counter-reset: item;
li {
counter-increment: item;
margin-bottom: 16px;
vertical-align: middle;
list-style: none;
counter-reset: item;
li {
counter-increment: item;
margin-bottom: 16px;
vertical-align: middle;
&:before {
content: counter(item);
padding-left: 12px;
font-weight: 500;
font-size: 18px;
line-height: 24px;
color: var(--color-white);
&:before {
content: counter(item);
padding-left: 12px;
font-weight: 500;
font-size: 18px;
line-height: 24px;
color: var(--color-white);
}
}
}
}
@define-mixin hr {
@mixin p;
@mixin p;
}
@define-mixin a {
text-decoration: none;
font-weight: 500;
transition: all 0.5s ease;
color: var(--color-white);
position: relative;
z-index: 2;
&:hover {
color: transparent;
-webkit-background-clip: text;
background-image: var(--linear-gradient);
cursor: url("./assets/img/icon/cursor.svg"), auto;
text-decoration: none;
}
&.m--link {
display: flex;
align-items: center;
border-bottom: 1px solid transparent;
&::after {
margin-left: 10px;
content: '';
width: 20px;
height: 20px;
background: svg-load('./assets/img/icon/ArrowRight.svg', stroke=#FFFF) no-repeat 100%;
background-size: cover;
transition: background 0.3s ease, width 0.3s ease;
}
font-weight: 500;
transition: all 0.5s ease;
color: var(--color-white);
position: relative;
z-index: 2;
&:hover {
color: var(--color-white);
border-bottom: 1px solid var(--color-white);
color: transparent;
-webkit-background-clip: text;
background-image: var(--linear-gradient);
cursor: url("./assets/img/icon/cursor.svg"), auto;
text-decoration: none;
}
&:hover::after {
width: 40px;
&.m--link {
display: flex;
align-items: center;
border-bottom: 1px solid transparent;
&::after {
margin-left: 10px;
content: '';
width: 20px;
height: 20px;
background: svg-load('./assets/img/icon/ArrowRight.svg', stroke=#FFFF) no-repeat 100%;
background-size: cover;
transition: background 0.3s ease, width 0.3s ease;
}
&:hover {
color: var(--color-white);
border-bottom: 1px solid var(--color-white);
}
&:hover::after {
width: 40px;
}
}
&.m--underline {
text-decoration: underline;
}
}
&.m--underline{
text-decoration: underline;
}
}
@define-mixin a--color $color {
color: $color;
border-color: rgba($color, 0.5);
color: $color;
border-color: rgba($color, 0.5);
}
@define-mixin table {
/* dummy */
/* dummy */
}
@define-mixin base {
font: $font-size-base/$line-height-base $font-family-base;
color: $color-base;
font: $font-size-base/$line-height-base $font-family-base;
color: $color-base;
}
@define-mixin format {
h1:not(.nostyle) {
@mixin h1;
}
h1:not(.nostyle) {
@mixin h1;
}
h2:not(.nostyle) {
@mixin h2;
}
h2:not(.nostyle) {
@mixin h2;
}
h3:not(.nostyle) {
@mixin h3;
}
h3:not(.nostyle) {
@mixin h3;
}
h4:not(.nostyle) {
@mixin h4;
}
h4:not(.nostyle) {
@mixin h4;
}
p:not(.nostyle) {
@mixin p;
}
p:not(.nostyle) {
@mixin p;
}
ul:not(.nostyle) {
@mixin ul;
}
ul:not(.nostyle) {
@mixin ul;
}
ol:not(.nostyle) {
@mixin ol;
}
ol:not(.nostyle) {
@mixin ol;
}
hr:not(.nostyle) {
@mixin hr;
}
hr:not(.nostyle) {
@mixin hr;
}
a:not(.nostyle) {
@mixin a;
}
a:not(.nostyle) {
@mixin a;
}
table:not(.nostyle) {
@mixin table;
}
table:not(.nostyle) {
@mixin table;
}
b:not(.nostyle),
strong:not(.nostyle) {
font-weight: bold;
}
b:not(.nostyle),
strong:not(.nostyle) {
font-weight: bold;
}
i:not(.nostyle),
em:not(.nostyle) {
font-style: italic;
}
i:not(.nostyle),
em:not(.nostyle) {
font-style: italic;
}
u:not(.nostyle),
ins:not(.nostyle) {
text-decoration: underline;
}
u:not(.nostyle),
ins:not(.nostyle) {
text-decoration: underline;
}
strike:not(.nostyle),
del:not(.nostyle) {
text-decoration: line-through;
}
strike:not(.nostyle),
del:not(.nostyle) {
text-decoration: line-through;
}
}
@define-mixin text {
@mixin format;
@mixin format;
font: $font-size-text/$line-height-text $font-family-text;
color: $color-text;
font: $font-size-text/$line-height-text $font-family-text;
color: $color-text;
}

View File

@ -1,43 +1,81 @@
.footer{
padding-bottom: var(--space-between-block);
width: 100%;
&__top{
padding-bottom: 80px;
margin-bottom: 80px;
border-bottom: 1px solid var(--color-white-opacity);
}
&__question{
display: flex;
align-items: center;
justify-content: space-between;
}
&__bottom{
display: flex;
justify-content: space-between;
}
&__menu{
display: grid;
grid-template-columns: var(--space-between-sections) var(--space-between-sections);
grid-template-rows: min-content min-content;
grid-column-gap: var(--space-between-sections);
grid-row-gap: var(--space-between-block);
&-item{
.footer {
padding-bottom: var(--space-between-block);
width: 100%;
&__top {
padding-bottom: 80px;
margin-bottom: 80px;
border-bottom: 1px solid var(--color-white-opacity);
}
&-link{
font-size: 1.2rem;
&__question {
display: flex;
align-items: center;
justify-content: space-between;
gap: 10px;
@mixin responsive-xs {
align-items: flex-start;
flex-direction: column;
}
&-button {
margin-left: auto;
}
}
&__bottom {
display: flex;
justify-content: space-between;
flex-wrap: wrap;
}
&__menu {
display: grid;
grid-template-columns: var(--space-between-sections) var(--space-between-sections);
grid-template-rows: fit-content fit-content;
grid-column-gap: var(--space-between-sections);
grid-row-gap: var(--space-between-block);
@mixin responsive-m {
width: 100%;
grid-row-gap: 10px;
grid-template-rows: min-content;
grid-template-columns: min-content min-content min-content;
margin-bottom: var(--space-between-block);
}
@mixin responsive-s {
display: flex;
flex-wrap: wrap;
}
&-item {
@mixin responsive-s {
width: 100%;
}
}
&-link {
font-size: 1.2rem;
}
}
&__social {
display: flex;
flex-wrap: wrap;
gap: var(--space-between-block);
@mixin responsive-m {
margin-bottom: var(--space-between-block);
grid-row-gap: 10px;
}
}
&__connection {
display: flex;
flex-direction: column;
align-items: flex-end;
justify-content: space-between;
gap: 1rem;
@mixin responsive-m {
width: 100%;
align-items: flex-start;
}
}
}
&__social{
display: flex;
flex-wrap: wrap;
gap: var(--space-between-block);
}
&__connection{
display: flex;
flex-direction: column;
align-items: flex-end;
justify-content: space-between;
gap: 1rem;
}
}

View File

@ -5,19 +5,39 @@
display: flex;
justify-content: space-between;
align-items: start;
flex-wrap: wrap;
margin-bottom: var(--space-between-block);
row-gap: var(--space-between-block);
&-title{
margin-bottom: 0;
}
&-btn{
margin-left: auto;
@mixin responsive-xxs {
width: 100%;
}
}
}
&__content {
display: flex;
gap: 80px;
@mixin responsive-m {
flex-direction: column;
}
}
&__cover {
width: 50%;
width: 100%;
max-height: 640px;
background: url("./assets/img/bg/blog.svg") no-repeat center;
position: relative;
z-index: 2;
background-size: cover;
@mixin responsive-m {
height: calc(var(--base-content-size) / 2);
max-height: none;
}
}
&__list {

View File

@ -118,7 +118,7 @@
display: flex;
justify-content: space-between;
flex-wrap: wrap;
margin-bottom: var(--space-between-sections);
margin-bottom: var(--space-between-sections);
@mixin responsive-m {
gap: var(--space-between-block);
}
@ -135,6 +135,10 @@
color: transparent;
background-clip: text;
background-image: var(--linear-gradient);
@mixin responsive-s {
flex-direction: column;
gap: 10px;
}
&.m--circle {
align-items: end;
@ -144,6 +148,12 @@
width: 130px;
height: 130px;
background: url("./assets/img/icon/circleGradient.svg") no-repeat center;
@mixin responsive-s {
margin-right: auto;
width: 80px;
height: 80px;
background-size: contain;
}
}
}
@ -154,6 +164,9 @@
@mixin responsive-m {
width: 75%;
}
@mixin responsive-s {
width: 100%;
}
}
}
}
@ -163,6 +176,7 @@
@mixin responsive-m {
display: flex;
align-items: flex-start;
flex-wrap: wrap;
gap: calc(var(--space-between-block) / 2);
max-width: none;
}
@ -171,11 +185,16 @@
flex-direction: column;
align-items: flex-start;
font-size: 16px;
margin-bottom: 2.5rem;
position: relative;
z-index: 2;
&:not(&:last-child){
margin-bottom: var(--space-between-block);
}
@mixin responsive-m {
width: calc(100% / 3);
width: calc((100% - var(--space-between-block)) / 3);
}
@mixin responsive-s {
width: 100%;
}
&:before {
content: '';
@ -234,10 +253,15 @@
display: flex;
justify-content: space-between;
align-items: flex-end;
flex-wrap: wrap;
row-gap: var(--space-between-block);
}
&--description {
max-width: 45%;
@mixin responsive-xs {
max-width: 100%;
}
}
&--btns {

View File

@ -5,16 +5,31 @@
display: flex;
justify-content: space-between;
align-items: start;
flex-wrap: wrap;
&-title{
margin-bottom: 0;
}
}
&__description{
max-width: 60%;
margin-bottom: 2.5rem;
@mixin responsive-xxs {
max-width: none;
}
&.m--50{
margin-bottom: 0;
max-width: 50%;
width: 100%;
}
}
&__link{
@mixin responsive-xxs {
order: 1;
margin: var(--space-between-block) 0;
width: 100%;
}
}
&__list{
position: relative;
padding-bottom: 10px;
@ -52,6 +67,8 @@
transition: all .3s ease;
margin-right: auto;
color: var(--color-white);
text-wrap: nowrap;
padding-right: 10px;
}
&__btn{
transition: all .35s ease, visibility .0s ease;

View File

@ -1,79 +1,122 @@
.team {
margin-left: auto;
max-width: calc(100vw - ((100vw - var(--container)) / 2));
margin-bottom: var(--space-between-sections);
position: relative;
z-index: 2;
&__header {
display: flex;
}
&__title {
margin-right: auto;
}
&__slider {
&:hover {
cursor: url("./assets/img/icon/cursorDragAndDrop.svg"), auto;
}
.swiper-wrapper {
}
}
&__item {
}
&__tools {
margin-top: 65px;
display: flex;
justify-content: space-between;
max-width: var(--container);
}
&__progress {
width: 100%;
height: 4px;
margin-left: auto;
max-width: calc(100vw - ((100vw - var(--container)) / 2));
margin-bottom: var(--space-between-sections);
position: relative;
max-width: 460px;
background: var(--color-white-opacity);
border-radius: 20px;
overflow: hidden;
span {
background: var(--color-white);
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
transform-origin: left top;
z-index: 2;
@mixin responsive-xl {
padding: 0 0 0 20px;
}
}
&__cover {
margin-bottom: 0.5rem;
height: 450px;
overflow: hidden;
}
&__name {
font-weight: 500;
font-size: 1.5rem;
display: flex;
flex-direction: column;
gap: 0.5rem;
span {
font-weight: 400;
font-size: 1.125rem;
&__header {
display: flex;
max-width: var(--container);
margin-right: auto;
padding: 0 50px 0;
margin-bottom: var(--space-between-block);
@mixin responsive-xl {
padding: 0 40px 0 0;
column-gap: var(--space-between-block);
}
@mixin responsive-s {
flex-wrap: wrap;
}
}
}
&__description {
max-width: 820px;
}
&__title {
margin-bottom: 0;
margin-right: auto;
text-wrap: nowrap;
}
&__slider {
&:hover {
cursor: url("./assets/img/icon/cursorDragAndDrop.svg"), auto;
}
.swiper-wrapper {
}
}
&__item {
}
&__tools {
margin-top: 65px;
display: flex;
align-items: center;
flex-wrap: wrap;
justify-content: space-between;
max-width: var(--container);
margin-right: auto;
row-gap: 1.5rem;
padding: 0 50px 0;
@mixin responsive-xl {
padding: 0 80px 0 35px;
}
@mixin responsive-l {
padding: 0 40px 0 0;
}
.m--link{
@mixin responsive-s {
width: 100%;
display: flex;
justify-content: flex-end;
}
}
}
&__progress {
width: 100%;
height: 4px;
position: relative;
max-width: 460px;
background: var(--color-white-opacity);
border-radius: 20px;
overflow: hidden;
@mixin responsive-s {
max-width: none;
}
span {
background: var(--color-white);
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
transform-origin: left top;
}
}
&__cover {
margin-bottom: 0.5rem;
height: 450px;
overflow: hidden;
}
&__name {
font-weight: 500;
font-size: 1.5rem;
display: flex;
flex-direction: column;
gap: 0.5rem;
span {
font-weight: 400;
font-size: 1.125rem;
}
}
&__description {
max-width: 820px;
@mixin responsive-xl {
max-width: calc(100% - var(--space-between-block));
}
@mixin responsive-s {
max-width: none;
width: 100%;
}
}
}

View File

@ -9,7 +9,7 @@ $color-primary: #5E5BFC;
--color-black: #232323;
--color-white: #FFFFFF;
--color-white-darker: #E7E7E7;
--color-white-opacity: #404145;
--color-white-opacity: #BBB9CA;
--bg-wrapper-modal: #000000E5;
--color-primary: $color-primary;
--color-black-cc: #000000CC;
@ -34,6 +34,7 @@ $color-primary: #5E5BFC;
}
@media (max-width: $document-width-m) {
--container: $document-width-s;
--space-between-sections: calc(var(--base-fz) * 2.7);
}
@media (max-width: $document-width-s) {
--container: $document-width-xs;

View File

@ -4,7 +4,7 @@
<h2 class="h2 m--white">Напишите нам</h2>
<div class="footer__question">
<div class="text">Остались вопросы? Мы с радостью вам ответим!</div>
<button class="button m--white m--arrow">Написать</button>
<button class="button m--white m--arrow footer__question-button">Написать</button>
</div>
</div>
<div class="footer__bottom">

View File

@ -100,8 +100,8 @@ export default {
{
$formkit: 'text',
name: 'email',
label: 'придумайте логин',
placeholder: 'Придумайте логин',
label: 'Ваша почта',
placeholder: 'Ваша почта',
validation: 'required',
outerClass: 'field--required'
},
@ -118,8 +118,8 @@ export default {
{
$formkit: 'text',
name: 'email',
label: 'логин',
placeholder: 'Логин',
label: 'Почта',
placeholder: 'Почта',
validation: 'required',
outerClass: 'field--required'
},

View File

@ -1,8 +1,8 @@
<template>
<div class="blog">
<div class="blog__header">
<h2 class="h2">Наш блог</h2>
<button class="button m--arrow">Смотреть все</button>
<h2 class="h2 blog__header-title">Наш блог</h2>
<button class="button m--arrow blog__header-btn">Смотреть все</button>
</div>
<div class="blog__content">
<div class="blog__cover"></div>

View File

@ -0,0 +1,26 @@
<template>
<ContentLoader
viewBox="0 0 1920 40"
:speed="2"
primaryColor="#bbb9ca"
secondaryColor="#ecebeb"
>
<rect x="68" y="18" rx="2" ry="2" width="25" height="5" />
<rect x="444" y="11" rx="2" ry="2" width="75" height="10" />
<rect x="68" y="11" rx="2" ry="2" width="25" height="5" />
<rect x="45" y="29" rx="2" ry="2" width="538" height="6" />
<rect x="9" y="10" rx="2" ry="2" width="25" height="25" />
<rect x="22" y="21" rx="0" ry="0" width="6" height="2" />
<circle cx="54" cy="17" r="8" />
<rect x="523" y="11" rx="2" ry="2" width="58" height="10" />
<rect x="101" y="11" rx="2" ry="2" width="13" height="13" />
</ContentLoader>
</template>
<script>
import { ContentLoader } from "vue-content-loader"
export default {
name: 'playLoader',
components: [ContentLoader]
}
</script>

View File

@ -1,147 +1,247 @@
<template>
<div class="player">
<div class="player__cover">
<img :src="currentPlay.art" alt="player"/>
</div>
<div class="player__content">
<div class="player__top">
<button v-if="currentPlay.isPlay" @click="handlerPause" class="button player__btn m--pause">
</button>
<button v-else @click="handlerPlay" class="button player__btn m--play">
</button>
<div class="player__executor">
{{ currentPlay.title || '—' }}
<span>{{ currentPlay.artist || '—' }}</span>
</div>
<div class="player__favorites" :class="[isFavorites&&'m--active']" @click="handlerFavorites">
</div>
<div class="player__tools">
<FormKit
v-model="isLive"
type="toggle"
label="Включить мою музыку"
@change="changeLive"
/>
<div class="player__volume">
<span @click="setVolume"/>
<input type="range" v-model="player.volume" @change="changeVolume">
</div>
</div>
</div>
<div class="player__bottom">
<div class="player__time m--ether">
Эфир
</div>
<div class="player__progress">
<input :disabled="player.live" type="range" v-model="player.progress">
</div>
</div>
</div>
</div>
<div class="player">
<playLoader v-if="false"/>
<template v-else>
<div class="player__cover">
<img :src="currentPlay.art" alt="player"/>
</div>
<div class="player__content">
<div class="player__top">
<button v-if="currentPlay.isPlay" @click="handlerPause" class="button player__btn m--pause">
</button>
<button v-else @click="handlerPlay" class="button player__btn m--play">
</button>
<div class="player__executor">
{{ currentPlay.title || '—' }}
<span>{{ currentPlay.artist || '—' }}</span>
</div>
<div class="player__favorites" :class="[isFavorites&&'m--active']" @click="handlerFavorites">
</div>
<div class="player__tools">
<FormKit
v-model="isUserMusic"
type="toggle"
label="Включить мою музыку"
:disabled="!user"
/>
<div class="player__volume">
<span @click="setVolume"/>
<input type="range" v-model="player.volume" @change="changeVolume">
</div>
</div>
</div>
<div class="player__bottom">
<div class="player__time" :class="!isUserMusic&&'m--ether'">
<template v-if="isUserMusic">
{{ getTime(playerInfo.currentTime) }}/{{ getTime(playerInfo.duration) }}
</template>
<template v-else>
Эфир
</template>
</div>
<div class="player__progress">
<input :disabled="!isUserMusic" type="range" v-model="playerInfo.progress">
</div>
</div>
</div>
</template>
</div>
</template>
<script>
import {audio as Player, app} from "@/services";
import PlayLoader from "components/player-loader.vue";
export default {
name: 'player',
data() {
return {
audioUrl: 'http://82.97.242.49:18000/radio.mp3',
isFavorites: false,
isPlayRadio: true,
connection: null,
isLive: !this.$store.state.currentPlay.live,
}
},
computed: {
user() {
return this.$store.state.user;
},
currentPlay() {
return this.$store.state.currentPlay
},
player() {
return this.$store.state.player
}
},
watch: {
'currentPlay': {
immediate: false,
handler() {
if (this.isLive!==!this.currentPlay.live){
this.isLive = !this.currentPlay.live;
}
},
},
},
created() {
this.connectionPlayer();
},
mounted() {
if (!this.player.target) {
this.$store.dispatch('initPlayer');
}
},
methods: {
connectionPlayer() {
if (this.connection) {
this.connection.removePlay();
}
this.connection = new Player();
this.connection.init();
this.connection.onHandler(this.getPlaying);
},
getPlaying(e) {
const jsonData = JSON.parse(e.data)
if (jsonData?.pub?.data) {
const data = jsonData?.pub?.data;
if (this.currentPlay.live) {
if (data.np.station.listen_url !== this.player.target.src) {
this.$store.dispatch('changePlayer', data.np.station.listen_url);
}
this.$store.dispatch('setCurrentPlay', {...data.np.now_playing.song, live: true});
}
}
},
updateProgress() {
console.log(this.$refs.player.currentTime)
console.log(this.$refs.player.duration)
},
handlerPlay() {
this.$store.dispatch('handlerPlayer', {play: true});
},
handlerPause() {
this.$store.dispatch('handlerPlayer', {pause: true});
},
handlerFavorites() {
if (this.user?.id) {
this.isFavorites = !this.isFavorites;
const params = {...this.currentPlay, azura_id: this.currentPlay.id};
app.createFavoriteForUser(params).then(() => {
})
} else {
this.$emit('shopAuthentication', true)
}
},
setVolume() {
this.$store.dispatch('handlerPlayer', {volume: 100});
},
changeVolume() {
this.$store.dispatch('handlerPlayer', {volume: 100});
},
changeLive(e) {
console.log(e)
if (this.currentPlay.live) {
this.$store.dispatch('setCurrentPlay', {live: false});
console.log('избранное')
} else {
this.$store.dispatch('setCurrentPlay', {live: true});
console.log('поток')
}
}
}
name: 'player',
components: {PlayLoader},
data() {
return {
audioUrl: 'http://82.97.242.49:18000/radio.mp3',
isFavorites: false,
isPlayRadio: true,
connection: null,
isUserMusic: !this.$store.state.currentPlay.live,
playerInfo: {
progress: 0,
currentTime: 0,
duration: 0
}
}
},
computed: {
user() {
return this.$store.state.user;
},
currentPlay() {
return this.$store.state.currentPlay
},
player() {
return this.$store.state.player
},
userSongList() {
return this.$store.state.userFavorite?.songs || []
}
},
watch: {
'currentPlay': {
immediate: false,
handler(to, from) {
if (this.isUserMusic !== !this.currentPlay.live) {
this.isUserMusic = !this.currentPlay.live;
}
if (to.id !== from.id && this.user.id) {
this.checkSongIsFavorite();
}
if (!this.currentPlay.live && to.id !== from.id) {
this.getAudio(this.userSongList[0].azura_id);
}
},
},
'isUserMusic': {
immediate: false,
handler() {
if (this.isUserMusic === this.currentPlay.live) {
this.changeLive();
}
},
},
},
created() {
this.connectionPlayer();
if (this.user.id) {
this.checkSongIsFavorite();
}
},
mounted() {
this.$store.dispatch('initPlayer');
this.playerInfo.progress = this.currentPlay.live ? 100 : 0;
if (!this.currentPlay.live) {
this.$store.dispatch('setCurrentPlay', {...this.userSongList[0], live: false});
this.getAudio(this.userSongList[0].azura_id);
if (this.player.target){
this.player.target.addEventListener('timeupdate',this.updateProgress)
}
}
},
methods: {
connectionPlayer() {
if (this.connection) {
this.connection.removePlay();
}
this.connection = new Player();
this.connection.init();
this.connection.onHandler(this.getPlaying);
},
checkSongIsFavorite() {
app.getCheckFavoriteSong(this.currentPlay.azura_id || this.currentPlay.id).then(res => {
this.isFavorites = true;
}).catch(err => {
this.isFavorites = false;
console.error(err)
})
},
getPlaying(e) {
const jsonData = JSON.parse(e.data)
if (jsonData?.pub?.data) {
const data = jsonData?.pub?.data;
if (this.currentPlay.live) {
if (data.np.station.listen_url !== this.player.target.src) {
console.log('data.np.station.listen_url', data.np.station.listen_url)
this.$store.dispatch('changePlayer', data.np.station.listen_url);
}
this.$store.dispatch('setCurrentPlay', {
...this.currentPlay,
...data.np.now_playing.song,
live: true
});
}
}
},
updateProgress(e) {
this.playerInfo = {
progress: this.player.target.currentTime / this.player.target.duration * 100,
currentTime: this.player.target.currentTime,
duration: this.player.target.duration
}
},
handlerPlay() {
console.log(this.player.target.src)
this.$store.dispatch('handlerPlayer', {play: true});
},
handlerPause() {
this.$store.dispatch('handlerPlayer', {pause: true});
},
getSongList() {
app.getFavoriteList().then(res => {
this.$store.dispatch('setUserFavorite', {songs: res})
}).catch(err => {
console.error(err)
})
},
handlerFavorites() {
if (this.user?.id) {
const params = {...this.currentPlay, azura_id: this.currentPlay.id};
if (!this.isFavorites) {
app.createFavoriteForUser(params).then(() => {
this.isFavorites = !this.isFavorites;
this.getSongList();
}).catch(err => {
console.error(err)
})
} else {
console.log(params)
// app.removeFavoriteForUser(params).then(()=>{
// this.isFavorites = !this.isFavorites;
// this.getSongList();
// }).catch(err=>{
// console.error(err)
// })
}
} else {
this.$emit('shopAuthentication', true)
}
},
setVolume() {
this.$store.dispatch('handlerPlayer', {volume: 100});
},
changeVolume() {
this.$store.dispatch('handlerPlayer', {volume: 100});
},
getAudio(id) {
app.getAudio(id).then(res => {
const blob = new Blob([res], {type: 'application/audio'});
const audioUrl = URL.createObjectURL(blob);
this.$store.dispatch('changePlayer', audioUrl);
}).catch(err => {
console.debug(err)
})
},
changeLive() {
if (this.currentPlay.live) {
console.log('избранное')
this.getAudio(this.userSongList[0].azura_id);
this.playerInfo.progress = 0;
this.player.target.addEventListener('timeupdate', this.updateProgress)
this.$store.dispatch('setCurrentPlay', {...this.userSongList[0], live: false});
} else {
this.playerInfo.progress = 100;
this.$store.dispatch('setCurrentPlay', {...this.currentPlay, live: true});
console.log('поток')
}
},
getTime(value) {
let minutes = Math.floor(value / 60);
let seconds = Math.round(value % 60);
let paddedMinutes = minutes < 10 ? "0" + minutes : minutes;
let paddedSeconds = seconds < 10 ? "0" + seconds : seconds;
return `${paddedMinutes}:${paddedSeconds}`;
}
}
}
</script>

View File

@ -1,42 +1,34 @@
<template>
<div class="rubric-block">
<div class="rubric-block__header">
<h2 class="h2">
<h2 class="h2 rubric-block__header-title">
Рубрики
</h2>
<button class="button m--arrow" v-if="$route.name !== 'rubric'" @click="next('rubric')">Смотреть все</button>
<button class="button m--arrow rubric-block__link" v-if="$route.name !== 'rubric'" @click="next('rubric')">Смотреть все</button>
<div class="p rubric-block__description">
IT-радио - это уникальная платформа для обмена опытом, знаниями и обсуждения актуальных вопросов, связанных с разработкой программного обеспечения, искусственного интеллекта, интернета вещей и других направлений IT-индустрии
</div>
</div>
<div class="p rubric-block__description">
IT-радио - это уникальная платформа для обмена опытом, знаниями и обсуждения актуальных вопросов, связанных с разработкой программного обеспечения, искусственного интеллекта, интернета вещей и других направлений IT-индустрии
</div>
<div class="rubric-block__list">
<div class="rubric-block__item">
<div class="title rubric-block__title">Новости</div>
<div class="p rubric-block__description m--50">Новости из мира цифровых и около цифровых технологий, рассказанные понятным языком.</div>
<button class="button m--arrow rubric-block__btn" @click="showModalRubric('news')">Узнать больше</button>
</div>
<div class="rubric-block__item">
<div class="title rubric-block__title">Подкасты</div>
<div class="p rubric-block__description m--50">Диалоги, записанные нашей командой с интересными гостями из мира IT.</div>
<button class="button m--arrow rubric-block__btn" @click="showModalRubric('podcast')">Узнать больше</button>
</div>
<div class="rubric-block__item">
<div class="title rubric-block__title">Поговори с ИИ</div>
<div class="p rubric-block__description m--50">Общаемся с искусственным интеллектом и нейросетями. Пытаемся понять на что он способен и что может.</div>
<button class="button m--arrow rubric-block__btn" @click="showModalRubric('AI')">Узнать больше</button>
</div>
<div class="rubric-block__item">
<div class="title rubric-block__title">Ликбез</div>
<div class="p rubric-block__description m--50">Ликвидация безграмотности в сфере IT. Объяснить понятия так, чтобы понял и ребёнок и подросток и старшее поколение. Рубрика, направленная на повышение уровня осведомленности в IT.</div>
<button class="button m--arrow rubric-block__btn" @click="showModalRubric('educational')">Узнать больше</button>
<template v-if="showLoader">
<div class="loader">
<div class="spinner"/>
Загрузка данных
</div>
</template>
<div v-else class="rubric-block__list">
<div class="rubric-block__item" v-for="rubrik in rubriks" :key="`rubrik_${rubrik.id}`">
<div class="title rubric-block__title">{{rubrik.name}}</div>
<div class="p rubric-block__description m--50">{{rubrik.title}}</div>
<button class="button m--arrow rubric-block__btn" @click="showModalRubric(rubrik.id)">Узнать больше</button>
</div>
</div>
</div>
<RubricModal :showModal="isShowModalRubric" @hideModal="hiddenModalRubric" :ModalTemplate="ModalTemplate"/>
<RubricModal :showModal="isShowModalRubric" @hideModal="hiddenModalRubric" :selectRubric="ModalTemplate"/>
</template>
<script>
import RubricModal from "@/components/rubric-modal.vue";
import {app} from "@/services";
export default {
name: 'rubric-block',
@ -44,10 +36,25 @@ export default {
data(){
return{
isShowModalRubric: false,
ModalTemplate:null
}
ModalTemplate:null,
rubriks: [],
showLoader: false,
}
},
methods:{
created() {
this.getRubriks();
},
methods:{
getRubriks() {
this.showLoader = true;
app.getRubriks().then((data) => {
this.showLoader = false;
this.rubriks = data;
}).catch(err => {
this.showLoader = false;
console.log(err)
})
},
showModalRubric(key){
this.ModalTemplate = key
this.isShowModalRubric = true;

View File

@ -1,69 +1,97 @@
<template>
<vue-final-modal
v-model="show"
class="modal__container m--right"
content-class="modal__block m--half rubric-modal"
content-transition="vfm-fade"
overlay-transition="vfm-fade"
:clickToClose="false"
@click-outside="$emit('hideModal')"
>
<div class="rubric-modal__header">
<div class="title m--white rubric-modal__title">
<template v-if="ModalTemplate==='news'">Новости</template>
<template v-else-if="ModalTemplate==='podcast'">Подкасты</template>
<template v-else-if="ModalTemplate==='AI'">Поговори с ИИ</template>
<template v-else-if="ModalTemplate==='educational'">Ликбез</template>
</div>
<button
class="button modal__close rubric-modal__close"
@click="$emit('hideModal')"
>
</button>
</div>
<div class="rubric-modal__cover">
<img src="@/assets/img/icon/remove-rubrics.png" alt=""/>
</div>
<div class="rubric-modal__description">
<template v-if="ModalTemplate==='news'">
В этой рубрике будут выходить ежедневные и еженедельные новостные выпуски, освещающие ключевые события в мире IT, включая новые разработки, релизы продуктов, изменения в законодательстве, касающиеся технологий, и другие важные события.<br/><br/> Так же будем выдавать аналитические материалы - глубокие и не очень глубокие обзоры, в которых рассматриваются тенденции развития IT-индустрии, анализируются стратегические направления развития крупных компаний и перспективы новых технологий
</template>
<template v-else-if="ModalTemplate==='podcast'">
В подкастах мы будем задавать вопросы айтишникам, специалистам из около айтишной среды, цифровым музыкантам, чиновникам от IT, бизнесменам, геймерам и киберспортсменам, криптовалютчикам и сами будем отвечать на вопросы простых людей, которые хотят разобраться в нашей сфере, которая пронизывает все сферы современной жизни. А если сами не сможем ответить, то будем искать тех, кто сможет ответить.
</template>
<template v-else-if="ModalTemplate==='AI'">
Редакция IT-Радио собирается за круглым столом и задаёт ИИ вопросы, которые приходят к нам на почту на разные темы.<br/><br/> Цель - понять насколько далеко ИИ уходит в своём развитии от человека, дать ответы на вопросы. Попытаемся разобрать ответы искусственного интеллекта, дать свои комментарии, дополнить. Попытаемся понять его логику и логику его разработчиков, которую закладывали в работу нейросети.
</template>
<template v-else-if="ModalTemplate==='educational'">
Будем рассказывать что такое IT-сфера. Какие у неё составляющие. Кого можно назвать айтишником, а кого нет и почему. Почему кому-то сложно понять IT и при чём тут точные науки.
</template>
</div>
<button class="button m--fit-content m--white m--arrow">Написать</button>
</vue-final-modal>
<vue-final-modal
v-model="show"
class="modal__container m--right"
content-class="modal__block m--half rubric-modal"
content-transition="vfm-fade"
overlay-transition="vfm-fade"
:clickToClose="false"
@click-outside="$emit('hideModal')"
>
<template v-if="showLoader">
<div class="loader">
<div class="spinner"/>
Загрузка данных
</div>
</template>
<template v-else>
<div class="rubric-modal__header">
<div class="title m--white rubric-modal__title">
{{rubrik.name}}
</div>
<button
class="button modal__close rubric-modal__close"
@click="$emit('hideModal')"
>
</button>
</div>
<div class="rubric-modal__cover">
<img :src="`${selfUrl}${rubrik.img}`" alt=""/>
</div>
<div class="rubric-modal__description">
{{rubrik.description}}
</div>
<button class="button m--fit-content m--white m--arrow" v-if="user.id">Написать</button>
</template>
</vue-final-modal>
</template>
<script>
import {app} from "@/services";
import {selfUrl} from '@/settings';
export default {
name: 'RubricModal',
props: {
showModal: {
type: Boolean,
default() {
return false;
}
},
ModalTemplate:{
type: String,
default() {
return '';
}
},
},
computed: {
show() {
return this.showModal;
},
},
name: 'RubricModal',
props: {
showModal: {
type: Boolean,
default() {
return false;
}
},
selectRubric: {
type: String,
default() {
return '';
}
},
},
data() {
return {
selfUrl,
rubrik: {},
showLoader: false
}
},
computed: {
show() {
return this.showModal;
},
user() {
return this.$store.state.user;
},
},
watch: {
'selectRubric': {
handler() {
if (this.selectRubric){
this.getRubrik();
}
}
}
},
methods: {
getRubrik() {
this.showLoader = true;
app.getRubrik(this.selectRubric).then((data) => {
this.showLoader = false;
this.rubrik = data[0];
}).catch(err => {
this.showLoader = false;
console.log(err)
})
},
}
}
</script>

View File

@ -1,5 +1,5 @@
<template>
<div class="song-item" :class="[currentPlay.id===song.id&&'m--select']" @click="handlerSelectSong">
<div class="song-item" :class="[selectSong&&'m--select']" @click="handlerSelectSong">
<div class="song-item__selected" :class="[!isPlay&&'m--stop']">
<svg width="33" height="28" viewBox="0 0 33 28" fill="none" xmlns="http://www.w3.org/2000/svg">
<path class="" d="M0 15C0 13.6193 1.11929 12.5 2.5 12.5C3.88071 12.5 5 13.6193 5 15V27.5H0V15Z" fill="#7138F4"/>
@ -10,9 +10,9 @@
</svg>
</div>
<div>
<button v-if="isPlay" @click="handlerPause" class="button song-item__btn m--pause">
<button v-if="isPlay" @click.stop="handlerPause" class="button song-item__btn m--pause">
</button>
<button v-else @click="handlerPlay" class="button song-item__btn m--play">
<button v-else @click.stop="handlerPlay" class="button song-item__btn m--play">
</button>
</div>
<div class="song-item__info">
@ -31,9 +31,9 @@ export default {
name: 'song-item',
props:{
selectSong:{
type: Number,
type: Boolean,
default(){
return null
return false
}
},
song:{
@ -47,7 +47,7 @@ export default {
default(){
return false
}
}
},
},
watch: {
'playSong': {
@ -62,20 +62,15 @@ export default {
isPlay: this.playSong
}
},
computed:{
currentPlay(){
return this.$store.state.currentPlay
}
},
methods:{
handlerPlay() {
this.isPlay = true;
this.$emit('playSong', {...this.song, live: false, isPlay: true,})
},
handlerPause() {
this.isPlay = false;
this.$emit('pauseSong', {...this.song, live: false, isPlay: false,})
},
handlerSelectSong(){
this.$store.dispatch('setCurrentPlay', {...this.song, live: false});
this.$emit('selectSong', {...this.song, live: false})
}
}
}

View File

@ -4,6 +4,11 @@
v-for="song in songList"
:key="song"
:song="song"
:playSong="!currentPlay.live && song.id === currentPlay.id? currentPlay.isPlay: false"
:selectSong="!currentPlay.live && song.id === currentPlay.id"
@selectSong="handlerSelectSong"
@playSong="handlerPlaySong"
@pauseSong="handlerPauseSong"
/>
</div>
</template>
@ -23,6 +28,22 @@ export default {
data() {
return {}
},
methods: {}
computed: {
currentPlay() {
return this.$store.state.currentPlay
}
},
methods: {
handlerSelectSong(params) {
this.$store.dispatch('setCurrentPlay', {...this.currentPlay, ...params});
},
handlerPlaySong(params) {
this.$store.dispatch('setCurrentPlay', params);
},
handlerPauseSong(params) {
this.$store.dispatch('setCurrentPlay', params);
},
}
}
</script>

View File

@ -1,80 +1,98 @@
<template>
<div class="team">
<div class="team__header">
<h2 class="h2 team__title">
Наша команда
</h2>
<div class="team__description">
На IT волне команда профессионалов неутомимо трудится, чтобы дарить слушателям самые свежие и актуальные
новости из мира технологий, отвечать на их вопросы и обсуждать горячие темы на IT радио.
</div>
</div>
<div class="team">
<div class="team__header">
<h2 class="h2 team__title">
Наша команда
</h2>
<div class="team__description">
На IT волне команда профессионалов неутомимо трудится, чтобы дарить слушателям самые свежие и актуальные
новости из мира технологий, отвечать на их вопросы и обсуждать горячие темы на IT радио.
</div>
</div>
<template v-if="showLoader">
<div class="loader">
<div class="spinner" /> Загрузка данных
<div class="spinner"/>
Загрузка данных
</div>
</template>
<Swiper
<Swiper
v-else
:slides-per-view="4"
:space-between="20"
:modules="modules"
:pagination="{
:slides-per-view="4"
:space-between="20"
:modules="modules"
:pagination="{
el: '.team__progress',
clickable: true,
type: 'progressbar',
}"
class="team__slider"
>
<SwiperSlide class="team__item" v-for="employee in team" :key="employee.id">
<div class="team__cover">
<img :src="`${selfUrl + employee.img_person}`" alt="user"/>
</div>
<div class="team__name">
{{employee.name}} {{employee.last_name}}
<span>{{employee.position}}</span>
</div>
</SwiperSlide>
</Swiper>
<div class="team__tools">
<div class="team__progress" ref="progressBar">
<span></span>
</div>
<router-link :to="{name: 'about'}" class="m--link">
Больше о нас
</router-link>
</div>
</div>
:breakpoints="{
0: {
slidesPerView: 1,
},
450: {
slidesPerView: 1.5,
},
768: {
slidesPerView: 2.5,
},
1020:{
slidesPerView: 3,
},
1280: {
slidesPerView: 4,
},
}"
class="team__slider"
>
<SwiperSlide class="team__item" v-for="employee in team" :key="employee.id">
<div class="team__cover">
<img :src="`${selfUrl + employee.img_person}`" alt="user"/>
</div>
<div class="team__name">
{{ employee.name }} {{ employee.last_name }}
<span>{{ employee.position }}</span>
</div>
</SwiperSlide>
</Swiper>
<div class="team__tools">
<div class="team__progress" ref="progressBar">
<span></span>
</div>
<router-link :to="{name: 'about'}" class="m--link">
Больше о нас
</router-link>
</div>
</div>
</template>
<script>
import 'swiper/css';
import {Swiper, SwiperSlide} from "swiper/vue";
import {Pagination, Scrollbar} from "swiper/modules";
import { app } from "@/services";
import {app} from "@/services";
import {selfUrl} from '@/settings';
export default {
name: 'team',
components: {Swiper, SwiperSlide},
data() {
return {
name: 'team',
components: {Swiper, SwiperSlide},
data() {
return {
selfUrl,
team: [],
showLoader: false,
modules: [Scrollbar, Pagination],
}
},
modules: [Scrollbar, Pagination],
}
},
created() {
this.getTeams();
},
methods:{
getTeams(){
methods: {
getTeams() {
this.showLoader = true;
app.getTeams().then((data)=>{
app.getTeams().then((data) => {
this.showLoader = false;
this.team = data;
}).catch(err=>{
}).catch(err => {
this.showLoader = false;
console.log(err)
})

View File

@ -44,7 +44,8 @@ export default route(function (/* { store, ssrContext } */) {
if (store.state.user && store.state.user.id) {
next();
} else {
next({ name: 'auth' });
next({ name: 'home' });
this.$store.dispatch('setShowAuthModal', true);
}
} else {
next();

View File

@ -34,6 +34,22 @@ export default class extends REST {
throw new RESTError(error, 'Ошибка при получении команды');
});
}
static getCheckFavoriteSong(id){
return this._get(`radio/song/check_is_favorite/${id}`, {}, {}).then((data) => {
return data;
}).catch((error) => {
throw new RESTError(error, 'Ошибка при проверке песни');
});
}
static getAudio(id){
return this._get(`radio/song/get_audio/${id}`, {}, {}, false, true).then((data) => {
return data;
}).catch((error) => {
throw new RESTError(error, 'Ошибка при получениии песни');
});
}
static createFavoriteForUser(params){
return this._post(`radio/song/add_favorite`, {}, params).then((data) => {
return data;
@ -42,6 +58,14 @@ export default class extends REST {
});
}
static removeFavoriteForUser(params){
return this._post(`radio/song/delete_song`, {}, params).then((data) => {
return data;
}).catch((error) => {
throw new RESTError(error, 'Ошибка при получении плейлистов');
});
}
static getFavoriteList(params){
return this._get(`radio/song`, {}, params).then((data) => {
return data;
@ -49,7 +73,20 @@ export default class extends REST {
throw new RESTError(error, 'Ошибка при получении плейлистов');
});
}
static getRubriks() {
return this._get(`radio/rubriks`, {}, {}).then((data) => {
return data;
}).catch((error) => {
throw new RESTError(error, 'Ошибка при получении рубрик');
});
}
static getRubrik(id) {
return this._get(`radio/rubriks/${id}`, {}, {}).then((data) => {
return data;
}).catch((error) => {
throw new RESTError(error, 'Ошибка при получении рубрик');
});
}
@ -69,12 +106,4 @@ export default class extends REST {
throw new RESTError(error, 'Ошибка при получении плейлистов');
});
}
static getRubriks(station, params) {
return this._get(`radio.mp3`, params, {}).then((data) => {
return data;
}).catch((error) => {
throw new RESTError(error, 'Ошибка при получении плейлистов');
});
}
}

View File

@ -35,8 +35,8 @@ class REST {
static get settings() {
throw new Error('settings must be overridden');
}
static _get(url, params={}, extraParams, use_cache=false) {
return this._request('get', url, params, {}, {}, extraParams, use_cache);
static _get(url, params={}, extraParams, use_cache=false, isBlob=false) {
return this._request('get', url, params, {}, {}, extraParams, use_cache, isBlob);
}
static _post(url, params, data) {
return this._request('post', url, params, data);
@ -50,7 +50,7 @@ class REST {
static _delete(url, params, data) {
return this._request('delete', url, params, data);
}
static _request(method, url, params={}, data={}, extraData={}, extraParams={}, use_cache=false) {
static _request(method, url, params={}, data={}, extraData={}, extraParams={}, use_cache=false, isBlob=false) {
let cache_key = null;
return ajax.request({
method,
@ -59,7 +59,8 @@ class REST {
data,
extraData,
extraParams,
headers: this._getAuthHeaders()
headers: this._getAuthHeaders(),
responseType: this._getResponseType(isBlob),
}).then((response) => {
if (cache_key) {
cache.set(cache_key, response.data);
@ -67,6 +68,11 @@ class REST {
return response.data;
});
}
static _getResponseType(value){
if (value) {
return 'arraybuffer'
}
}
static _getAuthHeaders() {
if (store.state.token) {
return { 'Authorization': `Bearer ${store.state.token}` };

View File

@ -1,4 +1,4 @@
import { createStore } from 'vuex'
import {createStore} from 'vuex'
import VuexPersist from 'vuex-persist';
const vuexPersist = new VuexPersist({
@ -9,7 +9,7 @@ const vuexPersist = new VuexPersist({
// Vue.use(Vuex);
export default createStore({
state () {
state() {
return {
token: null,
refreshToken: null,
@ -18,11 +18,18 @@ export default createStore({
station: {
id: 1
},
currentPlay: {},
player:{
currentPlay: {
isPlay: false
},
player: {
target: null,
volume: 50,
live: true,
},
userFavorite: {
podcast: [],
playlist: [],
songs: []
}
}
},
@ -39,38 +46,55 @@ export default createStore({
state.token = null;
state.refreshToken = null;
},
setCurrentPlay(state, song){
setCurrentPlay(state, song) {
state.currentPlay = song;
},
setShowAuthModal(state, show){
setShowAuthModal(state, show) {
state.showAuthModal = show
},
setPlayer(state, params){
state.player = {...state.player,...params}
setPlayer(state, params) {
state.player = {...state.player, ...params}
},
initPlayer(state){
initPlayer(state) {
state.player.target = document.createElement('audio')
state.player.target.src = '';
state.player.target.preload = 'auto';
state.player.target.controls = true;
console.log('initPlayer',state.player.target)
console.log('initPlayer', state.player.target)
},
changePlayer(state, params){
changePlayer(state, params) {
const awaitPlay = () => {
if (state.player.target.readyState >= 4) {
state.player.target.play();
state.player.target.removeEventListener('canplaythrough', awaitPlay);
} else {
awaitPlay();
}
}
state.player.target.src = params;
state.player.src = params;
if (state.currentPlay.isPlay){
state.player.target.addEventListener('canplaythrough', awaitPlay)
}
},
handlerPlayer(state, params){
if (params.pause){
handlerPlayer(state, params) {
if (params.pause) {
state.currentPlay.isPlay = false;
state.player.target.pause();
}
if (params.play){
if (params.play) {
state.currentPlay.isPlay = true;
console.log(state.player.target.readyState)
state.player.target.play();
}
if (params.volume){
if (params.volume) {
state.player.target.volume = params.volume;
}
},
setUserFavorite(state, params) {
state.userFavorite = {...state.userFavorite, ...params}
}
},
actions: {
setToken(context, tokens) {
@ -83,24 +107,26 @@ export default createStore({
context.commit('user', {});
context.commit('removeToken');
},
setCurrentPlay(context, song){
setCurrentPlay(context, song) {
context.commit('setCurrentPlay', song);
},
setShowAuthModal(context, show){
setShowAuthModal(context, show) {
context.commit('setShowAuthModal', show);
},
setPlayer(context, params){
setPlayer(context, params) {
context.commit('setPlayer', params);
},
initPlayer(context){
initPlayer(context) {
context.commit('initPlayer');
},
handlerPlayer(context, params){
handlerPlayer(context, params) {
context.commit('handlerPlayer', params);
},
changePlayer(context, params){
changePlayer(context, params) {
context.commit('changePlayer', params);
}
},
setUserFavorite(context, params) {
context.commit('setUserFavorite', params);
},
}
});

View File

@ -1,24 +1,25 @@
<template>
<div class="home">
<div class="home__meaning" ref="targetWrapper">
<div class="app__content">
<p class="text home__subtitle">
Мы цифровое <span>онлайн радио.</span><br/>
Помогаем разобраться в том, что такое <span>IT.</span><br/>
Находимся в <span>Челябинске</span>, но вещаем на весь <span>Мир</span><br/>
</p>
<h1 class="home__title" ref="targetTitle">
IT-Радио <span id="targetTitleSpan">радиостанция про сферу</span> технологий <br/> и развитие <span id="targetTitleSpan">в IT</span>
<div class="home">
<div class="home__meaning" ref="targetWrapper">
<div class="app__content">
<p class="text home__subtitle">
Мы цифровое <span>онлайн радио.</span><br/>
Помогаем разобраться в том, что такое <span>IT.</span><br/>
Находимся в <span>Челябинске</span>, но вещаем на весь <span>Мир</span><br/>
</p>
<h1 class="home__title" ref="targetTitle">
IT-Радио <span id="targetTitleSpan">радиостанция про сферу</span> технологий <br/> и развитие
<span id="targetTitleSpan">в IT</span>
<!-- IT Радио -->
<!-- <span id="targetTitleSpan">радиостанция</span>-->
<!-- <br/>-->
<!-- <span id="targetTitleSpan">про сферу</span> технологий<br/>-->
<!-- и развитие <span id="targetTitleSpan">в IT</span><br/>-->
</h1>
</div>
<div class="home__banner" ref="target">
</div>
</div>
<!-- <span id="targetTitleSpan">радиостанция</span>-->
<!-- <br/>-->
<!-- <span id="targetTitleSpan">про сферу</span> технологий<br/>-->
<!-- и развитие <span id="targetTitleSpan">в IT</span><br/>-->
</h1>
</div>
<div class="home__banner" ref="target">
</div>
</div>
<template v-if="true">
<div class="app__content">
@ -27,21 +28,17 @@
<div class="text home__info--item">
IT-RADIO. 2023
<span>
Сегодня IT-сфера развивается настолько быстро, что следить за всеми новинками и изменениями в ней становится все сложнее.
<br/>
<br/> Но есть способ всегда быть в курсе последних новостей и событий это IT-радио.
</span>
Сегодня IT-сфера развивается настолько быстро, что следить за всеми новинками и изменениями в ней становится все сложнее.<br/><br/> Но есть способ всегда быть в курсе последних новостей и событий это IT-радио.
</span>
</div>
<h2 class="h2 m--border">
Открывая новые горизонты в мире технологий
</h2>
<div class="text home__info--item m--circle">
<span>
IT-радио это уникальный проект, который объединяет в себе самых ярких представителей IT-индустрии, а также экспертов из различных областей, чтобы поделиться своими знаниями и опытом с широкой аудиторией.
<br/>
<br/>
Каждое шоу на IT-радио включает в себя актуальные темы, новости, обзоры, интервью с экспертами и многое другое.
</span>
<span>
IT-радио это уникальный проект, который объединяет в себе самых ярких представителей IT-индустрии, а также экспертов из различных областей, чтобы поделиться своими знаниями и опытом с широкой аудиторией.<br/><br/>
Каждое шоу на IT-радио включает в себя актуальные темы, новости, обзоры, интервью с экспертами и многое другое.
</span>
</div>
</div>
<div class="home__content">
@ -70,7 +67,8 @@
<div class="home__social--description">
<h2 class="h2 m--white">Соц сети</h2>
<div class="text">
Следите за обновлениями и новыми постами на IT Radio, чтобы быть в курсе последних новостей и
Следите за обновлениями и новыми постами на IT Radio, чтобы быть в курсе последних новостей
и
событий в мире IT, а также следить за анонсами и обновлениями!
</div>
</div>
@ -86,7 +84,7 @@
<blog/>
</div>
</template>
</div>
</div>
</template>
<script>
@ -97,39 +95,39 @@ import RubricBlock from "components/rubric-block.vue";
import Blog from "components/blog.vue";
export default {
name: 'home',
components: {Blog, RubricBlock, Team},
created() {
},
mounted() {
this.initScene();
},
methods: {
initScene() {
gsap.registerPlugin(ScrollTrigger);
gsap.to(this.$refs.target, {
scale: 1,
scrollTrigger: {
trigger: this.$refs.targetWrapper,
scrub: true,
start: 0,
end: 600,
pin: true,
},
},
);
gsap.to(this.$refs.targetTitle, {
color: 'transparent',
backgroundImage: 'linear-gradient(91.17deg, #C6F1F7 -4.01%, #F983E9 36.14%, #B877FF 77.44%, #C2E9CD 106.11%)',
scrollTrigger: {
trigger: this.$refs.targetWrapper,
scrub: true,
start: 0,
end: 600,
},
},
);
}
}
name: 'home',
components: {Blog, RubricBlock, Team},
created() {
},
mounted() {
this.initScene();
},
methods: {
initScene() {
gsap.registerPlugin(ScrollTrigger);
gsap.to(this.$refs.target, {
scale: 1,
scrollTrigger: {
trigger: this.$refs.targetWrapper,
scrub: true,
start: 0,
end: 600,
pin: true,
},
},
);
gsap.to(this.$refs.targetTitle, {
color: 'transparent',
backgroundImage: 'linear-gradient(91.17deg, #C6F1F7 -4.01%, #F983E9 36.14%, #B877FF 77.44%, #C2E9CD 106.11%)',
scrollTrigger: {
trigger: this.$refs.targetWrapper,
scrub: true,
start: 0,
end: 600,
},
},
);
}
}
}
</script>

View File

@ -6,7 +6,7 @@
{ name: 'Личный кабинет', route: { name: 'profile' } },
]"
/>
<h1 class="h2 profile__title">Имя фамилия</h1>
<h1 class="h2 profile__title">{{ user.email }}</h1>
<button class="button m--text-link">Редактировать профиль</button>
<div class="profile__tabs tabs m--btns">
<button
@ -22,10 +22,11 @@
<template v-if="currentTabsItem==='music'">
<template v-if="showLoader">
<div class="loader">
<div class="spinner" /> Загрузка данных
<div class="spinner"/>
Загрузка данных
</div>
</template>
<SongList v-else :songList="songList"/>
<SongList v-else :songList="userFavorite.songs"/>
</template>
</div>
</template>
@ -55,8 +56,16 @@ export default {
name: 'playlists'
},
],
songList:[],
showLoader:true,
songList: [],
showLoader: true,
}
},
computed: {
user() {
return this.$store.state.user;
},
userFavorite(){
return this.$store.state.userFavorite
}
},
watch: {
@ -75,12 +84,12 @@ export default {
this.getSongList();
},
methods: {
getSongList(){
getSongList() {
this.showLoader = true;
app.getFavoriteList().then(res=>{
app.getFavoriteList().then(res => {
this.showLoader = false;
this.songList = res;
}).catch(err=>{
this.$store.dispatch('setUserFavorite', {songs: res})
}).catch(err => {
this.showLoader = false;
console.error(err)
})