ContentShow.vue 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. <template>
  2. <div class="content printable">
  3. <template v-if="!content.loading">
  4. <div class="breadcrumbs">
  5. <breadcrumbs />
  6. </div>
  7. <div ref="printArea"
  8. class="print-area">
  9. <div class="content-title">
  10. {{content.title}}
  11. </div>
  12. <q-separator class="q-mt-md" />
  13. <div class="content-body"
  14. v-html="content.description" />
  15. <div class="attributes">
  16. <q-btn v-if="content.category_info?.parent?.parent?.id"
  17. :to="{name: 'Public.PostCategoryParentParent.Show', params: {category_id: content.category_info.parent.parent.id}}"
  18. class="attribute-item">
  19. {{content.category_info.parent.parent.title}}
  20. </q-btn>
  21. <q-btn v-if="content.category_info?.parent?.id"
  22. :to="{name: 'Public.PostCategoryParent.Show', params: {category_id: content.category_info.parent.id}}"
  23. class="attribute-item">
  24. {{content.category_info.parent.title}}
  25. </q-btn>
  26. <q-btn v-if="content.category_info?.id"
  27. :to="{name: 'Public.PostCategory.Show', params: {category_id: content.category_info.id}}"
  28. class="attribute-item">
  29. {{content.category_info.title}}
  30. </q-btn>
  31. </div>
  32. </div>
  33. </template>
  34. <template v-else>
  35. <q-skeleton type="circle"
  36. size="100px"
  37. class="q-mt-md" />
  38. <q-skeleton width="150px"
  39. class="q-mt-md" />
  40. <q-skeleton height="150px"
  41. class="q-mt-md" />
  42. </template>
  43. </div>
  44. </template>
  45. <script>
  46. import { Content } from 'src/models/Content.js'
  47. import ShamsiDate from 'src/assets/ShamsiDate.js'
  48. import { APIGateway } from 'src/api/APIGateway.js'
  49. import { mixinPrefetchServerData, mixinWidget } from 'src/mixin/Mixins.js'
  50. import Breadcrumbs from 'src/components/Widgets/Breadcrumbs/Breadcrumbs.vue'
  51. let html2pdf
  52. // if (typeof window !== 'undefined') {
  53. // import('html2pdf.js')
  54. // .then((html2pdfLib) => {
  55. // html2pdf = html2pdfLib.default
  56. // })
  57. // }
  58. export default {
  59. name: 'ShowClassroomInfo',
  60. components: { Breadcrumbs },
  61. mixins: [mixinWidget, mixinPrefetchServerData],
  62. emits: ['onloadn'],
  63. data () {
  64. return {
  65. breadcrumbs: {
  66. visible: true,
  67. loading: false,
  68. path: [
  69. {
  70. label: 'خانه',
  71. to: { name: 'Public.Home' }
  72. },
  73. {
  74. label: 'یادداشت ها و مقالات',
  75. to: { name: 'Public.Content.List' }
  76. }
  77. ]
  78. },
  79. defaultOptions: {
  80. profileMode: false,
  81. contentShowRouteName: 'Public.Content.Show'
  82. },
  83. content: new Content()
  84. }
  85. },
  86. computed: {
  87. contentId () {
  88. return this.$route.params.id ?? this.$route.params.content_id
  89. }
  90. },
  91. methods: {
  92. prefetchServerDataPromise () {
  93. return this.getContent()
  94. },
  95. prefetchServerDataPromiseThen (content) {
  96. this.content = new Content(content)
  97. this.breadcrumbs.path.push({
  98. label: this.content.title,
  99. to: { name: this.localOptions.contentShowRouteName, params: { id: this.content.id } }
  100. })
  101. this.$store.commit('AppLayout/updateBreadcrumbs', this.breadcrumbs)
  102. this.$emit('onloadn', this.content)
  103. },
  104. prefetchServerDataPromiseCatch () {
  105. },
  106. getTitledDateTime (dateTime) {
  107. return ShamsiDate.getTitledDateTime(dateTime)
  108. },
  109. getTime (time) {
  110. return ShamsiDate.getDateTime(time)
  111. },
  112. getContent () {
  113. return APIGateway.content.get(this.contentId)
  114. },
  115. print () {
  116. this.convertPhotosToBase64()
  117. const html2pdfConfig = {
  118. margin: [0, 0, 0, 0],
  119. image: {
  120. type: 'jpeg',
  121. quality: 0.6
  122. },
  123. filename: this.content.title,
  124. html2canvas: {
  125. dpi: 1,
  126. scale: 2.5,
  127. letterRendering: true,
  128. useCORS: true
  129. },
  130. jsPDF: { unit: 'mm', format: 'a4', orientation: 'portrait' }
  131. }
  132. html2pdf()
  133. .set(html2pdfConfig)
  134. .from(this.$refs.printArea)
  135. .save()
  136. .thenExternal(() => {
  137. this.downloadLoading = false
  138. })
  139. },
  140. toDataURL(src, callback, outputFormat) {
  141. const img = new Image()
  142. img.crossOrigin = 'Anonymous'
  143. img.onload = function() {
  144. const canvas = document.createElement('CANVAS')
  145. const ctx = canvas.getContext('2d')
  146. canvas.height = this.naturalHeight
  147. canvas.width = this.naturalWidth
  148. ctx.drawImage(this, 0, 0)
  149. const dataURL = canvas.toDataURL(outputFormat)
  150. callback(dataURL)
  151. }
  152. if (src.includes('?')) {
  153. img.src = src
  154. } else {
  155. img.src = src + '?test=123'
  156. }
  157. if (img.complete || img.complete === undefined) {
  158. img.src = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw=='
  159. img.src = src
  160. }
  161. },
  162. convertPhotosToBase64 () {
  163. this.convertSvgToBase64()
  164. this.convertImagesToBase64()
  165. },
  166. convertImagesToBase64 () {
  167. if (!this.$refs.printArea) {
  168. return
  169. }
  170. const images = this.$refs.printArea.getElementsByTagName('img')
  171. images.forEach(image => {
  172. this.toDataURL(image.src, function(dataUrl) {
  173. image.src = dataUrl
  174. })
  175. })
  176. },
  177. convertSvgToBase64 () {
  178. if (!this.$refs.printArea) {
  179. return
  180. }
  181. const svgs = this.$refs.printArea.getElementsByTagName('svg')
  182. svgs.forEach(svg => {
  183. // Convert the SVG node to HTML.
  184. const div = document.createElement('div')
  185. div.appendChild(svg.cloneNode(true))
  186. // Encode the SVG as base64
  187. const b64 = 'data:image/svg+xml;base64,' + window.btoa(div.innerHTML)
  188. const url = 'url("' + b64 + '")'
  189. svg.style.backgroundImage = url
  190. svg.style.backgroundSize = 'cover'
  191. if (svg.parentElement.classList.contains('brace-left')) {
  192. svg.style.backgroundPosition = 'left'
  193. }
  194. if (svg.parentElement.classList.contains('brace-center')) {
  195. svg.style.backgroundPosition = 'bottom'
  196. }
  197. if (svg.parentElement.classList.contains('brace-right')) {
  198. svg.style.backgroundPosition = 'right'
  199. }
  200. })
  201. }
  202. }
  203. }
  204. </script>
  205. <style scoped lang="scss">
  206. @import "src/css/HTMLContent.scss";
  207. .content {
  208. .breadcrumbs {
  209. margin-bottom: 40px;
  210. }
  211. .content-title {
  212. font-weight: 900;
  213. font-size: 1.4rem;
  214. }
  215. .attributes {
  216. display: flex;
  217. flex-flow: row;
  218. .attribute-item {
  219. color: #475f4a;
  220. background-color: #eaeaea;
  221. border-radius: 8px;
  222. padding: 4px 8px;
  223. margin: 0 4px;
  224. font-size: 16px;
  225. }
  226. }
  227. .print-btn {
  228. width: 252px;
  229. }
  230. :deep(.content-body) {
  231. margin-top: 32px;
  232. @include HTMLContent;
  233. }
  234. }
  235. </style>