Article.vue 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625
  1. <template>
  2. <div>
  3. <section class="detail section" :id="'news_' + news.id">
  4. <div :class="isNextNews ? 'detail__wrapper section-wrapper section-wrapper_extra inner-newspage detail__wrapper-next-news' : 'detail__wrapper section-wrapper section-wrapper_extra inner-newspage'">
  5. <main class="detail__main">
  6. <div class="detail-main">
  7. <div class="detail-main__wrapper">
  8. <div class="detail-main__topic">
  9. <div v-if="userPermissionGroup === 3 || userPermissionGroup === 5" class="admin-button-in-news">
  10. <a class="copy" :href="'/admin/publications/' + news.id" target="_blank">Редактирование новости</a>
  11. <a class="inject" @click="inject(news)">Инжект</a>
  12. </div>
  13. <ul class="detail-main__topic-list">
  14. <li class="detail-main__topic-item">
  15. <nuxt-link :to="{ name: 'news' }" class="detail-main__topic-link">
  16. Новости
  17. </nuxt-link>
  18. </li>
  19. <li v-for="category in breadcrumbs" :key="category.alias" class="detail-main__topic-item" >
  20. <nuxt-link :to="{name: 'news-category', params: { category: category.alias }}" class="detail-main__topic-link" href="javascript:void(0)">
  21. {{ category.name }}
  22. </nuxt-link>
  23. </li>
  24. </ul>
  25. </div>
  26. <div class="detail-main__top">
  27. <div class="detail-top">
  28. <div class="detail-top__header">
  29. <main class="detail-top__header-main">
  30. <h1 class="detail-top__title title_h1">
  31. {{ news.title }}
  32. </h1>
  33. <p class="article_preview">
  34. {{ news.description }}
  35. </p>
  36. <div class="detail-top__date">
  37. {{ convertDate(news.start_activity)
  38. }}{{
  39. news.show_author && news.author
  40. ? ", " + news.author.name
  41. : ""
  42. }}
  43. </div>
  44. </main>
  45. <aside class="detail-top__header-side">
  46. <div class="detail-top__actions">
  47. <div class="detail-top__share">
  48. <div class="socials-share">
  49. <ul class="socials-share__list">
  50. <li class="socials-share__item">
  51. <ShareNetwork
  52. network="Odnoklassniki"
  53. :url="'https://www.amic.ru' + $route.fullPath"
  54. :title="news.title"
  55. class="socials-share__link"
  56. :hashtags="
  57. news.hash_tags.length
  58. ? news.hash_tags.join(' ')
  59. : ''
  60. "
  61. >
  62. <BaseIcon :name="'ok'" clear/>
  63. </ShareNetwork>
  64. </li>
  65. <li class="socials-share__item">
  66. <ShareNetwork
  67. network="twitter"
  68. :url="'https://www.amic.ru' + $route.fullPath"
  69. :title="news.title"
  70. class="socials-share__link"
  71. :hashtags="
  72. news.hash_tags.length
  73. ? news.hash_tags.join(' ')
  74. : ''
  75. "
  76. >
  77. <BaseIcon :name="'tw'" clear/>
  78. </ShareNetwork>
  79. </li>
  80. <li class="socials-share__item">
  81. <ShareNetwork
  82. network="vk"
  83. :url="'https://www.amic.ru' + $route.fullPath"
  84. :title="news.title"
  85. :description="news.description"
  86. class="socials-share__link"
  87. :hashtags="
  88. news.hash_tags.length
  89. ? news.hash_tags.join(' ')
  90. : ''
  91. "
  92. >
  93. <BaseIcon :name="'vk'" clear/>
  94. </ShareNetwork>
  95. </li>
  96. </ul>
  97. </div>
  98. </div>
  99. </div>
  100. <a
  101. class="
  102. detail-top__action detail-top__comments-button
  103. button button_grey-dark
  104. "
  105. :href="hrefCommentsForm"
  106. >
  107. <span class="button__text">Комментировать</span>
  108. </a>
  109. </aside>
  110. </div>
  111. <div class="detail-top__footer">
  112. <main class="detail-top__footer-main">
  113. <div class="detail-top__gallery" v-if="news.gallery">
  114. <client-only>
  115. <div class="detail-top__images">
  116. <VueSlickCarousel
  117. ref="galleryImages"
  118. :asNavFor="$refs.galleryThumbs"
  119. :focusOnSelect="true"
  120. :draggable="false"
  121. :arrows="true"
  122. @beforeChange="syncSliders"
  123. >
  124. <div
  125. class="detail-top__image"
  126. v-for="(img, i) in news.gallery"
  127. :key="i"
  128. >
  129. <img
  130. @click="index = i"
  131. :src="img.url"
  132. :alt="img.title"
  133. />
  134. </div>
  135. <template #prevArrow="arrowOption">
  136. <button
  137. class="
  138. button
  139. button_news-slider
  140. button_news-slider-prev
  141. "
  142. >
  143. <BaseIcon :name="'h-slider-prev'" clear />
  144. </button>
  145. </template>
  146. <template #nextArrow="arrowOption">
  147. <button
  148. class="
  149. button
  150. button_news-slider
  151. button_news-slider-next
  152. "
  153. >
  154. <BaseIcon :name="'h-slider-next'" clear />
  155. </button>
  156. </template>
  157. </VueSlickCarousel>
  158. </div>
  159. <div class="detail-top__thumbs">
  160. <VueSlickCarousel
  161. ref="galleryThumbs"
  162. :asNavFor="$refs.galleryImages"
  163. :focusOnSelect="true"
  164. :draggable="false"
  165. :touchThreshold="4"
  166. :slidesToScroll="1"
  167. :slidesToShow="4"
  168. :arrows="false"
  169. @beforeChange="syncSliders"
  170. >
  171. <div
  172. class="detail-top__thumb"
  173. v-for="(img, i) in news.gallery"
  174. :key="i"
  175. >
  176. <img :src="img.url" :alt="img.title" />
  177. </div>
  178. </VueSlickCarousel>
  179. </div>
  180. <Tinybox v-model="index" :images="images" />
  181. </client-only>
  182. </div>
  183. <div class="detail-top__figure" v-else-if="news.image">
  184. <img
  185. v-show="imgloaded"
  186. class="detail-top__img"
  187. :src="news.image ? news.image.url : ''"
  188. alt=""
  189. @load="ImgLoaded"
  190. />
  191. <v-sheet style="sheet-custom">
  192. <v-progress-circular
  193. v-if="!imgloaded"
  194. indeterminate
  195. color="primary"
  196. ></v-progress-circular>
  197. </v-sheet>
  198. </div>
  199. </main>
  200. <aside class="detail-top__footer-side">
  201. <div class="detail-top__author">
  202. <div class="news-author news-author_article">
  203. <NewsAuthor :news="news" />
  204. </div>
  205. </div>
  206. <div class="detail-top__info">
  207. <div>{{ news.image && news.image.title && news.image.title !== 'example' ? news.image.title : news.source }}</div>
  208. </div>
  209. <div class="detail-top__tags">
  210. <ul class="detail-top__tags-list">
  211. <li class="detail-top__tags-item">
  212. <a
  213. class="detail-top__tags-link"
  214. href="javascript:void(0)"
  215. v-for="tag in news.hash_tags"
  216. :key="tag"
  217. >
  218. {{ tag }}</a
  219. >
  220. </li>
  221. </ul>
  222. </div>
  223. </aside>
  224. </div>
  225. </div>
  226. </div>
  227. <div v-if="news.video" class="detail__video">
  228. <iframe class="detail__iframe" :src="videoLink" frameborder="0"></iframe>
  229. </div>
  230. <NewsContent :rawHtml="news.content" :isAdvert="isAdvert()" ref="newsContent" />
  231. <!--<div class="detail-main__views">
  232. Просмотров: {{ news.views_counter }}
  233. </div>-->
  234. <div class="detail-main__files mt-5">
  235. <a
  236. v-for="file in news.files"
  237. :href="file.url"
  238. :key="file.url"
  239. target="_blank"
  240. >
  241. {{ file.original_name }}
  242. </a>
  243. </div>
  244. <div class="detail-main__properties mt-5">
  245. <span
  246. v-for="property in properties"
  247. :key="property.id"
  248. :class="property.code"
  249. :ref="property.code"
  250. >
  251. {{ property.title }}
  252. </span>
  253. </div>
  254. <BannersW960H90 :idMobil="news.id" />
  255. <div v-if="news.show_pull" :id="idForm">
  256. <ArticleCommentsList :pull_id="news.comment_pull_id" @commentResponse="commentResponseData" :newCommentsList="commentsList" />
  257. <ArticleCommentsForm :pull_id="news.comment_pull_id" :response_user_name="this.comment.response_user_name" :response_user_id="this.comment.response_user_id" @clearCommentResponse="clearCommentResponse" @commentsResponseData="commentsResponseData" />
  258. </div>
  259. </div>
  260. </div>
  261. </main>
  262. <aside class="detail__side lenta">
  263. <h2 class="news-side__title"><a href="/news">Лента новостей</a></h2>
  264. <ul class="news-side-list">
  265. <WidgetLent v-bind:params="{showad:true,showadMainPage:false}" />
  266. </ul>
  267. </aside>
  268. <div>
  269. <BannersSkyScrapperVideo v-if="!isNextNews" />
  270. </div>
  271. </div>
  272. </section>
  273. </div>
  274. </template>
  275. <script>
  276. import { mapGetters } from 'vuex'
  277. import NewsService from '../../services/NewsService'
  278. export default {
  279. components: {
  280. VueSlickCarousel: () => import("vue-slick-carousel").then((m) => m.default),
  281. Tinybox: () => import("vue-tinybox").then((m) => m.default),
  282. },
  283. props: {
  284. news: {
  285. type: Object,
  286. },
  287. isNextNews: {
  288. type: Boolean,
  289. default: false
  290. },
  291. },
  292. data() {
  293. return {
  294. userPermissionGroup: null,
  295. imgloaded: false,
  296. hrefCommentsForm: '#comment_form_' + this.news.comment_pull_id,
  297. comment: {
  298. text: "",
  299. response_user_id: "",
  300. response_user_name: "",
  301. pullId: this.news.comment_pull_id,
  302. },
  303. index: null,
  304. idForm: 'comment_form_' + this.news.comment_pull_id,
  305. commentsList: {},
  306. thisUrl: '/news/' + this.news.category.alias + '/' + this.news.alias + '/',
  307. };
  308. },
  309. computed: {
  310. videoLink() {
  311. if (this.news.video.match("(www.)?youtube|youtu.be")) {
  312. const youtubeId = this.news.video
  313. .split(/v\/|v=|youtu\.be\//)[1]
  314. .split(/[?&]/)[0];
  315. return `https://www.youtube.com/embed/${youtubeId}`;
  316. }
  317. return this.news.video;
  318. },
  319. properties() {
  320. return Object.values(this.news.properties).filter((property) =>
  321. Number(property.value)
  322. );
  323. },
  324. images() {
  325. return this.news.gallery.map((image) => image.url);
  326. },
  327. breadcrumbs() {
  328. let current = this.news.category;
  329. const categories = [current];
  330. while (current.parent) {
  331. current = current.parent;
  332. categories.push(current);
  333. }
  334. return categories.reverse();
  335. },
  336. },
  337. mounted () {
  338. if(this.$device.isMobile){
  339. addEventListener('scroll', this.linkUpdate, false);
  340. }
  341. if(this.$store.state.auth.user){
  342. this.userPermissionGroup = this.$store.state.auth.user.user_permission_group;
  343. }
  344. //Set height 5% of content for contraindications
  345. if (!this.$refs.contraindications) return;
  346. setTimeout(() => {
  347. let [block] = this.$refs.contraindications;
  348. let height = this.$refs.newsContent.$el.offsetHeight;
  349. if (height < 1500) height = 1500;
  350. block.style.minHeight = `${height * 0.05}px`;
  351. block.style.fontSize = `${height * 0.015}px`;
  352. }, 1000);
  353. },
  354. methods: {
  355. isAdvert() {
  356. let isAdvert = false
  357. try {
  358. isAdvert = Boolean(Number(this.news.properties.advertisement.value))
  359. } catch (e) {}
  360. return isAdvert
  361. },
  362. convertDate(dateTampstamp) {
  363. const date = new Date(dateTampstamp * 1000);
  364. const dateString =
  365. date.toLocaleString("ru", {
  366. day: "numeric",
  367. month: "long",
  368. }) +
  369. " " +
  370. date.getFullYear() +
  371. ", " +
  372. ("0" + date.getHours()).slice(-2) +
  373. ":" +
  374. ("0" + date.getMinutes()).slice(-2);
  375. return dateString;
  376. },
  377. commentsResponseData(data){
  378. this.commentsList = data;
  379. console.log(this.commentsList);
  380. },
  381. commentResponseData(data){
  382. this.comment.response_user_id = data.user_id;
  383. this.comment.response_user_name = data.user_name;
  384. },
  385. clearCommentResponse(){
  386. this.comment.response_user_id = "";
  387. this.comment.response_user_name = "";
  388. },
  389. syncSliders(currentPosition, nextPosition) {
  390. this.$refs.galleryThumbs.goTo(nextPosition);
  391. this.$refs.galleryImages.goTo(nextPosition);
  392. },
  393. ImgLoaded() {
  394. this.imgloaded = true
  395. },
  396. inject(item) {
  397. let res = "##news_" + item.id + "##";
  398. this.$clipboard(res);
  399. alert('Инжект скопирован в буфер обмена');
  400. },
  401. linkUpdate () {
  402. return true;
  403. let cont = document.querySelector('#news_' + this.news.id);
  404. if(cont!==null){
  405. let rect =cont.getBoundingClientRect();
  406. let targetPosition = {
  407. top: window.pageYOffset + (rect.top + document.documentElement.clientHeight - 150),
  408. left: window.pageXOffset + rect.left,
  409. right: window.pageXOffset + rect.right,
  410. bottom: window.pageYOffset + (rect.bottom - document.documentElement.clientHeight + 200)
  411. },
  412. windowPosition = {
  413. top: window.pageYOffset,
  414. left: window.pageXOffset,
  415. right: window.pageXOffset + document.documentElement.clientWidth,
  416. bottom: window.pageYOffset + document.documentElement.clientHeight
  417. };
  418. if (targetPosition.bottom > windowPosition.top && targetPosition.top < windowPosition.bottom && targetPosition.right > windowPosition.left && targetPosition.left < windowPosition.right) {
  419. if(this.thisUrl !== document.location.pathname){
  420. history.pushState(null, this.news.title, this.thisUrl);
  421. }
  422. }
  423. }
  424. }
  425. },
  426. };
  427. </script>
  428. <style lang="less">
  429. .detail__wrapper-next-news{
  430. margin-top: -30px;
  431. }
  432. .sheet-custom {
  433. text-align: center;
  434. }
  435. h2{
  436. margin-bottom: 20px;
  437. }
  438. .detail{
  439. &__video{
  440. column-count: 1;
  441. position: relative;
  442. &:before{
  443. content: " ";
  444. padding-bottom: calc(100% / (16 / 9));
  445. float: left;
  446. }
  447. }
  448. &__iframe{
  449. width: 100%;
  450. height: 100%;
  451. position: absolute;
  452. top: 0;
  453. left: 0;
  454. }
  455. }
  456. .article_preview {
  457. max-width: 810px;
  458. font-size: 20px;
  459. line-height: 140%;
  460. padding-bottom: 20px;
  461. color: #737373;
  462. }
  463. .news-author__figure {
  464. flex: none;
  465. width: 50px;
  466. height: 50px;
  467. }
  468. .news-author__img {
  469. max-width: 100%;
  470. }
  471. ol {
  472. /*вопрос-ответ*/
  473. margin: 40px 20px;
  474. li {
  475. padding-left: 20px;
  476. margin-bottom: 40px;
  477. strong {
  478. display: block;
  479. font-size: 22px;
  480. padding: 20px 0 10px;
  481. border-top: 2px solid #f6911f;
  482. }
  483. &::marker {
  484. color: #f6911f;
  485. text-align: right;
  486. font-weight: bold;
  487. font-size: 22px;
  488. content: counter(list-item) " ";
  489. }
  490. }
  491. }
  492. .Card-fon {
  493. .redLine {
  494. padding-left: 10px;
  495. margin-bottom: 40px;
  496. position: relative;
  497. .numeric {
  498. color: #f6911f;
  499. font-weight: bold;
  500. font-size: 22px;
  501. float: left;
  502. padding: 20px 10px 0 0;
  503. height: 70px;
  504. }
  505. }
  506. .Card-vnutr {
  507. display: block;
  508. font-size: 22px;
  509. padding: 18px 0 10px;
  510. border-top: 2px solid #f6911f;
  511. font-weight: bold;
  512. }
  513. .Card-text {
  514. clear: both;
  515. }
  516. }
  517. h5 {
  518. /*источник фото*/
  519. font-weight: normal;
  520. opacity: 0.6;
  521. }
  522. h6 {
  523. /* выделить цветом - colored */
  524. font-weight: normal;
  525. background-color: rgb(247 245 238);
  526. padding: 20px 10px 20px 20px;
  527. margin: 30px 0;
  528. border-left: 2px solid #f6911f;
  529. line-height: 150%;
  530. font-size: 20px;
  531. html.dark-mode & {
  532. background-color: rgba(128, 128, 128, 0.3);
  533. }
  534. }
  535. blockquote {
  536. /* цитата */
  537. margin: 30px 0;
  538. padding: 20px 0;
  539. border-top: 1px solid #ccc;
  540. border-bottom: 1px solid #ccc;
  541. p {
  542. font-size: 28px;
  543. line-height: 140%;
  544. }
  545. @media screen and(max-width: 768px) {
  546. margin: 20px 0;
  547. padding: 10px 0;
  548. p{
  549. font-size: 22px;
  550. }
  551. }
  552. }
  553. .admin-button-in-news{
  554. margin-bottom: 30px;
  555. a{
  556. background-color: #000;
  557. color: #fff;
  558. font-size: 14px;
  559. padding: 8px 19px;
  560. cursor: pointer;
  561. }
  562. .inject{
  563. background-color: #1976d2;
  564. }
  565. .copy{
  566. background-color: #4caf50;
  567. }
  568. }
  569. .inner-newspage{
  570. position: relative;
  571. .detail{
  572. &__main {
  573. @media (min-width: 1100px) and (max-width: 1380px) {
  574. max-width: 65%;
  575. }
  576. @media (min-width: 900px) and (max-width: 1100px) {
  577. max-width: 55%;
  578. }
  579. }
  580. }
  581. .lenta{
  582. position: absolute;
  583. width: 100%;
  584. right: 0;
  585. height: 100%;
  586. @media (min-width: 1100px) and (max-width: 1380px) {
  587. max-width: 34%;
  588. }
  589. @media (min-width: 900px) and (max-width: 1100px) {
  590. max-width: 44%;
  591. }
  592. .news-side{
  593. &__list{
  594. height: 100%;
  595. width: 100%;
  596. overflow: scroll;
  597. position: absolute;
  598. background: white;
  599. }
  600. }
  601. }
  602. }
  603. </style>