PageSourceOverlay.vue 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. <template lang="pug">
  2. q-layout(view='hHh lpR fFf', container)
  3. q-header.card-header.q-px-md.q-py-sm
  4. q-icon(name='img:/_assets/icons/fluent-code.svg', left, size='md')
  5. span Page Source
  6. q-space
  7. transition(name='syncing')
  8. q-spinner-tail.q-mr-sm(
  9. v-show='state.loading > 0'
  10. color='accent'
  11. size='24px'
  12. )
  13. q-btn.q-mr-md(
  14. icon='las la-download'
  15. color='teal-3'
  16. dense
  17. flat
  18. @click='download'
  19. )
  20. q-tooltip(anchor='bottom middle', self='top middle') {{t(`common.actions.download`)}}
  21. q-btn(
  22. icon='las la-times'
  23. color='pink-2'
  24. dense
  25. flat
  26. @click='close'
  27. )
  28. q-tooltip(anchor='bottom middle', self='top middle') {{t(`common.actions.close`)}}
  29. q-page-container
  30. q-page.bg-dark-6.text-white.font-robotomono.pagesource
  31. q-scroll-area(
  32. :thumb-style='thumb'
  33. :bar-style='bar'
  34. :horizontal-thumb-style='{ height: `5px` }'
  35. style="width: 100%; height: calc(100vh - 100px);"
  36. )
  37. pre.q-px-md(v-text='state.content')
  38. </template>
  39. <script setup>
  40. import { useI18n } from 'vue-i18n'
  41. import { exportFile, useQuasar } from 'quasar'
  42. import { onBeforeUnmount, onMounted, reactive, ref } from 'vue'
  43. import gql from 'graphql-tag'
  44. import { cloneDeep } from 'lodash-es'
  45. import { usePageStore } from 'src/stores/page'
  46. import { useSiteStore } from 'src/stores/site'
  47. // QUASAR
  48. const $q = useQuasar()
  49. // STORES
  50. const pageStore = usePageStore()
  51. const siteStore = useSiteStore()
  52. // I18N
  53. const { t } = useI18n()
  54. // DATA
  55. const state = reactive({
  56. loading: 0,
  57. content: ''
  58. })
  59. const thumb = {
  60. right: '2px',
  61. borderRadius: '5px',
  62. backgroundColor: '#FFF',
  63. width: '5px',
  64. opacity: 0.25
  65. }
  66. const bar = {
  67. backgroundColor: '#000',
  68. width: '9px',
  69. opacity: 0.25
  70. }
  71. const contentTypes = {
  72. markdown: {
  73. ext: 'md',
  74. mime: 'text/markdown'
  75. },
  76. html: {
  77. ext: 'html',
  78. mime: 'text/html'
  79. }
  80. }
  81. // METHODS
  82. function download () {
  83. const fileType = contentTypes[state.contentType] ?? { ext: 'txt', mime: 'text/plain' }
  84. exportFile(`page.${fileType.ext}`, state.content, { mimeType: `${fileType.mime};charset=UTF-8` })
  85. }
  86. function close () {
  87. siteStore.$patch({ overlay: '' })
  88. }
  89. async function load () {
  90. state.loading++
  91. $q.loading.show()
  92. try {
  93. const resp = await APOLLO_CLIENT.query({
  94. query: gql`
  95. query loadPageSource (
  96. $id: UUID!
  97. ) {
  98. pageById(
  99. id: $id
  100. ) {
  101. id
  102. content
  103. contentType
  104. }
  105. }
  106. `,
  107. variables: {
  108. id: pageStore.id
  109. },
  110. fetchPolicy: 'network-only'
  111. })
  112. const pageData = cloneDeep(resp?.data?.pageById ?? {})
  113. if (!pageData?.id) {
  114. throw new Error('ERR_PAGE_NOT_FOUND')
  115. }
  116. state.content = pageData.content
  117. state.contentType = pageData.contentType
  118. } catch (err) {
  119. $q.notify({
  120. type: 'negative',
  121. message: err.message
  122. })
  123. }
  124. $q.loading.hide()
  125. state.loading--
  126. }
  127. onMounted(() => {
  128. load()
  129. })
  130. onBeforeUnmount(() => {
  131. siteStore.overlayOpts = {}
  132. })
  133. </script>
  134. <style lang="scss" scoped>
  135. .pagesource {
  136. > pre {
  137. margin: 0;
  138. overflow-x: auto;
  139. }
  140. }
  141. </style>